diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/lisp/env.rs | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/src/lisp/env.rs b/src/lisp/env.rs new file mode 100644 index 0000000..ad7cc2b --- /dev/null +++ b/src/lisp/env.rs | |||
@@ -0,0 +1,117 @@ | |||
1 | use crate::{ | ||
2 | app::AppState, | ||
3 | lisp::{error::LispError, expr::LispExpr, number::LispNumber, Environment}, | ||
4 | }; | ||
5 | |||
6 | pub fn is_bound<S: AsRef<str>>(env: &mut Environment, name: S) -> bool { | ||
7 | env.get(name.as_ref()).is_some() | ||
8 | } | ||
9 | |||
10 | pub fn new_binding<S: AsRef<str>>(env: &mut Environment, name: S, value: LispExpr) { | ||
11 | let _ = env.insert(name.as_ref().into(), value); | ||
12 | } | ||
13 | |||
14 | pub fn with_prelude() -> Environment { | ||
15 | let mut env = Environment::new(); | ||
16 | new_binding( | ||
17 | &mut env, | ||
18 | "+", | ||
19 | LispExpr::PrimitiveFunc(|args, _| { | ||
20 | if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { | ||
21 | Err(LispError::EvalError) | ||
22 | } else { | ||
23 | let result = args | ||
24 | .iter() | ||
25 | .map(|n| unwrap_number(n)) | ||
26 | .fold(LispNumber::Integer(0), |acc, x| acc + *x); | ||
27 | Ok(LispExpr::Number(result)) | ||
28 | } | ||
29 | }), | ||
30 | ); | ||
31 | env | ||
32 | } | ||
33 | |||
34 | pub fn eval(expr: &LispExpr, app: &mut AppState) -> Result<LispExpr, LispError> { | ||
35 | let env = &mut app.lisp_env; | ||
36 | match expr { | ||
37 | LispExpr::Unit => Ok(expr.clone()), | ||
38 | LispExpr::StringLit(_) => Ok(expr.clone()), | ||
39 | LispExpr::Number(_) => Ok(expr.clone()), | ||
40 | LispExpr::BoolLit(_) => Ok(expr.clone()), | ||
41 | LispExpr::Ident(ref id) => env | ||
42 | .get(id) | ||
43 | .ok_or_else(|| LispError::EvalError) | ||
44 | .map(Clone::clone), | ||
45 | LispExpr::List(li) => { | ||
46 | let head = &li[0]; | ||
47 | match head { | ||
48 | LispExpr::Ident(func) => { | ||
49 | let func_expr = env | ||
50 | .get(func) | ||
51 | .map(Clone::clone) | ||
52 | .ok_or_else(|| LispError::EvalError)?; | ||
53 | match func_expr { | ||
54 | LispExpr::PrimitiveFunc(f) => { | ||
55 | let mut args = Vec::new(); | ||
56 | for item in li[1..].iter() { | ||
57 | args.push(eval(item, app)?); | ||
58 | } | ||
59 | (f)(&args, None) | ||
60 | } | ||
61 | _ => Err(LispError::EvalError), | ||
62 | } | ||
63 | } | ||
64 | LispExpr::List(_) => { | ||
65 | // TODO | ||
66 | todo!(); | ||
67 | } | ||
68 | _ => Err(LispError::EvalError), | ||
69 | } | ||
70 | } | ||
71 | _ => Err(LispError::ParseError), | ||
72 | } | ||
73 | } | ||
74 | |||
75 | pub fn unwrap_number(n: &LispExpr) -> &LispNumber { | ||
76 | match n { | ||
77 | LispExpr::Number(i) => i, | ||
78 | _ => panic!("unwrap_number expected number"), | ||
79 | } | ||
80 | } | ||
81 | |||
82 | #[cfg(test)] | ||
83 | mod tests { | ||
84 | use super::*; | ||
85 | |||
86 | #[test] | ||
87 | fn eval_primitive_call() { | ||
88 | let mut env = Environment::new(); | ||
89 | new_binding(&mut env, "age", LispExpr::Number(LispNumber::Float(1.4))); | ||
90 | new_binding( | ||
91 | &mut env, | ||
92 | "+", | ||
93 | LispExpr::PrimitiveFunc(|args, _| { | ||
94 | if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { | ||
95 | Err(LispError::EvalError) | ||
96 | } else { | ||
97 | let result = args | ||
98 | .iter() | ||
99 | .map(|n| unwrap_number(n)) | ||
100 | .fold(LispNumber::Integer(0), |acc, x| acc + *x); | ||
101 | Ok(LispExpr::Number(result)) | ||
102 | } | ||
103 | }), | ||
104 | ); | ||
105 | let mut numbers = (1..=3) | ||
106 | .map(LispNumber::Integer) | ||
107 | .map(LispExpr::Number) | ||
108 | .collect::<Vec<_>>(); | ||
109 | let mut expr = Vec::new(); | ||
110 | expr.push(LispExpr::Ident("+".into())); | ||
111 | expr.append(&mut numbers); | ||
112 | // assert!(matches!( | ||
113 | // eval(&LispExpr::List(expr), ).unwrap(), | ||
114 | // LispExpr::Number(LispNumber::Integer(6)) | ||
115 | // )); | ||
116 | } | ||
117 | } | ||