~jojo/effem

205a36ed55615cb88cf98381396b6971fcb6814e — JoJo 3 months ago 6c55ea0
parse & resolve `fun`s
8 files changed, 229 insertions(+), 65 deletions(-)

M src/abase.rs
M src/cache.rs
M src/eval.rs
M src/fem.rs
M src/main.rs
M src/name.rs
M src/parse.rs
M src/resolve.rs
M src/abase.rs => src/abase.rs +99 -14
@@ 6,24 6,109 @@ use crate::name::*;
use anyhow::anyhow;

#[derive(Clone)]
pub enum GlobDef {
    FunDef(FunDef),
    VarDef(GVarDef),
    Alias(DefId),
}

#[derive(Clone)]
pub struct FunDef {
    // Nested definitions (including anonymous functions)
    defs: Vec<(LocalId, GlobDef)>,
    params: Vec<()>,
    body: Expr,
}

#[derive(Clone)]
pub struct GVarDef {
    defs: Vec<(LocalId, GlobDef)>,
    body: Expr,
}

#[derive(Clone)]
pub enum Expr {
    Add(Operand, Operand),
    Mul(Operand, Operand),
    Operand(Operand),
}

#[derive(Clone)]
pub enum Operand {
    Const(Const),
    // Extern Extern
    Local(DefId),
    Global(DefId),
}

#[derive(Clone)]
pub enum Const {
    F64(f64),
    Add(Box<Expr>, Box<Expr>),
    Mul(Box<Expr>, Box<Expr>),
}

pub fn abase(cache: &mut Cache, expr: &FExpr) -> anyhow::Result<Expr> {
    match *expr {
        FExpr::F64(x) => Ok(Expr::F64(x)),
        FExpr::App(ref f, ref xs) => Ok(match **f {
            FExpr::Var(ResName { res: Res::Prim(p) }) => match p {
                Prim::Add => Expr::Add(Box::new(abase(cache, &xs[0])?), Box::new(abase(cache, &xs[1])?)),
                Prim::Mul => Expr::Mul(Box::new(abase(cache, &xs[0])?), Box::new(abase(cache, &xs[1])?)),
pub fn abase_def(cache: &mut Cache, rhs: &FExpr) -> anyhow::Result<GlobDef> {
    AbaseDef::new(cache).run(rhs)
}

struct AbaseDef<'c> {
    cache: &'c mut Cache,
    defs: Vec<(LocalId, GlobDef)>,
    vars: Vec<LocalId>,
    n_locals: u32,
}

impl<'c> AbaseDef<'c> {
    fn new(cache: &'c mut Cache) -> Self {
        Self { cache, defs: vec![], vars: vec![], n_locals: 0 }
    }

    fn run(mut self, rhs: &FExpr) -> anyhow::Result<GlobDef> {
        match self.abase(rhs)? {
            Expr::Operand(rand) => match rand {
                Operand::Local(id) => {
                    assert_eq!(id, 0);
                    assert_eq!(self.defs.len(), 1);
                    assert_eq!(self.vars.len(), 0);
                    Ok(self.defs.pop().unwrap().1)
                }
                Operand::Global(id) => Ok(GlobDef::Alias(id)),
                _ => Ok(GlobDef::VarDef(GVarDef { defs: self.defs, body: Expr::Operand(rand) })),
            },
            _ => todo!(),
        }),
        FExpr::Var(ResName { res: Res::Def(id) }) => cache.fetch_base(id).cloned(),
        FExpr::Var(ResName { res: Res::Prim(prim) }) =>
            Err(anyhow!("error: can't abase primitive `{prim}` in isolation")),
            body => Ok(GlobDef::VarDef(GVarDef { defs: self.defs, body })),
        }
    }

    fn abase(&mut self, expr: &FExpr) -> anyhow::Result<Expr> {
        match *expr {
            FExpr::F64(x) => Ok(Expr::Operand(Operand::Const(Const::F64(x)))),
            FExpr::Fun(ref ps, ref b) => Ok(Expr::Operand(Operand::Local(self.abase_fun(ps, b)?))),
            FExpr::App(ref f, ref xs) => Ok(match **f {
                FExpr::Var(ResName { res: Res::Prim(p) }) => match p {
                    Prim::Add => todo!(),
                    Prim::Mul => todo!(),
                },
                _ => todo!(),
            }),
            FExpr::Var(ResName { res: Res::Def(id) }) => {
                let _ = self.cache.fetch_base(id)?;
                Ok(Expr::Operand(Operand::Global(id)))
            }
            FExpr::Var(ResName { res: Res::Local(id) }) =>
                Ok(Expr::Operand(Operand::Local(self.vars.get(id as usize).unwrap_or_else(|| todo!()).clone()))),
            FExpr::Var(ResName { res: Res::Prim(prim) }) =>
                Err(anyhow!("error: can't abase primitive `{prim}` in isolation")),
        }
    }

    fn abase_fun(&mut self, params: &[(PubIdent, LocalId)], body: &FExpr) -> anyhow::Result<LocalId> {
        // TODO: closures. capture free vars etc.

        todo!()
    }

    fn gen_local(&mut self) -> LocalId {
        assert!(self.n_locals < u32::MAX);
        self.n_locals += 1;
        self.n_locals - 1
    }
}

M src/cache.rs => src/cache.rs +11 -13
@@ 17,7 17,7 @@ pub struct Cache {
    module_local_resolved_names: HashMap<FullName, HashMap<PubIdent, Res>>,
    resolveds: HashMap<DefId, RExpr>,
    // desugareds: HashMap<Query, fem::Expr>,
    abaseds: HashMap<DefId, abase::Expr>,
    abaseds: HashMap<DefId, abase::GlobDef>,
    evaluateds: HashMap<DefId, eval::Val>,
}



@@ 36,25 36,23 @@ impl Cache {
        }
    }

    pub fn fetch_evaluated(&mut self, query: Res) -> anyhow::Result<&eval::Val> {
        match query {
            Res::Prim(_prim) => todo!(),
            Res::Def(id) if self.evaluateds.contains_key(&id) => Ok(self.evaluateds.get(&id).unwrap()),
            Res::Def(id) => {
                let base = self.fetch_base(id)?.clone();
                let val = eval::eval(self, &base)?;
                self.evaluateds.insert(id, val);
                Ok(&self.evaluateds[&id])
            }
    pub fn fetch_evaluated(&mut self, def_query: DefId) -> anyhow::Result<&eval::Val> {
        if self.evaluateds.contains_key(&def_query) {
            Ok(self.evaluateds.get(&def_query).unwrap())
        } else {
            let base = self.fetch_base(def_query)?.clone();
            let val = eval::eval_def(self, &base)?;
            self.evaluateds.insert(def_query, val);
            Ok(&self.evaluateds[&def_query])
        }
    }

    pub fn fetch_base(&mut self, def_query: DefId) -> anyhow::Result<&abase::Expr> {
    pub fn fetch_base(&mut self, def_query: DefId) -> anyhow::Result<&abase::GlobDef> {
        Ok(if self.abaseds.contains_key(&def_query) {
            &self.abaseds[&def_query]
        } else {
            let checked: fem::Expr = self.fetch_checked(def_query)?.clone();
            let base = abase::abase(self, &checked)?;
            let base = abase::abase_def(self, &checked)?;
            self.abaseds.insert(def_query, base);
            &self.abaseds[&def_query]
        })

M src/eval.rs => src/eval.rs +3 -7
@@ 1,4 1,4 @@
use crate::abase::Expr;
use crate::abase::{Expr, GlobDef};
use crate::{cache::Cache, name::*};

#[derive(Debug, Clone, PartialEq)]


@@ 23,10 23,6 @@ impl std::ops::Mul for Val {
    }
}

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)?,
    })
pub fn eval_def(cache: &mut Cache, def: &GlobDef) -> anyhow::Result<Val> {
    todo!()
}

M src/fem.rs => src/fem.rs +0 -3
@@ 1,6 1,3 @@
//! Effem core IR




pub use crate::resolve::Expr;

M src/main.rs => src/main.rs +13 -0
@@ 31,6 31,8 @@ fn main() {
    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));
}


@@ 46,6 48,8 @@ mod test {
        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()
    }


@@ 73,4 77,13 @@ mod test {
        ";
        assert_eq!(run_tmp(src).unwrap(), Val::F64(1337.0))
    }

    #[test]
    fn test_app_lambda() {
        let src = "
            (def main (double 21.0))
            (def double (fun [x] (* x 2.0)))
        ";
        assert_eq!(run_tmp(src).unwrap(), Val::F64(42.0))
    }
}

M src/name.rs => src/name.rs +11 -0
@@ 39,10 39,21 @@ pub struct ResName {
#[derive(Debug, Clone, PartialEq)]
pub enum Res {
    Def(DefId),
    Local(LocalId),
    Prim(Prim),
}

impl Res {
    pub fn as_def(&self) -> Option<DefId> {
        match *self {
            Res::Def(id) => Some(id),
            _ => None,
        }
    }
}

pub type DefId = u32;
pub type LocalId = u32;

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Prim {

M src/parse.rs => src/parse.rs +44 -5
@@ 25,6 25,7 @@ pub struct Module {
#[derive(Debug, Clone)]
pub enum Expr {
    F64(f64),
    Fun(Vec<PrivIdent>, Box<Expr>),
    App(Box<Expr>, Vec<Expr>),
    Var(ParsedName),
}


@@ 121,11 122,19 @@ 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> {
    // TODO
    alt3(
        map(lit_float, Expr::F64),
        map(name, Expr::Var),
        map(parens(pair(expr, rest(expr))), |(f, xs)| Expr::App(Box::new(f), xs)),
    alt3(map(lit_float, Expr::F64), map(name, Expr::Var), parens(pexpr))(inp)
}

fn pexpr<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, Expr> {
    alt2(
        |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))))
        },
        map(pair(expr, rest(expr)), |(f, xs)| Expr::App(Box::new(f), xs)),
    )(inp)
}



@@ 199,6 208,22 @@ fn parens<'s: 't, 't, A>(mut f: impl Parser<'s, 't, A>) -> impl Parser<'s, 't, A
    }
}

fn brackets<'s: 't, 't, A>(mut f: impl Parser<'s, 't, A>) -> impl Parser<'s, 't, A> {
    move |inp| {
        inp.enter(
            |tok| match tok {
                Tok::Brackets(ts) => Ok(ts),
                _ => Err("[...]"),
            },
            |inp| {
                let (inp, x) = f(inp)?;
                let (inp, ()) = end(inp)?;
                Ok((inp, x))
            },
        )
    }
}

fn end<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, ()> {
    if let Some(t) = inp.tokens.first() {
        Err(Err {


@@ 320,4 345,18 @@ mod test {
                             && matches!(&xs[..], [Expr::F64(x1), Expr::F64(x2)] if *x1 == 1.0 && *x2 == 2.0)
        )
    }

    #[test]
    fn test_lambda() {
        let src = "(def add (fun [x y] (+ x y)))";
        let m = parse(None, src, &lex(None, src).unwrap()).unwrap();
        assert_matches!(
            &m.defs["add"],
            Expr::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))
        )
    }
}

M src/resolve.rs => src/resolve.rs +48 -23
@@ 1,6 1,7 @@
use crate::{cache::*, name::*, parse};
use anyhow::{anyhow, Result};
use std::borrow::Cow;
use std::collections::HashMap;

// https://rustc-dev-guide.rust-lang.org/name-resolution.html



@@ 11,6 12,7 @@ use std::borrow::Cow;
#[derive(Debug, Clone, PartialEq)]
pub enum Expr {
    F64(f64),
    Fun(Vec<(PubIdent, LocalId)>, Box<Expr>),
    App(Box<Expr>, Vec<Expr>),
    Var(ResName),
}


@@ 19,41 21,35 @@ pub fn resolve_def(cache: &mut Cache, def_name: &FullName, def_body: &parse::Exp
    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 };
    let mut resolver = Resolver::new(cache, 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 {
        cache.fetch_global_resolved_name(&FullName(name))
    } else {
        match name.split_first() {
            Some((first, [])) => cache
                .fetch_module_local_resolved_names(occuring_in_module)?
                .get(&PubIdent(first.clone()))
                .cloned()
                .ok_or_else(|| {
                    anyhow!("error: couldn' find definition `{}` in module `{}`", first, occuring_in_module)
                }),
            Some((_first, _rest)) => todo!(),
            None => todo!(), // Err
        }
    }
}

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

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 resolve(&mut self, e: &parse::Expr) -> Result<Expr> {
        use parse::Expr::*;
        Ok(match *e {
            F64(x) => Expr::F64(x),
            Fun(ref params, ref 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());
                let rbody = self.resolve(body)?;
                self.scopes.pop();
                Expr::Fun(rparams, Box::new(rbody))
            }
            App(ref f, ref args) =>
                Expr::App(Box::new(self.resolve(f)?), args.iter().map(|a| self.resolve(a)).collect::<Result<_>>()?),
            Var(ref v) => {


@@ 62,8 58,37 @@ impl<'c> Resolver<'c> {
                // - `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)? })
                Expr::Var(ResName { res: self.resolve_parsed_name(v)? })
            }
        })
    }

    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<_>>();
        if pname.rooted {
            self.cache.fetch_global_resolved_name(&FullName(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
            }
        }
    }

    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()))
    }

    fn gen_local_id(&mut self) -> LocalId {
        assert!(self.local_count < u32::MAX);
        self.local_count += 1;
        self.local_count - 1
    }
}