~jojo/effem

6c55ea0a62cdec6fda73961301314506f468efbd — JoJo 11 months ago 7624f62
resolve unrooted names (defs in same module & prelude (+, *))
5 files changed, 111 insertions(+), 40 deletions(-)

M src/abase.rs
M src/cache.rs
M src/main.rs
M src/name.rs
M src/resolve.rs
M src/abase.rs => src/abase.rs +9 -6
@@ 3,6 3,7 @@
use crate::cache::Cache;
use crate::fem::Expr as FExpr;
use crate::name::*;
use anyhow::anyhow;

#[derive(Clone)]
pub enum Expr {


@@ 12,15 13,17 @@ pub enum Expr {
}

pub fn abase(cache: &mut Cache, expr: &FExpr) -> anyhow::Result<Expr> {
    Ok(match *expr {
        FExpr::F64(x) => Expr::F64(x),
        FExpr::App(ref f, ref xs) => match **f {
    match *expr {
        FExpr::F64(x) => Ok(Expr::F64(x)),
        FExpr::App(ref f, ref xs) => Ok(match **f {
            FExpr::Var(ResName { res: Res::Prim(p) }) => match p {
                Prim::Add => Expr::Add(Box::new(abase(cache, &xs[0])?), Box::new(abase(cache, &xs[1])?)),
                Prim::Mul => Expr::Mul(Box::new(abase(cache, &xs[0])?), Box::new(abase(cache, &xs[1])?)),
            },
            _ => todo!(),
        },
        FExpr::Var(_) => todo!(),
    })
        }),
        FExpr::Var(ResName { res: Res::Def(id) }) => cache.fetch_base(id).cloned(),
        FExpr::Var(ResName { res: Res::Prim(prim) }) =>
            Err(anyhow!("error: can't abase primitive `{prim}` in isolation")),
    }
}

M src/cache.rs => src/cache.rs +56 -20
@@ 4,7 4,7 @@ use crate::resolve::{self, Expr as RExpr};
use crate::{abase, eval, fem, lex};
use anyhow::{anyhow, Context};
use std::borrow::Borrow;
use std::collections::HashMap;
use std::collections::hash_map::{Entry, HashMap};
use std::path::{Path, PathBuf};
use std::rc::Rc;



@@ 14,6 14,7 @@ pub struct Cache {
    parsed_modules: HashMap<FullName, PModule>,
    resolved_names: HashMap<FullName, DefId>,
    resolved_names_rev: HashMap<DefId, FullName>,
    module_local_resolved_names: HashMap<FullName, HashMap<PubIdent, Res>>,
    resolveds: HashMap<DefId, RExpr>,
    // desugareds: HashMap<Query, fem::Expr>,
    abaseds: HashMap<DefId, abase::Expr>,


@@ 28,24 29,27 @@ impl Cache {
            parsed_modules: HashMap::new(),
            resolved_names: HashMap::new(),
            resolved_names_rev: HashMap::new(),
            module_local_resolved_names: HashMap::new(),
            resolveds: HashMap::new(),
            abaseds: HashMap::new(),
            evaluateds: HashMap::new(),
        }
    }

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

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


@@ 98,13 102,45 @@ impl Cache {
        Ok((name, parsed))
    }

    pub fn fetch_module_local_resolved_names(&mut self, _module: &FullName) -> anyhow::Result<&HashMap<PubIdent, Res>> {
        todo!()
    pub fn fetch_module_local_resolved_names(
        &mut self,
        module_name: &FullName,
    ) -> anyhow::Result<&HashMap<PubIdent, Res>> {
        static PRELUDE: &[(&str, &[&str])] = &[("+", &["prim", "+"]), ("*", &["prim", "*"])];
        fn prelude_owned() -> impl Iterator<Item = (PubIdent, FullName)> {
            PRELUDE.iter().map(|(ident, name)| {
                (PubIdent(ident.to_string()), FullName(name.iter().map(|x| x.to_string()).collect()))
            })
        }

        if self.module_local_resolved_names.contains_key(module_name) {
            Ok(&self.module_local_resolved_names[module_name])
        } else {
            let def_names: Vec<PubIdent> = self.fetch_parsed_module(module_name)?.defs.keys().cloned().collect();
            let ids = def_names
                .into_iter()
                .map(|k| (k.clone(), module_name.with(k)))
                .chain(prelude_owned())
                .map(|(def_ident, name)| self.fetch_global_resolved_name(&name).map(|id| (def_ident, id)))
                .collect::<Result<_, _>>()?;
            self.module_local_resolved_names.insert(module_name.clone(), ids);
            Ok(&self.module_local_resolved_names[module_name])
        }
    }

    pub fn fetch_global_resolved_name(&mut self, name: &FullName) -> anyhow::Result<DefId> {
        if let Some(&res) = self.resolved_names.get(name) {
            Ok(res)
    pub fn fetch_global_resolved_name(&mut self, name: &FullName) -> anyhow::Result<Res> {
        assert!(name.0.len() > 1);
        if name.0[0] == "prim" {
            if name.0.len() != 2 {
                todo!() // Err
            }
            match name.0[1].as_str() {
                "+" => Ok(Res::Prim(Prim::Add)),
                "*" => Ok(Res::Prim(Prim::Mul)),
                _ => todo!(), // Err
            }
        } else if let Some(&res) = self.resolved_names.get(name) {
            Ok(Res::Def(res))
        } else {
            let (def_ident, module_name) = name.0.split_last().expect("ice: query should be nonempty");
            let module = self.fetch_parsed_module(&FullName(module_name.to_vec()))?;


@@ 113,7 149,7 @@ impl Cache {
                let id = self.resolved_names.len() as u32;
                self.resolved_names.insert(name.clone(), id);
                self.resolved_names_rev.insert(id, name.clone());
                Ok(id)
                Ok(Res::Def(id))
            } else {
                Err(anyhow!("item {def_ident:?} is not defined in module {module_name:?}"))
            }


@@ 122,22 158,22 @@ impl Cache {

    pub fn fetch_parsed_module(&mut self, module_name: &FullName) -> anyhow::Result<&PModule> {
        let (path, src) = self.fetch_source(module_name)?;
        let tokens = lex::lex(Some(path.into()), &src)?;
        let parsed = parse::parse(Some(path.into()), &src, &tokens)?;
        let tokens = lex::lex(Some(path.into()), src)?;
        let parsed = parse::parse(Some(path.into()), src, &tokens)?;
        self.parsed_modules.insert(module_name.clone(), parsed);
        Ok(&self.parsed_modules[module_name])
    }

    pub fn fetch_source(&mut self, module_name: &FullName) -> anyhow::Result<(&Path, &str)> {
        if self.sources.contains_key(module_name) {
            let &(ref path, ref src) = &self.sources[module_name];
            let (path, src) = &self.sources[module_name];
            Ok((path, src))
        } else {
            let path = find_module(&self.root_dir, module_name)?;
            let src =
                std::fs::read_to_string(&path).with_context(|| format!("ice: Failed to read source from {path:?}"))?;
            self.sources.insert(module_name.clone(), (path, src));
            let &(ref path, ref src) = &self.sources[module_name];
            let (path, src) = &self.sources[module_name];
            Ok((path, src))
        }
    }

M src/main.rs => src/main.rs +10 -0
@@ 2,6 2,7 @@

#![feature(entry_insert, is_ascii_octdigit, trait_alias, assert_matches)]
#![allow(unused_imports)]
#![allow(clippy::map_entry)]

mod abase;
mod cache;


@@ 63,4 64,13 @@ mod test {
    fn test_main_arithm2() {
        assert_eq!(run_tmp("(def main (+.prim. (*.prim. 13.0 100.0) 37.0))").unwrap(), Val::F64(1337.0))
    }

    #[test]
    fn test_main_arithm_var() {
        let src = "
            (def x 1300.0)
            (def main (+ x 37.0))
        ";
        assert_eq!(run_tmp(src).unwrap(), Val::F64(1337.0))
    }
}

M src/name.rs => src/name.rs +31 -0
@@ 50,9 50,40 @@ pub enum Prim {
    Mul = 1,
}

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

/// E.g. ["dep", "std", "str", "Str", "new"] represents the path "new.Str.str.std.dep."
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct FullName(pub Vec<String>);

impl FullName {
    pub fn with(&self, element: PubIdent) -> Self {
        let mut name = self.clone();
        name.0.push(element.0);
        name
    }
}

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

// The span of an identifier in the original source code string where the ident occured. Bookkeeping of source string
// must be handled manually.
//

M src/resolve.rs => src/resolve.rs +5 -14
@@ 1,5 1,5 @@
use crate::{cache::*, name::*, parse};
use anyhow::Result;
use anyhow::{anyhow, Result};
use std::borrow::Cow;

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


@@ 28,25 28,16 @@ pub fn resolve_parsed_name(cache: &mut Cache, occuring_in_module: &FullName, pna
    let (_, src) = cache.fetch_source(occuring_in_module)?;
    let name = pname.segments.iter().map(|seg| seg.in_source(src).to_owned()).collect::<Vec<_>>();
    if pname.rooted {
        if name[0] == "prim" {
            if name.len() != 2 {
                todo!() // Err
            }
            match name[1].as_str() {
                "+" => Ok(Res::Prim(Prim::Add)),
                "*" => Ok(Res::Prim(Prim::Mul)),
                _ => todo!(), // Err
            }
        } else {
            cache.fetch_global_resolved_name(&FullName(name)).map(Res::Def)
        }
        cache.fetch_global_resolved_name(&FullName(name))
    } else {
        match name.split_first() {
            Some((first, [])) => cache
                .fetch_module_local_resolved_names(occuring_in_module)?
                .get(&PubIdent(first.clone()))
                .cloned()
                .ok_or_else(|| todo!()),
                .ok_or_else(|| {
                    anyhow!("error: couldn' find definition `{}` in module `{}`", first, occuring_in_module)
                }),
            Some((_first, _rest)) => todo!(),
            None => todo!(), // Err
        }