~jojo/effem

7624f626ff9a25bce919045ef0d36e7aa3405a83 — JoJo 1 year, 1 month ago 0cd5649
rework names & ids a bit, and start doing a little real resolving

also a bunch of other misc changes & fixes.
haven't been checking into vc for a bit
14 files changed, 539 insertions(+), 395 deletions(-)

M src/abase.rs
D src/ast.rs
D src/base.rs
M src/cache.rs
M src/desugar.rs
M src/diag.rs
M src/eval.rs
M src/fem.rs
M src/lex.rs
M src/main.rs
M src/name.rs
M src/parse.rs
M src/resolve.rs
D src/val.rs
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),
}