~jojo/effem

fe0b7675acb56029747626dc084ba67d89789d1a — JoJo 3 months ago ff5b604
pretty errors

Had some consequences in Loc:s being added to ASTs.
Also, caching of modules & files was affected a bit.
M Cargo.lock => Cargo.lock +0 -7
@@ 3,12 3,6 @@
version = 3

[[package]]
name = "anyhow"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"

[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 30,7 24,6 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
name = "effem"
version = "0.1.0"
dependencies = [
 "anyhow",
 "tempfile",
]


M Cargo.toml => Cargo.toml +0 -3
@@ 5,8 5,5 @@ edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
anyhow = "1"

[dev-dependencies]
tempfile = "3.5"
\ No newline at end of file

M src/abase.rs => src/abase.rs +5 -5
@@ 1,9 1,7 @@
//! Base -- the lowest level IR in Effem before generating the output machine code.

use crate::cache::Cache;
use crate::fem::Expr as FExpr;
use crate::name::*;
use anyhow::{anyhow, Result};
use crate::prelude::*;
use fem::Expr as FExpr;

#[derive(Clone, Debug)]
pub enum GlobDef {


@@ 141,7 139,9 @@ impl<'c> AbaseDef<'c> {
            }
            Var(ResName { res: Res::Local(id) }) =>
                Ok(Expr::Operand(Operand::Local(*self.vars.get(*id as usize).unwrap_or_else(|| todo!())))),
            Var(ResName { res: Res::Prim(prim) }) => Err(anyhow!("error: can't abase primitive `{prim}` in isolation")),
            Var(ResName { res: Res::Prim(_) }) => todo!(), // TODO: generate a closure around the op or smth
            Var(ResName { res: Res::Module(_) }) =>
                panic!("ice: found module id in expr context when abasing. Should've been caught by type checker."),
        }
    }


M src/cache.rs => src/cache.rs +140 -111
@@ 1,8 1,6 @@
use crate::name::*;
use crate::parse::{self, Expr as PExpr, Module as PModule};
use crate::resolve::{self, Expr as RExpr};
use crate::{abase, check, eval, fem, lex};
use anyhow::{anyhow, Context};
use crate::prelude::*;
use parse::{Expr as PExpr, Module as PModule};
use resolve::Expr as RExpr;
use std::borrow::Borrow;
use std::collections::hash_map::{Entry, HashMap};
use std::path::{Path, PathBuf};


@@ 10,13 8,17 @@ use std::rc::Rc;

pub struct Cache {
    root_dir: PathBuf,
    sources: HashMap<FullName, (PathBuf, String)>,
    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>>,
    n_defs: u32,
    n_modules: u32,
    sources: HashMap<FileId, String>,
    file_paths: HashMap<FileId, PathBuf>,
    module_files: HashMap<ModuleId, FileId>,
    parsed_modules: HashMap<ModuleId, PModule>,
    parent_modules: HashMap<DefId, ModuleId>,
    resolved_names: HashMap<FullName<String>, Res>,
    resolved_names_rev: HashMap<Res, FullName<String>>,
    module_local_resolved_names: HashMap<ModuleId, HashMap<String, Res>>,
    resolveds: HashMap<DefId, RExpr>,
    // desugareds: HashMap<Query, fem::Expr>,
    checkeds: HashMap<DefId, fem::Expr>,
    abaseds: HashMap<DefId, abase::GlobDef>,
    evaluateds: HashMap<DefId, abase::Operand>,


@@ 26,10 28,15 @@ impl Cache {
    pub fn for_package_in(root_dir: &Path) -> Self {
        Self {
            root_dir: root_dir.to_owned(),
            n_defs: 0,
            n_modules: 0,
            file_paths: HashMap::new(),
            sources: HashMap::new(),
            module_files: HashMap::new(),
            parsed_modules: HashMap::new(),
            resolved_names: HashMap::new(),
            resolved_names_rev: HashMap::new(),
            parent_modules: HashMap::new(),
            module_local_resolved_names: HashMap::new(),
            resolveds: HashMap::new(),
            checkeds: HashMap::new(),


@@ 38,11 45,7 @@ impl Cache {
        }
    }

    pub fn fetch_evaluated_at(
        &mut self,
        fundef_query: DefId,
        args: &[abase::Operand],
    ) -> anyhow::Result<abase::Operand> {
    pub fn fetch_evaluated_at(&mut self, fundef_query: DefId, args: &[abase::Operand]) -> Result<abase::Operand> {
        let _ = self.fetch_evaluated(fundef_query)?;
        match self.abaseds[&fundef_query] {
            abase::GlobDef::FunDef(ref fdef) => eval::eval_fun_at(self, &fdef.clone(), args),


@@ 50,7 53,7 @@ impl Cache {
        }
    }

    pub fn fetch_evaluated(&mut self, def_query: DefId) -> anyhow::Result<&abase::Operand> {
    pub fn fetch_evaluated(&mut self, def_query: DefId) -> Result<&abase::Operand> {
        if self.evaluateds.contains_key(&def_query) {
            Ok(self.evaluateds.get(&def_query).unwrap())
        } else {


@@ 65,7 68,7 @@ impl Cache {
        }
    }

    pub fn fetch_base(&mut self, def_query: DefId) -> anyhow::Result<&abase::GlobDef> {
    pub fn fetch_base(&mut self, def_query: DefId) -> Result<&abase::GlobDef> {
        Ok(if self.abaseds.contains_key(&def_query) {
            &self.abaseds[&def_query]
        } else {


@@ 76,7 79,7 @@ impl Cache {
        })
    }

    pub fn fetch_checked(&mut self, def_query: DefId) -> anyhow::Result<&fem::Expr> {
    pub fn fetch_checked(&mut self, def_query: DefId) -> Result<&fem::Expr> {
        if self.checkeds.contains_key(&def_query) {
            Ok(&self.checkeds[&def_query])
        } else {


@@ 87,7 90,7 @@ impl Cache {
        }
    }

    pub fn fetch_desugared(&mut self, def_query: DefId) -> anyhow::Result<&RExpr> {
    pub fn fetch_desugared(&mut self, def_query: DefId) -> Result<&RExpr> {
        self.fetch_resolved(def_query)
        // Ok(if self.desugareds.contains_key(def_query) {
        //     &self.desugareds[def_query]


@@ 99,122 102,148 @@ impl Cache {
        // })
    }

    pub fn fetch_resolved(&mut self, id: DefId) -> anyhow::Result<&RExpr> {
        if self.resolveds.contains_key(&id) {
            Ok(&self.resolveds[&id])
    pub fn fetch_resolved(&mut self, def_id: DefId) -> Result<&RExpr> {
        if self.resolveds.contains_key(&def_id) {
            Ok(&self.resolveds[&def_id])
        } else {
            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])
            let module_id =
                *self.parent_modules.get(&def_id).expect("ice: if there's a def id, there should be a parent module");
            let def_ident = self.fetch_def_name(def_id).last().clone();
            let module = self.fetch_parsed_module(module_id)?;
            let parsed = module.defs[&def_ident].clone();
            let resolved = resolve::resolve_def(self, module_id, &parsed)?;
            self.resolveds.insert(def_id, resolved);
            Ok(&self.resolveds[&def_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_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()))
            })
        }
    pub fn fetch_module_local_resolved_names(&mut self, module_id: ModuleId) -> Result<&HashMap<String, Res>> {
        static PRELUDE: &[(&str, Res)] = &[("+", Res::Prim(Prim::Add)), ("*", Res::Prim(Prim::Mul))];

        if self.module_local_resolved_names.contains_key(module_name) {
            Ok(&self.module_local_resolved_names[module_name])
        if self.module_local_resolved_names.contains_key(&module_id) {
            Ok(&self.module_local_resolved_names[&module_id])
        } else {
            let def_names: Vec<PubIdent> = self.fetch_parsed_module(module_name)?.defs.keys().cloned().collect();
            let ids = def_names
            let def_idents: Vec<String> = self.fetch_parsed_module(module_id)?.defs.keys().cloned().collect();
            let ids = def_idents
                .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])
                .map(|def_ident| (def_ident.clone(), Res::Def(self.gen_def_resolution(def_ident, module_id))))
                .chain(PRELUDE.iter().map(|(ident, res)| (ident.to_string(), *res)))
                .collect();
            self.module_local_resolved_names.insert(module_id, ids);
            Ok(&self.module_local_resolved_names[&module_id])
        }
    }

    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 {
    pub fn fetch_global_resolved_name(&mut self, name: &FullName<PubIdent>) -> Result<Res> {
        use ResolveErr::*;
        debug_assert!(name.len() >= 2);
        let sname = name.clone().strip_locs();
        if sname.get(0).unwrap() == "prim" {
            if name.len() != 2 {
                todo!() // Err
            }
            match name.0[1].as_str() {
            match sname.get(1).unwrap().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()))?;
            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(Res::Def(id))
            } else {
                Err(anyhow!("item {def_ident:?} is not defined in module {module_name:?}"))
        } else if let Some(&res) = self.resolved_names.get(&sname) {
            Ok(res)
        } else if sname.get(0).unwrap() == "pkg" {
            todo!()
        } else if sname.get(0).unwrap() == "own" {
            let path = own_module_name_to_path(&self.root_dir, sname.rest().unwrap().iter().map(|s| s.as_str()));
            if path.exists() {
                return Ok(Res::Module(self.gen_module_resolution(name.clone().strip_locs(), path)));
            }
            let (def_ident, module_name) = mapr(name.clone().split_last(), Option::unwrap);
            match self.fetch_global_resolved_name(&module_name)? {
                Res::Module(module_id) => {
                    let module = self.fetch_parsed_module(module_id)?;
                    if module.defs.contains_key(&def_ident.s) {
                        return Ok(Res::Def(self.gen_def_resolution(def_ident.s, module_id)));
                    }
                }
                _ => return Err(ParentIsNotModule(def_ident.loc, module_name).into()),
            }
            Err(Unresolved(name.clone()).into())
        } else {
            Err(InvalidRootNamespace(name.get(0).unwrap().clone()).into())
        }
    }

    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])
    fn gen_module_resolution(&mut self, module_name: FullName<String>, path: PathBuf) -> ModuleId {
        assert!(self.n_modules < u32::MAX);
        let mid = ModuleId(self.n_modules);
        self.n_modules += 1;
        assert!(self.file_paths.len() < u32::MAX as usize);
        let fid = FileId(self.file_paths.len() as u32);
        self.file_paths.insert(fid, path);
        self.module_files.insert(mid, fid);
        self.resolved_names.insert(module_name.clone(), Res::Module(mid));
        self.resolved_names_rev.insert(Res::Module(mid), module_name);
        mid
    }

    pub fn fetch_source(&mut self, module_name: &FullName) -> anyhow::Result<(&Path, &str)> {
        if self.sources.contains_key(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 (path, src) = &self.sources[module_name];
            Ok((path, src))
    fn gen_def_resolution(&mut self, def_ident: String, module_id: ModuleId) -> DefId {
        assert!(self.n_defs < u32::MAX);
        debug_assert!(self.fetch_parsed_module(module_id).unwrap().defs.contains_key(&def_ident));
        let def_name = self.fetch_module_name(module_id).clone().with(def_ident);
        let def_id = self.n_defs;
        let res = Res::Def(def_id);
        self.n_defs += 1;
        self.resolved_names.insert(def_name.clone(), res);
        self.resolved_names_rev.insert(res, def_name.clone());
        self.parent_modules.insert(def_id, module_id);
        def_id
    }

    pub fn fetch_def_name(&self, def_id: DefId) -> &FullName<String> {
        self.resolved_names_rev.get(&Res::Def(def_id)).expect("ice: if we have a def id, there should be a reverse")
    }

    pub fn fetch_module_name(&self, module_id: ModuleId) -> &FullName<String> {
        self.resolved_names_rev
            .get(&Res::Module(module_id))
            .expect("ice: if we have a module id, there should be a reverse")
    }

    pub fn fetch_parsed_module(&mut self, module_id: ModuleId) -> Result<&PModule> {
        if !self.parsed_modules.contains_key(&module_id) {
            // TODO: nested modules in the same file?
            let file_id = *self
                .module_files
                .get(&module_id)
                .expect("ice: there should be a corresponding file id for each module id");
            let src = self.fetch_source(file_id)?;
            let tokens = lex::lex(Some(file_id), src)?;
            let parsed = parse::parse(Some(file_id), src, &tokens)?;
            self.parsed_modules.insert(module_id, parsed);
        }
        Ok(&self.parsed_modules[&module_id])
    }

    pub fn fetch_source(&mut self, file_id: FileId) -> Result<&str> {
        if !self.sources.contains_key(&file_id) {
            let path = self.fetch_path(file_id);
            let src = std::fs::read_to_string(path).unwrap();
            self.sources.insert(file_id, src);
        }
        Ok(&self.sources[&file_id])
    }

    pub fn fetch_module_file(&self, module_id: ModuleId) -> FileId {
        *self.module_files.get(&module_id).expect("ice: we shouldn't have a module id without an associated file id")
    }

    pub fn fetch_path(&self, file_id: FileId) -> &Path {
        self.file_paths.get(&file_id).expect("ice: we shouldn't have a file id without an associated file path")
    }
}

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(segments);
fn own_module_name_to_path<'a, I: IntoIterator<Item = &'a str>>(root: &Path, mname: I) -> PathBuf {
    let mut path = root.to_path_buf();
    path.extend(mname);
    path.set_extension("fm");
    if path.exists() {
        Ok(path)
    } else {
        Err(anyhow!("Couldn't find module {mname:?}. Searched: {path:?}"))
    }
    path
}

M src/check.rs => src/check.rs +37 -29
@@ 1,8 1,7 @@
use crate::cache::Cache;
use crate::fem::*;
use crate::name::*;
use crate::resolve::Expr as RExpr;
use anyhow::{anyhow, Result};
use crate::prelude::*;
use diag::TypeErr::*;
use fem::*;
use resolve::Expr as RExpr;
use std::collections::HashMap;

pub fn check_def(cache: &mut Cache, body: &RExpr) -> Result<Expr> {


@@ 24,66 23,71 @@ impl<'c> CheckDef<'c> {
    }

    fn infer(&mut self, expr: &RExpr) -> Result<Expr> {
        match expr {
            RExpr::F64(x) => Ok(Expr { typ: Type::F64, kind: ExprKind::F64(*x) }),
            RExpr::Fun(ps, b) if ps.is_empty() => {
        use resolve::ExprKind as Rek;
        match &expr.kind {
            Rek::F64(x) => Ok(Expr { typ: Type::F64, kind: ExprKind::F64(*x) }),
            Rek::Fun(ps, b) if ps.is_empty() => {
                let bi = self.infer(b)?;
                Ok(Expr { typ: Type::Fun(vec![], Box::new(bi.typ.clone())), kind: ExprKind::Fun(vec![], Box::new(bi)) })
            }
            RExpr::Fun(_, _) => Err(anyhow!("Can't infer type of lambdas with >0 parameters (yet). Please surround the lambda in a type annotation to have the type checked instead of inferred.")),
            RExpr::App(f, args) => {
            Rek::Fun(_, _) => err(InferLambda(expr.loc)),
            Rek::App(f, args) => {
                let fi = self.infer(f)?;
                if let Type::Fun(ps, r) = &fi.typ {
                    if ps.len() == args.len() {
                        let argsc = ps.iter().zip(args).map(|(p, a)| self.check(a, p)).collect::<Result<Vec<_>>>()?;
                        Ok(Expr { typ: (**r).clone(), kind: ExprKind::App(Box::new(fi), argsc)})
                        Ok(Expr { typ: (**r).clone(), kind: ExprKind::App(Box::new(fi), argsc) })
                    } else {
                        Err(anyhow!("Arity mismatch in function application. Function specifies {} parameters, but {} arguments were given.", ps.len(), args.len()))
                        err(ArityMisApp { loc: expr.loc, params: ps.len(), args: args.len() })
                    }
                } else {
                    Err(anyhow!("Expected a function to apply, found a {:?}", fi.typ))
                    err(AppNonFun(expr.loc, fi.typ))
                }
            }
            RExpr::Var(r@ResName { res: Res::Def(id) }) => {
                let typ = self.cache.fetch_checked(*id).map(|Expr { typ, ..}| typ.clone())?;
                Ok(Expr { typ, kind: ExprKind::Var(r.clone())})
            Rek::Var(r @ ResName { res: Res::Def(id) }) => {
                let typ = self.cache.fetch_checked(*id).map(|Expr { typ, .. }| typ.clone())?;
                Ok(Expr { typ, kind: ExprKind::Var(r.clone()) })
            }
            RExpr::Var(r@ResName { res: Res::Local(id) }) => match self.get_local(*id) {
                Some(t) => Ok(Expr { typ: t.clone(), kind: ExprKind::Var(r.clone())}),
                None => panic!("ice: undefined local var of id {id}")
            Rek::Var(r @ ResName { res: Res::Local(id) }) => match self.get_local(*id) {
                Some(t) => Ok(Expr { typ: t.clone(), kind: ExprKind::Var(r.clone()) }),
                None => panic!("ice: undefined local var of id {id}"),
            },
            RExpr::Var(r@ResName { res: Res::Prim(Prim::Add | Prim::Mul) }) => Ok
                (Expr { typ: Type::Fun(vec![Type::F64, Type::F64], Box::new(Type::F64)), kind: ExprKind::Var(r.clone())}),
            RExpr::Annot(e, t) => self.check(e, t),
            Rek::Var(r @ ResName { res: Res::Prim(Prim::Add | Prim::Mul) }) => Ok(Expr {
                typ: Type::Fun(vec![Type::F64, Type::F64], Box::new(Type::F64)),
                kind: ExprKind::Var(r.clone()),
            }),
            Rek::Var(ResName { res: Res::Module(id) }) => err(ModuleIsNotExpr(expr.loc, *id)),
            Rek::Annot(e, t) => self.check(e, t),
        }
    }

    fn check(&mut self, expr: &RExpr, expected: &Type) -> Result<Expr> {
        match expr {
            RExpr::Fun(ps, b) =>
        use resolve::ExprKind as Rek;
        match &expr.kind {
            Rek::Fun(ps, b) =>
                if let Type::Fun(tps, tr) = expected {
                    if ps.len() != tps.len() {
                        return Err(anyhow!("Arity mismatch in function parameters. Expectectations from the outside are that there be {} parameters, but the function here instead has {}.", tps.len(), ps.len()));
                        return err(ArityMisLambda { loc: expr.loc, check: tps.len(), lit: ps.len() });
                    }
                    self.scopes.push(ps.iter().zip(tps).map(|((_, id), t)| (*id, t.clone())).collect());
                    let bc = self.check(b, tr)?;
                    self.scopes.pop();
                    Ok(Expr { typ: expected.clone(), kind: ExprKind::Fun(ps.clone(), Box::new(bc)) })
                } else {
                    Err(anyhow!("Expected {expected:?}, found a function"))
                    err(ExpectedFoundLambda(expr.loc, expected.clone()))
                },
            RExpr::Annot(e, t) =>
            Rek::Annot(e, t) =>
                if t == expected {
                    self.check(e, t)
                } else {
                    Err(anyhow!("Expected {expected:?}, found {t:?} (according to annotation)."))
                    err(ExpectedFoundAnnot(expr.loc, expected.clone(), t.clone()))
                },
            _ => {
                let expri = self.infer(expr)?;
                if &expri.typ == expected {
                    Ok(expri)
                } else {
                    Err(anyhow!("Expected {expected:?}, found {:?}", expri.typ))
                    err(ExpectedFound(expr.loc, expected.clone(), expri.typ))
                }
            }
        }


@@ 93,3 97,7 @@ impl<'c> CheckDef<'c> {
        self.scopes.iter().rev().find_map(|locals| locals.get(&id))
    }
}

fn err<A>(c: TypeErr) -> Result<A> {
    Err(Error::Type(c))
}

M src/desugar.rs => src/desugar.rs +2 -1
@@ 1,4 1,5 @@
use crate::fem::*;
use crate::prelude::*;
use fem::*;

// pub fn desugar(expr: &ast::RExpr) -> anyhow::Result<Expr> {
//     Ok(match expr {

M src/diag.rs => src/diag.rs +204 -43
@@ 1,17 1,181 @@
use crate::prelude::*;
use resolve::Type as RType;
use std::collections::HashSet;
use std::io::*;
use std::path::Path;
use std::rc::Rc;
use std::path::{Path, PathBuf};

#[derive(Debug, Clone, PartialEq)]
pub type Result<T> = std::result::Result<T, Error>;

#[derive(Debug, Clone)]
pub enum Error {
    Type(TypeErr),
    Resolve(ResolveErr),
    Parse(ParseErr),
    Lex(LexErr),
}

impl Error {
    pub fn render(&self, cache: &mut Cache) -> String {
        match self {
            Error::Type(e) => e.render(cache),
            Error::Resolve(e) => e.render(cache),
            Error::Parse(e) => e.render(cache),
            Error::Lex(e) => e.render(cache),
        }
    }
}

impl From<TypeErr> for Error {
    fn from(x: TypeErr) -> Error {
        Error::Type(x)
    }
}
impl From<ResolveErr> for Error {
    fn from(x: ResolveErr) -> Error {
        Error::Resolve(x)
    }
}
impl From<ParseErr> for Error {
    fn from(x: ParseErr) -> Error {
        Error::Parse(x)
    }
}
impl From<LexErr> for Error {
    fn from(x: LexErr) -> Error {
        Error::Lex(x)
    }
}

#[derive(Debug, Clone)]
pub enum TypeErr {
    ExpectedFound(Loc, RType, RType),
    ExpectedFoundAnnot(Loc, RType, RType),
    ExpectedFoundLambda(Loc, RType),
    ArityMisLambda { loc: Loc, check: usize, lit: usize },
    ArityMisApp { loc: Loc, params: usize, args: usize },
    InferLambda(Loc),
    AppNonFun(Loc, RType),
    ModuleIsNotExpr(Loc, ModuleId),
}

impl TypeErr {
    pub fn render(&self, cache: &mut Cache) -> String {
        use TypeErr::*;
        match self {
            ExpectedFound(loc, e, f) => loc.error(cache, format!("Expected `{e}`, found `{f}`.")),
            ExpectedFoundAnnot(loc, e, f) => loc.error(cache, format!("Expected {e:?}, found {f:?} (according to annotation).")),
            ExpectedFoundLambda(loc, e) => loc.error(cache, format!("Expected {e:?}, found a function.")),
            ArityMisLambda { loc, check, lit } => loc.error(cache, format!("Arity mismatch in function parameters. Expectectations from the outside are that there be {check} parameters, but the function here instead has {lit}.")),
            ArityMisApp { loc, params, args } => loc.error(cache, format!("Arity mismatch in function application. Function specifies {params} parameters, but {args} arguments were given.")),
            InferLambda(loc) => loc.error(cache, "Can't infer type of lambdas with >0 parameters (yet). Please surround the lambda in a type annotation to have the type checked instead of inferred."),
            AppNonFun(loc, t) => loc.error(cache, format!("Expected a function to apply, found a {t}.")),
            ModuleIsNotExpr(loc, _mid) => loc.error(cache, "Expected an expression, but this name resolves to a module."),
        }
    }
}

#[derive(Debug, Clone)]
pub enum ResolveErr {
    UndefInMod(PubIdent, ModuleId),
    InvalidRootNamespace(PubIdent),
    Unresolved(FullName<PubIdent>),
    ParentIsNotModule(Loc, FullName<PubIdent>),
}

impl ResolveErr {
    pub fn render(&self, cache: &mut Cache) -> String {
        use ResolveErr::*;
        match self {
            UndefInMod(def, mid) => {
                let mname = cache.fetch_module_name(*mid);
                def.loc.error(cache, format!("Couldn't find definition `{def}` in module `{mname}`."))
            }
            InvalidRootNamespace(ns) =>
                ns.loc.error(cache, format!("Invalid module namespace `{ns}`. Expected one of `own`, `pkg`, `prim`.")),
            Unresolved(name) => name
                .loc()
                .error(cache, format!("Failed to resolve the name `{name}` to any existing module or definition.")),
            ParentIsNotModule(loc, parent) =>
                loc.error(cache, format!("The parent `{}` does not resolve to a module.", parent)),
        }
    }
}

#[derive(Debug, Clone)]
pub struct ParseErr {
    pub loc: Loc,
    pub dist: usize,
    pub expecteds: HashSet<String>,
}

impl ParseErr {
    pub fn render(&self, cache: &mut Cache) -> String {
        self.loc.error(
            cache,
            if self.expecteds.len() == 1 {
                format!("expected {}", self.expecteds.iter().next().unwrap())
            } else {
                format!("expected one of {:?}", self.expecteds)
            },
        )
    }
}

#[derive(Clone)]
pub struct LexErr {
    pub expected: LexThing,
    pub found: LexThing,
    pub loc: Loc,
}

impl LexErr {
    pub fn render(&self, cache: &mut Cache) -> String {
        self.loc.error(
            cache,
            match (self.expected, self.found) {
                (LexThing::Anon, f) => format!("unexpected {f}"),
                (e, LexThing::Anon) => format!("expected {e}"),
                (e, f) => format!("expected {e}, found {f}"),
            },
        )
    }
}

impl std::fmt::Debug for LexErr {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "loc={:?}: expected {}, found {}", self.loc, self.expected, self.found)
    }
}

#[derive(Debug, Clone, Copy)]
pub enum LexThing {
    Char(char),
    Descr(&'static str),
    Eof,
    Anon,
}

impl std::fmt::Display for LexThing {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            LexThing::Char(c) => write!(f, "char {:?}", c),
            LexThing::Descr(desc) => write!(f, "{}", desc),
            LexThing::Eof => write!(f, "end-of-file"),
            LexThing::Anon => write!(f, "<see source>"),
        }
    }
}

#[derive(Debug, Clone, PartialEq, Copy)]
pub enum Loc {
    File { file: Rc<Path>, offset: u32 },
    FileGeneral { file: Rc<Path> },
    File { file: FileId, offset: u32 },
    FileGeneral { file: FileId },
    Anon { offset: u32 },
    AnonGeneral,
}

impl Loc {
    pub fn new(file: Option<Rc<Path>>, offset: u32) -> Self {
    pub fn new(file: Option<FileId>, offset: u32) -> Self {
        match file {
            Some(file) => Self::File { file, offset },
            None => Self::Anon { offset },


@@ 26,56 190,53 @@ impl Loc {
        }
    }

    pub fn err<M: std::fmt::Display>(&self, msg: M) -> anyhow::Error {
    pub fn error<M: std::fmt::Display>(&self, cache: &mut Cache, msg: M) -> String {
        self.contextualize(cache, format!("error: {msg}"))
    }

    pub fn contextualize<M: std::fmt::Display>(&self, cache: &mut Cache, msg: M) -> String {
        match self {
            Loc::File { file, offset } => {
                // TODO: Get source from cache instead. Don't want IO effects in random functions, and it might be faster.
                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}^",
                )
                let path = cache.fetch_path(*file).to_owned();
                let src = cache.fetch_source(*file).unwrap();
                let (line_i, column_i, line_s) = line_and_column_at(src, *offset);
                let gutter = " ".repeat(column_i.max(1).ilog10() as usize + 1);
                let marker = " ".repeat(column_i as usize);
                let path = path.to_string_lossy();
                format!("{path}:{line_i}:{column_i}: {msg}\n{gutter} |\n{column_i} | {line_s}\n{gutter} | {marker}^",)
            }
            Loc::FileGeneral { file } => {
                anyhow::anyhow!("{}: error: {msg}", file.to_string_lossy())
                let path = cache.fetch_path(*file);
                format!("{}: {msg}", path.to_string_lossy())
            }
            Loc::Anon { offset } => {
                anyhow::anyhow!("offset={offset}: error: {msg}")
                format!("offset={offset}: {msg}")
            }
            Loc::AnonGeneral => anyhow::anyhow!("error: {msg}"),
            Loc::AnonGeneral => format!("{msg}"),
        }
    }
}

// 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) {
// Calculate what line and column index the byte `offset` is at in the given string.
// Also include a slice of that complete line.
fn line_and_column_at(mut s: &str, mut offset: u32) -> (u32, u32, &str) {
    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),
        let (i, eof) = match s.find('\n') {
            None => (s.len(), true),
            Some(i) => (i, false),
        };
        if i as u32 >= offset {
            let cr = i > 0 && s.as_bytes()[i - 1] == b'\r';
            let line_s = &s[0..(if cr { i - 1 } else { i })];
            let column = offset;
            break (line, column, line_s);
        }
        if eof {
            panic!("ice: unexpected end of file")
        }
        line += 1;
        s = &s[i + 1..];
        offset -= i as u32 + 1;
    }
}

M src/eval.rs => src/eval.rs +2 -5
@@ 1,15 1,12 @@
use crate::abase::*;
use crate::{cache::Cache, name::*};
use anyhow::Result;
use crate::prelude::*;
use abase::*;
use std::collections::HashMap;

pub fn eval_fun_at(cache: &mut Cache, def: &FunDef, args: &[Operand]) -> Result<Operand> {
    println!("eval_fun_at: args: {args:?}\ndef: {def:?}\n");
    EvalDef::new(cache, &def.defs).run_fun(&def.params, args, &def.body)
}

pub fn eval_var(cache: &mut Cache, def: &GVarDef) -> Result<Operand> {
    println!("eval_var: def: {def:?}\n");
    EvalDef::new(cache, &def.defs).run_var(&def.body)
}


M src/fem.rs => src/fem.rs +4 -4
@@ 1,15 1,15 @@
//! Effem core IR

use crate::name::{LocalId, PubIdent, ResName};
pub use crate::parse::Type;
use crate::prelude::*;
pub use parse::Type;

#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone)]
pub struct Expr {
    pub typ: Type,
    pub kind: ExprKind,
}

#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone)]
pub enum ExprKind {
    F64(f64),
    Fun(Vec<(PubIdent, LocalId)>, Box<Expr>),

M src/lex.rs => src/lex.rs +55 -89
@@ 1,8 1,6 @@
use crate::diag::*;
use crate::name::PrivIdent;
use crate::prelude::*;
use std::borrow::Cow;
use std::path::Path;
use std::rc::Rc;

// TODO: Compare using:
//       - just a deeply nested tree with boxes


@@ 32,39 30,35 @@ pub struct Token<'s> {
    pub tok: Tok<'s>,
}

pub fn lex(f: Option<Rc<Path>>, src: &str) -> anyhow::Result<Vec<Token>> {
pub fn lex(f: Option<FileId>, src: &str) -> LResult<Vec<Token>> {
    assert!(src.len() < u32::MAX as usize);
    let mut sc = Scanner::new(f, src);
    sc.terms().map_err(|Err { expected, found, loc }| {
        loc.err(match (expected, found) {
            (Thing::Anon, f) => format!("unexpected {f}"),
            (e, Thing::Anon) => format!("expected {e}"),
            (e, f) => format!("expected {e}, found {f}"),
        })
    })
    sc.terms()
}

type LResult<T> = std::result::Result<T, LexErr>;

struct Scanner<'s> {
    src: &'s str,
    i: usize,
    file: Option<Rc<Path>>,
    file: Option<FileId>,
}

impl<'s> Scanner<'s> {
    fn new(file: Option<Rc<Path>>, src: &'s str) -> Self {
    fn new(file: Option<FileId>, src: &'s str) -> Self {
        Self { src, i: 0, file }
    }

    fn loc(&self) -> Loc {
        Loc::new(self.file.clone(), self.i as u32)
        Loc::new(self.file, self.i as u32)
    }

    fn maybe_term(&mut self) -> Result<Option<Token<'s>>, Err> {
    fn maybe_term(&mut self) -> LResult<Option<Token<'s>>> {
        self.skip_unsemantic();
        self.maybe_term_nospace()
    }

    fn maybe_term_nospace(&mut self) -> Result<Option<Token<'s>>, Err> {
    fn maybe_term_nospace(&mut self) -> LResult<Option<Token<'s>>> {
        Ok(if let Some(x) = self.maybe_factor()? {
            Some(if let Some('.') = self.peek() {
                self.consume();


@@ 81,7 75,7 @@ impl<'s> Scanner<'s> {
        })
    }

    fn maybe_factor(&mut self) -> Result<Option<Token<'s>>, Err> {
    fn maybe_factor(&mut self) -> LResult<Option<Token<'s>>> {
        let offset = self.i as u32;
        let c = match self.peek() {
            Some(c) => c,


@@ 126,10 120,10 @@ impl<'s> Scanner<'s> {
                self.consume();
                match self.maybe_term_nospace()? {
                    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::new(self.file.clone(), offset),
                    None => Err(LexErr {
                        expected: LexThing::Descr("'\\' followed immediately by any token tree"),
                        found: LexThing::Anon,
                        loc: Loc::new(self.file, offset),
                    }),
                }
            }


@@ 139,15 133,15 @@ impl<'s> Scanner<'s> {
                _ => unreachable!(),
            })),
            _ 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),
            _ => Err(LexErr {
                expected: LexThing::Descr("any token tree"),
                found: LexThing::Char(c),
                loc: Loc::new(self.file, offset),
            }),
        }
    }

    fn terms(&mut self) -> Result<Vec<Token<'s>>, Err> {
    fn terms(&mut self) -> LResult<Vec<Token<'s>>> {
        let mut ts = Vec::new();
        while let Some(t) = self.maybe_term()? {
            ts.push(t)


@@ 155,11 149,11 @@ impl<'s> Scanner<'s> {
        Ok(ts)
    }

    fn ident(&mut self) -> Result<&'s str, Err> {
        self.find_on_line1(Thing::Descr("identifier"), |c| !ident_char(c))
    fn ident(&mut self) -> LResult<&'s str> {
        self.find_on_line1(LexThing::Descr("identifier"), |c| !ident_char(c))
    }

    fn find_on_line1(&mut self, expected: Thing, mut pred: impl FnMut(char) -> bool) -> Result<&'s str, Err> {
    fn find_on_line1(&mut self, expected: LexThing, mut pred: impl FnMut(char) -> bool) -> LResult<&'s str> {
        let i0 = self.i;
        match self.input().find(|c| c == '\n' || pred(c)) {
            None => {


@@ 167,10 161,10 @@ impl<'s> Scanner<'s> {
                if self.i > i0 {
                    Ok(&self.src[i0..self.i])
                } else {
                    Err(Err { expected, found: Thing::Eof, loc: self.loc() })
                    Err(LexErr { expected, found: LexThing::Eof, loc: self.loc() })
                }
            }
            Some(0) => Err(Err { expected, found: Thing::Char(self.peek().unwrap()), loc: self.loc() }),
            Some(0) => Err(LexErr { expected, found: LexThing::Char(self.peek().unwrap()), loc: self.loc() }),
            Some(n) => {
                self.i += n;
                Ok(&self.src[i0..self.i])


@@ 178,12 172,12 @@ impl<'s> Scanner<'s> {
        }
    }

    fn string(&mut self) -> Result<Cow<'s, str>, Err> {
    fn string(&mut self) -> LResult<Cow<'s, str>> {
        self.consume_expect('"')?;
        let start = self.i;
        let mut escapes = vec![];
        let end = loop {
            match self.consume_some(Thing::Descr("string literal closing quote '\"'"))? {
            match self.consume_some(LexThing::Descr("string literal closing quote '\"'"))? {
                '\\' => {
                    let esc_start = self.i - 1;
                    let c = self.escape_sequence()?;


@@ 210,9 204,9 @@ impl<'s> Scanner<'s> {
        })
    }

    fn escape_sequence(&mut self) -> Result<char, Err> {
    fn escape_sequence(&mut self) -> LResult<char> {
        let loc = self.loc();
        match self.consume_some(Thing::Descr("escape sequence"))? {
        match self.consume_some(LexThing::Descr("escape sequence"))? {
            'n' => Ok('\n'),
            'r' => Ok('\r'),
            't' => Ok('\t'),


@@ 230,9 224,9 @@ impl<'s> Scanner<'s> {
                    let x = self.hex_digit()?;
                    acc += (x as u32) << (4 * i);
                }
                char::from_u32(acc).ok_or(Err {
                    expected: Thing::Descr("valid unicode character code"),
                    found: Thing::Anon,
                char::from_u32(acc).ok_or(LexErr {
                    expected: LexThing::Descr("valid unicode character code"),
                    found: LexThing::Anon,
                    loc,
                })
            }


@@ 243,26 237,27 @@ impl<'s> Scanner<'s> {
                    let x = self.hex_digit()?;
                    acc += (x as u32) << (4 * i);
                }
                char::from_u32(acc).ok_or(Err {
                    expected: Thing::Descr("valid unicode character code"),
                    found: Thing::Anon,
                char::from_u32(acc).ok_or(LexErr {
                    expected: LexThing::Descr("valid unicode character code"),
                    found: LexThing::Anon,
                    loc,
                })
            }
            c => Err(Err { expected: Thing::Descr("escape sequence"), found: Thing::Char(c), loc }),
            c => Err(LexErr { expected: LexThing::Descr("escape sequence"), found: LexThing::Char(c), loc }),
        }
    }

    fn oct_digit(&mut self) -> Result<u8, Err> {
        self.consume_pred(Thing::Descr("octal digit"), |c| c.is_ascii_octdigit())
    fn oct_digit(&mut self) -> LResult<u8> {
        self.consume_pred(LexThing::Descr("octal digit"), |c| c.is_ascii_octdigit())
            .map(|c| c.to_digit(0o10).unwrap() as u8)
    }

    fn hex_digit(&mut self) -> Result<u8, Err> {
        self.consume_pred(Thing::Descr("hex digit"), |c| c.is_ascii_hexdigit()).map(|c| c.to_digit(0x10).unwrap() as u8)
    fn hex_digit(&mut self) -> LResult<u8> {
        self.consume_pred(LexThing::Descr("hex digit"), |c| c.is_ascii_hexdigit())
            .map(|c| c.to_digit(0x10).unwrap() as u8)
    }

    fn num(&mut self) -> Result<Token<'s>, Err> {
    fn num(&mut self) -> LResult<Token<'s>> {
        let offset = self.i as u32;
        let mut acc = 0u64;
        loop {


@@ 276,7 271,7 @@ impl<'s> Scanner<'s> {
        }
    }

    fn float(&mut self, left: u64) -> Result<Token<'s>, Err> {
    fn float(&mut self, left: u64) -> LResult<Token<'s>> {
        // TODO: Read floats accurately. See [(Clinger, "How to read floating point numbers accurately")](https://dl.acm.org/doi/10.1145/93542.93557).
        self.consume_expect('.')?;
        let mut factor = 0.1;


@@ 310,25 305,26 @@ impl<'s> Scanner<'s> {
        }
    }

    fn consume_pred(&mut self, expected: Thing, pred: impl FnOnce(char) -> bool) -> Result<char, Err> {
    fn consume_pred(&mut self, expected: LexThing, pred: impl FnOnce(char) -> bool) -> LResult<char> {
        match self.peek() {
            Some(found) if pred(found) => {
                self.consume();
                Ok(found)
            }
            Some(found) => Err(Err { expected, found: Thing::Char(found), loc: self.loc() }),
            None => Err(Err { expected, found: Thing::Eof, loc: self.loc() }),
            Some(found) => Err(LexErr { expected, found: LexThing::Char(found), loc: self.loc() }),
            None => Err(LexErr { expected, found: LexThing::Eof, loc: self.loc() }),
        }
    }

    fn consume_expect(&mut self, expected: char) -> Result<(), Err> {
    fn consume_expect(&mut self, expected: char) -> LResult<()> {
        match self.peek() {
            Some(found) if expected == found => {
                self.consume();
                Ok(())
            }
            Some(found) => Err(Err { expected: Thing::Char(expected), found: Thing::Char(found), loc: self.loc() }),
            None => Err(Err { expected: Thing::Char(expected), found: Thing::Eof, loc: self.loc() }),
            Some(found) =>
                Err(LexErr { expected: LexThing::Char(expected), found: LexThing::Char(found), loc: self.loc() }),
            None => Err(LexErr { expected: LexThing::Char(expected), found: LexThing::Eof, loc: self.loc() }),
        }
    }



@@ 340,8 336,8 @@ impl<'s> Scanner<'s> {
        self.input().chars().nth(1)
    }

    fn consume_some(&mut self, thing: Thing) -> Result<char, Err> {
        self.consume().ok_or_else(|| Err { expected: thing, found: Thing::Eof, loc: self.loc() })
    fn consume_some(&mut self, thing: LexThing) -> LResult<char> {
        self.consume().ok_or_else(|| LexErr { expected: thing, found: LexThing::Eof, loc: self.loc() })
    }

    fn consume(&mut self) -> Option<char> {


@@ 363,36 359,6 @@ fn ident_char(c: char) -> bool {
        || c.is_control())
}

pub struct Err {
    expected: Thing,
    found: Thing,
    loc: Loc,
}

impl std::fmt::Debug for Err {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "loc={:?}: expected {}, found {}", self.loc, self.expected, self.found)
    }
}

pub enum Thing {
    Char(char),
    Descr(&'static str),
    Eof,
    Anon,
}

impl std::fmt::Display for Thing {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            Thing::Char(c) => write!(f, "char {:?}", c),
            Thing::Descr(desc) => write!(f, "{}", desc),
            Thing::Eof => write!(f, "end-of-file"),
            Thing::Anon => write!(f, "<see source>"),
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;


@@ 488,12 454,12 @@ mod test {
    }

    #[test]
    fn test_pkg() {
        let src = "pkg. ";
    fn test_own() {
        let src = "own. ";
        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("pkg")) => (),
            Tok::DotPost(c) if matches!(c.tok, Tok::Ident("own")) => (),
            _ => panic!("Lexd source {src:?} didn't match the expected pattern. Got {t:?}"),
        }
    }

M src/main.rs => src/main.rs +48 -18
@@ 14,10 14,10 @@ mod fem;
mod lex;
mod name;
mod parse;
mod prelude;
mod resolve;

use cache::Cache;
use name::*;
use prelude::*;
use std::borrow::Cow;
use std::path::Path;



@@ 26,34 26,64 @@ fn main() {
        .nth(1)
        // .expect("first argument should be entrypoint file (e.g. \"src/main.fm\")");
        .unwrap_or("/home/jojo/Hack/effem/tests/main.fm".to_owned());
    println!("Running {main_path}");
    let (dir, module_name) = name::parse_source_file_path(Path::new(&main_path)).unwrap();
    println!("d: {dir:?}, m: {module_name:?}");
    println!("   Running {main_path}");
    let (dir, module_name) = parse_source_file_path(Path::new(&main_path));
    let mut cache = Cache::for_package_in(dir);
    let id = cache
        .fetch_global_resolved_name(&FullName(vec!["pkg".to_string(), module_name.to_string(), "main".to_string()]))
        .unwrap()
        .as_def()
        .unwrap();
    println!("result: {:?}", cache.fetch_evaluated(id));
    let main_name = name_in_cli(&["own", module_name, "main"]);
    if let Err(msg) = with_pretty_errors(&mut cache, |cache| {
        let id = cache.fetch_global_resolved_name(&main_name)?.as_def().unwrap();
        println!("result: {:?}", cache.fetch_evaluated(id)?);
        Ok(())
    }) {
        eprintln!("{msg}")
    }
}

fn name_in_cli(segs: &[&str]) -> FullName<PubIdent> {
    let cli_loc = diag::Loc::AnonGeneral;
    FullName::new(segs.iter().map(|s| PubIdent { loc: cli_loc, s: s.to_string() }).collect())
}

fn with_pretty_errors<A>(
    cache: &mut Cache,
    f: impl FnOnce(&mut Cache) -> diag::Result<A>,
) -> std::result::Result<A, String> {
    match f(cache) {
        Ok(a) => Ok(a),
        Err(e) => Err(e.render(cache)),
    }
}

fn parse_source_file_path(p: &Path) -> (&Path, &str) {
    match p.extension() {
        Some(os) if os == "fm" => (),
        ext => panic!("expected extension .fm, found {ext:?}"),
    }
    let d = p.parent().unwrap_or(Path::new(""));
    if let Some(s) = p.file_stem() {
        if let Some(s) = s.to_str() {
            return (d, s);
        }
    }
    panic!("invalid module path {p:?}")
}

#[cfg(test)]
mod test {
    use super::*;
    use abase::*;
    use diag::Result;
    use eval::*;

    fn run_tmp(main_src: &str) -> anyhow::Result<Operand> {
    fn run_tmp(main_src: &str) -> std::result::Result<Operand, String> {
        let dir = tempfile::tempdir().unwrap();
        std::fs::write(dir.path().join("main.fm"), main_src).unwrap();
        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()
            .as_def()
            .unwrap();
        cache.fetch_evaluated(main_id).cloned()
        with_pretty_errors(&mut cache, |cache| {
            let main_id =
                cache.fetch_global_resolved_name(&name_in_cli(&["own", "main", "main"])).unwrap().as_def().unwrap();
            cache.fetch_evaluated(main_id).cloned()
        })
    }

    #[test]

M src/name.rs => src/name.rs +103 -35
@@ 1,22 1,9 @@
use anyhow::{anyhow, bail};
use crate::prelude::*;
use std::borrow::Cow;
use std::fmt;
use std::hash::Hash;
use std::path::Path;

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:?}"),
    }
    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));
        }
    }
    Err(anyhow!("invalid module path {p:?}"))
}

#[derive(Debug, Clone)]
pub struct ParsedName {
    // TODO: This bool ends up wasting a whole word. Weave it into `segments` somehow & measure


@@ 36,11 23,12 @@ pub struct ResName {
    // pos
}

#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Res {
    Def(DefId),
    Local(LocalId),
    Prim(Prim),
    Module(ModuleId),
}

impl Res {


@@ 55,14 43,22 @@ impl Res {
pub type DefId = u32;
pub type LocalId = u32;

#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct ModuleId(pub u32);

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct FileId(pub u32);

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Prim {
    Add = 0,
    Mul = 1,
}

impl std::fmt::Display for Prim {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
impl fmt::Display for Prim {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{}",


@@ 74,20 70,73 @@ impl std::fmt::Display for Prim {
    }
}

/// 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>);
/// A nonempty list representing a fully qualified name.
///
/// For example, ["pkg", "std", "str", "Str", "new"] represents the path "new.Str.str.std.pkg."
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FullName<S>(Vec<S>);

impl<S> FullName<S> {
    pub fn new(segments: Vec<S>) -> Self {
        assert!(segments.len() > 0);
        Self(segments)
    }

    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    pub fn len(&self) -> usize {
        self.0.len()
    }

    pub fn rest(mut self) -> Option<Self> {
        self.0.remove(0);
        if self.is_empty() {
            None
        } else {
            Some(self)
        }
    }

    pub fn last(&self) -> &S {
        &self.0[self.0.len() - 1]
    }

    pub fn split_first(mut self) -> (S, Option<Self>) {
        (self.0.remove(0), (!self.0.is_empty()).then_some(self))
    }

impl FullName {
    pub fn with(&self, element: PubIdent) -> Self {
        let mut name = self.clone();
        name.0.push(element.0);
        name
    pub fn split_last(mut self) -> (S, Option<Self>) {
        (self.0.pop().unwrap(), (!self.0.is_empty()).then_some(self))
    }

    pub fn get(&self, i: usize) -> Option<&S> {
        self.0.get(i)
    }

    pub fn with(mut self, element: S) -> Self {
        self.0.push(element);
        self
    }

    pub fn iter(&self) -> impl Iterator<Item = &S> {
        self.0.iter()
    }
}

impl FullName<PubIdent> {
    pub fn loc(&self) -> Loc {
        self.last().loc
    }

    pub fn strip_locs(self) -> FullName<String> {
        FullName(self.0.into_iter().map(|PubIdent { s, .. }| s).collect())
    }
}

impl std::fmt::Display for FullName {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
impl<S: fmt::Display> fmt::Display for FullName<S> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for seg in self.0.iter().rev() {
            write!(f, "{}.", seg)?
        }


@@ 108,18 157,37 @@ pub struct PrivIdent {
}

impl PrivIdent {
    pub fn in_source<'s>(&self, src: &'s str) -> &'s str {
    pub fn substr_in<'s>(&self, src: &'s str) -> &'s str {
        let i = self.start as usize;
        &src[i..i + self.len as usize]
    }

    pub fn to_pub<'s>(&self, cache: &mut Cache, file: FileId) -> Result<PubIdent> {
        Ok(self.substr_to_pub(cache.fetch_source(file)?, file))
    }

    pub fn substr_to_pub<'s>(&self, src: &str, file: FileId) -> PubIdent {
        let loc = Loc::File { file, offset: self.start };
        let s = self.substr_in(src).to_string();
        PubIdent { loc, s }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PubIdent(pub String);
#[derive(Debug, Clone)]
pub struct PubIdent {
    pub loc: Loc,
    pub s: String,
}

impl PubIdent {
    pub fn as_str(&self) -> &str {
        &self.s
    }
}

impl std::borrow::Borrow<str> for PubIdent {
    fn borrow(&self) -> &str {
        &self.0
impl fmt::Display for PubIdent {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.s, f)
    }
}


M src/parse.rs => src/parse.rs +56 -67
@@ 1,11 1,11 @@
use crate::{diag::*, lex::*, name::*};
use crate::prelude::*;
use lex::*;
use std::collections::{HashMap, HashSet};
use std::iter::once;
use std::path::Path;
use std::rc::Rc;

pub struct Module {
    pub defs: HashMap<PubIdent, Expr>,
    pub defs: HashMap<String, Expr>,
}

// TODO: Parse directly to indexed flat tree.


@@ 23,7 23,13 @@ pub struct Module {
//       No syncing between threads necessary.
//       Different modules can't access eachothers locals.
#[derive(Debug, Clone)]
pub enum Expr {
pub struct Expr {
    pub loc: Loc,
    pub kind: ExprKind,
}

#[derive(Debug, Clone)]
pub enum ExprKind {
    F64(f64),
    Fun(Vec<PrivIdent>, Box<Expr>),
    App(Box<Expr>, Vec<Expr>),


@@ 37,26 43,29 @@ pub enum Type {
    Fun(Vec<Type>, Box<Type>),
}

pub fn parse<'s>(file: Option<Rc<Path>>, src: &'s str, tokens: &[Token<'s>]) -> anyhow::Result<Module> {
impl std::fmt::Display for Type {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            Type::F64 => write!(f, "F64"),
            Type::Fun(ps, r) => {
                write!(f, "(Fun [")?;
                for p in ps {
                    write!(f, "{p}")?;
                }
                write!(f, "] {r})")
            }
        }
    }
}

pub fn parse<'s>(file: Option<FileId>, src: &'s str, tokens: &[Token<'s>]) -> std::result::Result<Module, ParseErr> {
    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().map(|(lhs, rhs)| (PubIdent(lhs.in_source(src).to_owned()), rhs)).collect() })
        Ok(Module { defs: defs.into_iter().map(|(lhs, rhs)| (lhs.substr_in(src).to_owned(), rhs)).collect() })
    };
    go().map_err(|Err { loc, expecteds, dist: _ }| {
        loc.err(if expecteds.len() == 1 {
            format!("expected {}", expecteds.into_iter().next().unwrap())
        } else {
            format!("expected one of {:?}", expecteds)
        })
    })
}

pub struct Err {
    loc: Loc,
    dist: usize,
    expecteds: HashSet<String>,
    go()
}

#[derive(Debug, Clone)]


@@ 66,11 75,15 @@ struct Inp<'s, 't> {
    context: Loc,
}

type Res<'s, 't, A> = std::result::Result<(Inp<'s, 't>, A), ParseErr>;

trait Parser<'s, 't, A> = FnMut(Inp<'s, 't>) -> Res<'s, 't, A> where 's: 't;

impl<'s, 't> Inp<'s, 't> {
    fn next(&self) -> Res<'s, 't, &'t Token<'s>> {
        match self.tokens.split_first() {
            Some((t, rest)) => Ok((Inp { tokens: rest, dist: self.dist + 1, context: self.context.clone() }, t)),
            None => Err(Err {
            None => Err(ParseErr {
                loc: self.context.clone(),
                dist: self.dist,
                expecteds: once("continuation of token sequence".to_string()).collect(),


@@ 80,12 93,12 @@ impl<'s, 't> Inp<'s, 't> {

    fn filter_next<A, S: std::fmt::Display>(
        &self,
        extract: impl FnOnce(&'t Token<'s>) -> Result<A, S>,
        extract: impl FnOnce(&'t Token<'s>) -> std::result::Result<A, S>,
    ) -> Res<'s, 't, A> {
        let (inp, t) = self.next()?;
        match extract(t) {
            Ok(x) => Ok((inp, x)),
            Err(expected) => Err(Err {
            Err(expected) => Err(ParseErr {
                loc: self.context.with_offset(t.offset),
                dist: self.dist,
                expecteds: once(expected.to_string()).collect(),


@@ 95,7 108,7 @@ impl<'s, 't> Inp<'s, 't> {

    fn enter<A>(
        &self,
        extract: impl FnOnce(&'t Tok<'s>) -> Result<&'t [Token<'s>], &'static str>,
        extract: impl FnOnce(&'t Tok<'s>) -> std::result::Result<&'t [Token<'s>], &'static str>,
        mut f: impl Parser<'s, 't, A>,
    ) -> Res<'s, 't, A> {
        let (rem, t) = self.next()?;


@@ 106,7 119,7 @@ impl<'s, 't> Inp<'s, 't> {
                let inp = Inp { tokens: rem.tokens, dist: inp.dist, context: rem.context };
                Ok((inp, x))
            }
            Err(expected) => Err(Err {
            Err(expected) => Err(ParseErr {
                loc: self.context.with_offset(t.offset),
                dist: self.dist,
                expecteds: once(expected.to_string()).collect(),


@@ 115,9 128,6 @@ 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, (PrivIdent, Expr)> {
    parens(|inp| {
        let (inp, _) = special_form("def")(inp)?;


@@ 129,24 139,26 @@ fn def<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, (PrivIdent, Expr)> {
}

fn expr<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, Expr> {
    alt3(map(lit_float, Expr::F64), map(name, Expr::Var), parens(pexpr))(inp)
    let loc = inp.context.with_offset(inp.next()?.1.offset);
    let (inp, kind) = alt3(map(lit_float, ExprKind::F64), map(name, ExprKind::Var), parens(pexpr))(inp)?;
    Ok((inp, Expr { loc, kind }))
}

fn pexpr<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, Expr> {
fn pexpr<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, ExprKind> {
    alt3(
        |inp| {
            let (inp, _) = special_form("fun")(inp)?;
            let (inp, params) = brackets(rest(ident))(inp)?;
            let (inp, body) = expr(inp)?;
            let (inp, ()) = end(inp)?;
            Ok((inp, Expr::Fun(params, Box::new(body))))
            Ok((inp, ExprKind::Fun(params, Box::new(body))))
        },
        |inp| {
            let (inp, _) = special_form("of")(inp)?;
            let (inp, (t, e)) = pair(typ, expr)(inp)?;
            Ok((inp, Expr::Annot(Box::new(e), t)))
            Ok((inp, ExprKind::Annot(Box::new(e), t)))
        },
        map(pair(expr, rest(expr)), |(f, xs)| Expr::App(Box::new(f), xs)),
        map(pair(expr, rest(expr)), |(f, xs)| ExprKind::App(Box::new(f), xs)),
    )(inp)
}



@@ 179,7 191,7 @@ fn name<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, ParsedName> {
    inp.filter_next(name_)
}

fn name_(t: &Token) -> Result<ParsedName, &'static str> {
fn name_(t: &Token) -> std::result::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 {


@@ 254,7 266,7 @@ fn brackets<'s: 't, 't, A>(mut f: impl Parser<'s, 't, A>) -> impl Parser<'s, 't,

fn end<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, ()> {
    if let Some(t) = inp.tokens.first() {
        Err(Err {
        Err(ParseErr {
            loc: inp.context.with_offset(t.offset),
            dist: inp.dist,
            expecteds: once(format!("end of token sequence ({:?})", inp)).collect(),


@@ 264,30 276,6 @@ 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 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| {
        f(inp.clone()).or_else(|e1| {


@@ 295,7 283,7 @@ fn alt2<'s: 't, 't, A>(mut f: impl Parser<'s, 't, A>, mut g: impl Parser<'s, 't,
            g(inp).map_err(|e2| match e1.dist.cmp(&e2.dist) {
                Greater => e1,
                Less => e2,
                Equal => Err { expecteds: e1.expecteds.union(&e2.expecteds).cloned().collect(), ..e2 },
                Equal => ParseErr { expecteds: e1.expecteds.union(&e2.expecteds).cloned().collect(), ..e2 },
            })
        })
    }


@@ 373,8 361,9 @@ mod test {
        let m = parse(None, src, &lex(None, src).unwrap()).unwrap();
        assert_matches!(
            &m.defs["x"],
            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)
            Expr{kind:ExprKind::App(f, xs),..}
            if matches!(&**f, Expr{ kind: ExprKind::Var(name), ..} if name.segments.len() == 1 && matches!(name.segments[0], PrivIdent { start: 8, len: 1 }))
            && matches!(&xs[..], [Expr{kind:ExprKind::F64(x1),..}, Expr{kind:ExprKind::F64(x2),..}] if *x1 == 1.0 && *x2 == 2.0)
        )
    }



@@ 384,11 373,11 @@ mod test {
        let m = parse(None, src, &lex(None, src).unwrap()).unwrap();
        assert_matches!(
            &m.defs["add"],
            Expr::Fun(ps, b)
            Expr{kind:ExprKind::Fun(ps, b),..}
            if matches!(ps[..], [PrivIdent { start: 15, len: 1 }, PrivIdent { start: 17, len: 1 }])
            && matches!(&**b, Expr::App(f, xs)
                        if matches!(&**f, Expr::Var(f) if f.segments.len() == 1 && matches!(f.segments[0], PrivIdent { start: 21, len: 1 }))
                        && matches!(&xs[..], [Expr::Var(x1), Expr::Var(x2)] if x1.segments[0].start == 23 && x2.segments[0].start == 25))
            && matches!(&**b, Expr{kind:ExprKind::App(f, xs),..}
                if matches!(&**f, Expr{kind:ExprKind::Var(f),..} if f.segments.len() == 1 && matches!(f.segments[0], PrivIdent { start: 21, len: 1 }))
                && matches!(&xs[..], [Expr{kind:ExprKind::Var(x1),..}, Expr{kind:ExprKind::Var(x2),..}] if x1.segments[0].start == 23 && x2.segments[0].start == 25))
        )
    }



@@ 398,8 387,8 @@ mod test {
        let m = parse(None, src, &lex(None, src).unwrap()).unwrap();
        assert_matches!(
            &m.defs["foo"],
            Expr::Annot(e, t)
            if matches!(&**e, Expr::F64(x) if *x == 123.0)
            Expr{kind:ExprKind::Annot(e, t),..}
            if matches!(&**e, Expr{kind:ExprKind::F64(x),..} if *x == 123.0)
            && matches!(t, Type::F64)
        )
    }

A src/prelude.rs => src/prelude.rs +16 -0
@@ 0,0 1,16 @@
pub(crate) use crate::{abase, cache, check, diag, eval, fem, lex, name, parse, resolve};
pub(crate) use cache::Cache;
pub(crate) use diag::*;
pub(crate) use name::*;

// pub fn mapl<A1, A2, B>((a, b): (A1, B), f: impl Fn(A1) -> A2) -> (A2, B) {
//     (f(a), b)
// }

pub fn mapr<A, B1, B2>((a, b): (A, B1), f: impl Fn(B1) -> B2) -> (A, B2) {
    (a, f(b))
}

// pub fn bimap<A1, A2, B1, B2>((a, b): (A1, B1), f: impl Fn(A1) -> A2, g: impl Fn(B1) -> B2) -> (A2, B2) {
//     (f(a), g(b))
// }

M src/resolve.rs => src/resolve.rs +49 -41
@@ 1,6 1,6 @@
pub use crate::parse::Type;
use crate::{cache::*, name::*, parse};
use anyhow::{anyhow, Result};
use crate::prelude::*;
use diag::ResolveErr::*;
pub use parse::Type;
use std::borrow::Cow;
use std::collections::HashMap;



@@ 10,8 10,14 @@ use std::collections::HashMap;
// 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 {
#[derive(Debug, Clone)]
pub struct Expr {
    pub loc: Loc,
    pub kind: ExprKind,
}

#[derive(Debug, Clone)]
pub enum ExprKind {
    F64(f64),
    Fun(Vec<(PubIdent, LocalId)>, Box<Expr>),
    App(Box<Expr>, Vec<Expr>),


@@ 19,74 25,76 @@ pub enum Expr {
    Annot(Box<Expr>, Type),
}

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::new(cache, module_name)?;
pub fn resolve_def(cache: &mut Cache, parent_module: ModuleId, def_body: &parse::Expr) -> Result<Expr> {
    let mut resolver = Resolver::new(cache, parent_module)?;
    resolver.resolve(def_body)
}

struct Resolver<'c> {
    cache: &'c mut Cache,
    local_count: u32,
    scopes: Vec<HashMap<PubIdent, Res>>,
    module: FullName,
    scopes: Vec<HashMap<String, Res>>,
    module: ModuleId,
    file: FileId,
}

impl<'c> Resolver<'c> {
    fn new(cache: &'c mut Cache, module: FullName) -> Result<Self> {
        let module_top_level = cache.fetch_module_local_resolved_names(&module)?.clone();
        Ok(Self { cache, local_count: 0, scopes: vec![module_top_level], module })
    fn new(cache: &'c mut Cache, module: ModuleId) -> Result<Self> {
        let module_top_level = cache.fetch_module_local_resolved_names(module)?.clone();
        let file = cache.fetch_module_file(module);
        Ok(Self { cache, local_count: 0, scopes: vec![module_top_level], module, file })
    }

    fn resolve(&mut self, e: &parse::Expr) -> Result<Expr> {
        use parse::Expr::*;
        Ok(match e {
            F64(x) => Expr::F64(*x),
            Fun(params, body) => {
        use parse::ExprKind as Pek;
        use ExprKind as Ek;
        let kind = match &e.kind {
            Pek::F64(x) => Ek::F64(*x),
            Pek::Fun(params, body) => {
                let rparams =
                    params.iter().map(|p| Ok((self.pub_ident(p)?, self.gen_local_id()))).collect::<Result<Vec<_>>>()?;
                self.scopes.push(rparams.iter().map(|(p, id)| (p.clone(), Res::Local(*id))).collect());
                self.scopes.push(rparams.iter().map(|(p, id)| (p.s.clone(), Res::Local(*id))).collect());
                let rbody = self.resolve(body)?;
                self.scopes.pop();
                Expr::Fun(rparams, Box::new(rbody))
                Ek::Fun(rparams, Box::new(rbody))
            }
            App(f, args) =>
                Expr::App(Box::new(self.resolve(f)?), args.iter().map(|a| self.resolve(a)).collect::<Result<_>>()?),
            Var(v) => {
            Pek::App(f, args) =>
                Ek::App(Box::new(self.resolve(f)?), args.iter().map(|a| self.resolve(a)).collect::<Result<_>>()?),
            Pek::Var(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.`
                // - `foo` can refer to `foo'` as just `foo'`, but must refer to `bar` by the full `bar.misc.own.`
                // - `bar` must refer to `foo` by the full `foo.main.own.`
                // So `resolve` must be aware of the module it's in from the start
                Expr::Var(ResName { res: self.resolve_parsed_name(v)? })
                Ek::Var(ResName { res: self.resolve_parsed_name(v)? })
            }
            Annot(e, t) => Expr::Annot(Box::new(self.resolve(e)?), t.clone()),
        })
            Pek::Annot(e, t) => Ek::Annot(Box::new(self.resolve(e)?), t.clone()),
        };
        Ok(Expr { loc: e.loc, kind })
    }

    fn resolve_parsed_name(&mut self, pname: &ParsedName) -> Result<Res> {
        assert!(!pname.segments.is_empty());
        let (_, src) = self.cache.fetch_source(&self.module)?;
        let name = pname.segments.iter().map(|seg| seg.in_source(src).to_owned()).collect::<Vec<_>>();
        debug_assert!(!pname.segments.is_empty());
        let src = self.cache.fetch_source(self.file)?;
        let name = FullName::new(pname.segments.iter().map(|seg| seg.substr_to_pub(src, self.file)).collect());
        if pname.rooted {
            self.cache.fetch_global_resolved_name(&FullName(name))
            self.cache.fetch_global_resolved_name(&name)
        } else {
            match name.split_first() {
                Some((first, [])) =>
                    self.scopes.iter().rev().find_map(|scope| scope.get(&PubIdent(first.clone()))).cloned().ok_or_else(
                        || anyhow!("error: couldn' find definition `{}` in module `{}`", first, self.module),
                    ),
                Some((_first, _rest)) => todo!(),
                None => todo!(), // Err
                (first, None) => self
                    .scopes
                    .iter()
                    .rev()
                    .find_map(|scope| scope.get(first.as_str()))
                    .cloned()
                    .ok_or_else(|| Error::Resolve(UndefInMod(first.clone(), self.module))),
                (_first, _rest) => todo!(),
            }
        }
    }

    fn pub_ident(&mut self, priv_ident: &PrivIdent) -> Result<PubIdent> {
        let (_, src) = self.cache.fetch_source(&self.module)?;
        Ok(PubIdent(priv_ident.in_source(src).to_owned()))
        priv_ident.to_pub(self.cache, self.file)
    }

    fn gen_local_id(&mut self) -> LocalId {