M src/abase.rs => src/abase.rs +20 -10
@@ 1,16 1,26 @@
-use crate::base::*;
-use crate::fem;
+//! Base -- the lowest level IR in Effem before generating the output machine code.
-pub fn abase(expr: &fem::Expr) -> anyhow::Result<Expr> {
+use crate::cache::Cache;
+use crate::fem::Expr as FExpr;
+use crate::name::*;
+
+#[derive(Clone)]
+pub enum Expr {
+ F64(f64),
+ Add(Box<Expr>, Box<Expr>),
+ Mul(Box<Expr>, Box<Expr>),
+}
+
+pub fn abase(cache: &mut Cache, expr: &FExpr) -> anyhow::Result<Expr> {
Ok(match *expr {
- fem::Expr::F64(x) => Expr::F64(x),
- fem::Expr::App(ref f, ref xs) => match **f {
- fem::Expr::Var(ref x) if x.to_string() == "+.builtin." =>
- Expr::Add(Box::new(abase(&xs[0])?), Box::new(abase(&xs[1])?)),
- fem::Expr::Var(ref x) if x.to_string() == "*.builtin." =>
- Expr::Mul(Box::new(abase(&xs[0])?), Box::new(abase(&xs[1])?)),
+ FExpr::F64(x) => Expr::F64(x),
+ FExpr::App(ref f, ref xs) => match **f {
+ FExpr::Var(ResName { res: Res::Prim(p) }) => match p {
+ Prim::Add => Expr::Add(Box::new(abase(cache, &xs[0])?), Box::new(abase(cache, &xs[1])?)),
+ Prim::Mul => Expr::Mul(Box::new(abase(cache, &xs[0])?), Box::new(abase(cache, &xs[1])?)),
+ },
_ => todo!(),
},
- fem::Expr::Var(_) => todo!(),
+ FExpr::Var(_) => todo!(),
})
}
D src/ast.rs => src/ast.rs +0 -22
@@ 1,22 0,0 @@
-//! Abstract syntax
-
-use crate::name::*;
-use std::collections::HashMap;
-
-pub type PModule = Module<ParsedName>;
-pub type PExpr = Expr<ParsedName>;
-
-pub type RModule = Module<FullQualName>;
-pub type RExpr = Expr<FullQualName>;
-
-#[derive(Debug)]
-pub struct Module<N> {
- pub defs: HashMap<ItemName, Expr<N>>,
-}
-
-#[derive(Debug, PartialEq)]
-pub enum Expr<N> {
- F64(f64),
- App(Box<Expr<N>>, Vec<Expr<N>>),
- Var(N),
-}
D src/base.rs => src/base.rs +0 -8
@@ 1,8 0,0 @@
-//! Base -- the lowest level IR in Effem before generating the output machine code.
-
-#[derive(Clone)]
-pub enum Expr {
- F64(f64),
- Add(Box<Expr>, Box<Expr>),
- Mul(Box<Expr>, Box<Expr>),
-}
M src/cache.rs => src/cache.rs +122 -59
@@ 1,98 1,161 @@
use crate::name::*;
-use crate::{abase::abase, desugar::desugar, eval::interpret, lex::lex, parse::parse, resolve::resolve};
-use crate::{ast, base, fem, val};
+use crate::parse::{self, Expr as PExpr, Module as PModule};
+use crate::resolve::{self, Expr as RExpr};
+use crate::{abase, eval, fem, lex};
use anyhow::{anyhow, Context};
+use std::borrow::Borrow;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
+use std::rc::Rc;
pub struct Cache {
root_dir: PathBuf,
- resolved_module_cache: HashMap<QualModuleName, ast::RModule>,
- desugared_cache: HashMap<FullQualName, fem::Expr>,
- flat_cache: HashMap<FullQualName, base::Expr>,
- val_cache: HashMap<FullQualName, val::Val>,
+ sources: HashMap<FullName, (PathBuf, String)>,
+ parsed_modules: HashMap<FullName, PModule>,
+ resolved_names: HashMap<FullName, DefId>,
+ resolved_names_rev: HashMap<DefId, FullName>,
+ resolveds: HashMap<DefId, RExpr>,
+ // desugareds: HashMap<Query, fem::Expr>,
+ abaseds: HashMap<DefId, abase::Expr>,
+ evaluateds: HashMap<DefId, eval::Val>,
}
impl Cache {
pub fn for_package_in(root_dir: &Path) -> Self {
Self {
root_dir: root_dir.to_owned(),
- resolved_module_cache: HashMap::new(),
- desugared_cache: HashMap::new(),
- flat_cache: HashMap::new(),
- val_cache: HashMap::new(),
+ sources: HashMap::new(),
+ parsed_modules: HashMap::new(),
+ resolved_names: HashMap::new(),
+ resolved_names_rev: HashMap::new(),
+ resolveds: HashMap::new(),
+ abaseds: HashMap::new(),
+ evaluateds: HashMap::new(),
}
}
- pub fn fetch_val(&mut self, def_name: &FullQualName) -> anyhow::Result<&val::Val> {
- Ok(if self.val_cache.contains_key(def_name) {
- &self.val_cache[def_name]
+ pub fn fetch_evaluated(&mut self, def_query: DefId) -> anyhow::Result<&eval::Val> {
+ if self.evaluateds.contains_key(&def_query) {
+ Ok(self.evaluateds.get(&def_query).unwrap())
} else {
- let base = self.fetch_flattened(def_name)?.clone();
- let val = interpret(self, &base)?;
- self.val_cache.insert(def_name.clone(), val);
- self.val_cache.get(def_name).unwrap()
- })
+ let base = self.fetch_base(def_query)?.clone();
+ let val = eval::eval(self, &base)?;
+ self.evaluateds.insert(def_query, val);
+ Ok(&self.evaluateds[&def_query])
+ }
}
- pub fn fetch_flattened(&mut self, def_name: &FullQualName) -> anyhow::Result<&base::Expr> {
- Ok(if self.flat_cache.contains_key(def_name) {
- &self.flat_cache[def_name]
+ pub fn fetch_base<'c>(&'c mut self, def_query: DefId) -> anyhow::Result<&'c abase::Expr> {
+ Ok(if self.abaseds.contains_key(&def_query) {
+ &self.abaseds[&def_query]
} else {
- let checked = self.fetch_checked(def_name)?.clone();
- let base = abase(&checked)?;
- self.flat_cache.insert(def_name.clone(), base);
- self.flat_cache.get(def_name).unwrap()
+ let checked: fem::Expr = self.fetch_checked(def_query)?.clone();
+ let base = abase::abase(self, &checked)?;
+ self.abaseds.insert(def_query, base);
+ &self.abaseds[&def_query]
})
}
- pub fn fetch_checked(&mut self, def_name: &FullQualName) -> anyhow::Result<&fem::Expr> {
- self.fetch_desugared(def_name)
+ pub fn fetch_checked(&mut self, def_query: DefId) -> anyhow::Result<&fem::Expr> {
+ self.fetch_desugared(def_query)
}
- pub fn fetch_desugared(&mut self, def_name: &FullQualName) -> anyhow::Result<&fem::Expr> {
- Ok(if self.desugared_cache.contains_key(def_name) {
- &self.desugared_cache[def_name]
+ pub fn fetch_desugared(&mut self, def_query: DefId) -> anyhow::Result<&fem::Expr> {
+ self.fetch_resolved(def_query)
+ // Ok(if self.desugareds.contains_key(def_query) {
+ // &self.desugareds[def_query]
+ // } else {
+ // let resolved = self.fetch_resolved(def_query)?.clone();
+ // let desugared = desugar(&resolved)?;
+ // self.desugareds.insert(def_query.clone(), desugared);
+ // self.desugareds.get(def_query).unwrap()
+ // })
+ }
+
+ pub fn fetch_resolved(&mut self, id: DefId) -> anyhow::Result<&RExpr> {
+ if self.resolveds.contains_key(&id) {
+ Ok(&self.resolveds[&id])
} else {
- let resolved = self.fetch_resolved(def_name)?.clone();
- let desugared = desugar(&resolved)?;
- self.desugared_cache.insert(def_name.clone(), desugared);
- self.desugared_cache.get(def_name).unwrap()
- })
+ let (name, body) = self.fetch_parsed_def(id)?;
+ let (name, body) = (name.clone(), body.clone());
+ let resolved = resolve::resolve_def(self, &name, &body)?;
+ self.resolveds.insert(id, resolved);
+ Ok(&self.resolveds[&id])
+ }
+ }
+
+ pub fn fetch_parsed_def(&mut self, def_query: DefId) -> anyhow::Result<(FullName, &PExpr)> {
+ let name = self
+ .resolved_names_rev
+ .get(&def_query)
+ .unwrap_or_else(|| {
+ panic!("ice: no reverse for {def_query:?}. all resolved names: {:?}", self.resolved_names)
+ })
+ .clone();
+ let (def_ident, module_name) = name.0.split_last().unwrap();
+ let module = self.fetch_parsed_module(&FullName(module_name.to_vec()))?;
+ let parsed = &module.defs[&PubIdent(def_ident.clone())];
+ Ok((name, parsed))
}
- pub fn fetch_resolved(&mut self, def_name: &FullQualName) -> anyhow::Result<&ast::RExpr> {
- let mname = &def_name.module;
- let m = if self.resolved_module_cache.contains_key(mname) {
- &self.resolved_module_cache[mname]
+ pub fn fetch_module_local_resolved_names(&mut self, _module: &FullName) -> anyhow::Result<&HashMap<PubIdent, Res>> {
+ todo!()
+ }
+
+ pub fn fetch_global_resolved_name(&mut self, name: &FullName) -> anyhow::Result<DefId> {
+ if let Some(&res) = self.resolved_names.get(name) {
+ Ok(res)
+ } else {
+ let (def_ident, module_name) = name.0.split_last().expect("ice: query should be nonempty");
+ let module = self.fetch_parsed_module(&FullName(module_name.to_vec()))?;
+ if module.defs.contains_key(&PubIdent(def_ident.clone())) {
+ assert!(self.resolved_names.len() < u32::MAX as usize);
+ let id = self.resolved_names.len() as u32;
+ self.resolved_names.insert(name.clone(), id);
+ self.resolved_names_rev.insert(id, name.clone());
+ Ok(id)
+ } else {
+ Err(anyhow!("item {def_ident:?} is not defined in module {module_name:?}"))
+ }
+ }
+ }
+
+ pub fn fetch_parsed_module(&mut self, module_name: &FullName) -> anyhow::Result<&PModule> {
+ let (path, src) = self.fetch_source(module_name)?;
+ let tokens = lex::lex(Some(path.into()), &src)?;
+ let parsed = parse::parse(Some(path.into()), &src, &tokens)?;
+ self.parsed_modules.insert(module_name.clone(), parsed);
+ Ok(&self.parsed_modules[module_name])
+ }
+
+ pub fn fetch_source(&mut self, module_name: &FullName) -> anyhow::Result<(&Path, &str)> {
+ if self.sources.contains_key(module_name) {
+ let &(ref path, ref src) = &self.sources[module_name];
+ Ok((path, src))
} else {
- let path = std::rc::Rc::new(find_module(&self.root_dir, mname)?);
- let src = std::fs::read_to_string(path.as_ref())
- .with_context(|| format!("ICE: Failed to read source from {path:?}"))?;
- let tokens = lex(Some(path.clone()), &src)?;
- let parsed = parse(Some(path), &tokens)?;
- eprintln!("{parsed:?}");
- let resolved = resolve(parsed)?;
- self.resolved_module_cache.insert(mname.clone(), resolved);
- self.resolved_module_cache.get(mname).unwrap()
- };
- m.defs
- .get(&def_name.name)
- .ok_or_else(|| anyhow!("{} is not defined in module {}", def_name.name, def_name.module))
+ let path = find_module(&self.root_dir, module_name)?;
+ let src =
+ std::fs::read_to_string(&path).with_context(|| format!("ice: Failed to read source from {path:?}"))?;
+ self.sources.insert(module_name.clone(), (path, src));
+ let &(ref path, ref src) = &self.sources[module_name];
+ Ok((path, src))
+ }
}
}
-fn find_module(root: &Path, mname: &QualModuleName) -> anyhow::Result<PathBuf> {
- let mut path = match mname.root {
- PkgRef::Our => root.to_path_buf(),
- // PkgRef::Dep(_) => todo!(),
- PkgRef::Builtin => todo!(),
+fn find_module(root: &Path, mname: &FullName) -> anyhow::Result<PathBuf> {
+ let (namespace, segments) = mname.0.split_first().ok_or_else(|| anyhow!("unexpected empty module path"))?;
+ let mut path = match namespace.as_ref() {
+ "pkg" => root.to_path_buf(),
+ "dep" => todo!(),
+ "prim" => panic!("ice: find_module called with `prim` root. This should've been handled already."),
+ _ => return Err(anyhow!("Invalid module namespace `{namespace}`. Expected one of `pkg`, `dep`, `prim`.")),
};
- path.extend(&mname.segments);
+ path.extend(segments);
path.set_extension("fm");
if path.exists() {
Ok(path)
} else {
- Err(anyhow!("Couldn't find module {mname}. Searched: {path:?}"))
+ Err(anyhow!("Couldn't find module {mname:?}. Searched: {path:?}"))
}
}
M src/desugar.rs => src/desugar.rs +8 -9
@@ 1,11 1,10 @@
-use crate::ast;
use crate::fem::*;
-pub fn desugar(expr: &ast::RExpr) -> anyhow::Result<Expr> {
- Ok(match expr {
- ast::Expr::F64(x) => Expr::F64(*x),
- ast::Expr::Var(x) => Expr::Var(x.clone()),
- ast::Expr::App(f, xs) =>
- Expr::App(Box::new(desugar(f)?), xs.iter().map(|x| desugar(x)).collect::<Result<_, _>>()?),
- })
-}
+// pub fn desugar(expr: &ast::RExpr) -> anyhow::Result<Expr> {
+// Ok(match expr {
+// ast::Expr::F64(x) => Expr::F64(*x),
+// ast::Expr::Var(x) => Expr::Var(x.clone()),
+// ast::Expr::App(f, xs) =>
+// Expr::App(Box::new(desugar(f)?), xs.iter().map(|x| desugar(x)).collect::<Result<_, _>>()?),
+// })
+// }
M src/diag.rs => src/diag.rs +54 -23
@@ 1,27 1,39 @@
-use std::io::{BufRead, BufReader};
-
-pub type PathRef = std::rc::Rc<std::path::PathBuf>;
+use std::io::*;
+use std::path::Path;
+use std::rc::Rc;
#[derive(Debug, Clone, PartialEq)]
pub enum Loc {
- File { file: PathRef, line: u32, column: u32 },
- FileGeneral { file: PathRef },
- Anon { line: u32, column: u32 },
+ File { file: Rc<Path>, offset: u32 },
+ FileGeneral { file: Rc<Path> },
+ Anon { offset: u32 },
AnonGeneral,
}
impl Loc {
+ pub fn new(file: Option<Rc<Path>>, offset: u32) -> Self {
+ match file {
+ Some(file) => Self::File { file, offset },
+ None => Self::Anon { offset },
+ }
+ }
+
+ pub fn with_offset(&self, offset: u32) -> Self {
+ use Loc::*;
+ match self {
+ File { file, .. } | FileGeneral { file } => File { file: file.clone(), offset },
+ Anon { .. } | AnonGeneral => Anon { offset },
+ }
+ }
+
pub fn err<M: std::fmt::Display>(&self, msg: M) -> anyhow::Error {
match self {
- Loc::File { file, line, column } => {
- let gutter = " ".repeat(column.ilog10() as usize + 1);
- let marker = " ".repeat(*column as usize);
+ Loc::File { file, offset } => {
// TODO: Get source from cache instead. Don't want IO effects in random functions, and it might be faster.
- let src_line = BufReader::new(std::fs::File::open(file.as_ref()).unwrap())
- .lines()
- .nth(*line as usize)
- .unwrap()
- .unwrap();
+ let mut f = BufReader::new(std::fs::File::open(file.as_ref()).unwrap());
+ let (line, column, src_line) = seek_count_line_and_column(&mut f, *offset as u64);
+ let gutter = " ".repeat(column.ilog10() as usize + 1);
+ let marker = " ".repeat(column as usize);
let file = file.to_string_lossy();
anyhow::anyhow!(
"{file}:{line}:{column}: error: {msg}\n{gutter} |\n{column} | {src_line}\n{gutter} | {marker}^",
@@ 30,21 42,40 @@ impl Loc {
Loc::FileGeneral { file } => {
anyhow::anyhow!("{}: error: {msg}", file.to_string_lossy())
}
- Loc::Anon { line, column } => {
- anyhow::anyhow!("<unknown>:{line}:{column}: error: {msg}")
+ Loc::Anon { offset } => {
+ anyhow::anyhow!("offset={offset}: error: {msg}")
}
Loc::AnonGeneral => anyhow::anyhow!("error: {msg}"),
}
}
}
-impl std::fmt::Display for Loc {
- fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
- match self {
- Loc::File { file, line, column } => write!(f, "{}:{line}:{column}", file.to_string_lossy()),
- Loc::FileGeneral { file } => write!(f, "{}:-:-", file.to_string_lossy()),
- Loc::Anon { line, column } => write!(f, "-:{line}:{column}"),
- Loc::AnonGeneral => write!(f, "-:-:-"),
+// Seek to `offset`, returning the line an column number of where `offset` occured,
+// as well as a copy of that complete line.
+fn seek_count_line_and_column(f: &mut BufReader<impl Seek + Read>, mut offset: u64) -> (u64, u64, String) {
+ let mut line = 0;
+ let mut buf = String::new();
+ loop {
+ match f.read_line(&mut buf) {
+ Ok(0) => panic!("ice: unexpected end of file"),
+ Ok(n) =>
+ if n as u64 > offset {
+ if buf.ends_with('\n') {
+ buf.pop();
+ if buf.ends_with('\r') {
+ buf.pop();
+ }
+ }
+ let overshot = n as i64 - offset as i64;
+ f.seek_relative(-overshot).unwrap();
+ let column = offset;
+ break (line, column, buf);
+ } else {
+ buf.clear();
+ line += 1;
+ offset -= n as u64;
+ },
+ Err(e) => panic!("ice: error reading file: {}", e),
}
}
}
M src/eval.rs => src/eval.rs +25 -10
@@ 1,17 1,32 @@
-use crate::{base, cache::Cache, name::*, val::*};
+use crate::abase::Expr;
+use crate::{cache::Cache, name::*};
-pub fn run<'c>(cache: &'c mut Cache, entrypoint: &FullQualName) -> anyhow::Result<&'c Val> {
- cache.fetch_val(entrypoint)
+#[derive(Debug, Clone, PartialEq)]
+pub enum Val {
+ F64(f64),
}
-pub fn interpret(cache: &mut Cache, expr: &base::Expr) -> anyhow::Result<Val> {
- Ok(match *expr {
- base::Expr::F64(x) => Val::F64(x),
- base::Expr::Add(ref x, ref y) => match (interpret(cache, x)?, interpret(cache, y)?) {
+impl std::ops::Add for Val {
+ type Output = Val;
+ fn add(self, other: Val) -> Val {
+ match (self, other) {
(Val::F64(x), Val::F64(y)) => Val::F64(x + y),
- },
- base::Expr::Mul(ref x, ref y) => match (interpret(cache, x)?, interpret(cache, y)?) {
+ }
+ }
+}
+impl std::ops::Mul for Val {
+ type Output = Val;
+ fn mul(self, other: Val) -> Val {
+ match (self, other) {
(Val::F64(x), Val::F64(y)) => Val::F64(x * y),
- },
+ }
+ }
+}
+
+pub fn eval(cache: &mut Cache, expr: &Expr) -> anyhow::Result<Val> {
+ Ok(match *expr {
+ Expr::F64(x) => Val::F64(x),
+ Expr::Add(ref a, ref b) => eval(cache, a)? + eval(cache, b)?,
+ Expr::Mul(ref a, ref b) => eval(cache, a)? * eval(cache, b)?,
})
}
M src/fem.rs => src/fem.rs +2 -12
@@ 1,16 1,6 @@
//! Effem core IR
-use crate::name::*;
-use std::collections::HashMap;
-#[derive(Debug)]
-pub struct Module {
- pub defs: HashMap<ItemName, Expr>,
-}
-#[derive(Clone, Debug, PartialEq)]
-pub enum Expr {
- F64(f64),
- Var(FullQualName),
- App(Box<Expr>, Vec<Expr>),
-}
+
+pub use crate::resolve::Expr;
M src/lex.rs => src/lex.rs +53 -67
@@ 1,5 1,8 @@
use crate::diag::*;
+use crate::name::PrivIdent;
use std::borrow::Cow;
+use std::path::Path;
+use std::rc::Rc;
// TODO: Compare using:
// - just a deeply nested tree with boxes
@@ 11,10 14,10 @@ pub enum Tok<'s> {
Int(i64),
Float(f64),
Str(Cow<'s, str>),
- Ident(Ident<'s>),
+ Ident(&'s str),
DotIn(Box<Token<'s>>, Box<Token<'s>>),
DotPost(Box<Token<'s>>),
- Keyword(Ident<'s>),
+ Keyword(&'s str),
Octothorpe,
Octothorped(Box<Token<'s>>),
Backslashed(Box<Token<'s>>),
@@ 25,20 28,11 @@ pub enum Tok<'s> {
#[derive(Debug, Clone, PartialEq)]
pub struct Token<'s> {
- pub loc: Loc,
+ pub offset: u32,
pub tok: Tok<'s>,
}
-#[derive(Debug, Clone, PartialEq)]
-pub struct Ident<'s>(&'s str);
-
-impl<'s> Ident<'s> {
- pub fn to_str(&self) -> &'s str {
- self.0
- }
-}
-
-pub fn lex(f: Option<PathRef>, src: &str) -> anyhow::Result<Vec<Token>> {
+pub fn lex(f: Option<Rc<Path>>, src: &str) -> anyhow::Result<Vec<Token>> {
assert!(src.len() < u32::MAX as usize);
let mut sc = Scanner::new(f, src);
sc.terms().map_err(|Err { expected, found, loc }| {
@@ 53,21 47,16 @@ pub fn lex(f: Option<PathRef>, src: &str) -> anyhow::Result<Vec<Token>> {
struct Scanner<'s> {
src: &'s str,
i: usize,
- line: u32,
- col: u32, // column in number of bytes from line start bytes
- file: Option<PathRef>,
+ file: Option<Rc<Path>>,
}
impl<'s> Scanner<'s> {
- fn new(file: Option<PathRef>, src: &'s str) -> Self {
- Self { src, i: 0, line: 0, col: 0, file }
+ fn new(file: Option<Rc<Path>>, src: &'s str) -> Self {
+ Self { src, i: 0, file }
}
fn loc(&self) -> Loc {
- match self.file {
- Some(ref f) => Loc::File { file: f.clone(), line: self.line, column: self.col },
- None => Loc::Anon { line: self.line, column: self.col },
- }
+ Loc::new(self.file.clone(), self.i as u32)
}
fn maybe_term(&mut self) -> Result<Option<Token<'s>>, Err> {
@@ 80,9 69,9 @@ impl<'s> Scanner<'s> {
Some(if let Some('.') = self.peek() {
self.consume();
if let Some(y) = self.maybe_term_nospace()? {
- Token { loc: x.loc.clone(), tok: Tok::DotIn(Box::new(x), Box::new(y)) }
+ Token { offset: x.offset.clone(), tok: Tok::DotIn(Box::new(x), Box::new(y)) }
} else {
- Token { loc: x.loc.clone(), tok: Tok::DotPost(Box::new(x)) }
+ Token { offset: x.offset.clone(), tok: Tok::DotPost(Box::new(x)) }
}
} else {
x
@@ 93,7 82,7 @@ impl<'s> Scanner<'s> {
}
fn maybe_factor(&mut self) -> Result<Option<Token<'s>>, Err> {
- let loc = self.loc();
+ let offset = self.i as u32;
let c = match self.peek() {
Some(c) => c,
None => return Ok(None),
@@ 103,53 92,58 @@ impl<'s> Scanner<'s> {
self.consume();
let tts = self.terms()?;
self.consume_expect(')')?;
- Ok(Some(Token { loc, tok: Tok::Parens(tts) }))
+ Ok(Some(Token { offset, tok: Tok::Parens(tts) }))
}
'[' => {
self.consume();
let tts = self.terms()?;
self.consume_expect(']')?;
- Ok(Some(Token { loc, tok: Tok::Brackets(tts) }))
+ Ok(Some(Token { offset, tok: Tok::Brackets(tts) }))
}
'{' => {
self.consume();
let tts = self.terms()?;
self.consume_expect('}')?;
- Ok(Some(Token { loc, tok: Tok::Braces(tts) }))
+ Ok(Some(Token { offset, tok: Tok::Braces(tts) }))
}
')' | ']' | '}' => Ok(None),
+ ' ' => Ok(None),
'0'..='9' => self.num().map(Some),
- '"' => self.string().map(|s| Some(Token { tok: Tok::Str(s), loc })),
+ '"' => self.string().map(|s| Some(Token { tok: Tok::Str(s), offset })),
':' => {
self.consume();
let id = self.ident()?;
- Ok(Some(Token { loc, tok: Tok::Keyword(id) }))
+ Ok(Some(Token { offset, tok: Tok::Keyword(id) }))
}
'#' => {
self.consume();
Ok(Some(match self.maybe_term_nospace()? {
- Some(t) => Token { loc, tok: Tok::Octothorped(Box::new(t)) },
- None => Token { loc, tok: Tok::Octothorpe },
+ Some(t) => Token { offset, tok: Tok::Octothorped(Box::new(t)) },
+ None => Token { offset, tok: Tok::Octothorpe },
}))
}
'\\' => {
self.consume();
match self.maybe_term_nospace()? {
- Some(t) => Ok(Some(Token { loc, tok: Tok::Backslashed(Box::new(t)) })),
+ Some(t) => Ok(Some(Token { offset, tok: Tok::Backslashed(Box::new(t)) })),
None => Err(Err {
expected: Thing::Descr("'\\' followed immediately by any token tree"),
found: Thing::Anon,
- loc,
+ loc: Loc::new(self.file.clone(), offset),
}),
}
}
'-' if matches!(self.peek2(), Some('0'..='9')) => Ok(Some(match self.num()? {
- Token { loc, tok: Tok::Int(x) } => Token { loc, tok: Tok::Int(-x) },
- Token { loc, tok: Tok::Float(x) } => Token { loc, tok: Tok::Float(-x) },
+ Token { offset, tok: Tok::Int(x) } => Token { offset, tok: Tok::Int(-x) },
+ Token { offset, tok: Tok::Float(x) } => Token { offset, tok: Tok::Float(-x) },
_ => unreachable!(),
})),
- _ if ident_char(c) => self.ident().map(|id| Some(Token { loc, tok: Tok::Ident(id) })),
- _ => Err(Err { expected: Thing::Descr("any token tree"), found: Thing::Char(c), loc }),
+ _ if ident_char(c) => self.ident().map(|id| Some(Token { offset, tok: Tok::Ident(id) })),
+ _ => Err(Err {
+ expected: Thing::Descr("any token tree"),
+ found: Thing::Char(c),
+ loc: Loc::new(self.file.clone(), offset),
+ }),
}
}
@@ 161,8 155,8 @@ impl<'s> Scanner<'s> {
Ok(ts)
}
- fn ident(&mut self) -> Result<Ident<'s>, Err> {
- self.find_on_line1(Thing::Descr("identifier"), |c| !ident_char(c)).map(Ident)
+ fn ident(&mut self) -> Result<&'s str, Err> {
+ self.find_on_line1(Thing::Descr("identifier"), |c| !ident_char(c))
}
fn find_on_line1(&mut self, expected: Thing, mut pred: impl FnMut(char) -> bool) -> Result<&'s str, Err> {
@@ 170,7 164,6 @@ impl<'s> Scanner<'s> {
match self.input().find(|c| c == '\n' || pred(c)) {
None => {
self.i = self.src.len();
- self.col += (self.i - i0) as u32;
if self.i > i0 {
Ok(&self.src[i0..self.i])
} else {
@@ 180,7 173,6 @@ impl<'s> Scanner<'s> {
Some(0) => Err(Err { expected, found: Thing::Char(self.peek().unwrap()), loc: self.loc() }),
Some(n) => {
self.i += n;
- self.col += n as u32;
Ok(&self.src[i0..self.i])
}
}
@@ 271,14 263,14 @@ impl<'s> Scanner<'s> {
}
fn num(&mut self) -> Result<Token<'s>, Err> {
- let loc = self.loc();
+ let offset = self.i as u32;
let mut acc = 0u64;
loop {
match self.peek() {
Some('.') => break self.float(acc),
Some(c @ '0'..='9') => acc = acc * 10 + (c as u8 - b'0') as u64,
Some('_') => (),
- _ => break Ok(Token { tok: Tok::Int(acc as i64), loc }),
+ _ => break Ok(Token { tok: Tok::Int(acc as i64), offset }),
}
self.consume();
}
@@ 296,7 288,7 @@ impl<'s> Scanner<'s> {
factor *= 0.1;
}
Some('_') => (),
- _ => break Ok(Token { tok: Tok::Float(left as f64 + right), loc: self.loc() }),
+ _ => break Ok(Token { tok: Tok::Float(left as f64 + right), offset: self.i as u32 }),
}
self.consume();
}
@@ 356,12 348,6 @@ impl<'s> Scanner<'s> {
self.input().chars().next().map(|c| {
let n = c.len_utf8();
self.i += n;
- if c == '\n' {
- self.line += 1;
- self.col = 0;
- } else {
- self.col += n as u32;
- }
c
})
}
@@ 385,7 371,7 @@ pub struct Err {
impl std::fmt::Debug for Err {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
- write!(f, "{}: expected {}, found {}", self.loc, self.expected, self.found)
+ write!(f, "loc={:?}: expected {}, found {}", self.loc, self.expected, self.found)
}
}
@@ 430,10 416,10 @@ mod test {
#[test]
fn test_ident() {
- assert_eq!(Scanner::new(None, "foo").ident().unwrap(), Ident("foo"));
- assert_eq!(Scanner::new(None, "-bar").ident().unwrap(), Ident("-bar"));
- assert_eq!(Scanner::new(None, "-").ident().unwrap(), Ident("-"));
- assert_eq!(Scanner::new(None, "*").ident().unwrap(), Ident("*"));
+ assert_eq!(Scanner::new(None, "foo").ident().unwrap(), "foo");
+ assert_eq!(Scanner::new(None, "-bar").ident().unwrap(), "-bar");
+ assert_eq!(Scanner::new(None, "-").ident().unwrap(), "-");
+ assert_eq!(Scanner::new(None, "*").ident().unwrap(), "*");
}
#[test]
@@ 442,7 428,7 @@ mod test {
let tok = sc.maybe_term().unwrap().unwrap();
assert!(matches!(
tok,
- Token { tok: Tok::Keyword(Ident(s)), .. } if s == "foo"
+ Token { tok: Tok::Keyword(s), .. } if s == "foo"
))
}
@@ 468,9 454,9 @@ mod test {
Token { tok: Tok::Parens(ref tts), .. } => tts,
ref t => panic!("expected Tok::Parens, but result was {t:?}"),
};
- assert_eq!(tts[0].tok, Tok::Ident(Ident("+")));
+ assert_eq!(tts[0].tok, Tok::Ident("+"));
assert_eq!(tts[1].tok, Tok::Int(1));
- assert_eq!(tts[2].tok, Tok::Ident(Ident("x")));
+ assert_eq!(tts[2].tok, Tok::Ident("x"));
}
#[test]
@@ 481,7 467,7 @@ mod test {
Token { tok: Tok::Parens(ref tts), .. } => tts,
ref t => panic!("expected Tok::Parens, but result was {t:?}"),
};
- assert_eq!(tts[0].tok, Tok::Ident(Ident("+")));
+ assert_eq!(tts[0].tok, Tok::Ident("+"));
assert_eq!(tts[1].tok, Tok::Int(1));
assert_eq!(tts[2].tok, Tok::Int(2));
}
@@ 493,9 479,9 @@ mod test {
&ts[0],
Token { tok: Tok::Parens(ts1), ..} if matches!(
ts1.as_slice(),
- [Token{tok:Tok::Ident(Ident("def")), ..}, Token{tok: Tok::Ident(Ident("x")), ..}, Token{tok: Tok::Parens(ts2), ..}] if matches!(
+ [Token{tok:Tok::Ident("def"), ..}, Token{tok: Tok::Ident("x"), ..}, Token{tok: Tok::Parens(ts2), ..}] if matches!(
ts2.as_slice(),
- [Token{tok: Tok::Ident(Ident("+")), ..}, Token{ tok: Tok::Int(1), ..}, Token{ tok: Tok::Int(2), ..}]
+ [Token{tok: Tok::Ident("+"), ..}, Token{ tok: Tok::Int(1), ..}, Token{ tok: Tok::Int(2), ..}]
)
)
)
@@ 503,11 489,11 @@ mod test {
#[test]
fn test_pkg() {
- let src = "pkg.";
+ let src = "pkg. ";
let mut sc = Scanner::new(None, src);
let t = sc.maybe_term().unwrap().unwrap().tok;
match t {
- Tok::DotPost(c) if matches!(c.tok, Tok::Ident(Ident("pkg"))) => (),
+ Tok::DotPost(c) if matches!(c.tok, Tok::Ident("pkg")) => (),
_ => panic!("Lexd source {src:?} didn't match the expected pattern. Got {t:?}"),
}
}
@@ 519,8 505,8 @@ mod test {
let t = sc.maybe_term().unwrap().unwrap().tok;
let matches = match t.clone() {
Tok::DotIn(a, b) => match (a.tok, b.tok) {
- (Tok::Ident(Ident("str")), Tok::DotIn(b, c)) => match (b.tok, c.tok) {
- (Tok::Ident(Ident("std")), Tok::DotPost(c)) => matches!(c.tok, Tok::Ident(Ident("dep"))),
+ (Tok::Ident("str"), Tok::DotIn(b, c)) => match (b.tok, c.tok) {
+ (Tok::Ident("std"), Tok::DotPost(c)) => matches!(c.tok, Tok::Ident("dep")),
_ => false,
},
_ => false,
@@ 537,7 523,7 @@ mod test {
let mut sc = Scanner::new(None, "new.String");
let t = sc.maybe_term().unwrap().unwrap().tok;
assert!(match t {
- Tok::DotIn(a, b) => matches!((a.tok, b.tok), (Tok::Ident(Ident("new")), Tok::Ident(Ident("String")))),
+ Tok::DotIn(a, b) => matches!((a.tok, b.tok), (Tok::Ident("new"), Tok::Ident("String"))),
_ => false,
})
}
M src/main.rs => src/main.rs +15 -14
@@ 1,10 1,9 @@
//! The Effem Programming Language
#![feature(entry_insert, is_ascii_octdigit, trait_alias, assert_matches)]
+#![allow(unused_imports)]
mod abase;
-mod ast;
-mod base;
mod cache;
mod desugar;
mod diag;
@@ 14,14 13,12 @@ mod lex;
mod name;
mod parse;
mod resolve;
-mod val;
use cache::Cache;
-use eval::run;
+use name::*;
+use std::borrow::Cow;
use std::path::Path;
-use name::{FullQualName, PkgRef, QualModuleName};
-
fn main() {
let main_path = std::env::args()
.nth(1)
@@ 30,22 27,26 @@ fn main() {
println!("Running {main_path}");
let (dir, module_name) = name::parse_source_file_path(Path::new(&main_path)).unwrap();
println!("d: {dir:?}, m: {module_name:?}");
- let main_qname = FullQualName::new("main", QualModuleName::new(PkgRef::Our, [module_name]));
- println!("main qname: {main_qname}");
let mut cache = Cache::for_package_in(dir);
- println!("result: {:?}", run(&mut cache, &main_qname));
+ let id = cache
+ .fetch_global_resolved_name(&FullName(vec!["pkg".to_string(), module_name.to_string(), "main".to_string()]))
+ .unwrap();
+ println!("result: {:?}", cache.fetch_evaluated(id));
}
#[cfg(test)]
mod test {
use super::*;
- use val::*;
+ use eval::*;
fn run_tmp(main_src: &str) -> anyhow::Result<Val> {
let dir = tempfile::tempdir().unwrap();
std::fs::write(dir.path().join("main.fm"), main_src).unwrap();
- let main_name = FullQualName::new("main", QualModuleName::new(PkgRef::Our, ["main".to_string()]));
- run(&mut Cache::for_package_in(dir.path()), &main_name).cloned()
+ let mut cache = Cache::for_package_in(dir.path());
+ let main_id = cache
+ .fetch_global_resolved_name(&FullName(vec!["pkg".to_string(), "main".to_string(), "main".to_string()]))
+ .unwrap();
+ cache.fetch_evaluated(main_id).cloned()
}
#[test]
@@ 55,11 56,11 @@ mod test {
#[test]
fn test_main_arithm1() {
- assert_eq!(run_tmp("(def main (+ 1.0 2.0))").unwrap(), Val::F64(3.0))
+ assert_eq!(run_tmp("(def main (+.prim. 1.0 2.0))").unwrap(), Val::F64(3.0))
}
#[test]
fn test_main_arithm2() {
- assert_eq!(run_tmp("(def main (+ (* 13.0 100.0) 37.0))").unwrap(), Val::F64(1337.0))
+ assert_eq!(run_tmp("(def main (+.prim. (*.prim. 13.0 100.0) 37.0))").unwrap(), Val::F64(1337.0))
}
}
M src/name.rs => src/name.rs +56 -70
@@ 1,7 1,9 @@
use anyhow::{anyhow, bail};
+use std::borrow::Cow;
+use std::hash::Hash;
use std::path::Path;
-pub fn parse_source_file_path(p: &Path) -> anyhow::Result<(&Path, ModuleName)> {
+pub fn parse_source_file_path(p: &Path) -> anyhow::Result<(&Path, &str)> {
match p.extension() {
Some(os) if os == "fm" => (),
ext => bail!("expected extension .fm, found {ext:?}"),
@@ 9,104 11,88 @@ pub fn parse_source_file_path(p: &Path) -> anyhow::Result<(&Path, ModuleName)> {
let d = p.parent().unwrap_or(Path::new(""));
if let Some(s) = p.file_stem() {
if let Some(s) = s.to_str() {
- return Ok((d, s.to_owned()));
+ return Ok((d, s));
}
}
Err(anyhow!("invalid module path {p:?}"))
}
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct FullQualName {
- pub module: QualModuleName,
- pub name: ItemName,
+#[derive(Debug, Clone)]
+pub struct ParsedName {
+ // TODO: This bool ends up wasting a whole word. Weave it into `segments` somehow & measure
+ pub rooted: bool,
+ pub segments: Vec<PrivIdent>,
}
-impl FullQualName {
- pub fn new(name: impl Into<ItemName>, module: impl Into<QualModuleName>) -> Self {
- Self { name: name.into(), module: module.into() }
+impl ParsedName {
+ pub fn unqualified(x: PrivIdent) -> Self {
+ Self { rooted: false, segments: vec![x] }
}
}
-impl std::fmt::Display for FullQualName {
- fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
- write!(f, "{}.{}", self.name, self.module)
- }
+#[derive(Debug, Clone, PartialEq)]
+pub struct ResName {
+ pub res: Res,
+ // pos
}
-// TODO: remove? use FullQualName instead?
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct QualModuleName {
- pub root: PkgRef,
- pub segments: Vec<ModuleName>,
+#[derive(Debug, Clone, PartialEq)]
+pub enum Res {
+ Def(DefId),
+ Prim(Prim),
}
-impl QualModuleName {
- pub fn new(r: impl Into<PkgRef>, ss: impl Into<Vec<ModuleName>>) -> Self {
- Self { root: r.into(), segments: ss.into() }
- }
-}
+pub type DefId = u32;
-impl std::fmt::Display for QualModuleName {
- fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
- write!(f, "{}", self.root)?;
- for m in self.segments.iter().rev() {
- write!(f, "{m}.")?;
- }
- Ok(())
- }
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub enum Prim {
+ Add = 0,
+ Mul = 1,
}
-// TODO: remove
-#[derive(Debug, PartialEq)]
-pub struct ParsedName {
- pub root: Option<PkgRef>,
- pub prefix: Vec<ModuleName>,
- pub name: Name,
+/// E.g. ["dep", "std", "str", "Str", "new"] represents the path "new.Str.str.std.dep."
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+pub struct FullName(pub Vec<String>);
+// The span of an identifier in the original source code string where the ident occured. Bookkeeping of source string
+// must be handled manually.
+//
+// Wrt the naming, it's "private" like private vs. public IP addresses: a private IP (e.g. 192.168.0.1) is not globally
+// routable, and the same private IP can show up in different networks, referring to different hosts depending on the
+// network.
+#[derive(Debug, Clone)]
+pub struct PrivIdent {
+ pub start: u32,
+ pub len: u32, // TODO: Measure if leaving the length out and instead relying on re-tokenizing the ident is better.
}
-impl ParsedName {
- pub fn unqualified(s: impl Into<String>) -> Self {
- ParsedName { root: None, prefix: vec![], name: s.into() }
+impl PrivIdent {
+ pub fn in_source<'s>(&self, src: &'s str) -> &'s str {
+ let i = self.start as usize;
+ &src[i..i + self.len as usize]
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub enum PkgRef {
- Our,
- // Dep(PkgName),
- Builtin,
-}
+pub struct PubIdent(pub String);
-impl std::fmt::Display for PkgRef {
- fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
- match self {
- PkgRef::Our => write!(f, "our."),
- // PkgRef::Dep(p) => write!(f, "/pkg/{}", p),
- PkgRef::Builtin => write!(f, "builtin."),
- }
+impl std::borrow::Borrow<str> for PubIdent {
+ fn borrow(&self) -> &str {
+ &self.0
}
}
-// pub type PkgName = Name;
-pub type ModuleName = Name;
-pub type ItemName = Name;
-pub type Name = String;
-
#[cfg(test)]
mod test {
use super::*;
- #[test]
- fn test_parse_source_file_path() {
- assert_eq!(parse_source_file_path(Path::new("/foo.fm")).unwrap(), (Path::new("/"), "foo".to_string()));
- assert_eq!(
- parse_source_file_path(Path::new("proj/My Game/main.fm")).unwrap(),
- (Path::new("proj/My Game"), "main".to_string())
- );
- assert_eq!(
- parse_source_file_path(Path::new("bingo.new.fm")).unwrap(),
- (Path::new(""), "bingo.new".to_string())
- );
- assert!(parse_source_file_path(Path::new("bad")).is_err())
- }
+ // #[test]
+ // fn test_parse_source_file_path() {
+ // assert_eq!(parse_source_file_path(Path::new("/foo.fm")).unwrap(), (Path::new("/"), SIdent("foo")));
+ // assert_eq!(
+ // parse_source_file_path(Path::new("proj/My Game/main.fm")).unwrap(),
+ // (Path::new("proj/My Game"), SIdent("main"))
+ // );
+ // assert_eq!(parse_source_file_path(Path::new("bingo.new.fm")).unwrap(), (Path::new(""), SIdent("bingo.new")));
+ // assert!(parse_source_file_path(Path::new("bad")).is_err())
+ // }
}
M src/parse.rs => src/parse.rs +110 -60
@@ 1,13 1,40 @@
-use crate::{ast::*, diag::*, lex::*, name::*};
-use std::collections::HashSet;
+use crate::{diag::*, lex::*, name::*};
+use std::collections::{HashMap, HashSet};
use std::iter::once;
+use std::path::Path;
+use std::rc::Rc;
-pub fn parse(file: Option<PathRef>, tokens: &[Token]) -> anyhow::Result<PModule> {
+pub struct Module {
+ pub defs: HashMap<PubIdent, Expr>,
+}
+
+// TODO: Parse directly to indexed flat tree.
+// Resolve doesn't create new string-based names, but simply associates the stringy representation of the name with an ID.
+// Globally unique IDs for top level function / constant definitions.
+// Other ID for local vars & function params.
+// See https://doc.rust-lang.org/stable/nightly-rustc/rustc_hir/def/enum.Res.html
+//
+// Parse modules in parallel.
+// Global IDs generated from a shared atomic integer.
+// Definitions stored in a map per module, sparse keys.
+//
+// Locals (expr nodes?) are stored in a contiguous array.
+// IDs are not globally unique. Instead, per top level item.
+// No syncing between threads necessary.
+// Different modules can't access eachothers locals.
+#[derive(Debug, Clone)]
+pub enum Expr {
+ F64(f64),
+ App(Box<Expr>, Vec<Expr>),
+ Var(ParsedName),
+}
+
+pub fn parse<'s>(file: Option<Rc<Path>>, src: &'s str, tokens: &[Token<'s>]) -> anyhow::Result<Module> {
let inp = Inp { tokens, dist: 0, context: file.map(|file| Loc::FileGeneral { file }).unwrap_or(Loc::AnonGeneral) };
let go = move || {
let (inp, defs) = many0(def)(inp)?;
end(inp)?;
- Ok(Module { defs: defs.into_iter().collect() })
+ Ok(Module { defs: defs.into_iter().map(|(lhs, rhs)| (PubIdent(lhs.in_source(src).to_owned()), rhs)).collect() })
};
go().map_err(|Err { loc, expecteds, dist: _ }| {
loc.err(if expecteds.len() == 1 {
@@ 45,13 72,16 @@ impl<'s, 't> Inp<'s, 't> {
fn filter_next<A, S: std::fmt::Display>(
&self,
- extract: impl FnOnce(&'t Tok<'s>) -> Result<A, S>,
+ extract: impl FnOnce(&'t Token<'s>) -> Result<A, S>,
) -> Res<'s, 't, A> {
let (inp, t) = self.next()?;
- match extract(&t.tok) {
+ match extract(t) {
Ok(x) => Ok((inp, x)),
- Err(expected) =>
- Err(Err { loc: t.loc.clone(), dist: self.dist, expecteds: once(expected.to_string()).collect() }),
+ Err(expected) => Err(Err {
+ loc: self.context.with_offset(t.offset),
+ dist: self.dist,
+ expecteds: once(expected.to_string()).collect(),
+ }),
}
}
@@ 63,13 93,16 @@ impl<'s, 't> Inp<'s, 't> {
let (rem, t) = self.next()?;
match extract(&t.tok) {
Ok(ts) => {
- let inp = Inp { tokens: ts, dist: rem.dist, context: t.loc.clone() };
+ let inp = Inp { tokens: ts, dist: rem.dist, context: self.context.with_offset(t.offset) };
let (inp, x) = f(inp)?;
let inp = Inp { tokens: rem.tokens, dist: inp.dist, context: rem.context };
Ok((inp, x))
}
- Err(expected) =>
- Err(Err { loc: t.loc.clone(), dist: self.dist, expecteds: once(expected.to_string()).collect() }),
+ Err(expected) => Err(Err {
+ loc: self.context.with_offset(t.offset),
+ dist: self.dist,
+ expecteds: once(expected.to_string()).collect(),
+ }),
}
}
}
@@ 77,17 110,17 @@ impl<'s, 't> Inp<'s, 't> {
type Res<'s, 't, A> = Result<(Inp<'s, 't>, A), Err>;
trait Parser<'s, 't, A> = FnMut(Inp<'s, 't>) -> Res<'s, 't, A> where 's: 't;
-fn def<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, (ItemName, PExpr)> {
+fn def<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, (PrivIdent, Expr)> {
parens(|inp| {
let (inp, _) = special_form("def")(inp)?;
let (inp, lhs) = ident(inp)?;
let (inp, rhs) = expr(inp)?;
let (inp, ()) = end(inp)?;
- Ok((inp, (lhs.to_str().to_owned(), rhs)))
+ Ok((inp, (lhs, rhs)))
})(inp)
}
-fn expr<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, PExpr> {
+fn expr<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, Expr> {
// TODO
alt3(
map(lit_float, Expr::F64),
@@ 96,31 129,48 @@ fn expr<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, PExpr> {
)(inp)
}
-fn special_form<'s: 't, 't>(id: &'static str) -> impl Parser<'s, 't, &'t Ident<'s>> {
+fn special_form<'s: 't, 't>(id: &'static str) -> impl Parser<'s, 't, PrivIdent> {
move |inp| {
- inp.filter_next(|tok| match tok {
- Tok::Ident(x) if x.to_str() == id => Ok(x),
+ inp.filter_next(|t| match t.tok {
+ Tok::Ident(x) if x == id => Ok(PrivIdent { start: t.offset, len: x.len() as u32 }),
_ => Err(id),
})
}
}
fn name<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, ParsedName> {
- inp.filter_next(|tok| match tok {
- Tok::Ident(x) => Ok(ParsedName::unqualified(x.to_str())),
+ inp.filter_next(name_)
+}
+
+fn name_(t: &Token) -> Result<ParsedName, &'static str> {
+ match t.tok {
+ Tok::Ident(x) => Ok(ParsedName::unqualified(PrivIdent { start: t.offset, len: x.len() as u32 })),
+ Tok::DotIn(ref first, ref rest) => match first.tok {
+ Tok::Ident(x) => {
+ let mut n = name_(rest)?;
+ n.segments.push(PrivIdent { start: t.offset, len: x.len() as u32 });
+ Ok(n)
+ }
+ _ => Err("*ident*"),
+ },
+ Tok::DotPost(ref last) => match last.tok {
+ Tok::Ident(x) =>
+ Ok(ParsedName { rooted: true, segments: vec![PrivIdent { start: t.offset, len: x.len() as u32 }] }),
+ _ => Err("*ident*"),
+ },
_ => Err("*name*"),
- })
+ }
}
-fn ident<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, &'t Ident<'s>> {
- inp.filter_next(|tok| match tok {
- Tok::Ident(x) => Ok(x),
+fn ident<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, PrivIdent> {
+ inp.filter_next(|t| match t.tok {
+ Tok::Ident(x) => Ok(PrivIdent { start: t.offset, len: x.len() as u32 }),
_ => Err("*identifier*"),
})
}
fn lit_float<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, f64> {
- inp.filter_next(|tok| match *tok {
+ inp.filter_next(|t| match t.tok {
Tok::Float(x) => Ok(x),
_ => Err("*float literal*"),
})
@@ 152,7 202,7 @@ fn parens<'s: 't, 't, A>(mut f: impl Parser<'s, 't, A>) -> impl Parser<'s, 't, A
fn end<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, ()> {
if let Some(t) = inp.tokens.first() {
Err(Err {
- loc: t.loc.clone(),
+ loc: inp.context.with_offset(t.offset),
dist: inp.dist,
expecteds: once(format!("end of token sequence ({:?})", inp)).collect(),
})
@@ 161,29 211,29 @@ fn end<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, ()> {
}
}
-fn alt<'s: 't, 't, 'f, const N: usize, A>(
- fs: &'f mut [&'f mut dyn Parser<'s, 't, A>; N],
-) -> impl Parser<'s, 't, A> + 'f {
- use std::cmp::Ordering::*;
- |inp| {
- let mut e0 = None::<Err>;
- for f in fs.iter_mut() {
- match f(inp.clone()) {
- Ok(x) => return Ok(x),
- Err(e) =>
- e0 = Some(match e0 {
- Some(e0) => match e0.dist.cmp(&e.dist) {
- Greater => e0,
- Less => e,
- Equal => Err { expecteds: e0.expecteds.union(&e.expecteds).cloned().collect(), ..e },
- },
- None => e,
- }),
- }
- }
- Err(e0.unwrap())
- }
-}
+// fn alt<'s: 't, 't, 'f, const N: usize, A>(
+// fs: &'f mut [&'f mut dyn Parser<'s, 't, A>; N],
+// ) -> impl Parser<'s, 't, A> + 'f {
+// use std::cmp::Ordering::*;
+// |inp| {
+// let mut e0 = None::<Err>;
+// for f in fs.iter_mut() {
+// match f(inp.clone()) {
+// Ok(x) => return Ok(x),
+// Err(e) =>
+// e0 = Some(match e0 {
+// Some(e0) => match e0.dist.cmp(&e.dist) {
+// Greater => e0,
+// Less => e,
+// Equal => Err { expecteds: e0.expecteds.union(&e.expecteds).cloned().collect(), ..e },
+// },
+// None => e,
+// }),
+// }
+// }
+// Err(e0.unwrap())
+// }
+// }
fn alt2<'s: 't, 't, A>(mut f: impl Parser<'s, 't, A>, mut g: impl Parser<'s, 't, A>) -> impl Parser<'s, 't, A> {
move |inp| {
@@ 206,14 256,14 @@ fn alt3<'s: 't, 't, A>(
alt2(f, alt2(g, h))
}
-fn alt4<'s: 't, 't, A>(
- f: impl Parser<'s, 't, A>,
- g: impl Parser<'s, 't, A>,
- h: impl Parser<'s, 't, A>,
- i: impl Parser<'s, 't, A>,
-) -> impl Parser<'s, 't, A> {
- alt2(alt2(f, g), alt2(h, i))
-}
+// fn alt4<'s: 't, 't, A>(
+// f: impl Parser<'s, 't, A>,
+// g: impl Parser<'s, 't, A>,
+// h: impl Parser<'s, 't, A>,
+// i: impl Parser<'s, 't, A>,
+// ) -> impl Parser<'s, 't, A> {
+// alt2(alt2(f, g), alt2(h, i))
+// }
fn many0<'s: 't, 't, A>(mut f: impl Parser<'s, 't, A>) -> impl Parser<'s, 't, Vec<A>> {
move |mut inp| {
@@ 262,12 312,12 @@ mod test {
#[test]
fn test_arithm() {
- let m = parse(None, &lex(None, "(def x (+ 1.0 2.0))").unwrap()).unwrap();
+ let src = "(def x (+ 1.0 2.0))";
+ let m = parse(None, src, &lex(None, src).unwrap()).unwrap();
assert_matches!(
&m.defs["x"],
- Expr::App(f, xs) if **f == Expr::Var(ParsedName::unqualified("+"))
- && xs[0] == Expr::F64(1.0)
- && xs[1] == Expr::F64(2.0)
+ Expr::App(f, xs) if matches!(&**f, Expr::Var(name) if name.segments.len() == 1 && matches!(name.segments[0], PrivIdent { start: 8, len: 1 }))
+ && matches!(&xs[..], [Expr::F64(x1), Expr::F64(x2)] if *x1 == 1.0 && *x2 == 2.0)
)
}
}
M src/resolve.rs => src/resolve.rs +74 -27
@@ 1,31 1,78 @@
-use crate::ast::*;
-use crate::name::*;
+use crate::{cache::*, name::*, parse};
+use anyhow::Result;
+use std::borrow::Cow;
-pub fn resolve(module: PModule) -> anyhow::Result<RModule> {
- module
- .defs
- .into_iter()
- .map(|(lhs, rhs)| Ok((lhs, resolve_expr(rhs)?)))
- .collect::<Result<_, _>>()
- .map(|defs| RModule { defs })
+// https://rustc-dev-guide.rust-lang.org/name-resolution.html
+
+// ett item vet sitt fulla namn
+// vi resolvar alltid till item-referenser, inte namn. tänk på glob imports
+// => resolve dekorerar givet namn med ett item id
+
+#[derive(Debug, Clone, PartialEq)]
+pub enum Expr {
+ F64(f64),
+ App(Box<Expr>, Vec<Expr>),
+ Var(ResName),
+}
+
+pub fn resolve_def(cache: &mut Cache, def_name: &FullName, def_body: &parse::Expr) -> Result<Expr> {
+ let mut def_name = def_name.clone();
+ let _def_ident = def_name.0.pop();
+ let module_name = def_name;
+ let mut resolver = Resolver { cache, module: module_name };
+ resolver.resolve(def_body)
+}
+
+pub fn resolve_parsed_name(cache: &mut Cache, occuring_in_module: &FullName, pname: &ParsedName) -> Result<Res> {
+ assert!(!pname.segments.is_empty());
+ let (_, src) = cache.fetch_source(occuring_in_module)?;
+ let name = pname.segments.iter().map(|seg| seg.in_source(src).to_owned()).collect::<Vec<_>>();
+ if pname.rooted {
+ if name[0] == "prim" {
+ if name.len() != 2 {
+ todo!() // Err
+ }
+ match name[1].as_str() {
+ "+" => Ok(Res::Prim(Prim::Add)),
+ "*" => Ok(Res::Prim(Prim::Mul)),
+ _ => todo!(), // Err
+ }
+ } else {
+ cache.fetch_global_resolved_name(&FullName(name)).map(Res::Def)
+ }
+ } else {
+ match name.split_first() {
+ Some((first, [])) => cache
+ .fetch_module_local_resolved_names(occuring_in_module)?
+ .get(&PubIdent(first.clone()))
+ .cloned()
+ .ok_or_else(|| todo!()),
+ Some((_first, _rest)) => todo!(),
+ None => todo!(), // Err
+ }
+ }
+}
+
+struct Resolver<'c> {
+ cache: &'c mut Cache,
+ module: FullName,
}
-pub fn resolve_expr(expr: PExpr) -> anyhow::Result<RExpr> {
- use Expr::*;
- Ok(match expr {
- F64(x) => F64(x),
- Var(x) => Var(match x {
- // FIXME
- ParsedName { root: None, prefix, name } => FullQualName {
- module: QualModuleName { root: PkgRef::Builtin, segments: prefix.clone() },
- name: name.clone(),
- },
- ParsedName { root: Some(root), prefix, name } => FullQualName {
- module: QualModuleName { root: root.clone(), segments: prefix.clone() },
- name: name.clone(),
- },
- }),
- App(f, xs) =>
- App(Box::new(resolve_expr(*f)?), xs.into_iter().map(|x| resolve_expr(x)).collect::<anyhow::Result<_>>()?),
- })
+impl<'c> Resolver<'c> {
+ fn resolve(&mut self, e: &parse::Expr) -> Result<Expr> {
+ use parse::Expr::*;
+ Ok(match *e {
+ F64(x) => Expr::F64(x),
+ App(ref f, ref args) =>
+ Expr::App(Box::new(self.resolve(f)?), args.iter().map(|a| self.resolve(a)).collect::<Result<_>>()?),
+ Var(ref v) => {
+ // To begin with, our lang is very primitive, but there is still some symbol scoping to consider.
+ // If main.fm defines foo and foo', and misc.fm defines bar, then
+ // - `foo` can refer to `foo'` as just `foo'`, but must refer to `bar` by the full `bar.misc.pkg.`
+ // - `bar` must refer to `foo` by the full `foo.main.pkg.`
+ // So `resolve` must be aware of the module it's in from the start
+ Expr::Var(ResName { res: resolve_parsed_name(self.cache, &self.module, v)? })
+ }
+ })
+ }
}
D src/val.rs => src/val.rs +0 -4
@@ 1,4 0,0 @@
-#[derive(Debug, Clone, PartialEq)]
-pub enum Val {
- F64(f64),
-}