aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAkshay <[email protected]>2021-03-30 12:22:15 +0100
committerAkshay <[email protected]>2021-03-30 12:22:15 +0100
commitdbcaa0df655bdd11c6d01ce28e018fc1e80ed394 (patch)
tree633510de5e3b43ff14143f72e7096265af659eef /src
parenta76cd56b9f8cce132555f6c3b59d76da5ae86f6b (diff)
better parse errors; include lisp stdlib
Diffstat (limited to 'src')
-rw-r--r--src/lisp/error.rs25
-rw-r--r--src/lisp/eval.rs52
-rw-r--r--src/lisp/expr.rs60
-rw-r--r--src/lisp/lex.rs22
-rw-r--r--src/lisp/prelude.rs104
-rw-r--r--src/lisp/std.lisp26
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
6use std::fmt; 6use std::{fmt, io};
7 7
8#[derive(Debug, PartialEq, Clone)] 8#[derive(Debug)]
9pub enum LispError { 9pub enum LispError {
10 Parse(ParseError), 10 Parse(ParseError),
11 Eval(EvalError), 11 Eval(EvalError),
12 Stringified(String),
12} 13}
13 14
15impl std::error::Error for LispError {}
16
14impl fmt::Display for LispError { 17impl 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)]
86pub enum EvalError { 93pub 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
96impl fmt::Display for EvalError { 106impl 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
11use std::convert::TryInto; 12use 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
213pub 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
205pub fn lookup_extended(env_list: &[Environment], key: &str) -> Result<LispExpr, LispError> { 233pub 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
218pub fn unwrap_number(n: &LispExpr) -> &LispNumber {
219 match n {
220 LispExpr::Number(i) => i,
221 _ => panic!("unwrap_number expected number"),
222 }
223}
224
225pub 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)]
233mod tests { 247mod 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
147impl fmt::Display for LispExpr { 199impl fmt::Display for LispExpr {
@@ -270,11 +322,3 @@ impl TryFrom<LispExpr> for BoolLit {
270 } 322 }
271 } 323 }
272} 324}
273
274pub fn is_ident<E: AsRef<LispExpr>>(expr: E) -> bool {
275 matches!(expr.as_ref(), LispExpr::Ident(_))
276}
277
278pub 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 @@
1use std::{fmt, str::CharIndices}; 1use std::{fmt, str::CharIndices};
2 2
3use crate::lisp::error::{LispError, ParseError, ParseErrorKind}; 3use crate::lisp::error::{ParseError, ParseErrorKind};
4 4
5#[derive(Copy, Clone, Debug, Eq, PartialEq)] 5#[derive(Copy, Clone, Debug, Eq, PartialEq)]
6pub enum Token<'a> { 6pub enum Token<'a> {
@@ -75,21 +75,27 @@ impl Span {
75} 75}
76 76
77#[derive(Debug, Clone)] 77#[derive(Debug, Clone)]
78pub struct SpanDisplay<'src> { 78pub 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
84impl<'src> SpanDisplay<'src> { 85impl<'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
13use std::{convert::TryInto, fs::File, io::Write}; 14use std::{convert::TryInto, fs::File, io::Write};
@@ -27,7 +28,7 @@ macro_rules! primitive {
27 28
28#[macro_export] 29#[macro_export]
29macro_rules! type_match { 30macro_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
41pub fn new_env() -> Environment { 53pub 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))