add lips expression evaluation
This commit is contained in:
parent
63d84b1edb
commit
43ac1cc829
96
src/lisp/environment.rs
Normal file
96
src/lisp/environment.rs
Normal 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
265
src/lisp/expression.rs
Normal 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
2
src/lisp/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod environment;
|
||||
pub mod expression;
|
||||
36
src/main.rs
36
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));
|
||||
}
|
||||
|
||||
@ -159,6 +159,8 @@ where
|
||||
TokenStream::new(input)
|
||||
}
|
||||
|
||||
// ================== Scanner definitions ================== //
|
||||
|
||||
fn scan_par_open<I>(reader: &mut StagingReader<I>) -> Option<Token>
|
||||
where
|
||||
I: Iterator<Item = char>,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user