1use crate::op::*;
4use crate::program::*;
5use crate::value::Value;
6use indexmap::IndexMap;
7
8#[derive(Debug, Clone, thiserror::Error)]
9pub enum VmError {
10 #[error("runtime panic: {0}")]
11 Panic(String),
12 #[error("type mismatch at runtime: {0}")]
13 TypeMismatch(String),
14 #[error("stack underflow")]
15 StackUnderflow,
16 #[error("unknown function id: {0}")]
17 UnknownFunction(u32),
18 #[error("effect handler error: {0}")]
19 Effect(String),
20 #[error("call stack overflow: recursion depth exceeded ({0})")]
21 CallStackOverflow(u32),
22 #[error("refinement violated: argument {param_index} of `{fn_name}` (binding `{binding}`): {reason}")]
29 RefinementFailed {
30 fn_name: String,
31 param_index: usize,
32 binding: String,
33 reason: String,
34 },
35}
36
37pub const MAX_CALL_DEPTH: u32 = 1024;
44
45pub trait EffectHandler {
48 fn dispatch(&mut self, kind: &str, op: &str, args: Vec<Value>) -> Result<Value, String>;
49
50 fn note_call_budget(&mut self, _budget_cost: u64) -> Result<(), String> {
57 Ok(())
58 }
59}
60
61impl<'a> crate::parser_runtime::ClosureCaller for Vm<'a> {
68 fn call_closure(&mut self, closure: Value, args: Vec<Value>) -> Result<Value, String> {
69 self.invoke_closure_value(closure, args)
70 .map_err(|e| format!("{e:?}"))
71 }
72}
73
74pub struct DenyAllEffects;
76impl EffectHandler for DenyAllEffects {
77 fn dispatch(&mut self, kind: &str, op: &str, _args: Vec<Value>) -> Result<Value, String> {
78 Err(format!("effects not permitted (attempted {kind}.{op})"))
79 }
80}
81
82pub trait Tracer {
85 fn enter_call(&mut self, node_id: &str, name: &str, args: &[Value]);
86 fn enter_effect(&mut self, node_id: &str, kind: &str, op: &str, args: &[Value]);
87 fn exit_ok(&mut self, value: &Value);
88 fn exit_err(&mut self, message: &str);
89 fn exit_call_tail(&mut self);
92 fn override_effect(&mut self, _node_id: &str) -> Option<Value> { None }
94}
95
96pub struct NullTracer;
98impl Tracer for NullTracer {
99 fn enter_call(&mut self, _: &str, _: &str, _: &[Value]) {}
100 fn enter_effect(&mut self, _: &str, _: &str, _: &str, _: &[Value]) {}
101 fn exit_ok(&mut self, _: &Value) {}
102 fn exit_err(&mut self, _: &str) {}
103 fn exit_call_tail(&mut self) {}
104}
105
106#[derive(Debug, Clone)]
107pub(crate) enum FrameKind {
108 Entry,
110 Call(#[allow(dead_code)] String),
113}
114
115pub struct Vm<'a> {
116 program: &'a Program,
117 handler: Box<dyn EffectHandler + 'a>,
118 pub(crate) tracer: Box<dyn Tracer + 'a>,
119 frames: Vec<Frame>,
121 stack: Vec<Value>,
122 pub step_limit: u64,
124 pub steps: u64,
125 pure_memo: std::collections::HashMap<(u32, [u8; 16]), Value>,
131 pub pure_memo_hits: u64,
133 pub pure_memo_misses: u64,
134}
135
136struct Frame {
137 fn_id: u32,
138 pc: usize,
139 locals: Vec<Value>,
140 stack_base: usize,
142 trace_kind: FrameKind,
143 memo_key: Option<(u32, [u8; 16])>,
149}
150
151fn call_budget_cost(f: &crate::program::Function) -> u64 {
159 let mut total: u64 = 0;
160 for e in &f.effects {
161 if e.kind == "budget" {
162 if let Some(crate::program::EffectArg::Int(n)) = &e.arg {
163 if *n >= 0 {
164 total = total.saturating_add(*n as u64);
165 }
166 }
167 }
168 }
169 total
170}
171
172fn hash_call_args(args: &[Value]) -> [u8; 16] {
179 use sha2::{Digest, Sha256};
180 let json = serde_json::Value::Array(args.iter().map(Value::to_json).collect());
181 let canonical = lex_ast::canon_json::hash_canonical(&json);
182 let mut hasher = Sha256::new();
183 hasher.update(canonical);
184 let full = hasher.finalize();
185 let mut out = [0u8; 16];
186 out.copy_from_slice(&full[..16]);
187 out
188}
189
190fn eval_refinement(
199 predicate: &lex_ast::CExpr,
200 binding: &str,
201 arg: &Value,
202) -> Result<bool, String> {
203 match eval_refinement_inner(predicate, binding, arg) {
204 Ok(Value::Bool(b)) => Ok(b),
205 Ok(other) => Err(format!("predicate didn't reduce to a Bool, got {other:?}")),
206 Err(e) => Err(e),
207 }
208}
209
210fn eval_refinement_inner(
211 e: &lex_ast::CExpr,
212 binding: &str,
213 arg: &Value,
214) -> Result<Value, String> {
215 use lex_ast::{CExpr, CLit};
216 match e {
217 CExpr::Literal { value } => Ok(match value {
218 CLit::Int { value } => Value::Int(*value),
219 CLit::Float { value } => Value::Float(value.parse().unwrap_or(0.0)),
220 CLit::Bool { value } => Value::Bool(*value),
221 CLit::Str { value } => Value::Str(value.clone()),
222 CLit::Bytes { value } => Value::Str(value.clone()), CLit::Unit => Value::Unit,
224 }),
225 CExpr::Var { name } if name == binding => Ok(arg.clone()),
226 CExpr::Var { name } => Err(format!(
227 "predicate references free var `{name}`; runtime check \
228 only resolves the binding (slice 4 will plumb call-site \
229 context)")),
230 CExpr::UnaryOp { op, expr } => {
231 let v = eval_refinement_inner(expr, binding, arg)?;
232 match (op.as_str(), v) {
233 ("not", Value::Bool(b)) => Ok(Value::Bool(!b)),
234 ("-", Value::Int(n)) => Ok(Value::Int(-n)),
235 ("-", Value::Float(n)) => Ok(Value::Float(-n)),
236 (o, v) => Err(format!("unsupported unary `{o}` on {v:?}")),
237 }
238 }
239 CExpr::BinOp { op, lhs, rhs } => {
240 if op == "and" || op == "or" {
243 let l = eval_refinement_inner(lhs, binding, arg)?;
244 let lb = match l {
245 Value::Bool(b) => b,
246 other => return Err(format!("`{op}` on non-bool: {other:?}")),
247 };
248 if op == "and" && !lb { return Ok(Value::Bool(false)); }
249 if op == "or" && lb { return Ok(Value::Bool(true)); }
250 let r = eval_refinement_inner(rhs, binding, arg)?;
251 return match r {
252 Value::Bool(b) => Ok(Value::Bool(b)),
253 other => Err(format!("`{op}` on non-bool: {other:?}")),
254 };
255 }
256 let l = eval_refinement_inner(lhs, binding, arg)?;
257 let r = eval_refinement_inner(rhs, binding, arg)?;
258 apply_refinement_binop(op, &l, &r)
259 }
260 other => Err(format!("unsupported predicate node: {other:?}")),
266 }
267}
268
269fn apply_refinement_binop(op: &str, l: &Value, r: &Value) -> Result<Value, String> {
270 use Value::*;
271 match (op, l, r) {
272 ("+", Int(a), Int(b)) => Ok(Int(a + b)),
273 ("-", Int(a), Int(b)) => Ok(Int(a - b)),
274 ("*", Int(a), Int(b)) => Ok(Int(a * b)),
275 ("/", Int(a), Int(b)) if *b != 0 => Ok(Int(a / b)),
276 ("%", Int(a), Int(b)) if *b != 0 => Ok(Int(a % b)),
277 ("+", Float(a), Float(b)) => Ok(Float(a + b)),
278 ("-", Float(a), Float(b)) => Ok(Float(a - b)),
279 ("*", Float(a), Float(b)) => Ok(Float(a * b)),
280 ("/", Float(a), Float(b)) => Ok(Float(a / b)),
281
282 ("==", a, b) => Ok(Bool(a == b)),
283 ("!=", a, b) => Ok(Bool(a != b)),
284
285 ("<", Int(a), Int(b)) => Ok(Bool(a < b)),
286 ("<=", Int(a), Int(b)) => Ok(Bool(a <= b)),
287 (">", Int(a), Int(b)) => Ok(Bool(a > b)),
288 (">=", Int(a), Int(b)) => Ok(Bool(a >= b)),
289
290 ("<", Float(a), Float(b)) => Ok(Bool(a < b)),
291 ("<=", Float(a), Float(b)) => Ok(Bool(a <= b)),
292 (">", Float(a), Float(b)) => Ok(Bool(a > b)),
293 (">=", Float(a), Float(b)) => Ok(Bool(a >= b)),
294
295 (op, a, b) => Err(format!(
296 "unsupported binop `{op}` on {a:?} and {b:?}")),
297 }
298}
299
300fn const_str(constants: &[Const], idx: u32) -> String {
301 match constants.get(idx as usize) {
302 Some(Const::NodeId(s)) | Some(Const::Str(s)) => s.clone(),
303 _ => String::new(),
304 }
305}
306
307impl<'a> Vm<'a> {
308 pub fn new(program: &'a Program) -> Self {
309 Self::with_handler(program, Box::new(DenyAllEffects))
310 }
311
312 pub fn with_handler(program: &'a Program, handler: Box<dyn EffectHandler + 'a>) -> Self {
313 Self {
314 program,
315 handler,
316 tracer: Box::new(NullTracer),
317 frames: Vec::new(),
318 stack: Vec::new(),
319 step_limit: 10_000_000,
320 steps: 0,
321 pure_memo: std::collections::HashMap::new(),
322 pure_memo_hits: 0,
323 pure_memo_misses: 0,
324 }
325 }
326
327 pub fn set_tracer(&mut self, tracer: Box<dyn Tracer + 'a>) {
328 self.tracer = tracer;
329 }
330
331 pub fn set_step_limit(&mut self, limit: u64) {
337 self.step_limit = limit;
338 }
339
340 pub fn call(&mut self, name: &str, args: Vec<Value>) -> Result<Value, VmError> {
341 let fn_id = self.program.lookup(name).ok_or_else(|| VmError::Panic(format!("no function `{name}`")))?;
342 self.invoke(fn_id, args)
343 }
344
345 fn run_parser_op(&mut self, args: Vec<Value>) -> Result<Value, String> {
351 let parser = args.first().cloned()
352 .ok_or_else(|| "parser.run: missing parser arg".to_string())?;
353 let input = match args.get(1) {
354 Some(Value::Str(s)) => s.clone(),
355 _ => return Err("parser.run: input must be Str".into()),
356 };
357 match crate::parser_runtime::run_parser(&parser, &input, 0, self) {
358 Ok((value, _pos)) => Ok(Value::Variant {
359 name: "Ok".into(),
360 args: vec![value],
361 }),
362 Err((pos, msg)) => {
363 let mut e = indexmap::IndexMap::new();
364 e.insert("pos".into(), Value::Int(pos as i64));
365 e.insert("message".into(), Value::Str(msg));
366 Ok(Value::Variant {
367 name: "Err".into(),
368 args: vec![Value::Record(e)],
369 })
370 }
371 }
372 }
373
374 pub fn invoke_closure_value(
379 &mut self,
380 closure: Value,
381 args: Vec<Value>,
382 ) -> Result<Value, VmError> {
383 let (fn_id, captures) = match closure {
384 Value::Closure { fn_id, captures, .. } => (fn_id, captures),
385 other => return Err(VmError::TypeMismatch(
386 format!("invoke_closure_value: not a closure: {other:?}"))),
387 };
388 let mut combined = captures;
389 combined.extend(args);
390 self.invoke(fn_id, combined)
391 }
392
393 pub fn invoke(&mut self, fn_id: u32, args: Vec<Value>) -> Result<Value, VmError> {
394 let f = &self.program.functions[fn_id as usize];
395 if args.len() != f.arity as usize {
396 return Err(VmError::Panic(format!("arity mismatch calling {}", f.name)));
397 }
398 let f_name = f.name.clone();
404 let refinements = f.refinements.clone();
405 for (i, refinement) in refinements.iter().enumerate() {
406 if let Some(r) = refinement {
407 let arg = args.get(i).cloned().unwrap_or(Value::Unit);
408 match eval_refinement(&r.predicate, &r.binding, &arg) {
409 Ok(true) => {}
410 Ok(false) => return Err(VmError::RefinementFailed {
411 fn_name: f_name,
412 param_index: i,
413 binding: r.binding.clone(),
414 reason: format!("predicate failed for {} = {arg:?}", r.binding),
415 }),
416 Err(reason) => return Err(VmError::RefinementFailed {
417 fn_name: f_name,
418 param_index: i,
419 binding: r.binding.clone(),
420 reason,
421 }),
422 }
423 }
424 }
425 let f = &self.program.functions[fn_id as usize];
426 let mut locals = vec![Value::Unit; f.locals_count.max(f.arity) as usize];
427 for (i, v) in args.into_iter().enumerate() { locals[i] = v; }
428 let base_depth = self.frames.len();
432 self.push_frame(Frame {
433 fn_id, pc: 0, locals, stack_base: self.stack.len(),
434 trace_kind: FrameKind::Entry,
435 memo_key: None,
436 })?;
437 self.run_to(base_depth)
438 }
439
440 fn push_frame(&mut self, frame: Frame) -> Result<(), VmError> {
445 if self.frames.len() as u32 >= MAX_CALL_DEPTH {
446 return Err(VmError::CallStackOverflow(MAX_CALL_DEPTH));
447 }
448 self.frames.push(frame);
449 Ok(())
450 }
451
452 fn run_to(&mut self, base_depth: usize) -> Result<Value, VmError> {
457 loop {
458 if self.steps > self.step_limit {
459 return Err(VmError::Panic(format!(
460 "step limit exceeded ({} > {})",
461 self.steps, self.step_limit,
462 )));
463 }
464 self.steps += 1;
465 let frame_idx = self.frames.len() - 1;
466 let pc = self.frames[frame_idx].pc;
467 let fn_id = self.frames[frame_idx].fn_id;
468 let code = &self.program.functions[fn_id as usize].code;
469 if pc >= code.len() {
470 return Err(VmError::Panic("ran past end of code".into()));
471 }
472 let op = code[pc].clone();
473 self.frames[frame_idx].pc = pc + 1;
474
475 match op {
476 Op::PushConst(i) => {
477 let c = &self.program.constants[i as usize];
478 self.stack.push(const_to_value(c));
479 }
480 Op::Pop => { self.pop()?; }
481 Op::Dup => {
482 let v = self.peek()?.clone();
483 self.stack.push(v);
484 }
485 Op::LoadLocal(i) => {
486 let v = self.frames[frame_idx].locals[i as usize].clone();
487 self.stack.push(v);
488 }
489 Op::StoreLocal(i) => {
490 let v = self.pop()?;
491 self.frames[frame_idx].locals[i as usize] = v;
492 }
493 Op::MakeRecord { field_name_indices } => {
494 let n = field_name_indices.len();
495 let mut values: Vec<Value> = (0..n).map(|_| Value::Unit).collect();
496 for i in (0..n).rev() {
497 values[i] = self.pop()?;
498 }
499 let mut rec = IndexMap::new();
500 for (i, val) in values.into_iter().enumerate() {
501 let name = match &self.program.constants[field_name_indices[i] as usize] {
502 Const::FieldName(s) => s.clone(),
503 _ => return Err(VmError::TypeMismatch("expected FieldName const".into())),
504 };
505 rec.insert(name, val);
506 }
507 self.stack.push(Value::Record(rec));
508 }
509 Op::MakeTuple(n) => {
510 let mut items: Vec<Value> = (0..n).map(|_| Value::Unit).collect();
511 for i in (0..n as usize).rev() { items[i] = self.pop()?; }
512 self.stack.push(Value::Tuple(items));
513 }
514 Op::MakeList(n) => {
515 let mut items: Vec<Value> = (0..n).map(|_| Value::Unit).collect();
516 for i in (0..n as usize).rev() { items[i] = self.pop()?; }
517 self.stack.push(Value::List(items));
518 }
519 Op::MakeVariant { name_idx, arity } => {
520 let mut args: Vec<Value> = (0..arity).map(|_| Value::Unit).collect();
521 for i in (0..arity as usize).rev() { args[i] = self.pop()?; }
522 let name = match &self.program.constants[name_idx as usize] {
523 Const::VariantName(s) => s.clone(),
524 _ => return Err(VmError::TypeMismatch("expected VariantName const".into())),
525 };
526 self.stack.push(Value::Variant { name, args });
527 }
528 Op::GetField(i) => {
529 let name = match &self.program.constants[i as usize] {
530 Const::FieldName(s) => s.clone(),
531 _ => return Err(VmError::TypeMismatch("expected FieldName const".into())),
532 };
533 let v = self.pop()?;
534 match v {
535 Value::Record(r) => {
536 let v = r.get(&name).cloned()
537 .ok_or_else(|| VmError::TypeMismatch(format!("missing field `{name}`")))?;
538 self.stack.push(v);
539 }
540 other => return Err(VmError::TypeMismatch(format!("GetField on non-record: {other:?}"))),
541 }
542 }
543 Op::GetElem(i) => {
544 let v = self.pop()?;
545 match v {
546 Value::Tuple(items) => {
547 let v = items.into_iter().nth(i as usize)
548 .ok_or_else(|| VmError::TypeMismatch(format!("tuple index {i} out of range")))?;
549 self.stack.push(v);
550 }
551 other => return Err(VmError::TypeMismatch(format!("GetElem on non-tuple: {other:?}"))),
552 }
553 }
554 Op::TestVariant(i) => {
555 let name = match &self.program.constants[i as usize] {
556 Const::VariantName(s) => s.clone(),
557 _ => return Err(VmError::TypeMismatch("expected VariantName const".into())),
558 };
559 let v = self.pop()?;
560 match &v {
561 Value::Variant { name: vname, .. } => {
562 self.stack.push(Value::Bool(vname == &name));
563 }
564 other => return Err(VmError::TypeMismatch(format!("TestVariant on non-variant: {other:?}"))),
567 }
568 }
569 Op::GetVariant(_i) => {
570 let v = self.pop()?;
571 match v {
572 Value::Variant { args, .. } => {
573 self.stack.push(Value::Tuple(args));
574 }
575 other => return Err(VmError::TypeMismatch(format!("GetVariant on non-variant: {other:?}"))),
576 }
577 }
578 Op::GetVariantArg(i) => {
579 let v = self.pop()?;
580 match v {
581 Value::Variant { mut args, .. } => {
582 if (i as usize) >= args.len() {
583 return Err(VmError::TypeMismatch("variant arg index oob".into()));
584 }
585 self.stack.push(args.swap_remove(i as usize));
586 }
587 other => return Err(VmError::TypeMismatch(format!("GetVariantArg on non-variant: {other:?}"))),
588 }
589 }
590 Op::GetListLen => {
591 let v = self.pop()?;
592 match v {
593 Value::List(items) => self.stack.push(Value::Int(items.len() as i64)),
594 other => return Err(VmError::TypeMismatch(format!("GetListLen on non-list: {other:?}"))),
595 }
596 }
597 Op::GetListElem(i) => {
598 let v = self.pop()?;
599 match v {
600 Value::List(items) => {
601 let v = items.into_iter().nth(i as usize)
602 .ok_or_else(|| VmError::TypeMismatch("list index oob".into()))?;
603 self.stack.push(v);
604 }
605 other => return Err(VmError::TypeMismatch(format!("GetListElem on non-list: {other:?}"))),
606 }
607 }
608 Op::GetListElemDyn => {
609 let idx = match self.pop()? {
611 Value::Int(n) => n as usize,
612 other => return Err(VmError::TypeMismatch(format!("GetListElemDyn idx: {other:?}"))),
613 };
614 let v = self.pop()?;
615 match v {
616 Value::List(items) => {
617 let v = items.into_iter().nth(idx)
618 .ok_or_else(|| VmError::TypeMismatch("list index oob".into()))?;
619 self.stack.push(v);
620 }
621 other => return Err(VmError::TypeMismatch(format!("GetListElemDyn on non-list: {other:?}"))),
622 }
623 }
624 Op::ListAppend => {
625 let value = self.pop()?;
626 let list = self.pop()?;
627 match list {
628 Value::List(mut items) => {
629 items.push(value);
630 self.stack.push(Value::List(items));
631 }
632 other => return Err(VmError::TypeMismatch(format!("ListAppend on non-list: {other:?}"))),
633 }
634 }
635 Op::Jump(off) => {
636 let new_pc = (self.frames[frame_idx].pc as i32 + off) as usize;
637 self.frames[frame_idx].pc = new_pc;
638 }
639 Op::JumpIf(off) => {
640 let v = self.pop()?;
641 if v.as_bool() {
642 let new_pc = (self.frames[frame_idx].pc as i32 + off) as usize;
643 self.frames[frame_idx].pc = new_pc;
644 }
645 }
646 Op::JumpIfNot(off) => {
647 let v = self.pop()?;
648 if !v.as_bool() {
649 let new_pc = (self.frames[frame_idx].pc as i32 + off) as usize;
650 self.frames[frame_idx].pc = new_pc;
651 }
652 }
653 Op::MakeClosure { fn_id, capture_count } => {
654 let n = capture_count as usize;
655 let mut captures: Vec<Value> = (0..n).map(|_| Value::Unit).collect();
656 for i in (0..n).rev() { captures[i] = self.pop()?; }
657 let body_hash = self.program.functions[fn_id as usize].body_hash;
660 self.stack.push(Value::Closure { fn_id, body_hash, captures });
661 }
662 Op::CallClosure { arity, node_id_idx } => {
663 let mut args: Vec<Value> = (0..arity).map(|_| Value::Unit).collect();
664 for i in (0..arity as usize).rev() { args[i] = self.pop()?; }
665 let closure = self.pop()?;
666 let (fn_id, captures) = match closure {
667 Value::Closure { fn_id, captures, .. } => (fn_id, captures),
668 other => return Err(VmError::TypeMismatch(format!("CallClosure on non-closure: {other:?}"))),
669 };
670 let node_id = const_str(&self.program.constants, node_id_idx);
671 let callee_name = self.program.functions[fn_id as usize].name.clone();
672 let budget_cost = call_budget_cost(&self.program.functions[fn_id as usize]);
673 if budget_cost > 0 {
674 self.handler.note_call_budget(budget_cost)
675 .map_err(VmError::Effect)?;
676 }
677 let mut combined = captures;
678 combined.extend(args);
679 self.tracer.enter_call(&node_id, &callee_name, &combined);
680 let f = &self.program.functions[fn_id as usize];
681 let mut locals = vec![Value::Unit; f.locals_count.max(f.arity) as usize];
682 for (i, v) in combined.into_iter().enumerate() { locals[i] = v; }
683 self.push_frame(Frame {
684 fn_id, pc: 0, locals, stack_base: self.stack.len(),
685 trace_kind: FrameKind::Call(node_id),
686 memo_key: None,
691 })?;
692 }
693 Op::Call { fn_id, arity, node_id_idx } => {
694 let mut args: Vec<Value> = (0..arity).map(|_| Value::Unit).collect();
695 for i in (0..arity as usize).rev() { args[i] = self.pop()?; }
696 let node_id = const_str(&self.program.constants, node_id_idx);
697 let callee_name = self.program.functions[fn_id as usize].name.clone();
698 let budget_cost = call_budget_cost(&self.program.functions[fn_id as usize]);
699 if budget_cost > 0 {
700 self.handler.note_call_budget(budget_cost)
701 .map_err(VmError::Effect)?;
702 }
703 let refinements = self.program.functions[fn_id as usize]
712 .refinements.clone();
713 for (i, refinement) in refinements.iter().enumerate() {
714 if let Some(r) = refinement {
715 let arg = args.get(i).cloned().unwrap_or(Value::Unit);
716 match eval_refinement(&r.predicate, &r.binding, &arg) {
717 Ok(true) => { }
718 Ok(false) => {
719 return Err(VmError::RefinementFailed {
720 fn_name: callee_name.clone(),
721 param_index: i,
722 binding: r.binding.clone(),
723 reason: format!(
724 "predicate failed for {} = {arg:?}",
725 r.binding),
726 });
727 }
728 Err(reason) => {
729 return Err(VmError::RefinementFailed {
730 fn_name: callee_name.clone(),
731 param_index: i,
732 binding: r.binding.clone(),
733 reason,
734 });
735 }
736 }
737 }
738 }
739 let f = &self.program.functions[fn_id as usize];
745 let memo_key: Option<(u32, [u8; 16])> = if f.effects.is_empty() {
746 Some((fn_id, hash_call_args(&args)))
747 } else {
748 None
749 };
750 if let Some(key) = memo_key {
751 if let Some(cached) = self.pure_memo.get(&key).cloned() {
752 self.pure_memo_hits += 1;
753 self.tracer.enter_call(&node_id, &callee_name, &args);
754 self.tracer.exit_ok(&cached);
755 self.stack.push(cached);
756 continue;
757 }
758 self.pure_memo_misses += 1;
759 }
760 self.tracer.enter_call(&node_id, &callee_name, &args);
761 let mut locals = vec![Value::Unit; f.locals_count.max(f.arity) as usize];
762 for (i, v) in args.into_iter().enumerate() { locals[i] = v; }
763 self.push_frame(Frame {
764 fn_id, pc: 0, locals, stack_base: self.stack.len(),
765 trace_kind: FrameKind::Call(node_id),
766 memo_key,
767 })?;
768 }
769 Op::TailCall { fn_id, arity, node_id_idx } => {
770 let mut args: Vec<Value> = (0..arity).map(|_| Value::Unit).collect();
771 for i in (0..arity as usize).rev() { args[i] = self.pop()?; }
772 let node_id = const_str(&self.program.constants, node_id_idx);
773 let callee_name = self.program.functions[fn_id as usize].name.clone();
774 let budget_cost = call_budget_cost(&self.program.functions[fn_id as usize]);
775 if budget_cost > 0 {
776 self.handler.note_call_budget(budget_cost)
777 .map_err(VmError::Effect)?;
778 }
779 let refinements = self.program.functions[fn_id as usize]
782 .refinements.clone();
783 for (i, refinement) in refinements.iter().enumerate() {
784 if let Some(r) = refinement {
785 let arg = args.get(i).cloned().unwrap_or(Value::Unit);
786 match eval_refinement(&r.predicate, &r.binding, &arg) {
787 Ok(true) => {}
788 Ok(false) => return Err(VmError::RefinementFailed {
789 fn_name: callee_name.clone(),
790 param_index: i,
791 binding: r.binding.clone(),
792 reason: format!(
793 "predicate failed for {} = {arg:?}",
794 r.binding),
795 }),
796 Err(reason) => return Err(VmError::RefinementFailed {
797 fn_name: callee_name.clone(),
798 param_index: i,
799 binding: r.binding.clone(),
800 reason,
801 }),
802 }
803 }
804 }
805 self.tracer.exit_call_tail();
809 self.tracer.enter_call(&node_id, &callee_name, &args);
810 let f = &self.program.functions[fn_id as usize];
811 let frame = self.frames.last_mut().unwrap();
812 frame.fn_id = fn_id;
813 frame.pc = 0;
814 frame.locals = vec![Value::Unit; f.locals_count.max(f.arity) as usize];
815 for (i, v) in args.into_iter().enumerate() { frame.locals[i] = v; }
816 frame.trace_kind = FrameKind::Call(node_id);
817 }
818 Op::EffectCall { kind_idx, op_idx, arity, node_id_idx } => {
819 let mut args: Vec<Value> = (0..arity).map(|_| Value::Unit).collect();
820 for i in (0..arity as usize).rev() { args[i] = self.pop()?; }
821 let kind = match &self.program.constants[kind_idx as usize] {
822 Const::Str(s) => s.clone(),
823 _ => return Err(VmError::TypeMismatch("expected Str const for effect kind".into())),
824 };
825 let op_name = match &self.program.constants[op_idx as usize] {
826 Const::Str(s) => s.clone(),
827 _ => return Err(VmError::TypeMismatch("expected Str const for effect op".into())),
828 };
829 let node_id = const_str(&self.program.constants, node_id_idx);
830 self.tracer.enter_effect(&node_id, &kind, &op_name, &args);
831 let result = match self.tracer.override_effect(&node_id) {
832 Some(v) => Ok(v),
833 None if (kind.as_str(), op_name.as_str()) == ("parser", "run")
839 => self.run_parser_op(args.clone()),
840 None => self.handler.dispatch(&kind, &op_name, args.clone()),
841 };
842 match result {
843 Ok(v) => {
844 self.tracer.exit_ok(&v);
845 self.stack.push(v);
846 }
847 Err(e) => {
848 self.tracer.exit_err(&e);
849 return Err(VmError::Effect(e));
850 }
851 }
852 }
853 Op::Return => {
854 let v = self.pop()?;
855 let frame = self.frames.pop().unwrap();
856 self.stack.truncate(frame.stack_base);
858 if matches!(frame.trace_kind, FrameKind::Call(_)) {
859 self.tracer.exit_ok(&v);
860 }
861 if let Some(key) = frame.memo_key {
866 self.pure_memo.insert(key, v.clone());
867 }
868 if self.frames.len() <= base_depth {
873 return Ok(v);
874 }
875 self.stack.push(v);
876 }
877 Op::Panic(i) => {
878 let msg = match &self.program.constants[i as usize] {
879 Const::Str(s) => s.clone(),
880 _ => "panic".into(),
881 };
882 return Err(VmError::Panic(msg));
883 }
884 Op::IntAdd => self.bin_int(|a, b| Value::Int(a + b))?,
886 Op::IntSub => self.bin_int(|a, b| Value::Int(a - b))?,
887 Op::IntMul => self.bin_int(|a, b| Value::Int(a * b))?,
888 Op::IntDiv => self.bin_int(|a, b| Value::Int(a / b))?,
889 Op::IntMod => self.bin_int(|a, b| Value::Int(a % b))?,
890 Op::IntNeg => {
891 let a = self.pop()?.as_int();
892 self.stack.push(Value::Int(-a));
893 }
894 Op::IntEq => self.bin_int(|a, b| Value::Bool(a == b))?,
895 Op::IntLt => self.bin_int(|a, b| Value::Bool(a < b))?,
896 Op::IntLe => self.bin_int(|a, b| Value::Bool(a <= b))?,
897 Op::FloatAdd => self.bin_float(|a, b| Value::Float(a + b))?,
898 Op::FloatSub => self.bin_float(|a, b| Value::Float(a - b))?,
899 Op::FloatMul => self.bin_float(|a, b| Value::Float(a * b))?,
900 Op::FloatDiv => self.bin_float(|a, b| Value::Float(a / b))?,
901 Op::FloatNeg => {
902 let a = self.pop()?.as_float();
903 self.stack.push(Value::Float(-a));
904 }
905 Op::FloatEq => self.bin_float(|a, b| Value::Bool(a == b))?,
906 Op::FloatLt => self.bin_float(|a, b| Value::Bool(a < b))?,
907 Op::FloatLe => self.bin_float(|a, b| Value::Bool(a <= b))?,
908 Op::NumAdd => self.bin_num(|a, b| Value::Int(a + b), |a, b| Value::Float(a + b))?,
909 Op::NumSub => self.bin_num(|a, b| Value::Int(a - b), |a, b| Value::Float(a - b))?,
910 Op::NumMul => self.bin_num(|a, b| Value::Int(a * b), |a, b| Value::Float(a * b))?,
911 Op::NumDiv => self.bin_num(|a, b| Value::Int(a / b), |a, b| Value::Float(a / b))?,
912 Op::NumMod => self.bin_int(|a, b| Value::Int(a % b))?,
913 Op::NumNeg => {
914 let v = self.pop()?;
915 match v {
916 Value::Int(n) => self.stack.push(Value::Int(-n)),
917 Value::Float(f) => self.stack.push(Value::Float(-f)),
918 other => return Err(VmError::TypeMismatch(format!("NumNeg on {other:?}"))),
919 }
920 }
921 Op::NumEq => self.bin_eq()?,
922 Op::NumLt => self.bin_num(|a, b| Value::Bool(a < b), |a, b| Value::Bool(a < b))?,
923 Op::NumLe => self.bin_num(|a, b| Value::Bool(a <= b), |a, b| Value::Bool(a <= b))?,
924 Op::BoolAnd => {
925 let b = self.pop()?.as_bool();
926 let a = self.pop()?.as_bool();
927 self.stack.push(Value::Bool(a && b));
928 }
929 Op::BoolOr => {
930 let b = self.pop()?.as_bool();
931 let a = self.pop()?.as_bool();
932 self.stack.push(Value::Bool(a || b));
933 }
934 Op::BoolNot => {
935 let a = self.pop()?.as_bool();
936 self.stack.push(Value::Bool(!a));
937 }
938 Op::StrConcat => {
939 let b = self.pop()?;
940 let a = self.pop()?;
941 let s = format!("{}{}", a.as_str(), b.as_str());
942 self.stack.push(Value::Str(s));
943 }
944 Op::StrLen => {
945 let v = self.pop()?;
946 self.stack.push(Value::Int(v.as_str().len() as i64));
947 }
948 Op::StrEq => {
949 let b = self.pop()?;
950 let a = self.pop()?;
951 self.stack.push(Value::Bool(a.as_str() == b.as_str()));
952 }
953 Op::BytesLen => {
954 let v = self.pop()?;
955 match v {
956 Value::Bytes(b) => self.stack.push(Value::Int(b.len() as i64)),
957 other => return Err(VmError::TypeMismatch(format!("BytesLen on {other:?}"))),
958 }
959 }
960 Op::BytesEq => {
961 let b = self.pop()?;
962 let a = self.pop()?;
963 let eq = match (a, b) {
964 (Value::Bytes(x), Value::Bytes(y)) => x == y,
965 _ => return Err(VmError::TypeMismatch("BytesEq operands".into())),
966 };
967 self.stack.push(Value::Bool(eq));
968 }
969 }
970 }
971 }
972
973 fn pop(&mut self) -> Result<Value, VmError> {
974 self.stack.pop().ok_or(VmError::StackUnderflow)
975 }
976 fn peek(&self) -> Result<&Value, VmError> {
977 self.stack.last().ok_or(VmError::StackUnderflow)
978 }
979
980 fn bin_int(&mut self, f: impl Fn(i64, i64) -> Value) -> Result<(), VmError> {
981 let b = self.pop()?.as_int();
982 let a = self.pop()?.as_int();
983 self.stack.push(f(a, b));
984 Ok(())
985 }
986 fn bin_float(&mut self, f: impl Fn(f64, f64) -> Value) -> Result<(), VmError> {
987 let b = self.pop()?.as_float();
988 let a = self.pop()?.as_float();
989 self.stack.push(f(a, b));
990 Ok(())
991 }
992 fn bin_num(
993 &mut self,
994 i: impl Fn(i64, i64) -> Value,
995 f: impl Fn(f64, f64) -> Value,
996 ) -> Result<(), VmError> {
997 let b = self.pop()?;
998 let a = self.pop()?;
999 match (a, b) {
1000 (Value::Int(x), Value::Int(y)) => { self.stack.push(i(x, y)); Ok(()) }
1001 (Value::Float(x), Value::Float(y)) => { self.stack.push(f(x, y)); Ok(()) }
1002 (a, b) => Err(VmError::TypeMismatch(format!("Num op: {a:?} {b:?}"))),
1003 }
1004 }
1005 fn bin_eq(&mut self) -> Result<(), VmError> {
1006 let b = self.pop()?;
1007 let a = self.pop()?;
1008 self.stack.push(Value::Bool(a == b));
1009 Ok(())
1010 }
1011}
1012
1013fn const_to_value(c: &Const) -> Value {
1014 match c {
1015 Const::Int(n) => Value::Int(*n),
1016 Const::Float(f) => Value::Float(*f),
1017 Const::Bool(b) => Value::Bool(*b),
1018 Const::Str(s) => Value::Str(s.clone()),
1019 Const::Bytes(b) => Value::Bytes(b.clone()),
1020 Const::Unit => Value::Unit,
1021 Const::FieldName(s) | Const::VariantName(s) | Const::NodeId(s) => Value::Str(s.clone()),
1022 }
1023}