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 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 return Err(VmError::Panic(format!(
581 "step limit exceeded ({} > {})",
582 self.steps, self.step_limit,
583 )));
584 }
585 self.steps += 1;
586 let frame_idx = self.frames.len() - 1;
587 let pc = self.frames[frame_idx].pc;
588 let fn_id = self.frames[frame_idx].fn_id;
589 let code = &self.program.functions[fn_id as usize].code;
590 if pc >= code.len() {
591 return Err(VmError::Panic("ran past end of code".into()));
592 }
593 let op = code[pc].clone();
594 self.frames[frame_idx].pc = pc + 1;
595
596 match op {
597 Op::PushConst(i) => {
598 let c = &self.program.constants[i as usize];
599 self.stack.push(const_to_value(c));
600 }
601 Op::Pop => { self.pop()?; }
602 Op::Dup => {
603 let v = self.peek()?.clone();
604 self.stack.push(v);
605 }
606 Op::LoadLocal(i) => {
607 let v = self.frames[frame_idx].locals[i as usize].clone();
608 self.stack.push(v);
609 }
610 Op::StoreLocal(i) => {
611 let v = self.pop()?;
612 self.frames[frame_idx].locals[i as usize] = v;
613 }
614 Op::MakeRecord { field_name_indices } => {
615 let n = field_name_indices.len();
616 let mut values: Vec<Value> = (0..n).map(|_| Value::Unit).collect();
617 for i in (0..n).rev() {
618 values[i] = self.pop()?;
619 }
620 let mut rec = IndexMap::new();
621 for (i, val) in values.into_iter().enumerate() {
622 let name = match &self.program.constants[field_name_indices[i] as usize] {
623 Const::FieldName(s) => s.clone(),
624 _ => return Err(VmError::TypeMismatch("expected FieldName const".into())),
625 };
626 rec.insert(name, val);
627 }
628 self.stack.push(Value::Record(rec));
629 }
630 Op::MakeTuple(n) => {
631 let mut items: Vec<Value> = (0..n).map(|_| Value::Unit).collect();
632 for i in (0..n as usize).rev() { items[i] = self.pop()?; }
633 self.stack.push(Value::Tuple(items));
634 }
635 Op::MakeList(n) => {
636 let mut items: Vec<Value> = (0..n).map(|_| Value::Unit).collect();
637 for i in (0..n as usize).rev() { items[i] = self.pop()?; }
638 self.stack.push(Value::List(items));
639 }
640 Op::MakeVariant { name_idx, arity } => {
641 let mut args: Vec<Value> = (0..arity).map(|_| Value::Unit).collect();
642 for i in (0..arity as usize).rev() { args[i] = self.pop()?; }
643 let name = match &self.program.constants[name_idx as usize] {
644 Const::VariantName(s) => s.clone(),
645 _ => return Err(VmError::TypeMismatch("expected VariantName const".into())),
646 };
647 self.stack.push(Value::Variant { name, args });
648 }
649 Op::GetField(i) => {
650 let name = match &self.program.constants[i as usize] {
651 Const::FieldName(s) => s.clone(),
652 _ => return Err(VmError::TypeMismatch("expected FieldName const".into())),
653 };
654 let v = self.pop()?;
655 match v {
656 Value::Record(r) => {
657 let v = r.get(&name).cloned()
658 .ok_or_else(|| VmError::TypeMismatch(format!("missing field `{name}`")))?;
659 self.stack.push(v);
660 }
661 other => return Err(VmError::TypeMismatch(format!("GetField on non-record: {other:?}"))),
662 }
663 }
664 Op::GetElem(i) => {
665 let v = self.pop()?;
666 match v {
667 Value::Tuple(items) => {
668 let v = items.into_iter().nth(i as usize)
669 .ok_or_else(|| VmError::TypeMismatch(format!("tuple index {i} out of range")))?;
670 self.stack.push(v);
671 }
672 other => return Err(VmError::TypeMismatch(format!("GetElem on non-tuple: {other:?}"))),
673 }
674 }
675 Op::TestVariant(i) => {
676 let name = match &self.program.constants[i as usize] {
677 Const::VariantName(s) => s.clone(),
678 _ => return Err(VmError::TypeMismatch("expected VariantName const".into())),
679 };
680 let v = self.pop()?;
681 match &v {
682 Value::Variant { name: vname, .. } => {
683 self.stack.push(Value::Bool(vname == &name));
684 }
685 other => return Err(VmError::TypeMismatch(format!("TestVariant on non-variant: {other:?}"))),
688 }
689 }
690 Op::GetVariant(_i) => {
691 let v = self.pop()?;
692 match v {
693 Value::Variant { args, .. } => {
694 self.stack.push(Value::Tuple(args));
695 }
696 other => return Err(VmError::TypeMismatch(format!("GetVariant on non-variant: {other:?}"))),
697 }
698 }
699 Op::GetVariantArg(i) => {
700 let v = self.pop()?;
701 match v {
702 Value::Variant { mut args, .. } => {
703 if (i as usize) >= args.len() {
704 return Err(VmError::TypeMismatch("variant arg index oob".into()));
705 }
706 self.stack.push(args.swap_remove(i as usize));
707 }
708 other => return Err(VmError::TypeMismatch(format!("GetVariantArg on non-variant: {other:?}"))),
709 }
710 }
711 Op::GetListLen => {
712 let v = self.pop()?;
713 match v {
714 Value::List(items) => self.stack.push(Value::Int(items.len() as i64)),
715 other => return Err(VmError::TypeMismatch(format!("GetListLen on non-list: {other:?}"))),
716 }
717 }
718 Op::GetListElem(i) => {
719 let v = self.pop()?;
720 match v {
721 Value::List(items) => {
722 let v = items.into_iter().nth(i as usize)
723 .ok_or_else(|| VmError::TypeMismatch("list index oob".into()))?;
724 self.stack.push(v);
725 }
726 other => return Err(VmError::TypeMismatch(format!("GetListElem on non-list: {other:?}"))),
727 }
728 }
729 Op::GetListElemDyn => {
730 let idx = match self.pop()? {
732 Value::Int(n) => n as usize,
733 other => return Err(VmError::TypeMismatch(format!("GetListElemDyn idx: {other:?}"))),
734 };
735 let v = self.pop()?;
736 match v {
737 Value::List(items) => {
738 let v = items.into_iter().nth(idx)
739 .ok_or_else(|| VmError::TypeMismatch("list index oob".into()))?;
740 self.stack.push(v);
741 }
742 other => return Err(VmError::TypeMismatch(format!("GetListElemDyn on non-list: {other:?}"))),
743 }
744 }
745 Op::ListAppend => {
746 let value = self.pop()?;
747 let list = self.pop()?;
748 match list {
749 Value::List(mut items) => {
750 items.push(value);
751 self.stack.push(Value::List(items));
752 }
753 other => return Err(VmError::TypeMismatch(format!("ListAppend on non-list: {other:?}"))),
754 }
755 }
756 Op::Jump(off) => {
757 let new_pc = (self.frames[frame_idx].pc as i32 + off) as usize;
758 self.frames[frame_idx].pc = new_pc;
759 }
760 Op::JumpIf(off) => {
761 let v = self.pop()?;
762 if v.as_bool() {
763 let new_pc = (self.frames[frame_idx].pc as i32 + off) as usize;
764 self.frames[frame_idx].pc = new_pc;
765 }
766 }
767 Op::JumpIfNot(off) => {
768 let v = self.pop()?;
769 if !v.as_bool() {
770 let new_pc = (self.frames[frame_idx].pc as i32 + off) as usize;
771 self.frames[frame_idx].pc = new_pc;
772 }
773 }
774 Op::MakeClosure { fn_id, capture_count } => {
775 let n = capture_count as usize;
776 let mut captures: Vec<Value> = (0..n).map(|_| Value::Unit).collect();
777 for i in (0..n).rev() { captures[i] = self.pop()?; }
778 let body_hash = self.program.functions[fn_id as usize].body_hash;
781 self.stack.push(Value::Closure { fn_id, body_hash, captures });
782 }
783 Op::CallClosure { arity, node_id_idx } => {
784 let mut args: Vec<Value> = (0..arity).map(|_| Value::Unit).collect();
785 for i in (0..arity as usize).rev() { args[i] = self.pop()?; }
786 let closure = self.pop()?;
787 let (fn_id, captures) = match closure {
788 Value::Closure { fn_id, captures, .. } => (fn_id, captures),
789 other => return Err(VmError::TypeMismatch(format!("CallClosure on non-closure: {other:?}"))),
790 };
791 let node_id = const_str(&self.program.constants, node_id_idx);
792 let callee_name = self.program.functions[fn_id as usize].name.clone();
793 let budget_cost = call_budget_cost(&self.program.functions[fn_id as usize]);
794 if budget_cost > 0 {
795 self.handler.note_call_budget(budget_cost)
796 .map_err(VmError::Effect)?;
797 }
798 let mut combined = captures;
799 combined.extend(args);
800 self.tracer.enter_call(&node_id, &callee_name, &combined);
801 let f = &self.program.functions[fn_id as usize];
802 let mut locals = vec![Value::Unit; f.locals_count.max(f.arity) as usize];
803 for (i, v) in combined.into_iter().enumerate() { locals[i] = v; }
804 self.push_frame(Frame {
805 fn_id, pc: 0, locals, stack_base: self.stack.len(),
806 trace_kind: FrameKind::Call(node_id),
807 memo_key: None,
812 })?;
813 }
814 Op::SortByKey { node_id_idx: _ } => {
815 let f = self.pop()?;
823 let xs = self.pop()?;
824 let items = match xs {
825 Value::List(v) => v,
826 other => return Err(VmError::TypeMismatch(
827 format!("SortByKey requires a List, got: {other:?}"))),
828 };
829 if !matches!(f, Value::Closure { .. }) {
830 return Err(VmError::TypeMismatch(
831 format!("SortByKey requires a closure, got: {f:?}")));
832 }
833 let mut keyed: Vec<(Value, Value)> = Vec::with_capacity(items.len());
834 for item in items {
835 let key = self.invoke_closure_value(f.clone(), vec![item.clone()])?;
836 keyed.push((key, item));
837 }
838 keyed.sort_by(|(ka, _), (kb, _)| compare_sort_keys(ka, kb));
839 let sorted: Vec<Value> = keyed.into_iter().map(|(_, v)| v).collect();
840 self.stack.push(Value::List(sorted));
841 }
842 Op::ParallelMap { node_id_idx: _ } => {
843 let f = self.pop()?;
854 let xs = self.pop()?;
855 let items = match xs {
856 Value::List(v) => v,
857 other => return Err(VmError::TypeMismatch(
858 format!("ParallelMap requires a List, got: {other:?}"))),
859 };
860 if !matches!(f, Value::Closure { .. }) {
861 return Err(VmError::TypeMismatch(
862 format!("ParallelMap requires a closure, got: {f:?}")));
863 }
864 let n_workers = par_max_concurrency().max(1).min(items.len().max(1));
871 let mut worker_handlers: Vec<Box<dyn EffectHandler + Send>> =
872 Vec::with_capacity(n_workers);
873 for _ in 0..n_workers {
874 worker_handlers.push(
875 self.handler
876 .spawn_for_worker()
877 .unwrap_or_else(|| Box::new(DenyAllEffects)),
878 );
879 }
880 let results = par_map_run(self.program, f, items, worker_handlers)?;
881 self.stack.push(Value::List(results));
882 }
883 Op::Call { fn_id, arity, node_id_idx } => {
884 let mut args: Vec<Value> = (0..arity).map(|_| Value::Unit).collect();
885 for i in (0..arity as usize).rev() { args[i] = self.pop()?; }
886 let node_id = const_str(&self.program.constants, node_id_idx);
887 let callee_name = self.program.functions[fn_id as usize].name.clone();
888 let budget_cost = call_budget_cost(&self.program.functions[fn_id as usize]);
889 if budget_cost > 0 {
890 self.handler.note_call_budget(budget_cost)
891 .map_err(VmError::Effect)?;
892 }
893 let refinements = self.program.functions[fn_id as usize]
902 .refinements.clone();
903 for (i, refinement) in refinements.iter().enumerate() {
904 if let Some(r) = refinement {
905 let arg = args.get(i).cloned().unwrap_or(Value::Unit);
906 match eval_refinement(&r.predicate, &r.binding, &arg) {
907 Ok(true) => { }
908 Ok(false) => {
909 return Err(VmError::RefinementFailed {
910 fn_name: callee_name.clone(),
911 param_index: i,
912 binding: r.binding.clone(),
913 reason: format!(
914 "predicate failed for {} = {arg:?}",
915 r.binding),
916 });
917 }
918 Err(reason) => {
919 return Err(VmError::RefinementFailed {
920 fn_name: callee_name.clone(),
921 param_index: i,
922 binding: r.binding.clone(),
923 reason,
924 });
925 }
926 }
927 }
928 }
929 let f = &self.program.functions[fn_id as usize];
935 let memo_key: Option<(u32, [u8; 16])> = if f.effects.is_empty() {
936 Some((fn_id, hash_call_args(&args)))
937 } else {
938 None
939 };
940 if let Some(key) = memo_key {
941 if let Some(cached) = self.pure_memo.get(&key).cloned() {
942 self.pure_memo_hits += 1;
943 self.tracer.enter_call(&node_id, &callee_name, &args);
944 self.tracer.exit_ok(&cached);
945 self.stack.push(cached);
946 continue;
947 }
948 self.pure_memo_misses += 1;
949 }
950 self.tracer.enter_call(&node_id, &callee_name, &args);
951 let mut locals = vec![Value::Unit; f.locals_count.max(f.arity) as usize];
952 for (i, v) in args.into_iter().enumerate() { locals[i] = v; }
953 self.push_frame(Frame {
954 fn_id, pc: 0, locals, stack_base: self.stack.len(),
955 trace_kind: FrameKind::Call(node_id),
956 memo_key,
957 })?;
958 }
959 Op::TailCall { fn_id, arity, node_id_idx } => {
960 let mut args: Vec<Value> = (0..arity).map(|_| Value::Unit).collect();
961 for i in (0..arity as usize).rev() { args[i] = self.pop()?; }
962 let node_id = const_str(&self.program.constants, node_id_idx);
963 let callee_name = self.program.functions[fn_id as usize].name.clone();
964 let budget_cost = call_budget_cost(&self.program.functions[fn_id as usize]);
965 if budget_cost > 0 {
966 self.handler.note_call_budget(budget_cost)
967 .map_err(VmError::Effect)?;
968 }
969 let refinements = self.program.functions[fn_id as usize]
972 .refinements.clone();
973 for (i, refinement) in refinements.iter().enumerate() {
974 if let Some(r) = refinement {
975 let arg = args.get(i).cloned().unwrap_or(Value::Unit);
976 match eval_refinement(&r.predicate, &r.binding, &arg) {
977 Ok(true) => {}
978 Ok(false) => return Err(VmError::RefinementFailed {
979 fn_name: callee_name.clone(),
980 param_index: i,
981 binding: r.binding.clone(),
982 reason: format!(
983 "predicate failed for {} = {arg:?}",
984 r.binding),
985 }),
986 Err(reason) => return Err(VmError::RefinementFailed {
987 fn_name: callee_name.clone(),
988 param_index: i,
989 binding: r.binding.clone(),
990 reason,
991 }),
992 }
993 }
994 }
995 self.tracer.exit_call_tail();
999 self.tracer.enter_call(&node_id, &callee_name, &args);
1000 let f = &self.program.functions[fn_id as usize];
1001 let frame = self.frames.last_mut().unwrap();
1002 frame.fn_id = fn_id;
1003 frame.pc = 0;
1004 frame.locals = vec![Value::Unit; f.locals_count.max(f.arity) as usize];
1005 for (i, v) in args.into_iter().enumerate() { frame.locals[i] = v; }
1006 frame.trace_kind = FrameKind::Call(node_id);
1007 }
1008 Op::EffectCall { kind_idx, op_idx, arity, node_id_idx } => {
1009 let mut args: Vec<Value> = (0..arity).map(|_| Value::Unit).collect();
1010 for i in (0..arity as usize).rev() { args[i] = self.pop()?; }
1011 let kind = match &self.program.constants[kind_idx as usize] {
1012 Const::Str(s) => s.clone(),
1013 _ => return Err(VmError::TypeMismatch("expected Str const for effect kind".into())),
1014 };
1015 let op_name = match &self.program.constants[op_idx as usize] {
1016 Const::Str(s) => s.clone(),
1017 _ => return Err(VmError::TypeMismatch("expected Str const for effect op".into())),
1018 };
1019 let node_id = const_str(&self.program.constants, node_id_idx);
1020 self.tracer.enter_effect(&node_id, &kind, &op_name, &args);
1021 let result = match self.tracer.override_effect(&node_id) {
1022 Some(v) => Ok(v),
1023 None if (kind.as_str(), op_name.as_str()) == ("parser", "run")
1029 => self.run_parser_op(args.clone()),
1030 None => self.handler.dispatch(&kind, &op_name, args.clone()),
1031 };
1032 match result {
1033 Ok(v) => {
1034 self.tracer.exit_ok(&v);
1035 self.stack.push(v);
1036 }
1037 Err(e) => {
1038 self.tracer.exit_err(&e);
1039 return Err(VmError::Effect(e));
1040 }
1041 }
1042 }
1043 Op::Return => {
1044 let v = self.pop()?;
1045 let frame = self.frames.pop().unwrap();
1046 self.stack.truncate(frame.stack_base);
1048 if matches!(frame.trace_kind, FrameKind::Call(_)) {
1049 self.tracer.exit_ok(&v);
1050 }
1051 if let Some(key) = frame.memo_key {
1056 self.pure_memo.insert(key, v.clone());
1057 }
1058 if self.frames.len() <= base_depth {
1063 return Ok(v);
1064 }
1065 self.stack.push(v);
1066 }
1067 Op::Panic(i) => {
1068 let msg = match &self.program.constants[i as usize] {
1069 Const::Str(s) => s.clone(),
1070 _ => "panic".into(),
1071 };
1072 return Err(VmError::Panic(msg));
1073 }
1074 Op::IntAdd => self.bin_int(|a, b| Value::Int(a + b))?,
1076 Op::IntSub => self.bin_int(|a, b| Value::Int(a - b))?,
1077 Op::IntMul => self.bin_int(|a, b| Value::Int(a * b))?,
1078 Op::IntDiv => self.bin_int(|a, b| Value::Int(a / b))?,
1079 Op::IntMod => self.bin_int(|a, b| Value::Int(a % b))?,
1080 Op::IntNeg => {
1081 let a = self.pop()?.as_int();
1082 self.stack.push(Value::Int(-a));
1083 }
1084 Op::IntEq => self.bin_int(|a, b| Value::Bool(a == b))?,
1085 Op::IntLt => self.bin_int(|a, b| Value::Bool(a < b))?,
1086 Op::IntLe => self.bin_int(|a, b| Value::Bool(a <= b))?,
1087 Op::FloatAdd => self.bin_float(|a, b| Value::Float(a + b))?,
1088 Op::FloatSub => self.bin_float(|a, b| Value::Float(a - b))?,
1089 Op::FloatMul => self.bin_float(|a, b| Value::Float(a * b))?,
1090 Op::FloatDiv => self.bin_float(|a, b| Value::Float(a / b))?,
1091 Op::FloatNeg => {
1092 let a = self.pop()?.as_float();
1093 self.stack.push(Value::Float(-a));
1094 }
1095 Op::FloatEq => self.bin_float(|a, b| Value::Bool(a == b))?,
1096 Op::FloatLt => self.bin_float(|a, b| Value::Bool(a < b))?,
1097 Op::FloatLe => self.bin_float(|a, b| Value::Bool(a <= b))?,
1098 Op::NumAdd => {
1099 let b = self.pop()?;
1103 let a = self.pop()?;
1104 match (a, b) {
1105 (Value::Int(x), Value::Int(y)) => self.stack.push(Value::Int(x + y)),
1106 (Value::Float(x), Value::Float(y)) => self.stack.push(Value::Float(x + y)),
1107 (Value::Str(x), Value::Str(y)) => {
1108 let mut s = x;
1109 s.push_str(&y);
1110 self.stack.push(Value::Str(s));
1111 }
1112 (a, b) => return Err(VmError::TypeMismatch(format!("Num op: {a:?} {b:?}"))),
1113 }
1114 }
1115 Op::NumSub => self.bin_num(|a, b| Value::Int(a - b), |a, b| Value::Float(a - b))?,
1116 Op::NumMul => self.bin_num(|a, b| Value::Int(a * b), |a, b| Value::Float(a * b))?,
1117 Op::NumDiv => self.bin_num(|a, b| Value::Int(a / b), |a, b| Value::Float(a / b))?,
1118 Op::NumMod => self.bin_int(|a, b| Value::Int(a % b))?,
1119 Op::NumNeg => {
1120 let v = self.pop()?;
1121 match v {
1122 Value::Int(n) => self.stack.push(Value::Int(-n)),
1123 Value::Float(f) => self.stack.push(Value::Float(-f)),
1124 other => return Err(VmError::TypeMismatch(format!("NumNeg on {other:?}"))),
1125 }
1126 }
1127 Op::NumEq => self.bin_eq()?,
1128 Op::NumLt => self.bin_ord(|a, b| Value::Bool(a < b), |a, b| Value::Bool(a < b), |a, b| Value::Bool(a < b))?,
1129 Op::NumLe => self.bin_ord(|a, b| Value::Bool(a <= b), |a, b| Value::Bool(a <= b), |a, b| Value::Bool(a <= b))?,
1130 Op::BoolAnd => {
1131 let b = self.pop()?.as_bool();
1132 let a = self.pop()?.as_bool();
1133 self.stack.push(Value::Bool(a && b));
1134 }
1135 Op::BoolOr => {
1136 let b = self.pop()?.as_bool();
1137 let a = self.pop()?.as_bool();
1138 self.stack.push(Value::Bool(a || b));
1139 }
1140 Op::BoolNot => {
1141 let a = self.pop()?.as_bool();
1142 self.stack.push(Value::Bool(!a));
1143 }
1144 Op::StrConcat => {
1145 let b = self.pop()?;
1146 let a = self.pop()?;
1147 let s = format!("{}{}", a.as_str(), b.as_str());
1148 self.stack.push(Value::Str(s));
1149 }
1150 Op::StrLen => {
1151 let v = self.pop()?;
1152 self.stack.push(Value::Int(v.as_str().len() as i64));
1153 }
1154 Op::StrEq => {
1155 let b = self.pop()?;
1156 let a = self.pop()?;
1157 self.stack.push(Value::Bool(a.as_str() == b.as_str()));
1158 }
1159 Op::BytesLen => {
1160 let v = self.pop()?;
1161 match v {
1162 Value::Bytes(b) => self.stack.push(Value::Int(b.len() as i64)),
1163 other => return Err(VmError::TypeMismatch(format!("BytesLen on {other:?}"))),
1164 }
1165 }
1166 Op::BytesEq => {
1167 let b = self.pop()?;
1168 let a = self.pop()?;
1169 let eq = match (a, b) {
1170 (Value::Bytes(x), Value::Bytes(y)) => x == y,
1171 _ => return Err(VmError::TypeMismatch("BytesEq operands".into())),
1172 };
1173 self.stack.push(Value::Bool(eq));
1174 }
1175 }
1176 }
1177 }
1178
1179 fn pop(&mut self) -> Result<Value, VmError> {
1180 self.stack.pop().ok_or(VmError::StackUnderflow)
1181 }
1182 fn peek(&self) -> Result<&Value, VmError> {
1183 self.stack.last().ok_or(VmError::StackUnderflow)
1184 }
1185
1186 fn bin_int(&mut self, f: impl Fn(i64, i64) -> Value) -> Result<(), VmError> {
1187 let b = self.pop()?.as_int();
1188 let a = self.pop()?.as_int();
1189 self.stack.push(f(a, b));
1190 Ok(())
1191 }
1192 fn bin_float(&mut self, f: impl Fn(f64, f64) -> Value) -> Result<(), VmError> {
1193 let b = self.pop()?.as_float();
1194 let a = self.pop()?.as_float();
1195 self.stack.push(f(a, b));
1196 Ok(())
1197 }
1198 fn bin_num(
1199 &mut self,
1200 i: impl Fn(i64, i64) -> Value,
1201 f: impl Fn(f64, f64) -> Value,
1202 ) -> Result<(), VmError> {
1203 let b = self.pop()?;
1204 let a = self.pop()?;
1205 match (a, b) {
1206 (Value::Int(x), Value::Int(y)) => { self.stack.push(i(x, y)); Ok(()) }
1207 (Value::Float(x), Value::Float(y)) => { self.stack.push(f(x, y)); Ok(()) }
1208 (a, b) => Err(VmError::TypeMismatch(format!("Num op: {a:?} {b:?}"))),
1209 }
1210 }
1211
1212 fn bin_ord(
1216 &mut self,
1217 i: impl Fn(i64, i64) -> Value,
1218 f: impl Fn(f64, f64) -> Value,
1219 s: impl Fn(&str, &str) -> Value,
1220 ) -> Result<(), VmError> {
1221 let b = self.pop()?;
1222 let a = self.pop()?;
1223 match (a, b) {
1224 (Value::Int(x), Value::Int(y)) => { self.stack.push(i(x, y)); Ok(()) }
1225 (Value::Float(x), Value::Float(y)) => { self.stack.push(f(x, y)); Ok(()) }
1226 (Value::Str(x), Value::Str(y)) => { self.stack.push(s(&x, &y)); Ok(()) }
1227 (a, b) => Err(VmError::TypeMismatch(format!("Num op: {a:?} {b:?}"))),
1228 }
1229 }
1230 fn bin_eq(&mut self) -> Result<(), VmError> {
1231 let b = self.pop()?;
1232 let a = self.pop()?;
1233 self.stack.push(Value::Bool(a == b));
1234 Ok(())
1235 }
1236}
1237
1238fn const_to_value(c: &Const) -> Value {
1239 match c {
1240 Const::Int(n) => Value::Int(*n),
1241 Const::Float(f) => Value::Float(*f),
1242 Const::Bool(b) => Value::Bool(*b),
1243 Const::Str(s) => Value::Str(s.clone()),
1244 Const::Bytes(b) => Value::Bytes(b.clone()),
1245 Const::Unit => Value::Unit,
1246 Const::FieldName(s) | Const::VariantName(s) | Const::NodeId(s) => Value::Str(s.clone()),
1247 }
1248}