compiler/
compiler_test.rs

1use crate::compiler::Compiler;
2use crate::op_code::{concat_instructions, Instructions};
3use parser::parse;
4use std::borrow::Borrow;
5use std::rc::Rc;
6
7use object::Object;
8
9pub fn test_constants(expected: &Vec<Object>, actual: &Vec<Rc<Object>>) {
10    assert_eq!(expected.len(), actual.len());
11    for (exp, b_got) in expected.iter().zip(actual) {
12        let got = b_got.borrow();
13        assert_eq!(exp, got);
14    }
15}
16
17#[derive(Debug, Clone)]
18pub struct CompilerTestCase<'a> {
19    pub(crate) input: &'a str,
20    pub(crate) expected_constants: Vec<Object>,
21    pub(crate) expected_instructions: Vec<Instructions>,
22}
23
24pub fn run_compiler_test(tests: Vec<CompilerTestCase>) {
25    for t in tests {
26        let program = parse(t.input).unwrap();
27        let mut compiler = Compiler::new();
28        let bytecodes = compiler.compile(&program).unwrap();
29        test_instructions(&t.expected_instructions, &bytecodes.instructions);
30        test_constants(&t.expected_constants, &bytecodes.constants);
31    }
32}
33
34fn test_instructions(expected: &Vec<Instructions>, actual: &Instructions) {
35    let expected_ins = concat_instructions(expected);
36
37    // assert_eq!(expected_ins.data.len(), actual.data.len(), "instructions length not right");
38
39    for (&exp, got) in expected_ins.data.iter().zip(actual.data.clone()) {
40        assert_eq!(
41            exp,
42            got,
43            "instruction not equal\n actual  : \n{}\n expected: \n{}",
44            actual.string(),
45            expected_ins.string()
46        );
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53    use crate::op_code::make_instructions;
54    use crate::op_code::Opcode::*;
55
56    #[test]
57    fn integer_arithmetic() {
58        let tests = vec![
59            CompilerTestCase {
60                input: "1 + 2",
61                expected_constants: vec![Object::Integer(1), Object::Integer(2)],
62                expected_instructions: vec![
63                    make_instructions(OpConst, &vec![0]),
64                    make_instructions(OpConst, &vec![1]),
65                    make_instructions(OpAdd, &vec![1]),
66                    make_instructions(OpPop, &vec![0]),
67                ],
68            },
69            CompilerTestCase {
70                input: "1; 2",
71                expected_constants: vec![Object::Integer(1), Object::Integer(2)],
72                expected_instructions: vec![
73                    make_instructions(OpConst, &vec![0]),
74                    make_instructions(OpPop, &vec![0]),
75                    make_instructions(OpConst, &vec![1]),
76                    make_instructions(OpPop, &vec![1]),
77                ],
78            },
79            CompilerTestCase {
80                input: "1 - 2",
81                expected_constants: vec![Object::Integer(1), Object::Integer(2)],
82                expected_instructions: vec![
83                    make_instructions(OpConst, &vec![0]),
84                    make_instructions(OpConst, &vec![1]),
85                    make_instructions(OpSub, &vec![1]),
86                    make_instructions(OpPop, &vec![0]),
87                ],
88            },
89            CompilerTestCase {
90                input: "1 * 2",
91                expected_constants: vec![Object::Integer(1), Object::Integer(2)],
92                expected_instructions: vec![
93                    make_instructions(OpConst, &vec![0]),
94                    make_instructions(OpConst, &vec![1]),
95                    make_instructions(OpMul, &vec![1]),
96                    make_instructions(OpPop, &vec![0]),
97                ],
98            },
99            CompilerTestCase {
100                input: "2 / 1",
101                expected_constants: vec![Object::Integer(2), Object::Integer(1)],
102                expected_instructions: vec![
103                    make_instructions(OpConst, &vec![0]),
104                    make_instructions(OpConst, &vec![1]),
105                    make_instructions(OpDiv, &vec![1]),
106                    make_instructions(OpPop, &vec![0]),
107                ],
108            },
109            CompilerTestCase {
110                input: "-1",
111                expected_constants: vec![Object::Integer(1)],
112                expected_instructions: vec![
113                    make_instructions(OpConst, &vec![0]),
114                    make_instructions(OpMinus, &vec![1]),
115                    make_instructions(OpPop, &vec![0]),
116                ],
117            },
118            CompilerTestCase {
119                input: "!true",
120                expected_constants: vec![],
121                expected_instructions: vec![
122                    make_instructions(OpTrue, &vec![0]),
123                    make_instructions(OpBang, &vec![1]),
124                    make_instructions(OpPop, &vec![0]),
125                ],
126            },
127        ];
128
129        run_compiler_test(tests);
130    }
131    #[test]
132    fn boolean_expression() {
133        let tests = vec![
134            CompilerTestCase {
135                input: "true",
136                expected_constants: vec![],
137                expected_instructions: vec![
138                    make_instructions(OpTrue, &vec![0]),
139                    make_instructions(OpPop, &vec![0]),
140                ],
141            },
142            CompilerTestCase {
143                input: "false",
144                expected_constants: vec![],
145                expected_instructions: vec![
146                    make_instructions(OpFalse, &vec![0]),
147                    make_instructions(OpPop, &vec![0]),
148                ],
149            },
150            CompilerTestCase {
151                input: "1 > 2",
152                expected_constants: vec![Object::Integer(1), Object::Integer(2)],
153                expected_instructions: vec![
154                    make_instructions(OpConst, &vec![0]),
155                    make_instructions(OpConst, &vec![1]),
156                    make_instructions(OpGreaterThan, &vec![0]),
157                    make_instructions(OpPop, &vec![0]),
158                ],
159            },
160            CompilerTestCase {
161                input: "1 < 2",
162                expected_constants: vec![Object::Integer(2), Object::Integer(1)],
163                expected_instructions: vec![
164                    make_instructions(OpConst, &vec![0]),
165                    make_instructions(OpConst, &vec![1]),
166                    make_instructions(OpGreaterThan, &vec![0]),
167                    make_instructions(OpPop, &vec![0]),
168                ],
169            },
170            CompilerTestCase {
171                input: "1 == 2",
172                expected_constants: vec![Object::Integer(1), Object::Integer(2)],
173                expected_instructions: vec![
174                    make_instructions(OpConst, &vec![0]),
175                    make_instructions(OpConst, &vec![1]),
176                    make_instructions(OpEqual, &vec![0]),
177                    make_instructions(OpPop, &vec![0]),
178                ],
179            },
180            CompilerTestCase {
181                input: "1 != 2",
182                expected_constants: vec![Object::Integer(1), Object::Integer(2)],
183                expected_instructions: vec![
184                    make_instructions(OpConst, &vec![0]),
185                    make_instructions(OpConst, &vec![1]),
186                    make_instructions(OpNotEqual, &vec![0]),
187                    make_instructions(OpPop, &vec![0]),
188                ],
189            },
190            CompilerTestCase {
191                input: "true == false",
192                expected_constants: vec![],
193                expected_instructions: vec![
194                    make_instructions(OpTrue, &vec![0]),
195                    make_instructions(OpFalse, &vec![0]),
196                    make_instructions(OpEqual, &vec![0]),
197                    make_instructions(OpPop, &vec![0]),
198                ],
199            },
200            CompilerTestCase {
201                input: "true != false",
202                expected_constants: vec![],
203                expected_instructions: vec![
204                    make_instructions(OpTrue, &vec![0]),
205                    make_instructions(OpFalse, &vec![0]),
206                    make_instructions(OpNotEqual, &vec![0]),
207                    make_instructions(OpPop, &vec![0]),
208                ],
209            },
210        ];
211
212        run_compiler_test(tests);
213    }
214
215    #[test]
216    fn conditions_only_if() {
217        let tests = vec![CompilerTestCase {
218            input: "if (true) { 10 }; 3333;",
219            expected_constants: vec![Object::Integer(10), Object::Integer(3333)],
220            expected_instructions: vec![
221                make_instructions(OpTrue, &vec![0]),
222                make_instructions(OpJumpNotTruthy, &vec![10]),
223                make_instructions(OpConst, &vec![0]),
224                make_instructions(OpJump, &vec![11]),
225                make_instructions(OpNull, &vec![0]),
226                make_instructions(OpPop, &vec![0]),
227                make_instructions(OpConst, &vec![1]),
228                make_instructions(OpPop, &vec![0]),
229            ],
230        }];
231
232        run_compiler_test(tests);
233    }
234
235    #[test]
236    fn conditions_with_else() {
237        let tests = vec![CompilerTestCase {
238            input: "if (true) { 10 } else { 20 }; 3333;",
239            expected_constants: vec![
240                Object::Integer(10),
241                Object::Integer(20),
242                Object::Integer(3333),
243            ],
244            expected_instructions: vec![
245                make_instructions(OpTrue, &vec![0]),
246                make_instructions(OpJumpNotTruthy, &vec![10]),
247                make_instructions(OpConst, &vec![0]),
248                make_instructions(OpJump, &vec![13]),
249                make_instructions(OpConst, &vec![1]),
250                make_instructions(OpPop, &vec![0]),
251                make_instructions(OpConst, &vec![2]),
252                make_instructions(OpPop, &vec![0]),
253            ],
254        }];
255
256        run_compiler_test(tests);
257    }
258
259    #[test]
260    fn test_global_constants() {
261        let tests = vec![
262            CompilerTestCase {
263                input: "let one = 1; let two = 2;",
264                expected_constants: vec![Object::Integer(1), Object::Integer(2)],
265                expected_instructions: vec![
266                    make_instructions(OpConst, &vec![0]),
267                    make_instructions(OpSetGlobal, &vec![0]),
268                    make_instructions(OpConst, &vec![1]),
269                    make_instructions(OpSetGlobal, &vec![1]),
270                ],
271            },
272            CompilerTestCase {
273                input: "let one = 1; one",
274                expected_constants: vec![Object::Integer(1)],
275                expected_instructions: vec![
276                    make_instructions(OpConst, &vec![0]),
277                    make_instructions(OpSetGlobal, &vec![0]),
278                    make_instructions(OpGetGlobal, &vec![0]),
279                    make_instructions(OpPop, &vec![0]),
280                ],
281            },
282            CompilerTestCase {
283                input: "let one = 1; let two = one; two",
284                expected_constants: vec![Object::Integer(1)],
285                expected_instructions: vec![
286                    make_instructions(OpConst, &vec![0]),
287                    make_instructions(OpSetGlobal, &vec![0]),
288                    make_instructions(OpGetGlobal, &vec![0]),
289                    make_instructions(OpSetGlobal, &vec![1]),
290                    make_instructions(OpGetGlobal, &vec![1]),
291                    make_instructions(OpPop, &vec![0]),
292                ],
293            },
294        ];
295
296        run_compiler_test(tests);
297    }
298
299    #[test]
300    fn test_string() {
301        let tests = vec![
302            CompilerTestCase {
303                input: "\"monkey\"",
304                expected_constants: vec![Object::String("monkey".to_string())],
305                expected_instructions: vec![
306                    make_instructions(OpConst, &vec![0]),
307                    make_instructions(OpPop, &vec![0]),
308                ],
309            },
310            CompilerTestCase {
311                input: r#""mon" + "key""#,
312                expected_constants: vec![
313                    Object::String("mon".to_string()),
314                    Object::String("key".to_string()),
315                ],
316                expected_instructions: vec![
317                    make_instructions(OpConst, &vec![0]),
318                    make_instructions(OpConst, &vec![1]),
319                    make_instructions(OpAdd, &vec![0]),
320                    make_instructions(OpPop, &vec![0]),
321                ],
322            },
323        ];
324
325        run_compiler_test(tests);
326    }
327
328    #[test]
329    fn test_array() {
330        let tests = vec![
331            CompilerTestCase {
332                input: "[]",
333                expected_constants: vec![],
334                expected_instructions: vec![
335                    make_instructions(OpArray, &vec![0]),
336                    make_instructions(OpPop, &vec![0]),
337                ],
338            },
339            CompilerTestCase {
340                input: "[1, 2, 3]",
341                expected_constants: vec![
342                    Object::Integer(1),
343                    Object::Integer(2),
344                    Object::Integer(3),
345                ],
346                expected_instructions: vec![
347                    make_instructions(OpConst, &vec![0]),
348                    make_instructions(OpConst, &vec![1]),
349                    make_instructions(OpConst, &vec![2]),
350                    make_instructions(OpArray, &vec![3]),
351                    make_instructions(OpPop, &vec![0]),
352                ],
353            },
354            CompilerTestCase {
355                input: "[1 + 2, 3 - 4, 5 * 6]",
356                expected_constants: vec![
357                    Object::Integer(1),
358                    Object::Integer(2),
359                    Object::Integer(3),
360                    Object::Integer(4),
361                    Object::Integer(5),
362                    Object::Integer(6),
363                ],
364                expected_instructions: vec![
365                    make_instructions(OpConst, &vec![0]),
366                    make_instructions(OpConst, &vec![1]),
367                    make_instructions(OpAdd, &vec![0]),
368                    make_instructions(OpConst, &vec![2]),
369                    make_instructions(OpConst, &vec![3]),
370                    make_instructions(OpSub, &vec![0]),
371                    make_instructions(OpConst, &vec![4]),
372                    make_instructions(OpConst, &vec![5]),
373                    make_instructions(OpMul, &vec![0]),
374                    make_instructions(OpArray, &vec![3]),
375                    make_instructions(OpPop, &vec![0]),
376                ],
377            },
378        ];
379
380        run_compiler_test(tests);
381    }
382
383    #[test]
384    fn test_hashmap() {
385        let tests = vec![
386            CompilerTestCase {
387                input: "{}",
388                expected_constants: vec![],
389                expected_instructions: vec![
390                    make_instructions(OpHash, &vec![0]),
391                    make_instructions(OpPop, &vec![0]),
392                ],
393            },
394            CompilerTestCase {
395                input: "{1: 2, 3: 4, 5: 6}",
396                expected_constants: vec![
397                    Object::Integer(1),
398                    Object::Integer(2),
399                    Object::Integer(3),
400                    Object::Integer(4),
401                    Object::Integer(5),
402                    Object::Integer(6),
403                ],
404                expected_instructions: vec![
405                    make_instructions(OpConst, &vec![0]),
406                    make_instructions(OpConst, &vec![1]),
407                    make_instructions(OpConst, &vec![2]),
408                    make_instructions(OpConst, &vec![3]),
409                    make_instructions(OpConst, &vec![4]),
410                    make_instructions(OpConst, &vec![5]),
411                    make_instructions(OpHash, &vec![6]),
412                    make_instructions(OpPop, &vec![0]),
413                ],
414            },
415            CompilerTestCase {
416                input: "{1: 2 + 3, 4: 5 * 6}",
417                expected_constants: vec![
418                    Object::Integer(1),
419                    Object::Integer(2),
420                    Object::Integer(3),
421                    Object::Integer(4),
422                    Object::Integer(5),
423                    Object::Integer(6),
424                ],
425                expected_instructions: vec![
426                    make_instructions(OpConst, &vec![0]),
427                    make_instructions(OpConst, &vec![1]),
428                    make_instructions(OpConst, &vec![2]),
429                    make_instructions(OpAdd, &vec![0]),
430                    make_instructions(OpConst, &vec![3]),
431                    make_instructions(OpConst, &vec![4]),
432                    make_instructions(OpConst, &vec![5]),
433                    make_instructions(OpMul, &vec![0]),
434                    make_instructions(OpHash, &vec![4]),
435                    make_instructions(OpPop, &vec![0]),
436                ],
437            },
438        ];
439
440        run_compiler_test(tests);
441    }
442
443    #[test]
444    fn test_index() {
445        let tests = vec![
446            CompilerTestCase {
447                input: "[1, 2, 3][1 + 1]",
448                expected_constants: vec![
449                    Object::Integer(1),
450                    Object::Integer(2),
451                    Object::Integer(3),
452                    Object::Integer(1),
453                    Object::Integer(1),
454                ],
455                expected_instructions: vec![
456                    make_instructions(OpConst, &vec![0]),
457                    make_instructions(OpConst, &vec![1]),
458                    make_instructions(OpConst, &vec![2]),
459                    make_instructions(OpArray, &vec![3]),
460                    make_instructions(OpConst, &vec![3]),
461                    make_instructions(OpConst, &vec![4]),
462                    make_instructions(OpAdd, &vec![0]),
463                    make_instructions(OpIndex, &vec![0]),
464                    make_instructions(OpPop, &vec![0]),
465                ],
466            },
467            CompilerTestCase {
468                input: "{1: 2 }[2 -1]",
469                expected_constants: vec![
470                    Object::Integer(1),
471                    Object::Integer(2),
472                    Object::Integer(2),
473                    Object::Integer(1),
474                ],
475                expected_instructions: vec![
476                    make_instructions(OpConst, &vec![0]),
477                    make_instructions(OpConst, &vec![1]),
478                    make_instructions(OpHash, &vec![2]),
479                    make_instructions(OpConst, &vec![2]),
480                    make_instructions(OpConst, &vec![3]),
481                    make_instructions(OpSub, &vec![0]),
482                    make_instructions(OpIndex, &vec![0]),
483                    make_instructions(OpPop, &vec![0]),
484                ],
485            },
486        ];
487
488        run_compiler_test(tests);
489    }
490}