From 4afc8ffdfe7ca2976bbab9d59da4c2ae11902c2f Mon Sep 17 00:00:00 2001 From: Akshay Date: Tue, 30 Mar 2021 19:33:46 +0530 Subject: add assert primitive; pass state in Evaluator --- src/lisp/eval.rs | 400 +++++++++++++++++++++++++------------------------------ 1 file changed, 181 insertions(+), 219 deletions(-) (limited to 'src/lisp/eval.rs') diff --git a/src/lisp/eval.rs b/src/lisp/eval.rs index 44540c0..2e46251 100644 --- a/src/lisp/eval.rs +++ b/src/lisp/eval.rs @@ -3,7 +3,6 @@ use crate::{ lisp::{ error::{EvalError, LispError}, expr::{Arity, Ident, LispExpr, LispFunction}, - number::LispNumber, Environment, }, type_match, @@ -13,155 +12,208 @@ use std::convert::TryInto; use log::{error, info}; -pub fn eval(expr: &LispExpr, app: &mut AppState) -> Result { - match expr { - LispExpr::Unit => Ok(expr.clone()), - LispExpr::StringLit(_) => Ok(expr.clone()), - LispExpr::Char(_) => Ok(expr.clone()), - LispExpr::Number(_) => Ok(expr.clone()), - LispExpr::BoolLit(_) => Ok(expr.clone()), - LispExpr::Ident(ref id) => lookup_extended(&app.lisp_env, id), - LispExpr::Quote(item, _) => match item.as_ref() { - i @ LispExpr::Unit - | i @ LispExpr::StringLit(_) - | i @ LispExpr::Char(_) - | i @ LispExpr::Number(_) - | i @ LispExpr::BoolLit(_) => Ok(i.clone()), - _ => Ok(*item.clone()), - }, - LispExpr::List(li) => { - let func_expr = &li[0]; - match func_expr { - LispExpr::Ident(s) => match s.as_ref() { - "define" => define_var(&li[1..], app), - "set!" => set_var(&li[1..], app), - "lambda" => create_lambda(&li[1..]), - "if" => eval_if(&li[1..], app), - "quote" => eval_quote(&li[1..]), - _ => { - let mut new_ls = vec![eval(&func_expr, app)?]; - new_ls.extend(li[1..].to_vec()); - eval(&(LispExpr::List(new_ls)), app) - } - }, - LispExpr::PrimitiveFunc(f) => { - let mut args = Vec::new(); - for item in li[1..].iter() { - args.push(eval(item, app)?); - } - f.call(&args, app) - } - LispExpr::Function(f) => { - let mut args = Vec::new(); - for item in li[1..].iter() { - let i = eval(item, app)?; - args.push(i); +pub type Context = Vec; + +pub struct Evaluator<'ctx, 'file, 'global> { + pub app: &'global mut AppState<'ctx, 'file>, + pub context: Context, +} + +impl<'ctx, 'file, 'global> Evaluator<'ctx, 'file, 'global> +where + 'ctx: 'global, + 'file: 'global, +{ + pub fn eval(&mut self, expr: &LispExpr) -> Result { + match expr { + LispExpr::Unit => Ok(expr.clone()), + LispExpr::StringLit(_) => Ok(expr.clone()), + LispExpr::Char(_) => Ok(expr.clone()), + LispExpr::Number(_) => Ok(expr.clone()), + LispExpr::BoolLit(_) => Ok(expr.clone()), + LispExpr::Ident(ref id) => lookup(&self.app.lisp_env, id), + LispExpr::Quote(item, _) => match item.as_ref() { + i @ LispExpr::Unit + | i @ LispExpr::StringLit(_) + | i @ LispExpr::Char(_) + | i @ LispExpr::Number(_) + | i @ LispExpr::BoolLit(_) => Ok(i.clone()), + _ => Ok(*item.clone()), + }, + LispExpr::List(li) => { + let func_expr = &li[0]; + match func_expr { + LispExpr::Ident(s) => match s.as_ref() { + "define" => self.define_var(&li[1..]), + "set!" => self.set_var(&li[1..]), + "lambda" => create_lambda(&li[1..]), + "if" => self.eval_if(&li[1..]), + "quote" => eval_quote(&li[1..]), + _ => { + let mut new_ls = vec![self.eval(&func_expr)?]; + new_ls.extend(li[1..].to_vec()); + self.eval(&(LispExpr::List(new_ls))) + } + }, + LispExpr::PrimitiveFunc(f) => { + let mut args = Vec::new(); + // context.push(f.name.to_string()); + for item in li[1..].iter() { + args.push(self.eval(item)?); + } + f.call(&args, &mut self.app) } - if f.params.len() != args.len() { - info!("too many or too little number of args"); - Err(EvalError::ArgumentCount(Arity::Exact(f.params.len())).into()) - } else { - let nested_env: Environment = - f.params.clone().into_iter().zip(args).collect(); - app.lisp_env.push(nested_env); - let result = if f.body.is_empty() { - Ok(LispExpr::Unit) + LispExpr::Function(f) => { + let mut args = Vec::new(); + for item in li[1..].iter() { + let i = self.eval(item)?; + args.push(i); + } + if f.params.len() != args.len() { + info!("too many or too little number of args"); + Err(EvalError::ArgumentCount(Arity::Exact(f.params.len())).into()) } else { - eval(&LispExpr::List(f.body.clone()), app) - }; - app.lisp_env.pop(); - return result; + let nested_env: Environment = + f.params.clone().into_iter().zip(args).collect(); + self.app.lisp_env.push(nested_env); + let result = if f.body.is_empty() { + Ok(LispExpr::Unit) + } else { + self.eval(&LispExpr::List(f.body.clone())) + }; + self.app.lisp_env.pop(); + return result; + } } + LispExpr::List(_) => { + info!("list as funciton"); + let func_expr = self.eval(&func_expr)?; + let mut new_ls = vec![func_expr]; + new_ls.extend(li[1..].to_vec()); + self.eval(&(LispExpr::List(new_ls))) + } + _ => Err(EvalError::BadForm.into()), } - LispExpr::List(_) => { - info!("list as funciton"); - let func_expr = eval(&func_expr, app)?; - let mut new_ls = vec![func_expr]; - new_ls.extend(li[1..].to_vec()); - eval(&(LispExpr::List(new_ls)), app) - } - _ => Err(EvalError::BadForm.into()), } + _ => Err(EvalError::BadForm.into()), } - _ => Err(EvalError::BadForm.into()), } -} -pub fn define_var(args: &[LispExpr], app: &mut AppState) -> Result { - info!("defining"); - let arity = Arity::Exact(2); - if !arity.is_valid(args) { - return Err(arity.to_error()); - } - match args { - [LispExpr::Ident(id), expr] => { - let value = eval(&expr, app)?; - let local_env = app.lisp_env.last_mut(); - if let Some(env) = local_env { - env.insert(id.into(), value); - } else { - error!("Unable to create global definition"); - return Err(EvalError::BadForm.into()); - } - return Ok(LispExpr::Unit); + pub fn define_var(&mut self, args: &[LispExpr]) -> Result { + let arity = Arity::Exact(2); + if !arity.is_valid(args) { + return Err(arity.to_error()); } - [LispExpr::List(shorthand), LispExpr::List(body)] => { - // (define (func arg) ) shorthand + match args { + [LispExpr::Ident(id), expr] => { + let value = self.eval(&expr)?; + let local_env = &mut self.app.lisp_env.last_mut(); + if let Some(env) = local_env { + env.insert(id.into(), value); + } else { + error!("Unable to create global definition"); + return Err(EvalError::BadForm.into()); + } + return Ok(LispExpr::Unit); + } + [LispExpr::List(shorthand), LispExpr::List(body)] => { + // (define (func arg) ) shorthand - let id = shorthand[0].unwrap_ident(); - let params = if shorthand.len() > 1 { - &shorthand[1..] - } else { - &[] + let id = shorthand[0].unwrap_ident(); + let params = if shorthand.len() > 1 { + &shorthand[1..] + } else { + &[] + } + .to_vec() + .into_iter() + .map(|arg| arg.try_into()) + .collect::, LispError>>()?; + let value = LispExpr::Function(LispFunction { + params, + body: body.to_vec(), + }); + + let local_env = &mut self.app.lisp_env.last_mut(); + if let Some(env) = local_env { + env.insert(id.into(), value); + } else { + error!("Unable to create global definition"); + return Err(EvalError::BadForm.into()); + } + return Ok(LispExpr::Unit); + } + _ => { + error!("Invalid usage of `define`"); + Err(EvalError::BadForm.into()) } - .to_vec() - .into_iter() - .map(|arg| arg.try_into()) - .collect::, LispError>>()?; - let value = LispExpr::Function(LispFunction { - params, - body: body.to_vec(), - }); + } + } - let local_env = app.lisp_env.last_mut(); - if let Some(env) = local_env { - info!("creating function {}", id); - env.insert(id.into(), value); - } else { - error!("Unable to create global definition"); + pub fn set_var(&mut self, args: &[LispExpr]) -> Result { + let arity = Arity::Exact(2); + if !arity.is_valid(args) { + return Err(arity.to_error()); + } + match args { + [LispExpr::Ident(id), expr] => { + let value = self.eval(&expr)?; + let local_env = self.app.lisp_env.last_mut(); + if let Some(env) = local_env { + return env + .insert(id.into(), value) + .ok_or(EvalError::UnboundVariable(id.into()).into()); + } else { + error!("Unable to set in global env!"); + return Err(EvalError::BadForm.into()); + } + } + _ => { + error!("Invalid usage of `set!`"); return Err(EvalError::BadForm.into()); } - return Ok(LispExpr::Unit); } - _ => { - error!("Invalid usage of `define`"); - Err(EvalError::BadForm.into()) + } + + pub fn eval_if(&mut self, args: &[LispExpr]) -> Result { + let arity = Arity::Exact(3); + if !arity.is_valid(args) { + return Err(arity.to_error()); + } else { + match args { + [predicate, then, else_] => { + let predicate = self.eval(&predicate)?; + if matches!(predicate, LispExpr::BoolLit(false)) { + return self.eval(&else_); + } else { + return self.eval(&then); + } + } + _ => { + panic!("panicked at `if` expression") + } + } } } } -pub fn set_var(args: &[LispExpr], app: &mut AppState) -> Result { - let arity = Arity::Exact(2); +pub fn eval_quote(args: &[LispExpr]) -> Result { + let arity = Arity::Exact(1); if !arity.is_valid(args) { return Err(arity.to_error()); - } - match args { - [LispExpr::Ident(id), expr] => { - let value = eval(&expr, app)?; - let local_env = app.lisp_env.last_mut(); - if let Some(env) = local_env { - return env - .insert(id.into(), value) - .ok_or(EvalError::UnboundVariable(id.into()).into()); - } else { - error!("Unable to set in global env!"); - return Err(EvalError::BadForm.into()); + } else { + match &args[0] { + LispExpr::Quote(item, depth) => Ok(LispExpr::Quote(item.clone(), depth + 1)), + i @ LispExpr::Unit + | i @ LispExpr::StringLit(_) + | i @ LispExpr::Char(_) + | i @ LispExpr::Number(_) + | i @ LispExpr::BoolLit(_) => Ok(i.clone()), + _ => { + let quoted_expr = Box::new(args[0].clone()); + Ok(LispExpr::Quote(quoted_expr, 1)) } } - _ => { - error!("Invalid usage of `set!`"); - return Err(EvalError::BadForm.into()); - } } } @@ -170,7 +222,6 @@ pub fn create_lambda(cdr: &[LispExpr]) -> Result { if !arity.is_valid(cdr) { return Err(arity.to_error()); } - info!("creating lambda"); match cdr { [LispExpr::List(params), LispExpr::List(body)] if type_match!(params, (..) => LispExpr::Ident(_)) => { @@ -189,48 +240,7 @@ pub fn create_lambda(cdr: &[LispExpr]) -> Result { } } -pub fn eval_if(args: &[LispExpr], app: &mut AppState) -> Result { - let arity = Arity::Exact(3); - if !arity.is_valid(args) { - return Err(arity.to_error()); - } else { - match args { - [predicate, then, else_] => { - let predicate = eval(&predicate, app)?; - if matches!(predicate, LispExpr::BoolLit(false)) { - return eval(&else_, app); - } else { - return eval(&then, app); - } - } - _ => { - panic!("panicked at `if` expression") - } - } - } -} - -pub fn eval_quote(args: &[LispExpr]) -> Result { - let arity = Arity::Exact(1); - if !arity.is_valid(args) { - return Err(arity.to_error()); - } else { - match &args[0] { - LispExpr::Quote(item, depth) => Ok(LispExpr::Quote(item.clone(), depth + 1)), - i @ LispExpr::Unit - | i @ LispExpr::StringLit(_) - | i @ LispExpr::Char(_) - | i @ LispExpr::Number(_) - | i @ LispExpr::BoolLit(_) => Ok(i.clone()), - _ => { - let quoted_expr = Box::new(args[0].clone()); - Ok(LispExpr::Quote(quoted_expr, 1)) - } - } - } -} - -pub fn lookup_extended(env_list: &[Environment], key: &str) -> Result { +pub fn lookup(env_list: &[Environment], key: &str) -> Result { if env_list.is_empty() { return Err(EvalError::UnboundVariable(key.into()).into()); } else { @@ -238,55 +248,7 @@ pub fn lookup_extended(env_list: &[Environment], key: &str) -> Result LispExpr { - let mut parser = Parser::new(Lexer::new(code, 0)); - eval(&parser.parse_single_expr().unwrap(), app).unwrap() - } - - #[test] - fn eval_all() { - let sdl_context = sdl2::init().unwrap(); - let ttf_context = sdl2::ttf::init().unwrap(); - let mut app = AppState::init(100, 100, &sdl_context, &ttf_context, None, None); - eval_arithmetic(&mut app); - eval_logical(&mut app); - } - - fn eval_arithmetic(app: &mut AppState) { - assert_eq!( - run("(+ 1 2 3)", app), - LispExpr::Number(LispNumber::Integer(6)) - ); - assert_eq!( - run("(+ 1.1 2.2 3.3)", app), - LispExpr::Number(LispNumber::Float(6.6)) - ); - assert_eq!( - run("(* 1 2 3 4 5)", app), - LispExpr::Number(LispNumber::Integer(120)) - ); - assert_eq!(run("(< 1 2)", app), LispExpr::BoolLit(true)); - assert_eq!(run("(> 6 5 4 3 2 1)", app), LispExpr::BoolLit(true)); - assert_eq!(run("(< 1 2 3 4 5 6)", app), LispExpr::BoolLit(true)); - assert_eq!(run("(>= 5 5 4 3 2 1)", app), LispExpr::BoolLit(true)); - assert_eq!(run("(<= 2 2 3 4 5 6)", app), LispExpr::BoolLit(true)); - } - - fn eval_logical(app: &mut AppState) { - assert_eq!(run("(and #t #t)", app), LispExpr::BoolLit(true)); - assert_eq!(run("(or #f #t)", app), LispExpr::BoolLit(true)); - assert_eq!(run("(not #t)", app), LispExpr::BoolLit(false)); - assert_eq!(run("(not #f)", app), run("(not (not #t))", app)); - } -} -- cgit v1.2.3