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 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}