aboutsummaryrefslogtreecommitdiff
path: root/src/lisp/expr.rs
blob: 40b39eb7277b8f20f0510b925c8bace27b71f9bc (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
use std::fmt;

use crate::app::AppState;
use crate::lisp::{error::LispError, number::LispNumber};

#[derive(Clone)]
pub struct PrimitiveFunc {
    pub arity: Option<usize>, // minimim arity
    pub closure: fn(&[LispExpr], &mut AppState) -> Result<LispExpr, LispError>,
}

impl PrimitiveFunc {
    pub fn call(&self, args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, LispError> {
        if let Some(arity) = self.arity {
            if args.len() < arity {
                return Err(LispError::EvalError);
            }
        }
        (self.closure)(args, app)
    }
}

#[derive(Clone)]
pub struct LispFunction {
    pub params: Vec<String>,
    pub body: Vec<LispExpr>,
}

#[derive(Clone)]
pub enum LispExpr {
    Unit,
    Number(LispNumber),
    List(Vec<LispExpr>),
    StringLit(String),
    BoolLit(bool),
    Ident(String),
    PrimitiveFunc(PrimitiveFunc),
    Function(LispFunction),

    // none of these depths should be zero
    Quasiquote(Box<LispExpr>, u32),
    Comma(Box<LispExpr>, u32),
    CommaAt(Box<LispExpr>, u32),
    Quote(Box<LispExpr>, u32),
}

impl LispExpr {
    pub fn comma(self, n: u32) -> LispExpr {
        match self {
            LispExpr::Comma(v, i) => LispExpr::Comma(v, i.checked_add(n).expect("comma overflow")),
            LispExpr::CommaAt(v, i) => LispExpr::CommaAt(v, i + n),
            v => LispExpr::Comma(Box::new(v), n),
        }
    }

    pub fn comma_at(self, n: u32) -> LispExpr {
        match self {
            LispExpr::CommaAt(v, i) => {
                LispExpr::CommaAt(v, i.checked_add(n).expect("comma_at overflow"))
            }
            v => LispExpr::CommaAt(Box::new(v), n),
        }
    }

    pub fn quote(self, n: u32) -> LispExpr {
        match self {
            LispExpr::Quote(v, i) => LispExpr::Quote(v, i.checked_add(n).expect("quote overflow")),
            v => LispExpr::Quote(Box::new(v), n),
        }
    }

    pub fn quasiquote(self, n: u32) -> LispExpr {
        match self {
            LispExpr::Quasiquote(v, i) => {
                LispExpr::Quasiquote(v, i.checked_add(n).expect("quasiquote overflow"))
            }
            v => LispExpr::Quasiquote(Box::new(v), n),
        }
    }
}

impl fmt::Display for LispExpr {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            LispExpr::Unit => write!(f, "()")?,
            LispExpr::Number(n) => write!(f, "{}", n)?,
            LispExpr::List(l) => {
                for expr in l.iter() {
                    write!(f, " {} ", expr)?
                }
            }
            LispExpr::StringLit(s) => write!(f, "{}", s)?,
            LispExpr::BoolLit(b) => {
                if *b {
                    write!(f, "#t")?
                } else {
                    write!(f, "#f")?
                }
            }
            LispExpr::Ident(s) => write!(f, "{}", s)?,
            LispExpr::PrimitiveFunc(_) => write!(f, "<#primitive>")?,
            LispExpr::Function(func) => write!(f, "<#lambda {}>", func.params.join(" "))?,
            LispExpr::Quasiquote(val, depth) => {
                write!(f, "{}{}", "`".repeat(*depth as usize), val)?
            }
            LispExpr::Comma(val, depth) => write!(f, "{}{}", ",".repeat(*depth as usize), val)?,
            LispExpr::CommaAt(val, depth) => write!(f, "{}@{}", ",".repeat(*depth as usize), val)?,
            LispExpr::Quote(val, depth) => write!(f, "{}{}", "'".repeat(*depth as usize), val)?,
        };
        Ok(())
    }
}