aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkshay <[email protected]>2021-03-24 07:34:17 +0000
committerAkshay <[email protected]>2021-03-24 07:34:17 +0000
commitca48cd3a51a19f35f6d2b25a5b3c011c759b6be3 (patch)
tree7ec0311795332f9f2ca5c5ff00d270338022a6d2
parent0c3b0c10791c332537b4050e33429feb09db7c62 (diff)
add lisp primitives: set!, define, lambda, if
-rw-r--r--src/lisp/env.rs206
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 @@
1use crate::{ 1use 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
7use log::warn; 12use std::collections::HashMap;
8 13
9pub fn is_bound<S: AsRef<str>>(env: &mut Environment, name: S) -> bool { 14use log::{error, info};
10 env.get(name.as_ref()).is_some()
11}
12
13pub 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
17pub fn with_prelude() -> Environment { 16pub 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
55pub fn eval(expr: &LispExpr, app: &mut AppState) -> Result<LispExpr, LispError> { 83pub 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
147pub 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
165pub 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
185pub 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
211pub 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
96pub fn unwrap_number(n: &LispExpr) -> &LispNumber { 225pub 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
232pub fn unwrap_ident(i: LispExpr) -> String {
233 match i {
234 LispExpr::Ident(i) => i,
235 _ => panic!("unwrap_ident expected string"),
236 }
237}