M src/abase.rs => src/abase.rs +9 -6
@@ 3,6 3,7 @@
use crate::cache::Cache;
use crate::fem::Expr as FExpr;
use crate::name::*;
+use anyhow::anyhow;
#[derive(Clone)]
pub enum Expr {
@@ 12,15 13,17 @@ pub enum Expr {
}
pub fn abase(cache: &mut Cache, expr: &FExpr) -> anyhow::Result<Expr> {
- Ok(match *expr {
- FExpr::F64(x) => Expr::F64(x),
- FExpr::App(ref f, ref xs) => match **f {
+ match *expr {
+ FExpr::F64(x) => Ok(Expr::F64(x)),
+ FExpr::App(ref f, ref xs) => Ok(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!(),
- },
- FExpr::Var(_) => todo!(),
- })
+ }),
+ FExpr::Var(ResName { res: Res::Def(id) }) => cache.fetch_base(id).cloned(),
+ FExpr::Var(ResName { res: Res::Prim(prim) }) =>
+ Err(anyhow!("error: can't abase primitive `{prim}` in isolation")),
+ }
}
M src/cache.rs => src/cache.rs +56 -20
@@ 4,7 4,7 @@ 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::collections::hash_map::{Entry, HashMap};
use std::path::{Path, PathBuf};
use std::rc::Rc;
@@ 14,6 14,7 @@ pub struct Cache {
parsed_modules: HashMap<FullName, PModule>,
resolved_names: HashMap<FullName, DefId>,
resolved_names_rev: HashMap<DefId, FullName>,
+ module_local_resolved_names: HashMap<FullName, HashMap<PubIdent, Res>>,
resolveds: HashMap<DefId, RExpr>,
// desugareds: HashMap<Query, fem::Expr>,
abaseds: HashMap<DefId, abase::Expr>,
@@ 28,24 29,27 @@ impl Cache {
parsed_modules: HashMap::new(),
resolved_names: HashMap::new(),
resolved_names_rev: HashMap::new(),
+ module_local_resolved_names: HashMap::new(),
resolveds: HashMap::new(),
abaseds: HashMap::new(),
evaluateds: HashMap::new(),
}
}
- 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_base(def_query)?.clone();
- let val = eval::eval(self, &base)?;
- self.evaluateds.insert(def_query, val);
- Ok(&self.evaluateds[&def_query])
+ pub fn fetch_evaluated(&mut self, query: Res) -> anyhow::Result<&eval::Val> {
+ match query {
+ Res::Prim(_prim) => todo!(),
+ Res::Def(id) if self.evaluateds.contains_key(&id) => Ok(self.evaluateds.get(&id).unwrap()),
+ Res::Def(id) => {
+ let base = self.fetch_base(id)?.clone();
+ let val = eval::eval(self, &base)?;
+ self.evaluateds.insert(id, val);
+ Ok(&self.evaluateds[&id])
+ }
}
}
- pub fn fetch_base<'c>(&'c mut self, def_query: DefId) -> anyhow::Result<&'c abase::Expr> {
+ pub fn fetch_base(&mut self, def_query: DefId) -> anyhow::Result<&abase::Expr> {
Ok(if self.abaseds.contains_key(&def_query) {
&self.abaseds[&def_query]
} else {
@@ 98,13 102,45 @@ impl Cache {
Ok((name, parsed))
}
- pub fn fetch_module_local_resolved_names(&mut self, _module: &FullName) -> anyhow::Result<&HashMap<PubIdent, Res>> {
- todo!()
+ pub fn fetch_module_local_resolved_names(
+ &mut self,
+ module_name: &FullName,
+ ) -> anyhow::Result<&HashMap<PubIdent, Res>> {
+ static PRELUDE: &[(&str, &[&str])] = &[("+", &["prim", "+"]), ("*", &["prim", "*"])];
+ fn prelude_owned() -> impl Iterator<Item = (PubIdent, FullName)> {
+ PRELUDE.iter().map(|(ident, name)| {
+ (PubIdent(ident.to_string()), FullName(name.iter().map(|x| x.to_string()).collect()))
+ })
+ }
+
+ if self.module_local_resolved_names.contains_key(module_name) {
+ Ok(&self.module_local_resolved_names[module_name])
+ } else {
+ let def_names: Vec<PubIdent> = self.fetch_parsed_module(module_name)?.defs.keys().cloned().collect();
+ let ids = def_names
+ .into_iter()
+ .map(|k| (k.clone(), module_name.with(k)))
+ .chain(prelude_owned())
+ .map(|(def_ident, name)| self.fetch_global_resolved_name(&name).map(|id| (def_ident, id)))
+ .collect::<Result<_, _>>()?;
+ self.module_local_resolved_names.insert(module_name.clone(), ids);
+ Ok(&self.module_local_resolved_names[module_name])
+ }
}
- pub fn fetch_global_resolved_name(&mut self, name: &FullName) -> anyhow::Result<DefId> {
- if let Some(&res) = self.resolved_names.get(name) {
- Ok(res)
+ pub fn fetch_global_resolved_name(&mut self, name: &FullName) -> anyhow::Result<Res> {
+ assert!(name.0.len() > 1);
+ if name.0[0] == "prim" {
+ if name.0.len() != 2 {
+ todo!() // Err
+ }
+ match name.0[1].as_str() {
+ "+" => Ok(Res::Prim(Prim::Add)),
+ "*" => Ok(Res::Prim(Prim::Mul)),
+ _ => todo!(), // Err
+ }
+ } else if let Some(&res) = self.resolved_names.get(name) {
+ Ok(Res::Def(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()))?;
@@ 113,7 149,7 @@ impl Cache {
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)
+ Ok(Res::Def(id))
} else {
Err(anyhow!("item {def_ident:?} is not defined in module {module_name:?}"))
}
@@ 122,22 158,22 @@ impl Cache {
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)?;
+ 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];
+ let (path, src) = &self.sources[module_name];
Ok((path, src))
} else {
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];
+ let (path, src) = &self.sources[module_name];
Ok((path, src))
}
}
M src/main.rs => src/main.rs +10 -0
@@ 2,6 2,7 @@
#![feature(entry_insert, is_ascii_octdigit, trait_alias, assert_matches)]
#![allow(unused_imports)]
+#![allow(clippy::map_entry)]
mod abase;
mod cache;
@@ 63,4 64,13 @@ mod test {
fn test_main_arithm2() {
assert_eq!(run_tmp("(def main (+.prim. (*.prim. 13.0 100.0) 37.0))").unwrap(), Val::F64(1337.0))
}
+
+ #[test]
+ fn test_main_arithm_var() {
+ let src = "
+ (def x 1300.0)
+ (def main (+ x 37.0))
+ ";
+ assert_eq!(run_tmp(src).unwrap(), Val::F64(1337.0))
+ }
}
M src/name.rs => src/name.rs +31 -0
@@ 50,9 50,40 @@ pub enum Prim {
Mul = 1,
}
+impl std::fmt::Display for Prim {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(
+ f,
+ "{}",
+ match self {
+ Prim::Add => "+",
+ Prim::Mul => "*",
+ }
+ )
+ }
+}
+
/// 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>);
+
+impl FullName {
+ pub fn with(&self, element: PubIdent) -> Self {
+ let mut name = self.clone();
+ name.0.push(element.0);
+ name
+ }
+}
+
+impl std::fmt::Display for FullName {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ for seg in self.0.iter().rev() {
+ write!(f, "{}.", seg)?
+ }
+ Ok(())
+ }
+}
+
// The span of an identifier in the original source code string where the ident occured. Bookkeeping of source string
// must be handled manually.
//
M src/resolve.rs => src/resolve.rs +5 -14
@@ 1,5 1,5 @@
use crate::{cache::*, name::*, parse};
-use anyhow::Result;
+use anyhow::{anyhow, Result};
use std::borrow::Cow;
// https://rustc-dev-guide.rust-lang.org/name-resolution.html
@@ 28,25 28,16 @@ pub fn resolve_parsed_name(cache: &mut Cache, occuring_in_module: &FullName, pna
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)
- }
+ cache.fetch_global_resolved_name(&FullName(name))
} 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!()),
+ .ok_or_else(|| {
+ anyhow!("error: couldn' find definition `{}` in module `{}`", first, occuring_in_module)
+ }),
Some((_first, _rest)) => todo!(),
None => todo!(), // Err
}