1use sema_core::{intern, resolve as resolve_spur, SemaError, Spur, Value};
2
3use crate::chunk::{Chunk, ExceptionEntry, Function, UpvalueDesc};
4use crate::core_expr::{
5 ResolvedDoLoop, ResolvedExpr, ResolvedLambda, ResolvedPromptEntry, VarRef, VarResolution,
6};
7use crate::emit::Emitter;
8use crate::opcodes::Op;
9
10pub struct CompileResult {
12 pub chunk: Chunk,
14 pub functions: Vec<Function>,
16}
17
18const MAX_COMPILE_DEPTH: usize = 256;
21
22pub fn compile(expr: &ResolvedExpr) -> Result<CompileResult, SemaError> {
24 compile_with_locals(expr, 0)
25}
26
27pub fn compile_with_locals(expr: &ResolvedExpr, n_locals: u16) -> Result<CompileResult, SemaError> {
29 let mut compiler = Compiler::new();
30 compiler.n_locals = n_locals;
31 compiler.compile_expr(expr)?;
32 compiler.emit.emit_op(Op::Return);
33 let (chunk, functions) = compiler.finish();
34 Ok(CompileResult { chunk, functions })
35}
36
37pub fn compile_many(exprs: &[ResolvedExpr]) -> Result<CompileResult, SemaError> {
39 compile_many_with_locals(exprs, 0)
40}
41
42pub fn compile_many_with_locals(
44 exprs: &[ResolvedExpr],
45 n_locals: u16,
46) -> Result<CompileResult, SemaError> {
47 let mut compiler = Compiler::new();
48 compiler.n_locals = n_locals;
49 for (i, expr) in exprs.iter().enumerate() {
50 compiler.compile_expr(expr)?;
51 if i < exprs.len() - 1 {
52 compiler.emit.emit_op(Op::Pop);
53 }
54 }
55 if exprs.is_empty() {
56 compiler.emit.emit_op(Op::Nil);
57 }
58 compiler.emit.emit_op(Op::Return);
59 let (chunk, functions) = compiler.finish();
60 Ok(CompileResult { chunk, functions })
61}
62
63fn patch_closure_func_ids(chunk: &mut Chunk, offset: u16) {
65 let code = &mut chunk.code;
66 let mut pc = 0;
67 while pc < code.len() {
68 let Some(op) = Op::from_u8(code[pc]) else {
69 break;
70 };
71 match op {
72 Op::MakeClosure => {
73 let old = u16::from_le_bytes([code[pc + 1], code[pc + 2]]);
75 let new = old + offset;
76 let bytes = new.to_le_bytes();
77 code[pc + 1] = bytes[0];
78 code[pc + 2] = bytes[1];
79 let n_upvalues = u16::from_le_bytes([code[pc + 3], code[pc + 4]]) as usize;
81 pc += 1 + 2 + 2 + n_upvalues * 4;
83 }
84 Op::Const
86 | Op::LoadLocal
87 | Op::StoreLocal
88 | Op::LoadUpvalue
89 | Op::StoreUpvalue
90 | Op::Call
91 | Op::TailCall
92 | Op::MakeList
93 | Op::MakeVector
94 | Op::MakeMap
95 | Op::MakeHashMap => {
96 pc += 1 + 2; }
98 Op::CallNative => {
99 pc += 1 + 2 + 2; }
101 Op::LoadGlobal | Op::StoreGlobal | Op::DefineGlobal => {
102 pc += 1 + 4; }
104 Op::CallGlobal => {
105 pc += 1 + 4 + 2; }
107 Op::Jump | Op::JumpIfFalse | Op::JumpIfTrue => {
108 pc += 1 + 4; }
110 _ => {
112 pc += 1;
113 }
114 }
115 }
116}
117
118struct Compiler {
119 emit: Emitter,
120 functions: Vec<Function>,
121 exception_entries: Vec<ExceptionEntry>,
122 n_locals: u16,
123 stack_height: u16,
125 depth: usize,
126}
127
128impl Compiler {
129 fn new() -> Self {
130 Compiler {
131 emit: Emitter::new(),
132 functions: Vec::new(),
133 exception_entries: Vec::new(),
134 n_locals: 0,
135 stack_height: 0,
136 depth: 0,
137 }
138 }
139
140 fn finish(self) -> (Chunk, Vec<Function>) {
141 let mut chunk = self.emit.into_chunk();
142 chunk.n_locals = self.n_locals;
143 chunk.exception_table = self.exception_entries;
144 (chunk, self.functions)
145 }
146
147 fn compile_expr(&mut self, expr: &ResolvedExpr) -> Result<(), SemaError> {
148 self.depth += 1;
149 if self.depth > MAX_COMPILE_DEPTH {
150 self.depth -= 1;
151 return Err(SemaError::eval("maximum compilation depth exceeded"));
152 }
153 let result = self.compile_expr_inner(expr);
158 self.depth -= 1;
159 result
160 }
161
162 fn compile_expr_inner(&mut self, expr: &ResolvedExpr) -> Result<(), SemaError> {
163 match expr {
164 ResolvedExpr::Const(val) => self.compile_const(val),
165 ResolvedExpr::Var(vr) => self.compile_var_load(vr),
166 ResolvedExpr::If { test, then, else_ } => self.compile_if(test, then, else_),
167 ResolvedExpr::Begin(exprs) => self.compile_begin(exprs),
168 ResolvedExpr::Set(vr, val) => self.compile_set(vr, val),
169 ResolvedExpr::Lambda(def) => self.compile_lambda(def),
170 ResolvedExpr::Call { func, args, tail } => self.compile_call(func, args, *tail),
171 ResolvedExpr::Define(spur, val) => self.compile_define(*spur, val),
172 ResolvedExpr::Let { bindings, body } => self.compile_let(bindings, body),
173 ResolvedExpr::LetStar { bindings, body } => self.compile_let_star(bindings, body),
174 ResolvedExpr::Letrec { bindings, body } => self.compile_letrec(bindings, body),
175 ResolvedExpr::NamedLet {
176 name,
177 bindings,
178 body,
179 } => self.compile_named_let(name, bindings, body),
180 ResolvedExpr::Do(do_loop) => self.compile_do(do_loop),
181 ResolvedExpr::Try {
182 body,
183 catch_var,
184 handler,
185 } => self.compile_try(body, catch_var, handler),
186 ResolvedExpr::Throw(val) => self.compile_throw(val),
187 ResolvedExpr::And(exprs) => self.compile_and(exprs),
188 ResolvedExpr::Or(exprs) => self.compile_or(exprs),
189 ResolvedExpr::Quote(val) => self.compile_const(val),
190 ResolvedExpr::MakeList(exprs) => self.compile_make_list(exprs),
191 ResolvedExpr::MakeVector(exprs) => self.compile_make_vector(exprs),
192 ResolvedExpr::MakeMap(pairs) => self.compile_make_map(pairs),
193 ResolvedExpr::Defmacro {
194 name,
195 params,
196 rest,
197 body,
198 } => self.compile_defmacro(*name, params, rest, body),
199 ResolvedExpr::DefineRecordType {
200 type_name,
201 ctor_name,
202 pred_name,
203 field_names,
204 field_specs,
205 } => self.compile_define_record_type(
206 *type_name,
207 *ctor_name,
208 *pred_name,
209 field_names,
210 field_specs,
211 ),
212 ResolvedExpr::Module {
213 name,
214 exports,
215 body,
216 } => self.compile_module(*name, exports, body),
217 ResolvedExpr::Import { path, selective } => self.compile_import(path, selective),
218 ResolvedExpr::Load(path) => self.compile_load(path),
219 ResolvedExpr::Eval(expr) => self.compile_eval(expr),
220 ResolvedExpr::Prompt(entries) => self.compile_prompt(entries),
221 ResolvedExpr::Message { role, parts } => self.compile_message(role, parts),
222 ResolvedExpr::Deftool {
223 name,
224 description,
225 parameters,
226 handler,
227 } => self.compile_deftool(*name, description, parameters, handler),
228 ResolvedExpr::Defagent { name, options } => self.compile_defagent(*name, options),
229 ResolvedExpr::Delay(expr) => self.compile_delay(expr),
230 ResolvedExpr::Force(expr) => self.compile_force(expr),
231 ResolvedExpr::Macroexpand(expr) => self.compile_macroexpand(expr),
232 }
233 }
234
235 fn compile_const(&mut self, val: &Value) -> Result<(), SemaError> {
238 if val.is_nil() {
239 self.emit.emit_op(Op::Nil);
240 } else if val.as_bool() == Some(true) {
241 self.emit.emit_op(Op::True);
242 } else if val.as_bool() == Some(false) {
243 self.emit.emit_op(Op::False);
244 } else {
245 self.emit.emit_const(val.clone());
246 }
247 Ok(())
248 }
249
250 fn compile_var_load(&mut self, vr: &VarRef) -> Result<(), SemaError> {
253 match vr.resolution {
254 VarResolution::Local { slot } => match slot {
255 0 => self.emit.emit_op(Op::LoadLocal0),
256 1 => self.emit.emit_op(Op::LoadLocal1),
257 2 => self.emit.emit_op(Op::LoadLocal2),
258 3 => self.emit.emit_op(Op::LoadLocal3),
259 _ => {
260 self.emit.emit_op(Op::LoadLocal);
261 self.emit.emit_u16(slot);
262 }
263 },
264 VarResolution::Upvalue { index } => {
265 self.emit.emit_op(Op::LoadUpvalue);
266 self.emit.emit_u16(index);
267 }
268 VarResolution::Global { spur } => {
269 self.emit.emit_op(Op::LoadGlobal);
270 self.emit.emit_u32(spur_to_u32(spur));
271 }
272 }
273 Ok(())
274 }
275
276 fn compile_var_store(&mut self, vr: &VarRef) {
277 match vr.resolution {
278 VarResolution::Local { slot } => match slot {
279 0 => self.emit.emit_op(Op::StoreLocal0),
280 1 => self.emit.emit_op(Op::StoreLocal1),
281 2 => self.emit.emit_op(Op::StoreLocal2),
282 3 => self.emit.emit_op(Op::StoreLocal3),
283 _ => {
284 self.emit.emit_op(Op::StoreLocal);
285 self.emit.emit_u16(slot);
286 }
287 },
288 VarResolution::Upvalue { index } => {
289 self.emit.emit_op(Op::StoreUpvalue);
290 self.emit.emit_u16(index);
291 }
292 VarResolution::Global { spur } => {
293 self.emit.emit_op(Op::StoreGlobal);
294 self.emit.emit_u32(spur_to_u32(spur));
295 }
296 }
297 }
298
299 fn compile_if(
302 &mut self,
303 test: &ResolvedExpr,
304 then: &ResolvedExpr,
305 else_: &ResolvedExpr,
306 ) -> Result<(), SemaError> {
307 if let ResolvedExpr::Call { func, args, .. } = test {
310 if args.len() == 1 {
311 if let ResolvedExpr::Var(vr) = func.as_ref() {
312 if let VarResolution::Global { spur } = vr.resolution {
313 if resolve_spur(spur) == "not" {
314 self.compile_expr(&args[0])?;
315 let else_jump = self.emit.emit_jump(Op::JumpIfTrue);
316 self.compile_expr(then)?;
317 let end_jump = self.emit.emit_jump(Op::Jump);
318 self.emit.patch_jump(else_jump);
319 self.compile_expr(else_)?;
320 self.emit.patch_jump(end_jump);
321 return Ok(());
322 }
323 }
324 }
325 }
326 }
327
328 self.compile_expr(test)?;
329 let else_jump = self.emit.emit_jump(Op::JumpIfFalse);
330 self.compile_expr(then)?;
331 let end_jump = self.emit.emit_jump(Op::Jump);
332 self.emit.patch_jump(else_jump);
333 self.compile_expr(else_)?;
334 self.emit.patch_jump(end_jump);
335 Ok(())
336 }
337
338 fn compile_begin(&mut self, exprs: &[ResolvedExpr]) -> Result<(), SemaError> {
339 if exprs.is_empty() {
340 self.emit.emit_op(Op::Nil);
341 return Ok(());
342 }
343 for (i, expr) in exprs.iter().enumerate() {
344 self.compile_expr(expr)?;
345 if i < exprs.len() - 1 {
346 self.emit.emit_op(Op::Pop);
347 }
349 }
351 Ok(())
352 }
353
354 fn compile_set(&mut self, vr: &VarRef, val: &ResolvedExpr) -> Result<(), SemaError> {
357 self.compile_expr(val)?;
358 self.emit.emit_op(Op::Dup); self.compile_var_store(vr);
360 Ok(())
361 }
362
363 fn compile_define(&mut self, spur: Spur, val: &ResolvedExpr) -> Result<(), SemaError> {
364 self.compile_expr(val)?;
365 self.emit.emit_op(Op::DefineGlobal);
366 self.emit.emit_u32(spur_to_u32(spur));
367 self.emit.emit_op(Op::Nil); Ok(())
369 }
370
371 fn compile_lambda(&mut self, def: &ResolvedLambda) -> Result<(), SemaError> {
374 let mut inner = Compiler::new();
376 inner.n_locals = def.n_locals;
377
378 if def.body.is_empty() {
380 inner.emit.emit_op(Op::Nil);
381 } else {
382 for (i, expr) in def.body.iter().enumerate() {
383 inner.compile_expr(expr)?;
384 if i < def.body.len() - 1 {
385 inner.emit.emit_op(Op::Pop);
386 }
387 }
388 }
389 inner.emit.emit_op(Op::Return);
390
391 let func_id = self.functions.len() as u16;
392 let (mut chunk, mut child_functions) = inner.finish();
393
394 let offset = func_id + 1;
398 if offset > 0 && !child_functions.is_empty() {
399 patch_closure_func_ids(&mut chunk, offset);
400 for f in &mut child_functions {
401 patch_closure_func_ids(&mut f.chunk, offset);
402 }
403 }
404
405 let func = Function {
406 name: def.name,
407 chunk,
408 upvalue_descs: def.upvalues.clone(),
409 arity: def.params.len() as u16,
410 has_rest: def.rest.is_some(),
411 local_names: Vec::new(),
412 };
413 self.functions.push(func);
414 self.functions.extend(child_functions);
415
416 let n_upvalues = def.upvalues.len() as u16;
418 self.emit.emit_op(Op::MakeClosure);
419 self.emit.emit_u16(func_id);
420 self.emit.emit_u16(n_upvalues);
421
422 for uv in &def.upvalues {
424 match uv {
425 UpvalueDesc::ParentLocal(slot) => {
426 self.emit.emit_u16(1); self.emit.emit_u16(*slot);
428 }
429 UpvalueDesc::ParentUpvalue(idx) => {
430 self.emit.emit_u16(0); self.emit.emit_u16(*idx);
432 }
433 }
434 }
435
436 Ok(())
437 }
438
439 fn try_compile_intrinsic(
444 &mut self,
445 spur: Spur,
446 args: &[ResolvedExpr],
447 ) -> Result<bool, SemaError> {
448 let name = resolve_spur(spur);
449 let argc = args.len();
450
451 let op = match (name.as_str(), argc) {
452 ("not", 1) => Op::Not,
454 ("-", 1) => Op::Negate,
455 ("+", 2) => Op::AddInt,
457 ("-", 2) => Op::SubInt,
458 ("*", 2) => Op::MulInt,
459 ("/", 2) => Op::Div,
460 ("<", 2) => Op::LtInt,
462 (">", 2) => Op::Gt,
463 ("<=", 2) => Op::Le,
464 (">=", 2) => Op::Ge,
465 ("=", 2) => Op::EqInt,
466 _ => return Ok(false),
467 };
468
469 for arg in args {
471 self.compile_expr(arg)?;
472 self.stack_height += 1;
473 }
474 self.emit.emit_op(op);
475 self.stack_height -= argc as u16;
479 Ok(true)
480 }
481
482 fn compile_call(
483 &mut self,
484 func: &ResolvedExpr,
485 args: &[ResolvedExpr],
486 tail: bool,
487 ) -> Result<(), SemaError> {
488 if let ResolvedExpr::Var(vr) = func {
491 if let VarResolution::Global { spur } = vr.resolution {
492 if self.try_compile_intrinsic(spur, args)? {
493 return Ok(());
494 }
495 }
496 }
497
498 if !tail {
502 if let ResolvedExpr::Var(vr) = func {
503 if let VarResolution::Global { spur } = vr.resolution {
504 for arg in args {
506 self.compile_expr(arg)?;
507 self.stack_height += 1;
508 }
509 let argc = args.len() as u16;
510 self.emit.emit_op(Op::CallGlobal);
511 self.emit.emit_u32(spur_to_u32(spur));
512 self.emit.emit_u16(argc);
513 self.stack_height -= argc;
515 return Ok(());
516 }
517 }
518 }
519
520 self.compile_expr(func)?;
522 self.stack_height += 1;
523 for arg in args {
525 self.compile_expr(arg)?;
526 self.stack_height += 1;
527 }
528 let argc = args.len() as u16;
529 if tail {
530 self.emit.emit_op(Op::TailCall);
531 } else {
532 self.emit.emit_op(Op::Call);
533 }
534 self.emit.emit_u16(argc);
535 self.stack_height -= 1 + argc;
538 Ok(())
539 }
540
541 fn compile_let(
544 &mut self,
545 bindings: &[(VarRef, ResolvedExpr)],
546 body: &[ResolvedExpr],
547 ) -> Result<(), SemaError> {
548 for (_, init) in bindings {
550 self.compile_expr(init)?;
551 }
552 for (vr, _) in bindings.iter().rev() {
554 self.compile_var_store(vr);
555 }
556 self.compile_begin(body)
558 }
559
560 fn compile_let_star(
561 &mut self,
562 bindings: &[(VarRef, ResolvedExpr)],
563 body: &[ResolvedExpr],
564 ) -> Result<(), SemaError> {
565 for (vr, init) in bindings {
567 self.compile_expr(init)?;
568 self.compile_var_store(vr);
569 }
570 self.compile_begin(body)
571 }
572
573 fn compile_letrec(
574 &mut self,
575 bindings: &[(VarRef, ResolvedExpr)],
576 body: &[ResolvedExpr],
577 ) -> Result<(), SemaError> {
578 for (vr, _) in bindings {
580 self.emit.emit_op(Op::Nil);
581 self.compile_var_store(vr);
582 }
583 for (vr, init) in bindings {
585 self.compile_expr(init)?;
586 self.compile_var_store(vr);
587 }
588 self.compile_begin(body)
589 }
590
591 fn compile_named_let(
592 &mut self,
593 name: &VarRef,
594 bindings: &[(VarRef, ResolvedExpr)],
595 body: &[ResolvedExpr],
596 ) -> Result<(), SemaError> {
597 let params: Vec<Spur> = bindings.iter().map(|(vr, _)| vr.name).collect();
605
606 let mut inner = Compiler::new();
608 let max_slot = bindings
611 .iter()
612 .map(|(vr, _)| match vr.resolution {
613 VarResolution::Local { slot } => slot + 1,
614 _ => 0,
615 })
616 .max()
617 .unwrap_or(0);
618 let name_slot = match name.resolution {
619 VarResolution::Local { slot } => slot + 1,
620 _ => 0,
621 };
622 inner.n_locals = max_slot.max(name_slot);
623
624 if body.is_empty() {
626 inner.emit.emit_op(Op::Nil);
627 } else {
628 for (i, expr) in body.iter().enumerate() {
629 inner.compile_expr(expr)?;
630 if i < body.len() - 1 {
631 inner.emit.emit_op(Op::Pop);
632 }
633 }
634 }
635 inner.emit.emit_op(Op::Return);
636
637 let func_id = self.functions.len() as u16;
638 let (chunk, child_functions) = inner.finish();
639
640 let func = Function {
641 name: Some(name.name),
642 chunk,
643 upvalue_descs: Vec::new(),
644 arity: params.len() as u16,
645 has_rest: false,
646 local_names: Vec::new(),
647 };
648 self.functions.push(func);
649 self.functions.extend(child_functions);
650
651 self.emit.emit_op(Op::MakeClosure);
653 self.emit.emit_u16(func_id);
654 self.emit.emit_u16(0);
656
657 self.compile_var_store(name);
659
660 self.compile_var_load(name)?;
662 for (_, init) in bindings {
663 self.compile_expr(init)?;
664 }
665 let argc = bindings.len() as u16;
666 self.emit.emit_op(Op::Call);
667 self.emit.emit_u16(argc);
668
669 Ok(())
670 }
671
672 fn compile_do(&mut self, do_loop: &ResolvedDoLoop) -> Result<(), SemaError> {
675 for var in &do_loop.vars {
677 self.compile_expr(&var.init)?;
678 self.compile_var_store(&var.name);
679 }
680
681 let loop_top = self.emit.current_pc();
683
684 self.compile_expr(&do_loop.test)?;
686 let exit_jump = self.emit.emit_jump(Op::JumpIfTrue);
687
688 for expr in &do_loop.body {
690 self.compile_expr(expr)?;
691 self.emit.emit_op(Op::Pop);
692 }
693
694 let mut step_vars = Vec::new();
697 for var in &do_loop.vars {
698 if let Some(step) = &var.step {
699 self.compile_expr(step)?;
700 step_vars.push(&var.name);
701 }
702 }
703 for vr in step_vars.iter().rev() {
705 self.compile_var_store(vr);
706 }
707
708 self.emit.emit_op(Op::Jump);
710 let jump_end_pc = self.emit.current_pc();
711 let offset = loop_top as i32 - (jump_end_pc as i32 + 4);
712 self.emit.emit_i32(offset);
713
714 self.emit.patch_jump(exit_jump);
716 if do_loop.result.is_empty() {
717 self.emit.emit_op(Op::Nil);
718 } else {
719 for (i, expr) in do_loop.result.iter().enumerate() {
720 self.compile_expr(expr)?;
721 if i < do_loop.result.len() - 1 {
722 self.emit.emit_op(Op::Pop);
723 }
724 }
725 }
726
727 Ok(())
728 }
729
730 fn compile_try(
733 &mut self,
734 body: &[ResolvedExpr],
735 catch_var: &VarRef,
736 handler: &[ResolvedExpr],
737 ) -> Result<(), SemaError> {
738 let try_start = self.emit.current_pc();
739
740 self.compile_begin(body)?;
742 let try_end = self.emit.current_pc();
743
744 let success_jump = self.emit.emit_jump(Op::Jump);
746
747 let handler_pc = self.emit.current_pc();
748
749 let catch_slot = match catch_var.resolution {
752 VarResolution::Local { slot } => slot,
753 _ => 0,
754 };
755 self.emit.emit_op(Op::StoreLocal);
756 self.emit.emit_u16(catch_slot);
757
758 self.compile_begin(handler)?;
760
761 self.emit.patch_jump(success_jump);
762
763 self.add_exception_entry(ExceptionEntry {
771 try_start,
772 try_end,
773 handler_pc,
774 stack_depth: self.n_locals + self.stack_height,
779 catch_slot,
780 });
781
782 Ok(())
783 }
784
785 fn add_exception_entry(&mut self, entry: ExceptionEntry) {
786 self.exception_entries.push(entry);
791 }
792
793 fn compile_throw(&mut self, val: &ResolvedExpr) -> Result<(), SemaError> {
794 self.compile_expr(val)?;
795 self.emit.emit_op(Op::Throw);
796 Ok(())
797 }
798
799 fn compile_and(&mut self, exprs: &[ResolvedExpr]) -> Result<(), SemaError> {
802 if exprs.is_empty() {
803 self.emit.emit_op(Op::True);
804 return Ok(());
805 }
806
807 let mut jumps = Vec::new();
808 for (i, expr) in exprs.iter().enumerate() {
809 self.compile_expr(expr)?;
810 if i < exprs.len() - 1 {
811 self.emit.emit_op(Op::Dup);
813 let jump = self.emit.emit_jump(Op::JumpIfFalse);
814 jumps.push(jump);
815 self.emit.emit_op(Op::Pop); }
817 }
818 let end_jump = self.emit.emit_jump(Op::Jump);
819 for jump in jumps {
821 self.emit.patch_jump(jump);
822 }
823 self.emit.patch_jump(end_jump);
824 Ok(())
825 }
826
827 fn compile_or(&mut self, exprs: &[ResolvedExpr]) -> Result<(), SemaError> {
828 if exprs.is_empty() {
829 self.emit.emit_op(Op::False);
830 return Ok(());
831 }
832
833 let mut jumps = Vec::new();
834 for (i, expr) in exprs.iter().enumerate() {
835 self.compile_expr(expr)?;
836 if i < exprs.len() - 1 {
837 self.emit.emit_op(Op::Dup);
838 let jump = self.emit.emit_jump(Op::JumpIfTrue);
839 jumps.push(jump);
840 self.emit.emit_op(Op::Pop);
841 }
842 }
843 let end_jump = self.emit.emit_jump(Op::Jump);
844 for jump in jumps {
845 self.emit.patch_jump(jump);
846 }
847 self.emit.patch_jump(end_jump);
848 Ok(())
849 }
850
851 fn compile_make_list(&mut self, exprs: &[ResolvedExpr]) -> Result<(), SemaError> {
854 for expr in exprs {
855 self.compile_expr(expr)?;
856 }
857 self.emit.emit_op(Op::MakeList);
858 self.emit.emit_u16(exprs.len() as u16);
859 Ok(())
860 }
861
862 fn compile_make_vector(&mut self, exprs: &[ResolvedExpr]) -> Result<(), SemaError> {
863 for expr in exprs {
864 self.compile_expr(expr)?;
865 }
866 self.emit.emit_op(Op::MakeVector);
867 self.emit.emit_u16(exprs.len() as u16);
868 Ok(())
869 }
870
871 fn compile_make_map(
872 &mut self,
873 pairs: &[(ResolvedExpr, ResolvedExpr)],
874 ) -> Result<(), SemaError> {
875 for (key, val) in pairs {
876 self.compile_expr(key)?;
877 self.compile_expr(val)?;
878 }
879 self.emit.emit_op(Op::MakeMap);
880 self.emit.emit_u16(pairs.len() as u16);
881 Ok(())
882 }
883
884 fn compile_eval(&mut self, expr: &ResolvedExpr) -> Result<(), SemaError> {
891 self.emit_runtime_call("__vm-eval", &[expr])
892 }
893
894 fn compile_load(&mut self, path: &ResolvedExpr) -> Result<(), SemaError> {
895 self.emit_runtime_call("__vm-load", &[path])
896 }
897
898 fn compile_import(&mut self, path: &ResolvedExpr, selective: &[Spur]) -> Result<(), SemaError> {
899 let sel_list: Vec<Value> = selective
900 .iter()
901 .map(|s| Value::symbol_from_spur(*s))
902 .collect();
903 self.emit_runtime_call_with_const("__vm-import", path, &Value::list(sel_list))
904 }
905
906 fn compile_module(
907 &mut self,
908 _name: Spur,
909 _exports: &[Spur],
910 body: &[ResolvedExpr],
911 ) -> Result<(), SemaError> {
912 for (i, expr) in body.iter().enumerate() {
914 self.compile_expr(expr)?;
915 if i < body.len() - 1 {
916 self.emit.emit_op(Op::Pop);
917 }
918 }
919 if body.is_empty() {
920 self.emit.emit_op(Op::Nil);
921 }
922 Ok(())
925 }
926
927 fn compile_defmacro(
928 &mut self,
929 name: Spur,
930 params: &[Spur],
931 rest: &Option<Spur>,
932 body: &[ResolvedExpr],
933 ) -> Result<(), SemaError> {
934 let param_vals: Vec<Value> = params.iter().map(|s| Value::symbol_from_spur(*s)).collect();
937 self.emit.emit_op(Op::LoadGlobal);
938 self.emit.emit_u32(spur_to_u32(intern("__vm-defmacro")));
939 self.emit.emit_const(Value::symbol_from_spur(name));
940 self.emit.emit_const(Value::list(param_vals));
941 if let Some(r) = rest {
942 self.emit.emit_const(Value::symbol_from_spur(*r));
943 } else {
944 self.emit.emit_op(Op::Nil);
945 }
946 self.compile_begin(body)?;
948 self.emit.emit_op(Op::Call);
949 self.emit.emit_u16(4);
950 Ok(())
951 }
952
953 fn compile_define_record_type(
954 &mut self,
955 type_name: Spur,
956 ctor_name: Spur,
957 pred_name: Spur,
958 field_names: &[Spur],
959 field_specs: &[(Spur, Spur)],
960 ) -> Result<(), SemaError> {
961 self.emit.emit_op(Op::LoadGlobal);
964 self.emit
965 .emit_u32(spur_to_u32(intern("__vm-define-record-type")));
966 self.emit.emit_const(Value::symbol_from_spur(type_name));
967 self.emit.emit_const(Value::symbol_from_spur(ctor_name));
968 self.emit.emit_const(Value::symbol_from_spur(pred_name));
969 let fields: Vec<Value> = field_names
970 .iter()
971 .map(|s| Value::symbol_from_spur(*s))
972 .collect();
973 self.emit.emit_const(Value::list(fields));
974 let specs: Vec<Value> = field_specs
975 .iter()
976 .map(|(f, a)| {
977 Value::list(vec![
978 Value::symbol_from_spur(*f),
979 Value::symbol_from_spur(*a),
980 ])
981 })
982 .collect();
983 self.emit.emit_const(Value::list(specs));
984 self.emit.emit_op(Op::Call);
985 self.emit.emit_u16(5);
986 Ok(())
987 }
988
989 fn compile_prompt(&mut self, entries: &[ResolvedPromptEntry]) -> Result<(), SemaError> {
990 self.emit.emit_op(Op::LoadGlobal);
992 self.emit.emit_u32(spur_to_u32(intern("__vm-prompt")));
993 for entry in entries {
995 match entry {
996 ResolvedPromptEntry::RoleContent { role, parts } => {
997 self.emit.emit_const(Value::string(role));
998 for part in parts {
999 self.compile_expr(part)?;
1000 }
1001 self.emit.emit_op(Op::MakeList);
1002 self.emit.emit_u16(parts.len() as u16);
1003 self.emit.emit_op(Op::MakeList);
1005 self.emit.emit_u16(2);
1006 }
1007 ResolvedPromptEntry::Expr(expr) => {
1008 self.compile_expr(expr)?;
1009 }
1010 }
1011 }
1012 self.emit.emit_op(Op::MakeList);
1013 self.emit.emit_u16(entries.len() as u16);
1014 self.emit.emit_op(Op::Call);
1015 self.emit.emit_u16(1);
1016 Ok(())
1017 }
1018
1019 fn compile_message(
1020 &mut self,
1021 role: &ResolvedExpr,
1022 parts: &[ResolvedExpr],
1023 ) -> Result<(), SemaError> {
1024 self.emit.emit_op(Op::LoadGlobal);
1025 self.emit.emit_u32(spur_to_u32(intern("__vm-message")));
1026 self.compile_expr(role)?;
1027 for part in parts {
1028 self.compile_expr(part)?;
1029 }
1030 self.emit.emit_op(Op::MakeList);
1031 self.emit.emit_u16(parts.len() as u16);
1032 self.emit.emit_op(Op::Call);
1033 self.emit.emit_u16(2);
1034 Ok(())
1035 }
1036
1037 fn compile_deftool(
1038 &mut self,
1039 name: Spur,
1040 description: &ResolvedExpr,
1041 parameters: &ResolvedExpr,
1042 handler: &ResolvedExpr,
1043 ) -> Result<(), SemaError> {
1044 self.emit.emit_op(Op::LoadGlobal);
1045 self.emit.emit_u32(spur_to_u32(intern("__vm-deftool")));
1046 self.emit.emit_const(Value::symbol_from_spur(name));
1047 self.compile_expr(description)?;
1048 self.compile_expr(parameters)?;
1049 self.compile_expr(handler)?;
1050 self.emit.emit_op(Op::Call);
1051 self.emit.emit_u16(4);
1052 Ok(())
1053 }
1054
1055 fn compile_defagent(&mut self, name: Spur, options: &ResolvedExpr) -> Result<(), SemaError> {
1056 self.emit.emit_op(Op::LoadGlobal);
1057 self.emit.emit_u32(spur_to_u32(intern("__vm-defagent")));
1058 self.emit.emit_const(Value::symbol_from_spur(name));
1059 self.compile_expr(options)?;
1060 self.emit.emit_op(Op::Call);
1061 self.emit.emit_u16(2);
1062 Ok(())
1063 }
1064
1065 fn compile_delay(&mut self, expr: &ResolvedExpr) -> Result<(), SemaError> {
1066 self.emit.emit_op(Op::LoadGlobal);
1070 self.emit.emit_u32(spur_to_u32(intern("__vm-delay")));
1071 self.compile_expr(expr)?;
1072 self.emit.emit_op(Op::Call);
1073 self.emit.emit_u16(1);
1074 Ok(())
1075 }
1076
1077 fn compile_force(&mut self, expr: &ResolvedExpr) -> Result<(), SemaError> {
1078 self.emit.emit_op(Op::LoadGlobal);
1079 self.emit.emit_u32(spur_to_u32(intern("__vm-force")));
1080 self.compile_expr(expr)?;
1081 self.emit.emit_op(Op::Call);
1082 self.emit.emit_u16(1);
1083 Ok(())
1084 }
1085
1086 fn compile_macroexpand(&mut self, expr: &ResolvedExpr) -> Result<(), SemaError> {
1087 self.emit.emit_op(Op::LoadGlobal);
1088 self.emit.emit_u32(spur_to_u32(intern("__vm-macroexpand")));
1089 self.compile_expr(expr)?;
1090 self.emit.emit_op(Op::Call);
1091 self.emit.emit_u16(1);
1092 Ok(())
1093 }
1094
1095 fn emit_runtime_call(&mut self, name: &str, args: &[&ResolvedExpr]) -> Result<(), SemaError> {
1098 self.emit.emit_op(Op::LoadGlobal);
1099 self.emit.emit_u32(spur_to_u32(intern(name)));
1100 for arg in args {
1101 self.compile_expr(arg)?;
1102 }
1103 self.emit.emit_op(Op::Call);
1104 self.emit.emit_u16(args.len() as u16);
1105 Ok(())
1106 }
1107
1108 fn emit_runtime_call_with_const(
1109 &mut self,
1110 name: &str,
1111 arg1: &ResolvedExpr,
1112 arg2: &Value,
1113 ) -> Result<(), SemaError> {
1114 self.emit.emit_op(Op::LoadGlobal);
1115 self.emit.emit_u32(spur_to_u32(intern(name)));
1116 self.compile_expr(arg1)?;
1117 self.emit.emit_const(arg2.clone());
1118 self.emit.emit_op(Op::Call);
1119 self.emit.emit_u16(2);
1120 Ok(())
1121 }
1122}
1123
1124fn spur_to_u32(spur: Spur) -> u32 {
1125 spur.into_inner().get()
1126}
1127
1128#[cfg(test)]
1129mod tests {
1130 use super::*;
1131 use crate::lower::lower;
1132 use crate::resolve::resolve;
1133
1134 fn compile_str(input: &str) -> CompileResult {
1135 let val = sema_reader::read(input).unwrap();
1136 let core = lower(&val).unwrap();
1137 let resolved = resolve(&core).unwrap();
1138 compile(&resolved).unwrap()
1139 }
1140
1141 fn compile_many_str(input: &str) -> CompileResult {
1142 let vals = sema_reader::read_many(input).unwrap();
1143 let mut resolved = Vec::new();
1144 for val in &vals {
1145 let core = lower(val).unwrap();
1146 resolved.push(resolve(&core).unwrap());
1147 }
1148 compile_many(&resolved).unwrap()
1149 }
1150
1151 fn extract_ops(chunk: &Chunk) -> Vec<Op> {
1153 let code = &chunk.code;
1154 let mut ops = Vec::new();
1155 let mut pc = 0;
1156 while pc < code.len() {
1157 let op = unsafe { std::mem::transmute::<u8, Op>(code[pc]) };
1158 ops.push(op);
1159 pc += 1;
1160 match op {
1162 Op::Const
1163 | Op::LoadLocal
1164 | Op::StoreLocal
1165 | Op::LoadUpvalue
1166 | Op::StoreUpvalue
1167 | Op::Call
1168 | Op::TailCall
1169 | Op::MakeList
1170 | Op::MakeVector
1171 | Op::MakeMap
1172 | Op::MakeHashMap => pc += 2,
1173 Op::LoadGlobal | Op::StoreGlobal | Op::DefineGlobal => pc += 4,
1174 Op::CallGlobal => pc += 6, Op::Jump | Op::JumpIfFalse | Op::JumpIfTrue => pc += 4,
1176 Op::CallNative => pc += 4,
1177 Op::MakeClosure => {
1178 let func_id = u16::from_le_bytes([code[pc], code[pc + 1]]);
1179 let n_upvalues = u16::from_le_bytes([code[pc + 2], code[pc + 3]]);
1180 pc += 4;
1181 pc += n_upvalues as usize * 4; let _ = func_id;
1183 }
1184 _ => {} }
1186 }
1187 ops
1188 }
1189
1190 fn read_jump_offset(chunk: &Chunk, op_pc: usize) -> i32 {
1192 i32::from_le_bytes([
1193 chunk.code[op_pc + 1],
1194 chunk.code[op_pc + 2],
1195 chunk.code[op_pc + 3],
1196 chunk.code[op_pc + 4],
1197 ])
1198 }
1199
1200 #[test]
1203 fn test_compile_int_literal() {
1204 let result = compile_str("42");
1205 let ops = extract_ops(&result.chunk);
1206 assert_eq!(ops, vec![Op::Const, Op::Return]);
1207 assert_eq!(result.chunk.consts[0], Value::int(42));
1208 }
1209
1210 #[test]
1211 fn test_compile_nil() {
1212 let result = compile_str("()");
1213 let ops = extract_ops(&result.chunk);
1214 assert_eq!(ops, vec![Op::Nil, Op::Return]);
1215 }
1216
1217 #[test]
1218 fn test_compile_true_false() {
1219 let t = compile_str("#t");
1220 assert_eq!(extract_ops(&t.chunk), vec![Op::True, Op::Return]);
1221
1222 let f = compile_str("#f");
1223 assert_eq!(extract_ops(&f.chunk), vec![Op::False, Op::Return]);
1224 }
1225
1226 #[test]
1227 fn test_compile_string_literal() {
1228 let result = compile_str("\"hello\"");
1229 let ops = extract_ops(&result.chunk);
1230 assert_eq!(ops, vec![Op::Const, Op::Return]);
1231 assert_eq!(result.chunk.consts[0].as_str(), Some("hello"));
1232 }
1233
1234 #[test]
1237 fn test_compile_global_var() {
1238 let result = compile_str("x");
1239 let ops = extract_ops(&result.chunk);
1240 assert_eq!(ops, vec![Op::LoadGlobal, Op::Return]);
1241 }
1242
1243 #[test]
1246 fn test_compile_if() {
1247 let result = compile_str("(if #t 1 2)");
1248 let ops = extract_ops(&result.chunk);
1249 assert_eq!(
1251 ops,
1252 vec![
1253 Op::True,
1254 Op::JumpIfFalse,
1255 Op::Const,
1256 Op::Jump,
1257 Op::Const,
1258 Op::Return
1259 ]
1260 );
1261 }
1262
1263 #[test]
1264 fn test_compile_nested_if() {
1265 let result = compile_str("(if #t (if #f 1 2) 3)");
1266 let ops = extract_ops(&result.chunk);
1267 let jif_count = ops.iter().filter(|&&op| op == Op::JumpIfFalse).count();
1268 assert_eq!(jif_count, 2);
1269 }
1270
1271 #[test]
1272 fn test_compile_begin() {
1273 let result = compile_str("(begin 1 2 3)");
1274 let ops = extract_ops(&result.chunk);
1275 assert_eq!(
1277 ops,
1278 vec![
1279 Op::Const,
1280 Op::Pop,
1281 Op::Const,
1282 Op::Pop,
1283 Op::Const,
1284 Op::Return
1285 ]
1286 );
1287 }
1288
1289 #[test]
1292 fn test_compile_define() {
1293 let result = compile_str("(define x 42)");
1294 let ops = extract_ops(&result.chunk);
1295 assert_eq!(ops, vec![Op::Const, Op::DefineGlobal, Op::Nil, Op::Return]);
1297 }
1298
1299 #[test]
1302 fn test_compile_lambda() {
1303 let result = compile_str("(lambda (x) x)");
1304 assert_eq!(result.functions.len(), 1);
1305 let func = &result.functions[0];
1306 assert_eq!(func.arity, 1);
1307 assert!(!func.has_rest);
1308
1309 let inner_ops = extract_ops(&func.chunk);
1311 assert_eq!(inner_ops, vec![Op::LoadLocal0, Op::Return]);
1312
1313 let top_ops = extract_ops(&result.chunk);
1315 assert_eq!(top_ops, vec![Op::MakeClosure, Op::Return]);
1316 }
1317
1318 #[test]
1319 fn test_compile_lambda_rest_param() {
1320 let result = compile_str("(lambda (x . rest) x)");
1321 assert_eq!(result.functions.len(), 1);
1322 let func = &result.functions[0];
1323 assert_eq!(func.arity, 1);
1324 assert!(func.has_rest);
1325 }
1326
1327 #[test]
1330 fn test_compile_non_tail_call() {
1331 let result = compile_str("(+ 1 2)");
1334 let ops = extract_ops(&result.chunk);
1335 assert_eq!(ops, vec![Op::Const, Op::Const, Op::AddInt, Op::Return]);
1337 }
1338
1339 #[test]
1340 fn test_compile_tail_call() {
1341 let result = compile_str("(lambda () (f 1))");
1342 assert_eq!(result.functions.len(), 1);
1343 let inner_ops = extract_ops(&result.functions[0].chunk);
1344 assert_eq!(
1346 inner_ops,
1347 vec![Op::LoadGlobal, Op::Const, Op::TailCall, Op::Return]
1348 );
1349 assert!(!inner_ops.contains(&Op::Call));
1351 }
1352
1353 #[test]
1354 fn test_compile_non_tail_in_begin() {
1355 let result = compile_str("(lambda () (f 1) (g 2))");
1357 let inner_ops = extract_ops(&result.functions[0].chunk);
1358 assert_eq!(
1361 inner_ops,
1362 vec![
1363 Op::Const,
1364 Op::CallGlobal,
1365 Op::Pop,
1366 Op::LoadGlobal,
1367 Op::Const,
1368 Op::TailCall,
1369 Op::Return
1370 ]
1371 );
1372 }
1373
1374 #[test]
1377 fn test_compile_intrinsic_sub() {
1378 let result = compile_str("(- 5 3)");
1379 let ops = extract_ops(&result.chunk);
1380 assert_eq!(ops, vec![Op::Const, Op::Const, Op::SubInt, Op::Return]);
1381 }
1382
1383 #[test]
1384 fn test_compile_intrinsic_lt() {
1385 let result = compile_str("(< 1 2)");
1386 let ops = extract_ops(&result.chunk);
1387 assert_eq!(ops, vec![Op::Const, Op::Const, Op::LtInt, Op::Return]);
1388 }
1389
1390 #[test]
1391 fn test_compile_intrinsic_not() {
1392 let result = compile_str("(not #t)");
1393 let ops = extract_ops(&result.chunk);
1394 assert_eq!(ops, vec![Op::True, Op::Not, Op::Return]);
1395 }
1396
1397 #[test]
1398 fn test_compile_intrinsic_negate() {
1399 let result = compile_str("(- 5)");
1400 let ops = extract_ops(&result.chunk);
1401 assert_eq!(ops, vec![Op::Const, Op::Negate, Op::Return]);
1402 }
1403
1404 #[test]
1405 fn test_compile_intrinsic_in_tail_position() {
1406 let result = compile_str("(lambda (x y) (+ x y))");
1408 let inner_ops = extract_ops(&result.functions[0].chunk);
1409 assert_eq!(
1410 inner_ops,
1411 vec![Op::LoadLocal0, Op::LoadLocal1, Op::AddInt, Op::Return]
1412 );
1413 }
1414
1415 #[test]
1416 fn test_compile_non_intrinsic_still_uses_call_global() {
1417 let result = compile_str("(foo 1 2)");
1419 let ops = extract_ops(&result.chunk);
1420 assert_eq!(ops, vec![Op::Const, Op::Const, Op::CallGlobal, Op::Return]);
1421 }
1422
1423 #[test]
1424 fn test_compile_if_not_peephole() {
1425 let result = compile_str("(lambda (x) (if (not x) 1 2))");
1427 let inner_ops = extract_ops(&result.functions[0].chunk);
1428 assert_eq!(
1430 inner_ops,
1431 vec![
1432 Op::LoadLocal0,
1433 Op::JumpIfTrue,
1434 Op::Const,
1435 Op::Jump,
1436 Op::Const,
1437 Op::Return
1438 ]
1439 );
1440 assert!(!inner_ops.contains(&Op::Not));
1442 }
1443
1444 #[test]
1447 fn test_compile_let() {
1448 let result = compile_str("(lambda () (let ((x 1) (y 2)) x))");
1449 let inner_ops = extract_ops(&result.functions[0].chunk);
1450 assert_eq!(
1452 inner_ops,
1453 vec![
1454 Op::Const,
1455 Op::Const,
1456 Op::StoreLocal1,
1457 Op::StoreLocal0,
1458 Op::LoadLocal0,
1459 Op::Return
1460 ]
1461 );
1462 }
1463
1464 #[test]
1465 fn test_compile_let_star() {
1466 let result = compile_str("(lambda () (let* ((x 1) (y x)) y))");
1468 let inner_ops = extract_ops(&result.functions[0].chunk);
1469 assert_eq!(
1471 inner_ops,
1472 vec![
1473 Op::Const,
1474 Op::StoreLocal0,
1475 Op::LoadLocal0,
1476 Op::StoreLocal1,
1477 Op::LoadLocal1,
1478 Op::Return
1479 ]
1480 );
1481 }
1482
1483 #[test]
1484 fn test_compile_letrec() {
1485 let result = compile_str("(lambda () (letrec ((x 1)) x))");
1486 let inner_ops = extract_ops(&result.functions[0].chunk);
1487 assert_eq!(
1489 inner_ops,
1490 vec![
1491 Op::Nil,
1492 Op::StoreLocal0,
1493 Op::Const,
1494 Op::StoreLocal0,
1495 Op::LoadLocal0,
1496 Op::Return
1497 ]
1498 );
1499 }
1500
1501 #[test]
1504 fn test_compile_set_local() {
1505 let result = compile_str("(lambda (x) (set! x 42))");
1506 let inner_ops = extract_ops(&result.functions[0].chunk);
1507 assert_eq!(
1509 inner_ops,
1510 vec![Op::Const, Op::Dup, Op::StoreLocal0, Op::Return]
1511 );
1512 }
1513
1514 #[test]
1515 fn test_compile_set_global() {
1516 let result = compile_str("(set! x 42)");
1517 let ops = extract_ops(&result.chunk);
1518 assert_eq!(ops, vec![Op::Const, Op::Dup, Op::StoreGlobal, Op::Return]);
1520 }
1521
1522 #[test]
1523 fn test_compile_set_upvalue() {
1524 let result = compile_str("(lambda (x) (lambda () (set! x 1)))");
1526 assert_eq!(result.functions.len(), 2);
1527 let inner_ops = extract_ops(&result.functions[1].chunk);
1528 assert_eq!(
1530 inner_ops,
1531 vec![Op::Const, Op::Dup, Op::StoreUpvalue, Op::Return]
1532 );
1533 }
1534
1535 #[test]
1538 fn test_compile_and_empty() {
1539 let result = compile_str("(and)");
1540 let ops = extract_ops(&result.chunk);
1541 assert_eq!(ops, vec![Op::True, Op::Return]);
1542 }
1543
1544 #[test]
1545 fn test_compile_or_empty() {
1546 let result = compile_str("(or)");
1547 let ops = extract_ops(&result.chunk);
1548 assert_eq!(ops, vec![Op::False, Op::Return]);
1549 }
1550
1551 #[test]
1552 fn test_compile_and_short_circuit() {
1553 let result = compile_str("(and 1 2)");
1554 let ops = extract_ops(&result.chunk);
1555 assert_eq!(
1557 ops,
1558 vec![
1559 Op::Const,
1560 Op::Dup,
1561 Op::JumpIfFalse,
1562 Op::Pop,
1563 Op::Const,
1564 Op::Jump,
1565 Op::Return
1566 ]
1567 );
1568 }
1569
1570 #[test]
1571 fn test_compile_or_short_circuit() {
1572 let result = compile_str("(or 1 2)");
1573 let ops = extract_ops(&result.chunk);
1574 assert_eq!(
1576 ops,
1577 vec![
1578 Op::Const,
1579 Op::Dup,
1580 Op::JumpIfTrue,
1581 Op::Pop,
1582 Op::Const,
1583 Op::Jump,
1584 Op::Return
1585 ]
1586 );
1587 }
1588
1589 #[test]
1592 fn test_compile_vector_literal() {
1593 let result = compile_str("[1 2 3]");
1594 let ops = extract_ops(&result.chunk);
1595 assert_eq!(
1596 ops,
1597 vec![Op::Const, Op::Const, Op::Const, Op::MakeVector, Op::Return]
1598 );
1599 }
1600
1601 #[test]
1602 fn test_compile_quote() {
1603 let result = compile_str("'(1 2 3)");
1604 let ops = extract_ops(&result.chunk);
1605 assert_eq!(ops, vec![Op::Const, Op::Return]);
1606 }
1607
1608 #[test]
1611 fn test_compile_throw() {
1612 let result = compile_str("(throw 42)");
1613 let ops = extract_ops(&result.chunk);
1614 assert_eq!(ops, vec![Op::Const, Op::Throw, Op::Return]);
1615 }
1616
1617 #[test]
1618 fn test_compile_try_catch() {
1619 let result = compile_str("(lambda () (try (/ 1 0) (catch e e)))");
1620 let func = &result.functions[0];
1621 assert_eq!(func.chunk.exception_table.len(), 1);
1623 let entry = &func.chunk.exception_table[0];
1624 assert!(entry.try_start < entry.try_end);
1625 assert!(entry.handler_pc > entry.try_end);
1626 assert!((entry.handler_pc as usize) < func.chunk.code.len());
1627 let ops = extract_ops(&func.chunk);
1629 assert!(ops.contains(&Op::StoreLocal)); assert!(ops.contains(&Op::Jump));
1632 }
1633
1634 #[test]
1637 fn test_compile_closure_with_upvalue() {
1638 let result = compile_str("(lambda (x) (lambda () x))");
1639 assert_eq!(result.functions.len(), 2);
1640 let outer = &result.functions[0];
1642 let outer_ops = extract_ops(&outer.chunk);
1643 assert!(outer_ops.contains(&Op::MakeClosure));
1644 let inner = &result.functions[1];
1646 let inner_ops = extract_ops(&inner.chunk);
1647 assert_eq!(inner_ops, vec![Op::LoadUpvalue, Op::Return]);
1648 assert_eq!(inner.upvalue_descs.len(), 1);
1650 assert!(matches!(
1651 inner.upvalue_descs[0],
1652 UpvalueDesc::ParentLocal(0)
1653 ));
1654 }
1655
1656 #[test]
1657 fn test_compile_nested_lambda_func_ids() {
1658 let result = compile_str("(lambda () (lambda () 1) (lambda () 2))");
1661 assert_eq!(result.functions.len(), 3);
1662 let f1 = &result.functions[1];
1664 let f1_ops = extract_ops(&f1.chunk);
1665 assert_eq!(f1_ops, vec![Op::Const, Op::Return]);
1666 assert_eq!(f1.chunk.consts[0], Value::int(1));
1667
1668 let f2 = &result.functions[2];
1669 let f2_ops = extract_ops(&f2.chunk);
1670 assert_eq!(f2_ops, vec![Op::Const, Op::Return]);
1671 assert_eq!(f2.chunk.consts[0], Value::int(2));
1672
1673 let outer = &result.functions[0];
1676 let outer_ops = extract_ops(&outer.chunk);
1677 let mc_count = outer_ops
1678 .iter()
1679 .filter(|&&op| op == Op::MakeClosure)
1680 .count();
1681 assert_eq!(mc_count, 2);
1682 }
1683
1684 #[test]
1687 fn test_compile_do_loop() {
1688 let result = compile_str("(lambda () (do ((i 0 (+ i 1))) ((= i 10) i) (display i)))");
1689 let func = &result.functions[0];
1690 let ops = extract_ops(&func.chunk);
1691 let jump_pcs: Vec<usize> = (0..func.chunk.code.len())
1693 .filter(|&pc| func.chunk.code[pc] == Op::Jump as u8)
1694 .collect();
1695 let has_back_edge = jump_pcs
1697 .iter()
1698 .any(|&pc| read_jump_offset(&func.chunk, pc) < 0);
1699 assert!(has_back_edge, "do loop must have a backward jump");
1700 assert!(ops.contains(&Op::JumpIfTrue));
1702 }
1703
1704 #[test]
1707 fn test_compile_named_let() {
1708 let result = compile_str("(lambda () (let loop ((n 10)) (if (= n 0) n (loop (- n 1)))))");
1710 assert!(result.functions.len() >= 2);
1712 let outer = &result.functions[0];
1714 let outer_ops = extract_ops(&outer.chunk);
1715 assert!(outer_ops.contains(&Op::MakeClosure));
1716 assert!(outer_ops.contains(&Op::TailCall) || outer_ops.contains(&Op::Call));
1717 }
1718
1719 #[test]
1722 fn test_compile_many_empty() {
1723 let result = compile_many(&[]).unwrap();
1724 let ops = extract_ops(&result.chunk);
1725 assert_eq!(ops, vec![Op::Nil, Op::Return]);
1726 }
1727
1728 #[test]
1729 fn test_compile_many_multiple() {
1730 let result = compile_many_str("1 2 3");
1731 let ops = extract_ops(&result.chunk);
1732 assert_eq!(
1734 ops,
1735 vec![
1736 Op::Const,
1737 Op::Pop,
1738 Op::Const,
1739 Op::Pop,
1740 Op::Const,
1741 Op::Return
1742 ]
1743 );
1744 }
1745
1746 #[test]
1747 fn test_compile_many_single() {
1748 let result = compile_many_str("42");
1749 let ops = extract_ops(&result.chunk);
1750 assert_eq!(ops, vec![Op::Const, Op::Return]);
1752 }
1753
1754 #[test]
1757 fn test_calling_convention_runtime_call() {
1758 let result = compile_str("(eval 42)");
1760 let ops = extract_ops(&result.chunk);
1761 assert_eq!(ops, vec![Op::LoadGlobal, Op::Const, Op::Call, Op::Return]);
1762 assert_eq!(ops[0], Op::LoadGlobal);
1764 }
1765
1766 #[test]
1769 fn test_compile_map_literal() {
1770 let result = compile_str("{:a 1 :b 2}");
1771 let ops = extract_ops(&result.chunk);
1772 assert_eq!(
1774 ops,
1775 vec![
1776 Op::Const,
1777 Op::Const,
1778 Op::Const,
1779 Op::Const,
1780 Op::MakeMap,
1781 Op::Return
1782 ]
1783 );
1784 }
1785
1786 #[test]
1787 fn test_compile_depth_limit() {
1788 let mut expr = ResolvedExpr::Const(Value::int(1));
1790 for _ in 0..300 {
1791 expr = ResolvedExpr::Begin(vec![expr]);
1792 }
1793 let result = compile(&expr);
1794 let err = result.err().expect("expected compilation to fail");
1795 let msg = err.to_string();
1796 assert!(
1797 msg.contains("compilation depth"),
1798 "expected compilation depth error, got: {msg}"
1799 );
1800 }
1801}