aboutsummaryrefslogtreecommitdiff
path: root/src/lisp/prelude.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lisp/prelude.rs')
-rw-r--r--src/lisp/prelude.rs132
1 files changed, 95 insertions, 37 deletions
diff --git a/src/lisp/prelude.rs b/src/lisp/prelude.rs
index ad94cd2..6153e4f 100644
--- a/src/lisp/prelude.rs
+++ b/src/lisp/prelude.rs
@@ -1,14 +1,16 @@
1use crate::{ 1use crate::{
2 lisp::{ 2 lisp::{
3 error::{EvalError, LispError}, 3 error::{EvalError, LispError},
4 expr::LispExpr, 4 expr::{Arity, LispExpr},
5 number::LispNumber, 5 number::LispNumber,
6 Environment, 6 Environment,
7 }, 7 },
8 primitive, 8 primitive,
9}; 9};
10 10
11use std::convert::TryInto; 11use std::{convert::TryInto, fs::File, io::Write, path::PathBuf};
12
13use log::info;
12 14
13#[macro_export] 15#[macro_export]
14macro_rules! primitive { 16macro_rules! primitive {
@@ -24,7 +26,7 @@ macro_rules! primitive {
24pub fn new_env() -> Environment { 26pub fn new_env() -> Environment {
25 let mut env = Environment::new(); 27 let mut env = Environment::new();
26 28
27 primitive!(env, Some(2), "+", |args, _| { 29 primitive!(env, Arity::Atleast(2), "+", |args, _| {
28 let nums = args 30 let nums = args
29 .into_iter() 31 .into_iter()
30 .map(|arg| arg.try_into()) 32 .map(|arg| arg.try_into())
@@ -34,7 +36,7 @@ pub fn new_env() -> Environment {
34 )); 36 ));
35 }); 37 });
36 38
37 primitive!(env, Some(2), "-", |args, _| { 39 primitive!(env, Arity::Atleast(2), "-", |args, _| {
38 let nums = args 40 let nums = args
39 .into_iter() 41 .into_iter()
40 .map(|arg| arg.try_into()) 42 .map(|arg| arg.try_into())
@@ -46,7 +48,7 @@ pub fn new_env() -> Environment {
46 Ok(LispExpr::Number(acc)) 48 Ok(LispExpr::Number(acc))
47 }); 49 });
48 50
49 primitive!(env, Some(2), "*", |args, _| { 51 primitive!(env, Arity::Atleast(2), "*", |args, _| {
50 let nums = args 52 let nums = args
51 .into_iter() 53 .into_iter()
52 .map(|arg| arg.try_into()) 54 .map(|arg| arg.try_into())
@@ -56,7 +58,7 @@ pub fn new_env() -> Environment {
56 )); 58 ));
57 }); 59 });
58 60
59 primitive!(env, Some(2), "/", |args, _| { 61 primitive!(env, Arity::Atleast(2), "/", |args, _| {
60 let nums = args 62 let nums = args
61 .into_iter() 63 .into_iter()
62 .map(|arg| arg.try_into()) 64 .map(|arg| arg.try_into())
@@ -68,27 +70,12 @@ pub fn new_env() -> Environment {
68 Ok(LispExpr::Number(acc)) 70 Ok(LispExpr::Number(acc))
69 }); 71 });
70 72
71 primitive!(env, Some(0), "toggle-grid", |_, app| { 73 primitive!(env, Arity::Exact(0), "toggle-grid", |_, app| {
72 app.toggle_grid(); 74 app.toggle_grid();
73 Ok(LispExpr::Unit) 75 Ok(LispExpr::Unit)
74 }); 76 });
75 77
76 primitive!(env, Some(3), "if", |args, _| { 78 primitive!(env, Arity::Atleast(2), "and", |args, _| {
77 match args {
78 [predicate, then, else_] => {
79 if matches!(predicate, LispExpr::BoolLit(false)) {
80 Ok(else_.clone())
81 } else {
82 Ok(then.clone())
83 }
84 }
85 _ => {
86 panic!("panicked at `if` expression")
87 }
88 }
89 });
90
91 primitive!(env, Some(2), "and", |args, _| {
92 if args 79 if args
93 .iter() 80 .iter()
94 .any(|arg| matches!(arg, LispExpr::BoolLit(false))) 81 .any(|arg| matches!(arg, LispExpr::BoolLit(false)))
@@ -99,7 +86,7 @@ pub fn new_env() -> Environment {
99 } 86 }
100 }); 87 });
101 88
102 primitive!(env, Some(2), "or", |args, _| { 89 primitive!(env, Arity::Atleast(2), "or", |args, _| {
103 if args 90 if args
104 .iter() 91 .iter()
105 .any(|arg| matches!(arg, LispExpr::BoolLit(true))) 92 .any(|arg| matches!(arg, LispExpr::BoolLit(true)))
@@ -110,30 +97,101 @@ pub fn new_env() -> Environment {
110 } 97 }
111 }); 98 });
112 99
113 primitive!(env, Some(1), "not", |args, _| { 100 primitive!(env, Arity::Exact(1), "not", |args, _| {
114 match args { 101 if matches!(&args[0], LispExpr::BoolLit(false)) {
115 [val] => { 102 Ok(LispExpr::BoolLit(true))
116 if matches!(val, LispExpr::BoolLit(false)) { 103 } else {
117 Ok(LispExpr::BoolLit(true)) 104 Ok(LispExpr::BoolLit(false))
118 } else {
119 Ok(LispExpr::BoolLit(false))
120 }
121 }
122 _ => Err(EvalError::ArgumentCount(Some(1)).into()),
123 } 105 }
124 }); 106 });
125 107
126 primitive!(env, None, "begin", |args, _| { 108 primitive!(env, Arity::Atleast(1), "begin", |args, _| {
127 if args.is_empty() { 109 if args.is_empty() {
128 Err(EvalError::ArgumentCount(None).into()) 110 Err(EvalError::ArgumentCount(Arity::Atleast(1)).into())
129 } else { 111 } else {
130 Ok(args.into_iter().last().unwrap().clone()) 112 Ok(args.into_iter().last().unwrap().clone())
131 } 113 }
132 }); 114 });
133 115
134 primitive!(env, Some(0), "quit", |_, app| { 116 primitive!(env, Arity::Exact(0), "quit", |_, app| {
135 app.quit(); 117 app.quit();
136 Ok(LispExpr::Unit) 118 Ok(LispExpr::Unit)
137 }); 119 });
120
121 primitive!(env, Arity::Exact(2), "eq?", |args, app| {
122 let s = &args[0];
123 let o = &args[1];
124 info!("comparing {} {}", s, o);
125 let result = s.compare(o, &app.lisp_env);
126 result.map(LispExpr::BoolLit)
127 });
128
129 primitive!(env, Arity::Exact(2), ">", |args, _| {
130 let nums = args
131 .into_iter()
132 .map(|arg| arg.try_into())
133 .collect::<Result<Vec<&LispNumber>, LispError>>()?;
134 let acc = nums[0].clone();
135 Ok(LispExpr::BoolLit(
136 nums.into_iter().skip(1).all(|&arg| acc > arg),
137 ))
138 });
139
140 primitive!(env, Arity::Exact(2), ">=", |args, _| {
141 let nums = args
142 .into_iter()
143 .map(|arg| arg.try_into())
144 .collect::<Result<Vec<&LispNumber>, LispError>>()?;
145 let acc = nums[0].clone();
146 Ok(LispExpr::BoolLit(
147 nums.into_iter().skip(1).all(|&arg| acc >= arg),
148 ))
149 });
150
151 primitive!(env, Arity::Exact(2), "<", |args, _| {
152 let nums = args
153 .into_iter()
154 .map(|arg| arg.try_into())
155 .collect::<Result<Vec<&LispNumber>, LispError>>()?;
156 let acc = nums[0].clone();
157 Ok(LispExpr::BoolLit(
158 nums.into_iter().skip(1).all(|&arg| acc < arg),
159 ))
160 });
161
162 primitive!(env, Arity::Exact(2), "<=", |args, _| {
163 let nums = args
164 .into_iter()
165 .map(|arg| arg.try_into())
166 .collect::<Result<Vec<&LispNumber>, LispError>>()?;
167 let acc = nums[0].clone();
168 Ok(LispExpr::BoolLit(
169 nums.into_iter().skip(1).all(|&arg| acc <= arg),
170 ))
171 });
172
173 primitive!(env, Arity::Exact(1), "string-len", |args, _| {
174 match &args[0] {
175 LispExpr::StringLit(s) => Ok(LispExpr::Number(LispNumber::Integer(s.len() as i64))),
176 _ => Err(EvalError::TypeMismatch.into()),
177 }
178 });
179
180 primitive!(env, Arity::Atmost(1), "save", |args, app| {
181 let image = app.export().encode().unwrap();
182 if args.len() == 1 && matches!(&args[0], LispExpr::StringLit(_)) {
183 let mut buffer = File::create(&args[0].as_ref()).unwrap();
184 buffer.write_all(&image[..]).unwrap();
185 } else if let Some(p) = app.file_name {
186 let file_name = p;
187 let mut buffer = File::create(&file_name).unwrap();
188 buffer.write_all(&image[..]).unwrap();
189 } else {
190 return Err(EvalError::NoFileName.into());
191 }
192
193 return Ok(LispExpr::Unit);
194 });
195
138 env 196 env
139} 197}