aboutsummaryrefslogtreecommitdiff
path: root/src/lisp/prelude.rs
blob: ad94cd2df16e4753f408e9f05ae898fa29e0e121 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use crate::{
    lisp::{
        error::{EvalError, LispError},
        expr::LispExpr,
        number::LispNumber,
        Environment,
    },
    primitive,
};

use std::convert::TryInto;

#[macro_export]
macro_rules! primitive {
    ($env:expr, $arity:expr, $name:expr, $closure:expr) => {
        let val = crate::lisp::expr::LispExpr::PrimitiveFunc(crate::lisp::expr::PrimitiveFunc {
            arity: $arity,
            closure: $closure,
        });
        let _ = $env.insert($name.to_string(), val);
    };
}

pub fn new_env() -> Environment {
    let mut env = Environment::new();

    primitive!(env, Some(2), "+", |args, _| {
        let nums = args
            .into_iter()
            .map(|arg| arg.try_into())
            .collect::<Result<Vec<&LispNumber>, LispError>>()?;
        return Ok(LispExpr::Number(
            nums.iter().fold(LispNumber::Integer(0), |acc, &x| acc + *x),
        ));
    });

    primitive!(env, Some(2), "-", |args, _| {
        let nums = args
            .into_iter()
            .map(|arg| arg.try_into())
            .collect::<Result<Vec<&LispNumber>, LispError>>()?;
        let mut acc = nums[0].clone();
        for arg in nums.into_iter().skip(1) {
            acc = acc - *arg;
        }
        Ok(LispExpr::Number(acc))
    });

    primitive!(env, Some(2), "*", |args, _| {
        let nums = args
            .into_iter()
            .map(|arg| arg.try_into())
            .collect::<Result<Vec<&LispNumber>, LispError>>()?;
        return Ok(LispExpr::Number(
            nums.iter().fold(LispNumber::Integer(1), |acc, &x| acc * *x),
        ));
    });

    primitive!(env, Some(2), "/", |args, _| {
        let nums = args
            .into_iter()
            .map(|arg| arg.try_into())
            .collect::<Result<Vec<&LispNumber>, LispError>>()?;
        let mut acc = nums[0].clone();
        for arg in nums.into_iter().skip(1) {
            acc = acc.div(*arg)?;
        }
        Ok(LispExpr::Number(acc))
    });

    primitive!(env, Some(0), "toggle-grid", |_, app| {
        app.toggle_grid();
        Ok(LispExpr::Unit)
    });

    primitive!(env, Some(3), "if", |args, _| {
        match args {
            [predicate, then, else_] => {
                if matches!(predicate, LispExpr::BoolLit(false)) {
                    Ok(else_.clone())
                } else {
                    Ok(then.clone())
                }
            }
            _ => {
                panic!("panicked at `if` expression")
            }
        }
    });

    primitive!(env, Some(2), "and", |args, _| {
        if args
            .iter()
            .any(|arg| matches!(arg, LispExpr::BoolLit(false)))
        {
            Ok(LispExpr::BoolLit(false))
        } else {
            Ok(LispExpr::BoolLit(true))
        }
    });

    primitive!(env, Some(2), "or", |args, _| {
        if args
            .iter()
            .any(|arg| matches!(arg, LispExpr::BoolLit(true)))
        {
            Ok(LispExpr::BoolLit(true))
        } else {
            Ok(LispExpr::BoolLit(false))
        }
    });

    primitive!(env, Some(1), "not", |args, _| {
        match args {
            [val] => {
                if matches!(val, LispExpr::BoolLit(false)) {
                    Ok(LispExpr::BoolLit(true))
                } else {
                    Ok(LispExpr::BoolLit(false))
                }
            }
            _ => Err(EvalError::ArgumentCount(Some(1)).into()),
        }
    });

    primitive!(env, None, "begin", |args, _| {
        if args.is_empty() {
            Err(EvalError::ArgumentCount(None).into())
        } else {
            Ok(args.into_iter().last().unwrap().clone())
        }
    });

    primitive!(env, Some(0), "quit", |_, app| {
        app.quit();
        Ok(LispExpr::Unit)
    });
    env
}