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_SMALL_PATTERN,
7 NAN_PAYLOAD_BITS, NAN_PAYLOAD_MASK, NAN_TAG_MASK, TAG_NATIVE_FN,
8};
9
10use crate::chunk::Function;
11use crate::opcodes::op;
12
13#[derive(Debug)]
15pub struct UpvalueCell {
16 pub value: RefCell<Value>,
17}
18
19impl UpvalueCell {
20 pub fn new(value: Value) -> Self {
21 UpvalueCell {
22 value: RefCell::new(value),
23 }
24 }
25}
26
27#[derive(Debug, Clone)]
29pub struct Closure {
30 pub func: Rc<Function>,
31 pub upvalues: Vec<Rc<UpvalueCell>>,
32}
33
34struct VmClosurePayload {
37 closure: Rc<Closure>,
38 functions: Rc<Vec<Rc<Function>>>,
39}
40
41struct CallFrame {
43 closure: Rc<Closure>,
44 pc: usize,
45 base: usize,
46 open_upvalues: Option<Vec<Option<Rc<UpvalueCell>>>>,
50}
51
52const GLOBAL_CACHE_SIZE: usize = 16;
54
55pub struct VM {
57 stack: Vec<Value>,
58 frames: Vec<CallFrame>,
59 globals: Rc<Env>,
60 functions: Rc<Vec<Rc<Function>>>,
61 global_cache: [(u32, u64, Value); GLOBAL_CACHE_SIZE],
63}
64
65impl VM {
66 pub fn new(globals: Rc<Env>, functions: Vec<Rc<Function>>) -> Self {
67 VM {
68 stack: Vec::with_capacity(256),
69 frames: Vec::with_capacity(64),
70 globals,
71 functions: Rc::new(functions),
72 global_cache: std::array::from_fn(|_| (u32::MAX, u64::MAX, Value::nil())),
73 }
74 }
75
76 fn new_with_rc_functions(globals: Rc<Env>, functions: Rc<Vec<Rc<Function>>>) -> Self {
77 VM {
78 stack: Vec::with_capacity(256),
79 frames: Vec::with_capacity(64),
80 globals,
81 functions,
82 global_cache: std::array::from_fn(|_| (u32::MAX, u64::MAX, Value::nil())),
83 }
84 }
85
86 pub fn execute(&mut self, closure: Rc<Closure>, ctx: &EvalContext) -> Result<Value, SemaError> {
87 let base = self.stack.len();
88 let n_locals = closure.func.chunk.n_locals as usize;
90 self.stack.resize(base + n_locals, Value::nil());
91 self.frames.push(CallFrame {
92 closure,
93 pc: 0,
94 base,
95 open_upvalues: None,
96 });
97 self.run(ctx)
98 }
99
100 fn run(&mut self, ctx: &EvalContext) -> Result<Value, SemaError> {
101 macro_rules! read_u16 {
103 ($code:expr, $pc:expr) => {{
104 let v = unsafe { u16::from_le_bytes([*$code.add($pc), *$code.add($pc + 1)]) };
105 $pc += 2;
106 v
107 }};
108 }
109 macro_rules! read_i32 {
110 ($code:expr, $pc:expr) => {{
111 let v = unsafe {
112 i32::from_le_bytes([
113 *$code.add($pc),
114 *$code.add($pc + 1),
115 *$code.add($pc + 2),
116 *$code.add($pc + 3),
117 ])
118 };
119 $pc += 4;
120 v
121 }};
122 }
123 macro_rules! read_u32 {
124 ($code:expr, $pc:expr) => {{
125 let v = unsafe {
126 u32::from_le_bytes([
127 *$code.add($pc),
128 *$code.add($pc + 1),
129 *$code.add($pc + 2),
130 *$code.add($pc + 3),
131 ])
132 };
133 $pc += 4;
134 v
135 }};
136 }
137
138 #[inline(always)]
140 unsafe fn pop_unchecked(stack: &mut Vec<Value>) -> Value {
141 let len = stack.len();
142 debug_assert!(len > 0, "pop_unchecked on empty stack");
143 let v = std::ptr::read(stack.as_ptr().add(len - 1));
144 stack.set_len(len - 1);
145 v
146 }
147
148 const SIGN_SHIFT: u32 = 64 - NAN_PAYLOAD_BITS;
150
151 macro_rules! handle_err {
154 ($self:expr, $fi:expr, $pc:expr, $err:expr, $saved_pc:expr, $label:tt) => {{
155 $self.frames[$fi].pc = $pc;
156 match $self.handle_exception($err, $saved_pc)? {
157 ExceptionAction::Handled => continue $label,
158 ExceptionAction::Propagate(e) => return Err(e),
159 }
160 }};
161 }
162
163 'dispatch: loop {
166 let fi = self.frames.len() - 1;
167 let frame = &self.frames[fi];
168 let code = frame.closure.func.chunk.code.as_ptr();
169 let consts: *const [Value] = frame.closure.func.chunk.consts.as_slice();
170 let base = frame.base;
171 let mut pc = frame.pc;
172 let has_open_upvalues = frame.open_upvalues.is_some();
173 #[cfg(debug_assertions)]
174 let code_len = frame.closure.func.chunk.code.len();
175 let _ = frame; loop {
178 #[cfg(debug_assertions)]
179 debug_assert!(pc < code_len, "pc {pc} out of bounds (len {code_len})");
180 let op = unsafe { *code.add(pc) };
181 pc += 1;
182
183 match op {
184 op::CONST => {
186 let idx = read_u16!(code, pc) as usize;
187 let val = unsafe { (&(*consts)).get_unchecked(idx) }.clone();
188 self.stack.push(val);
189 }
190 op::NIL => {
191 self.stack.push(Value::nil());
192 }
193 op::TRUE => {
194 self.stack.push(Value::bool(true));
195 }
196 op::FALSE => {
197 self.stack.push(Value::bool(false));
198 }
199 op::POP => {
200 unsafe { pop_unchecked(&mut self.stack) };
201 }
202 op::DUP => {
203 let val =
204 unsafe { &*self.stack.as_ptr().add(self.stack.len() - 1) }.clone();
205 self.stack.push(val);
206 }
207
208 op::LOAD_LOCAL => {
210 let slot = read_u16!(code, pc) as usize;
211 let val = if has_open_upvalues {
212 if let Some(ref open) = self.frames[fi].open_upvalues {
213 if let Some(Some(cell)) = open.get(slot) {
214 cell.value.borrow().clone()
215 } else {
216 self.stack[base + slot].clone()
217 }
218 } else {
219 unreachable!()
220 }
221 } else {
222 self.stack[base + slot].clone()
223 };
224 self.stack.push(val);
225 }
226 op::STORE_LOCAL => {
227 let slot = read_u16!(code, pc) as usize;
228 let val = unsafe { pop_unchecked(&mut self.stack) };
229 self.stack[base + slot] = val.clone();
230 if has_open_upvalues {
231 if let Some(ref open) = self.frames[fi].open_upvalues {
232 if let Some(Some(cell)) = open.get(slot) {
233 *cell.value.borrow_mut() = val;
234 }
235 }
236 }
237 }
238
239 op::LOAD_UPVALUE => {
241 let idx = read_u16!(code, pc) as usize;
242 let val = self.frames[fi].closure.upvalues[idx].value.borrow().clone();
243 self.stack.push(val);
244 }
245 op::STORE_UPVALUE => {
246 let idx = read_u16!(code, pc) as usize;
247 let val = unsafe { pop_unchecked(&mut self.stack) };
248 *self.frames[fi].closure.upvalues[idx].value.borrow_mut() = val;
249 }
250
251 op::LOAD_GLOBAL => {
253 let bits = read_u32!(code, pc);
254 let version = self.globals.version.get();
255 let slot = (bits as usize) & (GLOBAL_CACHE_SIZE - 1);
256 let entry = &self.global_cache[slot];
257 if entry.0 == bits && entry.1 == version {
258 self.stack.push(entry.2.clone());
259 } else {
260 let spur: Spur = unsafe { std::mem::transmute::<u32, Spur>(bits) };
261 match self.globals.get(spur) {
262 Some(val) => {
263 self.global_cache[slot] = (bits, version, val.clone());
264 self.stack.push(val);
265 }
266 None => {
267 let err = SemaError::Unbound(resolve_spur(spur));
268 handle_err!(self, fi, pc, err, pc - op::SIZE_OP_U32, 'dispatch);
269 }
270 }
271 }
272 }
273 op::STORE_GLOBAL => {
274 let bits = read_u32!(code, pc);
275 let spur: Spur = unsafe { std::mem::transmute::<u32, Spur>(bits) };
276 let val = unsafe { pop_unchecked(&mut self.stack) };
277 if !self.globals.set_existing(spur, val.clone()) {
278 self.globals.set(spur, val);
279 }
280 }
281 op::DEFINE_GLOBAL => {
282 let bits = read_u32!(code, pc);
283 let spur: Spur = unsafe { std::mem::transmute::<u32, Spur>(bits) };
284 let val = unsafe { pop_unchecked(&mut self.stack) };
285 self.globals.set(spur, val);
286 }
287
288 op::JUMP => {
290 let offset = read_i32!(code, pc);
291 pc = (pc as i64 + offset as i64) as usize;
292 }
293 op::JUMP_IF_FALSE => {
294 let offset = read_i32!(code, pc);
295 let val = unsafe { pop_unchecked(&mut self.stack) };
296 if !val.is_truthy() {
297 pc = (pc as i64 + offset as i64) as usize;
298 }
299 }
300 op::JUMP_IF_TRUE => {
301 let offset = read_i32!(code, pc);
302 let val = unsafe { pop_unchecked(&mut self.stack) };
303 if val.is_truthy() {
304 pc = (pc as i64 + offset as i64) as usize;
305 }
306 }
307
308 op::CALL => {
310 let argc = read_u16!(code, pc) as usize;
311 self.frames[fi].pc = pc;
312 let saved_pc = pc - op::SIZE_OP_U16;
313 if let Err(err) = self.call_value(argc, ctx) {
314 match self.handle_exception(err, saved_pc)? {
315 ExceptionAction::Handled => {}
316 ExceptionAction::Propagate(e) => return Err(e),
317 }
318 }
319 continue 'dispatch;
320 }
321 op::TAIL_CALL => {
322 let argc = read_u16!(code, pc) as usize;
323 self.frames[fi].pc = pc;
324 let saved_pc = pc - op::SIZE_OP_U16;
325 if let Err(err) = self.tail_call_value(argc, ctx) {
326 match self.handle_exception(err, saved_pc)? {
327 ExceptionAction::Handled => {}
328 ExceptionAction::Propagate(e) => return Err(e),
329 }
330 }
331 continue 'dispatch;
332 }
333 op::RETURN => {
334 let result = if !self.stack.is_empty() {
335 unsafe { pop_unchecked(&mut self.stack) }
336 } else {
337 Value::nil()
338 };
339 let frame = self.frames.pop().unwrap();
340 self.stack.truncate(frame.base);
341 if self.frames.is_empty() {
342 return Ok(result);
343 }
344 self.stack.push(result);
345 continue 'dispatch;
346 }
347
348 op::MAKE_CLOSURE => {
350 self.frames[fi].pc = pc - op::SIZE_OP; self.make_closure()?;
352 continue 'dispatch;
353 }
354
355 op::CALL_NATIVE => {
356 let _native_id = read_u16!(code, pc);
357 let _argc = read_u16!(code, pc);
358 self.frames[fi].pc = pc;
359 return Err(SemaError::eval("VM: CallNative not yet implemented"));
360 }
361
362 op::MAKE_LIST => {
364 let n = read_u16!(code, pc) as usize;
365 let start = self.stack.len() - n;
366 let items: Vec<Value> = self.stack.drain(start..).collect();
367 self.stack.push(Value::list(items));
368 }
369 op::MAKE_VECTOR => {
370 let n = read_u16!(code, pc) as usize;
371 let start = self.stack.len() - n;
372 let items: Vec<Value> = self.stack.drain(start..).collect();
373 self.stack.push(Value::vector(items));
374 }
375 op::MAKE_MAP => {
376 let n = read_u16!(code, pc) as usize;
377 let start = self.stack.len() - n * 2;
378 let items: Vec<Value> = self.stack.drain(start..).collect();
379 let mut map = BTreeMap::new();
380 for pair in items.chunks(2) {
381 map.insert(pair[0].clone(), pair[1].clone());
382 }
383 self.stack.push(Value::map(map));
384 }
385 op::MAKE_HASH_MAP => {
386 let n = read_u16!(code, pc) as usize;
387 let start = self.stack.len() - n * 2;
388 let items: Vec<Value> = self.stack.drain(start..).collect();
389 let mut map = hashbrown::HashMap::new();
390 for pair in items.chunks(2) {
391 map.insert(pair[0].clone(), pair[1].clone());
392 }
393 self.stack.push(Value::hashmap_from_rc(Rc::new(map)));
394 }
395
396 op::THROW => {
398 let val = unsafe { pop_unchecked(&mut self.stack) };
399 let err = SemaError::UserException(val);
400 handle_err!(self, fi, pc, err, pc - op::SIZE_OP, 'dispatch);
401 }
402
403 op::ADD => {
405 let b = unsafe { pop_unchecked(&mut self.stack) };
406 let a = unsafe { pop_unchecked(&mut self.stack) };
407 match vm_add(&a, &b) {
408 Ok(v) => self.stack.push(v),
409 Err(err) => handle_err!(self, fi, pc, err, pc - op::SIZE_OP, 'dispatch),
410 }
411 }
412 op::SUB => {
413 let b = unsafe { pop_unchecked(&mut self.stack) };
414 let a = unsafe { pop_unchecked(&mut self.stack) };
415 match vm_sub(&a, &b) {
416 Ok(v) => self.stack.push(v),
417 Err(err) => handle_err!(self, fi, pc, err, pc - op::SIZE_OP, 'dispatch),
418 }
419 }
420 op::MUL => {
421 let b = unsafe { pop_unchecked(&mut self.stack) };
422 let a = unsafe { pop_unchecked(&mut self.stack) };
423 match vm_mul(&a, &b) {
424 Ok(v) => self.stack.push(v),
425 Err(err) => handle_err!(self, fi, pc, err, pc - op::SIZE_OP, 'dispatch),
426 }
427 }
428 op::DIV => {
429 let b = unsafe { pop_unchecked(&mut self.stack) };
430 let a = unsafe { pop_unchecked(&mut self.stack) };
431 match vm_div(&a, &b) {
432 Ok(v) => self.stack.push(v),
433 Err(err) => handle_err!(self, fi, pc, err, pc - op::SIZE_OP, 'dispatch),
434 }
435 }
436 op::NEGATE => {
437 let a = unsafe { pop_unchecked(&mut self.stack) };
438 if let Some(n) = a.as_int() {
439 self.stack.push(Value::int(-n));
440 } else if let Some(f) = a.as_float() {
441 self.stack.push(Value::float(-f));
442 } else {
443 let err = SemaError::type_error("number", a.type_name());
444 handle_err!(self, fi, pc, err, pc - op::SIZE_OP, 'dispatch);
445 }
446 }
447 op::NOT => {
448 let a = unsafe { pop_unchecked(&mut self.stack) };
449 self.stack.push(Value::bool(!a.is_truthy()));
450 }
451 op::EQ => {
452 let b = unsafe { pop_unchecked(&mut self.stack) };
453 let a = unsafe { pop_unchecked(&mut self.stack) };
454 self.stack.push(Value::bool(vm_eq(&a, &b)));
455 }
456 op::LT => {
457 let b = unsafe { pop_unchecked(&mut self.stack) };
458 let a = unsafe { pop_unchecked(&mut self.stack) };
459 match vm_lt(&a, &b) {
460 Ok(v) => self.stack.push(Value::bool(v)),
461 Err(err) => handle_err!(self, fi, pc, err, pc - op::SIZE_OP, 'dispatch),
462 }
463 }
464 op::GT => {
465 let b = unsafe { pop_unchecked(&mut self.stack) };
466 let a = unsafe { pop_unchecked(&mut self.stack) };
467 match vm_lt(&b, &a) {
468 Ok(v) => self.stack.push(Value::bool(v)),
469 Err(err) => handle_err!(self, fi, pc, err, pc - op::SIZE_OP, 'dispatch),
470 }
471 }
472 op::LE => {
473 let b = unsafe { pop_unchecked(&mut self.stack) };
474 let a = unsafe { pop_unchecked(&mut self.stack) };
475 match vm_lt(&b, &a) {
476 Ok(v) => self.stack.push(Value::bool(!v)),
477 Err(err) => handle_err!(self, fi, pc, err, pc - op::SIZE_OP, 'dispatch),
478 }
479 }
480 op::GE => {
481 let b = unsafe { pop_unchecked(&mut self.stack) };
482 let a = unsafe { pop_unchecked(&mut self.stack) };
483 match vm_lt(&a, &b) {
484 Ok(v) => self.stack.push(Value::bool(!v)),
485 Err(err) => handle_err!(self, fi, pc, err, pc - op::SIZE_OP, 'dispatch),
486 }
487 }
488
489 op::ADD_INT => {
494 let len = self.stack.len();
495 let a_bits = unsafe { (*self.stack.as_ptr().add(len - 2)).raw_bits() };
496 let b_bits = unsafe { (*self.stack.as_ptr().add(len - 1)).raw_bits() };
497 if (a_bits & NAN_TAG_MASK) == NAN_INT_SMALL_PATTERN
498 && (b_bits & NAN_TAG_MASK) == NAN_INT_SMALL_PATTERN
499 {
500 let sum = (a_bits.wrapping_add(b_bits)) & NAN_PAYLOAD_MASK;
501 let result = NAN_INT_SMALL_PATTERN | sum;
502 unsafe {
503 std::ptr::write(
504 self.stack.as_mut_ptr().add(len - 2),
505 Value::from_raw_bits(result),
506 );
507 self.stack.set_len(len - 1);
508 }
509 } else {
510 let b = unsafe { pop_unchecked(&mut self.stack) };
511 let a = unsafe { pop_unchecked(&mut self.stack) };
512 match vm_add(&a, &b) {
513 Ok(v) => self.stack.push(v),
514 Err(err) => {
515 handle_err!(self, fi, pc, err, pc - op::SIZE_OP, 'dispatch)
516 }
517 }
518 }
519 }
520 op::SUB_INT => {
521 let len = self.stack.len();
522 let a_bits = unsafe { (*self.stack.as_ptr().add(len - 2)).raw_bits() };
523 let b_bits = unsafe { (*self.stack.as_ptr().add(len - 1)).raw_bits() };
524 if (a_bits & NAN_TAG_MASK) == NAN_INT_SMALL_PATTERN
525 && (b_bits & NAN_TAG_MASK) == NAN_INT_SMALL_PATTERN
526 {
527 let diff = (a_bits.wrapping_sub(b_bits)) & NAN_PAYLOAD_MASK;
528 let result = NAN_INT_SMALL_PATTERN | diff;
529 unsafe {
530 std::ptr::write(
531 self.stack.as_mut_ptr().add(len - 2),
532 Value::from_raw_bits(result),
533 );
534 self.stack.set_len(len - 1);
535 }
536 } else {
537 let b = unsafe { pop_unchecked(&mut self.stack) };
538 let a = unsafe { pop_unchecked(&mut self.stack) };
539 match vm_sub(&a, &b) {
540 Ok(v) => self.stack.push(v),
541 Err(err) => {
542 handle_err!(self, fi, pc, err, pc - op::SIZE_OP, 'dispatch)
543 }
544 }
545 }
546 }
547 op::MUL_INT => {
548 let len = self.stack.len();
549 let a_bits = unsafe { (*self.stack.as_ptr().add(len - 2)).raw_bits() };
550 let b_bits = unsafe { (*self.stack.as_ptr().add(len - 1)).raw_bits() };
551 if (a_bits & NAN_TAG_MASK) == NAN_INT_SMALL_PATTERN
552 && (b_bits & NAN_TAG_MASK) == NAN_INT_SMALL_PATTERN
553 {
554 let ax =
556 (((a_bits & NAN_PAYLOAD_MASK) << SIGN_SHIFT) as i64) >> SIGN_SHIFT;
557 let bx =
558 (((b_bits & NAN_PAYLOAD_MASK) << SIGN_SHIFT) as i64) >> SIGN_SHIFT;
559 unsafe {
561 std::ptr::write(
562 self.stack.as_mut_ptr().add(len - 2),
563 Value::int(ax.wrapping_mul(bx)),
564 );
565 self.stack.set_len(len - 1);
566 }
567 } else {
568 let b = unsafe { pop_unchecked(&mut self.stack) };
569 let a = unsafe { pop_unchecked(&mut self.stack) };
570 match vm_mul(&a, &b) {
571 Ok(v) => self.stack.push(v),
572 Err(err) => {
573 handle_err!(self, fi, pc, err, pc - op::SIZE_OP, 'dispatch)
574 }
575 }
576 }
577 }
578 op::LT_INT => {
579 let len = self.stack.len();
580 let a_bits = unsafe { (*self.stack.as_ptr().add(len - 2)).raw_bits() };
581 let b_bits = unsafe { (*self.stack.as_ptr().add(len - 1)).raw_bits() };
582 if (a_bits & NAN_TAG_MASK) == NAN_INT_SMALL_PATTERN
583 && (b_bits & NAN_TAG_MASK) == NAN_INT_SMALL_PATTERN
584 {
585 let ax =
587 (((a_bits & NAN_PAYLOAD_MASK) << SIGN_SHIFT) as i64) >> SIGN_SHIFT;
588 let bx =
589 (((b_bits & NAN_PAYLOAD_MASK) << SIGN_SHIFT) as i64) >> SIGN_SHIFT;
590 unsafe {
591 std::ptr::write(
592 self.stack.as_mut_ptr().add(len - 2),
593 Value::bool(ax < bx),
594 );
595 self.stack.set_len(len - 1);
596 }
597 } else {
598 let b = unsafe { pop_unchecked(&mut self.stack) };
599 let a = unsafe { pop_unchecked(&mut self.stack) };
600 match vm_lt(&a, &b) {
601 Ok(v) => self.stack.push(Value::bool(v)),
602 Err(err) => {
603 handle_err!(self, fi, pc, err, pc - op::SIZE_OP, 'dispatch)
604 }
605 }
606 }
607 }
608 op::EQ_INT => {
609 let len = self.stack.len();
610 let a_bits = unsafe { (*self.stack.as_ptr().add(len - 2)).raw_bits() };
611 let b_bits = unsafe { (*self.stack.as_ptr().add(len - 1)).raw_bits() };
612 if (a_bits & NAN_TAG_MASK) == NAN_INT_SMALL_PATTERN
613 && (b_bits & NAN_TAG_MASK) == NAN_INT_SMALL_PATTERN
614 {
615 unsafe {
617 std::ptr::write(
618 self.stack.as_mut_ptr().add(len - 2),
619 Value::bool(a_bits == b_bits),
620 );
621 self.stack.set_len(len - 1);
622 }
623 } else {
624 let b = unsafe { pop_unchecked(&mut self.stack) };
625 let a = unsafe { pop_unchecked(&mut self.stack) };
626 self.stack.push(Value::bool(vm_eq(&a, &b)));
627 }
628 }
629
630 op::LOAD_LOCAL0 => {
631 let val = if has_open_upvalues {
632 if let Some(ref open) = self.frames[fi].open_upvalues {
633 if let Some(Some(cell)) = open.first() {
634 cell.value.borrow().clone()
635 } else {
636 self.stack[base].clone()
637 }
638 } else {
639 unreachable!()
640 }
641 } else {
642 self.stack[base].clone()
643 };
644 self.stack.push(val);
645 }
646 op::LOAD_LOCAL1 => {
647 let val = if has_open_upvalues {
648 if let Some(ref open) = self.frames[fi].open_upvalues {
649 if let Some(Some(cell)) = open.get(1) {
650 cell.value.borrow().clone()
651 } else {
652 self.stack[base + 1].clone()
653 }
654 } else {
655 unreachable!()
656 }
657 } else {
658 self.stack[base + 1].clone()
659 };
660 self.stack.push(val);
661 }
662 op::LOAD_LOCAL2 => {
663 let val = if has_open_upvalues {
664 if let Some(ref open) = self.frames[fi].open_upvalues {
665 if let Some(Some(cell)) = open.get(2) {
666 cell.value.borrow().clone()
667 } else {
668 self.stack[base + 2].clone()
669 }
670 } else {
671 unreachable!()
672 }
673 } else {
674 self.stack[base + 2].clone()
675 };
676 self.stack.push(val);
677 }
678 op::LOAD_LOCAL3 => {
679 let val = if has_open_upvalues {
680 if let Some(ref open) = self.frames[fi].open_upvalues {
681 if let Some(Some(cell)) = open.get(3) {
682 cell.value.borrow().clone()
683 } else {
684 self.stack[base + 3].clone()
685 }
686 } else {
687 unreachable!()
688 }
689 } else {
690 self.stack[base + 3].clone()
691 };
692 self.stack.push(val);
693 }
694
695 op::CALL_GLOBAL => {
698 let bits = read_u32!(code, pc);
699 let argc = read_u16!(code, pc) as usize;
700 self.frames[fi].pc = pc;
701 let saved_pc = pc - op::SIZE_CALL_GLOBAL;
702
703 let version = self.globals.version.get();
705 let slot = (bits as usize) & (GLOBAL_CACHE_SIZE - 1);
706 let entry = &self.global_cache[slot];
707 let func_val = if entry.0 == bits && entry.1 == version {
708 entry.2.clone()
709 } else {
710 let spur: Spur = unsafe { std::mem::transmute::<u32, Spur>(bits) };
711 match self.globals.get(spur) {
712 Some(val) => {
713 self.global_cache[slot] = (bits, version, val.clone());
714 val
715 }
716 None => {
717 let err = SemaError::Unbound(resolve_spur(spur));
718 handle_err!(self, fi, pc, err, saved_pc, 'dispatch);
719 }
720 }
721 };
722
723 if func_val.raw_tag() == Some(TAG_NATIVE_FN) {
725 let vm_closure_data = {
726 let native = func_val.as_native_fn_ref().unwrap();
727 native.payload.as_ref().and_then(|p| {
728 p.downcast_ref::<VmClosurePayload>().map(|vmc| {
729 let closure = vmc.closure.clone();
730 let functions =
731 if Rc::ptr_eq(&vmc.functions, &self.functions) {
732 None
733 } else {
734 Some(vmc.functions.clone())
735 };
736 (closure, functions)
737 })
738 })
739 };
740 if let Some((closure, functions)) = vm_closure_data {
741 if let Some(f) = functions {
742 self.functions = f;
743 }
744 if let Err(err) = self.call_vm_closure_direct(closure, argc) {
745 match self.handle_exception(err, saved_pc)? {
746 ExceptionAction::Handled => {}
747 ExceptionAction::Propagate(e) => return Err(e),
748 }
749 }
750 continue 'dispatch;
751 }
752 }
753
754 if let Err(err) = self.call_value_with(func_val, argc, ctx) {
756 match self.handle_exception(err, saved_pc)? {
757 ExceptionAction::Handled => {}
758 ExceptionAction::Propagate(e) => return Err(e),
759 }
760 }
761 continue 'dispatch;
762 }
763
764 op::STORE_LOCAL0 => {
765 let val = unsafe { pop_unchecked(&mut self.stack) };
766 self.stack[base] = val.clone();
767 if has_open_upvalues {
768 if let Some(ref open) = self.frames[fi].open_upvalues {
769 if let Some(Some(cell)) = open.first() {
770 *cell.value.borrow_mut() = val;
771 }
772 }
773 }
774 }
775 op::STORE_LOCAL1 => {
776 let val = unsafe { pop_unchecked(&mut self.stack) };
777 self.stack[base + 1] = val.clone();
778 if has_open_upvalues {
779 if let Some(ref open) = self.frames[fi].open_upvalues {
780 if let Some(Some(cell)) = open.get(1) {
781 *cell.value.borrow_mut() = val;
782 }
783 }
784 }
785 }
786 op::STORE_LOCAL2 => {
787 let val = unsafe { pop_unchecked(&mut self.stack) };
788 self.stack[base + 2] = val.clone();
789 if has_open_upvalues {
790 if let Some(ref open) = self.frames[fi].open_upvalues {
791 if let Some(Some(cell)) = open.get(2) {
792 *cell.value.borrow_mut() = val;
793 }
794 }
795 }
796 }
797 op::STORE_LOCAL3 => {
798 let val = unsafe { pop_unchecked(&mut self.stack) };
799 self.stack[base + 3] = val.clone();
800 if has_open_upvalues {
801 if let Some(ref open) = self.frames[fi].open_upvalues {
802 if let Some(Some(cell)) = open.get(3) {
803 *cell.value.borrow_mut() = val;
804 }
805 }
806 }
807 }
808
809 _ => {
810 return Err(SemaError::eval(format!("VM: invalid opcode {}", op)));
811 }
812 }
813 }
814 }
815 }
816
817 fn call_value(&mut self, argc: usize, ctx: &EvalContext) -> Result<(), SemaError> {
820 let func_idx = self.stack.len() - 1 - argc;
821
822 if self.stack[func_idx].raw_tag() == Some(TAG_NATIVE_FN) {
824 let vm_closure_data = {
827 let native = self.stack[func_idx].as_native_fn_ref().unwrap();
828 native.payload.as_ref().and_then(|p| {
829 p.downcast_ref::<VmClosurePayload>().map(|vmc| {
830 let closure = vmc.closure.clone();
831 let functions = if Rc::ptr_eq(&vmc.functions, &self.functions) {
833 None
834 } else {
835 Some(vmc.functions.clone())
836 };
837 (closure, functions)
838 })
839 })
840 };
841 if let Some((closure, functions)) = vm_closure_data {
842 if let Some(f) = functions {
843 self.functions = f;
844 }
845 return self.call_vm_closure(closure, argc);
846 }
847 let func_rc = self.stack[func_idx].as_native_fn_rc().unwrap();
849 let args_start = func_idx + 1;
850 let args: Vec<Value> = self.stack.drain(args_start..).collect();
851 self.stack.truncate(func_idx);
852 let result = (func_rc.func)(ctx, &args)?;
853 self.stack.push(result);
854 Ok(())
855 } else if let Some(kw) = self.stack[func_idx].as_keyword_spur() {
856 if argc != 1 {
858 return Err(SemaError::arity(resolve_spur(kw), "1", argc));
859 }
860 let arg = self.stack.pop().unwrap();
861 self.stack.pop(); let kw_val = Value::keyword_from_spur(kw);
863 let result = if let Some(m) = arg.as_map_rc() {
864 m.get(&kw_val).cloned().unwrap_or(Value::nil())
865 } else if let Some(m) = arg.as_hashmap_rc() {
866 m.get(&kw_val).cloned().unwrap_or(Value::nil())
867 } else {
868 return Err(SemaError::type_error("map or hashmap", arg.type_name()));
869 };
870 self.stack.push(result);
871 Ok(())
872 } else {
873 let func_val = self.stack[func_idx].clone();
875 let args_start = func_idx + 1;
876 let args: Vec<Value> = self.stack.drain(args_start..).collect();
877 self.stack.truncate(func_idx);
878 let result = sema_core::call_callback(ctx, &func_val, &args)?;
879 self.stack.push(result);
880 Ok(())
881 }
882 }
883
884 fn tail_call_value(&mut self, argc: usize, ctx: &EvalContext) -> Result<(), SemaError> {
885 let func_idx = self.stack.len() - 1 - argc;
886
887 if self.stack[func_idx].raw_tag() == Some(TAG_NATIVE_FN) {
889 let vm_closure_data = {
890 let native = self.stack[func_idx].as_native_fn_ref().unwrap();
891 native.payload.as_ref().and_then(|p| {
892 p.downcast_ref::<VmClosurePayload>().map(|vmc| {
893 let closure = vmc.closure.clone();
894 let functions = if Rc::ptr_eq(&vmc.functions, &self.functions) {
895 None
896 } else {
897 Some(vmc.functions.clone())
898 };
899 (closure, functions)
900 })
901 })
902 };
903 if let Some((closure, functions)) = vm_closure_data {
904 if let Some(f) = functions {
905 self.functions = f;
906 }
907 return self.tail_call_vm_closure(closure, argc);
908 }
909 }
910
911 self.call_value(argc, ctx)
913 }
914
915 fn call_value_with(
918 &mut self,
919 func_val: Value,
920 argc: usize,
921 ctx: &EvalContext,
922 ) -> Result<(), SemaError> {
923 if func_val.raw_tag() == Some(TAG_NATIVE_FN) {
924 let func_rc = func_val.as_native_fn_rc().unwrap();
925 let args_start = self.stack.len() - argc;
926 let args: Vec<Value> = self.stack.drain(args_start..).collect();
927 let result = (func_rc.func)(ctx, &args)?;
928 self.stack.push(result);
929 Ok(())
930 } else if let Some(kw) = func_val.as_keyword_spur() {
931 if argc != 1 {
932 return Err(SemaError::arity(resolve_spur(kw), "1", argc));
933 }
934 let arg = self.stack.pop().unwrap();
935 let kw_val = Value::keyword_from_spur(kw);
936 let result = if let Some(m) = arg.as_map_rc() {
937 m.get(&kw_val).cloned().unwrap_or(Value::nil())
938 } else if let Some(m) = arg.as_hashmap_rc() {
939 m.get(&kw_val).cloned().unwrap_or(Value::nil())
940 } else {
941 return Err(SemaError::type_error("map or hashmap", arg.type_name()));
942 };
943 self.stack.push(result);
944 Ok(())
945 } else {
946 let args_start = self.stack.len() - argc;
947 let args: Vec<Value> = self.stack.drain(args_start..).collect();
948 let result = sema_core::call_callback(ctx, &func_val, &args)?;
949 self.stack.push(result);
950 Ok(())
951 }
952 }
953
954 fn call_vm_closure_direct(
958 &mut self,
959 closure: Rc<Closure>,
960 argc: usize,
961 ) -> Result<(), SemaError> {
962 let func = &closure.func;
963 let arity = func.arity as usize;
964 let has_rest = func.has_rest;
965 let n_locals = func.chunk.n_locals as usize;
966
967 if has_rest {
969 if argc < arity {
970 return Err(SemaError::arity(
971 func.name
972 .map(resolve_spur)
973 .unwrap_or_else(|| "<lambda>".to_string()),
974 format!("{}+", arity),
975 argc,
976 ));
977 }
978 } else if argc != arity {
979 return Err(SemaError::arity(
980 func.name
981 .map(resolve_spur)
982 .unwrap_or_else(|| "<lambda>".to_string()),
983 arity.to_string(),
984 argc,
985 ));
986 }
987
988 let base = self.stack.len() - argc;
991
992 if has_rest {
993 let rest: Vec<Value> = self.stack[base + arity..base + argc].to_vec();
995 self.stack.truncate(base + arity);
996 self.stack.push(Value::list(rest));
997 }
998
999 self.stack.resize(base + n_locals, Value::nil());
1001
1002 self.frames.push(CallFrame {
1003 closure,
1004 pc: 0,
1005 base,
1006 open_upvalues: None,
1007 });
1008
1009 Ok(())
1010 }
1011
1012 fn call_vm_closure(&mut self, closure: Rc<Closure>, argc: usize) -> Result<(), SemaError> {
1016 let func = &closure.func;
1017 let arity = func.arity as usize;
1018 let has_rest = func.has_rest;
1019 let n_locals = func.chunk.n_locals as usize;
1020
1021 if has_rest {
1023 if argc < arity {
1024 return Err(SemaError::arity(
1025 func.name
1026 .map(resolve_spur)
1027 .unwrap_or_else(|| "<lambda>".to_string()),
1028 format!("{}+", arity),
1029 argc,
1030 ));
1031 }
1032 } else if argc != arity {
1033 return Err(SemaError::arity(
1034 func.name
1035 .map(resolve_spur)
1036 .unwrap_or_else(|| "<lambda>".to_string()),
1037 arity.to_string(),
1038 argc,
1039 ));
1040 }
1041
1042 let func_idx = self.stack.len() - 1 - argc;
1044 let base = func_idx; Self::copy_args_to_locals(&mut self.stack, base, func_idx + 1, arity, argc, has_rest);
1049
1050 self.stack.resize(base + n_locals, Value::nil());
1052
1053 self.frames.push(CallFrame {
1055 closure,
1056 pc: 0,
1057 base,
1058 open_upvalues: None,
1059 });
1060
1061 Ok(())
1062 }
1063
1064 fn tail_call_vm_closure(&mut self, closure: Rc<Closure>, argc: usize) -> Result<(), SemaError> {
1068 let func = &closure.func;
1069 let arity = func.arity as usize;
1070 let has_rest = func.has_rest;
1071 let n_locals = func.chunk.n_locals as usize;
1072
1073 if has_rest {
1075 if argc < arity {
1076 return Err(SemaError::arity(
1077 func.name
1078 .map(resolve_spur)
1079 .unwrap_or_else(|| "<lambda>".to_string()),
1080 format!("{}+", arity),
1081 argc,
1082 ));
1083 }
1084 } else if argc != arity {
1085 return Err(SemaError::arity(
1086 func.name
1087 .map(resolve_spur)
1088 .unwrap_or_else(|| "<lambda>".to_string()),
1089 arity.to_string(),
1090 argc,
1091 ));
1092 }
1093
1094 let func_idx = self.stack.len() - 1 - argc;
1096 let base = self.frames.last().unwrap().base;
1097
1098 Self::copy_args_to_locals(&mut self.stack, base, func_idx + 1, arity, argc, has_rest);
1100
1101 self.stack.resize(base + n_locals, Value::nil());
1103
1104 let frame = self.frames.last_mut().unwrap();
1106 frame.closure = closure;
1107 frame.pc = 0;
1108 frame.open_upvalues = None;
1110
1111 Ok(())
1112 }
1113
1114 #[inline(always)]
1117 fn copy_args_to_locals(
1118 stack: &mut [Value],
1119 dst: usize,
1120 src: usize,
1121 arity: usize,
1122 argc: usize,
1123 has_rest: bool,
1124 ) {
1125 if has_rest {
1126 let rest: Vec<Value> = stack[src + arity..src + argc].to_vec();
1127 for i in 0..arity {
1128 stack[dst + i] = stack[src + i].clone();
1129 }
1130 stack[dst + arity] = Value::list(rest);
1131 } else {
1132 for i in 0..arity {
1133 stack[dst + i] = stack[src + i].clone();
1134 }
1135 }
1136 }
1137
1138 fn make_closure(&mut self) -> Result<(), SemaError> {
1141 let frame = self.frames.last().unwrap();
1144 let code = &frame.closure.func.chunk.code;
1145 let pc = frame.pc + 1;
1146 let func_id = u16::from_le_bytes([code[pc], code[pc + 1]]) as usize;
1147 let n_upvalues = u16::from_le_bytes([code[pc + 2], code[pc + 3]]) as usize;
1148
1149 let mut uv_descs = Vec::with_capacity(n_upvalues);
1151 let mut uv_pc = pc + 4;
1152 for _ in 0..n_upvalues {
1153 let is_local = u16::from_le_bytes([code[uv_pc], code[uv_pc + 1]]);
1154 let idx = u16::from_le_bytes([code[uv_pc + 2], code[uv_pc + 3]]) as usize;
1155 uv_pc += 4;
1156 uv_descs.push((is_local != 0, idx));
1157 }
1158
1159 let base = frame.base;
1160 let parent_upvalues = frame.closure.upvalues.clone();
1161 let _ = frame;
1163
1164 let func = self.functions[func_id].clone();
1165 let mut upvalues = Vec::with_capacity(n_upvalues);
1166
1167 for (is_local, idx) in &uv_descs {
1168 if *is_local {
1169 let frame = self.frames.last_mut().unwrap();
1172 let n_locals = frame.closure.func.chunk.n_locals as usize;
1173 let open = frame
1174 .open_upvalues
1175 .get_or_insert_with(|| vec![None; n_locals]);
1176 let cell = if let Some(existing) = &open[*idx] {
1177 existing.clone()
1178 } else {
1179 let val = self.stack[base + *idx].clone();
1180 let cell = Rc::new(UpvalueCell::new(val));
1181 open[*idx] = Some(cell.clone());
1182 cell
1183 };
1184 upvalues.push(cell);
1185 } else {
1186 upvalues.push(parent_upvalues[*idx].clone());
1188 }
1189 }
1190
1191 self.frames.last_mut().unwrap().pc = uv_pc;
1193
1194 let closure = Rc::new(Closure { func, upvalues });
1195 let payload: Rc<dyn std::any::Any> = Rc::new(VmClosurePayload {
1196 closure: closure.clone(),
1197 functions: self.functions.clone(),
1198 });
1199 let closure_for_fallback = closure.clone();
1200 let functions = self.functions.clone();
1201 let globals = self.globals.clone();
1202
1203 let native = Value::native_fn_from_rc(Rc::new(sema_core::NativeFn::with_payload(
1207 closure_for_fallback
1208 .func
1209 .name
1210 .map(resolve_spur)
1211 .unwrap_or_else(|| "<vm-closure>".to_string()),
1212 payload,
1213 move |ctx, args| {
1214 let mut vm = VM::new_with_rc_functions(globals.clone(), functions.clone());
1215 let func = &closure_for_fallback.func;
1216 let arity = func.arity as usize;
1217 let has_rest = func.has_rest;
1218 let n_locals = func.chunk.n_locals as usize;
1219
1220 if has_rest {
1221 if args.len() < arity {
1222 return Err(SemaError::arity(
1223 func.name
1224 .map(resolve_spur)
1225 .unwrap_or_else(|| "<lambda>".to_string()),
1226 format!("{}+", arity),
1227 args.len(),
1228 ));
1229 }
1230 } else if args.len() != arity {
1231 return Err(SemaError::arity(
1232 func.name
1233 .map(resolve_spur)
1234 .unwrap_or_else(|| "<lambda>".to_string()),
1235 arity.to_string(),
1236 args.len(),
1237 ));
1238 }
1239
1240 vm.stack.resize(n_locals, Value::nil());
1241
1242 if has_rest {
1243 for i in 0..arity {
1244 vm.stack[i] = args.get(i).cloned().unwrap_or(Value::nil());
1245 }
1246 let rest: Vec<Value> = args[arity..].to_vec();
1247 vm.stack[arity] = Value::list(rest);
1248 } else {
1249 for i in 0..arity {
1250 vm.stack[i] = args.get(i).cloned().unwrap_or(Value::nil());
1251 }
1252 }
1253
1254 vm.frames.push(CallFrame {
1255 closure: closure_for_fallback.clone(),
1256 pc: 0,
1257 base: 0,
1258 open_upvalues: None,
1259 });
1260 vm.run(ctx)
1261 },
1262 )));
1263
1264 self.stack.push(native);
1265 Ok(())
1266 }
1267
1268 #[cold]
1271 #[inline(never)]
1272 fn handle_exception(
1273 &mut self,
1274 err: SemaError,
1275 failing_pc: usize,
1276 ) -> Result<ExceptionAction, SemaError> {
1277 let mut pc_for_lookup = failing_pc as u32;
1278 while let Some(frame) = self.frames.last() {
1280 let chunk = &frame.closure.func.chunk;
1281
1282 let mut found = None;
1284 for entry in &chunk.exception_table {
1285 if pc_for_lookup >= entry.try_start && pc_for_lookup < entry.try_end {
1286 found = Some(entry.clone());
1287 break;
1288 }
1289 }
1290
1291 if let Some(entry) = found {
1292 let base = frame.base;
1294 self.stack.truncate(base + entry.stack_depth as usize);
1295
1296 let error_val = error_to_value(&err);
1298 self.stack.push(error_val);
1299
1300 let frame = self.frames.last_mut().unwrap();
1302 frame.pc = entry.handler_pc as usize;
1303 return Ok(ExceptionAction::Handled);
1304 }
1305
1306 let frame = self.frames.pop().unwrap();
1308 self.stack.truncate(frame.base);
1309 if let Some(parent) = self.frames.last() {
1315 pc_for_lookup = parent.pc.saturating_sub(1) as u32;
1316 }
1317 }
1318
1319 Ok(ExceptionAction::Propagate(err))
1321 }
1322}
1323
1324enum ExceptionAction {
1325 Handled,
1326 Propagate(SemaError),
1327}
1328
1329fn error_to_value(err: &SemaError) -> Value {
1331 let inner = err.inner();
1332 let mut map = BTreeMap::new();
1333 match inner {
1334 SemaError::Eval(msg) => {
1335 map.insert(Value::keyword("type"), Value::keyword("eval"));
1336 map.insert(Value::keyword("message"), Value::string(msg));
1337 }
1338 SemaError::Type { expected, got } => {
1339 map.insert(Value::keyword("type"), Value::keyword("type-error"));
1340 map.insert(
1341 Value::keyword("message"),
1342 Value::string(&format!("expected {expected}, got {got}")),
1343 );
1344 map.insert(Value::keyword("expected"), Value::string(expected));
1345 map.insert(Value::keyword("got"), Value::string(got));
1346 }
1347 SemaError::Arity {
1348 name,
1349 expected,
1350 got,
1351 } => {
1352 map.insert(Value::keyword("type"), Value::keyword("arity"));
1353 map.insert(
1354 Value::keyword("message"),
1355 Value::string(&format!("{name} expects {expected} args, got {got}")),
1356 );
1357 }
1358 SemaError::Unbound(name) => {
1359 map.insert(Value::keyword("type"), Value::keyword("unbound"));
1360 map.insert(
1361 Value::keyword("message"),
1362 Value::string(&format!("Unbound variable: {name}")),
1363 );
1364 map.insert(Value::keyword("name"), Value::string(name));
1365 }
1366 SemaError::UserException(val) => {
1367 map.insert(Value::keyword("type"), Value::keyword("user"));
1368 map.insert(Value::keyword("message"), Value::string(&val.to_string()));
1369 map.insert(Value::keyword("value"), val.clone());
1370 }
1371 SemaError::Io(msg) => {
1372 map.insert(Value::keyword("type"), Value::keyword("io"));
1373 map.insert(Value::keyword("message"), Value::string(msg));
1374 }
1375 SemaError::Llm(msg) => {
1376 map.insert(Value::keyword("type"), Value::keyword("llm"));
1377 map.insert(Value::keyword("message"), Value::string(msg));
1378 }
1379 SemaError::Reader { message, span } => {
1380 map.insert(Value::keyword("type"), Value::keyword("reader"));
1381 map.insert(
1382 Value::keyword("message"),
1383 Value::string(&format!("{message} at {span}")),
1384 );
1385 }
1386 SemaError::PermissionDenied {
1387 function,
1388 capability,
1389 } => {
1390 map.insert(Value::keyword("type"), Value::keyword("permission-denied"));
1391 map.insert(
1392 Value::keyword("message"),
1393 Value::string(&format!(
1394 "Permission denied: {function} requires '{capability}' capability"
1395 )),
1396 );
1397 map.insert(Value::keyword("function"), Value::string(function));
1398 map.insert(Value::keyword("capability"), Value::string(capability));
1399 }
1400 SemaError::PathDenied { function, path } => {
1401 map.insert(Value::keyword("type"), Value::keyword("permission-denied"));
1402 map.insert(
1403 Value::keyword("message"),
1404 Value::string(&format!(
1405 "Permission denied: {function} — path '{path}' is outside allowed directories"
1406 )),
1407 );
1408 map.insert(Value::keyword("function"), Value::string(function));
1409 map.insert(Value::keyword("path"), Value::string(path));
1410 }
1411 SemaError::WithTrace { .. } | SemaError::WithContext { .. } => {
1412 unreachable!("inner() already unwraps these")
1413 }
1414 }
1415 Value::map(map)
1416}
1417
1418#[inline(always)]
1421fn vm_add(a: &Value, b: &Value) -> Result<Value, SemaError> {
1422 use sema_core::ValueView;
1423 match (a.view(), b.view()) {
1424 (ValueView::Int(x), ValueView::Int(y)) => Ok(Value::int(x.wrapping_add(y))),
1425 (ValueView::Float(x), ValueView::Float(y)) => Ok(Value::float(x + y)),
1426 (ValueView::Int(x), ValueView::Float(y)) => Ok(Value::float(x as f64 + y)),
1427 (ValueView::Float(x), ValueView::Int(y)) => Ok(Value::float(x + y as f64)),
1428 (ValueView::String(x), ValueView::String(y)) => {
1429 let mut s = (*x).clone();
1430 s.push_str(&y);
1431 Ok(Value::string(&s))
1432 }
1433 _ => Err(SemaError::type_error(
1434 "number or string",
1435 format!("{} and {}", a.type_name(), b.type_name()),
1436 )),
1437 }
1438}
1439
1440#[inline(always)]
1441fn vm_sub(a: &Value, b: &Value) -> Result<Value, SemaError> {
1442 use sema_core::ValueView;
1443 match (a.view(), b.view()) {
1444 (ValueView::Int(x), ValueView::Int(y)) => Ok(Value::int(x.wrapping_sub(y))),
1445 (ValueView::Float(x), ValueView::Float(y)) => Ok(Value::float(x - y)),
1446 (ValueView::Int(x), ValueView::Float(y)) => Ok(Value::float(x as f64 - y)),
1447 (ValueView::Float(x), ValueView::Int(y)) => Ok(Value::float(x - y as f64)),
1448 _ => Err(SemaError::type_error(
1449 "number",
1450 format!("{} and {}", a.type_name(), b.type_name()),
1451 )),
1452 }
1453}
1454
1455#[inline(always)]
1456fn vm_mul(a: &Value, b: &Value) -> Result<Value, SemaError> {
1457 use sema_core::ValueView;
1458 match (a.view(), b.view()) {
1459 (ValueView::Int(x), ValueView::Int(y)) => Ok(Value::int(x.wrapping_mul(y))),
1460 (ValueView::Float(x), ValueView::Float(y)) => Ok(Value::float(x * y)),
1461 (ValueView::Int(x), ValueView::Float(y)) => Ok(Value::float(x as f64 * y)),
1462 (ValueView::Float(x), ValueView::Int(y)) => Ok(Value::float(x * y as f64)),
1463 _ => Err(SemaError::type_error(
1464 "number",
1465 format!("{} and {}", a.type_name(), b.type_name()),
1466 )),
1467 }
1468}
1469
1470#[inline(always)]
1471fn vm_div(a: &Value, b: &Value) -> Result<Value, SemaError> {
1472 use sema_core::ValueView;
1473 match (a.view(), b.view()) {
1474 (ValueView::Int(_), ValueView::Int(0)) => Err(SemaError::eval("division by zero")),
1475 (ValueView::Int(x), ValueView::Int(y)) => {
1476 let result = x as f64 / y as f64;
1477 if result.fract() == 0.0 {
1478 Ok(Value::int(result as i64))
1479 } else {
1480 Ok(Value::float(result))
1481 }
1482 }
1483 (ValueView::Float(x), ValueView::Float(y)) => Ok(Value::float(x / y)),
1484 (ValueView::Int(x), ValueView::Float(y)) => Ok(Value::float(x as f64 / y)),
1485 (ValueView::Float(x), ValueView::Int(y)) => Ok(Value::float(x / y as f64)),
1486 _ => Err(SemaError::type_error(
1487 "number",
1488 format!("{} and {}", a.type_name(), b.type_name()),
1489 )),
1490 }
1491}
1492
1493#[inline(always)]
1495fn vm_eq(a: &Value, b: &Value) -> bool {
1496 use sema_core::ValueView;
1497 match (a.view(), b.view()) {
1498 (ValueView::Int(x), ValueView::Int(y)) => x == y,
1499 (ValueView::Float(x), ValueView::Float(y)) => x == y,
1500 (ValueView::Int(x), ValueView::Float(y)) | (ValueView::Float(y), ValueView::Int(x)) => {
1501 (x as f64) == y
1502 }
1503 _ => a == b,
1504 }
1505}
1506
1507fn vm_lt(a: &Value, b: &Value) -> Result<bool, SemaError> {
1508 use sema_core::ValueView;
1509 match (a.view(), b.view()) {
1510 (ValueView::Int(x), ValueView::Int(y)) => Ok(x < y),
1511 (ValueView::Float(x), ValueView::Float(y)) => Ok(x < y),
1512 (ValueView::Int(x), ValueView::Float(y)) => Ok((x as f64) < y),
1513 (ValueView::Float(x), ValueView::Int(y)) => Ok(x < (y as f64)),
1514 (ValueView::String(x), ValueView::String(y)) => Ok(x < y),
1515 _ => Err(SemaError::type_error(
1516 "comparable values",
1517 format!("{} and {}", a.type_name(), b.type_name()),
1518 )),
1519 }
1520}
1521
1522pub fn compile_program(vals: &[Value]) -> Result<(Rc<Closure>, Vec<Rc<Function>>), SemaError> {
1525 let mut resolved = Vec::new();
1526 let mut total_locals: u16 = 0;
1527 for val in vals {
1528 let core = crate::lower::lower(val)?;
1529 let (res, n) = crate::resolve::resolve_with_locals(&core)?;
1530 total_locals = total_locals.max(n);
1531 resolved.push(res);
1532 }
1533 let result = crate::compiler::compile_many_with_locals(&resolved, total_locals)?;
1534
1535 let functions: Vec<Rc<Function>> = result.functions.into_iter().map(Rc::new).collect();
1536 let closure = Rc::new(Closure {
1537 func: Rc::new(Function {
1538 name: None,
1539 chunk: result.chunk,
1540 upvalue_descs: Vec::new(),
1541 arity: 0,
1542 has_rest: false,
1543 local_names: Vec::new(),
1544 }),
1545 upvalues: Vec::new(),
1546 });
1547
1548 Ok((closure, functions))
1549}
1550
1551pub fn eval_str(input: &str, globals: &Rc<Env>, ctx: &EvalContext) -> Result<Value, SemaError> {
1553 let vals =
1554 sema_reader::read_many(input).map_err(|e| SemaError::eval(format!("parse error: {e}")))?;
1555 let (closure, functions) = compile_program(&vals)?;
1556 let mut vm = VM::new(globals.clone(), functions);
1557 vm.execute(closure, ctx)
1558}
1559
1560#[cfg(test)]
1561mod tests {
1562 use super::*;
1563 use sema_core::{intern, NativeFn};
1564
1565 fn make_test_env() -> Rc<Env> {
1566 let env = Rc::new(Env::new());
1567 env.set(
1568 intern("+"),
1569 Value::native_fn(NativeFn::simple("+", |args| vm_add(&args[0], &args[1]))),
1570 );
1571 env.set(
1572 intern("-"),
1573 Value::native_fn(NativeFn::simple("-", |args| vm_sub(&args[0], &args[1]))),
1574 );
1575 env.set(
1576 intern("*"),
1577 Value::native_fn(NativeFn::simple("*", |args| vm_mul(&args[0], &args[1]))),
1578 );
1579 env.set(
1580 intern("/"),
1581 Value::native_fn(NativeFn::simple("/", |args| vm_div(&args[0], &args[1]))),
1582 );
1583 env.set(
1584 intern("="),
1585 Value::native_fn(NativeFn::simple("=", |args| {
1586 Ok(Value::bool(vm_eq(&args[0], &args[1])))
1587 })),
1588 );
1589 env.set(
1590 intern("<"),
1591 Value::native_fn(NativeFn::simple("<", |args| {
1592 Ok(Value::bool(vm_lt(&args[0], &args[1])?))
1593 })),
1594 );
1595 env.set(
1596 intern(">"),
1597 Value::native_fn(NativeFn::simple(">", |args| {
1598 Ok(Value::bool(vm_lt(&args[1], &args[0])?))
1599 })),
1600 );
1601 env.set(
1602 intern("not"),
1603 Value::native_fn(NativeFn::simple("not", |args| {
1604 Ok(Value::bool(!args[0].is_truthy()))
1605 })),
1606 );
1607 env.set(
1608 intern("list"),
1609 Value::native_fn(NativeFn::simple("list", |args| {
1610 Ok(Value::list(args.to_vec()))
1611 })),
1612 );
1613 env
1614 }
1615
1616 fn eval(input: &str) -> Result<Value, SemaError> {
1617 let globals = make_test_env();
1618 let ctx = EvalContext::new();
1619 eval_str(input, &globals, &ctx)
1620 }
1621
1622 #[test]
1623 fn test_vm_int_literal() {
1624 assert_eq!(eval("42").unwrap(), Value::int(42));
1625 }
1626
1627 #[test]
1628 fn test_vm_nil() {
1629 assert_eq!(eval("nil").unwrap(), Value::nil());
1630 }
1631
1632 #[test]
1633 fn test_vm_bool() {
1634 assert_eq!(eval("#t").unwrap(), Value::bool(true));
1635 assert_eq!(eval("#f").unwrap(), Value::bool(false));
1636 }
1637
1638 #[test]
1639 fn test_vm_string() {
1640 assert_eq!(eval("\"hello\"").unwrap(), Value::string("hello"));
1641 }
1642
1643 #[test]
1644 fn test_vm_if_true() {
1645 assert_eq!(eval("(if #t 42 99)").unwrap(), Value::int(42));
1646 }
1647
1648 #[test]
1649 fn test_vm_if_false() {
1650 assert_eq!(eval("(if #f 42 99)").unwrap(), Value::int(99));
1651 }
1652
1653 #[test]
1654 fn test_vm_begin() {
1655 assert_eq!(eval("(begin 1 2 3)").unwrap(), Value::int(3));
1656 }
1657
1658 #[test]
1659 fn test_vm_define_and_load() {
1660 let globals = make_test_env();
1661 let ctx = EvalContext::new();
1662 eval_str("(define x 42)", &globals, &ctx).unwrap();
1663 let result = eval_str("x", &globals, &ctx).unwrap();
1664 assert_eq!(result, Value::int(42));
1665 }
1666
1667 #[test]
1668 fn test_vm_let() {
1669 assert_eq!(eval("(let ((x 10)) x)").unwrap(), Value::int(10));
1670 }
1671
1672 #[test]
1673 fn test_vm_let_multiple() {
1674 assert_eq!(eval("(let ((x 10) (y 20)) y)").unwrap(), Value::int(20));
1675 }
1676
1677 #[test]
1678 fn test_vm_nested_if() {
1679 assert_eq!(eval("(if (if #t #f #t) 1 2)").unwrap(), Value::int(2));
1680 }
1681
1682 #[test]
1683 fn test_vm_lambda_call() {
1684 assert_eq!(eval("((lambda (x) x) 42)").unwrap(), Value::int(42));
1685 }
1686
1687 #[test]
1688 fn test_vm_lambda_two_args() {
1689 assert_eq!(eval("((lambda (x y) y) 1 2)").unwrap(), Value::int(2));
1690 }
1691
1692 #[test]
1693 fn test_vm_closure_capture() {
1694 assert_eq!(
1695 eval("(let ((x 10)) ((lambda () x)))").unwrap(),
1696 Value::int(10)
1697 );
1698 }
1699
1700 #[test]
1701 fn test_vm_list_literal() {
1702 let result = eval("(list 1 2 3)").unwrap();
1703 let items = result.as_list().expect("Expected list");
1704 assert_eq!(items.len(), 3);
1705 assert_eq!(items[0], Value::int(1));
1706 }
1707
1708 #[test]
1709 fn test_vm_make_vector() {
1710 let result = eval("[1 2 3]").unwrap();
1711 let items = result.as_vector().expect("Expected vector");
1712 assert_eq!(items.len(), 3);
1713 }
1714
1715 #[test]
1716 fn test_vm_and_short_circuit() {
1717 assert_eq!(eval("(and #f 42)").unwrap(), Value::bool(false));
1718 assert_eq!(eval("(and #t 42)").unwrap(), Value::int(42));
1719 }
1720
1721 #[test]
1722 fn test_vm_or_short_circuit() {
1723 assert_eq!(eval("(or 42 99)").unwrap(), Value::int(42));
1724 assert_eq!(eval("(or #f 99)").unwrap(), Value::int(99));
1725 }
1726
1727 #[test]
1728 fn test_vm_throw_catch() {
1729 let result = eval("(try (throw \"boom\") (catch e (:value e)))").unwrap();
1731 assert_eq!(result, Value::string("boom"));
1732 }
1733
1734 #[test]
1735 fn test_vm_throw_catch_type() {
1736 let result = eval("(try (throw \"boom\") (catch e (:type e)))").unwrap();
1737 assert_eq!(result, Value::keyword("user"));
1738 }
1739
1740 #[test]
1741 fn test_vm_try_no_throw() {
1742 assert_eq!(eval("(try 42 (catch e 99))").unwrap(), Value::int(42));
1743 }
1744
1745 #[test]
1746 fn test_vm_try_catch_native_error() {
1747 let result = eval("(try (/ 1 0) (catch e \"caught\"))").unwrap();
1749 assert_eq!(result, Value::string("caught"));
1750 }
1751
1752 #[test]
1753 fn test_vm_try_catch_native_error_message() {
1754 let result = eval("(try (/ 1 0) (catch e (:message e)))").unwrap();
1755 let s = result.as_str().expect("Expected string");
1756 assert!(s.contains("division by zero"), "got: {s}");
1757 }
1758
1759 #[test]
1760 fn test_vm_try_catch_type_error() {
1761 let result = eval("(try (+ 1 \"a\") (catch e (:type e)))").unwrap();
1762 assert_eq!(result, Value::keyword("type-error"));
1763 }
1764
1765 #[test]
1766 fn test_vm_try_catch_from_closure_call() {
1767 let globals = make_test_env();
1769 let ctx = EvalContext::new();
1770 eval_str("(define (thrower) (throw \"boom\"))", &globals, &ctx).unwrap();
1771 let result = eval_str("(try (thrower) (catch e \"caught\"))", &globals, &ctx).unwrap();
1772 assert_eq!(result, Value::string("caught"));
1773 }
1774
1775 #[test]
1776 fn test_vm_try_catch_from_lambda_call() {
1777 let result = eval("(try ((fn () (throw 42))) (catch e (:value e)))").unwrap();
1779 assert_eq!(result, Value::int(42));
1780 }
1781
1782 #[test]
1783 fn test_vm_try_catch_nested_call() {
1784 let globals = make_test_env();
1786 let ctx = EvalContext::new();
1787 eval_str("(define (inner) (throw \"deep\"))", &globals, &ctx).unwrap();
1788 eval_str("(define (outer) (inner))", &globals, &ctx).unwrap();
1789 let result = eval_str("(try (outer) (catch e \"caught\"))", &globals, &ctx).unwrap();
1790 assert_eq!(result, Value::string("caught"));
1791 }
1792
1793 #[test]
1794 fn test_vm_try_catch_in_call_arg() {
1795 let globals = make_test_env();
1797 let ctx = EvalContext::new();
1798 eval_str("(define (thrower) (throw \"boom\"))", &globals, &ctx).unwrap();
1799 let result = eval_str("(+ 1 (try (thrower) (catch e 2)))", &globals, &ctx).unwrap();
1801 assert_eq!(result, Value::int(3));
1802 }
1803
1804 #[test]
1805 fn test_vm_try_catch_in_list_constructor() {
1806 let globals = make_test_env();
1808 let ctx = EvalContext::new();
1809 eval_str("(define (thrower) (throw \"boom\"))", &globals, &ctx).unwrap();
1810 let result = eval_str("(list 1 2 (try (thrower) (catch e 3)) 4)", &globals, &ctx).unwrap();
1811 let items = result.as_list().expect("list");
1812 assert_eq!(items.len(), 4);
1813 assert_eq!(items[2], Value::int(3));
1814 }
1815
1816 #[test]
1817 fn test_vm_try_catch_call_not_last() {
1818 let globals = make_test_env();
1820 let ctx = EvalContext::new();
1821 eval_str("(define (thrower) (throw \"boom\"))", &globals, &ctx).unwrap();
1822 let result = eval_str(
1823 "(try (begin (thrower) 123) (catch e \"caught\"))",
1824 &globals,
1825 &ctx,
1826 )
1827 .unwrap();
1828 assert_eq!(result, Value::string("caught"));
1829 }
1830
1831 #[test]
1832 fn test_vm_quote() {
1833 let result = eval("'(a b c)").unwrap();
1834 let items = result.as_list().expect("Expected list");
1835 assert_eq!(items.len(), 3);
1836 }
1837
1838 #[test]
1839 fn test_vm_set() {
1840 let globals = make_test_env();
1841 let ctx = EvalContext::new();
1842 eval_str("(define x 1)", &globals, &ctx).unwrap();
1843 eval_str("(set! x 42)", &globals, &ctx).unwrap();
1844 let result = eval_str("x", &globals, &ctx).unwrap();
1845 assert_eq!(result, Value::int(42));
1846 }
1847
1848 #[test]
1849 fn test_vm_recursive_define() {
1850 let globals = make_test_env();
1851 let ctx = EvalContext::new();
1852 eval_str(
1853 "(define (fact n) (if (= n 0) 1 (* n (fact (- n 1)))))",
1854 &globals,
1855 &ctx,
1856 )
1857 .unwrap();
1858 let result = eval_str("(fact 5)", &globals, &ctx).unwrap();
1859 assert_eq!(result, Value::int(120));
1860 }
1861
1862 #[test]
1863 fn test_vm_do_loop() {
1864 let result = eval("(do ((i 0 (+ i 1))) ((= i 5) i))").unwrap();
1865 assert_eq!(result, Value::int(5));
1866 }
1867
1868 #[test]
1869 fn test_vm_named_let() {
1870 let result =
1871 eval("(let loop ((n 5) (acc 1)) (if (= n 0) acc (loop (- n 1) (* acc n))))").unwrap();
1872 assert_eq!(result, Value::int(120));
1873 }
1874
1875 #[test]
1876 fn test_vm_letrec() {
1877 let globals = make_test_env();
1878 let ctx = EvalContext::new();
1879 let result = eval_str(
1880 "(letrec ((even? (lambda (n) (if (= n 0) #t (odd? (- n 1))))) (odd? (lambda (n) (if (= n 0) #f (even? (- n 1)))))) (even? 4))",
1881 &globals,
1882 &ctx,
1883 ).unwrap();
1884 assert_eq!(result, Value::bool(true));
1885 }
1886
1887 #[test]
1888 fn test_vm_rest_params() {
1889 let result = eval("((lambda (x . rest) rest) 1 2 3)").unwrap();
1890 let items = result.as_list().expect("Expected list");
1891 assert_eq!(items.len(), 2);
1892 assert_eq!(items[0], Value::int(2));
1893 assert_eq!(items[1], Value::int(3));
1894 }
1895
1896 #[test]
1899 fn test_vm_counter_closure() {
1900 let result =
1902 eval("(let ((n 0)) (let ((inc (lambda () (set! n (+ n 1)) n))) (inc) (inc) (inc)))")
1903 .unwrap();
1904 assert_eq!(result, Value::int(3));
1905 }
1906
1907 #[test]
1908 fn test_vm_shared_mutable_upvalue() {
1909 let result = eval(
1911 "(let ((n 0)) (let ((inc (lambda () (set! n (+ n 1)))) (get (lambda () n))) (inc) (inc) (get)))",
1912 )
1913 .unwrap();
1914 assert_eq!(result, Value::int(2));
1915 }
1916
1917 #[test]
1918 fn test_vm_set_local_in_let() {
1919 let result = eval("(let ((x 1)) (set! x 42) x)").unwrap();
1921 assert_eq!(result, Value::int(42));
1922 }
1923
1924 #[test]
1925 fn test_vm_closure_captures_after_mutation() {
1926 let result = eval("(let ((x 1)) (set! x 10) ((lambda () x)))").unwrap();
1928 assert_eq!(result, Value::int(10));
1929 }
1930
1931 #[test]
1932 fn test_vm_closure_returns_closure() {
1933 let result = eval("(let ((f (lambda () (lambda (x) x)))) ((f) 42))").unwrap();
1935 assert_eq!(result, Value::int(42));
1936 }
1937
1938 #[test]
1939 fn test_vm_make_adder() {
1940 let globals = make_test_env();
1942 let ctx = EvalContext::new();
1943 eval_str(
1944 "(define (make-adder n) (lambda (x) (+ n x)))",
1945 &globals,
1946 &ctx,
1947 )
1948 .unwrap();
1949 eval_str("(define add5 (make-adder 5))", &globals, &ctx).unwrap();
1950 let result = eval_str("(add5 3)", &globals, &ctx).unwrap();
1951 assert_eq!(result, Value::int(8));
1952 }
1953
1954 #[test]
1955 fn test_vm_compose() {
1956 let globals = make_test_env();
1958 let ctx = EvalContext::new();
1959 eval_str(
1960 "(define (compose f g) (lambda (x) (f (g x))))",
1961 &globals,
1962 &ctx,
1963 )
1964 .unwrap();
1965 eval_str("(define inc (lambda (x) (+ x 1)))", &globals, &ctx).unwrap();
1966 eval_str("(define dbl (lambda (x) (* x 2)))", &globals, &ctx).unwrap();
1967 let result = eval_str("((compose dbl inc) 5)", &globals, &ctx).unwrap();
1968 assert_eq!(result, Value::int(12));
1969 }
1970
1971 #[test]
1972 fn test_vm_nested_make_closure() {
1973 let result = eval("((((lambda () (lambda () (lambda () 42))))))").unwrap();
1975 assert_eq!(result, Value::int(42));
1976 }
1977
1978 #[test]
1979 fn test_vm_named_fn_rest_params() {
1980 let globals = make_test_env();
1981 let ctx = EvalContext::new();
1982 eval_str("(define (f . args) args)", &globals, &ctx).unwrap();
1983 let result = eval_str("(f 1 2 3)", &globals, &ctx).unwrap();
1984 let items = result.as_list().expect("Expected list");
1985 assert_eq!(items.len(), 3);
1986 assert_eq!(items[0], Value::int(1));
1987 }
1988
1989 #[test]
1990 fn test_vm_named_let_still_works_with_fix() {
1991 let globals = make_test_env();
1992 let ctx = EvalContext::new();
1993 let result = eval_str(
1994 "(let loop ((n 5) (acc 1)) (if (= n 0) acc (loop (- n 1) (* acc n))))",
1995 &globals,
1996 &ctx,
1997 )
1998 .unwrap();
1999 assert_eq!(result, Value::int(120));
2000 }
2001
2002 #[test]
2003 fn test_vm_curry() {
2004 let globals = make_test_env();
2006 let ctx = EvalContext::new();
2007 eval_str(
2008 "(define (curry f) (lambda (x) (lambda (y) (f x y))))",
2009 &globals,
2010 &ctx,
2011 )
2012 .unwrap();
2013 let result = eval_str("(((curry +) 3) 4)", &globals, &ctx).unwrap();
2014 assert_eq!(result, Value::int(7));
2015 }
2016
2017 #[test]
2020 fn test_vm_div_int_returns_float_when_non_whole() {
2021 let globals = make_test_env();
2022 let ctx = EvalContext::new();
2023 let result = eval_str("(/ 3 2)", &globals, &ctx).unwrap();
2024 assert_eq!(result, Value::float(1.5));
2025 }
2026
2027 #[test]
2028 fn test_vm_div_int_returns_int_when_whole() {
2029 let globals = make_test_env();
2030 let ctx = EvalContext::new();
2031 let result = eval_str("(/ 4 2)", &globals, &ctx).unwrap();
2032 assert_eq!(result, Value::int(2));
2033 }
2034
2035 #[test]
2036 fn test_vm_div_int_negative_non_whole() {
2037 let globals = make_test_env();
2038 let ctx = EvalContext::new();
2039 let result = eval_str("(/ 7 3)", &globals, &ctx).unwrap();
2040 assert!(
2041 result.as_float().is_some(),
2042 "expected float, got {:?}",
2043 result
2044 );
2045 }
2046
2047 #[test]
2048 fn test_vm_div_by_zero() {
2049 let globals = make_test_env();
2050 let ctx = EvalContext::new();
2051 assert!(eval_str("(/ 1 0)", &globals, &ctx).is_err());
2052 }
2053
2054 #[test]
2055 fn test_vm_eq_int_float_coercion() {
2056 let globals = make_test_env();
2057 let ctx = EvalContext::new();
2058 assert_eq!(
2059 eval_str("(= 1 1.0)", &globals, &ctx).unwrap(),
2060 Value::bool(true)
2061 );
2062 }
2063
2064 #[test]
2065 fn test_vm_eq_float_int_coercion() {
2066 let globals = make_test_env();
2067 let ctx = EvalContext::new();
2068 assert_eq!(
2069 eval_str("(= 1.0 1)", &globals, &ctx).unwrap(),
2070 Value::bool(true)
2071 );
2072 }
2073
2074 #[test]
2075 fn test_vm_eq_int_float_not_equal() {
2076 let globals = make_test_env();
2077 let ctx = EvalContext::new();
2078 assert_eq!(
2079 eval_str("(= 1 2.0)", &globals, &ctx).unwrap(),
2080 Value::bool(false)
2081 );
2082 }
2083
2084 #[test]
2085 fn test_vm_eq_same_type_int() {
2086 let globals = make_test_env();
2087 let ctx = EvalContext::new();
2088 assert_eq!(
2089 eval_str("(= 1 1)", &globals, &ctx).unwrap(),
2090 Value::bool(true)
2091 );
2092 assert_eq!(
2093 eval_str("(= 1 2)", &globals, &ctx).unwrap(),
2094 Value::bool(false)
2095 );
2096 }
2097
2098 #[test]
2099 fn test_vm_eq_opcode_direct() {
2100 use crate::emit::Emitter;
2101 use crate::opcodes::Op;
2102 let globals = Rc::new(Env::new());
2103 let ctx = EvalContext::new();
2104 let mut e = Emitter::new();
2105 e.emit_const(Value::int(1));
2106 e.emit_const(Value::float(1.0));
2107 e.emit_op(Op::Eq);
2108 e.emit_op(Op::Return);
2109 let func = Rc::new(crate::chunk::Function {
2110 name: None,
2111 chunk: e.into_chunk(),
2112 upvalue_descs: vec![],
2113 arity: 0,
2114 has_rest: false,
2115 local_names: vec![],
2116 });
2117 let closure = Rc::new(Closure {
2118 func,
2119 upvalues: vec![],
2120 });
2121 let mut vm = VM::new(globals, vec![]);
2122 let result = vm.execute(closure, &ctx).unwrap();
2123 assert_eq!(
2124 result,
2125 Value::bool(true),
2126 "Op::Eq should coerce int 1 == float 1.0"
2127 );
2128 }
2129
2130 #[test]
2131 fn test_vm_div_opcode_direct() {
2132 use crate::emit::Emitter;
2133 use crate::opcodes::Op;
2134 let globals = Rc::new(Env::new());
2135 let ctx = EvalContext::new();
2136 let mut e = Emitter::new();
2137 e.emit_const(Value::int(3));
2138 e.emit_const(Value::int(2));
2139 e.emit_op(Op::Div);
2140 e.emit_op(Op::Return);
2141 let func = Rc::new(crate::chunk::Function {
2142 name: None,
2143 chunk: e.into_chunk(),
2144 upvalue_descs: vec![],
2145 arity: 0,
2146 has_rest: false,
2147 local_names: vec![],
2148 });
2149 let closure = Rc::new(Closure {
2150 func,
2151 upvalues: vec![],
2152 });
2153 let mut vm = VM::new(globals, vec![]);
2154 let result = vm.execute(closure, &ctx).unwrap();
2155 assert_eq!(
2156 result,
2157 Value::float(1.5),
2158 "Op::Div 3/2 should return 1.5, not 1"
2159 );
2160 }
2161}