aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAkshay <[email protected]>2021-03-31 12:21:41 +0100
committerAkshay <[email protected]>2021-03-31 12:21:41 +0100
commitdbc0ab93141784dc07a7e193024fc5716dd34214 (patch)
treea25f5d43cde0931d387ec58f1877b282d8aa4daf /src
parent6e18888f652bbce37a2ef8a059bd9e187b83f6dd (diff)
handle quoted objects better; add cond form
Diffstat (limited to 'src')
-rw-r--r--src/lisp/eval.rs132
-rw-r--r--src/lisp/expr.rs8
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
15pub type Context = Vec<String>; 15pub type Context = Vec<String>;
16 16
17pub struct Evaluator<'ctx, 'file, 'global> { 17pub 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
22impl<'ctx, 'file, 'global> Evaluator<'ctx, 'file, 'global> 22impl<'ctx, 'global> Evaluator<'ctx, 'global>
23where 23where
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
200pub fn eval_quote(args: &[LispExpr]) -> Result<LispExpr, LispError> { 218pub 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
220pub fn create_lambda(cdr: &[LispExpr]) -> Result<LispExpr, LispError> { 227pub 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)]
264mod 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};
3use crate::app::AppState; 3use crate::app::AppState;
4use crate::lisp::{ 4use 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
20impl Arity { 20impl 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
42impl PrimitiveFunc { 42impl 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 }