add lips expression evaluation

This commit is contained in:
Jonas Röger 2024-11-05 17:13:53 +01:00
parent 63d84b1edb
commit 43ac1cc829
Signed by: jonas
GPG Key ID: 4000EB35E1AE0F07
5 changed files with 401 additions and 0 deletions

96
src/lisp/environment.rs Normal file
View File

@ -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<String, Expression>,
}
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);
}

265
src/lisp/expression.rs Normal file
View File

@ -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<Expression>, Box<Expression>),
/// A function expression pointing to native code.
Function(fn(&Environment, Expression) -> Result<Expression, EvalError>),
/// A anonymous function expression consisting of bound symbols and a body expression.
AnonymousFunction {
argument_symbols: Vec<String>,
body: Box<Expression>,
},
/// A Quoted expression.
Quote(Box<Expression>),
/// 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<Expression>,
}
impl CellIterator {
pub fn new(expr: Expression) -> CellIterator {
CellIterator { expr: Some(expr) }
}
}
impl Iterator for CellIterator {
type Item = Result<Expression, EvalError>;
fn next(&mut self) -> Option<Self::Item> {
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<fn(&Environment, Expression) -> Result<Expression, EvalError>> for Expression {
fn from(f: fn(&Environment, Expression) -> Result<Expression, EvalError>) -> Self {
Expression::Function(f)
}
}
impl TryInto<i64> for Expression {
type Error = EvalError;
fn try_into(self) -> Result<i64, Self::Error> {
match self {
Expression::Integer(i) => Ok(i),
_ => Err(EvalError::TypeError(
"Expression is not an Integer".to_string(),
)),
}
}
}
impl From<Vec<Expression>> for Expression {
fn from(mut value: Vec<Expression>) -> 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<Vec<Expression>> for Expression {
type Error = EvalError;
fn try_into(self) -> Result<Vec<Expression>, Self::Error> {
CellIterator::new(self).collect()
}
}
impl<const N: usize> TryInto<[Expression; N]> for Expression {
type Error = EvalError;
fn try_into(self) -> Result<[Expression; N], Self::Error> {
let buf: Vec<Expression> = 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<String>,
body: Expression,
args: Expression,
) -> Result<Expression, EvalError> {
let mut args: Vec<Expression> = 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<Expression, EvalError> {
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<Expression, EvalError> {
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<Expression, EvalError> {
let [args, body] = expr.try_into()?;
let mut arg_exprs: Vec<Expression> = args.try_into()?;
let argument_symbols: Vec<String> = arg_exprs
.iter_mut()
.map(|a| match a {
Expression::Symbol(s) => Ok(s.to_owned()),
x => Err(EvalError::NotASymbol(x.to_owned())),
})
.collect::<Result<Vec<String>, EvalError>>()?;
Ok(Expression::AnonymousFunction {
argument_symbols,
body: Box::new(body),
})
}
fn prelude_if(env: &Environment, expr: Expression) -> Result<Expression, EvalError> {
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<Expression, EvalError> {
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<Expression, EvalError> {
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<Expression, EvalError> {
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<Expression, EvalError> {
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)
}

2
src/lisp/mod.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod environment;
pub mod expression;

View File

@ -1,4 +1,6 @@
mod lisp;
mod parser; mod parser;
use lisp::expression::{eval_prelude, Expression};
fn main() { fn main() {
let mut test = "(add 10 (sub 1.1 200.5)) (concat-if true \"true\" 'nil (a . b))".chars(); 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() { while let Some(tk) = tkns.next() {
println!("{:?}", tk); 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));
} }

View File

@ -159,6 +159,8 @@ where
TokenStream::new(input) TokenStream::new(input)
} }
// ================== Scanner definitions ================== //
fn scan_par_open<I>(reader: &mut StagingReader<I>) -> Option<Token> fn scan_par_open<I>(reader: &mut StagingReader<I>) -> Option<Token>
where where
I: Iterator<Item = char>, I: Iterator<Item = char>,