diff options
author | Akshay <[email protected]> | 2021-03-30 15:03:46 +0100 |
---|---|---|
committer | Akshay <[email protected]> | 2021-03-30 15:03:46 +0100 |
commit | 4afc8ffdfe7ca2976bbab9d59da4c2ae11902c2f (patch) | |
tree | 1b924e7fdfddbb33d2bc47e1dfdf31d65c3d95f4 | |
parent | 07ae09ee91182e88fe548b71703c445fe9a1e28a (diff) |
add assert primitive; pass state in Evaluator
-rw-r--r-- | src/lisp/error.rs | 6 | ||||
-rw-r--r-- | src/lisp/eval.rs | 400 | ||||
-rw-r--r-- | src/lisp/expr.rs | 7 | ||||
-rw-r--r-- | src/lisp/prelude.rs | 57 | ||||
-rw-r--r-- | src/lisp/std.lisp | 22 | ||||
-rw-r--r-- | src/utils.rs | 8 |
6 files changed, 240 insertions, 260 deletions
diff --git a/src/lisp/error.rs b/src/lisp/error.rs index 53681d8..f905e6d 100644 --- a/src/lisp/error.rs +++ b/src/lisp/error.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | use crate::lisp::{ | 1 | use crate::lisp::{ |
2 | expr::Arity, | 2 | expr::{Arity, LispExpr}, |
3 | lex::{Span, SpanDisplay}, | 3 | lex::{Span, SpanDisplay}, |
4 | }; | 4 | }; |
5 | 5 | ||
@@ -98,6 +98,7 @@ pub enum EvalError { | |||
98 | TypeMismatch, | 98 | TypeMismatch, |
99 | NoFileName, | 99 | NoFileName, |
100 | AccessEmptyList, | 100 | AccessEmptyList, |
101 | AssertionError { expected: LispExpr, got: LispExpr }, | ||
101 | ScriptLoadError(io::Error), | 102 | ScriptLoadError(io::Error), |
102 | CustomInternal(&'static str), | 103 | CustomInternal(&'static str), |
103 | Custom(String), | 104 | Custom(String), |
@@ -124,6 +125,9 @@ impl fmt::Display for EvalError { | |||
124 | Self::DivByZero => write!(f, "attempt to divide by zero"), | 125 | Self::DivByZero => write!(f, "attempt to divide by zero"), |
125 | Self::NoFileName => write!(f, "no file name specified"), | 126 | Self::NoFileName => write!(f, "no file name specified"), |
126 | Self::AccessEmptyList => write!(f, "attempted to access empty list"), | 127 | Self::AccessEmptyList => write!(f, "attempted to access empty list"), |
128 | Self::AssertionError { expected, got } => { | ||
129 | write!(f, "assertion error: expected `{}` got `{}`", expected, got) | ||
130 | } | ||
127 | Self::ScriptLoadError(s) => write!(f, "error while loading script: {}", s), | 131 | Self::ScriptLoadError(s) => write!(f, "error while loading script: {}", s), |
128 | Self::CustomInternal(s) => write!(f, "{}", s), | 132 | Self::CustomInternal(s) => write!(f, "{}", s), |
129 | Self::Custom(s) => write!(f, "error: {}", s), | 133 | Self::Custom(s) => write!(f, "error: {}", s), |
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::{ | |||
3 | lisp::{ | 3 | lisp::{ |
4 | error::{EvalError, LispError}, | 4 | error::{EvalError, LispError}, |
5 | expr::{Arity, Ident, LispExpr, LispFunction}, | 5 | expr::{Arity, Ident, LispExpr, LispFunction}, |
6 | number::LispNumber, | ||
7 | Environment, | 6 | Environment, |
8 | }, | 7 | }, |
9 | type_match, | 8 | type_match, |
@@ -13,155 +12,208 @@ use std::convert::TryInto; | |||
13 | 12 | ||
14 | use log::{error, info}; | 13 | use log::{error, info}; |
15 | 14 | ||
16 | pub fn eval(expr: &LispExpr, app: &mut AppState) -> Result<LispExpr, LispError> { | 15 | pub type Context = Vec<String>; |
17 | match expr { | 16 | |
18 | LispExpr::Unit => Ok(expr.clone()), | 17 | pub struct Evaluator<'ctx, 'file, 'global> { |
19 | LispExpr::StringLit(_) => Ok(expr.clone()), | 18 | pub app: &'global mut AppState<'ctx, 'file>, |
20 | LispExpr::Char(_) => Ok(expr.clone()), | 19 | pub context: Context, |
21 | LispExpr::Number(_) => Ok(expr.clone()), | 20 | } |
22 | LispExpr::BoolLit(_) => Ok(expr.clone()), | 21 | |
23 | LispExpr::Ident(ref id) => lookup_extended(&app.lisp_env, id), | 22 | impl<'ctx, 'file, 'global> Evaluator<'ctx, 'file, 'global> |
24 | LispExpr::Quote(item, _) => match item.as_ref() { | 23 | where |
25 | i @ LispExpr::Unit | 24 | 'ctx: 'global, |
26 | | i @ LispExpr::StringLit(_) | 25 | 'file: 'global, |
27 | | i @ LispExpr::Char(_) | 26 | { |
28 | | i @ LispExpr::Number(_) | 27 | pub fn eval(&mut self, expr: &LispExpr) -> Result<LispExpr, LispError> { |
29 | | i @ LispExpr::BoolLit(_) => Ok(i.clone()), | 28 | match expr { |
30 | _ => Ok(*item.clone()), | 29 | LispExpr::Unit => Ok(expr.clone()), |
31 | }, | 30 | LispExpr::StringLit(_) => Ok(expr.clone()), |
32 | LispExpr::List(li) => { | 31 | LispExpr::Char(_) => Ok(expr.clone()), |
33 | let func_expr = &li[0]; | 32 | LispExpr::Number(_) => Ok(expr.clone()), |
34 | match func_expr { | 33 | LispExpr::BoolLit(_) => Ok(expr.clone()), |
35 | LispExpr::Ident(s) => match s.as_ref() { | 34 | LispExpr::Ident(ref id) => lookup(&self.app.lisp_env, id), |
36 | "define" => define_var(&li[1..], app), | 35 | LispExpr::Quote(item, _) => match item.as_ref() { |
37 | "set!" => set_var(&li[1..], app), | 36 | i @ LispExpr::Unit |
38 | "lambda" => create_lambda(&li[1..]), | 37 | | i @ LispExpr::StringLit(_) |
39 | "if" => eval_if(&li[1..], app), | 38 | | i @ LispExpr::Char(_) |
40 | "quote" => eval_quote(&li[1..]), | 39 | | i @ LispExpr::Number(_) |
41 | _ => { | 40 | | i @ LispExpr::BoolLit(_) => Ok(i.clone()), |
42 | let mut new_ls = vec![eval(&func_expr, app)?]; | 41 | _ => Ok(*item.clone()), |
43 | new_ls.extend(li[1..].to_vec()); | 42 | }, |
44 | eval(&(LispExpr::List(new_ls)), app) | 43 | LispExpr::List(li) => { |
45 | } | 44 | let func_expr = &li[0]; |
46 | }, | 45 | match func_expr { |
47 | LispExpr::PrimitiveFunc(f) => { | 46 | LispExpr::Ident(s) => match s.as_ref() { |
48 | let mut args = Vec::new(); | 47 | "define" => self.define_var(&li[1..]), |
49 | for item in li[1..].iter() { | 48 | "set!" => self.set_var(&li[1..]), |
50 | args.push(eval(item, app)?); | 49 | "lambda" => create_lambda(&li[1..]), |
51 | } | 50 | "if" => self.eval_if(&li[1..]), |
52 | f.call(&args, app) | 51 | "quote" => eval_quote(&li[1..]), |
53 | } | 52 | _ => { |
54 | LispExpr::Function(f) => { | 53 | let mut new_ls = vec![self.eval(&func_expr)?]; |
55 | let mut args = Vec::new(); | 54 | new_ls.extend(li[1..].to_vec()); |
56 | for item in li[1..].iter() { | 55 | self.eval(&(LispExpr::List(new_ls))) |
57 | let i = eval(item, app)?; | 56 | } |
58 | args.push(i); | 57 | }, |
58 | LispExpr::PrimitiveFunc(f) => { | ||
59 | let mut args = Vec::new(); | ||
60 | // context.push(f.name.to_string()); | ||
61 | for item in li[1..].iter() { | ||
62 | args.push(self.eval(item)?); | ||
63 | } | ||
64 | f.call(&args, &mut self.app) | ||
59 | } | 65 | } |
60 | if f.params.len() != args.len() { | 66 | LispExpr::Function(f) => { |
61 | info!("too many or too little number of args"); | 67 | let mut args = Vec::new(); |
62 | Err(EvalError::ArgumentCount(Arity::Exact(f.params.len())).into()) | 68 | for item in li[1..].iter() { |
63 | } else { | 69 | let i = self.eval(item)?; |
64 | let nested_env: Environment = | 70 | args.push(i); |
65 | f.params.clone().into_iter().zip(args).collect(); | 71 | } |
66 | app.lisp_env.push(nested_env); | 72 | if f.params.len() != args.len() { |
67 | let result = if f.body.is_empty() { | 73 | info!("too many or too little number of args"); |
68 | Ok(LispExpr::Unit) | 74 | Err(EvalError::ArgumentCount(Arity::Exact(f.params.len())).into()) |
69 | } else { | 75 | } else { |
70 | eval(&LispExpr::List(f.body.clone()), app) | 76 | let nested_env: Environment = |
71 | }; | 77 | f.params.clone().into_iter().zip(args).collect(); |
72 | app.lisp_env.pop(); | 78 | self.app.lisp_env.push(nested_env); |
73 | return result; | 79 | let result = if f.body.is_empty() { |
80 | Ok(LispExpr::Unit) | ||
81 | } else { | ||
82 | self.eval(&LispExpr::List(f.body.clone())) | ||
83 | }; | ||
84 | self.app.lisp_env.pop(); | ||
85 | return result; | ||
86 | } | ||
74 | } | 87 | } |
88 | LispExpr::List(_) => { | ||
89 | info!("list as funciton"); | ||
90 | let func_expr = self.eval(&func_expr)?; | ||
91 | let mut new_ls = vec![func_expr]; | ||
92 | new_ls.extend(li[1..].to_vec()); | ||
93 | self.eval(&(LispExpr::List(new_ls))) | ||
94 | } | ||
95 | _ => Err(EvalError::BadForm.into()), | ||
75 | } | 96 | } |
76 | LispExpr::List(_) => { | ||
77 | info!("list as funciton"); | ||
78 | let func_expr = eval(&func_expr, app)?; | ||
79 | let mut new_ls = vec![func_expr]; | ||
80 | new_ls.extend(li[1..].to_vec()); | ||
81 | eval(&(LispExpr::List(new_ls)), app) | ||
82 | } | ||
83 | _ => Err(EvalError::BadForm.into()), | ||
84 | } | 97 | } |
98 | _ => Err(EvalError::BadForm.into()), | ||
85 | } | 99 | } |
86 | _ => Err(EvalError::BadForm.into()), | ||
87 | } | 100 | } |
88 | } | ||
89 | 101 | ||
90 | pub fn define_var(args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, LispError> { | 102 | pub fn define_var(&mut self, args: &[LispExpr]) -> Result<LispExpr, LispError> { |
91 | info!("defining"); | 103 | let arity = Arity::Exact(2); |
92 | let arity = Arity::Exact(2); | 104 | if !arity.is_valid(args) { |
93 | if !arity.is_valid(args) { | 105 | return Err(arity.to_error()); |
94 | return Err(arity.to_error()); | ||
95 | } | ||
96 | match args { | ||
97 | [LispExpr::Ident(id), expr] => { | ||
98 | let value = eval(&expr, app)?; | ||
99 | let local_env = app.lisp_env.last_mut(); | ||
100 | if let Some(env) = local_env { | ||
101 | env.insert(id.into(), value); | ||
102 | } else { | ||
103 | error!("Unable to create global definition"); | ||
104 | return Err(EvalError::BadForm.into()); | ||
105 | } | ||
106 | return Ok(LispExpr::Unit); | ||
107 | } | 106 | } |
108 | [LispExpr::List(shorthand), LispExpr::List(body)] => { | 107 | match args { |
109 | // (define (func arg) <body>) shorthand | 108 | [LispExpr::Ident(id), expr] => { |
109 | let value = self.eval(&expr)?; | ||
110 | let local_env = &mut self.app.lisp_env.last_mut(); | ||
111 | if let Some(env) = local_env { | ||
112 | env.insert(id.into(), value); | ||
113 | } else { | ||
114 | error!("Unable to create global definition"); | ||
115 | return Err(EvalError::BadForm.into()); | ||
116 | } | ||
117 | return Ok(LispExpr::Unit); | ||
118 | } | ||
119 | [LispExpr::List(shorthand), LispExpr::List(body)] => { | ||
120 | // (define (func arg) <body>) shorthand | ||
110 | 121 | ||
111 | let id = shorthand[0].unwrap_ident(); | 122 | let id = shorthand[0].unwrap_ident(); |
112 | let params = if shorthand.len() > 1 { | 123 | let params = if shorthand.len() > 1 { |
113 | &shorthand[1..] | 124 | &shorthand[1..] |
114 | } else { | 125 | } else { |
115 | &[] | 126 | &[] |
127 | } | ||
128 | .to_vec() | ||
129 | .into_iter() | ||
130 | .map(|arg| arg.try_into()) | ||
131 | .collect::<Result<Vec<Ident>, LispError>>()?; | ||
132 | let value = LispExpr::Function(LispFunction { | ||
133 | params, | ||
134 | body: body.to_vec(), | ||
135 | }); | ||
136 | |||
137 | let local_env = &mut self.app.lisp_env.last_mut(); | ||
138 | if let Some(env) = local_env { | ||
139 | env.insert(id.into(), value); | ||
140 | } else { | ||
141 | error!("Unable to create global definition"); | ||
142 | return Err(EvalError::BadForm.into()); | ||
143 | } | ||
144 | return Ok(LispExpr::Unit); | ||
145 | } | ||
146 | _ => { | ||
147 | error!("Invalid usage of `define`"); | ||
148 | Err(EvalError::BadForm.into()) | ||
116 | } | 149 | } |
117 | .to_vec() | 150 | } |
118 | .into_iter() | 151 | } |
119 | .map(|arg| arg.try_into()) | ||
120 | .collect::<Result<Vec<Ident>, LispError>>()?; | ||
121 | let value = LispExpr::Function(LispFunction { | ||
122 | params, | ||
123 | body: body.to_vec(), | ||
124 | }); | ||
125 | 152 | ||
126 | let local_env = app.lisp_env.last_mut(); | 153 | pub fn set_var(&mut self, args: &[LispExpr]) -> Result<LispExpr, LispError> { |
127 | if let Some(env) = local_env { | 154 | let arity = Arity::Exact(2); |
128 | info!("creating function {}", id); | 155 | if !arity.is_valid(args) { |
129 | env.insert(id.into(), value); | 156 | return Err(arity.to_error()); |
130 | } else { | 157 | } |
131 | error!("Unable to create global definition"); | 158 | match args { |
159 | [LispExpr::Ident(id), expr] => { | ||
160 | let value = self.eval(&expr)?; | ||
161 | let local_env = self.app.lisp_env.last_mut(); | ||
162 | if let Some(env) = local_env { | ||
163 | return env | ||
164 | .insert(id.into(), value) | ||
165 | .ok_or(EvalError::UnboundVariable(id.into()).into()); | ||
166 | } else { | ||
167 | error!("Unable to set in global env!"); | ||
168 | return Err(EvalError::BadForm.into()); | ||
169 | } | ||
170 | } | ||
171 | _ => { | ||
172 | error!("Invalid usage of `set!`"); | ||
132 | return Err(EvalError::BadForm.into()); | 173 | return Err(EvalError::BadForm.into()); |
133 | } | 174 | } |
134 | return Ok(LispExpr::Unit); | ||
135 | } | 175 | } |
136 | _ => { | 176 | } |
137 | error!("Invalid usage of `define`"); | 177 | |
138 | Err(EvalError::BadForm.into()) | 178 | pub fn eval_if(&mut self, args: &[LispExpr]) -> Result<LispExpr, LispError> { |
179 | let arity = Arity::Exact(3); | ||
180 | if !arity.is_valid(args) { | ||
181 | return Err(arity.to_error()); | ||
182 | } else { | ||
183 | match args { | ||
184 | [predicate, then, else_] => { | ||
185 | let predicate = self.eval(&predicate)?; | ||
186 | if matches!(predicate, LispExpr::BoolLit(false)) { | ||
187 | return self.eval(&else_); | ||
188 | } else { | ||
189 | return self.eval(&then); | ||
190 | } | ||
191 | } | ||
192 | _ => { | ||
193 | panic!("panicked at `if` expression") | ||
194 | } | ||
195 | } | ||
139 | } | 196 | } |
140 | } | 197 | } |
141 | } | 198 | } |
142 | 199 | ||
143 | pub fn set_var(args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, LispError> { | 200 | pub fn eval_quote(args: &[LispExpr]) -> Result<LispExpr, LispError> { |
144 | let arity = Arity::Exact(2); | 201 | let arity = Arity::Exact(1); |
145 | if !arity.is_valid(args) { | 202 | if !arity.is_valid(args) { |
146 | return Err(arity.to_error()); | 203 | return Err(arity.to_error()); |
147 | } | 204 | } else { |
148 | match args { | 205 | match &args[0] { |
149 | [LispExpr::Ident(id), expr] => { | 206 | LispExpr::Quote(item, depth) => Ok(LispExpr::Quote(item.clone(), depth + 1)), |
150 | let value = eval(&expr, app)?; | 207 | i @ LispExpr::Unit |
151 | let local_env = app.lisp_env.last_mut(); | 208 | | i @ LispExpr::StringLit(_) |
152 | if let Some(env) = local_env { | 209 | | i @ LispExpr::Char(_) |
153 | return env | 210 | | i @ LispExpr::Number(_) |
154 | .insert(id.into(), value) | 211 | | i @ LispExpr::BoolLit(_) => Ok(i.clone()), |
155 | .ok_or(EvalError::UnboundVariable(id.into()).into()); | 212 | _ => { |
156 | } else { | 213 | let quoted_expr = Box::new(args[0].clone()); |
157 | error!("Unable to set in global env!"); | 214 | Ok(LispExpr::Quote(quoted_expr, 1)) |
158 | return Err(EvalError::BadForm.into()); | ||
159 | } | 215 | } |
160 | } | 216 | } |
161 | _ => { | ||
162 | error!("Invalid usage of `set!`"); | ||
163 | return Err(EvalError::BadForm.into()); | ||
164 | } | ||
165 | } | 217 | } |
166 | } | 218 | } |
167 | 219 | ||
@@ -170,7 +222,6 @@ pub fn create_lambda(cdr: &[LispExpr]) -> Result<LispExpr, LispError> { | |||
170 | if !arity.is_valid(cdr) { | 222 | if !arity.is_valid(cdr) { |
171 | return Err(arity.to_error()); | 223 | return Err(arity.to_error()); |
172 | } | 224 | } |
173 | info!("creating lambda"); | ||
174 | match cdr { | 225 | match cdr { |
175 | [LispExpr::List(params), LispExpr::List(body)] if type_match!(params, (..) => LispExpr::Ident(_)) => | 226 | [LispExpr::List(params), LispExpr::List(body)] if type_match!(params, (..) => LispExpr::Ident(_)) => |
176 | { | 227 | { |
@@ -189,48 +240,7 @@ pub fn create_lambda(cdr: &[LispExpr]) -> Result<LispExpr, LispError> { | |||
189 | } | 240 | } |
190 | } | 241 | } |
191 | 242 | ||
192 | pub fn eval_if(args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, LispError> { | 243 | pub fn lookup(env_list: &[Environment], key: &str) -> Result<LispExpr, LispError> { |
193 | let arity = Arity::Exact(3); | ||
194 | if !arity.is_valid(args) { | ||
195 | return Err(arity.to_error()); | ||
196 | } else { | ||
197 | match args { | ||
198 | [predicate, then, else_] => { | ||
199 | let predicate = eval(&predicate, app)?; | ||
200 | if matches!(predicate, LispExpr::BoolLit(false)) { | ||
201 | return eval(&else_, app); | ||
202 | } else { | ||
203 | return eval(&then, app); | ||
204 | } | ||
205 | } | ||
206 | _ => { | ||
207 | panic!("panicked at `if` expression") | ||
208 | } | ||
209 | } | ||
210 | } | ||
211 | } | ||
212 | |||
213 | pub fn eval_quote(args: &[LispExpr]) -> Result<LispExpr, LispError> { | ||
214 | let arity = Arity::Exact(1); | ||
215 | if !arity.is_valid(args) { | ||
216 | return Err(arity.to_error()); | ||
217 | } else { | ||
218 | match &args[0] { | ||
219 | LispExpr::Quote(item, depth) => Ok(LispExpr::Quote(item.clone(), depth + 1)), | ||
220 | i @ LispExpr::Unit | ||
221 | | i @ LispExpr::StringLit(_) | ||
222 | | i @ LispExpr::Char(_) | ||
223 | | i @ LispExpr::Number(_) | ||
224 | | i @ LispExpr::BoolLit(_) => Ok(i.clone()), | ||
225 | _ => { | ||
226 | let quoted_expr = Box::new(args[0].clone()); | ||
227 | Ok(LispExpr::Quote(quoted_expr, 1)) | ||
228 | } | ||
229 | } | ||
230 | } | ||
231 | } | ||
232 | |||
233 | pub fn lookup_extended(env_list: &[Environment], key: &str) -> Result<LispExpr, LispError> { | ||
234 | if env_list.is_empty() { | 244 | if env_list.is_empty() { |
235 | return Err(EvalError::UnboundVariable(key.into()).into()); | 245 | return Err(EvalError::UnboundVariable(key.into()).into()); |
236 | } else { | 246 | } else { |
@@ -238,55 +248,7 @@ pub fn lookup_extended(env_list: &[Environment], key: &str) -> Result<LispExpr, | |||
238 | if let Some(val) = local_env.get(key) { | 248 | if let Some(val) = local_env.get(key) { |
239 | return Ok(val.clone()); | 249 | return Ok(val.clone()); |
240 | } else { | 250 | } else { |
241 | return lookup_extended(&env_list[..env_list.len() - 1], key); | 251 | return lookup(&env_list[..env_list.len() - 1], key); |
242 | } | 252 | } |
243 | } | 253 | } |
244 | } | 254 | } |
245 | |||
246 | #[cfg(test)] | ||
247 | mod tests { | ||
248 | use super::*; | ||
249 | |||
250 | use crate::lisp::{expr::LispExpr, lex::Lexer, number::LispNumber, parse::Parser}; | ||
251 | |||
252 | fn run(code: &str, app: &mut AppState) -> LispExpr { | ||
253 | let mut parser = Parser::new(Lexer::new(code, 0)); | ||
254 | eval(&parser.parse_single_expr().unwrap(), app).unwrap() | ||
255 | } | ||
256 | |||
257 | #[test] | ||
258 | fn eval_all() { | ||
259 | let sdl_context = sdl2::init().unwrap(); | ||
260 | let ttf_context = sdl2::ttf::init().unwrap(); | ||
261 | let mut app = AppState::init(100, 100, &sdl_context, &ttf_context, None, None); | ||
262 | eval_arithmetic(&mut app); | ||
263 | eval_logical(&mut app); | ||
264 | } | ||
265 | |||
266 | fn eval_arithmetic(app: &mut AppState) { | ||
267 | assert_eq!( | ||
268 | run("(+ 1 2 3)", app), | ||
269 | LispExpr::Number(LispNumber::Integer(6)) | ||
270 | ); | ||
271 | assert_eq!( | ||
272 | run("(+ 1.1 2.2 3.3)", app), | ||
273 | LispExpr::Number(LispNumber::Float(6.6)) | ||
274 | ); | ||
275 | assert_eq!( | ||
276 | run("(* 1 2 3 4 5)", app), | ||
277 | LispExpr::Number(LispNumber::Integer(120)) | ||
278 | ); | ||
279 | assert_eq!(run("(< 1 2)", app), LispExpr::BoolLit(true)); | ||
280 | assert_eq!(run("(> 6 5 4 3 2 1)", app), LispExpr::BoolLit(true)); | ||
281 | assert_eq!(run("(< 1 2 3 4 5 6)", app), LispExpr::BoolLit(true)); | ||
282 | assert_eq!(run("(>= 5 5 4 3 2 1)", app), LispExpr::BoolLit(true)); | ||
283 | assert_eq!(run("(<= 2 2 3 4 5 6)", app), LispExpr::BoolLit(true)); | ||
284 | } | ||
285 | |||
286 | fn eval_logical(app: &mut AppState) { | ||
287 | assert_eq!(run("(and #t #t)", app), LispExpr::BoolLit(true)); | ||
288 | assert_eq!(run("(or #f #t)", app), LispExpr::BoolLit(true)); | ||
289 | assert_eq!(run("(not #t)", app), LispExpr::BoolLit(false)); | ||
290 | assert_eq!(run("(not #f)", app), run("(not (not #t))", app)); | ||
291 | } | ||
292 | } | ||
diff --git a/src/lisp/expr.rs b/src/lisp/expr.rs index 9f2dc8d..1f3e0fb 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_extended, | 6 | eval::{lookup, Evaluator}, |
7 | number::LispNumber, | 7 | number::LispNumber, |
8 | EnvList, | 8 | EnvList, |
9 | }; | 9 | }; |
@@ -36,6 +36,7 @@ impl Arity { | |||
36 | pub struct PrimitiveFunc { | 36 | pub struct PrimitiveFunc { |
37 | pub arity: Arity, // minimim arity | 37 | pub arity: Arity, // minimim arity |
38 | pub closure: fn(&[LispExpr], &mut AppState) -> Result<LispExpr, LispError>, | 38 | pub closure: fn(&[LispExpr], &mut AppState) -> Result<LispExpr, LispError>, |
39 | pub name: &'static str, | ||
39 | } | 40 | } |
40 | 41 | ||
41 | impl PrimitiveFunc { | 42 | impl PrimitiveFunc { |
@@ -123,8 +124,8 @@ impl LispExpr { | |||
123 | (LispExpr::Char(s), LispExpr::Char(o)) => Ok(s == o), | 124 | (LispExpr::Char(s), LispExpr::Char(o)) => Ok(s == o), |
124 | (LispExpr::BoolLit(s), LispExpr::BoolLit(o)) => Ok(s == o), | 125 | (LispExpr::BoolLit(s), LispExpr::BoolLit(o)) => Ok(s == o), |
125 | (LispExpr::Ident(s), LispExpr::Ident(o)) => { | 126 | (LispExpr::Ident(s), LispExpr::Ident(o)) => { |
126 | let s = lookup_extended(envs, s)?; | 127 | let s = lookup(envs, s)?; |
127 | let o = lookup_extended(envs, o)?; | 128 | let o = lookup(envs, o)?; |
128 | s.compare(&o, envs) | 129 | s.compare(&o, envs) |
129 | } | 130 | } |
130 | (LispExpr::PrimitiveFunc(s), LispExpr::PrimitiveFunc(o)) => { | 131 | (LispExpr::PrimitiveFunc(s), LispExpr::PrimitiveFunc(o)) => { |
diff --git a/src/lisp/prelude.rs b/src/lisp/prelude.rs index dffd9f4..27e7ad2 100644 --- a/src/lisp/prelude.rs +++ b/src/lisp/prelude.rs | |||
@@ -2,7 +2,7 @@ use crate::{ | |||
2 | brush::Brush, | 2 | brush::Brush, |
3 | lisp::{ | 3 | lisp::{ |
4 | error::{EvalError, LispError}, | 4 | error::{EvalError, LispError}, |
5 | eval::eval, | 5 | eval::Evaluator, |
6 | expr::{Arity, LispExpr}, | 6 | expr::{Arity, LispExpr}, |
7 | number::LispNumber, | 7 | number::LispNumber, |
8 | Environment, | 8 | Environment, |
@@ -21,6 +21,7 @@ macro_rules! primitive { | |||
21 | let val = crate::lisp::expr::LispExpr::PrimitiveFunc(crate::lisp::expr::PrimitiveFunc { | 21 | let val = crate::lisp::expr::LispExpr::PrimitiveFunc(crate::lisp::expr::PrimitiveFunc { |
22 | arity: $arity, | 22 | arity: $arity, |
23 | closure: $closure, | 23 | closure: $closure, |
24 | name: $name, | ||
24 | }); | 25 | }); |
25 | let _ = $env.insert($name.to_string(), val); | 26 | let _ = $env.insert($name.to_string(), val); |
26 | }; | 27 | }; |
@@ -241,41 +242,15 @@ pub fn new_env() -> Result<Environment, LispError> { | |||
241 | return Ok(LispExpr::Unit); | 242 | return Ok(LispExpr::Unit); |
242 | }); | 243 | }); |
243 | 244 | ||
244 | primitive!(env, Arity::Exact(2), "map", |args, app| { | 245 | primitive!(env, Arity::Exact(2), "cons", |args, _| { |
245 | let mut apply_map = | 246 | if type_match!(args, 1 => LispExpr::Unit) { |
246 | |func: &LispExpr, ls: &Vec<LispExpr>| -> Result<Vec<LispExpr>, LispError> { | 247 | return Ok(LispExpr::List(vec![args[0].clone()])); |
247 | ls.into_iter() | 248 | } else if !type_match!(args, 1 => LispExpr::List(_)) { |
248 | .map(|arg| eval(&LispExpr::List(vec![func.clone(), arg.clone()]), app)) | 249 | return Ok(LispExpr::List(vec![args[0].clone(), args[1].clone()])); |
249 | .collect() | ||
250 | }; | ||
251 | if matches!(&args[0], LispExpr::Function(_) | LispExpr::PrimitiveFunc(_)) { | ||
252 | match &args[1] { | ||
253 | LispExpr::List(ls) => return Ok(LispExpr::List(apply_map(&args[0], ls)?)), | ||
254 | _ => return Err(EvalError::TypeMismatch.into()), | ||
255 | } | ||
256 | } else { | ||
257 | return Err(EvalError::TypeMismatch.into()); | ||
258 | } | ||
259 | }); | ||
260 | |||
261 | primitive!(env, Arity::Exact(2), "filter", |args, app| { | ||
262 | let mut apply_filter = | ||
263 | |func: &LispExpr, ls: &Vec<LispExpr>| -> Result<Vec<LispExpr>, LispError> { | ||
264 | let mut result = vec![]; | ||
265 | for arg in ls.into_iter() { | ||
266 | if eval(&LispExpr::List(vec![func.clone(), arg.clone()]), app)?.cast_bool() { | ||
267 | result.push(arg.clone()) | ||
268 | } | ||
269 | } | ||
270 | Ok(result) | ||
271 | }; | ||
272 | if matches!(&args[0], LispExpr::Function(_) | LispExpr::PrimitiveFunc(_)) { | ||
273 | match &args[1] { | ||
274 | LispExpr::List(ls) => return Ok(LispExpr::List(apply_filter(&args[0], ls)?)), | ||
275 | _ => return Err(EvalError::TypeMismatch.into()), | ||
276 | } | ||
277 | } else { | 250 | } else { |
278 | return Err(EvalError::TypeMismatch.into()); | 251 | let mut rest = args[1].unwrap_list(); |
252 | rest.insert(0, args[0].clone()); | ||
253 | return Ok(LispExpr::List(rest)); | ||
279 | } | 254 | } |
280 | }); | 255 | }); |
281 | 256 | ||
@@ -332,5 +307,17 @@ pub fn new_env() -> Result<Environment, LispError> { | |||
332 | } | 307 | } |
333 | }); | 308 | }); |
334 | 309 | ||
310 | primitive!(env, Arity::Exact(2), "assert-eq", |args, app| { | ||
311 | if args[0].compare(&args[1], &app.lisp_env)? { | ||
312 | return Ok(LispExpr::Unit); | ||
313 | } else { | ||
314 | return Err(EvalError::AssertionError { | ||
315 | expected: args[0].clone(), | ||
316 | got: args[1].clone(), | ||
317 | } | ||
318 | .into()); | ||
319 | } | ||
320 | }); | ||
321 | |||
335 | Ok(env) | 322 | Ok(env) |
336 | } | 323 | } |
diff --git a/src/lisp/std.lisp b/src/lisp/std.lisp index c723a13..8350482 100644 --- a/src/lisp/std.lisp +++ b/src/lisp/std.lisp | |||
@@ -22,5 +22,27 @@ | |||
22 | accumulator | 22 | accumulator |
23 | (cdr ls)))) | 23 | (cdr ls)))) |
24 | 24 | ||
25 | (define (map func ls) | ||
26 | (if (null? ls) | ||
27 | '() | ||
28 | (cons (func (car ls)) | ||
29 | (map func (cdr ls))))) | ||
30 | |||
31 | (define (filter pred ls) | ||
32 | (if (null? ls) | ||
33 | '() | ||
34 | (if (pred (car ls)) | ||
35 | (cons (car ls) (filter pred (cdr ls))) | ||
36 | (filter pred (cdr ls))))) | ||
37 | |||
38 | (define (member? item ls) | ||
39 | (if (null? ls) | ||
40 | #f | ||
41 | (or (eq? item (car ls)) | ||
42 | (member? item (cdr ls))))) | ||
43 | |||
44 | (define (assert expr) | ||
45 | (assert-eq #t expr)) | ||
46 | |||
25 | (define (sum ls) (fold 0 + ls)) | 47 | (define (sum ls) (fold 0 + ls)) |
26 | (define (product ls) (fold 1 * ls)) | 48 | (define (product ls) (fold 1 * ls)) |
diff --git a/src/utils.rs b/src/utils.rs index fc96615..dcf652d 100644 --- a/src/utils.rs +++ b/src/utils.rs | |||
@@ -3,7 +3,7 @@ use crate::{ | |||
3 | consts::FONT_PATH, | 3 | consts::FONT_PATH, |
4 | lisp::{ | 4 | lisp::{ |
5 | error::{EvalError, LispError, ParseError}, | 5 | error::{EvalError, LispError, ParseError}, |
6 | eval::eval, | 6 | eval::Evaluator, |
7 | expr::LispExpr, | 7 | expr::LispExpr, |
8 | lex::Lexer, | 8 | lex::Lexer, |
9 | parse::Parser, | 9 | parse::Parser, |
@@ -80,10 +80,14 @@ pub fn load_script<P: AsRef<Path>>(path: P, app: &mut AppState) -> Result<(), Li | |||
80 | .map_err(EvalError::ScriptLoadError)?; | 80 | .map_err(EvalError::ScriptLoadError)?; |
81 | 81 | ||
82 | let mut parser = Parser::new(Lexer::new(&buf, 0)); | 82 | let mut parser = Parser::new(Lexer::new(&buf, 0)); |
83 | let mut evaluator = Evaluator { | ||
84 | app, | ||
85 | context: Vec::new(), | ||
86 | }; | ||
83 | for expr in parser.parse_exprs().map_err(|err| { | 87 | for expr in parser.parse_exprs().map_err(|err| { |
84 | LispError::Stringified(err.display(&buf, path.as_ref().to_str().unwrap_or("<unknown>"))) | 88 | LispError::Stringified(err.display(&buf, path.as_ref().to_str().unwrap_or("<unknown>"))) |
85 | })? { | 89 | })? { |
86 | eval(&expr, app)?; | 90 | evaluator.eval(&expr)?; |
87 | } | 91 | } |
88 | return Ok(()); | 92 | return Ok(()); |
89 | } | 93 | } |