1use std::cell::RefCell;
2use std::collections::BTreeMap;
3use std::rc::Rc;
4
5use sema_core::{
6 resolve as resolve_spur, Env, EvalContext, SemaError, Spur, Value, NAN_INT_SIGN_BIT,
7 NAN_INT_SMALL_PATTERN, NAN_PAYLOAD_MASK, NAN_TAG_MASK,
8};
9
10use crate::chunk::Function;
11
12#[derive(Debug)]
14pub struct UpvalueCell {
15 pub value: RefCell<Value>,
16}
17
18impl UpvalueCell {
19 pub fn new(value: Value) -> Self {
20 UpvalueCell {
21 value: RefCell::new(value),
22 }
23 }
24}
25
26#[derive(Debug, Clone)]
28pub struct Closure {
29 pub func: Rc<Function>,
30 pub upvalues: Vec<Rc<UpvalueCell>>,
31}
32
33struct VmClosurePayload {
36 closure: Rc<Closure>,
37 functions: Rc<Vec<Rc<Function>>>,
38}
39
40struct CallFrame {
42 closure: Rc<Closure>,
43 pc: usize,
44 base: usize,
45 open_upvalues: Option<Vec<Option<Rc<UpvalueCell>>>>,
49}
50
51const GLOBAL_CACHE_SIZE: usize = 16;
53
54pub struct VM {
56 stack: Vec<Value>,
57 frames: Vec<CallFrame>,
58 globals: Rc<Env>,
59 functions: Rc<Vec<Rc<Function>>>,
60 global_cache: [(u32, u64, Value); GLOBAL_CACHE_SIZE],
62}
63
64impl VM {
65 pub fn new(globals: Rc<Env>, functions: Vec<Rc<Function>>) -> Self {
66 VM {
67 stack: Vec::with_capacity(256),
68 frames: Vec::with_capacity(64),
69 globals,
70 functions: Rc::new(functions),
71 global_cache: std::array::from_fn(|_| (u32::MAX, u64::MAX, Value::nil())),
72 }
73 }
74
75 fn new_with_rc_functions(globals: Rc<Env>, functions: Rc<Vec<Rc<Function>>>) -> Self {
76 VM {
77 stack: Vec::with_capacity(256),
78 frames: Vec::with_capacity(64),
79 globals,
80 functions,
81 global_cache: std::array::from_fn(|_| (u32::MAX, u64::MAX, Value::nil())),
82 }
83 }
84
85 pub fn execute(&mut self, closure: Rc<Closure>, ctx: &EvalContext) -> Result<Value, SemaError> {
86 let base = self.stack.len();
87 let n_locals = closure.func.chunk.n_locals as usize;
89 for _ in 0..n_locals {
90 self.stack.push(Value::nil());
91 }
92 self.frames.push(CallFrame {
93 closure,
94 pc: 0,
95 base,
96 open_upvalues: None,
97 });
98 self.run(ctx)
99 }
100
101 fn run(&mut self, ctx: &EvalContext) -> Result<Value, SemaError> {
102 macro_rules! read_u16 {
104 ($code:expr, $pc:expr) => {{
105 let v = unsafe { u16::from_le_bytes([*$code.add($pc), *$code.add($pc + 1)]) };
106 $pc += 2;
107 v
108 }};
109 }
110 macro_rules! read_i32 {
111 ($code:expr, $pc:expr) => {{
112 let v = unsafe {
113 i32::from_le_bytes([
114 *$code.add($pc),
115 *$code.add($pc + 1),
116 *$code.add($pc + 2),
117 *$code.add($pc + 3),
118 ])
119 };
120 $pc += 4;
121 v
122 }};
123 }
124 macro_rules! read_u32 {
125 ($code:expr, $pc:expr) => {{
126 let v = unsafe {
127 u32::from_le_bytes([
128 *$code.add($pc),
129 *$code.add($pc + 1),
130 *$code.add($pc + 2),
131 *$code.add($pc + 3),
132 ])
133 };
134 $pc += 4;
135 v
136 }};
137 }
138
139 'dispatch: loop {
142 let fi = self.frames.len() - 1;
143 let frame = &self.frames[fi];
144 let code = frame.closure.func.chunk.code.as_ptr();
145 let consts: *const [Value] = frame.closure.func.chunk.consts.as_slice();
146 let base = frame.base;
147 let mut pc = frame.pc;
148 #[cfg(debug_assertions)]
149 let code_len = frame.closure.func.chunk.code.len();
150 let _ = frame; loop {
153 #[cfg(debug_assertions)]
154 debug_assert!(pc < code_len, "pc {pc} out of bounds (len {code_len})");
155 let op = unsafe { *code.add(pc) };
156 pc += 1;
157
158 match op {
159 0 => {
161 let idx = read_u16!(code, pc) as usize;
162 let val = unsafe { &*consts }[idx].clone();
163 self.stack.push(val);
164 }
165 1 => {
166 self.stack.push(Value::nil());
167 }
168 2 => {
169 self.stack.push(Value::bool(true));
170 }
171 3 => {
172 self.stack.push(Value::bool(false));
173 }
174 4 => {
175 self.stack.pop();
176 }
177 5 => {
178 let val = self.stack[self.stack.len() - 1].clone();
179 self.stack.push(val);
180 }
181
182 6 => {
184 let slot = read_u16!(code, pc) as usize;
185 let val = if let Some(ref open) = self.frames[fi].open_upvalues {
186 if let Some(Some(cell)) = open.get(slot) {
187 cell.value.borrow().clone()
188 } else {
189 self.stack[base + slot].clone()
190 }
191 } else {
192 self.stack[base + slot].clone()
193 };
194 self.stack.push(val);
195 }
196 7 => {
197 let slot = read_u16!(code, pc) as usize;
198 let val = self.stack.pop().unwrap();
199 self.stack[base + slot] = val.clone();
200 if let Some(ref open) = self.frames[fi].open_upvalues {
201 if let Some(Some(cell)) = open.get(slot) {
202 *cell.value.borrow_mut() = val;
203 }
204 }
205 }
206
207 8 => {
209 let idx = read_u16!(code, pc) as usize;
210 let val = self.frames[fi].closure.upvalues[idx].value.borrow().clone();
211 self.stack.push(val);
212 }
213 9 => {
214 let idx = read_u16!(code, pc) as usize;
215 let val = self.stack.pop().unwrap();
216 *self.frames[fi].closure.upvalues[idx].value.borrow_mut() = val;
217 }
218
219 10 => {
221 let bits = read_u32!(code, pc);
222 let version = self.globals.version.get();
223 let slot = (bits as usize) & (GLOBAL_CACHE_SIZE - 1);
224 let entry = &self.global_cache[slot];
225 if entry.0 == bits && entry.1 == version {
226 self.stack.push(entry.2.clone());
227 } else {
228 let spur: Spur = unsafe { std::mem::transmute::<u32, Spur>(bits) };
229 match self.globals.get(spur) {
230 Some(val) => {
231 self.global_cache[slot] = (bits, version, val.clone());
232 self.stack.push(val);
233 }
234 None => {
235 self.frames[fi].pc = pc;
236 let err = SemaError::Unbound(resolve_spur(spur));
237 match self.handle_exception(err, pc - 5)? {
238 ExceptionAction::Handled => continue 'dispatch,
239 ExceptionAction::Propagate(e) => return Err(e),
240 }
241 }
242 }
243 }
244 }
245 11 => {
246 let bits = read_u32!(code, pc);
247 let spur: Spur = unsafe { std::mem::transmute::<u32, Spur>(bits) };
248 let val = self.stack.pop().unwrap();
249 if !self.globals.set_existing(spur, val.clone()) {
250 self.globals.set(spur, val);
251 }
252 }
253 12 => {
254 let bits = read_u32!(code, pc);
255 let spur: Spur = unsafe { std::mem::transmute::<u32, Spur>(bits) };
256 let val = self.stack.pop().unwrap();
257 self.globals.set(spur, val);
258 }
259
260 13 => {
262 let offset = read_i32!(code, pc);
263 pc = (pc as i64 + offset as i64) as usize;
264 }
265 14 => {
266 let offset = read_i32!(code, pc);
267 let val = self.stack.pop().unwrap();
268 if !val.is_truthy() {
269 pc = (pc as i64 + offset as i64) as usize;
270 }
271 }
272 15 => {
273 let offset = read_i32!(code, pc);
274 let val = self.stack.pop().unwrap();
275 if val.is_truthy() {
276 pc = (pc as i64 + offset as i64) as usize;
277 }
278 }
279
280 16 => {
282 let argc = read_u16!(code, pc) as usize;
283 self.frames[fi].pc = pc;
284 let saved_pc = pc - 3;
285 if let Err(err) = self.call_value(argc, ctx) {
286 match self.handle_exception(err, saved_pc)? {
287 ExceptionAction::Handled => {}
288 ExceptionAction::Propagate(e) => return Err(e),
289 }
290 }
291 continue 'dispatch;
292 }
293 17 => {
294 let argc = read_u16!(code, pc) as usize;
295 self.frames[fi].pc = pc;
296 let saved_pc = pc - 3;
297 if let Err(err) = self.tail_call_value(argc, ctx) {
298 match self.handle_exception(err, saved_pc)? {
299 ExceptionAction::Handled => {}
300 ExceptionAction::Propagate(e) => return Err(e),
301 }
302 }
303 continue 'dispatch;
304 }
305 18 => {
306 let result = self.stack.pop().unwrap_or(Value::nil());
307 let frame = self.frames.pop().unwrap();
308 self.stack.truncate(frame.base);
309 if self.frames.is_empty() {
310 return Ok(result);
311 }
312 self.stack.push(result);
313 continue 'dispatch;
314 }
315
316 19 => {
318 self.frames[fi].pc = pc - 1; self.make_closure()?;
320 continue 'dispatch;
321 }
322
323 20 => {
324 let _native_id = read_u16!(code, pc);
325 let _argc = read_u16!(code, pc);
326 self.frames[fi].pc = pc;
327 return Err(SemaError::eval("VM: CallNative not yet implemented"));
328 }
329
330 21 => {
332 let n = read_u16!(code, pc) as usize;
333 let start = self.stack.len() - n;
334 let items: Vec<Value> = self.stack.drain(start..).collect();
335 self.stack.push(Value::list(items));
336 }
337 22 => {
338 let n = read_u16!(code, pc) as usize;
339 let start = self.stack.len() - n;
340 let items: Vec<Value> = self.stack.drain(start..).collect();
341 self.stack.push(Value::vector(items));
342 }
343 23 => {
344 let n = read_u16!(code, pc) as usize;
345 let start = self.stack.len() - n * 2;
346 let items: Vec<Value> = self.stack.drain(start..).collect();
347 let mut map = BTreeMap::new();
348 for pair in items.chunks(2) {
349 map.insert(pair[0].clone(), pair[1].clone());
350 }
351 self.stack.push(Value::map(map));
352 }
353 24 => {
354 let n = read_u16!(code, pc) as usize;
355 let start = self.stack.len() - n * 2;
356 let items: Vec<Value> = self.stack.drain(start..).collect();
357 let mut map = hashbrown::HashMap::new();
358 for pair in items.chunks(2) {
359 map.insert(pair[0].clone(), pair[1].clone());
360 }
361 self.stack.push(Value::hashmap_from_rc(Rc::new(map)));
362 }
363
364 25 => {
366 self.frames[fi].pc = pc;
367 let val = self.stack.pop().unwrap();
368 let err = SemaError::UserException(val);
369 match self.handle_exception(err, pc - 1)? {
370 ExceptionAction::Handled => continue 'dispatch,
371 ExceptionAction::Propagate(e) => return Err(e),
372 }
373 }
374
375 26 => {
377 let b = self.stack.pop().unwrap();
378 let a = self.stack.pop().unwrap();
379 match vm_add(&a, &b) {
380 Ok(v) => self.stack.push(v),
381 Err(err) => {
382 self.frames[fi].pc = pc;
383 match self.handle_exception(err, pc - 1)? {
384 ExceptionAction::Handled => continue 'dispatch,
385 ExceptionAction::Propagate(e) => return Err(e),
386 }
387 }
388 }
389 }
390 27 => {
391 let b = self.stack.pop().unwrap();
392 let a = self.stack.pop().unwrap();
393 match vm_sub(&a, &b) {
394 Ok(v) => self.stack.push(v),
395 Err(err) => {
396 self.frames[fi].pc = pc;
397 match self.handle_exception(err, pc - 1)? {
398 ExceptionAction::Handled => continue 'dispatch,
399 ExceptionAction::Propagate(e) => return Err(e),
400 }
401 }
402 }
403 }
404 28 => {
405 let b = self.stack.pop().unwrap();
406 let a = self.stack.pop().unwrap();
407 match vm_mul(&a, &b) {
408 Ok(v) => self.stack.push(v),
409 Err(err) => {
410 self.frames[fi].pc = pc;
411 match self.handle_exception(err, pc - 1)? {
412 ExceptionAction::Handled => continue 'dispatch,
413 ExceptionAction::Propagate(e) => return Err(e),
414 }
415 }
416 }
417 }
418 29 => {
419 let b = self.stack.pop().unwrap();
420 let a = self.stack.pop().unwrap();
421 match vm_div(&a, &b) {
422 Ok(v) => self.stack.push(v),
423 Err(err) => {
424 self.frames[fi].pc = pc;
425 match self.handle_exception(err, pc - 1)? {
426 ExceptionAction::Handled => continue 'dispatch,
427 ExceptionAction::Propagate(e) => return Err(e),
428 }
429 }
430 }
431 }
432 30 => {
433 let a = self.stack.pop().unwrap();
434 if let Some(n) = a.as_int() {
435 self.stack.push(Value::int(-n));
436 } else if let Some(f) = a.as_float() {
437 self.stack.push(Value::float(-f));
438 } else {
439 self.frames[fi].pc = pc;
440 let err = SemaError::type_error("number", a.type_name());
441 match self.handle_exception(err, pc - 1)? {
442 ExceptionAction::Handled => continue 'dispatch,
443 ExceptionAction::Propagate(e) => return Err(e),
444 }
445 }
446 }
447 31 => {
448 let a = self.stack.pop().unwrap();
449 self.stack.push(Value::bool(!a.is_truthy()));
450 }
451 32 => {
452 let b = self.stack.pop().unwrap();
453 let a = self.stack.pop().unwrap();
454 self.stack.push(Value::bool(a == b));
455 }
456 33 => {
457 let b = self.stack.pop().unwrap();
458 let a = self.stack.pop().unwrap();
459 match vm_lt(&a, &b) {
460 Ok(v) => self.stack.push(Value::bool(v)),
461 Err(err) => {
462 self.frames[fi].pc = pc;
463 match self.handle_exception(err, pc - 1)? {
464 ExceptionAction::Handled => continue 'dispatch,
465 ExceptionAction::Propagate(e) => return Err(e),
466 }
467 }
468 }
469 }
470 34 => {
471 let b = self.stack.pop().unwrap();
472 let a = self.stack.pop().unwrap();
473 match vm_lt(&b, &a) {
474 Ok(v) => self.stack.push(Value::bool(v)),
475 Err(err) => {
476 self.frames[fi].pc = pc;
477 match self.handle_exception(err, pc - 1)? {
478 ExceptionAction::Handled => continue 'dispatch,
479 ExceptionAction::Propagate(e) => return Err(e),
480 }
481 }
482 }
483 }
484 35 => {
485 let b = self.stack.pop().unwrap();
486 let a = self.stack.pop().unwrap();
487 match vm_lt(&b, &a) {
488 Ok(v) => self.stack.push(Value::bool(!v)),
489 Err(err) => {
490 self.frames[fi].pc = pc;
491 match self.handle_exception(err, pc - 1)? {
492 ExceptionAction::Handled => continue 'dispatch,
493 ExceptionAction::Propagate(e) => return Err(e),
494 }
495 }
496 }
497 }
498 36 => {
499 let b = self.stack.pop().unwrap();
500 let a = self.stack.pop().unwrap();
501 match vm_lt(&a, &b) {
502 Ok(v) => self.stack.push(Value::bool(!v)),
503 Err(err) => {
504 self.frames[fi].pc = pc;
505 match self.handle_exception(err, pc - 1)? {
506 ExceptionAction::Handled => continue 'dispatch,
507 ExceptionAction::Propagate(e) => return Err(e),
508 }
509 }
510 }
511 }
512
513 37 => {
518 let len = self.stack.len();
519 let a_bits = unsafe { (*self.stack.as_ptr().add(len - 2)).raw_bits() };
520 let b_bits = unsafe { (*self.stack.as_ptr().add(len - 1)).raw_bits() };
521 if (a_bits & NAN_TAG_MASK) == NAN_INT_SMALL_PATTERN
522 && (b_bits & NAN_TAG_MASK) == NAN_INT_SMALL_PATTERN
523 {
524 let sum = (a_bits.wrapping_add(b_bits)) & NAN_PAYLOAD_MASK;
525 let result = NAN_INT_SMALL_PATTERN | sum;
526 unsafe {
527 std::ptr::write(
528 self.stack.as_mut_ptr().add(len - 2),
529 Value::from_raw_bits(result),
530 );
531 self.stack.set_len(len - 1);
532 }
533 } else {
534 let b = self.stack.pop().unwrap();
535 let a = self.stack.pop().unwrap();
536 match vm_add(&a, &b) {
537 Ok(v) => self.stack.push(v),
538 Err(err) => {
539 self.frames[fi].pc = pc;
540 match self.handle_exception(err, pc - 1)? {
541 ExceptionAction::Handled => continue 'dispatch,
542 ExceptionAction::Propagate(e) => return Err(e),
543 }
544 }
545 }
546 }
547 }
548 38 => {
549 let len = self.stack.len();
550 let a_bits = unsafe { (*self.stack.as_ptr().add(len - 2)).raw_bits() };
551 let b_bits = unsafe { (*self.stack.as_ptr().add(len - 1)).raw_bits() };
552 if (a_bits & NAN_TAG_MASK) == NAN_INT_SMALL_PATTERN
553 && (b_bits & NAN_TAG_MASK) == NAN_INT_SMALL_PATTERN
554 {
555 let diff = (a_bits.wrapping_sub(b_bits)) & NAN_PAYLOAD_MASK;
556 let result = NAN_INT_SMALL_PATTERN | diff;
557 unsafe {
558 std::ptr::write(
559 self.stack.as_mut_ptr().add(len - 2),
560 Value::from_raw_bits(result),
561 );
562 self.stack.set_len(len - 1);
563 }
564 } else {
565 let b = self.stack.pop().unwrap();
566 let a = self.stack.pop().unwrap();
567 match vm_sub(&a, &b) {
568 Ok(v) => self.stack.push(v),
569 Err(err) => {
570 self.frames[fi].pc = pc;
571 match self.handle_exception(err, pc - 1)? {
572 ExceptionAction::Handled => continue 'dispatch,
573 ExceptionAction::Propagate(e) => return Err(e),
574 }
575 }
576 }
577 }
578 }
579 39 => {
580 let len = self.stack.len();
581 let a_bits = unsafe { (*self.stack.as_ptr().add(len - 2)).raw_bits() };
582 let b_bits = unsafe { (*self.stack.as_ptr().add(len - 1)).raw_bits() };
583 if (a_bits & NAN_TAG_MASK) == NAN_INT_SMALL_PATTERN
584 && (b_bits & NAN_TAG_MASK) == NAN_INT_SMALL_PATTERN
585 {
586 let a_payload = a_bits & NAN_PAYLOAD_MASK;
588 let b_payload = b_bits & NAN_PAYLOAD_MASK;
589 let ax = if a_payload & NAN_INT_SIGN_BIT != 0 {
590 (a_payload | !NAN_PAYLOAD_MASK) as i64
591 } else {
592 a_payload as i64
593 };
594 let bx = if b_payload & NAN_INT_SIGN_BIT != 0 {
595 (b_payload | !NAN_PAYLOAD_MASK) as i64
596 } else {
597 b_payload as i64
598 };
599 unsafe {
601 std::ptr::write(
602 self.stack.as_mut_ptr().add(len - 2),
603 Value::int(ax.wrapping_mul(bx)),
604 );
605 self.stack.set_len(len - 1);
606 }
607 } else {
608 let b = self.stack.pop().unwrap();
609 let a = self.stack.pop().unwrap();
610 match vm_mul(&a, &b) {
611 Ok(v) => self.stack.push(v),
612 Err(err) => {
613 self.frames[fi].pc = pc;
614 match self.handle_exception(err, pc - 1)? {
615 ExceptionAction::Handled => continue 'dispatch,
616 ExceptionAction::Propagate(e) => return Err(e),
617 }
618 }
619 }
620 }
621 }
622 40 => {
623 let len = self.stack.len();
624 let a_bits = unsafe { (*self.stack.as_ptr().add(len - 2)).raw_bits() };
625 let b_bits = unsafe { (*self.stack.as_ptr().add(len - 1)).raw_bits() };
626 if (a_bits & NAN_TAG_MASK) == NAN_INT_SMALL_PATTERN
627 && (b_bits & NAN_TAG_MASK) == NAN_INT_SMALL_PATTERN
628 {
629 let a_payload = a_bits & NAN_PAYLOAD_MASK;
631 let b_payload = b_bits & NAN_PAYLOAD_MASK;
632 let ax = if a_payload & NAN_INT_SIGN_BIT != 0 {
633 (a_payload | !NAN_PAYLOAD_MASK) as i64
634 } else {
635 a_payload as i64
636 };
637 let bx = if b_payload & NAN_INT_SIGN_BIT != 0 {
638 (b_payload | !NAN_PAYLOAD_MASK) as i64
639 } else {
640 b_payload as i64
641 };
642 unsafe {
643 std::ptr::write(
644 self.stack.as_mut_ptr().add(len - 2),
645 Value::bool(ax < bx),
646 );
647 self.stack.set_len(len - 1);
648 }
649 } else {
650 let b = self.stack.pop().unwrap();
651 let a = self.stack.pop().unwrap();
652 match vm_lt(&a, &b) {
653 Ok(v) => self.stack.push(Value::bool(v)),
654 Err(err) => {
655 self.frames[fi].pc = pc;
656 match self.handle_exception(err, pc - 1)? {
657 ExceptionAction::Handled => continue 'dispatch,
658 ExceptionAction::Propagate(e) => return Err(e),
659 }
660 }
661 }
662 }
663 }
664 41 => {
665 let len = self.stack.len();
666 let a_bits = unsafe { (*self.stack.as_ptr().add(len - 2)).raw_bits() };
667 let b_bits = unsafe { (*self.stack.as_ptr().add(len - 1)).raw_bits() };
668 if (a_bits & NAN_TAG_MASK) == NAN_INT_SMALL_PATTERN
669 && (b_bits & NAN_TAG_MASK) == NAN_INT_SMALL_PATTERN
670 {
671 unsafe {
673 std::ptr::write(
674 self.stack.as_mut_ptr().add(len - 2),
675 Value::bool(a_bits == b_bits),
676 );
677 self.stack.set_len(len - 1);
678 }
679 } else {
680 let b = self.stack.pop().unwrap();
681 let a = self.stack.pop().unwrap();
682 self.stack.push(Value::bool(a == b));
683 }
684 }
685
686 42 => {
687 let val = if let Some(ref open) = self.frames[fi].open_upvalues {
688 if let Some(Some(cell)) = open.first() {
689 cell.value.borrow().clone()
690 } else {
691 self.stack[base].clone()
692 }
693 } else {
694 self.stack[base].clone()
695 };
696 self.stack.push(val);
697 }
698 43 => {
699 let val = if let Some(ref open) = self.frames[fi].open_upvalues {
700 if let Some(Some(cell)) = open.get(1) {
701 cell.value.borrow().clone()
702 } else {
703 self.stack[base + 1].clone()
704 }
705 } else {
706 self.stack[base + 1].clone()
707 };
708 self.stack.push(val);
709 }
710 44 => {
711 let val = if let Some(ref open) = self.frames[fi].open_upvalues {
712 if let Some(Some(cell)) = open.get(2) {
713 cell.value.borrow().clone()
714 } else {
715 self.stack[base + 2].clone()
716 }
717 } else {
718 self.stack[base + 2].clone()
719 };
720 self.stack.push(val);
721 }
722 45 => {
723 let val = if let Some(ref open) = self.frames[fi].open_upvalues {
724 if let Some(Some(cell)) = open.get(3) {
725 cell.value.borrow().clone()
726 } else {
727 self.stack[base + 3].clone()
728 }
729 } else {
730 self.stack[base + 3].clone()
731 };
732 self.stack.push(val);
733 }
734
735 _ => {
736 return Err(SemaError::eval(format!("VM: invalid opcode {}", op)));
737 }
738 }
739 }
740 }
741 }
742
743 fn call_value(&mut self, argc: usize, ctx: &EvalContext) -> Result<(), SemaError> {
746 let func_idx = self.stack.len() - 1 - argc;
747
748 if self.stack[func_idx].raw_tag() == Some(15) {
751 let vm_closure_data = {
754 let native = self.stack[func_idx].as_native_fn_ref().unwrap();
755 native.payload.as_ref().and_then(|p| {
756 p.downcast_ref::<VmClosurePayload>()
757 .map(|vmc| (vmc.closure.clone(), vmc.functions.clone()))
758 })
759 };
760 if let Some((closure, functions)) = vm_closure_data {
761 self.functions = functions;
762 return self.call_vm_closure_from_rc(&closure, argc);
763 }
764 let func_rc = self.stack[func_idx].as_native_fn_rc().unwrap();
766 let args_start = func_idx + 1;
767 let args: Vec<Value> = self.stack[args_start..].to_vec();
768 self.stack.truncate(func_idx);
769 let result = (func_rc.func)(ctx, &args)?;
770 self.stack.push(result);
771 Ok(())
772 } else if let Some(kw) = self.stack[func_idx].as_keyword_spur() {
773 if argc != 1 {
775 return Err(SemaError::arity(resolve_spur(kw), "1", argc));
776 }
777 let arg = self.stack.pop().unwrap();
778 self.stack.pop(); let kw_val = Value::keyword_from_spur(kw);
780 let result = if let Some(m) = arg.as_map_rc() {
781 m.get(&kw_val).cloned().unwrap_or(Value::nil())
782 } else if let Some(m) = arg.as_hashmap_rc() {
783 m.get(&kw_val).cloned().unwrap_or(Value::nil())
784 } else {
785 return Err(SemaError::type_error("map or hashmap", arg.type_name()));
786 };
787 self.stack.push(result);
788 Ok(())
789 } else {
790 let func_val = self.stack[func_idx].clone();
792 let args_start = func_idx + 1;
793 let args: Vec<Value> = self.stack[args_start..].to_vec();
794 self.stack.truncate(func_idx);
795 let result = sema_core::call_callback(ctx, &func_val, &args)?;
796 self.stack.push(result);
797 Ok(())
798 }
799 }
800
801 fn tail_call_value(&mut self, argc: usize, ctx: &EvalContext) -> Result<(), SemaError> {
802 let func_idx = self.stack.len() - 1 - argc;
803
804 if self.stack[func_idx].raw_tag() == Some(15) {
806 let vm_closure_data = {
807 let native = self.stack[func_idx].as_native_fn_ref().unwrap();
808 native.payload.as_ref().and_then(|p| {
809 p.downcast_ref::<VmClosurePayload>()
810 .map(|vmc| (vmc.closure.clone(), vmc.functions.clone()))
811 })
812 };
813 if let Some((closure, functions)) = vm_closure_data {
814 self.functions = functions;
815 return self.tail_call_vm_closure_from_rc(&closure, argc);
816 }
817 }
818
819 self.call_value(argc, ctx)
821 }
822
823 fn call_vm_closure_from_rc(
826 &mut self,
827 closure: &Rc<Closure>,
828 argc: usize,
829 ) -> Result<(), SemaError> {
830 let closure = closure.clone();
831 let func = &closure.func;
832 let arity = func.arity as usize;
833 let has_rest = func.has_rest;
834 let n_locals = func.chunk.n_locals as usize;
835
836 if has_rest {
838 if argc < arity {
839 return Err(SemaError::arity(
840 func.name
841 .map(resolve_spur)
842 .unwrap_or_else(|| "<lambda>".to_string()),
843 format!("{}+", arity),
844 argc,
845 ));
846 }
847 } else if argc != arity {
848 return Err(SemaError::arity(
849 func.name
850 .map(resolve_spur)
851 .unwrap_or_else(|| "<lambda>".to_string()),
852 arity.to_string(),
853 argc,
854 ));
855 }
856
857 let func_idx = self.stack.len() - 1 - argc;
859 let base = func_idx; if has_rest {
863 let rest: Vec<Value> = self.stack[func_idx + 1 + arity..func_idx + 1 + argc].to_vec();
864 for i in 0..arity {
865 self.stack[base + i] = self.stack[func_idx + 1 + i].clone();
866 }
867 self.stack[base + arity] = Value::list(rest);
868 } else {
869 for i in 0..arity {
870 self.stack[base + i] = self.stack[func_idx + 1 + i].clone();
871 }
872 }
873
874 self.stack.resize(base + n_locals, Value::nil());
876
877 self.frames.push(CallFrame {
879 closure,
880 pc: 0,
881 base,
882 open_upvalues: None,
883 });
884
885 Ok(())
886 }
887
888 fn tail_call_vm_closure_from_rc(
891 &mut self,
892 closure: &Rc<Closure>,
893 argc: usize,
894 ) -> Result<(), SemaError> {
895 let closure = closure.clone();
896 let func = &closure.func;
897 let arity = func.arity as usize;
898 let has_rest = func.has_rest;
899 let n_locals = func.chunk.n_locals as usize;
900
901 if has_rest {
903 if argc < arity {
904 return Err(SemaError::arity(
905 func.name
906 .map(resolve_spur)
907 .unwrap_or_else(|| "<lambda>".to_string()),
908 format!("{}+", arity),
909 argc,
910 ));
911 }
912 } else if argc != arity {
913 return Err(SemaError::arity(
914 func.name
915 .map(resolve_spur)
916 .unwrap_or_else(|| "<lambda>".to_string()),
917 arity.to_string(),
918 argc,
919 ));
920 }
921
922 let func_idx = self.stack.len() - 1 - argc;
924 let base = self.frames.last().unwrap().base;
925
926 if has_rest {
928 let rest: Vec<Value> = self.stack[func_idx + 1 + arity..func_idx + 1 + argc].to_vec();
929 for i in 0..arity {
930 self.stack[base + i] = self.stack[func_idx + 1 + i].clone();
931 }
932 self.stack[base + arity] = Value::list(rest);
933 } else {
934 for i in 0..arity {
935 self.stack[base + i] = self.stack[func_idx + 1 + i].clone();
936 }
937 }
938
939 self.stack.resize(base + n_locals, Value::nil());
941
942 let frame = self.frames.last_mut().unwrap();
944 frame.closure = closure;
945 frame.pc = 0;
946 frame.open_upvalues = None;
948
949 Ok(())
950 }
951
952 fn make_closure(&mut self) -> Result<(), SemaError> {
955 let frame = self.frames.last().unwrap();
958 let code = &frame.closure.func.chunk.code;
959 let pc = frame.pc + 1;
960 let func_id = u16::from_le_bytes([code[pc], code[pc + 1]]) as usize;
961 let n_upvalues = u16::from_le_bytes([code[pc + 2], code[pc + 3]]) as usize;
962
963 let mut uv_descs = Vec::with_capacity(n_upvalues);
965 let mut uv_pc = pc + 4;
966 for _ in 0..n_upvalues {
967 let is_local = u16::from_le_bytes([code[uv_pc], code[uv_pc + 1]]);
968 let idx = u16::from_le_bytes([code[uv_pc + 2], code[uv_pc + 3]]) as usize;
969 uv_pc += 4;
970 uv_descs.push((is_local != 0, idx));
971 }
972
973 let base = frame.base;
974 let parent_upvalues = frame.closure.upvalues.clone();
975 let _ = frame;
977
978 let func = self.functions[func_id].clone();
979 let mut upvalues = Vec::with_capacity(n_upvalues);
980
981 for (is_local, idx) in &uv_descs {
982 if *is_local {
983 let frame = self.frames.last_mut().unwrap();
986 let n_locals = frame.closure.func.chunk.n_locals as usize;
987 let open = frame
988 .open_upvalues
989 .get_or_insert_with(|| vec![None; n_locals]);
990 let cell = if let Some(existing) = &open[*idx] {
991 existing.clone()
992 } else {
993 let val = self.stack[base + *idx].clone();
994 let cell = Rc::new(UpvalueCell::new(val));
995 open[*idx] = Some(cell.clone());
996 cell
997 };
998 upvalues.push(cell);
999 } else {
1000 upvalues.push(parent_upvalues[*idx].clone());
1002 }
1003 }
1004
1005 self.frames.last_mut().unwrap().pc = uv_pc;
1007
1008 let closure = Rc::new(Closure { func, upvalues });
1009 let payload: Rc<dyn std::any::Any> = Rc::new(VmClosurePayload {
1010 closure: closure.clone(),
1011 functions: self.functions.clone(),
1012 });
1013 let closure_for_fallback = closure.clone();
1014 let functions = self.functions.clone();
1015 let globals = self.globals.clone();
1016
1017 let native = Value::native_fn_from_rc(Rc::new(sema_core::NativeFn::with_payload(
1021 closure_for_fallback
1022 .func
1023 .name
1024 .map(resolve_spur)
1025 .unwrap_or_else(|| "<vm-closure>".to_string()),
1026 payload,
1027 move |ctx, args| {
1028 let mut vm = VM::new_with_rc_functions(globals.clone(), functions.clone());
1029 let func = &closure_for_fallback.func;
1030 let arity = func.arity as usize;
1031 let has_rest = func.has_rest;
1032 let n_locals = func.chunk.n_locals as usize;
1033
1034 if has_rest {
1035 if args.len() < arity {
1036 return Err(SemaError::arity(
1037 func.name
1038 .map(resolve_spur)
1039 .unwrap_or_else(|| "<lambda>".to_string()),
1040 format!("{}+", arity),
1041 args.len(),
1042 ));
1043 }
1044 } else if args.len() != arity {
1045 return Err(SemaError::arity(
1046 func.name
1047 .map(resolve_spur)
1048 .unwrap_or_else(|| "<lambda>".to_string()),
1049 arity.to_string(),
1050 args.len(),
1051 ));
1052 }
1053
1054 for _ in 0..n_locals {
1055 vm.stack.push(Value::nil());
1056 }
1057
1058 if has_rest {
1059 for i in 0..arity {
1060 vm.stack[i] = args.get(i).cloned().unwrap_or(Value::nil());
1061 }
1062 let rest: Vec<Value> = args[arity..].to_vec();
1063 vm.stack[arity] = Value::list(rest);
1064 } else {
1065 for i in 0..arity {
1066 vm.stack[i] = args.get(i).cloned().unwrap_or(Value::nil());
1067 }
1068 }
1069
1070 vm.frames.push(CallFrame {
1071 closure: closure_for_fallback.clone(),
1072 pc: 0,
1073 base: 0,
1074 open_upvalues: None,
1075 });
1076 vm.run(ctx)
1077 },
1078 )));
1079
1080 self.stack.push(native);
1081 Ok(())
1082 }
1083
1084 fn handle_exception(
1087 &mut self,
1088 err: SemaError,
1089 failing_pc: usize,
1090 ) -> Result<ExceptionAction, SemaError> {
1091 let mut pc_for_lookup = failing_pc as u32;
1092 while let Some(frame) = self.frames.last() {
1094 let chunk = &frame.closure.func.chunk;
1095
1096 let mut found = None;
1098 for entry in &chunk.exception_table {
1099 if pc_for_lookup >= entry.try_start && pc_for_lookup < entry.try_end {
1100 found = Some(entry.clone());
1101 break;
1102 }
1103 }
1104
1105 if let Some(entry) = found {
1106 let base = frame.base;
1108 self.stack.truncate(base + entry.stack_depth as usize);
1109
1110 let error_val = error_to_value(&err);
1112 self.stack.push(error_val);
1113
1114 let frame = self.frames.last_mut().unwrap();
1116 frame.pc = entry.handler_pc as usize;
1117 return Ok(ExceptionAction::Handled);
1118 }
1119
1120 let frame = self.frames.pop().unwrap();
1122 self.stack.truncate(frame.base);
1123 if let Some(parent) = self.frames.last() {
1125 pc_for_lookup = parent.pc as u32;
1126 }
1127 }
1128
1129 Ok(ExceptionAction::Propagate(err))
1131 }
1132}
1133
1134enum ExceptionAction {
1135 Handled,
1136 Propagate(SemaError),
1137}
1138
1139fn error_to_value(err: &SemaError) -> Value {
1141 let inner = err.inner();
1142 let mut map = BTreeMap::new();
1143 match inner {
1144 SemaError::Eval(msg) => {
1145 map.insert(Value::keyword("type"), Value::keyword("eval"));
1146 map.insert(Value::keyword("message"), Value::string(msg));
1147 }
1148 SemaError::Type { expected, got } => {
1149 map.insert(Value::keyword("type"), Value::keyword("type-error"));
1150 map.insert(
1151 Value::keyword("message"),
1152 Value::string(&format!("expected {expected}, got {got}")),
1153 );
1154 map.insert(Value::keyword("expected"), Value::string(expected));
1155 map.insert(Value::keyword("got"), Value::string(got));
1156 }
1157 SemaError::Arity {
1158 name,
1159 expected,
1160 got,
1161 } => {
1162 map.insert(Value::keyword("type"), Value::keyword("arity"));
1163 map.insert(
1164 Value::keyword("message"),
1165 Value::string(&format!("{name} expects {expected} args, got {got}")),
1166 );
1167 }
1168 SemaError::Unbound(name) => {
1169 map.insert(Value::keyword("type"), Value::keyword("unbound"));
1170 map.insert(
1171 Value::keyword("message"),
1172 Value::string(&format!("Unbound variable: {name}")),
1173 );
1174 map.insert(Value::keyword("name"), Value::string(name));
1175 }
1176 SemaError::UserException(val) => {
1177 map.insert(Value::keyword("type"), Value::keyword("user"));
1178 map.insert(Value::keyword("message"), Value::string(&val.to_string()));
1179 map.insert(Value::keyword("value"), val.clone());
1180 }
1181 SemaError::Io(msg) => {
1182 map.insert(Value::keyword("type"), Value::keyword("io"));
1183 map.insert(Value::keyword("message"), Value::string(msg));
1184 }
1185 SemaError::Llm(msg) => {
1186 map.insert(Value::keyword("type"), Value::keyword("llm"));
1187 map.insert(Value::keyword("message"), Value::string(msg));
1188 }
1189 SemaError::Reader { message, span } => {
1190 map.insert(Value::keyword("type"), Value::keyword("reader"));
1191 map.insert(
1192 Value::keyword("message"),
1193 Value::string(&format!("{message} at {span}")),
1194 );
1195 }
1196 SemaError::PermissionDenied {
1197 function,
1198 capability,
1199 } => {
1200 map.insert(Value::keyword("type"), Value::keyword("permission-denied"));
1201 map.insert(
1202 Value::keyword("message"),
1203 Value::string(&format!(
1204 "Permission denied: {function} requires '{capability}' capability"
1205 )),
1206 );
1207 map.insert(Value::keyword("function"), Value::string(function));
1208 map.insert(Value::keyword("capability"), Value::string(capability));
1209 }
1210 SemaError::WithTrace { .. } | SemaError::WithContext { .. } => {
1211 unreachable!("inner() already unwraps these")
1212 }
1213 }
1214 Value::map(map)
1215}
1216
1217#[inline(always)]
1220fn vm_add(a: &Value, b: &Value) -> Result<Value, SemaError> {
1221 use sema_core::ValueView;
1222 match (a.view(), b.view()) {
1223 (ValueView::Int(x), ValueView::Int(y)) => Ok(Value::int(x.wrapping_add(y))),
1224 (ValueView::Float(x), ValueView::Float(y)) => Ok(Value::float(x + y)),
1225 (ValueView::Int(x), ValueView::Float(y)) => Ok(Value::float(x as f64 + y)),
1226 (ValueView::Float(x), ValueView::Int(y)) => Ok(Value::float(x + y as f64)),
1227 (ValueView::String(x), ValueView::String(y)) => {
1228 let mut s = (*x).clone();
1229 s.push_str(&y);
1230 Ok(Value::string(&s))
1231 }
1232 _ => Err(SemaError::type_error(
1233 "number or string",
1234 format!("{} and {}", a.type_name(), b.type_name()),
1235 )),
1236 }
1237}
1238
1239#[inline(always)]
1240fn vm_sub(a: &Value, b: &Value) -> Result<Value, SemaError> {
1241 use sema_core::ValueView;
1242 match (a.view(), b.view()) {
1243 (ValueView::Int(x), ValueView::Int(y)) => Ok(Value::int(x.wrapping_sub(y))),
1244 (ValueView::Float(x), ValueView::Float(y)) => Ok(Value::float(x - y)),
1245 (ValueView::Int(x), ValueView::Float(y)) => Ok(Value::float(x as f64 - y)),
1246 (ValueView::Float(x), ValueView::Int(y)) => Ok(Value::float(x - y as f64)),
1247 _ => Err(SemaError::type_error(
1248 "number",
1249 format!("{} and {}", a.type_name(), b.type_name()),
1250 )),
1251 }
1252}
1253
1254#[inline(always)]
1255fn vm_mul(a: &Value, b: &Value) -> Result<Value, SemaError> {
1256 use sema_core::ValueView;
1257 match (a.view(), b.view()) {
1258 (ValueView::Int(x), ValueView::Int(y)) => Ok(Value::int(x.wrapping_mul(y))),
1259 (ValueView::Float(x), ValueView::Float(y)) => Ok(Value::float(x * y)),
1260 (ValueView::Int(x), ValueView::Float(y)) => Ok(Value::float(x as f64 * y)),
1261 (ValueView::Float(x), ValueView::Int(y)) => Ok(Value::float(x * y as f64)),
1262 _ => Err(SemaError::type_error(
1263 "number",
1264 format!("{} and {}", a.type_name(), b.type_name()),
1265 )),
1266 }
1267}
1268
1269#[inline(always)]
1270fn vm_div(a: &Value, b: &Value) -> Result<Value, SemaError> {
1271 use sema_core::ValueView;
1272 match (a.view(), b.view()) {
1273 (ValueView::Int(_), ValueView::Int(0)) => Err(SemaError::eval("division by zero")),
1274 (ValueView::Int(x), ValueView::Int(y)) => Ok(Value::int(x / y)),
1275 (ValueView::Float(x), ValueView::Float(y)) => Ok(Value::float(x / y)),
1276 (ValueView::Int(x), ValueView::Float(y)) => Ok(Value::float(x as f64 / y)),
1277 (ValueView::Float(x), ValueView::Int(y)) => Ok(Value::float(x / y as f64)),
1278 _ => Err(SemaError::type_error(
1279 "number",
1280 format!("{} and {}", a.type_name(), b.type_name()),
1281 )),
1282 }
1283}
1284
1285#[inline(always)]
1286fn vm_lt(a: &Value, b: &Value) -> Result<bool, SemaError> {
1287 use sema_core::ValueView;
1288 match (a.view(), b.view()) {
1289 (ValueView::Int(x), ValueView::Int(y)) => Ok(x < y),
1290 (ValueView::Float(x), ValueView::Float(y)) => Ok(x < y),
1291 (ValueView::Int(x), ValueView::Float(y)) => Ok((x as f64) < y),
1292 (ValueView::Float(x), ValueView::Int(y)) => Ok(x < (y as f64)),
1293 (ValueView::String(x), ValueView::String(y)) => Ok(x < y),
1294 _ => Err(SemaError::type_error(
1295 "comparable values",
1296 format!("{} and {}", a.type_name(), b.type_name()),
1297 )),
1298 }
1299}
1300
1301pub fn compile_program(vals: &[Value]) -> Result<(Rc<Closure>, Vec<Rc<Function>>), SemaError> {
1304 let mut resolved = Vec::new();
1305 let mut total_locals: u16 = 0;
1306 for val in vals {
1307 let core = crate::lower::lower(val)?;
1308 let (res, n) = crate::resolve::resolve_with_locals(&core)?;
1309 total_locals = total_locals.max(n);
1310 resolved.push(res);
1311 }
1312 let result = crate::compiler::compile_many_with_locals(&resolved, total_locals)?;
1313
1314 let functions: Vec<Rc<Function>> = result.functions.into_iter().map(Rc::new).collect();
1315 let closure = Rc::new(Closure {
1316 func: Rc::new(Function {
1317 name: None,
1318 chunk: result.chunk,
1319 upvalue_descs: Vec::new(),
1320 arity: 0,
1321 has_rest: false,
1322 local_names: Vec::new(),
1323 }),
1324 upvalues: Vec::new(),
1325 });
1326
1327 Ok((closure, functions))
1328}
1329
1330pub fn eval_str(input: &str, globals: &Rc<Env>, ctx: &EvalContext) -> Result<Value, SemaError> {
1332 let vals =
1333 sema_reader::read_many(input).map_err(|e| SemaError::eval(format!("parse error: {e}")))?;
1334 let (closure, functions) = compile_program(&vals)?;
1335 let mut vm = VM::new(globals.clone(), functions);
1336 vm.execute(closure, ctx)
1337}
1338
1339#[cfg(test)]
1340mod tests {
1341 use super::*;
1342 use sema_core::{intern, NativeFn};
1343
1344 fn make_test_env() -> Rc<Env> {
1345 let env = Rc::new(Env::new());
1346 env.set(
1347 intern("+"),
1348 Value::native_fn(NativeFn::simple("+", |args| vm_add(&args[0], &args[1]))),
1349 );
1350 env.set(
1351 intern("-"),
1352 Value::native_fn(NativeFn::simple("-", |args| vm_sub(&args[0], &args[1]))),
1353 );
1354 env.set(
1355 intern("*"),
1356 Value::native_fn(NativeFn::simple("*", |args| vm_mul(&args[0], &args[1]))),
1357 );
1358 env.set(
1359 intern("/"),
1360 Value::native_fn(NativeFn::simple("/", |args| vm_div(&args[0], &args[1]))),
1361 );
1362 env.set(
1363 intern("="),
1364 Value::native_fn(NativeFn::simple("=", |args| {
1365 Ok(Value::bool(args[0] == args[1]))
1366 })),
1367 );
1368 env.set(
1369 intern("<"),
1370 Value::native_fn(NativeFn::simple("<", |args| {
1371 Ok(Value::bool(vm_lt(&args[0], &args[1])?))
1372 })),
1373 );
1374 env.set(
1375 intern(">"),
1376 Value::native_fn(NativeFn::simple(">", |args| {
1377 Ok(Value::bool(vm_lt(&args[1], &args[0])?))
1378 })),
1379 );
1380 env.set(
1381 intern("not"),
1382 Value::native_fn(NativeFn::simple("not", |args| {
1383 Ok(Value::bool(!args[0].is_truthy()))
1384 })),
1385 );
1386 env.set(
1387 intern("list"),
1388 Value::native_fn(NativeFn::simple("list", |args| {
1389 Ok(Value::list(args.to_vec()))
1390 })),
1391 );
1392 env
1393 }
1394
1395 fn eval(input: &str) -> Result<Value, SemaError> {
1396 let globals = make_test_env();
1397 let ctx = EvalContext::new();
1398 eval_str(input, &globals, &ctx)
1399 }
1400
1401 #[test]
1402 fn test_vm_int_literal() {
1403 assert_eq!(eval("42").unwrap(), Value::int(42));
1404 }
1405
1406 #[test]
1407 fn test_vm_nil() {
1408 assert_eq!(eval("nil").unwrap(), Value::nil());
1409 }
1410
1411 #[test]
1412 fn test_vm_bool() {
1413 assert_eq!(eval("#t").unwrap(), Value::bool(true));
1414 assert_eq!(eval("#f").unwrap(), Value::bool(false));
1415 }
1416
1417 #[test]
1418 fn test_vm_string() {
1419 assert_eq!(eval("\"hello\"").unwrap(), Value::string("hello"));
1420 }
1421
1422 #[test]
1423 fn test_vm_if_true() {
1424 assert_eq!(eval("(if #t 42 99)").unwrap(), Value::int(42));
1425 }
1426
1427 #[test]
1428 fn test_vm_if_false() {
1429 assert_eq!(eval("(if #f 42 99)").unwrap(), Value::int(99));
1430 }
1431
1432 #[test]
1433 fn test_vm_begin() {
1434 assert_eq!(eval("(begin 1 2 3)").unwrap(), Value::int(3));
1435 }
1436
1437 #[test]
1438 fn test_vm_define_and_load() {
1439 let globals = make_test_env();
1440 let ctx = EvalContext::new();
1441 eval_str("(define x 42)", &globals, &ctx).unwrap();
1442 let result = eval_str("x", &globals, &ctx).unwrap();
1443 assert_eq!(result, Value::int(42));
1444 }
1445
1446 #[test]
1447 fn test_vm_let() {
1448 assert_eq!(eval("(let ((x 10)) x)").unwrap(), Value::int(10));
1449 }
1450
1451 #[test]
1452 fn test_vm_let_multiple() {
1453 assert_eq!(eval("(let ((x 10) (y 20)) y)").unwrap(), Value::int(20));
1454 }
1455
1456 #[test]
1457 fn test_vm_nested_if() {
1458 assert_eq!(eval("(if (if #t #f #t) 1 2)").unwrap(), Value::int(2));
1459 }
1460
1461 #[test]
1462 fn test_vm_lambda_call() {
1463 assert_eq!(eval("((lambda (x) x) 42)").unwrap(), Value::int(42));
1464 }
1465
1466 #[test]
1467 fn test_vm_lambda_two_args() {
1468 assert_eq!(eval("((lambda (x y) y) 1 2)").unwrap(), Value::int(2));
1469 }
1470
1471 #[test]
1472 fn test_vm_closure_capture() {
1473 assert_eq!(
1474 eval("(let ((x 10)) ((lambda () x)))").unwrap(),
1475 Value::int(10)
1476 );
1477 }
1478
1479 #[test]
1480 fn test_vm_list_literal() {
1481 let result = eval("(list 1 2 3)").unwrap();
1482 let items = result.as_list().expect("Expected list");
1483 assert_eq!(items.len(), 3);
1484 assert_eq!(items[0], Value::int(1));
1485 }
1486
1487 #[test]
1488 fn test_vm_make_vector() {
1489 let result = eval("[1 2 3]").unwrap();
1490 let items = result.as_vector().expect("Expected vector");
1491 assert_eq!(items.len(), 3);
1492 }
1493
1494 #[test]
1495 fn test_vm_and_short_circuit() {
1496 assert_eq!(eval("(and #f 42)").unwrap(), Value::bool(false));
1497 assert_eq!(eval("(and #t 42)").unwrap(), Value::int(42));
1498 }
1499
1500 #[test]
1501 fn test_vm_or_short_circuit() {
1502 assert_eq!(eval("(or 42 99)").unwrap(), Value::int(42));
1503 assert_eq!(eval("(or #f 99)").unwrap(), Value::int(99));
1504 }
1505
1506 #[test]
1507 fn test_vm_throw_catch() {
1508 let result = eval("(try (throw \"boom\") (catch e (:value e)))").unwrap();
1510 assert_eq!(result, Value::string("boom"));
1511 }
1512
1513 #[test]
1514 fn test_vm_throw_catch_type() {
1515 let result = eval("(try (throw \"boom\") (catch e (:type e)))").unwrap();
1516 assert_eq!(result, Value::keyword("user"));
1517 }
1518
1519 #[test]
1520 fn test_vm_try_no_throw() {
1521 assert_eq!(eval("(try 42 (catch e 99))").unwrap(), Value::int(42));
1522 }
1523
1524 #[test]
1525 fn test_vm_try_catch_native_error() {
1526 let result = eval("(try (/ 1 0) (catch e \"caught\"))").unwrap();
1528 assert_eq!(result, Value::string("caught"));
1529 }
1530
1531 #[test]
1532 fn test_vm_try_catch_native_error_message() {
1533 let result = eval("(try (/ 1 0) (catch e (:message e)))").unwrap();
1534 let s = result.as_str().expect("Expected string");
1535 assert!(s.contains("division by zero"), "got: {s}");
1536 }
1537
1538 #[test]
1539 fn test_vm_try_catch_type_error() {
1540 let result = eval("(try (+ 1 \"a\") (catch e (:type e)))").unwrap();
1541 assert_eq!(result, Value::keyword("type-error"));
1542 }
1543
1544 #[test]
1545 fn test_vm_quote() {
1546 let result = eval("'(a b c)").unwrap();
1547 let items = result.as_list().expect("Expected list");
1548 assert_eq!(items.len(), 3);
1549 }
1550
1551 #[test]
1552 fn test_vm_set() {
1553 let globals = make_test_env();
1554 let ctx = EvalContext::new();
1555 eval_str("(define x 1)", &globals, &ctx).unwrap();
1556 eval_str("(set! x 42)", &globals, &ctx).unwrap();
1557 let result = eval_str("x", &globals, &ctx).unwrap();
1558 assert_eq!(result, Value::int(42));
1559 }
1560
1561 #[test]
1562 fn test_vm_recursive_define() {
1563 let globals = make_test_env();
1564 let ctx = EvalContext::new();
1565 eval_str(
1566 "(define (fact n) (if (= n 0) 1 (* n (fact (- n 1)))))",
1567 &globals,
1568 &ctx,
1569 )
1570 .unwrap();
1571 let result = eval_str("(fact 5)", &globals, &ctx).unwrap();
1572 assert_eq!(result, Value::int(120));
1573 }
1574
1575 #[test]
1576 fn test_vm_do_loop() {
1577 let result = eval("(do ((i 0 (+ i 1))) ((= i 5) i))").unwrap();
1578 assert_eq!(result, Value::int(5));
1579 }
1580
1581 #[test]
1582 fn test_vm_named_let() {
1583 let result =
1584 eval("(let loop ((n 5) (acc 1)) (if (= n 0) acc (loop (- n 1) (* acc n))))").unwrap();
1585 assert_eq!(result, Value::int(120));
1586 }
1587
1588 #[test]
1589 fn test_vm_letrec() {
1590 let globals = make_test_env();
1591 let ctx = EvalContext::new();
1592 let result = eval_str(
1593 "(letrec ((even? (lambda (n) (if (= n 0) #t (odd? (- n 1))))) (odd? (lambda (n) (if (= n 0) #f (even? (- n 1)))))) (even? 4))",
1594 &globals,
1595 &ctx,
1596 ).unwrap();
1597 assert_eq!(result, Value::bool(true));
1598 }
1599
1600 #[test]
1601 fn test_vm_rest_params() {
1602 let result = eval("((lambda (x . rest) rest) 1 2 3)").unwrap();
1603 let items = result.as_list().expect("Expected list");
1604 assert_eq!(items.len(), 2);
1605 assert_eq!(items[0], Value::int(2));
1606 assert_eq!(items[1], Value::int(3));
1607 }
1608
1609 #[test]
1612 fn test_vm_counter_closure() {
1613 let result =
1615 eval("(let ((n 0)) (let ((inc (lambda () (set! n (+ n 1)) n))) (inc) (inc) (inc)))")
1616 .unwrap();
1617 assert_eq!(result, Value::int(3));
1618 }
1619
1620 #[test]
1621 fn test_vm_shared_mutable_upvalue() {
1622 let result = eval(
1624 "(let ((n 0)) (let ((inc (lambda () (set! n (+ n 1)))) (get (lambda () n))) (inc) (inc) (get)))",
1625 )
1626 .unwrap();
1627 assert_eq!(result, Value::int(2));
1628 }
1629
1630 #[test]
1631 fn test_vm_set_local_in_let() {
1632 let result = eval("(let ((x 1)) (set! x 42) x)").unwrap();
1634 assert_eq!(result, Value::int(42));
1635 }
1636
1637 #[test]
1638 fn test_vm_closure_captures_after_mutation() {
1639 let result = eval("(let ((x 1)) (set! x 10) ((lambda () x)))").unwrap();
1641 assert_eq!(result, Value::int(10));
1642 }
1643
1644 #[test]
1645 fn test_vm_closure_returns_closure() {
1646 let result = eval("(let ((f (lambda () (lambda (x) x)))) ((f) 42))").unwrap();
1648 assert_eq!(result, Value::int(42));
1649 }
1650
1651 #[test]
1652 fn test_vm_make_adder() {
1653 let globals = make_test_env();
1655 let ctx = EvalContext::new();
1656 eval_str(
1657 "(define (make-adder n) (lambda (x) (+ n x)))",
1658 &globals,
1659 &ctx,
1660 )
1661 .unwrap();
1662 eval_str("(define add5 (make-adder 5))", &globals, &ctx).unwrap();
1663 let result = eval_str("(add5 3)", &globals, &ctx).unwrap();
1664 assert_eq!(result, Value::int(8));
1665 }
1666
1667 #[test]
1668 fn test_vm_compose() {
1669 let globals = make_test_env();
1671 let ctx = EvalContext::new();
1672 eval_str(
1673 "(define (compose f g) (lambda (x) (f (g x))))",
1674 &globals,
1675 &ctx,
1676 )
1677 .unwrap();
1678 eval_str("(define inc (lambda (x) (+ x 1)))", &globals, &ctx).unwrap();
1679 eval_str("(define dbl (lambda (x) (* x 2)))", &globals, &ctx).unwrap();
1680 let result = eval_str("((compose dbl inc) 5)", &globals, &ctx).unwrap();
1681 assert_eq!(result, Value::int(12));
1682 }
1683
1684 #[test]
1685 fn test_vm_nested_make_closure() {
1686 let result = eval("((((lambda () (lambda () (lambda () 42))))))").unwrap();
1688 assert_eq!(result, Value::int(42));
1689 }
1690
1691 #[test]
1692 fn test_vm_named_fn_rest_params() {
1693 let globals = make_test_env();
1694 let ctx = EvalContext::new();
1695 eval_str("(define (f . args) args)", &globals, &ctx).unwrap();
1696 let result = eval_str("(f 1 2 3)", &globals, &ctx).unwrap();
1697 let items = result.as_list().expect("Expected list");
1698 assert_eq!(items.len(), 3);
1699 assert_eq!(items[0], Value::int(1));
1700 }
1701
1702 #[test]
1703 fn test_vm_named_let_still_works_with_fix() {
1704 let globals = make_test_env();
1705 let ctx = EvalContext::new();
1706 let result = eval_str(
1707 "(let loop ((n 5) (acc 1)) (if (= n 0) acc (loop (- n 1) (* acc n))))",
1708 &globals,
1709 &ctx,
1710 )
1711 .unwrap();
1712 assert_eq!(result, Value::int(120));
1713 }
1714
1715 #[test]
1716 fn test_vm_curry() {
1717 let globals = make_test_env();
1719 let ctx = EvalContext::new();
1720 eval_str(
1721 "(define (curry f) (lambda (x) (lambda (y) (f x y))))",
1722 &globals,
1723 &ctx,
1724 )
1725 .unwrap();
1726 let result = eval_str("(((curry +) 3) 4)", &globals, &ctx).unwrap();
1727 assert_eq!(result, Value::int(7));
1728 }
1729}