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;
|
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));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user