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[args_start..].to_vec();
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[args_start..].to_vec();
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[args_start..].to_vec();
927 self.stack.truncate(args_start);
928 let result = (func_rc.func)(ctx, &args)?;
929 self.stack.push(result);
930 Ok(())
931 } else if let Some(kw) = func_val.as_keyword_spur() {
932 if argc != 1 {
933 return Err(SemaError::arity(resolve_spur(kw), "1", argc));
934 }
935 let arg = self.stack.pop().unwrap();
936 let kw_val = Value::keyword_from_spur(kw);
937 let result = if let Some(m) = arg.as_map_rc() {
938 m.get(&kw_val).cloned().unwrap_or(Value::nil())
939 } else if let Some(m) = arg.as_hashmap_rc() {
940 m.get(&kw_val).cloned().unwrap_or(Value::nil())
941 } else {
942 return Err(SemaError::type_error("map or hashmap", arg.type_name()));
943 };
944 self.stack.push(result);
945 Ok(())
946 } else {
947 let args_start = self.stack.len() - argc;
948 let args: Vec<Value> = self.stack[args_start..].to_vec();
949 self.stack.truncate(args_start);
950 let result = sema_core::call_callback(ctx, &func_val, &args)?;
951 self.stack.push(result);
952 Ok(())
953 }
954 }
955
956 fn call_vm_closure_direct(
960 &mut self,
961 closure: Rc<Closure>,
962 argc: usize,
963 ) -> Result<(), SemaError> {
964 let func = &closure.func;
965 let arity = func.arity as usize;
966 let has_rest = func.has_rest;
967 let n_locals = func.chunk.n_locals as usize;
968
969 if has_rest {
971 if argc < arity {
972 return Err(SemaError::arity(
973 func.name
974 .map(resolve_spur)
975 .unwrap_or_else(|| "<lambda>".to_string()),
976 format!("{}+", arity),
977 argc,
978 ));
979 }
980 } else if argc != arity {
981 return Err(SemaError::arity(
982 func.name
983 .map(resolve_spur)
984 .unwrap_or_else(|| "<lambda>".to_string()),
985 arity.to_string(),
986 argc,
987 ));
988 }
989
990 let base = self.stack.len() - argc;
993
994 if has_rest {
995 let rest: Vec<Value> = self.stack[base + arity..base + argc].to_vec();
997 self.stack.truncate(base + arity);
998 self.stack.push(Value::list(rest));
999 }
1000
1001 self.stack.resize(base + n_locals, Value::nil());
1003
1004 self.frames.push(CallFrame {
1005 closure,
1006 pc: 0,
1007 base,
1008 open_upvalues: None,
1009 });
1010
1011 Ok(())
1012 }
1013
1014 fn call_vm_closure(&mut self, closure: Rc<Closure>, argc: usize) -> Result<(), SemaError> {
1018 let func = &closure.func;
1019 let arity = func.arity as usize;
1020 let has_rest = func.has_rest;
1021 let n_locals = func.chunk.n_locals as usize;
1022
1023 if has_rest {
1025 if argc < arity {
1026 return Err(SemaError::arity(
1027 func.name
1028 .map(resolve_spur)
1029 .unwrap_or_else(|| "<lambda>".to_string()),
1030 format!("{}+", arity),
1031 argc,
1032 ));
1033 }
1034 } else if argc != arity {
1035 return Err(SemaError::arity(
1036 func.name
1037 .map(resolve_spur)
1038 .unwrap_or_else(|| "<lambda>".to_string()),
1039 arity.to_string(),
1040 argc,
1041 ));
1042 }
1043
1044 let func_idx = self.stack.len() - 1 - argc;
1046 let base = func_idx; Self::copy_args_to_locals(&mut self.stack, base, func_idx + 1, arity, argc, has_rest);
1051
1052 self.stack.resize(base + n_locals, Value::nil());
1054
1055 self.frames.push(CallFrame {
1057 closure,
1058 pc: 0,
1059 base,
1060 open_upvalues: None,
1061 });
1062
1063 Ok(())
1064 }
1065
1066 fn tail_call_vm_closure(&mut self, closure: Rc<Closure>, argc: usize) -> Result<(), SemaError> {
1070 let func = &closure.func;
1071 let arity = func.arity as usize;
1072 let has_rest = func.has_rest;
1073 let n_locals = func.chunk.n_locals as usize;
1074
1075 if has_rest {
1077 if argc < arity {
1078 return Err(SemaError::arity(
1079 func.name
1080 .map(resolve_spur)
1081 .unwrap_or_else(|| "<lambda>".to_string()),
1082 format!("{}+", arity),
1083 argc,
1084 ));
1085 }
1086 } else if argc != arity {
1087 return Err(SemaError::arity(
1088 func.name
1089 .map(resolve_spur)
1090 .unwrap_or_else(|| "<lambda>".to_string()),
1091 arity.to_string(),
1092 argc,
1093 ));
1094 }
1095
1096 let func_idx = self.stack.len() - 1 - argc;
1098 let base = self.frames.last().unwrap().base;
1099
1100 Self::copy_args_to_locals(&mut self.stack, base, func_idx + 1, arity, argc, has_rest);
1102
1103 self.stack.resize(base + n_locals, Value::nil());
1105
1106 let frame = self.frames.last_mut().unwrap();
1108 frame.closure = closure;
1109 frame.pc = 0;
1110 frame.open_upvalues = None;
1112
1113 Ok(())
1114 }
1115
1116 #[inline(always)]
1119 fn copy_args_to_locals(
1120 stack: &mut [Value],
1121 dst: usize,
1122 src: usize,
1123 arity: usize,
1124 argc: usize,
1125 has_rest: bool,
1126 ) {
1127 if has_rest {
1128 let rest: Vec<Value> = stack[src + arity..src + argc].to_vec();
1129 for i in 0..arity {
1130 stack[dst + i] = stack[src + i].clone();
1131 }
1132 stack[dst + arity] = Value::list(rest);
1133 } else {
1134 for i in 0..arity {
1135 stack[dst + i] = stack[src + i].clone();
1136 }
1137 }
1138 }
1139
1140 fn make_closure(&mut self) -> Result<(), SemaError> {
1143 let frame = self.frames.last().unwrap();
1146 let code = &frame.closure.func.chunk.code;
1147 let pc = frame.pc + 1;
1148 let func_id = u16::from_le_bytes([code[pc], code[pc + 1]]) as usize;
1149 let n_upvalues = u16::from_le_bytes([code[pc + 2], code[pc + 3]]) as usize;
1150
1151 let mut uv_descs = Vec::with_capacity(n_upvalues);
1153 let mut uv_pc = pc + 4;
1154 for _ in 0..n_upvalues {
1155 let is_local = u16::from_le_bytes([code[uv_pc], code[uv_pc + 1]]);
1156 let idx = u16::from_le_bytes([code[uv_pc + 2], code[uv_pc + 3]]) as usize;
1157 uv_pc += 4;
1158 uv_descs.push((is_local != 0, idx));
1159 }
1160
1161 let base = frame.base;
1162 let parent_upvalues = frame.closure.upvalues.clone();
1163 let _ = frame;
1165
1166 let func = self.functions[func_id].clone();
1167 let mut upvalues = Vec::with_capacity(n_upvalues);
1168
1169 for (is_local, idx) in &uv_descs {
1170 if *is_local {
1171 let frame = self.frames.last_mut().unwrap();
1174 let n_locals = frame.closure.func.chunk.n_locals as usize;
1175 let open = frame
1176 .open_upvalues
1177 .get_or_insert_with(|| vec![None; n_locals]);
1178 let cell = if let Some(existing) = &open[*idx] {
1179 existing.clone()
1180 } else {
1181 let val = self.stack[base + *idx].clone();
1182 let cell = Rc::new(UpvalueCell::new(val));
1183 open[*idx] = Some(cell.clone());
1184 cell
1185 };
1186 upvalues.push(cell);
1187 } else {
1188 upvalues.push(parent_upvalues[*idx].clone());
1190 }
1191 }
1192
1193 self.frames.last_mut().unwrap().pc = uv_pc;
1195
1196 let closure = Rc::new(Closure { func, upvalues });
1197 let payload: Rc<dyn std::any::Any> = Rc::new(VmClosurePayload {
1198 closure: closure.clone(),
1199 functions: self.functions.clone(),
1200 });
1201 let closure_for_fallback = closure.clone();
1202 let functions = self.functions.clone();
1203 let globals = self.globals.clone();
1204
1205 let native = Value::native_fn_from_rc(Rc::new(sema_core::NativeFn::with_payload(
1209 closure_for_fallback
1210 .func
1211 .name
1212 .map(resolve_spur)
1213 .unwrap_or_else(|| "<vm-closure>".to_string()),
1214 payload,
1215 move |ctx, args| {
1216 let mut vm = VM::new_with_rc_functions(globals.clone(), functions.clone());
1217 let func = &closure_for_fallback.func;
1218 let arity = func.arity as usize;
1219 let has_rest = func.has_rest;
1220 let n_locals = func.chunk.n_locals as usize;
1221
1222 if has_rest {
1223 if args.len() < arity {
1224 return Err(SemaError::arity(
1225 func.name
1226 .map(resolve_spur)
1227 .unwrap_or_else(|| "<lambda>".to_string()),
1228 format!("{}+", arity),
1229 args.len(),
1230 ));
1231 }
1232 } else if args.len() != arity {
1233 return Err(SemaError::arity(
1234 func.name
1235 .map(resolve_spur)
1236 .unwrap_or_else(|| "<lambda>".to_string()),
1237 arity.to_string(),
1238 args.len(),
1239 ));
1240 }
1241
1242 vm.stack.resize(n_locals, Value::nil());
1243
1244 if has_rest {
1245 for i in 0..arity {
1246 vm.stack[i] = args.get(i).cloned().unwrap_or(Value::nil());
1247 }
1248 let rest: Vec<Value> = args[arity..].to_vec();
1249 vm.stack[arity] = Value::list(rest);
1250 } else {
1251 for i in 0..arity {
1252 vm.stack[i] = args.get(i).cloned().unwrap_or(Value::nil());
1253 }
1254 }
1255
1256 vm.frames.push(CallFrame {
1257 closure: closure_for_fallback.clone(),
1258 pc: 0,
1259 base: 0,
1260 open_upvalues: None,
1261 });
1262 vm.run(ctx)
1263 },
1264 )));
1265
1266 self.stack.push(native);
1267 Ok(())
1268 }
1269
1270 #[cold]
1273 #[inline(never)]
1274 fn handle_exception(
1275 &mut self,
1276 err: SemaError,
1277 failing_pc: usize,
1278 ) -> Result<ExceptionAction, SemaError> {
1279 let mut pc_for_lookup = failing_pc as u32;
1280 while let Some(frame) = self.frames.last() {
1282 let chunk = &frame.closure.func.chunk;
1283
1284 let mut found = None;
1286 for entry in &chunk.exception_table {
1287 if pc_for_lookup >= entry.try_start && pc_for_lookup < entry.try_end {
1288 found = Some(entry.clone());
1289 break;
1290 }
1291 }
1292
1293 if let Some(entry) = found {
1294 let base = frame.base;
1296 self.stack.truncate(base + entry.stack_depth as usize);
1297
1298 let error_val = error_to_value(&err);
1300 self.stack.push(error_val);
1301
1302 let frame = self.frames.last_mut().unwrap();
1304 frame.pc = entry.handler_pc as usize;
1305 return Ok(ExceptionAction::Handled);
1306 }
1307
1308 let frame = self.frames.pop().unwrap();
1310 self.stack.truncate(frame.base);
1311 if let Some(parent) = self.frames.last() {
1317 pc_for_lookup = parent.pc.saturating_sub(1) as u32;
1318 }
1319 }
1320
1321 Ok(ExceptionAction::Propagate(err))
1323 }
1324}
1325
1326enum ExceptionAction {
1327 Handled,
1328 Propagate(SemaError),
1329}
1330
1331fn error_to_value(err: &SemaError) -> Value {
1333 let inner = err.inner();
1334 let mut map = BTreeMap::new();
1335 match inner {
1336 SemaError::Eval(msg) => {
1337 map.insert(Value::keyword("type"), Value::keyword("eval"));
1338 map.insert(Value::keyword("message"), Value::string(msg));
1339 }
1340 SemaError::Type { expected, got } => {
1341 map.insert(Value::keyword("type"), Value::keyword("type-error"));
1342 map.insert(
1343 Value::keyword("message"),
1344 Value::string(&format!("expected {expected}, got {got}")),
1345 );
1346 map.insert(Value::keyword("expected"), Value::string(expected));
1347 map.insert(Value::keyword("got"), Value::string(got));
1348 }
1349 SemaError::Arity {
1350 name,
1351 expected,
1352 got,
1353 } => {
1354 map.insert(Value::keyword("type"), Value::keyword("arity"));
1355 map.insert(
1356 Value::keyword("message"),
1357 Value::string(&format!("{name} expects {expected} args, got {got}")),
1358 );
1359 }
1360 SemaError::Unbound(name) => {
1361 map.insert(Value::keyword("type"), Value::keyword("unbound"));
1362 map.insert(
1363 Value::keyword("message"),
1364 Value::string(&format!("Unbound variable: {name}")),
1365 );
1366 map.insert(Value::keyword("name"), Value::string(name));
1367 }
1368 SemaError::UserException(val) => {
1369 map.insert(Value::keyword("type"), Value::keyword("user"));
1370 map.insert(Value::keyword("message"), Value::string(&val.to_string()));
1371 map.insert(Value::keyword("value"), val.clone());
1372 }
1373 SemaError::Io(msg) => {
1374 map.insert(Value::keyword("type"), Value::keyword("io"));
1375 map.insert(Value::keyword("message"), Value::string(msg));
1376 }
1377 SemaError::Llm(msg) => {
1378 map.insert(Value::keyword("type"), Value::keyword("llm"));
1379 map.insert(Value::keyword("message"), Value::string(msg));
1380 }
1381 SemaError::Reader { message, span } => {
1382 map.insert(Value::keyword("type"), Value::keyword("reader"));
1383 map.insert(
1384 Value::keyword("message"),
1385 Value::string(&format!("{message} at {span}")),
1386 );
1387 }
1388 SemaError::PermissionDenied {
1389 function,
1390 capability,
1391 } => {
1392 map.insert(Value::keyword("type"), Value::keyword("permission-denied"));
1393 map.insert(
1394 Value::keyword("message"),
1395 Value::string(&format!(
1396 "Permission denied: {function} requires '{capability}' capability"
1397 )),
1398 );
1399 map.insert(Value::keyword("function"), Value::string(function));
1400 map.insert(Value::keyword("capability"), Value::string(capability));
1401 }
1402 SemaError::PathDenied { function, path } => {
1403 map.insert(Value::keyword("type"), Value::keyword("permission-denied"));
1404 map.insert(
1405 Value::keyword("message"),
1406 Value::string(&format!(
1407 "Permission denied: {function} — path '{path}' is outside allowed directories"
1408 )),
1409 );
1410 map.insert(Value::keyword("function"), Value::string(function));
1411 map.insert(Value::keyword("path"), Value::string(path));
1412 }
1413 SemaError::WithTrace { .. } | SemaError::WithContext { .. } => {
1414 unreachable!("inner() already unwraps these")
1415 }
1416 }
1417 Value::map(map)
1418}
1419
1420#[inline(always)]
1423fn vm_add(a: &Value, b: &Value) -> Result<Value, SemaError> {
1424 use sema_core::ValueView;
1425 match (a.view(), b.view()) {
1426 (ValueView::Int(x), ValueView::Int(y)) => Ok(Value::int(x.wrapping_add(y))),
1427 (ValueView::Float(x), ValueView::Float(y)) => Ok(Value::float(x + y)),
1428 (ValueView::Int(x), ValueView::Float(y)) => Ok(Value::float(x as f64 + y)),
1429 (ValueView::Float(x), ValueView::Int(y)) => Ok(Value::float(x + y as f64)),
1430 (ValueView::String(x), ValueView::String(y)) => {
1431 let mut s = (*x).clone();
1432 s.push_str(&y);
1433 Ok(Value::string(&s))
1434 }
1435 _ => Err(SemaError::type_error(
1436 "number or string",
1437 format!("{} and {}", a.type_name(), b.type_name()),
1438 )),
1439 }
1440}
1441
1442#[inline(always)]
1443fn vm_sub(a: &Value, b: &Value) -> Result<Value, SemaError> {
1444 use sema_core::ValueView;
1445 match (a.view(), b.view()) {
1446 (ValueView::Int(x), ValueView::Int(y)) => Ok(Value::int(x.wrapping_sub(y))),
1447 (ValueView::Float(x), ValueView::Float(y)) => Ok(Value::float(x - y)),
1448 (ValueView::Int(x), ValueView::Float(y)) => Ok(Value::float(x as f64 - y)),
1449 (ValueView::Float(x), ValueView::Int(y)) => Ok(Value::float(x - y as f64)),
1450 _ => Err(SemaError::type_error(
1451 "number",
1452 format!("{} and {}", a.type_name(), b.type_name()),
1453 )),
1454 }
1455}
1456
1457#[inline(always)]
1458fn vm_mul(a: &Value, b: &Value) -> Result<Value, SemaError> {
1459 use sema_core::ValueView;
1460 match (a.view(), b.view()) {
1461 (ValueView::Int(x), ValueView::Int(y)) => Ok(Value::int(x.wrapping_mul(y))),
1462 (ValueView::Float(x), ValueView::Float(y)) => Ok(Value::float(x * y)),
1463 (ValueView::Int(x), ValueView::Float(y)) => Ok(Value::float(x as f64 * y)),
1464 (ValueView::Float(x), ValueView::Int(y)) => Ok(Value::float(x * y as f64)),
1465 _ => Err(SemaError::type_error(
1466 "number",
1467 format!("{} and {}", a.type_name(), b.type_name()),
1468 )),
1469 }
1470}
1471
1472#[inline(always)]
1473fn vm_div(a: &Value, b: &Value) -> Result<Value, SemaError> {
1474 use sema_core::ValueView;
1475 match (a.view(), b.view()) {
1476 (ValueView::Int(_), ValueView::Int(0)) => Err(SemaError::eval("division by zero")),
1477 (ValueView::Int(x), ValueView::Int(y)) => {
1478 let result = x as f64 / y as f64;
1479 if result.fract() == 0.0 {
1480 Ok(Value::int(result as i64))
1481 } else {
1482 Ok(Value::float(result))
1483 }
1484 }
1485 (ValueView::Float(x), ValueView::Float(y)) => Ok(Value::float(x / y)),
1486 (ValueView::Int(x), ValueView::Float(y)) => Ok(Value::float(x as f64 / y)),
1487 (ValueView::Float(x), ValueView::Int(y)) => Ok(Value::float(x / y as f64)),
1488 _ => Err(SemaError::type_error(
1489 "number",
1490 format!("{} and {}", a.type_name(), b.type_name()),
1491 )),
1492 }
1493}
1494
1495#[inline(always)]
1497fn vm_eq(a: &Value, b: &Value) -> bool {
1498 use sema_core::ValueView;
1499 match (a.view(), b.view()) {
1500 (ValueView::Int(x), ValueView::Int(y)) => x == y,
1501 (ValueView::Float(x), ValueView::Float(y)) => x == y,
1502 (ValueView::Int(x), ValueView::Float(y)) | (ValueView::Float(y), ValueView::Int(x)) => {
1503 (x as f64) == y
1504 }
1505 _ => a == b,
1506 }
1507}
1508
1509fn vm_lt(a: &Value, b: &Value) -> Result<bool, SemaError> {
1510 use sema_core::ValueView;
1511 match (a.view(), b.view()) {
1512 (ValueView::Int(x), ValueView::Int(y)) => Ok(x < y),
1513 (ValueView::Float(x), ValueView::Float(y)) => Ok(x < y),
1514 (ValueView::Int(x), ValueView::Float(y)) => Ok((x as f64) < y),
1515 (ValueView::Float(x), ValueView::Int(y)) => Ok(x < (y as f64)),
1516 (ValueView::String(x), ValueView::String(y)) => Ok(x < y),
1517 _ => Err(SemaError::type_error(
1518 "comparable values",
1519 format!("{} and {}", a.type_name(), b.type_name()),
1520 )),
1521 }
1522}
1523
1524pub fn compile_program(vals: &[Value]) -> Result<(Rc<Closure>, Vec<Rc<Function>>), SemaError> {
1527 let mut resolved = Vec::new();
1528 let mut total_locals: u16 = 0;
1529 for val in vals {
1530 let core = crate::lower::lower(val)?;
1531 let (res, n) = crate::resolve::resolve_with_locals(&core)?;
1532 total_locals = total_locals.max(n);
1533 resolved.push(res);
1534 }
1535 let result = crate::compiler::compile_many_with_locals(&resolved, total_locals)?;
1536
1537 let functions: Vec<Rc<Function>> = result.functions.into_iter().map(Rc::new).collect();
1538 let closure = Rc::new(Closure {
1539 func: Rc::new(Function {
1540 name: None,
1541 chunk: result.chunk,
1542 upvalue_descs: Vec::new(),
1543 arity: 0,
1544 has_rest: false,
1545 local_names: Vec::new(),
1546 }),
1547 upvalues: Vec::new(),
1548 });
1549
1550 Ok((closure, functions))
1551}
1552
1553pub fn eval_str(input: &str, globals: &Rc<Env>, ctx: &EvalContext) -> Result<Value, SemaError> {
1555 let vals =
1556 sema_reader::read_many(input).map_err(|e| SemaError::eval(format!("parse error: {e}")))?;
1557 let (closure, functions) = compile_program(&vals)?;
1558 let mut vm = VM::new(globals.clone(), functions);
1559 vm.execute(closure, ctx)
1560}
1561
1562#[cfg(test)]
1563mod tests {
1564 use super::*;
1565 use sema_core::{intern, NativeFn};
1566
1567 fn make_test_env() -> Rc<Env> {
1568 let env = Rc::new(Env::new());
1569 env.set(
1570 intern("+"),
1571 Value::native_fn(NativeFn::simple("+", |args| vm_add(&args[0], &args[1]))),
1572 );
1573 env.set(
1574 intern("-"),
1575 Value::native_fn(NativeFn::simple("-", |args| vm_sub(&args[0], &args[1]))),
1576 );
1577 env.set(
1578 intern("*"),
1579 Value::native_fn(NativeFn::simple("*", |args| vm_mul(&args[0], &args[1]))),
1580 );
1581 env.set(
1582 intern("/"),
1583 Value::native_fn(NativeFn::simple("/", |args| vm_div(&args[0], &args[1]))),
1584 );
1585 env.set(
1586 intern("="),
1587 Value::native_fn(NativeFn::simple("=", |args| {
1588 Ok(Value::bool(vm_eq(&args[0], &args[1])))
1589 })),
1590 );
1591 env.set(
1592 intern("<"),
1593 Value::native_fn(NativeFn::simple("<", |args| {
1594 Ok(Value::bool(vm_lt(&args[0], &args[1])?))
1595 })),
1596 );
1597 env.set(
1598 intern(">"),
1599 Value::native_fn(NativeFn::simple(">", |args| {
1600 Ok(Value::bool(vm_lt(&args[1], &args[0])?))
1601 })),
1602 );
1603 env.set(
1604 intern("not"),
1605 Value::native_fn(NativeFn::simple("not", |args| {
1606 Ok(Value::bool(!args[0].is_truthy()))
1607 })),
1608 );
1609 env.set(
1610 intern("list"),
1611 Value::native_fn(NativeFn::simple("list", |args| {
1612 Ok(Value::list(args.to_vec()))
1613 })),
1614 );
1615 env
1616 }
1617
1618 fn eval(input: &str) -> Result<Value, SemaError> {
1619 let globals = make_test_env();
1620 let ctx = EvalContext::new();
1621 eval_str(input, &globals, &ctx)
1622 }
1623
1624 #[test]
1625 fn test_vm_int_literal() {
1626 assert_eq!(eval("42").unwrap(), Value::int(42));
1627 }
1628
1629 #[test]
1630 fn test_vm_nil() {
1631 assert_eq!(eval("nil").unwrap(), Value::nil());
1632 }
1633
1634 #[test]
1635 fn test_vm_bool() {
1636 assert_eq!(eval("#t").unwrap(), Value::bool(true));
1637 assert_eq!(eval("#f").unwrap(), Value::bool(false));
1638 }
1639
1640 #[test]
1641 fn test_vm_string() {
1642 assert_eq!(eval("\"hello\"").unwrap(), Value::string("hello"));
1643 }
1644
1645 #[test]
1646 fn test_vm_if_true() {
1647 assert_eq!(eval("(if #t 42 99)").unwrap(), Value::int(42));
1648 }
1649
1650 #[test]
1651 fn test_vm_if_false() {
1652 assert_eq!(eval("(if #f 42 99)").unwrap(), Value::int(99));
1653 }
1654
1655 #[test]
1656 fn test_vm_begin() {
1657 assert_eq!(eval("(begin 1 2 3)").unwrap(), Value::int(3));
1658 }
1659
1660 #[test]
1661 fn test_vm_define_and_load() {
1662 let globals = make_test_env();
1663 let ctx = EvalContext::new();
1664 eval_str("(define x 42)", &globals, &ctx).unwrap();
1665 let result = eval_str("x", &globals, &ctx).unwrap();
1666 assert_eq!(result, Value::int(42));
1667 }
1668
1669 #[test]
1670 fn test_vm_let() {
1671 assert_eq!(eval("(let ((x 10)) x)").unwrap(), Value::int(10));
1672 }
1673
1674 #[test]
1675 fn test_vm_let_multiple() {
1676 assert_eq!(eval("(let ((x 10) (y 20)) y)").unwrap(), Value::int(20));
1677 }
1678
1679 #[test]
1680 fn test_vm_nested_if() {
1681 assert_eq!(eval("(if (if #t #f #t) 1 2)").unwrap(), Value::int(2));
1682 }
1683
1684 #[test]
1685 fn test_vm_lambda_call() {
1686 assert_eq!(eval("((lambda (x) x) 42)").unwrap(), Value::int(42));
1687 }
1688
1689 #[test]
1690 fn test_vm_lambda_two_args() {
1691 assert_eq!(eval("((lambda (x y) y) 1 2)").unwrap(), Value::int(2));
1692 }
1693
1694 #[test]
1695 fn test_vm_closure_capture() {
1696 assert_eq!(
1697 eval("(let ((x 10)) ((lambda () x)))").unwrap(),
1698 Value::int(10)
1699 );
1700 }
1701
1702 #[test]
1703 fn test_vm_list_literal() {
1704 let result = eval("(list 1 2 3)").unwrap();
1705 let items = result.as_list().expect("Expected list");
1706 assert_eq!(items.len(), 3);
1707 assert_eq!(items[0], Value::int(1));
1708 }
1709
1710 #[test]
1711 fn test_vm_make_vector() {
1712 let result = eval("[1 2 3]").unwrap();
1713 let items = result.as_vector().expect("Expected vector");
1714 assert_eq!(items.len(), 3);
1715 }
1716
1717 #[test]
1718 fn test_vm_and_short_circuit() {
1719 assert_eq!(eval("(and #f 42)").unwrap(), Value::bool(false));
1720 assert_eq!(eval("(and #t 42)").unwrap(), Value::int(42));
1721 }
1722
1723 #[test]
1724 fn test_vm_or_short_circuit() {
1725 assert_eq!(eval("(or 42 99)").unwrap(), Value::int(42));
1726 assert_eq!(eval("(or #f 99)").unwrap(), Value::int(99));
1727 }
1728
1729 #[test]
1730 fn test_vm_throw_catch() {
1731 let result = eval("(try (throw \"boom\") (catch e (:value e)))").unwrap();
1733 assert_eq!(result, Value::string("boom"));
1734 }
1735
1736 #[test]
1737 fn test_vm_throw_catch_type() {
1738 let result = eval("(try (throw \"boom\") (catch e (:type e)))").unwrap();
1739 assert_eq!(result, Value::keyword("user"));
1740 }
1741
1742 #[test]
1743 fn test_vm_try_no_throw() {
1744 assert_eq!(eval("(try 42 (catch e 99))").unwrap(), Value::int(42));
1745 }
1746
1747 #[test]
1748 fn test_vm_try_catch_native_error() {
1749 let result = eval("(try (/ 1 0) (catch e \"caught\"))").unwrap();
1751 assert_eq!(result, Value::string("caught"));
1752 }
1753
1754 #[test]
1755 fn test_vm_try_catch_native_error_message() {
1756 let result = eval("(try (/ 1 0) (catch e (:message e)))").unwrap();
1757 let s = result.as_str().expect("Expected string");
1758 assert!(s.contains("division by zero"), "got: {s}");
1759 }
1760
1761 #[test]
1762 fn test_vm_try_catch_type_error() {
1763 let result = eval("(try (+ 1 \"a\") (catch e (:type e)))").unwrap();
1764 assert_eq!(result, Value::keyword("type-error"));
1765 }
1766
1767 #[test]
1768 fn test_vm_try_catch_from_closure_call() {
1769 let globals = make_test_env();
1771 let ctx = EvalContext::new();
1772 eval_str("(define (thrower) (throw \"boom\"))", &globals, &ctx).unwrap();
1773 let result = eval_str("(try (thrower) (catch e \"caught\"))", &globals, &ctx).unwrap();
1774 assert_eq!(result, Value::string("caught"));
1775 }
1776
1777 #[test]
1778 fn test_vm_try_catch_from_lambda_call() {
1779 let result = eval("(try ((fn () (throw 42))) (catch e (:value e)))").unwrap();
1781 assert_eq!(result, Value::int(42));
1782 }
1783
1784 #[test]
1785 fn test_vm_try_catch_nested_call() {
1786 let globals = make_test_env();
1788 let ctx = EvalContext::new();
1789 eval_str("(define (inner) (throw \"deep\"))", &globals, &ctx).unwrap();
1790 eval_str("(define (outer) (inner))", &globals, &ctx).unwrap();
1791 let result = eval_str("(try (outer) (catch e \"caught\"))", &globals, &ctx).unwrap();
1792 assert_eq!(result, Value::string("caught"));
1793 }
1794
1795 #[test]
1796 fn test_vm_try_catch_in_call_arg() {
1797 let globals = make_test_env();
1799 let ctx = EvalContext::new();
1800 eval_str("(define (thrower) (throw \"boom\"))", &globals, &ctx).unwrap();
1801 let result = eval_str("(+ 1 (try (thrower) (catch e 2)))", &globals, &ctx).unwrap();
1803 assert_eq!(result, Value::int(3));
1804 }
1805
1806 #[test]
1807 fn test_vm_try_catch_in_list_constructor() {
1808 let globals = make_test_env();
1810 let ctx = EvalContext::new();
1811 eval_str("(define (thrower) (throw \"boom\"))", &globals, &ctx).unwrap();
1812 let result = eval_str("(list 1 2 (try (thrower) (catch e 3)) 4)", &globals, &ctx).unwrap();
1813 let items = result.as_list().expect("list");
1814 assert_eq!(items.len(), 4);
1815 assert_eq!(items[2], Value::int(3));
1816 }
1817
1818 #[test]
1819 fn test_vm_try_catch_call_not_last() {
1820 let globals = make_test_env();
1822 let ctx = EvalContext::new();
1823 eval_str("(define (thrower) (throw \"boom\"))", &globals, &ctx).unwrap();
1824 let result = eval_str(
1825 "(try (begin (thrower) 123) (catch e \"caught\"))",
1826 &globals,
1827 &ctx,
1828 )
1829 .unwrap();
1830 assert_eq!(result, Value::string("caught"));
1831 }
1832
1833 #[test]
1834 fn test_vm_quote() {
1835 let result = eval("'(a b c)").unwrap();
1836 let items = result.as_list().expect("Expected list");
1837 assert_eq!(items.len(), 3);
1838 }
1839
1840 #[test]
1841 fn test_vm_set() {
1842 let globals = make_test_env();
1843 let ctx = EvalContext::new();
1844 eval_str("(define x 1)", &globals, &ctx).unwrap();
1845 eval_str("(set! x 42)", &globals, &ctx).unwrap();
1846 let result = eval_str("x", &globals, &ctx).unwrap();
1847 assert_eq!(result, Value::int(42));
1848 }
1849
1850 #[test]
1851 fn test_vm_recursive_define() {
1852 let globals = make_test_env();
1853 let ctx = EvalContext::new();
1854 eval_str(
1855 "(define (fact n) (if (= n 0) 1 (* n (fact (- n 1)))))",
1856 &globals,
1857 &ctx,
1858 )
1859 .unwrap();
1860 let result = eval_str("(fact 5)", &globals, &ctx).unwrap();
1861 assert_eq!(result, Value::int(120));
1862 }
1863
1864 #[test]
1865 fn test_vm_do_loop() {
1866 let result = eval("(do ((i 0 (+ i 1))) ((= i 5) i))").unwrap();
1867 assert_eq!(result, Value::int(5));
1868 }
1869
1870 #[test]
1871 fn test_vm_named_let() {
1872 let result =
1873 eval("(let loop ((n 5) (acc 1)) (if (= n 0) acc (loop (- n 1) (* acc n))))").unwrap();
1874 assert_eq!(result, Value::int(120));
1875 }
1876
1877 #[test]
1878 fn test_vm_letrec() {
1879 let globals = make_test_env();
1880 let ctx = EvalContext::new();
1881 let result = eval_str(
1882 "(letrec ((even? (lambda (n) (if (= n 0) #t (odd? (- n 1))))) (odd? (lambda (n) (if (= n 0) #f (even? (- n 1)))))) (even? 4))",
1883 &globals,
1884 &ctx,
1885 ).unwrap();
1886 assert_eq!(result, Value::bool(true));
1887 }
1888
1889 #[test]
1890 fn test_vm_rest_params() {
1891 let result = eval("((lambda (x . rest) rest) 1 2 3)").unwrap();
1892 let items = result.as_list().expect("Expected list");
1893 assert_eq!(items.len(), 2);
1894 assert_eq!(items[0], Value::int(2));
1895 assert_eq!(items[1], Value::int(3));
1896 }
1897
1898 #[test]
1901 fn test_vm_counter_closure() {
1902 let result =
1904 eval("(let ((n 0)) (let ((inc (lambda () (set! n (+ n 1)) n))) (inc) (inc) (inc)))")
1905 .unwrap();
1906 assert_eq!(result, Value::int(3));
1907 }
1908
1909 #[test]
1910 fn test_vm_shared_mutable_upvalue() {
1911 let result = eval(
1913 "(let ((n 0)) (let ((inc (lambda () (set! n (+ n 1)))) (get (lambda () n))) (inc) (inc) (get)))",
1914 )
1915 .unwrap();
1916 assert_eq!(result, Value::int(2));
1917 }
1918
1919 #[test]
1920 fn test_vm_set_local_in_let() {
1921 let result = eval("(let ((x 1)) (set! x 42) x)").unwrap();
1923 assert_eq!(result, Value::int(42));
1924 }
1925
1926 #[test]
1927 fn test_vm_closure_captures_after_mutation() {
1928 let result = eval("(let ((x 1)) (set! x 10) ((lambda () x)))").unwrap();
1930 assert_eq!(result, Value::int(10));
1931 }
1932
1933 #[test]
1934 fn test_vm_closure_returns_closure() {
1935 let result = eval("(let ((f (lambda () (lambda (x) x)))) ((f) 42))").unwrap();
1937 assert_eq!(result, Value::int(42));
1938 }
1939
1940 #[test]
1941 fn test_vm_make_adder() {
1942 let globals = make_test_env();
1944 let ctx = EvalContext::new();
1945 eval_str(
1946 "(define (make-adder n) (lambda (x) (+ n x)))",
1947 &globals,
1948 &ctx,
1949 )
1950 .unwrap();
1951 eval_str("(define add5 (make-adder 5))", &globals, &ctx).unwrap();
1952 let result = eval_str("(add5 3)", &globals, &ctx).unwrap();
1953 assert_eq!(result, Value::int(8));
1954 }
1955
1956 #[test]
1957 fn test_vm_compose() {
1958 let globals = make_test_env();
1960 let ctx = EvalContext::new();
1961 eval_str(
1962 "(define (compose f g) (lambda (x) (f (g x))))",
1963 &globals,
1964 &ctx,
1965 )
1966 .unwrap();
1967 eval_str("(define inc (lambda (x) (+ x 1)))", &globals, &ctx).unwrap();
1968 eval_str("(define dbl (lambda (x) (* x 2)))", &globals, &ctx).unwrap();
1969 let result = eval_str("((compose dbl inc) 5)", &globals, &ctx).unwrap();
1970 assert_eq!(result, Value::int(12));
1971 }
1972
1973 #[test]
1974 fn test_vm_nested_make_closure() {
1975 let result = eval("((((lambda () (lambda () (lambda () 42))))))").unwrap();
1977 assert_eq!(result, Value::int(42));
1978 }
1979
1980 #[test]
1981 fn test_vm_named_fn_rest_params() {
1982 let globals = make_test_env();
1983 let ctx = EvalContext::new();
1984 eval_str("(define (f . args) args)", &globals, &ctx).unwrap();
1985 let result = eval_str("(f 1 2 3)", &globals, &ctx).unwrap();
1986 let items = result.as_list().expect("Expected list");
1987 assert_eq!(items.len(), 3);
1988 assert_eq!(items[0], Value::int(1));
1989 }
1990
1991 #[test]
1992 fn test_vm_named_let_still_works_with_fix() {
1993 let globals = make_test_env();
1994 let ctx = EvalContext::new();
1995 let result = eval_str(
1996 "(let loop ((n 5) (acc 1)) (if (= n 0) acc (loop (- n 1) (* acc n))))",
1997 &globals,
1998 &ctx,
1999 )
2000 .unwrap();
2001 assert_eq!(result, Value::int(120));
2002 }
2003
2004 #[test]
2005 fn test_vm_curry() {
2006 let globals = make_test_env();
2008 let ctx = EvalContext::new();
2009 eval_str(
2010 "(define (curry f) (lambda (x) (lambda (y) (f x y))))",
2011 &globals,
2012 &ctx,
2013 )
2014 .unwrap();
2015 let result = eval_str("(((curry +) 3) 4)", &globals, &ctx).unwrap();
2016 assert_eq!(result, Value::int(7));
2017 }
2018
2019 #[test]
2022 fn test_vm_div_int_returns_float_when_non_whole() {
2023 let globals = make_test_env();
2024 let ctx = EvalContext::new();
2025 let result = eval_str("(/ 3 2)", &globals, &ctx).unwrap();
2026 assert_eq!(result, Value::float(1.5));
2027 }
2028
2029 #[test]
2030 fn test_vm_div_int_returns_int_when_whole() {
2031 let globals = make_test_env();
2032 let ctx = EvalContext::new();
2033 let result = eval_str("(/ 4 2)", &globals, &ctx).unwrap();
2034 assert_eq!(result, Value::int(2));
2035 }
2036
2037 #[test]
2038 fn test_vm_div_int_negative_non_whole() {
2039 let globals = make_test_env();
2040 let ctx = EvalContext::new();
2041 let result = eval_str("(/ 7 3)", &globals, &ctx).unwrap();
2042 assert!(
2043 result.as_float().is_some(),
2044 "expected float, got {:?}",
2045 result
2046 );
2047 }
2048
2049 #[test]
2050 fn test_vm_div_by_zero() {
2051 let globals = make_test_env();
2052 let ctx = EvalContext::new();
2053 assert!(eval_str("(/ 1 0)", &globals, &ctx).is_err());
2054 }
2055
2056 #[test]
2057 fn test_vm_eq_int_float_coercion() {
2058 let globals = make_test_env();
2059 let ctx = EvalContext::new();
2060 assert_eq!(
2061 eval_str("(= 1 1.0)", &globals, &ctx).unwrap(),
2062 Value::bool(true)
2063 );
2064 }
2065
2066 #[test]
2067 fn test_vm_eq_float_int_coercion() {
2068 let globals = make_test_env();
2069 let ctx = EvalContext::new();
2070 assert_eq!(
2071 eval_str("(= 1.0 1)", &globals, &ctx).unwrap(),
2072 Value::bool(true)
2073 );
2074 }
2075
2076 #[test]
2077 fn test_vm_eq_int_float_not_equal() {
2078 let globals = make_test_env();
2079 let ctx = EvalContext::new();
2080 assert_eq!(
2081 eval_str("(= 1 2.0)", &globals, &ctx).unwrap(),
2082 Value::bool(false)
2083 );
2084 }
2085
2086 #[test]
2087 fn test_vm_eq_same_type_int() {
2088 let globals = make_test_env();
2089 let ctx = EvalContext::new();
2090 assert_eq!(
2091 eval_str("(= 1 1)", &globals, &ctx).unwrap(),
2092 Value::bool(true)
2093 );
2094 assert_eq!(
2095 eval_str("(= 1 2)", &globals, &ctx).unwrap(),
2096 Value::bool(false)
2097 );
2098 }
2099
2100 #[test]
2101 fn test_vm_eq_opcode_direct() {
2102 use crate::emit::Emitter;
2103 use crate::opcodes::Op;
2104 let globals = Rc::new(Env::new());
2105 let ctx = EvalContext::new();
2106 let mut e = Emitter::new();
2107 e.emit_const(Value::int(1));
2108 e.emit_const(Value::float(1.0));
2109 e.emit_op(Op::Eq);
2110 e.emit_op(Op::Return);
2111 let func = Rc::new(crate::chunk::Function {
2112 name: None,
2113 chunk: e.into_chunk(),
2114 upvalue_descs: vec![],
2115 arity: 0,
2116 has_rest: false,
2117 local_names: vec![],
2118 });
2119 let closure = Rc::new(Closure {
2120 func,
2121 upvalues: vec![],
2122 });
2123 let mut vm = VM::new(globals, vec![]);
2124 let result = vm.execute(closure, &ctx).unwrap();
2125 assert_eq!(
2126 result,
2127 Value::bool(true),
2128 "Op::Eq should coerce int 1 == float 1.0"
2129 );
2130 }
2131
2132 #[test]
2133 fn test_vm_div_opcode_direct() {
2134 use crate::emit::Emitter;
2135 use crate::opcodes::Op;
2136 let globals = Rc::new(Env::new());
2137 let ctx = EvalContext::new();
2138 let mut e = Emitter::new();
2139 e.emit_const(Value::int(3));
2140 e.emit_const(Value::int(2));
2141 e.emit_op(Op::Div);
2142 e.emit_op(Op::Return);
2143 let func = Rc::new(crate::chunk::Function {
2144 name: None,
2145 chunk: e.into_chunk(),
2146 upvalue_descs: vec![],
2147 arity: 0,
2148 has_rest: false,
2149 local_names: vec![],
2150 });
2151 let closure = Rc::new(Closure {
2152 func,
2153 upvalues: vec![],
2154 });
2155 let mut vm = VM::new(globals, vec![]);
2156 let result = vm.execute(closure, &ctx).unwrap();
2157 assert_eq!(
2158 result,
2159 Value::float(1.5),
2160 "Op::Div 3/2 should return 1.5, not 1"
2161 );
2162 }
2163}