rust_lisp/
default_environment.rs

1use crate::{
2    interpreter::eval,
3    lisp,
4    model::{Env, HashMapRc, IntType, List, RuntimeError, Symbol, Value},
5    utils::{require_arg, require_typed_arg},
6};
7use cfg_if::cfg_if;
8use std::{cell::RefCell, collections::HashMap, convert::TryInto, rc::Rc};
9cfg_if! {
10    if #[cfg(feature = "bigint")] {
11        use num_traits::ToPrimitive;
12    }
13}
14
15/// Initialize an instance of `Env` with several core Lisp functions implemented
16/// in Rust. **Without this, you will only have access to the functions you
17/// implement yourself.**
18pub fn default_env() -> Env {
19    let mut env = Env::new();
20
21    env.define(
22        Symbol::from("print"),
23        Value::NativeFunc(|_env, args| {
24            let expr = require_arg("print", &args, 0)?;
25
26            println!("{}", &expr);
27            Ok(expr.clone())
28        }),
29    );
30
31    env.define(
32        Symbol::from("is_null"),
33        Value::NativeFunc(|_env, args| {
34            let val = require_arg("is_null", &args, 0)?;
35
36            Ok(Value::from(*val == Value::NIL))
37        }),
38    );
39
40    env.define(
41        Symbol::from("is_number"),
42        Value::NativeFunc(|_env, args| {
43            let val = require_arg("is_number", &args, 0)?;
44
45            Ok(match val {
46                Value::Int(_) => Value::True,
47                Value::Float(_) => Value::True,
48                _ => Value::NIL,
49            })
50        }),
51    );
52
53    env.define(
54        Symbol::from("is_symbol"),
55        Value::NativeFunc(|_env, args| {
56            let val = require_arg("is_symbol", &args, 0)?;
57
58            Ok(match val {
59                Value::Symbol(_) => Value::True,
60                _ => Value::NIL,
61            })
62        }),
63    );
64
65    env.define(
66        Symbol::from("is_boolean"),
67        Value::NativeFunc(|_env, args| {
68            let val = require_arg("is_boolean", &args, 0)?;
69
70            Ok(match val {
71                Value::True => Value::True,
72                Value::False => Value::True,
73                _ => Value::NIL,
74            })
75        }),
76    );
77
78    env.define(
79        Symbol::from("is_procedure"),
80        Value::NativeFunc(|_env, args| {
81            let val = require_arg("is_procedure", &args, 0)?;
82
83            Ok(match val {
84                Value::Lambda(_) => Value::True,
85                Value::NativeFunc(_) => Value::True,
86                _ => Value::NIL,
87            })
88        }),
89    );
90
91    env.define(
92        Symbol::from("is_pair"),
93        Value::NativeFunc(|_env, args| {
94            let val = require_arg("is_pair", &args, 0)?;
95
96            Ok(match val {
97                Value::List(_) => Value::True,
98                _ => Value::NIL,
99            })
100        }),
101    );
102
103    env.define(
104        Symbol::from("car"),
105        Value::NativeFunc(|_env, args| {
106            let list = require_typed_arg::<&List>("car", &args, 0)?;
107
108            list.car()
109        }),
110    );
111
112    env.define(
113        Symbol::from("cdr"),
114        Value::NativeFunc(|_env, args| {
115            let list = require_typed_arg::<&List>("cdr", &args, 0)?;
116
117            Ok(Value::List(list.cdr()))
118        }),
119    );
120
121    env.define(
122        Symbol::from("cons"),
123        Value::NativeFunc(|_env, args| {
124            let car = require_arg("cons", &args, 0)?;
125            let cdr = require_typed_arg::<&List>("cons", &args, 1)?;
126
127            Ok(Value::List(cdr.cons(car.clone())))
128        }),
129    );
130
131    env.define(
132        Symbol::from("list"),
133        Value::NativeFunc(|_env, args| Ok(Value::List(args.iter().collect::<List>()))),
134    );
135
136    env.define(
137        Symbol::from("nth"),
138        Value::NativeFunc(|_env, args| {
139            let index = require_typed_arg::<IntType>("nth", &args, 0)?;
140            let list = require_typed_arg::<&List>("nth", &args, 1)?;
141
142            let index = TryInto::<usize>::try_into(index).map_err(|_| RuntimeError {
143                msg: "Failed converting to `usize`".to_owned(),
144            })?;
145
146            Ok(list.into_iter().nth(index).unwrap_or(Value::NIL))
147        }),
148    );
149
150    env.define(
151        Symbol::from("sort"),
152        Value::NativeFunc(|_env, args| {
153            let list = require_typed_arg::<&List>("sort", &args, 0)?;
154
155            let mut v: Vec<Value> = list.into_iter().collect();
156
157            v.sort();
158
159            Ok(Value::List(v.into_iter().collect()))
160        }),
161    );
162
163    env.define(
164        Symbol::from("reverse"),
165        Value::NativeFunc(|_env, args| {
166            let list = require_typed_arg::<&List>("reverse", &args, 0)?;
167
168            let mut v: Vec<Value> = list.into_iter().collect();
169
170            v.reverse();
171
172            Ok(Value::List(v.into_iter().collect()))
173        }),
174    );
175
176    env.define(
177        Symbol::from("map"),
178        Value::NativeFunc(|env, args| {
179            let func = require_arg("map", &args, 0)?;
180            let list = require_typed_arg::<&List>("map", &args, 1)?;
181
182            list.into_iter()
183                .map(|val| {
184                    let expr = lisp! { ({func.clone()} (quote {val})) };
185
186                    eval(env.clone(), &expr)
187                })
188                .collect::<Result<List, RuntimeError>>()
189                .map(Value::List)
190        }),
191    );
192
193    // 🦀 Oh the poor `filter`, you must feel really sad being unused.
194    env.define(
195        Symbol::from("filter"),
196        Value::NativeFunc(|env, args| {
197            let func = require_arg("filter", &args, 0)?;
198            let list = require_typed_arg::<&List>("filter", &args, 1)?;
199
200            list.into_iter()
201                .filter_map(|val: Value| -> Option<Result<Value, RuntimeError>> {
202                    let expr = lisp! { ({func.clone()} (quote {val.clone()})) };
203
204                    match eval(env.clone(), &expr) {
205                        Ok(matches) => {
206                            if matches.into() {
207                                Some(Ok(val))
208                            } else {
209                                None
210                            }
211                        }
212                        Err(e) => Some(Err(e)),
213                    }
214                })
215                .collect::<Result<List, RuntimeError>>()
216                .map(Value::List)
217        }),
218    );
219
220    env.define(
221        Symbol::from("length"),
222        Value::NativeFunc(|_env, args| {
223            let list = require_typed_arg::<&List>("length", &args, 0)?;
224
225            cfg_if! {
226                if #[cfg(feature = "bigint")] {
227                    Ok(Value::Int(list.into_iter().len().into()))
228                } else {
229                    Ok(Value::Int(list.into_iter().len() as IntType))
230                }
231            }
232        }),
233    );
234
235    env.define(
236        Symbol::from("range"),
237        Value::NativeFunc(|_env, args| {
238            let start = require_typed_arg::<IntType>("range", &args, 0)?;
239            let end = require_typed_arg::<IntType>("range", &args, 1)?;
240
241            let mut current = start;
242
243            Ok(Value::List(
244                std::iter::from_fn(move || {
245                    if current == end {
246                        None
247                    } else {
248                        let res = Some(current.clone());
249
250                        current += 1;
251
252                        res
253                    }
254                })
255                .map(Value::from)
256                .collect(),
257            ))
258        }),
259    );
260
261    env.define(
262        Symbol::from("hash"),
263        Value::NativeFunc(|_env, args| {
264            let chunks = args.chunks(2);
265
266            let mut hash = HashMap::new();
267
268            for pair in chunks {
269                let key = pair.get(0).unwrap();
270                let value = pair.get(1);
271
272                if let Some(value) = value {
273                    hash.insert(key.clone(), value.clone());
274                } else {
275                    return Err(RuntimeError {
276                        msg: format!("Must pass an even number of arguments to 'hash', because they're used as key/value pairs; found extra argument {}", key)
277                    });
278                }
279            }
280
281            Ok(Value::HashMap(Rc::new(RefCell::new(hash))))
282        }),
283    );
284
285    env.define(
286        Symbol::from("hash_get"),
287        Value::NativeFunc(|_env, args| {
288            let hash = require_typed_arg::<&HashMapRc>("hash_get", &args, 0)?;
289            let key = require_arg("hash_get", &args, 1)?;
290
291            Ok(hash
292                .borrow()
293                .get(key)
294                .map(|v| v.clone())
295                .unwrap_or(Value::NIL))
296        }),
297    );
298
299    env.define(
300        Symbol::from("hash_set"),
301        Value::NativeFunc(|_env, args| {
302            let hash = require_typed_arg::<&HashMapRc>("hash_set", &args, 0)?;
303            let key = require_arg("hash_set", &args, 1)?;
304            let value = require_arg("hash_set", &args, 2)?;
305
306            hash.borrow_mut().insert(key.clone(), value.clone());
307
308            Ok(Value::HashMap(hash.clone()))
309        }),
310    );
311
312    env.define(
313        Symbol::from("+"),
314        Value::NativeFunc(|_env, args| {
315            let first_arg = require_arg("+", &args, 1)?;
316
317            let mut total = match first_arg {
318                Value::Int(_) => Ok(Value::Int(0.into())),
319                Value::Float(_) => Ok(Value::Float(0.0)),
320                Value::String(_) => Ok(Value::String("".into())),
321                _ => Err(RuntimeError {
322                    msg: format!(
323                        "Function \"+\" requires arguments to be numbers or strings; found {}",
324                        first_arg
325                    ),
326                }),
327            }?;
328
329            for arg in args {
330                total = (&total + &arg).map_err(|_| RuntimeError {
331                    msg: format!(
332                        "Function \"+\" requires arguments to be numbers or strings; found {}",
333                        arg
334                    ),
335                })?;
336            }
337
338            Ok(total)
339        }),
340    );
341
342    env.define(
343        Symbol::from("-"),
344        Value::NativeFunc(|_env, args| {
345            let a = require_arg("-", &args, 0)?;
346            let b = require_arg("-", &args, 1)?;
347
348            (a - b).map_err(|_| RuntimeError {
349                msg: String::from("Function \"-\" requires arguments to be numbers"),
350            })
351        }),
352    );
353
354    env.define(
355        Symbol::from("*"),
356        Value::NativeFunc(|_env, args| {
357            let mut product = Value::Int(1.into());
358
359            for arg in args {
360                product = (&product * &arg).map_err(|_| RuntimeError {
361                    msg: format!(
362                        "Function \"*\" requires arguments to be numbers; found {}",
363                        arg
364                    ),
365                })?;
366            }
367
368            Ok(product)
369        }),
370    );
371
372    env.define(
373        Symbol::from("/"),
374        Value::NativeFunc(|_env, args| {
375            let a = require_arg("/", &args, 0)?;
376            let b = require_arg("/", &args, 1)?;
377
378            (a / b).map_err(|_| RuntimeError {
379                msg: String::from("Function \"/\" requires arguments to be numbers"),
380            })
381        }),
382    );
383
384    env.define(
385        Symbol::from("truncate"),
386        Value::NativeFunc(|_env, args| {
387            let a = require_arg("truncate", &args, 0)?;
388            let b = require_arg("truncate", &args, 1)?;
389
390            if let (Ok(a), Ok(b)) = (
391                TryInto::<IntType>::try_into(a),
392                TryInto::<IntType>::try_into(b),
393            ) {
394                return Ok(Value::Int(a / b));
395            }
396
397            Err(RuntimeError {
398                msg: String::from("Function \"truncate\" requires arguments to be integers"),
399            })
400        }),
401    );
402
403    env.define(
404        Symbol::from("not"),
405        Value::NativeFunc(|_env, args| {
406            let a = require_arg("not", &args, 0)?;
407            let a: bool = a.into();
408
409            Ok(Value::from(!a))
410        }),
411    );
412
413    env.define(
414        Symbol::from("=="),
415        Value::NativeFunc(|_env, args| {
416            let a = require_arg("==", &args, 0)?;
417            let b = require_arg("==", &args, 1)?;
418
419            Ok(Value::from(a == b))
420        }),
421    );
422
423    env.define(
424        Symbol::from("!="),
425        Value::NativeFunc(|_env, args| {
426            let a = require_arg("!=", &args, 0)?;
427            let b = require_arg("!=", &args, 1)?;
428
429            Ok(Value::from(a != b))
430        }),
431    );
432
433    env.define(
434        Symbol::from("<"),
435        Value::NativeFunc(|_env, args| {
436            let a = require_arg("<", &args, 0)?;
437            let b = require_arg("<", &args, 1)?;
438
439            Ok(Value::from(a < b))
440        }),
441    );
442
443    env.define(
444        Symbol::from("<="),
445        Value::NativeFunc(|_env, args| {
446            let a = require_arg("<=", &args, 0)?;
447            let b = require_arg("<=", &args, 1)?;
448
449            Ok(Value::from(a <= b))
450        }),
451    );
452
453    env.define(
454        Symbol::from(">"),
455        Value::NativeFunc(|_env, args| {
456            let a = require_arg(">", &args, 0)?;
457            let b = require_arg(">", &args, 1)?;
458
459            Ok(Value::from(a > b))
460        }),
461    );
462
463    env.define(
464        Symbol::from(">="),
465        Value::NativeFunc(|_env, args| {
466            let a = require_arg(">=", &args, 0)?;
467            let b = require_arg(">=", &args, 1)?;
468
469            Ok(Value::from(a >= b))
470        }),
471    );
472
473    env.define(
474        Symbol::from("eval"),
475        Value::NativeFunc(|env, args| {
476            let expr = require_arg("eval", &args, 0)?;
477
478            eval(env, expr)
479        }),
480    );
481
482    env.define(
483        Symbol::from("apply"),
484        Value::NativeFunc(|env, args| {
485            let func = require_arg("apply", &args, 0)?;
486            let params = require_typed_arg::<&List>("apply", &args, 1)?;
487
488            eval(env, &Value::List(params.cons(func.clone())))
489        }),
490    );
491
492    env
493}