add lisp prelude_let

This commit is contained in:
Jonas Röger 2024-11-05 17:56:55 +01:00
parent 43ac1cc829
commit d604dcecc5
Signed by: jonas
GPG Key ID: 4000EB35E1AE0F07
3 changed files with 42 additions and 3 deletions

View File

@ -36,6 +36,12 @@ impl EnvironmentLayer {
} }
} }
impl From<HashMap<String, Expression>> for EnvironmentLayer {
fn from(map: HashMap<String, Expression>) -> Self {
EnvironmentLayer { symbols: map }
}
}
impl<'a> Environment<'a> { impl<'a> Environment<'a> {
/// Construct an empty `Environment`. /// Construct an empty `Environment`.
pub fn new() -> Self { pub fn new() -> Self {

View File

@ -1,4 +1,5 @@
use super::environment::{Environment, EnvironmentLayer}; use super::environment::{Environment, EnvironmentLayer};
use std::collections::HashMap;
#[derive(Clone, Debug, PartialEq, PartialOrd)] #[derive(Clone, Debug, PartialEq, PartialOrd)]
/// A sum type of all possible lisp expressions. /// A sum type of all possible lisp expressions.
@ -175,7 +176,10 @@ fn eval(env: &Environment, expr: Expression) -> Result<Expression, EvalError> {
a => Err(EvalError::NotAFunction(a)), a => Err(EvalError::NotAFunction(a)),
}, },
Expression::Quote(e) => Ok(*e), Expression::Quote(e) => Ok(*e),
Expression::Symbol(s) => env.get(&s).ok_or(EvalError::SymbolNotBound(s)).cloned(), Expression::Symbol(s) => eval(
env,
env.get(&s).ok_or(EvalError::SymbolNotBound(s)).cloned()?,
),
x => Ok(x), x => Ok(x),
} }
} }
@ -207,6 +211,25 @@ fn prelude_lambda(_env: &Environment, expr: Expression) -> Result<Expression, Ev
}) })
} }
fn prelude_let(env: &Environment, expr: Expression) -> Result<Expression, EvalError> {
let [bindings, body] = expr.try_into()?;
let bindings = CellIterator::new(bindings)
.map(|e| {
let (s, e) = e?.try_into()?;
if let Expression::Symbol(s) = s {
Ok((s, eval(env, e)?))
} else {
Err(EvalError::ArgumentError(
"Let bindings must be an alist with elements (symbol . expr)".to_string(),
))
}
})
.collect::<Result<HashMap<String, Expression>, EvalError>>()?;
eval(&env.overlay(bindings.into()), body)
}
fn prelude_if(env: &Environment, expr: Expression) -> Result<Expression, EvalError> { fn prelude_if(env: &Environment, expr: Expression) -> Result<Expression, EvalError> {
let [predicate, e_then, e_else] = expr.try_into()?; let [predicate, e_then, e_else] = expr.try_into()?;
@ -260,6 +283,7 @@ pub fn eval_prelude(expr: Expression) -> Result<Expression, EvalError> {
prelude.set("==".to_string(), Expression::Function(prelude_eq)); prelude.set("==".to_string(), Expression::Function(prelude_eq));
prelude.set("<".to_string(), Expression::Function(prelude_lt)); prelude.set("<".to_string(), Expression::Function(prelude_lt));
prelude.set(">".to_string(), Expression::Function(prelude_gt)); prelude.set(">".to_string(), Expression::Function(prelude_gt));
prelude.set("let".to_string(), Expression::Function(prelude_let));
eval(&prelude, expr) eval(&prelude, expr)
} }

View File

@ -38,8 +38,17 @@ fn main() {
.into(), .into(),
] ]
.into(), .into(),
Expression::Integer(4), Expression::Integer(5),
Expression::Integer(7), vec![
Expression::Symbol("let".to_string()),
vec![Expression::Cell(
Box::new(Expression::Symbol("y".to_string())),
Box::new(Expression::Integer(7)),
)]
.into(),
Expression::Symbol("y".to_string()),
]
.into(),
] ]
.into(); .into();