From dbc0ab93141784dc07a7e193024fc5716dd34214 Mon Sep 17 00:00:00 2001 From: Akshay Date: Wed, 31 Mar 2021 16:51:41 +0530 Subject: handle quoted objects better; add cond form --- src/lisp/eval.rs | 132 ++++++++++++++++++++++++++++++++++++++++++------------- src/lisp/expr.rs | 8 ++-- 2 files changed, 105 insertions(+), 35 deletions(-) diff --git a/src/lisp/eval.rs b/src/lisp/eval.rs index 2e46251..cb8ce68 100644 --- a/src/lisp/eval.rs +++ b/src/lisp/eval.rs @@ -14,15 +14,14 @@ use log::{error, info}; pub type Context = Vec; -pub struct Evaluator<'ctx, 'file, 'global> { - pub app: &'global mut AppState<'ctx, 'file>, +pub struct Evaluator<'ctx, 'global> { + pub app: &'global mut AppState<'ctx>, pub context: Context, } -impl<'ctx, 'file, 'global> Evaluator<'ctx, 'file, 'global> +impl<'ctx, 'global> Evaluator<'ctx, 'global> where 'ctx: 'global, - 'file: 'global, { pub fn eval(&mut self, expr: &LispExpr) -> Result { match expr { @@ -32,14 +31,7 @@ where 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::Quote(_, _) => Ok(expr.clone()), LispExpr::List(li) => { let func_expr = &li[0]; match func_expr { @@ -48,7 +40,8 @@ where "set!" => self.set_var(&li[1..]), "lambda" => create_lambda(&li[1..]), "if" => self.eval_if(&li[1..]), - "quote" => eval_quote(&li[1..]), + "cond" => self.eval_cond(&li[1..]), + "quote" => quote_var(&li[1..]), _ => { let mut new_ls = vec![self.eval(&func_expr)?]; new_ls.extend(li[1..].to_vec()); @@ -101,7 +94,7 @@ where pub fn define_var(&mut self, args: &[LispExpr]) -> Result { let arity = Arity::Exact(2); - if !arity.is_valid(args) { + if !arity.check(args) { return Err(arity.to_error()); } match args { @@ -152,7 +145,7 @@ where pub fn set_var(&mut self, args: &[LispExpr]) -> Result { let arity = Arity::Exact(2); - if !arity.is_valid(args) { + if !arity.check(args) { return Err(arity.to_error()); } match args { @@ -177,7 +170,7 @@ where pub fn eval_if(&mut self, args: &[LispExpr]) -> Result { let arity = Arity::Exact(3); - if !arity.is_valid(args) { + if !arity.check(args) { return Err(arity.to_error()); } else { match args { @@ -195,31 +188,45 @@ where } } } + + pub fn eval_cond(&mut self, args: &[LispExpr]) -> Result { + let arity = Arity::Atleast(1); + let valid_cond_stmt = |expr: &LispExpr| matches!(expr, LispExpr::List(v) if v.len() == 2); + if !arity.check(args) { + return Err(arity.to_error()); + } else { + for cond_stmt in args { + if valid_cond_stmt(cond_stmt) { + match &cond_stmt.unwrap_list()[..] { + [predicate, then] => { + if self.eval(&predicate)?.cast_bool() { + return self.eval(&then); + } + } + _ => return Err(EvalError::BadForm.into()), + } + } else { + error!("bad `cond` form"); + return Err(EvalError::BadForm.into()); + } + } + return Ok(LispExpr::Unit); + } + } } -pub fn eval_quote(args: &[LispExpr]) -> Result { +pub fn quote_var(args: &[LispExpr]) -> Result { let arity = Arity::Exact(1); - if !arity.is_valid(args) { + if !arity.check(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)) - } - } + return Ok(args[0].clone().quote(1)); } } pub fn create_lambda(cdr: &[LispExpr]) -> Result { let arity: Arity = Arity::Exact(2); - if !arity.is_valid(cdr) { + if !arity.check(cdr) { return Err(arity.to_error()); } match cdr { @@ -252,3 +259,66 @@ pub fn lookup(env_list: &[Environment], key: &str) -> Result LispExpr { + let mut parser = Parser::new(Lexer::new(code, 0)); + let mut evaluator = Evaluator { + app, + context: Vec::new(), + }; + evaluator + .eval(&parser.parse_single_expr().unwrap()) + .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).unwrap(); + eval_arithmetic(&mut app); + eval_logical(&mut app); + eval_quote(&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_quote(app: &mut AppState) { + assert!(run("(quote a)", app).cast_bool()); + assert!(run("(eq? 'a 'a)", app).cast_bool()); + assert!(run("(eq? '(1 2 3) '(1 2 3))", app).cast_bool()); + assert!(run("(eq? '(1 '(1 2 3)) '(1 '(1 2 3)))", app).cast_bool(),); + assert!(run("(eq? '#t '#t)", app).cast_bool()); + } + + fn eval_logical(app: &mut AppState) { + assert!(run("(and #t #t)", app).cast_bool()); + assert!(run("(or #f #t)", app).cast_bool()); + assert!(run("(not #t)", app).cast_bool()); + assert_eq!(run("(not #f)", app), run("(not (not #t))", app)); + } +} diff --git a/src/lisp/expr.rs b/src/lisp/expr.rs index 1f3e0fb..31476c9 100644 --- a/src/lisp/expr.rs +++ b/src/lisp/expr.rs @@ -3,7 +3,7 @@ use std::{cmp::PartialEq, convert::TryFrom, fmt}; use crate::app::AppState; use crate::lisp::{ error::{EvalError, LispError}, - eval::{lookup, Evaluator}, + eval::lookup, number::LispNumber, EnvList, }; @@ -18,7 +18,7 @@ pub enum Arity { } impl Arity { - pub fn is_valid(self, args: &[T]) -> bool { + pub fn check(self, args: &[T]) -> bool { match self { Arity::Exact(a) => args.len() == a, Arity::Atleast(a) => args.len() >= a, @@ -41,7 +41,7 @@ pub struct PrimitiveFunc { impl PrimitiveFunc { pub fn call(&self, args: &[LispExpr], app: &mut AppState) -> Result { - if !self.arity.is_valid(args) { + if !self.arity.check(args) { return Err(EvalError::ArgumentCount(self.arity).into()); } (self.closure)(args, app) @@ -226,7 +226,7 @@ impl fmt::Display for LispExpr { } LispExpr::Comma(val, depth) => write!(f, "{}{}", ",".repeat(*depth as usize), val)?, LispExpr::CommaAt(val, depth) => write!(f, "{}@{}", ",".repeat(*depth as usize), val)?, - LispExpr::Quote(val, depth) => write!(f, "{}{}", "'".repeat(*depth as usize), val)?, + LispExpr::Quote(val, depth) => write!(f, "{}{}", "'".repeat(*depth as usize - 1), val)?, }; Ok(()) } -- cgit v1.2.3