use std::sync::Arc;
use sim_kernel::{
Args, Callable, ClassRef, Cx, Env, Error, Expr, Object, ObjectCompat, RawArgs, Result, Symbol,
Value,
};
#[derive(Clone, Copy)]
pub struct LetForm;
impl LetForm {
pub fn symbol() -> Symbol {
Symbol::new("let")
}
}
fn parse_clause(clause: Expr) -> Result<(Symbol, Expr)> {
let bad = || Error::Eval("let binding must be (name init)".to_owned());
match clause {
Expr::Call { operator, args } => {
let Expr::Symbol(name) = *operator else {
return Err(bad());
};
let mut args = args.into_iter();
let (Some(init), None) = (args.next(), args.next()) else {
return Err(bad());
};
Ok((name, init))
}
Expr::List(items) | Expr::Vector(items) => {
let mut items = items.into_iter();
match (items.next(), items.next(), items.next()) {
(Some(Expr::Symbol(name)), Some(init), None) => Ok((name, init)),
_ => Err(bad()),
}
}
_ => Err(bad()),
}
}
fn parse_bindings(bindings: Expr) -> Result<Vec<(Symbol, Expr)>> {
let clauses = match bindings {
Expr::List(items) | Expr::Vector(items) => items,
Expr::Nil => Vec::new(),
_ => return Err(Error::Eval("let bindings must be a list".to_owned())),
};
clauses.into_iter().map(parse_clause).collect()
}
impl Object for LetForm {
fn display(&self, _cx: &mut Cx) -> Result<String> {
Ok("#<special-form let>".to_owned())
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl ObjectCompat for LetForm {
fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
cx.resolve_class(&Symbol::qualified("core", "Function"))
}
fn as_callable(&self) -> Option<&dyn Callable> {
Some(self)
}
}
impl Callable for LetForm {
fn call(&self, _cx: &mut Cx, _args: Args) -> Result<Value> {
Err(Error::Eval(
"let is a special form and cannot be applied to evaluated arguments".to_owned(),
))
}
fn call_exprs(&self, cx: &mut Cx, args: RawArgs) -> Result<Value> {
let mut exprs = args.into_exprs().into_iter();
let Some(bindings) = exprs.next() else {
return Err(Error::Eval(
"let expects (let (bindings...) body...)".to_owned(),
));
};
let body: Vec<Expr> = exprs.collect();
let clauses = parse_bindings(bindings)?;
let mut bound = Vec::with_capacity(clauses.len());
for (name, init) in clauses {
let value = cx.eval_expr(init)?;
bound.push((name, value));
}
let child = Env::child(Arc::new(cx.env().clone()));
cx.with_env(child, |cx| {
for (name, value) in bound {
cx.env_mut().define(name, value);
}
let mut last = cx.factory().nil()?;
for expr in body {
last = cx.eval_expr(expr)?;
}
Ok(last)
})
}
}