diff options
-rw-r--r-- | src/lisp/eval.rs | 132 | ||||
-rw-r--r-- | 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}; | |||
14 | 14 | ||
15 | pub type Context = Vec<String>; | 15 | pub type Context = Vec<String>; |
16 | 16 | ||
17 | pub struct Evaluator<'ctx, 'file, 'global> { | 17 | pub struct Evaluator<'ctx, 'global> { |
18 | pub app: &'global mut AppState<'ctx, 'file>, | 18 | pub app: &'global mut AppState<'ctx>, |
19 | pub context: Context, | 19 | pub context: Context, |
20 | } | 20 | } |
21 | 21 | ||
22 | impl<'ctx, 'file, 'global> Evaluator<'ctx, 'file, 'global> | 22 | impl<'ctx, 'global> Evaluator<'ctx, 'global> |
23 | where | 23 | where |
24 | 'ctx: 'global, | 24 | 'ctx: 'global, |
25 | 'file: 'global, | ||
26 | { | 25 | { |
27 | pub fn eval(&mut self, expr: &LispExpr) -> Result<LispExpr, LispError> { | 26 | pub fn eval(&mut self, expr: &LispExpr) -> Result<LispExpr, LispError> { |
28 | match expr { | 27 | match expr { |
@@ -32,14 +31,7 @@ where | |||
32 | LispExpr::Number(_) => Ok(expr.clone()), | 31 | LispExpr::Number(_) => Ok(expr.clone()), |
33 | LispExpr::BoolLit(_) => Ok(expr.clone()), | 32 | LispExpr::BoolLit(_) => Ok(expr.clone()), |
34 | LispExpr::Ident(ref id) => lookup(&self.app.lisp_env, id), | 33 | LispExpr::Ident(ref id) => lookup(&self.app.lisp_env, id), |
35 | LispExpr::Quote(item, _) => match item.as_ref() { | 34 | LispExpr::Quote(_, _) => Ok(expr.clone()), |
36 | i @ LispExpr::Unit | ||
37 | | i @ LispExpr::StringLit(_) | ||
38 | | i @ LispExpr::Char(_) | ||
39 | | i @ LispExpr::Number(_) | ||
40 | | i @ LispExpr::BoolLit(_) => Ok(i.clone()), | ||
41 | _ => Ok(*item.clone()), | ||
42 | }, | ||
43 | LispExpr::List(li) => { | 35 | LispExpr::List(li) => { |
44 | let func_expr = &li[0]; | 36 | let func_expr = &li[0]; |
45 | match func_expr { | 37 | match func_expr { |
@@ -48,7 +40,8 @@ where | |||
48 | "set!" => self.set_var(&li[1..]), | 40 | "set!" => self.set_var(&li[1..]), |
49 | "lambda" => create_lambda(&li[1..]), | 41 | "lambda" => create_lambda(&li[1..]), |
50 | "if" => self.eval_if(&li[1..]), | 42 | "if" => self.eval_if(&li[1..]), |
51 | "quote" => eval_quote(&li[1..]), | 43 | "cond" => self.eval_cond(&li[1..]), |
44 | "quote" => quote_var(&li[1..]), | ||
52 | _ => { | 45 | _ => { |
53 | let mut new_ls = vec![self.eval(&func_expr)?]; | 46 | let mut new_ls = vec![self.eval(&func_expr)?]; |
54 | new_ls.extend(li[1..].to_vec()); | 47 | new_ls.extend(li[1..].to_vec()); |
@@ -101,7 +94,7 @@ where | |||
101 | 94 | ||
102 | pub fn define_var(&mut self, args: &[LispExpr]) -> Result<LispExpr, LispError> { | 95 | pub fn define_var(&mut self, args: &[LispExpr]) -> Result<LispExpr, LispError> { |
103 | let arity = Arity::Exact(2); | 96 | let arity = Arity::Exact(2); |
104 | if !arity.is_valid(args) { | 97 | if !arity.check(args) { |
105 | return Err(arity.to_error()); | 98 | return Err(arity.to_error()); |
106 | } | 99 | } |
107 | match args { | 100 | match args { |
@@ -152,7 +145,7 @@ where | |||
152 | 145 | ||
153 | pub fn set_var(&mut self, args: &[LispExpr]) -> Result<LispExpr, LispError> { | 146 | pub fn set_var(&mut self, args: &[LispExpr]) -> Result<LispExpr, LispError> { |
154 | let arity = Arity::Exact(2); | 147 | let arity = Arity::Exact(2); |
155 | if !arity.is_valid(args) { | 148 | if !arity.check(args) { |
156 | return Err(arity.to_error()); | 149 | return Err(arity.to_error()); |
157 | } | 150 | } |
158 | match args { | 151 | match args { |
@@ -177,7 +170,7 @@ where | |||
177 | 170 | ||
178 | pub fn eval_if(&mut self, args: &[LispExpr]) -> Result<LispExpr, LispError> { | 171 | pub fn eval_if(&mut self, args: &[LispExpr]) -> Result<LispExpr, LispError> { |
179 | let arity = Arity::Exact(3); | 172 | let arity = Arity::Exact(3); |
180 | if !arity.is_valid(args) { | 173 | if !arity.check(args) { |
181 | return Err(arity.to_error()); | 174 | return Err(arity.to_error()); |
182 | } else { | 175 | } else { |
183 | match args { | 176 | match args { |
@@ -195,31 +188,45 @@ where | |||
195 | } | 188 | } |
196 | } | 189 | } |
197 | } | 190 | } |
191 | |||
192 | pub fn eval_cond(&mut self, args: &[LispExpr]) -> Result<LispExpr, LispError> { | ||
193 | let arity = Arity::Atleast(1); | ||
194 | let valid_cond_stmt = |expr: &LispExpr| matches!(expr, LispExpr::List(v) if v.len() == 2); | ||
195 | if !arity.check(args) { | ||
196 | return Err(arity.to_error()); | ||
197 | } else { | ||
198 | for cond_stmt in args { | ||
199 | if valid_cond_stmt(cond_stmt) { | ||
200 | match &cond_stmt.unwrap_list()[..] { | ||
201 | [predicate, then] => { | ||
202 | if self.eval(&predicate)?.cast_bool() { | ||
203 | return self.eval(&then); | ||
204 | } | ||
205 | } | ||
206 | _ => return Err(EvalError::BadForm.into()), | ||
207 | } | ||
208 | } else { | ||
209 | error!("bad `cond` form"); | ||
210 | return Err(EvalError::BadForm.into()); | ||
211 | } | ||
212 | } | ||
213 | return Ok(LispExpr::Unit); | ||
214 | } | ||
215 | } | ||
198 | } | 216 | } |
199 | 217 | ||
200 | pub fn eval_quote(args: &[LispExpr]) -> Result<LispExpr, LispError> { | 218 | pub fn quote_var(args: &[LispExpr]) -> Result<LispExpr, LispError> { |
201 | let arity = Arity::Exact(1); | 219 | let arity = Arity::Exact(1); |
202 | if !arity.is_valid(args) { | 220 | if !arity.check(args) { |
203 | return Err(arity.to_error()); | 221 | return Err(arity.to_error()); |
204 | } else { | 222 | } else { |
205 | match &args[0] { | 223 | return Ok(args[0].clone().quote(1)); |
206 | LispExpr::Quote(item, depth) => Ok(LispExpr::Quote(item.clone(), depth + 1)), | ||
207 | i @ LispExpr::Unit | ||
208 | | i @ LispExpr::StringLit(_) | ||
209 | | i @ LispExpr::Char(_) | ||
210 | | i @ LispExpr::Number(_) | ||
211 | | i @ LispExpr::BoolLit(_) => Ok(i.clone()), | ||
212 | _ => { | ||
213 | let quoted_expr = Box::new(args[0].clone()); | ||
214 | Ok(LispExpr::Quote(quoted_expr, 1)) | ||
215 | } | ||
216 | } | ||
217 | } | 224 | } |
218 | } | 225 | } |
219 | 226 | ||
220 | pub fn create_lambda(cdr: &[LispExpr]) -> Result<LispExpr, LispError> { | 227 | pub fn create_lambda(cdr: &[LispExpr]) -> Result<LispExpr, LispError> { |
221 | let arity: Arity = Arity::Exact(2); | 228 | let arity: Arity = Arity::Exact(2); |
222 | if !arity.is_valid(cdr) { | 229 | if !arity.check(cdr) { |
223 | return Err(arity.to_error()); | 230 | return Err(arity.to_error()); |
224 | } | 231 | } |
225 | match cdr { | 232 | match cdr { |
@@ -252,3 +259,66 @@ pub fn lookup(env_list: &[Environment], key: &str) -> Result<LispExpr, LispError | |||
252 | } | 259 | } |
253 | } | 260 | } |
254 | } | 261 | } |
262 | |||
263 | #[cfg(test)] | ||
264 | mod tests { | ||
265 | use super::*; | ||
266 | |||
267 | use crate::lisp::{expr::LispExpr, lex::Lexer, number::LispNumber, parse::Parser}; | ||
268 | |||
269 | fn run(code: &str, app: &mut AppState) -> LispExpr { | ||
270 | let mut parser = Parser::new(Lexer::new(code, 0)); | ||
271 | let mut evaluator = Evaluator { | ||
272 | app, | ||
273 | context: Vec::new(), | ||
274 | }; | ||
275 | evaluator | ||
276 | .eval(&parser.parse_single_expr().unwrap()) | ||
277 | .unwrap() | ||
278 | } | ||
279 | |||
280 | #[test] | ||
281 | fn eval_all() { | ||
282 | let sdl_context = sdl2::init().unwrap(); | ||
283 | let ttf_context = sdl2::ttf::init().unwrap(); | ||
284 | let mut app = AppState::init(100, 100, &sdl_context, &ttf_context, None, None).unwrap(); | ||
285 | eval_arithmetic(&mut app); | ||
286 | eval_logical(&mut app); | ||
287 | eval_quote(&mut app); | ||
288 | } | ||
289 | |||
290 | fn eval_arithmetic(app: &mut AppState) { | ||
291 | assert_eq!( | ||
292 | run("(+ 1 2 3)", app), | ||
293 | LispExpr::Number(LispNumber::Integer(6)) | ||
294 | ); | ||
295 | assert_eq!( | ||
296 | run("(+ 1.1 2.2 3.3)", app), | ||
297 | LispExpr::Number(LispNumber::Float(6.6)) | ||
298 | ); | ||
299 | assert_eq!( | ||
300 | run("(* 1 2 3 4 5)", app), | ||
301 | LispExpr::Number(LispNumber::Integer(120)) | ||
302 | ); | ||
303 | assert_eq!(run("(< 1 2)", app), LispExpr::BoolLit(true)); | ||
304 | assert_eq!(run("(> 6 5 4 3 2 1)", app), LispExpr::BoolLit(true)); | ||
305 | assert_eq!(run("(< 1 2 3 4 5 6)", app), LispExpr::BoolLit(true)); | ||
306 | assert_eq!(run("(>= 5 5 4 3 2 1)", app), LispExpr::BoolLit(true)); | ||
307 | assert_eq!(run("(<= 2 2 3 4 5 6)", app), LispExpr::BoolLit(true)); | ||
308 | } | ||
309 | |||
310 | fn eval_quote(app: &mut AppState) { | ||
311 | assert!(run("(quote a)", app).cast_bool()); | ||
312 | assert!(run("(eq? 'a 'a)", app).cast_bool()); | ||
313 | assert!(run("(eq? '(1 2 3) '(1 2 3))", app).cast_bool()); | ||
314 | assert!(run("(eq? '(1 '(1 2 3)) '(1 '(1 2 3)))", app).cast_bool(),); | ||
315 | assert!(run("(eq? '#t '#t)", app).cast_bool()); | ||
316 | } | ||
317 | |||
318 | fn eval_logical(app: &mut AppState) { | ||
319 | assert!(run("(and #t #t)", app).cast_bool()); | ||
320 | assert!(run("(or #f #t)", app).cast_bool()); | ||
321 | assert!(run("(not #t)", app).cast_bool()); | ||
322 | assert_eq!(run("(not #f)", app), run("(not (not #t))", app)); | ||
323 | } | ||
324 | } | ||
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}; | |||
3 | use crate::app::AppState; | 3 | use crate::app::AppState; |
4 | use crate::lisp::{ | 4 | use crate::lisp::{ |
5 | error::{EvalError, LispError}, | 5 | error::{EvalError, LispError}, |
6 | eval::{lookup, Evaluator}, | 6 | eval::lookup, |
7 | number::LispNumber, | 7 | number::LispNumber, |
8 | EnvList, | 8 | EnvList, |
9 | }; | 9 | }; |
@@ -18,7 +18,7 @@ pub enum Arity { | |||
18 | } | 18 | } |
19 | 19 | ||
20 | impl Arity { | 20 | impl Arity { |
21 | pub fn is_valid<T>(self, args: &[T]) -> bool { | 21 | pub fn check<T>(self, args: &[T]) -> bool { |
22 | match self { | 22 | match self { |
23 | Arity::Exact(a) => args.len() == a, | 23 | Arity::Exact(a) => args.len() == a, |
24 | Arity::Atleast(a) => args.len() >= a, | 24 | Arity::Atleast(a) => args.len() >= a, |
@@ -41,7 +41,7 @@ pub struct PrimitiveFunc { | |||
41 | 41 | ||
42 | impl PrimitiveFunc { | 42 | impl PrimitiveFunc { |
43 | pub fn call(&self, args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, LispError> { | 43 | pub fn call(&self, args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, LispError> { |
44 | if !self.arity.is_valid(args) { | 44 | if !self.arity.check(args) { |
45 | return Err(EvalError::ArgumentCount(self.arity).into()); | 45 | return Err(EvalError::ArgumentCount(self.arity).into()); |
46 | } | 46 | } |
47 | (self.closure)(args, app) | 47 | (self.closure)(args, app) |
@@ -226,7 +226,7 @@ impl fmt::Display for LispExpr { | |||
226 | } | 226 | } |
227 | LispExpr::Comma(val, depth) => write!(f, "{}{}", ",".repeat(*depth as usize), val)?, | 227 | LispExpr::Comma(val, depth) => write!(f, "{}{}", ",".repeat(*depth as usize), val)?, |
228 | LispExpr::CommaAt(val, depth) => write!(f, "{}@{}", ",".repeat(*depth as usize), val)?, | 228 | LispExpr::CommaAt(val, depth) => write!(f, "{}@{}", ",".repeat(*depth as usize), val)?, |
229 | LispExpr::Quote(val, depth) => write!(f, "{}{}", "'".repeat(*depth as usize), val)?, | 229 | LispExpr::Quote(val, depth) => write!(f, "{}{}", "'".repeat(*depth as usize - 1), val)?, |
230 | }; | 230 | }; |
231 | Ok(()) | 231 | Ok(()) |
232 | } | 232 | } |