1
mod context;
2
mod emit;
3
pub(crate) mod expr;
4
mod layout;
5
mod native;
6
pub mod special;
7

            
8
use tracing::debug;
9
use wasm_encoder::{HeapType, RefType, ValType};
10

            
11
use crate::ast::Program;
12
use crate::error::Result;
13
use crate::runtime::SymbolTable;
14

            
15
use context::CompileContext;
16
use emit::FunctionEmitter;
17

            
18
pub struct Compiler;
19

            
20
impl Compiler {
21
    #[must_use]
22
5653
    pub fn new() -> Self {
23
5653
        Self
24
5653
    }
25

            
26
12113
    pub fn compile(&mut self, program: &Program, symbols: &mut SymbolTable) -> Result<Vec<u8>> {
27
12113
        debug!(expr_count = program.exprs.len(), "compilation start");
28
12113
        let mut ctx = CompileContext::new();
29

            
30
12113
        ctx.add_should_apply();
31

            
32
12113
        let gc_arr_ref = ValType::Ref(RefType {
33
12113
            nullable: true,
34
12113
            heap_type: HeapType::Concrete(ctx.type_idx("i8_array")),
35
12113
        });
36
12113
        let mut process = FunctionEmitter::new_with_locals(&[
37
12113
            (1, gc_arr_ref),
38
12113
            (1, ValType::I32),
39
12113
            (1, ValType::I32),
40
12113
        ]);
41

            
42
        // output_base = get_output_offset()
43
12113
        process.call(ctx.func("get_output_offset"));
44
12113
        process.local_set(expr::LOCAL_OUTPUT_BASE);
45

            
46
12113
        expr::compile_program(&mut ctx, &mut process, symbols, program)?;
47
11058
        process.end();
48
11058
        ctx.add_process(process.finish());
49

            
50
11058
        let wasm = ctx.finish();
51
11058
        debug!(wasm_size = wasm.len(), "compilation complete");
52
11058
        Ok(wasm)
53
12113
    }
54
}
55

            
56
impl Default for Compiler {
57
    fn default() -> Self {
58
        Self::new()
59
    }
60
}
61

            
62
#[cfg(test)]
63
mod tests {
64
    use super::*;
65
    use crate::ast::Expr;
66
    use crate::runtime::{Symbol, SymbolKind};
67

            
68
    #[test]
69
1
    fn test_compile_empty_program() {
70
1
        let program = Program::default();
71
1
        let mut compiler = Compiler::new();
72
1
        let wasm = compiler.compile(&program, &mut SymbolTable::new()).unwrap();
73
1
        assert!(!wasm.is_empty());
74
1
        assert_eq!(&wasm[0..4], b"\0asm");
75
1
    }
76

            
77
    #[test]
78
1
    fn test_compile_nil() {
79
1
        let program = Program::new(vec![Expr::Nil]);
80
1
        let mut compiler = Compiler::new();
81
1
        let wasm = compiler.compile(&program, &mut SymbolTable::new()).unwrap();
82
1
        assert!(!wasm.is_empty());
83
1
        assert_eq!(&wasm[0..4], b"\0asm");
84
1
    }
85

            
86
    #[test]
87
1
    fn test_compile_bool() {
88
1
        let program = Program::new(vec![Expr::Bool(true)]);
89
1
        let mut compiler = Compiler::new();
90
1
        let wasm = compiler.compile(&program, &mut SymbolTable::new()).unwrap();
91
1
        assert!(!wasm.is_empty());
92
1
    }
93

            
94
    #[test]
95
1
    fn test_compile_number() {
96
        use num_rational::Ratio;
97
1
        let program = Program::new(vec![Expr::Number(Ratio::new(1, 2))]);
98
1
        let mut compiler = Compiler::new();
99
1
        let wasm = compiler.compile(&program, &mut SymbolTable::new()).unwrap();
100
1
        assert!(!wasm.is_empty());
101
1
    }
102

            
103
    #[test]
104
1
    fn test_compile_string() {
105
1
        let program = Program::new(vec![Expr::String("hello".into())]);
106
1
        let mut compiler = Compiler::new();
107
1
        let wasm = compiler.compile(&program, &mut SymbolTable::new()).unwrap();
108
1
        assert!(!wasm.is_empty());
109
1
        assert_eq!(&wasm[0..4], b"\0asm");
110
1
    }
111

            
112
    #[test]
113
1
    fn test_compile_symbol_with_value() {
114
1
        let mut symbols = SymbolTable::new();
115
1
        symbols.define(Symbol::new("REVISION", SymbolKind::Variable).with_value(Expr::Bool(true)));
116
1
        let program = Program::new(vec![Expr::Symbol("REVISION".into())]);
117
1
        let mut compiler = Compiler::new();
118
1
        let wasm = compiler.compile(&program, &mut symbols).unwrap();
119
1
        assert!(!wasm.is_empty());
120
1
    }
121

            
122
    #[test]
123
1
    fn test_compile_undefined_symbol() {
124
1
        let program = Program::new(vec![Expr::Symbol("UNKNOWN".into())]);
125
1
        let mut compiler = Compiler::new();
126
1
        let result = compiler.compile(&program, &mut SymbolTable::new());
127
1
        assert!(result.is_err());
128
1
        let err = result.unwrap_err();
129
1
        assert!(matches!(err, crate::error::Error::UndefinedSymbol(_)));
130
1
    }
131

            
132
    #[test]
133
1
    fn test_defun_populates_function_cell() {
134
1
        let program = Program::new(vec![Expr::List(vec![
135
1
            Expr::Symbol("DEFUN".into()),
136
1
            Expr::Symbol("SUM".into()),
137
1
            Expr::List(vec![
138
1
                Expr::Symbol("A".into()),
139
1
                Expr::Symbol("B".into()),
140
1
                Expr::Symbol("C".into()),
141
1
            ]),
142
1
            Expr::String("Sums A, B, C".into()),
143
1
            Expr::List(vec![
144
1
                Expr::Symbol("+".into()),
145
1
                Expr::Symbol("A".into()),
146
1
                Expr::Symbol("B".into()),
147
1
                Expr::Symbol("C".into()),
148
1
            ]),
149
1
        ])]);
150
1
        let mut compiler = Compiler::new();
151
1
        let mut symbols = SymbolTable::with_builtins();
152
1
        compiler.compile(&program, &mut symbols).unwrap();
153

            
154
1
        let sym = symbols.lookup("SUM").expect("SUM should be defined");
155
1
        assert!(sym.function().is_some());
156
1
        assert!(matches!(sym.function(), Some(Expr::Lambda(_, _))));
157
1
        assert_eq!(sym.doc(), Some("Sums A, B, C"));
158
1
    }
159

            
160
    #[test]
161
1
    fn test_defun_no_doc_populates_function_cell() {
162
1
        let program = Program::new(vec![Expr::List(vec![
163
1
            Expr::Symbol("DEFUN".into()),
164
1
            Expr::Symbol("ADD".into()),
165
1
            Expr::List(vec![Expr::Symbol("A".into()), Expr::Symbol("B".into())]),
166
1
            Expr::List(vec![
167
1
                Expr::Symbol("+".into()),
168
1
                Expr::Symbol("A".into()),
169
1
                Expr::Symbol("B".into()),
170
1
            ]),
171
1
        ])]);
172
1
        let mut compiler = Compiler::new();
173
1
        let mut symbols = SymbolTable::with_builtins();
174
1
        compiler.compile(&program, &mut symbols).unwrap();
175

            
176
1
        let sym = symbols.lookup("ADD").expect("ADD should be defined");
177
1
        assert!(sym.function().is_some());
178
1
        assert!(sym.doc().is_none());
179
1
    }
180
}