From 43ac1cc82978d06d0bd9efa5755c1d3eae6d28e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20R=C3=B6ger?= Date: Tue, 5 Nov 2024 17:13:53 +0100 Subject: [PATCH] add lips expression evaluation --- src/lisp/environment.rs | 96 +++++++++++++++ src/lisp/expression.rs | 265 ++++++++++++++++++++++++++++++++++++++++ src/lisp/mod.rs | 2 + src/main.rs | 36 ++++++ src/parser/tokenizer.rs | 2 + 5 files changed, 401 insertions(+) create mode 100644 src/lisp/environment.rs create mode 100644 src/lisp/expression.rs create mode 100644 src/lisp/mod.rs diff --git a/src/lisp/environment.rs b/src/lisp/environment.rs new file mode 100644 index 0000000..913eff6 --- /dev/null +++ b/src/lisp/environment.rs @@ -0,0 +1,96 @@ +use super::expression::Expression; +use std::collections::HashMap; + +#[derive(PartialEq, Clone, Debug)] +/// A Environment is a stack of `EnvironmentLayer`s. Each `EnvironmentLayer` is a mapping from +/// variable names to their values. +pub struct Environment<'a> { + /// The current mapping. + layer: EnvironmentLayer, + /// The outer _fallback_ mapping. + outer: Option<&'a Environment<'a>>, +} + +#[derive(PartialEq, Clone, Debug)] +/// A concrete EnvironmentLayer, containing a mapping from symbol names to Expressions. +pub struct EnvironmentLayer { + symbols: HashMap, +} + +impl EnvironmentLayer { + /// Construct an empty `EnvironmentLayer`. + pub fn new() -> Self { + EnvironmentLayer { + symbols: HashMap::new(), + } + } + + /// Set a value in the `EnvironmentLayer`. + pub fn set(&mut self, key: String, value: Expression) { + self.symbols.insert(key, value); + } + + /// Get a value in the `EnvironmentLayer`. + pub fn get(&self, key: &str) -> Option<&Expression> { + self.symbols.get(key) + } +} + +impl<'a> Environment<'a> { + /// Construct an empty `Environment`. + pub fn new() -> Self { + Environment { + layer: EnvironmentLayer::new(), + outer: None, + } + } + + /// Construct an `Environment` from a `EnvironmentLayer` with no outer `Environment`. + pub fn from_layer(layer: EnvironmentLayer) -> Self { + Environment { layer, outer: None } + } + + /// Construct a new `Environment` with `self` as the outer `Environment`. + pub fn mk_inner(&self) -> Environment { + Environment { + layer: EnvironmentLayer::new(), + outer: Some(self), + } + } + + /// Construct a new `Environment` with `self` as the outer `Environment` and `layer` as the + pub fn overlay(&'a self, layer: EnvironmentLayer) -> Environment<'a> { + Environment { + layer, + outer: Some(&self), + } + } + + /// Get a value from the `Environment`. + pub fn get(&self, key: &str) -> Option<&Expression> { + if let Some(e) = self.layer.get(key) { + Some(e) + } else { + self.outer?.get(key) + } + } + + /// Set a value in the current `EnvironmentLayer`. + pub fn set(&mut self, key: String, value: Expression) { + self.layer.set(key, value); + } +} + +#[test] +fn test_environment() { + let mut env = Environment::new(); + env.set("a".to_string(), Expression::Integer(1)); + env.set("b".to_string(), Expression::Integer(2)); + let mut inner = env.mk_inner(); + inner.set("a".to_string(), Expression::Integer(3)); + assert_eq!(inner.get("a"), Some(&Expression::Integer(3))); + assert_eq!(inner.get("b"), Some(&Expression::Integer(2))); + assert_eq!(env.get("a"), Some(&Expression::Integer(1))); + assert_eq!(env.get("b"), Some(&Expression::Integer(2))); + assert_eq!(env.get("c"), None); +} diff --git a/src/lisp/expression.rs b/src/lisp/expression.rs new file mode 100644 index 0000000..0cdff2a --- /dev/null +++ b/src/lisp/expression.rs @@ -0,0 +1,265 @@ +use super::environment::{Environment, EnvironmentLayer}; + +#[derive(Clone, Debug, PartialEq, PartialOrd)] +/// A sum type of all possible lisp expressions. +pub enum Expression { + /// The classic lisp cons cell aka (a . b) used to construct expressions. + Cell(Box, Box), + /// A function expression pointing to native code. + Function(fn(&Environment, Expression) -> Result), + /// A anonymous function expression consisting of bound symbols and a body expression. + AnonymousFunction { + argument_symbols: Vec, + body: Box, + }, + /// A Quoted expression. + Quote(Box), + /// A symbol. + Symbol(String), + /// Integer values. + Integer(i64), + /// Float values. + Float(f64), + /// String values. + String(String), + /// True + True, + /// Nil + Nil, +} + +#[derive(Debug)] +/// All possible evaluation errors +pub enum EvalError { + SymbolNotBound(String), + NotAFunction(Expression), + ArgumentError(String), + TypeError(String), + NotASymbol(Expression), +} + +/// A CellIterator is a convenience struct to iterate a linked cons list. +/// The Iterator returns Ok(Expression) as long, as there are elements in the list. +/// Err(EvalError) is returned when the right side of a cons cell is not another cons cell or nil. +pub struct CellIterator { + expr: Option, +} + +impl CellIterator { + pub fn new(expr: Expression) -> CellIterator { + CellIterator { expr: Some(expr) } + } +} + +impl Iterator for CellIterator { + type Item = Result; + fn next(&mut self) -> Option { + if let Some(expr) = self.expr.take() { + match expr { + Expression::Cell(head, tail) => { + self.expr = Some(*tail); + return Some(Ok(*head)); + } + Expression::Nil => { + return None; + } + _ => { + return Some(Err(EvalError::TypeError( + "Expected a cell or nil".to_string(), + ))); + } + } + } else { + None + } + } +} + +impl From Result> for Expression { + fn from(f: fn(&Environment, Expression) -> Result) -> Self { + Expression::Function(f) + } +} + +impl TryInto for Expression { + type Error = EvalError; + fn try_into(self) -> Result { + match self { + Expression::Integer(i) => Ok(i), + _ => Err(EvalError::TypeError( + "Expression is not an Integer".to_string(), + )), + } + } +} + +impl From> for Expression { + fn from(mut value: Vec) -> Self { + let mut current = Expression::Nil; + + for e in value.iter_mut().rev() { + current = Expression::Cell(Box::new(e.to_owned()), Box::new(current)); + } + + current + } +} + +impl TryInto> for Expression { + type Error = EvalError; + + fn try_into(self) -> Result, Self::Error> { + CellIterator::new(self).collect() + } +} + +impl TryInto<[Expression; N]> for Expression { + type Error = EvalError; + + fn try_into(self) -> Result<[Expression; N], Self::Error> { + let buf: Vec = self.try_into()?; + let n = buf.len(); + + buf.try_into() + .map_err(|_| EvalError::ArgumentError(format!("Expected {} arguments, got {}", N, n))) + } +} + +impl TryInto<(Expression, Expression)> for Expression { + type Error = EvalError; + fn try_into(self) -> Result<(Expression, Expression), Self::Error> { + match self { + Expression::Cell(a, b) => Ok((*a, *b)), + _ => Err(EvalError::TypeError( + "Expression must be a Cell".to_string(), + )), + } + } +} + +/// Dispatch an anonymous function call. Evaluates `body` in `env`, binding `args` to `argument_symbols` +fn dispatch_anonymous_function( + env: &Environment, + argument_symbols: Vec, + body: Expression, + args: Expression, +) -> Result { + let mut args: Vec = args.try_into()?; + + let mut overlay = EnvironmentLayer::new(); + + if args.len() != argument_symbols.len() { + return Err(EvalError::ArgumentError(format!( + "Exprected {} arguments, got {}", + argument_symbols.len(), + args.len() + ))); + } + + for (arg, symbol) in args.iter_mut().zip(argument_symbols.iter()) { + overlay.set(symbol.to_owned(), arg.to_owned()); + } + + eval(&env.overlay(overlay), body) +} + +/// Evaluate an expression inside an environment +fn eval(env: &Environment, expr: Expression) -> Result { + match expr { + Expression::Cell(lhs, rhs) => match eval(env, *lhs)? { + Expression::Function(f) => f(env, *rhs), + Expression::AnonymousFunction { + argument_symbols, + body, + } => dispatch_anonymous_function(env, argument_symbols, *body, *rhs), + a => Err(EvalError::NotAFunction(a)), + }, + Expression::Quote(e) => Ok(*e), + Expression::Symbol(s) => env.get(&s).ok_or(EvalError::SymbolNotBound(s)).cloned(), + x => Ok(x), + } +} + +//==================Prelude evaluation environment============================== + +fn prelude_add(env: &Environment, expr: Expression) -> Result { + let [a, b] = expr.try_into()?; + + let a: i64 = eval(env, a)?.try_into()?; + let b: i64 = eval(env, b)?.try_into()?; + + Ok(Expression::Integer(a + b)) +} + +fn prelude_lambda(_env: &Environment, expr: Expression) -> Result { + let [args, body] = expr.try_into()?; + let mut arg_exprs: Vec = args.try_into()?; + let argument_symbols: Vec = arg_exprs + .iter_mut() + .map(|a| match a { + Expression::Symbol(s) => Ok(s.to_owned()), + x => Err(EvalError::NotASymbol(x.to_owned())), + }) + .collect::, EvalError>>()?; + Ok(Expression::AnonymousFunction { + argument_symbols, + body: Box::new(body), + }) +} + +fn prelude_if(env: &Environment, expr: Expression) -> Result { + let [predicate, e_then, e_else] = expr.try_into()?; + + match eval(env, predicate)? { + Expression::Nil => eval(env, e_else), + _ => eval(env, e_then), + } +} + +fn prelude_eq(env: &Environment, expr: Expression) -> Result { + let [a, b] = expr.try_into()?; + let a = eval(env, a)?; + let b = eval(env, b)?; + + if a == b { + Ok(Expression::True) + } else { + Ok(Expression::Nil) + } +} + +fn prelude_lt(env: &Environment, expr: Expression) -> Result { + let [a, b] = expr.try_into()?; + let a = eval(env, a)?; + let b = eval(env, b)?; + + if a < b { + Ok(Expression::True) + } else { + Ok(Expression::Nil) + } +} + +fn prelude_gt(env: &Environment, expr: Expression) -> Result { + let [a, b] = expr.try_into()?; + let a = eval(env, a)?; + let b = eval(env, b)?; + + if a > b { + Ok(Expression::True) + } else { + Ok(Expression::Nil) + } +} + +pub fn eval_prelude(expr: Expression) -> Result { + let mut prelude = Environment::new(); + prelude.set("add".to_string(), Expression::Function(prelude_add)); + prelude.set("lambda".to_string(), Expression::Function(prelude_lambda)); + prelude.set("if".to_string(), Expression::Function(prelude_if)); + prelude.set("==".to_string(), Expression::Function(prelude_eq)); + prelude.set("<".to_string(), Expression::Function(prelude_lt)); + prelude.set(">".to_string(), Expression::Function(prelude_gt)); + + eval(&prelude, expr) +} diff --git a/src/lisp/mod.rs b/src/lisp/mod.rs new file mode 100644 index 0000000..44d6dbe --- /dev/null +++ b/src/lisp/mod.rs @@ -0,0 +1,2 @@ +pub mod environment; +pub mod expression; diff --git a/src/main.rs b/src/main.rs index 456a5eb..341aae7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,6 @@ +mod lisp; mod parser; +use lisp::expression::{eval_prelude, Expression}; fn main() { let mut test = "(add 10 (sub 1.1 200.5)) (concat-if true \"true\" 'nil (a . b))".chars(); @@ -8,4 +10,38 @@ fn main() { while let Some(tk) = tkns.next() { println!("{:?}", tk); } + + let expr: Expression = vec![ + vec![ + Expression::Symbol("lambda".to_string()), + vec![ + Expression::Symbol("x".to_string()), + Expression::Symbol("y".to_string()), + ] + .into(), + vec![ + Expression::Symbol("if".to_string()), + vec![ + Expression::Symbol("==".to_string()), + Expression::Symbol("x".to_string()), + Expression::Integer(5), + ] + .into(), + vec![ + Expression::Symbol("add".to_string()), + Expression::Symbol("x".to_string()), + Expression::Symbol("y".to_string()), + ] + .into(), + Expression::String("x is not 5".to_string()), + ] + .into(), + ] + .into(), + Expression::Integer(4), + Expression::Integer(7), + ] + .into(); + + println!("{:?} evaluates to {:?}", expr.clone(), eval_prelude(expr)); } diff --git a/src/parser/tokenizer.rs b/src/parser/tokenizer.rs index 8416817..808bdee 100644 --- a/src/parser/tokenizer.rs +++ b/src/parser/tokenizer.rs @@ -159,6 +159,8 @@ where TokenStream::new(input) } +// ================== Scanner definitions ================== // + fn scan_par_open(reader: &mut StagingReader) -> Option where I: Iterator,