diff options
author | Akshay <[email protected]> | 2021-03-23 13:26:25 +0000 |
---|---|---|
committer | Akshay <[email protected]> | 2021-03-23 13:26:25 +0000 |
commit | 305bf638f823a41f391936712eef302bc6733d00 (patch) | |
tree | 6a9cc8a41c691bed2027c8debdc4391aab837c89 /src/lisp | |
parent | a0fce05399b3ee284b6c60a409fad74c23432ce8 (diff) |
expose functions to lisp interface, add primitives with macros
Diffstat (limited to 'src/lisp')
-rw-r--r-- | src/lisp/env.rs | 88 | ||||
-rw-r--r-- | src/lisp/expr.rs | 19 | ||||
-rw-r--r-- | src/lisp/lex.rs | 5 | ||||
-rw-r--r-- | src/lisp/mod.rs | 2 |
4 files changed, 57 insertions, 57 deletions
diff --git a/src/lisp/env.rs b/src/lisp/env.rs index ad7cc2b..94c0e05 100644 --- a/src/lisp/env.rs +++ b/src/lisp/env.rs | |||
@@ -1,8 +1,11 @@ | |||
1 | use crate::{ | 1 | use crate::{ |
2 | app::AppState, | 2 | app::AppState, |
3 | lisp::{error::LispError, expr::LispExpr, number::LispNumber, Environment}, | 3 | lisp::{error::LispError, expr::LispExpr, number::LispNumber, Environment}, |
4 | primitive, | ||
4 | }; | 5 | }; |
5 | 6 | ||
7 | use log::warn; | ||
8 | |||
6 | pub fn is_bound<S: AsRef<str>>(env: &mut Environment, name: S) -> bool { | 9 | pub fn is_bound<S: AsRef<str>>(env: &mut Environment, name: S) -> bool { |
7 | env.get(name.as_ref()).is_some() | 10 | env.get(name.as_ref()).is_some() |
8 | } | 11 | } |
@@ -13,21 +16,39 @@ pub fn new_binding<S: AsRef<str>>(env: &mut Environment, name: S, value: LispExp | |||
13 | 16 | ||
14 | pub fn with_prelude() -> Environment { | 17 | pub fn with_prelude() -> Environment { |
15 | let mut env = Environment::new(); | 18 | let mut env = Environment::new(); |
16 | new_binding( | 19 | primitive!(env, Some(2), "+", |args, _| { |
17 | &mut env, | 20 | if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { |
18 | "+", | 21 | Err(LispError::EvalError) |
19 | LispExpr::PrimitiveFunc(|args, _| { | 22 | } else { |
20 | if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { | 23 | Ok(LispExpr::Number( |
21 | Err(LispError::EvalError) | 24 | args.iter() |
22 | } else { | 25 | .map(|arg| unwrap_number(arg)) |
23 | let result = args | 26 | .fold(LispNumber::Integer(0), |acc, x| acc + *x), |
24 | .iter() | 27 | )) |
25 | .map(|n| unwrap_number(n)) | 28 | } |
26 | .fold(LispNumber::Integer(0), |acc, x| acc + *x); | 29 | }); |
27 | Ok(LispExpr::Number(result)) | 30 | primitive!(env, Some(2), "sub", |args, _| { |
31 | if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { | ||
32 | Err(LispError::EvalError) | ||
33 | } else { | ||
34 | let mut acc = unwrap_number(&args[0]).clone(); | ||
35 | for arg in args.into_iter().skip(1) { | ||
36 | acc = acc - *unwrap_number(&arg); | ||
28 | } | 37 | } |
29 | }), | 38 | Ok(LispExpr::Number(acc)) |
30 | ); | 39 | } |
40 | }); | ||
41 | primitive!(env, Some(2), "*", |args, _| { | ||
42 | if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { | ||
43 | Err(LispError::EvalError) | ||
44 | } else { | ||
45 | Ok(LispExpr::Number( | ||
46 | args.iter() | ||
47 | .map(|arg| unwrap_number(arg)) | ||
48 | .fold(LispNumber::Integer(1), |acc, x| acc * *x), | ||
49 | )) | ||
50 | } | ||
51 | }); | ||
31 | env | 52 | env |
32 | } | 53 | } |
33 | 54 | ||
@@ -56,7 +77,7 @@ pub fn eval(expr: &LispExpr, app: &mut AppState) -> Result<LispExpr, LispError> | |||
56 | for item in li[1..].iter() { | 77 | for item in li[1..].iter() { |
57 | args.push(eval(item, app)?); | 78 | args.push(eval(item, app)?); |
58 | } | 79 | } |
59 | (f)(&args, None) | 80 | f.call(&args, app) |
60 | } | 81 | } |
61 | _ => Err(LispError::EvalError), | 82 | _ => Err(LispError::EvalError), |
62 | } | 83 | } |
@@ -78,40 +99,3 @@ pub fn unwrap_number(n: &LispExpr) -> &LispNumber { | |||
78 | _ => panic!("unwrap_number expected number"), | 99 | _ => panic!("unwrap_number expected number"), |
79 | } | 100 | } |
80 | } | 101 | } |
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 | } | ||
diff --git a/src/lisp/expr.rs b/src/lisp/expr.rs index 4676a3e..adacc1c 100644 --- a/src/lisp/expr.rs +++ b/src/lisp/expr.rs | |||
@@ -11,7 +11,7 @@ pub enum LispExpr { | |||
11 | StringLit(String), | 11 | StringLit(String), |
12 | BoolLit(bool), | 12 | BoolLit(bool), |
13 | Ident(String), | 13 | Ident(String), |
14 | PrimitiveFunc(fn(&[LispExpr], Option<&mut AppState>) -> Result<LispExpr, LispError>), | 14 | PrimitiveFunc(PrimitiveFunc), |
15 | Function(LispFunction), | 15 | Function(LispFunction), |
16 | 16 | ||
17 | // none of these depths should be zero | 17 | // none of these depths should be zero |
@@ -21,6 +21,23 @@ pub enum LispExpr { | |||
21 | Quote(Box<LispExpr>, u32), | 21 | Quote(Box<LispExpr>, u32), |
22 | } | 22 | } |
23 | 23 | ||
24 | #[derive(Clone)] | ||
25 | pub struct PrimitiveFunc { | ||
26 | pub arity: Option<usize>, | ||
27 | pub closure: fn(&[LispExpr], &mut AppState) -> Result<LispExpr, LispError>, | ||
28 | } | ||
29 | |||
30 | impl PrimitiveFunc { | ||
31 | pub fn call(&self, args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, LispError> { | ||
32 | if let Some(arity) = self.arity { | ||
33 | if args.len() < arity { | ||
34 | return Err(LispError::EvalError); | ||
35 | } | ||
36 | } | ||
37 | (self.closure)(args, app) | ||
38 | } | ||
39 | } | ||
40 | |||
24 | impl LispExpr { | 41 | impl LispExpr { |
25 | pub fn comma(self, n: u32) -> LispExpr { | 42 | pub fn comma(self, n: u32) -> LispExpr { |
26 | match self { | 43 | match self { |
diff --git a/src/lisp/lex.rs b/src/lisp/lex.rs index 3b1389d..30f49fa 100644 --- a/src/lisp/lex.rs +++ b/src/lisp/lex.rs | |||
@@ -94,10 +94,7 @@ impl<'a> Lexer<'a> { | |||
94 | self.cur_pos += ch.len_utf8() as u32; | 94 | self.cur_pos += ch.len_utf8() as u32; |
95 | continue; | 95 | continue; |
96 | } | 96 | } |
97 | ch => { | 97 | _ => Err(LispError::ParseError), |
98 | eprintln!("some unexpected character: {}", ch); | ||
99 | Err(LispError::ParseError) | ||
100 | } | ||
101 | }; | 98 | }; |
102 | let (size, token) = match res { | 99 | let (size, token) = match res { |
103 | Ok(v) => v, | 100 | Ok(v) => v, |
diff --git a/src/lisp/mod.rs b/src/lisp/mod.rs index 5166a04..2a19314 100644 --- a/src/lisp/mod.rs +++ b/src/lisp/mod.rs | |||
@@ -4,6 +4,8 @@ pub mod expr; | |||
4 | pub mod lex; | 4 | pub mod lex; |
5 | pub mod number; | 5 | pub mod number; |
6 | pub mod parse; | 6 | pub mod parse; |
7 | #[macro_use] | ||
8 | mod primitives; | ||
7 | 9 | ||
8 | use std::collections::HashMap; | 10 | use std::collections::HashMap; |
9 | 11 | ||