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: {0}")]
17 UnknownFunction(String),
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 fn spawn_for_worker(&self) -> Option<Box<dyn EffectHandler + Send>> {
76 None
77 }
78}
79
80impl<'a> crate::parser_runtime::ClosureCaller for Vm<'a> {
87 fn call_closure(&mut self, closure: Value, args: Vec<Value>) -> Result<Value, String> {
88 self.invoke_closure_value(closure, args)
89 .map_err(|e| format!("{e:?}"))
90 }
91}
92
93pub struct DenyAllEffects;
95impl EffectHandler for DenyAllEffects {
96 fn dispatch(&mut self, kind: &str, op: &str, _args: Vec<Value>) -> Result<Value, String> {
97 Err(format!("effects not permitted (attempted {kind}.{op})"))
98 }
99}
100
101pub trait Tracer {
104 fn enter_call(&mut self, node_id: &str, name: &str, args: &[Value]);
105 fn enter_effect(&mut self, node_id: &str, kind: &str, op: &str, args: &[Value]);
106 fn exit_ok(&mut self, value: &Value);
107 fn exit_err(&mut self, message: &str);
108 fn exit_call_tail(&mut self);
111 fn override_effect(&mut self, _node_id: &str) -> Option<Value> { None }
113}
114
115pub struct NullTracer;
117impl Tracer for NullTracer {
118 fn enter_call(&mut self, _: &str, _: &str, _: &[Value]) {}
119 fn enter_effect(&mut self, _: &str, _: &str, _: &str, _: &[Value]) {}
120 fn exit_ok(&mut self, _: &Value) {}
121 fn exit_err(&mut self, _: &str) {}
122 fn exit_call_tail(&mut self) {}
123}
124
125#[derive(Debug, Clone)]
126pub(crate) enum FrameKind {
127 Entry,
129 Call(#[allow(dead_code)] String),
132}
133
134pub struct Vm<'a> {
135 program: &'a Program,
136 handler: Box<dyn EffectHandler + 'a>,
137 pub(crate) tracer: Box<dyn Tracer + 'a>,
138 frames: Vec<Frame>,
140 stack: Vec<Value>,
141 pub step_limit: u64,
143 pub steps: u64,
144 pure_memo: std::collections::HashMap<(u32, [u8; 16]), Value>,
150 pub pure_memo_hits: u64,
152 pub pure_memo_misses: u64,
153}
154
155struct Frame {
156 fn_id: u32,
157 pc: usize,
158 locals: Vec<Value>,
159 stack_base: usize,
161 trace_kind: FrameKind,
162 memo_key: Option<(u32, [u8; 16])>,
168}
169
170fn call_budget_cost(f: &crate::program::Function) -> u64 {
178 let mut total: u64 = 0;
179 for e in &f.effects {
180 if e.kind == "budget" {
181 if let Some(crate::program::EffectArg::Int(n)) = &e.arg {
182 if *n >= 0 {
183 total = total.saturating_add(*n as u64);
184 }
185 }
186 }
187 }
188 total
189}
190
191fn hash_call_args(args: &[Value]) -> [u8; 16] {
198 use sha2::{Digest, Sha256};
199 let json = serde_json::Value::Array(args.iter().map(Value::to_json).collect());
200 let canonical = lex_ast::canon_json::hash_canonical(&json);
201 let mut hasher = Sha256::new();
202 hasher.update(canonical);
203 let full = hasher.finalize();
204 let mut out = [0u8; 16];
205 out.copy_from_slice(&full[..16]);
206 out
207}
208
209fn eval_refinement(
218 predicate: &lex_ast::CExpr,
219 binding: &str,
220 arg: &Value,
221) -> Result<bool, String> {
222 match eval_refinement_inner(predicate, binding, arg) {
223 Ok(Value::Bool(b)) => Ok(b),
224 Ok(other) => Err(format!("predicate didn't reduce to a Bool, got {other:?}")),
225 Err(e) => Err(e),
226 }
227}
228
229fn eval_refinement_inner(
230 e: &lex_ast::CExpr,
231 binding: &str,
232 arg: &Value,
233) -> Result<Value, String> {
234 use lex_ast::{CExpr, CLit};
235 match e {
236 CExpr::Literal { value } => Ok(match value {
237 CLit::Int { value } => Value::Int(*value),
238 CLit::Float { value } => Value::Float(value.parse().unwrap_or(0.0)),
239 CLit::Bool { value } => Value::Bool(*value),
240 CLit::Str { value } => Value::Str(value.clone()),
241 CLit::Bytes { value } => Value::Str(value.clone()), CLit::Unit => Value::Unit,
243 }),
244 CExpr::Var { name } if name == binding => Ok(arg.clone()),
245 CExpr::Var { name } => Err(format!(
246 "predicate references free var `{name}`; runtime check \
247 only resolves the binding (slice 4 will plumb call-site \
248 context)")),
249 CExpr::UnaryOp { op, expr } => {
250 let v = eval_refinement_inner(expr, binding, arg)?;
251 match (op.as_str(), v) {
252 ("not", Value::Bool(b)) => Ok(Value::Bool(!b)),
253 ("-", Value::Int(n)) => Ok(Value::Int(-n)),
254 ("-", Value::Float(n)) => Ok(Value::Float(-n)),
255 (o, v) => Err(format!("unsupported unary `{o}` on {v:?}")),
256 }
257 }
258 CExpr::BinOp { op, lhs, rhs } => {
259 if op == "and" || op == "or" {
262 let l = eval_refinement_inner(lhs, binding, arg)?;
263 let lb = match l {
264 Value::Bool(b) => b,
265 other => return Err(format!("`{op}` on non-bool: {other:?}")),
266 };
267 if op == "and" && !lb { return Ok(Value::Bool(false)); }
268 if op == "or" && lb { return Ok(Value::Bool(true)); }
269 let r = eval_refinement_inner(rhs, binding, arg)?;
270 return match r {
271 Value::Bool(b) => Ok(Value::Bool(b)),
272 other => Err(format!("`{op}` on non-bool: {other:?}")),
273 };
274 }
275 let l = eval_refinement_inner(lhs, binding, arg)?;
276 let r = eval_refinement_inner(rhs, binding, arg)?;
277 apply_refinement_binop(op, &l, &r)
278 }
279 other => Err(format!("unsupported predicate node: {other:?}")),
285 }
286}
287
288fn apply_refinement_binop(op: &str, l: &Value, r: &Value) -> Result<Value, String> {
289 use Value::*;
290 match (op, l, r) {
291 ("+", Int(a), Int(b)) => Ok(Int(a + b)),
292 ("-", Int(a), Int(b)) => Ok(Int(a - b)),
293 ("*", Int(a), Int(b)) => Ok(Int(a * b)),
294 ("/", Int(a), Int(b)) if *b != 0 => Ok(Int(a / b)),
295 ("%", Int(a), Int(b)) if *b != 0 => Ok(Int(a % b)),
296 ("+", Float(a), Float(b)) => Ok(Float(a + b)),
297 ("-", Float(a), Float(b)) => Ok(Float(a - b)),
298 ("*", Float(a), Float(b)) => Ok(Float(a * b)),
299 ("/", Float(a), Float(b)) => Ok(Float(a / b)),
300
301 ("==", a, b) => Ok(Bool(a == b)),
302 ("!=", a, b) => Ok(Bool(a != b)),
303
304 ("<", Int(a), Int(b)) => Ok(Bool(a < b)),
305 ("<=", Int(a), Int(b)) => Ok(Bool(a <= b)),
306 (">", Int(a), Int(b)) => Ok(Bool(a > b)),
307 (">=", Int(a), Int(b)) => Ok(Bool(a >= b)),
308
309 ("<", Float(a), Float(b)) => Ok(Bool(a < b)),
310 ("<=", Float(a), Float(b)) => Ok(Bool(a <= b)),
311 (">", Float(a), Float(b)) => Ok(Bool(a > b)),
312 (">=", Float(a), Float(b)) => Ok(Bool(a >= b)),
313
314 (op, a, b) => Err(format!(
315 "unsupported binop `{op}` on {a:?} and {b:?}")),
316 }
317}
318
319fn const_str(constants: &[Const], idx: u32) -> String {
320 match constants.get(idx as usize) {
321 Some(Const::NodeId(s)) | Some(Const::Str(s)) => s.clone(),
322 _ => String::new(),
323 }
324}
325
326fn compare_sort_keys(a: &Value, b: &Value) -> std::cmp::Ordering {
336 use std::cmp::Ordering;
337 match (a, b) {
338 (Value::Int(x), Value::Int(y)) => x.cmp(y),
339 (Value::Float(x), Value::Float(y)) => x.partial_cmp(y).unwrap_or(Ordering::Equal),
340 (Value::Str(x), Value::Str(y)) => x.cmp(y),
341 _ => Ordering::Equal,
342 }
343}
344
345fn par_max_concurrency() -> usize {
346 let from_env = std::env::var("LEX_PAR_MAX_CONCURRENCY")
347 .ok()
348 .and_then(|s| s.parse::<usize>().ok())
349 .filter(|n| *n > 0);
350 let default = std::thread::available_parallelism()
351 .map(|n| n.get())
352 .unwrap_or(4);
353 from_env.unwrap_or(default).min(64)
354}
355
356fn par_map_run<'a>(
363 program: &'a Program,
364 closure: Value,
365 items: Vec<Value>,
366 worker_handlers: Vec<Box<dyn EffectHandler + Send>>,
367) -> Result<Vec<Value>, VmError> {
368 if items.is_empty() {
369 return Ok(Vec::new());
370 }
371 let n_workers = worker_handlers.len().min(items.len()).max(1);
372 let mut buckets: Vec<Vec<(usize, Value)>> = (0..n_workers).map(|_| Vec::new()).collect();
376 for (i, v) in items.into_iter().enumerate() {
377 buckets[i % n_workers].push((i, v));
378 }
379 let n_total: usize = buckets.iter().map(|b| b.len()).sum();
380 let results: std::sync::Mutex<Vec<Option<Result<Value, String>>>> =
381 std::sync::Mutex::new((0..n_total).map(|_| None).collect());
382
383 let mut worker_handlers = worker_handlers;
387 worker_handlers.truncate(n_workers);
388 type Pair = (Vec<(usize, Value)>, Box<dyn EffectHandler + Send>);
389 let pairs: Vec<Pair> = buckets.into_iter().zip(worker_handlers).collect();
390
391 std::thread::scope(|s| {
392 let mut handles = Vec::with_capacity(pairs.len());
393 for (bucket, handler) in pairs {
394 let closure = closure.clone();
395 let results = &results;
396 handles.push(s.spawn(move || {
397 let handler_for_vm: Box<dyn EffectHandler + 'a> = handler;
402 let mut vm = Vm::with_handler(program, handler_for_vm);
403 for (idx, item) in bucket {
404 let r = vm
405 .invoke_closure_value(closure.clone(), vec![item])
406 .map_err(|e| format!("{e:?}"));
407 results.lock().unwrap()[idx] = Some(r);
408 }
409 }));
410 }
411 for h in handles {
412 h.join().map_err(|_| ()).ok();
413 }
414 });
415
416 let mut out = Vec::with_capacity(n_total);
417 let inner = results.into_inner().unwrap();
418 for r in inner {
419 match r {
420 Some(Ok(v)) => out.push(v),
421 Some(Err(e)) => return Err(VmError::Effect(format!("par_map worker: {e}"))),
422 None => return Err(VmError::Panic("par_map worker did not produce a result".into())),
423 }
424 }
425 Ok(out)
426}
427
428impl<'a> Vm<'a> {
429 pub fn new(program: &'a Program) -> Self {
430 Self::with_handler(program, Box::new(DenyAllEffects))
431 }
432
433 pub fn with_handler(program: &'a Program, handler: Box<dyn EffectHandler + 'a>) -> Self {
434 Self {
435 program,
436 handler,
437 tracer: Box::new(NullTracer),
438 frames: Vec::new(),
439 stack: Vec::new(),
440 step_limit: 10_000_000,
441 steps: 0,
442 pure_memo: std::collections::HashMap::new(),
443 pure_memo_hits: 0,
444 pure_memo_misses: 0,
445 }
446 }
447
448 pub fn set_tracer(&mut self, tracer: Box<dyn Tracer + 'a>) {
449 self.tracer = tracer;
450 }
451
452 pub fn set_step_limit(&mut self, limit: u64) {
458 self.step_limit = limit;
459 }
460
461 pub fn call(&mut self, name: &str, args: Vec<Value>) -> Result<Value, VmError> {
462 let fn_id = self.program.lookup(name).ok_or_else(|| VmError::Panic(format!("no function `{name}`")))?;
463 self.invoke(fn_id, args)
464 }
465
466 fn run_parser_op(&mut self, args: Vec<Value>) -> Result<Value, String> {
472 let parser = args.first().cloned()
473 .ok_or_else(|| "parser.run: missing parser arg".to_string())?;
474 let input = match args.get(1) {
475 Some(Value::Str(s)) => s.clone(),
476 _ => return Err("parser.run: input must be Str".into()),
477 };
478 match crate::parser_runtime::run_parser(&parser, &input, 0, self) {
479 Ok((value, _pos)) => Ok(Value::Variant {
480 name: "Ok".into(),
481 args: vec![value],
482 }),
483 Err((pos, msg)) => {
484 let mut e = indexmap::IndexMap::new();
485 e.insert("pos".into(), Value::Int(pos as i64));
486 e.insert("message".into(), Value::Str(msg));
487 Ok(Value::Variant {
488 name: "Err".into(),
489 args: vec![Value::Record(e)],
490 })
491 }
492 }
493 }
494
495 pub fn invoke_closure_value(
500 &mut self,
501 closure: Value,
502 args: Vec<Value>,
503 ) -> Result<Value, VmError> {
504 let (fn_id, captures) = match closure {
505 Value::Closure { fn_id, captures, .. } => (fn_id, captures),
506 other => return Err(VmError::TypeMismatch(
507 format!("invoke_closure_value: not a closure: {other:?}"))),
508 };
509 let mut combined = captures;
510 combined.extend(args);
511 self.invoke(fn_id, combined)
512 }
513
514 pub fn invoke(&mut self, fn_id: u32, args: Vec<Value>) -> Result<Value, VmError> {
515 let f = &self.program.functions[fn_id as usize];
516 if args.len() != f.arity as usize {
517 return Err(VmError::Panic(format!("arity mismatch calling {}", f.name)));
518 }
519 let f_name = f.name.clone();
525 let refinements = f.refinements.clone();
526 for (i, refinement) in refinements.iter().enumerate() {
527 if let Some(r) = refinement {
528 let arg = args.get(i).cloned().unwrap_or(Value::Unit);
529 match eval_refinement(&r.predicate, &r.binding, &arg) {
530 Ok(true) => {}
531 Ok(false) => return Err(VmError::RefinementFailed {
532 fn_name: f_name,
533 param_index: i,
534 binding: r.binding.clone(),
535 reason: format!("predicate failed for {} = {arg:?}", r.binding),
536 }),
537 Err(reason) => return Err(VmError::RefinementFailed {
538 fn_name: f_name,
539 param_index: i,
540 binding: r.binding.clone(),
541 reason,
542 }),
543 }
544 }
545 }
546 let f = &self.program.functions[fn_id as usize];
547 let mut locals = vec![Value::Unit; f.locals_count.max(f.arity) as usize];
548 for (i, v) in args.into_iter().enumerate() { locals[i] = v; }
549 let base_depth = self.frames.len();
553 self.push_frame(Frame {
554 fn_id, pc: 0, locals, stack_base: self.stack.len(),
555 trace_kind: FrameKind::Entry,
556 memo_key: None,
557 })?;
558 self.run_to(base_depth)
559 }
560
561 fn push_frame(&mut self, frame: Frame) -> Result<(), VmError> {
566 if self.frames.len() as u32 >= MAX_CALL_DEPTH {
567 return Err(VmError::CallStackOverflow(MAX_CALL_DEPTH));
568 }
569 self.frames.push(frame);
570 Ok(())
571 }
572
573 fn run_to(&mut self, base_depth: usize) -> Result<Value, VmError> {
578 loop {
579 if self.steps > self.step_limit {
580 let frame_idx = self.frames.len() - 1;
581 let fn_id = self.frames[frame_idx].fn_id;
582 let fn_name = &self.program.functions[fn_id as usize].name;
583 return Err(VmError::Panic(format!(
584 "step limit exceeded in `{fn_name}` ({} > {})",
585 self.steps, self.step_limit,
586 )));
587 }
588 self.steps += 1;
589 let frame_idx = self.frames.len() - 1;
590 let pc = self.frames[frame_idx].pc;
591 let fn_id = self.frames[frame_idx].fn_id;
592 let code = &self.program.functions[fn_id as usize].code;
593 if pc >= code.len() {
594 let fn_name = &self.program.functions[fn_id as usize].name;
595 return Err(VmError::Panic(format!("ran past end of code in `{fn_name}`")));
596 }
597 let op = code[pc].clone();
598 self.frames[frame_idx].pc = pc + 1;
599
600 match op {
601 Op::PushConst(i) => {
602 let c = &self.program.constants[i as usize];
603 self.stack.push(const_to_value(c));
604 }
605 Op::Pop => { self.pop()?; }
606 Op::Dup => {
607 let v = self.peek()?.clone();
608 self.stack.push(v);
609 }
610 Op::LoadLocal(i) => {
611 let v = self.frames[frame_idx].locals[i as usize].clone();
612 self.stack.push(v);
613 }
614 Op::StoreLocal(i) => {
615 let v = self.pop()?;
616 self.frames[frame_idx].locals[i as usize] = v;
617 }
618 Op::MakeRecord { field_name_indices } => {
619 let n = field_name_indices.len();
620 let mut values: Vec<Value> = (0..n).map(|_| Value::Unit).collect();
621 for i in (0..n).rev() {
622 values[i] = self.pop()?;
623 }
624 let mut rec = IndexMap::new();
625 for (i, val) in values.into_iter().enumerate() {
626 let name = match &self.program.constants[field_name_indices[i] as usize] {
627 Const::FieldName(s) => s.clone(),
628 _ => return Err(VmError::TypeMismatch("expected FieldName const".into())),
629 };
630 rec.insert(name, val);
631 }
632 self.stack.push(Value::Record(rec));
633 }
634 Op::MakeTuple(n) => {
635 let mut items: Vec<Value> = (0..n).map(|_| Value::Unit).collect();
636 for i in (0..n as usize).rev() { items[i] = self.pop()?; }
637 self.stack.push(Value::Tuple(items));
638 }
639 Op::MakeList(n) => {
640 let mut items: Vec<Value> = (0..n).map(|_| Value::Unit).collect();
641 for i in (0..n as usize).rev() { items[i] = self.pop()?; }
642 self.stack.push(Value::List(items));
643 }
644 Op::MakeVariant { name_idx, arity } => {
645 let mut args: Vec<Value> = (0..arity).map(|_| Value::Unit).collect();
646 for i in (0..arity as usize).rev() { args[i] = self.pop()?; }
647 let name = match &self.program.constants[name_idx as usize] {
648 Const::VariantName(s) => s.clone(),
649 _ => return Err(VmError::TypeMismatch("expected VariantName const".into())),
650 };
651 self.stack.push(Value::Variant { name, args });
652 }
653 Op::GetField(i) => {
654 let name = match &self.program.constants[i as usize] {
655 Const::FieldName(s) => s.clone(),
656 _ => return Err(VmError::TypeMismatch("expected FieldName const".into())),
657 };
658 let v = self.pop()?;
659 match v {
660 Value::Record(r) => {
661 let v = r.get(&name).cloned()
662 .ok_or_else(|| VmError::TypeMismatch(format!("missing field `{name}`")))?;
663 self.stack.push(v);
664 }
665 other => return Err(VmError::TypeMismatch(format!("GetField on non-record: {other:?}"))),
666 }
667 }
668 Op::GetElem(i) => {
669 let v = self.pop()?;
670 match v {
671 Value::Tuple(items) => {
672 let v = items.into_iter().nth(i as usize)
673 .ok_or_else(|| VmError::TypeMismatch(format!("tuple index {i} out of range")))?;
674 self.stack.push(v);
675 }
676 other => return Err(VmError::TypeMismatch(format!("GetElem on non-tuple: {other:?}"))),
677 }
678 }
679 Op::TestVariant(i) => {
680 let name = match &self.program.constants[i as usize] {
681 Const::VariantName(s) => s.clone(),
682 _ => return Err(VmError::TypeMismatch("expected VariantName const".into())),
683 };
684 let v = self.pop()?;
685 match &v {
686 Value::Variant { name: vname, .. } => {
687 self.stack.push(Value::Bool(vname == &name));
688 }
689 other => return Err(VmError::TypeMismatch(format!("TestVariant on non-variant: {other:?}"))),
692 }
693 }
694 Op::GetVariant(_i) => {
695 let v = self.pop()?;
696 match v {
697 Value::Variant { args, .. } => {
698 self.stack.push(Value::Tuple(args));
699 }
700 other => return Err(VmError::TypeMismatch(format!("GetVariant on non-variant: {other:?}"))),
701 }
702 }
703 Op::GetVariantArg(i) => {
704 let v = self.pop()?;
705 match v {
706 Value::Variant { mut args, .. } => {
707 if (i as usize) >= args.len() {
708 return Err(VmError::TypeMismatch("variant arg index oob".into()));
709 }
710 self.stack.push(args.swap_remove(i as usize));
711 }
712 other => return Err(VmError::TypeMismatch(format!("GetVariantArg on non-variant: {other:?}"))),
713 }
714 }
715 Op::GetListLen => {
716 let v = self.pop()?;
717 match v {
718 Value::List(items) => self.stack.push(Value::Int(items.len() as i64)),
719 other => return Err(VmError::TypeMismatch(format!("GetListLen on non-list: {other:?}"))),
720 }
721 }
722 Op::GetListElem(i) => {
723 let v = self.pop()?;
724 match v {
725 Value::List(items) => {
726 let v = items.into_iter().nth(i as usize)
727 .ok_or_else(|| VmError::TypeMismatch("list index oob".into()))?;
728 self.stack.push(v);
729 }
730 other => return Err(VmError::TypeMismatch(format!("GetListElem on non-list: {other:?}"))),
731 }
732 }
733 Op::GetListElemDyn => {
734 let idx = match self.pop()? {
736 Value::Int(n) => n as usize,
737 other => return Err(VmError::TypeMismatch(format!("GetListElemDyn idx: {other:?}"))),
738 };
739 let v = self.pop()?;
740 match v {
741 Value::List(items) => {
742 let v = items.into_iter().nth(idx)
743 .ok_or_else(|| VmError::TypeMismatch("list index oob".into()))?;
744 self.stack.push(v);
745 }
746 other => return Err(VmError::TypeMismatch(format!("GetListElemDyn on non-list: {other:?}"))),
747 }
748 }
749 Op::ListAppend => {
750 let value = self.pop()?;
751 let list = self.pop()?;
752 match list {
753 Value::List(mut items) => {
754 items.push(value);
755 self.stack.push(Value::List(items));
756 }
757 other => return Err(VmError::TypeMismatch(format!("ListAppend on non-list: {other:?}"))),
758 }
759 }
760 Op::Jump(off) => {
761 let new_pc = (self.frames[frame_idx].pc as i32 + off) as usize;
762 self.frames[frame_idx].pc = new_pc;
763 }
764 Op::JumpIf(off) => {
765 let v = self.pop()?;
766 if v.as_bool() {
767 let new_pc = (self.frames[frame_idx].pc as i32 + off) as usize;
768 self.frames[frame_idx].pc = new_pc;
769 }
770 }
771 Op::JumpIfNot(off) => {
772 let v = self.pop()?;
773 if !v.as_bool() {
774 let new_pc = (self.frames[frame_idx].pc as i32 + off) as usize;
775 self.frames[frame_idx].pc = new_pc;
776 }
777 }
778 Op::MakeClosure { fn_id, capture_count } => {
779 let n = capture_count as usize;
780 let mut captures: Vec<Value> = (0..n).map(|_| Value::Unit).collect();
781 for i in (0..n).rev() { captures[i] = self.pop()?; }
782 let body_hash = self.program.functions[fn_id as usize].body_hash;
785 self.stack.push(Value::Closure { fn_id, body_hash, captures });
786 }
787 Op::CallClosure { arity, node_id_idx } => {
788 let mut args: Vec<Value> = (0..arity).map(|_| Value::Unit).collect();
789 for i in (0..arity as usize).rev() { args[i] = self.pop()?; }
790 let closure = self.pop()?;
791 let (fn_id, captures) = match closure {
792 Value::Closure { fn_id, captures, .. } => (fn_id, captures),
793 other => return Err(VmError::TypeMismatch(format!("CallClosure on non-closure: {other:?}"))),
794 };
795 let node_id = const_str(&self.program.constants, node_id_idx);
796 let callee_name = self.program.functions[fn_id as usize].name.clone();
797 let budget_cost = call_budget_cost(&self.program.functions[fn_id as usize]);
798 if budget_cost > 0 {
799 self.handler.note_call_budget(budget_cost)
800 .map_err(VmError::Effect)?;
801 }
802 let mut combined = captures;
803 combined.extend(args);
804 self.tracer.enter_call(&node_id, &callee_name, &combined);
805 let f = &self.program.functions[fn_id as usize];
806 let mut locals = vec![Value::Unit; f.locals_count.max(f.arity) as usize];
807 for (i, v) in combined.into_iter().enumerate() { locals[i] = v; }
808 self.push_frame(Frame {
809 fn_id, pc: 0, locals, stack_base: self.stack.len(),
810 trace_kind: FrameKind::Call(node_id),
811 memo_key: None,
816 })?;
817 }
818 Op::SortByKey { node_id_idx: _ } => {
819 let f = self.pop()?;
827 let xs = self.pop()?;
828 let items = match xs {
829 Value::List(v) => v,
830 other => return Err(VmError::TypeMismatch(
831 format!("SortByKey requires a List, got: {other:?}"))),
832 };
833 if !matches!(f, Value::Closure { .. }) {
834 return Err(VmError::TypeMismatch(
835 format!("SortByKey requires a closure, got: {f:?}")));
836 }
837 let mut keyed: Vec<(Value, Value)> = Vec::with_capacity(items.len());
838 for item in items {
839 let key = self.invoke_closure_value(f.clone(), vec![item.clone()])?;
840 keyed.push((key, item));
841 }
842 keyed.sort_by(|(ka, _), (kb, _)| compare_sort_keys(ka, kb));
843 let sorted: Vec<Value> = keyed.into_iter().map(|(_, v)| v).collect();
844 self.stack.push(Value::List(sorted));
845 }
846 Op::ParallelMap { node_id_idx: _ } => {
847 let f = self.pop()?;
858 let xs = self.pop()?;
859 let items = match xs {
860 Value::List(v) => v,
861 other => return Err(VmError::TypeMismatch(
862 format!("ParallelMap requires a List, got: {other:?}"))),
863 };
864 if !matches!(f, Value::Closure { .. }) {
865 return Err(VmError::TypeMismatch(
866 format!("ParallelMap requires a closure, got: {f:?}")));
867 }
868 let n_workers = par_max_concurrency().max(1).min(items.len().max(1));
875 let mut worker_handlers: Vec<Box<dyn EffectHandler + Send>> =
876 Vec::with_capacity(n_workers);
877 for _ in 0..n_workers {
878 worker_handlers.push(
879 self.handler
880 .spawn_for_worker()
881 .unwrap_or_else(|| Box::new(DenyAllEffects)),
882 );
883 }
884 let results = par_map_run(self.program, f, items, worker_handlers)?;
885 self.stack.push(Value::List(results));
886 }
887 Op::Call { fn_id, arity, node_id_idx } => {
888 let mut args: Vec<Value> = (0..arity).map(|_| Value::Unit).collect();
889 for i in (0..arity as usize).rev() { args[i] = self.pop()?; }
890 let node_id = const_str(&self.program.constants, node_id_idx);
891 let callee_name = self.program.functions[fn_id as usize].name.clone();
892 let budget_cost = call_budget_cost(&self.program.functions[fn_id as usize]);
893 if budget_cost > 0 {
894 self.handler.note_call_budget(budget_cost)
895 .map_err(VmError::Effect)?;
896 }
897 let refinements = self.program.functions[fn_id as usize]
906 .refinements.clone();
907 for (i, refinement) in refinements.iter().enumerate() {
908 if let Some(r) = refinement {
909 let arg = args.get(i).cloned().unwrap_or(Value::Unit);
910 match eval_refinement(&r.predicate, &r.binding, &arg) {
911 Ok(true) => { }
912 Ok(false) => {
913 return Err(VmError::RefinementFailed {
914 fn_name: callee_name.clone(),
915 param_index: i,
916 binding: r.binding.clone(),
917 reason: format!(
918 "predicate failed for {} = {arg:?}",
919 r.binding),
920 });
921 }
922 Err(reason) => {
923 return Err(VmError::RefinementFailed {
924 fn_name: callee_name.clone(),
925 param_index: i,
926 binding: r.binding.clone(),
927 reason,
928 });
929 }
930 }
931 }
932 }
933 let f = &self.program.functions[fn_id as usize];
939 let memo_key: Option<(u32, [u8; 16])> = if f.effects.is_empty() {
940 Some((fn_id, hash_call_args(&args)))
941 } else {
942 None
943 };
944 if let Some(key) = memo_key {
945 if let Some(cached) = self.pure_memo.get(&key).cloned() {
946 self.pure_memo_hits += 1;
947 self.tracer.enter_call(&node_id, &callee_name, &args);
948 self.tracer.exit_ok(&cached);
949 self.stack.push(cached);
950 continue;
951 }
952 self.pure_memo_misses += 1;
953 }
954 self.tracer.enter_call(&node_id, &callee_name, &args);
955 let mut locals = vec![Value::Unit; f.locals_count.max(f.arity) as usize];
956 for (i, v) in args.into_iter().enumerate() { locals[i] = v; }
957 self.push_frame(Frame {
958 fn_id, pc: 0, locals, stack_base: self.stack.len(),
959 trace_kind: FrameKind::Call(node_id),
960 memo_key,
961 })?;
962 }
963 Op::TailCall { fn_id, arity, node_id_idx } => {
964 let mut args: Vec<Value> = (0..arity).map(|_| Value::Unit).collect();
965 for i in (0..arity as usize).rev() { args[i] = self.pop()?; }
966 let node_id = const_str(&self.program.constants, node_id_idx);
967 let callee_name = self.program.functions[fn_id as usize].name.clone();
968 let budget_cost = call_budget_cost(&self.program.functions[fn_id as usize]);
969 if budget_cost > 0 {
970 self.handler.note_call_budget(budget_cost)
971 .map_err(VmError::Effect)?;
972 }
973 let refinements = self.program.functions[fn_id as usize]
976 .refinements.clone();
977 for (i, refinement) in refinements.iter().enumerate() {
978 if let Some(r) = refinement {
979 let arg = args.get(i).cloned().unwrap_or(Value::Unit);
980 match eval_refinement(&r.predicate, &r.binding, &arg) {
981 Ok(true) => {}
982 Ok(false) => return Err(VmError::RefinementFailed {
983 fn_name: callee_name.clone(),
984 param_index: i,
985 binding: r.binding.clone(),
986 reason: format!(
987 "predicate failed for {} = {arg:?}",
988 r.binding),
989 }),
990 Err(reason) => return Err(VmError::RefinementFailed {
991 fn_name: callee_name.clone(),
992 param_index: i,
993 binding: r.binding.clone(),
994 reason,
995 }),
996 }
997 }
998 }
999 self.tracer.exit_call_tail();
1003 self.tracer.enter_call(&node_id, &callee_name, &args);
1004 let f = &self.program.functions[fn_id as usize];
1005 let frame = self.frames.last_mut().unwrap();
1006 frame.fn_id = fn_id;
1007 frame.pc = 0;
1008 frame.locals = vec![Value::Unit; f.locals_count.max(f.arity) as usize];
1009 for (i, v) in args.into_iter().enumerate() { frame.locals[i] = v; }
1010 frame.trace_kind = FrameKind::Call(node_id);
1011 }
1012 Op::EffectCall { kind_idx, op_idx, arity, node_id_idx } => {
1013 let mut args: Vec<Value> = (0..arity).map(|_| Value::Unit).collect();
1014 for i in (0..arity as usize).rev() { args[i] = self.pop()?; }
1015 let kind = match &self.program.constants[kind_idx as usize] {
1016 Const::Str(s) => s.clone(),
1017 _ => return Err(VmError::TypeMismatch("expected Str const for effect kind".into())),
1018 };
1019 let op_name = match &self.program.constants[op_idx as usize] {
1020 Const::Str(s) => s.clone(),
1021 _ => return Err(VmError::TypeMismatch("expected Str const for effect op".into())),
1022 };
1023 let node_id = const_str(&self.program.constants, node_id_idx);
1024 self.tracer.enter_effect(&node_id, &kind, &op_name, &args);
1025 let result = match self.tracer.override_effect(&node_id) {
1026 Some(v) => Ok(v),
1027 None if (kind.as_str(), op_name.as_str()) == ("parser", "run")
1033 => self.run_parser_op(args.clone()),
1034 None => self.handler.dispatch(&kind, &op_name, args.clone()),
1035 };
1036 match result {
1037 Ok(v) => {
1038 self.tracer.exit_ok(&v);
1039 self.stack.push(v);
1040 }
1041 Err(e) => {
1042 self.tracer.exit_err(&e);
1043 return Err(VmError::Effect(e));
1044 }
1045 }
1046 }
1047 Op::Return => {
1048 let v = self.pop()?;
1049 let frame = self.frames.pop().unwrap();
1050 self.stack.truncate(frame.stack_base);
1052 if matches!(frame.trace_kind, FrameKind::Call(_)) {
1053 self.tracer.exit_ok(&v);
1054 }
1055 if let Some(key) = frame.memo_key {
1060 self.pure_memo.insert(key, v.clone());
1061 }
1062 if self.frames.len() <= base_depth {
1067 return Ok(v);
1068 }
1069 self.stack.push(v);
1070 }
1071 Op::Panic(i) => {
1072 let msg = match &self.program.constants[i as usize] {
1073 Const::Str(s) => s.clone(),
1074 _ => "panic".into(),
1075 };
1076 return Err(VmError::Panic(msg));
1077 }
1078 Op::IntAdd => self.bin_int(|a, b| Value::Int(a + b))?,
1080 Op::IntSub => self.bin_int(|a, b| Value::Int(a - b))?,
1081 Op::IntMul => self.bin_int(|a, b| Value::Int(a * b))?,
1082 Op::IntDiv => self.bin_int(|a, b| Value::Int(a / b))?,
1083 Op::IntMod => self.bin_int(|a, b| Value::Int(a % b))?,
1084 Op::IntNeg => {
1085 let a = self.pop()?.as_int();
1086 self.stack.push(Value::Int(-a));
1087 }
1088 Op::IntEq => self.bin_int(|a, b| Value::Bool(a == b))?,
1089 Op::IntLt => self.bin_int(|a, b| Value::Bool(a < b))?,
1090 Op::IntLe => self.bin_int(|a, b| Value::Bool(a <= b))?,
1091 Op::FloatAdd => self.bin_float(|a, b| Value::Float(a + b))?,
1092 Op::FloatSub => self.bin_float(|a, b| Value::Float(a - b))?,
1093 Op::FloatMul => self.bin_float(|a, b| Value::Float(a * b))?,
1094 Op::FloatDiv => self.bin_float(|a, b| Value::Float(a / b))?,
1095 Op::FloatNeg => {
1096 let a = self.pop()?.as_float();
1097 self.stack.push(Value::Float(-a));
1098 }
1099 Op::FloatEq => self.bin_float(|a, b| Value::Bool(a == b))?,
1100 Op::FloatLt => self.bin_float(|a, b| Value::Bool(a < b))?,
1101 Op::FloatLe => self.bin_float(|a, b| Value::Bool(a <= b))?,
1102 Op::NumAdd => {
1103 let b = self.pop()?;
1107 let a = self.pop()?;
1108 match (a, b) {
1109 (Value::Int(x), Value::Int(y)) => self.stack.push(Value::Int(x + y)),
1110 (Value::Float(x), Value::Float(y)) => self.stack.push(Value::Float(x + y)),
1111 (Value::Str(x), Value::Str(y)) => {
1112 let mut s = x;
1113 s.push_str(&y);
1114 self.stack.push(Value::Str(s));
1115 }
1116 (a, b) => return Err(VmError::TypeMismatch(format!("Num op: {a:?} {b:?}"))),
1117 }
1118 }
1119 Op::NumSub => self.bin_num(|a, b| Value::Int(a - b), |a, b| Value::Float(a - b))?,
1120 Op::NumMul => self.bin_num(|a, b| Value::Int(a * b), |a, b| Value::Float(a * b))?,
1121 Op::NumDiv => self.bin_num(|a, b| Value::Int(a / b), |a, b| Value::Float(a / b))?,
1122 Op::NumMod => self.bin_int(|a, b| Value::Int(a % b))?,
1123 Op::NumNeg => {
1124 let v = self.pop()?;
1125 match v {
1126 Value::Int(n) => self.stack.push(Value::Int(-n)),
1127 Value::Float(f) => self.stack.push(Value::Float(-f)),
1128 other => return Err(VmError::TypeMismatch(format!("NumNeg on {other:?}"))),
1129 }
1130 }
1131 Op::NumEq => self.bin_eq()?,
1132 Op::NumLt => self.bin_ord(|a, b| Value::Bool(a < b), |a, b| Value::Bool(a < b), |a, b| Value::Bool(a < b))?,
1133 Op::NumLe => self.bin_ord(|a, b| Value::Bool(a <= b), |a, b| Value::Bool(a <= b), |a, b| Value::Bool(a <= b))?,
1134 Op::BoolAnd => {
1135 let b = self.pop()?.as_bool();
1136 let a = self.pop()?.as_bool();
1137 self.stack.push(Value::Bool(a && b));
1138 }
1139 Op::BoolOr => {
1140 let b = self.pop()?.as_bool();
1141 let a = self.pop()?.as_bool();
1142 self.stack.push(Value::Bool(a || b));
1143 }
1144 Op::BoolNot => {
1145 let a = self.pop()?.as_bool();
1146 self.stack.push(Value::Bool(!a));
1147 }
1148 Op::StrConcat => {
1149 let b = self.pop()?;
1150 let a = self.pop()?;
1151 let s = format!("{}{}", a.as_str(), b.as_str());
1152 self.stack.push(Value::Str(s));
1153 }
1154 Op::StrLen => {
1155 let v = self.pop()?;
1156 self.stack.push(Value::Int(v.as_str().len() as i64));
1157 }
1158 Op::StrEq => {
1159 let b = self.pop()?;
1160 let a = self.pop()?;
1161 self.stack.push(Value::Bool(a.as_str() == b.as_str()));
1162 }
1163 Op::BytesLen => {
1164 let v = self.pop()?;
1165 match v {
1166 Value::Bytes(b) => self.stack.push(Value::Int(b.len() as i64)),
1167 other => return Err(VmError::TypeMismatch(format!("BytesLen on {other:?}"))),
1168 }
1169 }
1170 Op::BytesEq => {
1171 let b = self.pop()?;
1172 let a = self.pop()?;
1173 let eq = match (a, b) {
1174 (Value::Bytes(x), Value::Bytes(y)) => x == y,
1175 _ => return Err(VmError::TypeMismatch("BytesEq operands".into())),
1176 };
1177 self.stack.push(Value::Bool(eq));
1178 }
1179 }
1180 }
1181 }
1182
1183 fn pop(&mut self) -> Result<Value, VmError> {
1184 self.stack.pop().ok_or(VmError::StackUnderflow)
1185 }
1186 fn peek(&self) -> Result<&Value, VmError> {
1187 self.stack.last().ok_or(VmError::StackUnderflow)
1188 }
1189
1190 fn bin_int(&mut self, f: impl Fn(i64, i64) -> Value) -> Result<(), VmError> {
1191 let b = self.pop()?.as_int();
1192 let a = self.pop()?.as_int();
1193 self.stack.push(f(a, b));
1194 Ok(())
1195 }
1196 fn bin_float(&mut self, f: impl Fn(f64, f64) -> Value) -> Result<(), VmError> {
1197 let b = self.pop()?.as_float();
1198 let a = self.pop()?.as_float();
1199 self.stack.push(f(a, b));
1200 Ok(())
1201 }
1202 fn bin_num(
1203 &mut self,
1204 i: impl Fn(i64, i64) -> Value,
1205 f: impl Fn(f64, f64) -> Value,
1206 ) -> Result<(), VmError> {
1207 let b = self.pop()?;
1208 let a = self.pop()?;
1209 match (a, b) {
1210 (Value::Int(x), Value::Int(y)) => { self.stack.push(i(x, y)); Ok(()) }
1211 (Value::Float(x), Value::Float(y)) => { self.stack.push(f(x, y)); Ok(()) }
1212 (a, b) => Err(VmError::TypeMismatch(format!("Num op: {a:?} {b:?}"))),
1213 }
1214 }
1215
1216 fn bin_ord(
1220 &mut self,
1221 i: impl Fn(i64, i64) -> Value,
1222 f: impl Fn(f64, f64) -> Value,
1223 s: impl Fn(&str, &str) -> Value,
1224 ) -> Result<(), VmError> {
1225 let b = self.pop()?;
1226 let a = self.pop()?;
1227 match (a, b) {
1228 (Value::Int(x), Value::Int(y)) => { self.stack.push(i(x, y)); Ok(()) }
1229 (Value::Float(x), Value::Float(y)) => { self.stack.push(f(x, y)); Ok(()) }
1230 (Value::Str(x), Value::Str(y)) => { self.stack.push(s(&x, &y)); Ok(()) }
1231 (a, b) => Err(VmError::TypeMismatch(format!("Num op: {a:?} {b:?}"))),
1232 }
1233 }
1234 fn bin_eq(&mut self) -> Result<(), VmError> {
1235 let b = self.pop()?;
1236 let a = self.pop()?;
1237 self.stack.push(Value::Bool(a == b));
1238 Ok(())
1239 }
1240}
1241
1242fn const_to_value(c: &Const) -> Value {
1243 match c {
1244 Const::Int(n) => Value::Int(*n),
1245 Const::Float(f) => Value::Float(*f),
1246 Const::Bool(b) => Value::Bool(*b),
1247 Const::Str(s) => Value::Str(s.clone()),
1248 Const::Bytes(b) => Value::Bytes(b.clone()),
1249 Const::Unit => Value::Unit,
1250 Const::FieldName(s) | Const::VariantName(s) | Const::NodeId(s) => Value::Str(s.clone()),
1251 }
1252}