diff options
author | Akshay <[email protected]> | 2021-03-30 12:22:15 +0100 |
---|---|---|
committer | Akshay <[email protected]> | 2021-03-30 12:22:15 +0100 |
commit | dbcaa0df655bdd11c6d01ce28e018fc1e80ed394 (patch) | |
tree | 633510de5e3b43ff14143f72e7096265af659eef | |
parent | a76cd56b9f8cce132555f6c3b59d76da5ae86f6b (diff) |
better parse errors; include lisp stdlib
-rw-r--r-- | src/lisp/error.rs | 25 | ||||
-rw-r--r-- | src/lisp/eval.rs | 52 | ||||
-rw-r--r-- | src/lisp/expr.rs | 60 | ||||
-rw-r--r-- | src/lisp/lex.rs | 22 | ||||
-rw-r--r-- | src/lisp/prelude.rs | 104 | ||||
-rw-r--r-- | src/lisp/std.lisp | 26 |
6 files changed, 235 insertions, 54 deletions
diff --git a/src/lisp/error.rs b/src/lisp/error.rs index 6d28c22..53681d8 100644 --- a/src/lisp/error.rs +++ b/src/lisp/error.rs | |||
@@ -3,19 +3,23 @@ use crate::lisp::{ | |||
3 | lex::{Span, SpanDisplay}, | 3 | lex::{Span, SpanDisplay}, |
4 | }; | 4 | }; |
5 | 5 | ||
6 | use std::fmt; | 6 | use std::{fmt, io}; |
7 | 7 | ||
8 | #[derive(Debug, PartialEq, Clone)] | 8 | #[derive(Debug)] |
9 | pub enum LispError { | 9 | pub enum LispError { |
10 | Parse(ParseError), | 10 | Parse(ParseError), |
11 | Eval(EvalError), | 11 | Eval(EvalError), |
12 | Stringified(String), | ||
12 | } | 13 | } |
13 | 14 | ||
15 | impl std::error::Error for LispError {} | ||
16 | |||
14 | impl fmt::Display for LispError { | 17 | impl fmt::Display for LispError { |
15 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 18 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
16 | match self { | 19 | match self { |
17 | Self::Parse(p) => write!(f, "parse error: {}", p.kind), | 20 | Self::Parse(p) => write!(f, "parse error: {}", p.kind), |
18 | Self::Eval(e) => write!(f, "eval error: {}", e), | 21 | Self::Eval(e) => write!(f, "eval error: {}", e), |
22 | Self::Stringified(s) => write!(f, "{}", s), | ||
19 | } | 23 | } |
20 | } | 24 | } |
21 | } | 25 | } |
@@ -30,9 +34,12 @@ impl ParseError { | |||
30 | pub fn new(span: Span, kind: ParseErrorKind) -> Self { | 34 | pub fn new(span: Span, kind: ParseErrorKind) -> Self { |
31 | Self { span, kind } | 35 | Self { span, kind } |
32 | } | 36 | } |
33 | pub fn display(&self, text: &str) -> String { | 37 | pub fn display(&self, text: &str, file_name: &str) -> String { |
34 | let SpanDisplay { line, col, .. } = SpanDisplay::highlight_span(self.span, text); | 38 | let SpanDisplay { line, col, .. } = SpanDisplay::highlight_span(self.span, text, file_name); |
35 | format!("line {}, col {}: {}", line, col, self.kind) | 39 | format!( |
40 | "in file `{}`; line {}, col {}: {}", | ||
41 | file_name, line, col, self.kind | ||
42 | ) | ||
36 | } | 43 | } |
37 | } | 44 | } |
38 | 45 | ||
@@ -82,7 +89,7 @@ impl From<ParseError> for LispError { | |||
82 | } | 89 | } |
83 | } | 90 | } |
84 | 91 | ||
85 | #[derive(Debug, PartialEq, Clone)] | 92 | #[derive(Debug)] |
86 | pub enum EvalError { | 93 | pub enum EvalError { |
87 | ArgumentCount(Arity), | 94 | ArgumentCount(Arity), |
88 | BadForm, | 95 | BadForm, |
@@ -90,7 +97,10 @@ pub enum EvalError { | |||
90 | DivByZero, | 97 | DivByZero, |
91 | TypeMismatch, | 98 | TypeMismatch, |
92 | NoFileName, | 99 | NoFileName, |
100 | AccessEmptyList, | ||
101 | ScriptLoadError(io::Error), | ||
93 | CustomInternal(&'static str), | 102 | CustomInternal(&'static str), |
103 | Custom(String), | ||
94 | } | 104 | } |
95 | 105 | ||
96 | impl fmt::Display for EvalError { | 106 | impl fmt::Display for EvalError { |
@@ -113,7 +123,10 @@ impl fmt::Display for EvalError { | |||
113 | Self::TypeMismatch => write!(f, "mismatched types"), | 123 | Self::TypeMismatch => write!(f, "mismatched types"), |
114 | Self::DivByZero => write!(f, "attempt to divide by zero"), | 124 | Self::DivByZero => write!(f, "attempt to divide by zero"), |
115 | Self::NoFileName => write!(f, "no file name specified"), | 125 | Self::NoFileName => write!(f, "no file name specified"), |
126 | Self::AccessEmptyList => write!(f, "attempted to access empty list"), | ||
127 | Self::ScriptLoadError(s) => write!(f, "error while loading script: {}", s), | ||
116 | Self::CustomInternal(s) => write!(f, "{}", s), | 128 | Self::CustomInternal(s) => write!(f, "{}", s), |
129 | Self::Custom(s) => write!(f, "error: {}", s), | ||
117 | } | 130 | } |
118 | } | 131 | } |
119 | } | 132 | } |
diff --git a/src/lisp/eval.rs b/src/lisp/eval.rs index 3a3a61e..44540c0 100644 --- a/src/lisp/eval.rs +++ b/src/lisp/eval.rs | |||
@@ -6,6 +6,7 @@ use crate::{ | |||
6 | number::LispNumber, | 6 | number::LispNumber, |
7 | Environment, | 7 | Environment, |
8 | }, | 8 | }, |
9 | type_match, | ||
9 | }; | 10 | }; |
10 | 11 | ||
11 | use std::convert::TryInto; | 12 | use std::convert::TryInto; |
@@ -20,7 +21,14 @@ pub fn eval(expr: &LispExpr, app: &mut AppState) -> Result<LispExpr, LispError> | |||
20 | LispExpr::Number(_) => Ok(expr.clone()), | 21 | LispExpr::Number(_) => Ok(expr.clone()), |
21 | LispExpr::BoolLit(_) => Ok(expr.clone()), | 22 | LispExpr::BoolLit(_) => Ok(expr.clone()), |
22 | LispExpr::Ident(ref id) => lookup_extended(&app.lisp_env, id), | 23 | LispExpr::Ident(ref id) => lookup_extended(&app.lisp_env, id), |
23 | LispExpr::Quote(_, _) => Ok(expr.clone()), | 24 | LispExpr::Quote(item, _) => match item.as_ref() { |
25 | i @ LispExpr::Unit | ||
26 | | i @ LispExpr::StringLit(_) | ||
27 | | i @ LispExpr::Char(_) | ||
28 | | i @ LispExpr::Number(_) | ||
29 | | i @ LispExpr::BoolLit(_) => Ok(i.clone()), | ||
30 | _ => Ok(*item.clone()), | ||
31 | }, | ||
24 | LispExpr::List(li) => { | 32 | LispExpr::List(li) => { |
25 | let func_expr = &li[0]; | 33 | let func_expr = &li[0]; |
26 | match func_expr { | 34 | match func_expr { |
@@ -29,6 +37,7 @@ pub fn eval(expr: &LispExpr, app: &mut AppState) -> Result<LispExpr, LispError> | |||
29 | "set!" => set_var(&li[1..], app), | 37 | "set!" => set_var(&li[1..], app), |
30 | "lambda" => create_lambda(&li[1..]), | 38 | "lambda" => create_lambda(&li[1..]), |
31 | "if" => eval_if(&li[1..], app), | 39 | "if" => eval_if(&li[1..], app), |
40 | "quote" => eval_quote(&li[1..]), | ||
32 | _ => { | 41 | _ => { |
33 | let mut new_ls = vec![eval(&func_expr, app)?]; | 42 | let mut new_ls = vec![eval(&func_expr, app)?]; |
34 | new_ls.extend(li[1..].to_vec()); | 43 | new_ls.extend(li[1..].to_vec()); |
@@ -99,7 +108,7 @@ pub fn define_var(args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, Lis | |||
99 | [LispExpr::List(shorthand), LispExpr::List(body)] => { | 108 | [LispExpr::List(shorthand), LispExpr::List(body)] => { |
100 | // (define (func arg) <body>) shorthand | 109 | // (define (func arg) <body>) shorthand |
101 | 110 | ||
102 | let id = unwrap_ident(shorthand[0].clone()); | 111 | let id = shorthand[0].unwrap_ident(); |
103 | let params = if shorthand.len() > 1 { | 112 | let params = if shorthand.len() > 1 { |
104 | &shorthand[1..] | 113 | &shorthand[1..] |
105 | } else { | 114 | } else { |
@@ -163,13 +172,12 @@ pub fn create_lambda(cdr: &[LispExpr]) -> Result<LispExpr, LispError> { | |||
163 | } | 172 | } |
164 | info!("creating lambda"); | 173 | info!("creating lambda"); |
165 | match cdr { | 174 | match cdr { |
166 | [LispExpr::List(params), LispExpr::List(body)] | 175 | [LispExpr::List(params), LispExpr::List(body)] if type_match!(params, (..) => LispExpr::Ident(_)) => |
167 | if params.iter().all(|p| matches!(p, LispExpr::Ident(_))) => | ||
168 | { | 176 | { |
169 | return Ok(LispExpr::Function(LispFunction { | 177 | return Ok(LispExpr::Function(LispFunction { |
170 | params: params | 178 | params: params |
171 | .into_iter() | 179 | .into_iter() |
172 | .map(|p| unwrap_ident(p.clone())) | 180 | .map(|p| p.unwrap_ident()) |
173 | .collect::<Vec<_>>(), | 181 | .collect::<Vec<_>>(), |
174 | body: body.clone(), | 182 | body: body.clone(), |
175 | })); | 183 | })); |
@@ -202,6 +210,26 @@ pub fn eval_if(args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, LispEr | |||
202 | } | 210 | } |
203 | } | 211 | } |
204 | 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 | |||
205 | pub fn lookup_extended(env_list: &[Environment], key: &str) -> Result<LispExpr, LispError> { | 233 | pub fn lookup_extended(env_list: &[Environment], key: &str) -> Result<LispExpr, LispError> { |
206 | if env_list.is_empty() { | 234 | if env_list.is_empty() { |
207 | return Err(EvalError::UnboundVariable(key.into()).into()); | 235 | return Err(EvalError::UnboundVariable(key.into()).into()); |
@@ -215,20 +243,6 @@ pub fn lookup_extended(env_list: &[Environment], key: &str) -> Result<LispExpr, | |||
215 | } | 243 | } |
216 | } | 244 | } |
217 | 245 | ||
218 | pub fn unwrap_number(n: &LispExpr) -> &LispNumber { | ||
219 | match n { | ||
220 | LispExpr::Number(i) => i, | ||
221 | _ => panic!("unwrap_number expected number"), | ||
222 | } | ||
223 | } | ||
224 | |||
225 | pub fn unwrap_ident(i: LispExpr) -> String { | ||
226 | match i { | ||
227 | LispExpr::Ident(i) => i, | ||
228 | _ => panic!("unwrap_ident expected string"), | ||
229 | } | ||
230 | } | ||
231 | |||
232 | #[cfg(test)] | 246 | #[cfg(test)] |
233 | mod tests { | 247 | mod tests { |
234 | use super::*; | 248 | use super::*; |
diff --git a/src/lisp/expr.rs b/src/lisp/expr.rs index 934bb4a..9f2dc8d 100644 --- a/src/lisp/expr.rs +++ b/src/lisp/expr.rs | |||
@@ -117,6 +117,8 @@ impl LispExpr { | |||
117 | .iter() | 117 | .iter() |
118 | .zip(o) | 118 | .zip(o) |
119 | .all(|(a, b)| matches!(a.compare(b, envs), Ok(true)))), | 119 | .all(|(a, b)| matches!(a.compare(b, envs), Ok(true)))), |
120 | (LispExpr::List(s), LispExpr::Unit) => Ok(s.len() == 0), | ||
121 | (LispExpr::Unit, LispExpr::List(s)) => Ok(s.len() == 0), | ||
120 | (LispExpr::StringLit(s), LispExpr::StringLit(o)) => Ok(s == o), | 122 | (LispExpr::StringLit(s), LispExpr::StringLit(o)) => Ok(s == o), |
121 | (LispExpr::Char(s), LispExpr::Char(o)) => Ok(s == o), | 123 | (LispExpr::Char(s), LispExpr::Char(o)) => Ok(s == o), |
122 | (LispExpr::BoolLit(s), LispExpr::BoolLit(o)) => Ok(s == o), | 124 | (LispExpr::BoolLit(s), LispExpr::BoolLit(o)) => Ok(s == o), |
@@ -142,6 +144,56 @@ impl LispExpr { | |||
142 | _ => Err(EvalError::TypeMismatch.into()), | 144 | _ => Err(EvalError::TypeMismatch.into()), |
143 | } | 145 | } |
144 | } | 146 | } |
147 | |||
148 | // have these be code gen'd somehow | ||
149 | pub fn unwrap_number(&self) -> LispNumber { | ||
150 | match &self { | ||
151 | LispExpr::Number(p) => p.clone(), | ||
152 | _ => panic!("attempt to call `unwrap_number` on invalid type"), | ||
153 | } | ||
154 | } | ||
155 | |||
156 | pub fn unwrap_list(&self) -> Vec<LispExpr> { | ||
157 | match &self { | ||
158 | LispExpr::List(p) => p.clone(), | ||
159 | _ => panic!("attempt to call `unwrap_number` on invalid type"), | ||
160 | } | ||
161 | } | ||
162 | |||
163 | pub fn unwrap_stringlit(&self) -> String { | ||
164 | match &self { | ||
165 | LispExpr::StringLit(p) => p.clone(), | ||
166 | _ => panic!("attempt to call `unwrap_stringlit` on invalid type"), | ||
167 | } | ||
168 | } | ||
169 | |||
170 | pub fn unwrap_ident(&self) -> String { | ||
171 | match &self { | ||
172 | LispExpr::Ident(p) => p.clone(), | ||
173 | _ => panic!("attempt to call `unwrap_ident` on invalid type"), | ||
174 | } | ||
175 | } | ||
176 | |||
177 | pub fn unwrap_primitive_func(&self) -> PrimitiveFunc { | ||
178 | match &self { | ||
179 | LispExpr::PrimitiveFunc(p) => p.clone(), | ||
180 | _ => panic!("attempt to call `unwrap_primitive_func` on invalid type"), | ||
181 | } | ||
182 | } | ||
183 | |||
184 | pub fn unwrap_function(&self) -> LispFunction { | ||
185 | match &self { | ||
186 | LispExpr::Function(p) => p.clone(), | ||
187 | _ => panic!("attempt to call `unwrap_function` on invalid type"), | ||
188 | } | ||
189 | } | ||
190 | |||
191 | pub fn cast_bool(&self) -> bool { | ||
192 | match &self { | ||
193 | LispExpr::BoolLit(false) => false, | ||
194 | _ => true, | ||
195 | } | ||
196 | } | ||
145 | } | 197 | } |
146 | 198 | ||
147 | impl fmt::Display for LispExpr { | 199 | impl fmt::Display for LispExpr { |
@@ -270,11 +322,3 @@ impl TryFrom<LispExpr> for BoolLit { | |||
270 | } | 322 | } |
271 | } | 323 | } |
272 | } | 324 | } |
273 | |||
274 | pub fn is_ident<E: AsRef<LispExpr>>(expr: E) -> bool { | ||
275 | matches!(expr.as_ref(), LispExpr::Ident(_)) | ||
276 | } | ||
277 | |||
278 | pub fn is_number<E: AsRef<LispExpr>>(expr: E) -> bool { | ||
279 | matches!(expr.as_ref(), LispExpr::Number(_)) | ||
280 | } | ||
diff --git a/src/lisp/lex.rs b/src/lisp/lex.rs index e514d7f..5d3030b 100644 --- a/src/lisp/lex.rs +++ b/src/lisp/lex.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use std::{fmt, str::CharIndices}; | 1 | use std::{fmt, str::CharIndices}; |
2 | 2 | ||
3 | use crate::lisp::error::{LispError, ParseError, ParseErrorKind}; | 3 | use crate::lisp::error::{ParseError, ParseErrorKind}; |
4 | 4 | ||
5 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] | 5 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] |
6 | pub enum Token<'a> { | 6 | pub enum Token<'a> { |
@@ -75,21 +75,27 @@ impl Span { | |||
75 | } | 75 | } |
76 | 76 | ||
77 | #[derive(Debug, Clone)] | 77 | #[derive(Debug, Clone)] |
78 | pub struct SpanDisplay<'src> { | 78 | pub struct SpanDisplay<'src, 'file> { |
79 | pub file_name: &'file str, | ||
79 | pub source: &'src str, | 80 | pub source: &'src str, |
80 | pub line: usize, | 81 | pub line: usize, |
81 | pub col: usize, | 82 | pub col: usize, |
82 | } | 83 | } |
83 | 84 | ||
84 | impl<'src> SpanDisplay<'src> { | 85 | impl<'src, 'file> SpanDisplay<'src, 'file> { |
85 | pub fn highlight_span(span: Span, source: &'src str) -> Self { | 86 | pub fn highlight_span(span: Span, source: &'src str, file_name: &'file str) -> Self { |
86 | let line_start = match source[..span.low as usize].rfind('\n') { | 87 | let line_start = match source[..span.low as usize].rfind('\n') { |
87 | Some(pos) => pos + 1, | 88 | Some(pos) => pos + 1, |
88 | None => 0, | 89 | None => 0, |
89 | }; | 90 | }; |
90 | let line = source[..line_start].chars().filter(|&c| c == '\n').count() + 1; | 91 | let line = source[..line_start].chars().filter(|&c| c == '\n').count() + 1; |
91 | let col = source[line_start..span.low as usize].chars().count(); | 92 | let col = source[line_start..span.low as usize].chars().count(); |
92 | Self { source, line, col } | 93 | Self { |
94 | file_name, | ||
95 | source, | ||
96 | line, | ||
97 | col, | ||
98 | } | ||
93 | } | 99 | } |
94 | } | 100 | } |
95 | 101 | ||
@@ -108,7 +114,7 @@ impl<'a> Lexer<'a> { | |||
108 | } | 114 | } |
109 | } | 115 | } |
110 | 116 | ||
111 | pub fn next_token(&mut self) -> Result<(Span, Token<'a>), LispError> { | 117 | pub fn next_token(&mut self) -> Result<(Span, Token<'a>), ParseError> { |
112 | let mut chars = self.input.char_indices(); | 118 | let mut chars = self.input.char_indices(); |
113 | 119 | ||
114 | while let Some((ind, chr)) = chars.next() { | 120 | while let Some((ind, chr)) = chars.next() { |
@@ -142,13 +148,13 @@ impl<'a> Lexer<'a> { | |||
142 | let (size, token) = match res { | 148 | let (size, token) = match res { |
143 | Ok(v) => v, | 149 | Ok(v) => v, |
144 | Err(kind) => { | 150 | Err(kind) => { |
145 | return Err(LispError::Parse(ParseError { | 151 | return Err(ParseError { |
146 | span: Span { | 152 | span: Span { |
147 | low, | 153 | low, |
148 | high: low + chr.len_utf8() as u32, | 154 | high: low + chr.len_utf8() as u32, |
149 | }, | 155 | }, |
150 | kind, | 156 | kind, |
151 | })) | 157 | }) |
152 | } | 158 | } |
153 | }; | 159 | }; |
154 | self.cur_pos += size as u32; | 160 | self.cur_pos += size as u32; |
diff --git a/src/lisp/prelude.rs b/src/lisp/prelude.rs index d8a930b..dffd9f4 100644 --- a/src/lisp/prelude.rs +++ b/src/lisp/prelude.rs | |||
@@ -3,11 +3,12 @@ use crate::{ | |||
3 | lisp::{ | 3 | lisp::{ |
4 | error::{EvalError, LispError}, | 4 | error::{EvalError, LispError}, |
5 | eval::eval, | 5 | eval::eval, |
6 | expr::{is_ident, Arity, LispExpr}, | 6 | expr::{Arity, LispExpr}, |
7 | number::LispNumber, | 7 | number::LispNumber, |
8 | Environment, | 8 | Environment, |
9 | }, | 9 | }, |
10 | primitive, | 10 | primitive, |
11 | utils::load_script, | ||
11 | }; | 12 | }; |
12 | 13 | ||
13 | use std::{convert::TryInto, fs::File, io::Write}; | 14 | use std::{convert::TryInto, fs::File, io::Write}; |
@@ -27,7 +28,7 @@ macro_rules! primitive { | |||
27 | 28 | ||
28 | #[macro_export] | 29 | #[macro_export] |
29 | macro_rules! type_match { | 30 | macro_rules! type_match { |
30 | ($args:expr, $($range:expr => $kind:pat),+) => { | 31 | ($args:expr, $($range:literal => $kind:pat),+) => { |
31 | { | 32 | { |
32 | let mut temp_vec = vec![]; | 33 | let mut temp_vec = vec![]; |
33 | $( | 34 | $( |
@@ -35,10 +36,21 @@ macro_rules! type_match { | |||
35 | )+ | 36 | )+ |
36 | temp_vec.iter().all(|&t| t) | 37 | temp_vec.iter().all(|&t| t) |
37 | } | 38 | } |
39 | }; | ||
40 | ($args:expr, $($range:expr => $kind:pat),+) => { | ||
41 | { | ||
42 | let mut temp_vec = vec![]; | ||
43 | $( | ||
44 | for arg in &$args[$range] { | ||
45 | temp_vec.push(matches!(arg, $kind)); | ||
46 | } | ||
47 | )+ | ||
48 | temp_vec.iter().all(|&t| t) | ||
49 | } | ||
38 | } | 50 | } |
39 | } | 51 | } |
40 | 52 | ||
41 | pub fn new_env() -> Environment { | 53 | pub fn new_env() -> Result<Environment, LispError> { |
42 | let mut env = Environment::new(); | 54 | let mut env = Environment::new(); |
43 | 55 | ||
44 | primitive!(env, Arity::Atleast(2), "+", |args, _| { | 56 | primitive!(env, Arity::Atleast(2), "+", |args, _| { |
@@ -113,7 +125,7 @@ pub fn new_env() -> Environment { | |||
113 | }); | 125 | }); |
114 | 126 | ||
115 | primitive!(env, Arity::Exact(1), "not", |args, _| { | 127 | primitive!(env, Arity::Exact(1), "not", |args, _| { |
116 | if matches!(&args[0], LispExpr::BoolLit(false)) { | 128 | if type_match!(args, 0 => LispExpr::BoolLit(false)) { |
117 | Ok(LispExpr::BoolLit(true)) | 129 | Ok(LispExpr::BoolLit(true)) |
118 | } else { | 130 | } else { |
119 | Ok(LispExpr::BoolLit(false)) | 131 | Ok(LispExpr::BoolLit(false)) |
@@ -121,11 +133,7 @@ pub fn new_env() -> Environment { | |||
121 | }); | 133 | }); |
122 | 134 | ||
123 | primitive!(env, Arity::Atleast(1), "begin", |args, _| { | 135 | primitive!(env, Arity::Atleast(1), "begin", |args, _| { |
124 | if args.is_empty() { | 136 | Ok(args.into_iter().last().unwrap().clone()) |
125 | Err(EvalError::ArgumentCount(Arity::Atleast(1)).into()) | ||
126 | } else { | ||
127 | Ok(args.into_iter().last().unwrap().clone()) | ||
128 | } | ||
129 | }); | 137 | }); |
130 | 138 | ||
131 | primitive!(env, Arity::Exact(0), "quit", |_, app| { | 139 | primitive!(env, Arity::Exact(0), "quit", |_, app| { |
@@ -136,7 +144,7 @@ pub fn new_env() -> Environment { | |||
136 | primitive!(env, Arity::Exact(2), "eq?", |args, app| { | 144 | primitive!(env, Arity::Exact(2), "eq?", |args, app| { |
137 | let s = &args[0]; | 145 | let s = &args[0]; |
138 | let o = &args[1]; | 146 | let o = &args[1]; |
139 | info!("comparing {} {}", s, o); | 147 | info!("comparing s: {} and o: {}", s, o); |
140 | let result = s.compare(o, &app.lisp_env); | 148 | let result = s.compare(o, &app.lisp_env); |
141 | result.map(LispExpr::BoolLit) | 149 | result.map(LispExpr::BoolLit) |
142 | }); | 150 | }); |
@@ -188,7 +196,7 @@ pub fn new_env() -> Environment { | |||
188 | primitive!(env, Arity::Exact(1), "string-len", |args, _| { | 196 | primitive!(env, Arity::Exact(1), "string-len", |args, _| { |
189 | if type_match!(args, 0 => LispExpr::StringLit(_)) { | 197 | if type_match!(args, 0 => LispExpr::StringLit(_)) { |
190 | Ok(LispExpr::Number(LispNumber::Integer( | 198 | Ok(LispExpr::Number(LispNumber::Integer( |
191 | args[0].as_ref().len() as i64, | 199 | args[0].unwrap_stringlit().len() as i64, |
192 | ))) | 200 | ))) |
193 | } else { | 201 | } else { |
194 | Err(EvalError::TypeMismatch.into()) | 202 | Err(EvalError::TypeMismatch.into()) |
@@ -218,7 +226,7 @@ pub fn new_env() -> Environment { | |||
218 | 0 | 226 | 0 |
219 | }; | 227 | }; |
220 | if let [LispExpr::Quote(kind, _)] = args { | 228 | if let [LispExpr::Quote(kind, _)] = args { |
221 | if is_ident(kind) { | 229 | if matches!(kind.as_ref(), LispExpr::Ident(_)) { |
222 | match (&**kind).as_ref() { | 230 | match (&**kind).as_ref() { |
223 | "fill" => app.brush = Brush::Fill, | 231 | "fill" => app.brush = Brush::Fill, |
224 | "circle" => app.brush = Brush::new(old_size), | 232 | "circle" => app.brush = Brush::new(old_size), |
@@ -250,9 +258,79 @@ pub fn new_env() -> Environment { | |||
250 | } | 258 | } |
251 | }); | 259 | }); |
252 | 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 { | ||
278 | return Err(EvalError::TypeMismatch.into()); | ||
279 | } | ||
280 | }); | ||
281 | |||
282 | primitive!(env, Arity::Exact(1), "car", |args, _| { | ||
283 | if type_match!(args, 0 => LispExpr::List(_)) { | ||
284 | return Ok(args[0].unwrap_list().swap_remove(0)); | ||
285 | } else if type_match!(args, 0 => LispExpr::Unit) { | ||
286 | return Err(EvalError::AccessEmptyList.into()); | ||
287 | } else { | ||
288 | return Err(EvalError::TypeMismatch.into()); | ||
289 | } | ||
290 | }); | ||
291 | |||
292 | primitive!(env, Arity::Exact(1), "cdr", |args, _| { | ||
293 | if type_match!(args, 0 => LispExpr::List(_)) { | ||
294 | let mut ls = args[0].unwrap_list(); | ||
295 | if ls.len() == 0 { | ||
296 | return Err(EvalError::AccessEmptyList.into()); | ||
297 | } else if ls.len() == 1 { | ||
298 | return Ok(LispExpr::Unit); | ||
299 | } else { | ||
300 | ls.remove(0); | ||
301 | return Ok(LispExpr::List(ls)); | ||
302 | } | ||
303 | } else if type_match!(args, 0 => LispExpr::Unit) { | ||
304 | return Err(EvalError::AccessEmptyList.into()); | ||
305 | } else { | ||
306 | return Err(EvalError::TypeMismatch.into()); | ||
307 | } | ||
308 | }); | ||
309 | |||
253 | primitive!(env, Arity::Atleast(1), "list", |args, _| { | 310 | primitive!(env, Arity::Atleast(1), "list", |args, _| { |
254 | return Ok(LispExpr::List(args.to_vec())); | 311 | return Ok(LispExpr::List(args.to_vec())); |
255 | }); | 312 | }); |
256 | 313 | ||
257 | env | 314 | primitive!(env, Arity::Exact(1), "load-script", |args, app| { |
315 | if type_match!(args, 0 => LispExpr::StringLit(_)) { | ||
316 | let path = args[0].unwrap_stringlit(); | ||
317 | load_script(&path, app).map(|_| LispExpr::Unit) | ||
318 | } else { | ||
319 | return Err(EvalError::TypeMismatch.into()); | ||
320 | } | ||
321 | }); | ||
322 | |||
323 | primitive!(env, Arity::Atleast(1), "error", |args, _| { | ||
324 | if type_match!(args, 0 => LispExpr::StringLit(_)) { | ||
325 | let mut s = String::from(args[0].unwrap_stringlit()); | ||
326 | for arg in args.into_iter().skip(1) { | ||
327 | s.push_str(&format!(" {}", arg)); | ||
328 | } | ||
329 | return Err(EvalError::Custom(s).into()); | ||
330 | } else { | ||
331 | return Err(EvalError::TypeMismatch.into()); | ||
332 | } | ||
333 | }); | ||
334 | |||
335 | Ok(env) | ||
258 | } | 336 | } |
diff --git a/src/lisp/std.lisp b/src/lisp/std.lisp new file mode 100644 index 0000000..c723a13 --- /dev/null +++ b/src/lisp/std.lisp | |||
@@ -0,0 +1,26 @@ | |||
1 | (define (caar ls) (car (car ls))) | ||
2 | (define (caaar ls) (car (caar ls))) | ||
3 | (define (caaaar ls) (car (caaar ls))) | ||
4 | (define (caaaaar ls) (car (caaaar ls))) | ||
5 | |||
6 | (define (cddr ls) (cdr (cdr ls))) | ||
7 | (define (cdddr ls) (cdr (cddr ls))) | ||
8 | (define (cddddr ls) (cdr (cdddr ls))) | ||
9 | (define (cdddddr ls) (cdr (cddddr ls))) | ||
10 | |||
11 | (define (null? ls) (eq? ls '())) | ||
12 | |||
13 | (define (length ls) | ||
14 | (if (null? ls) | ||
15 | 0 | ||
16 | (+ 1 (length (cdr ls))))) | ||
17 | |||
18 | (define (fold init accumulator ls) | ||
19 | (if (null? ls) | ||
20 | init | ||
21 | (fold (accumulator init (car ls)) | ||
22 | accumulator | ||
23 | (cdr ls)))) | ||
24 | |||
25 | (define (sum ls) (fold 0 + ls)) | ||
26 | (define (product ls) (fold 1 * ls)) | ||