diff options
author | Akshay <[email protected]> | 2021-03-24 07:34:17 +0000 |
---|---|---|
committer | Akshay <[email protected]> | 2021-03-24 07:34:17 +0000 |
commit | ca48cd3a51a19f35f6d2b25a5b3c011c759b6be3 (patch) | |
tree | 7ec0311795332f9f2ca5c5ff00d270338022a6d2 | |
parent | 0c3b0c10791c332537b4050e33429feb09db7c62 (diff) |
add lisp primitives: set!, define, lambda, if
-rw-r--r-- | src/lisp/env.rs | 206 |
1 files changed, 171 insertions, 35 deletions
diff --git a/src/lisp/env.rs b/src/lisp/env.rs index 94c0e05..6338f91 100644 --- a/src/lisp/env.rs +++ b/src/lisp/env.rs | |||
@@ -1,18 +1,17 @@ | |||
1 | use crate::{ | 1 | use crate::{ |
2 | app::AppState, | 2 | app::AppState, |
3 | lisp::{error::LispError, expr::LispExpr, number::LispNumber, Environment}, | 3 | lisp::{ |
4 | error::LispError, | ||
5 | expr::{LispExpr, LispFunction}, | ||
6 | number::LispNumber, | ||
7 | Environment, | ||
8 | }, | ||
4 | primitive, | 9 | primitive, |
5 | }; | 10 | }; |
6 | 11 | ||
7 | use log::warn; | 12 | use std::collections::HashMap; |
8 | 13 | ||
9 | pub fn is_bound<S: AsRef<str>>(env: &mut Environment, name: S) -> bool { | 14 | use log::{error, info}; |
10 | env.get(name.as_ref()).is_some() | ||
11 | } | ||
12 | |||
13 | pub fn new_binding<S: AsRef<str>>(env: &mut Environment, name: S, value: LispExpr) { | ||
14 | let _ = env.insert(name.as_ref().into(), value); | ||
15 | } | ||
16 | 15 | ||
17 | pub fn with_prelude() -> Environment { | 16 | pub fn with_prelude() -> Environment { |
18 | let mut env = Environment::new(); | 17 | let mut env = Environment::new(); |
@@ -27,7 +26,7 @@ pub fn with_prelude() -> Environment { | |||
27 | )) | 26 | )) |
28 | } | 27 | } |
29 | }); | 28 | }); |
30 | primitive!(env, Some(2), "sub", |args, _| { | 29 | primitive!(env, Some(2), "-", |args, _| { |
31 | if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { | 30 | if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { |
32 | Err(LispError::EvalError) | 31 | Err(LispError::EvalError) |
33 | } else { | 32 | } else { |
@@ -49,43 +48,95 @@ pub fn with_prelude() -> Environment { | |||
49 | )) | 48 | )) |
50 | } | 49 | } |
51 | }); | 50 | }); |
51 | primitive!(env, Some(0), "toggle-grid", |_, app| { | ||
52 | app.toggle_grid(); | ||
53 | Ok(LispExpr::Unit) | ||
54 | }); | ||
55 | primitive!(env, Some(3), "if", |args, _| { | ||
56 | match args { | ||
57 | [predicate, then, else_] => { | ||
58 | if matches!(predicate, LispExpr::BoolLit(false)) { | ||
59 | Ok(else_.clone()) | ||
60 | } else { | ||
61 | Ok(then.clone()) | ||
62 | } | ||
63 | } | ||
64 | _ => { | ||
65 | error!("invalid args for `if` primitive"); | ||
66 | Err(LispError::EvalError) | ||
67 | } | ||
68 | } | ||
69 | }); | ||
70 | primitive!(env, Some(1), "and", |args, _| { | ||
71 | if args | ||
72 | .iter() | ||
73 | .any(|arg| matches!(arg, LispExpr::BoolLit(false))) | ||
74 | { | ||
75 | Ok(LispExpr::BoolLit(false)) | ||
76 | } else { | ||
77 | Ok(LispExpr::BoolLit(true)) | ||
78 | } | ||
79 | }); | ||
52 | env | 80 | env |
53 | } | 81 | } |
54 | 82 | ||
55 | pub fn eval(expr: &LispExpr, app: &mut AppState) -> Result<LispExpr, LispError> { | 83 | pub fn eval( |
56 | let env = &mut app.lisp_env; | 84 | expr: &LispExpr, |
85 | app: &mut AppState, | ||
86 | extra_env: Option<&Environment>, | ||
87 | ) -> Result<LispExpr, LispError> { | ||
57 | match expr { | 88 | match expr { |
58 | LispExpr::Unit => Ok(expr.clone()), | 89 | LispExpr::Unit => Ok(expr.clone()), |
59 | LispExpr::StringLit(_) => Ok(expr.clone()), | 90 | LispExpr::StringLit(_) => Ok(expr.clone()), |
60 | LispExpr::Number(_) => Ok(expr.clone()), | 91 | LispExpr::Number(_) => Ok(expr.clone()), |
61 | LispExpr::BoolLit(_) => Ok(expr.clone()), | 92 | LispExpr::BoolLit(_) => Ok(expr.clone()), |
62 | LispExpr::Ident(ref id) => env | 93 | LispExpr::Ident(ref id) => { |
63 | .get(id) | 94 | lookup_extended(extra_env.unwrap_or(&Environment::new()), &app.lisp_env, id) |
64 | .ok_or_else(|| LispError::EvalError) | 95 | } |
65 | .map(Clone::clone), | ||
66 | LispExpr::List(li) => { | 96 | LispExpr::List(li) => { |
67 | let head = &li[0]; | 97 | let func_expr = &li[0]; |
68 | match head { | 98 | match func_expr { |
69 | LispExpr::Ident(func) => { | 99 | LispExpr::Ident(s) => match s.as_ref() { |
70 | let func_expr = env | 100 | "define" => define_var(&li[1..], app), |
71 | .get(func) | 101 | "set!" => set_var(&li[1..], app), |
72 | .map(Clone::clone) | 102 | "lambda" => create_lambda(&li[1..]), |
73 | .ok_or_else(|| LispError::EvalError)?; | 103 | _ => { |
74 | match func_expr { | 104 | let func_expr = eval(&func_expr, app, extra_env)?; |
75 | LispExpr::PrimitiveFunc(f) => { | 105 | match func_expr { |
76 | let mut args = Vec::new(); | 106 | LispExpr::PrimitiveFunc(f) => { |
77 | for item in li[1..].iter() { | 107 | let mut args = Vec::new(); |
78 | args.push(eval(item, app)?); | 108 | for item in li[1..].iter() { |
109 | args.push(eval(item, app, extra_env)?); | ||
110 | } | ||
111 | f.call(&args, app) | ||
79 | } | 112 | } |
80 | f.call(&args, app) | 113 | LispExpr::Function(f) => { |
114 | info!("eval custom func"); | ||
115 | let mut args = Vec::new(); | ||
116 | for item in li[1..].iter() { | ||
117 | args.push(eval(item, app, extra_env)?); | ||
118 | } | ||
119 | if f.params.len() != args.len() { | ||
120 | info!("too many or too little number of args"); | ||
121 | Err(LispError::EvalError) // too many or too little number of args | ||
122 | } else { | ||
123 | let mut local_env: Environment = | ||
124 | f.params.into_iter().zip(args).collect(); | ||
125 | local_env.extend(app.lisp_env.clone()); | ||
126 | if let Some(env) = extra_env { | ||
127 | local_env.extend(env.clone()); | ||
128 | } | ||
129 | if f.body.is_empty() { | ||
130 | return Ok(LispExpr::Unit); | ||
131 | } else { | ||
132 | eval(&LispExpr::List(f.body), app, Some(&local_env)) | ||
133 | } | ||
134 | } | ||
135 | } | ||
136 | _ => Err(LispError::EvalError), | ||
81 | } | 137 | } |
82 | _ => Err(LispError::EvalError), | ||
83 | } | 138 | } |
84 | } | 139 | }, |
85 | LispExpr::List(_) => { | ||
86 | // TODO | ||
87 | todo!(); | ||
88 | } | ||
89 | _ => Err(LispError::EvalError), | 140 | _ => Err(LispError::EvalError), |
90 | } | 141 | } |
91 | } | 142 | } |
@@ -93,9 +144,94 @@ pub fn eval(expr: &LispExpr, app: &mut AppState) -> Result<LispExpr, LispError> | |||
93 | } | 144 | } |
94 | } | 145 | } |
95 | 146 | ||
147 | pub fn define_var(args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, LispError> { | ||
148 | if args.len() != 2 { | ||
149 | error!("Invalid arity for `define`"); | ||
150 | return Err(LispError::EvalError); | ||
151 | } | ||
152 | match args { | ||
153 | [LispExpr::Ident(id), expr] => { | ||
154 | let value = eval(&expr, app, None)?; | ||
155 | app.lisp_env.insert(id.into(), value); | ||
156 | return Ok(LispExpr::Unit); | ||
157 | } | ||
158 | _ => { | ||
159 | error!("Invalid usage of `define`"); | ||
160 | return Err(LispError::EvalError); | ||
161 | } | ||
162 | } | ||
163 | } | ||
164 | |||
165 | pub fn set_var(args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, LispError> { | ||
166 | if args.len() != 2 { | ||
167 | error!("Invalid arity for `define`"); | ||
168 | return Err(LispError::EvalError); | ||
169 | } | ||
170 | match args { | ||
171 | [LispExpr::Ident(id), expr] => { | ||
172 | let value = eval(&expr, app, None)?; | ||
173 | return app | ||
174 | .lisp_env | ||
175 | .insert(id.into(), value) | ||
176 | .ok_or(LispError::EvalError); | ||
177 | } | ||
178 | _ => { | ||
179 | error!("Invalid usage of `define`"); | ||
180 | return Err(LispError::EvalError); | ||
181 | } | ||
182 | } | ||
183 | } | ||
184 | |||
185 | pub fn create_lambda(cdr: &[LispExpr]) -> Result<LispExpr, LispError> { | ||
186 | if cdr.len() != 2 { | ||
187 | // needs params and body | ||
188 | error!("needs params and body"); | ||
189 | return Err(LispError::EvalError); | ||
190 | } | ||
191 | info!("creating lambda"); | ||
192 | match cdr { | ||
193 | [LispExpr::List(params), LispExpr::List(body)] | ||
194 | if params.iter().all(|p| matches!(p, LispExpr::Ident(_))) => | ||
195 | { | ||
196 | return Ok(LispExpr::Function(LispFunction { | ||
197 | params: params | ||
198 | .into_iter() | ||
199 | .map(|p| unwrap_ident(p.clone())) | ||
200 | .collect::<Vec<_>>(), | ||
201 | body: body.clone(), | ||
202 | })); | ||
203 | } | ||
204 | _ => { | ||
205 | error!("Invalid usage of `define`"); | ||
206 | return Err(LispError::EvalError); | ||
207 | } | ||
208 | } | ||
209 | } | ||
210 | |||
211 | pub fn lookup_extended( | ||
212 | local: &Environment, | ||
213 | super_: &Environment, | ||
214 | key: &str, | ||
215 | ) -> Result<LispExpr, LispError> { | ||
216 | if let Some(e) = local.get(key) { | ||
217 | Ok(e.clone()) | ||
218 | } else if let Some(e) = super_.get(key) { | ||
219 | Ok(e.clone()) | ||
220 | } else { | ||
221 | Err(LispError::EvalError) | ||
222 | } | ||
223 | } | ||
224 | |||
96 | pub fn unwrap_number(n: &LispExpr) -> &LispNumber { | 225 | pub fn unwrap_number(n: &LispExpr) -> &LispNumber { |
97 | match n { | 226 | match n { |
98 | LispExpr::Number(i) => i, | 227 | LispExpr::Number(i) => i, |
99 | _ => panic!("unwrap_number expected number"), | 228 | _ => panic!("unwrap_number expected number"), |
100 | } | 229 | } |
101 | } | 230 | } |
231 | |||
232 | pub fn unwrap_ident(i: LispExpr) -> String { | ||
233 | match i { | ||
234 | LispExpr::Ident(i) => i, | ||
235 | _ => panic!("unwrap_ident expected string"), | ||
236 | } | ||
237 | } | ||