handball/src/eval.rs

114 lines
3.3 KiB
Rust

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<Value, RTError> {
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::<Result<Vec<_>, _>>()?;
// 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<T>(s: String) -> Result<T, RTError> {
Err(RTError(s))
}
pub(crate) enum Callable<'a> {
Func(prims::Func),
Lambda(&'a ast::Lambda),
}
pub(crate) struct Env<'a> {
pub(crate) vars: HashMap<String, Value>,
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<Value> {
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,
}
}