use std::assert_matches::assert_matches; use std::collections::HashMap; use crate::value::Value; use crate::{ast, prims}; pub(crate) fn eval(t: &ast::Tree, env: &mut Env) -> Result { Ok(match t { ast::Tree::Leaf(l) => match l { ast::Literal::Sym(s) => match env.lookup(s) { Some(v) => v, None => return err(format!("Undefined variable `{}`", s)), }, ast::Literal::Num(v) => Value::Num(*v), ast::Literal::Bool(b) => Value::Bool(*b), }, ast::Tree::Lambda(l) => Value::Lambda(l.clone()), ast::Tree::Branch(args) => { let Some(fun) = args.get(0) else { return err("No argument given".to_owned()) }; let fun = eval(fun, env)?; let fun = fun.as_func()?; let args = args .iter() .skip(1) .map(|a| eval(a, env)) .collect::, _>>()?; // return fun(&args); match fun { Callable::Func(f) => f(&args)?, Callable::Lambda(l) => { if l.0.len() == args.len() { let mut env = env.child(); for (x, y) in l.0.iter().zip(args) { env.define(x.clone(), y) } let [main @ .., tail] = &l.1[..] else { unreachable!("Body has 1+ element by parser") }; for i in main { eval(i, &mut env)?; } eval(tail, &mut env)? } else { return err(format!("Need {} args, got {}", l.0.len(), args.len())); } } } } ast::Tree::Define(name, to) => { let val = eval(to, env)?; env.define(name.to_owned(), val); Value::Trap } ast::Tree::If(box [cond, tcase, fcase]) => { let b = eval(cond, env)?.as_bool()?; let body = if b { tcase } else { fcase }; eval(body, env)? } }) } #[derive(Debug)] crate struct RTError(crate String); pub(crate) fn err(s: String) -> Result { Err(RTError(s)) } pub(crate) enum Callable<'a> { Func(prims::Func), Lambda(&'a ast::Lambda), } pub(crate) struct Env<'a> { pub(crate) vars: HashMap, pub(crate) enclosing: Option<&'a Env<'a>>, } impl<'a> Env<'a> { pub(crate) fn child(&'a self) -> Env<'a> { Env { vars: HashMap::new(), enclosing: Some(self), } } pub(crate) fn lookup(&self, s: &str) -> Option { if let Some(v) = self.vars.get(s) { Some(v.clone()) } else if let Some(parent) = self.enclosing { parent.lookup(s) } else { None } } pub(crate) fn define(&mut self, name: String, val: Value) { self.vars.insert(name, val); } } pub(crate) fn default_env() -> Env<'static> { let mut vars = HashMap::new(); for (name, fun) in prims::prims() { assert_matches!(vars.insert((*name).to_owned(), Value::Func(*fun)), None) } Env { vars, enclosing: None, } }