1use crate::op::*;
4use crate::program::*;
5use crate::value::{ActorCell, Value};
6use std::sync::{Arc, Mutex};
7use indexmap::IndexMap;
8use std::collections::VecDeque;
9
10#[derive(Debug, Clone, thiserror::Error)]
11pub enum VmError {
12 #[error("runtime panic: {0}")]
13 Panic(String),
14 #[error("type mismatch at runtime: {0}")]
15 TypeMismatch(String),
16 #[error("stack underflow")]
17 StackUnderflow,
18 #[error("unknown function: {0}")]
19 UnknownFunction(String),
20 #[error("effect handler error: {0}")]
21 Effect(String),
22 #[error("call stack overflow: recursion depth exceeded ({0})")]
23 CallStackOverflow(u32),
24 #[error("refinement violated: argument {param_index} of `{fn_name}` (binding `{binding}`): {reason}")]
31 RefinementFailed {
32 fn_name: String,
33 param_index: usize,
34 binding: String,
35 reason: String,
36 },
37}
38
39pub const MAX_CALL_DEPTH: u32 = 1024;
46
47pub trait EffectHandler {
50 fn dispatch(&mut self, kind: &str, op: &str, args: Vec<Value>) -> Result<Value, String>;
51
52 fn note_call_budget(&mut self, _budget_cost: u64) -> Result<(), String> {
59 Ok(())
60 }
61
62 fn spawn_for_worker(&self) -> Option<Box<dyn EffectHandler + Send>> {
78 None
79 }
80}
81
82impl<'a> crate::parser_runtime::ClosureCaller for Vm<'a> {
89 fn call_closure(&mut self, closure: Value, args: Vec<Value>) -> Result<Value, String> {
90 self.invoke_closure_value(closure, args)
91 .map_err(|e| format!("{e:?}"))
92 }
93}
94
95pub struct DenyAllEffects;
97impl EffectHandler for DenyAllEffects {
98 fn dispatch(&mut self, kind: &str, op: &str, _args: Vec<Value>) -> Result<Value, String> {
99 Err(format!("effects not permitted (attempted {kind}.{op})"))
100 }
101}
102
103pub trait Tracer {
106 fn enter_call(&mut self, node_id: &str, name: &str, args: &[Value]);
107 fn enter_effect(&mut self, node_id: &str, kind: &str, op: &str, args: &[Value]);
108 fn exit_ok(&mut self, value: &Value);
109 fn exit_err(&mut self, message: &str);
110 fn exit_call_tail(&mut self);
113 fn override_effect(&mut self, _node_id: &str) -> Option<Value> { None }
115}
116
117pub struct NullTracer;
119impl Tracer for NullTracer {
120 fn enter_call(&mut self, _: &str, _: &str, _: &[Value]) {}
121 fn enter_effect(&mut self, _: &str, _: &str, _: &str, _: &[Value]) {}
122 fn exit_ok(&mut self, _: &Value) {}
123 fn exit_err(&mut self, _: &str) {}
124 fn exit_call_tail(&mut self) {}
125}
126
127#[derive(Debug, Clone)]
128pub(crate) enum FrameKind {
129 Entry,
131 Call(#[allow(dead_code)] String),
134}
135
136pub struct Vm<'a> {
137 program: &'a Program,
138 handler: Box<dyn EffectHandler + 'a>,
139 pub(crate) tracer: Box<dyn Tracer + 'a>,
140 frames: Vec<Frame>,
142 stack: Vec<Value>,
143 pub step_limit: u64,
145 pub steps: u64,
146 pure_memo: std::collections::HashMap<(u32, [u8; 16]), Value>,
152 pub pure_memo_hits: u64,
154 pub pure_memo_misses: u64,
155 field_ics: std::collections::HashMap<u64, usize>,
162 locals_storage: Vec<Value>,
176}
177
178struct Frame {
179 fn_id: u32,
180 pc: usize,
181 locals_start: usize,
186 locals_len: usize,
187 stack_base: usize,
189 trace_kind: FrameKind,
190 memo_key: Option<(u32, [u8; 16])>,
196}
197
198fn call_budget_cost(f: &crate::program::Function) -> u64 {
206 let mut total: u64 = 0;
207 for e in &f.effects {
208 if e.kind == "budget" {
209 if let Some(crate::program::EffectArg::Int(n)) = &e.arg {
210 if *n >= 0 {
211 total = total.saturating_add(*n as u64);
212 }
213 }
214 }
215 }
216 total
217}
218
219fn hash_call_args(args: &[Value]) -> [u8; 16] {
226 use sha2::{Digest, Sha256};
227 let json = serde_json::Value::Array(args.iter().map(Value::to_json).collect());
228 let canonical = lex_ast::canon_json::hash_canonical(&json);
229 let mut hasher = Sha256::new();
230 hasher.update(canonical);
231 let full = hasher.finalize();
232 let mut out = [0u8; 16];
233 out.copy_from_slice(&full[..16]);
234 out
235}
236
237fn eval_refinement(
246 predicate: &lex_ast::CExpr,
247 binding: &str,
248 arg: &Value,
249) -> Result<bool, String> {
250 match eval_refinement_inner(predicate, binding, arg) {
251 Ok(Value::Bool(b)) => Ok(b),
252 Ok(other) => Err(format!("predicate didn't reduce to a Bool, got {other:?}")),
253 Err(e) => Err(e),
254 }
255}
256
257fn eval_refinement_inner(
258 e: &lex_ast::CExpr,
259 binding: &str,
260 arg: &Value,
261) -> Result<Value, String> {
262 use lex_ast::{CExpr, CLit};
263 match e {
264 CExpr::Literal { value } => Ok(match value {
265 CLit::Int { value } => Value::Int(*value),
266 CLit::Float { value } => Value::Float(value.parse().unwrap_or(0.0)),
267 CLit::Bool { value } => Value::Bool(*value),
268 CLit::Str { value } => Value::Str(value.as_str().into()),
269 CLit::Bytes { value } => Value::Str(value.as_str().into()), CLit::Unit => Value::Unit,
271 }),
272 CExpr::Var { name } if name == binding => Ok(arg.clone()),
273 CExpr::Var { name } => Err(format!(
274 "predicate references free var `{name}`; runtime check \
275 only resolves the binding (slice 4 will plumb call-site \
276 context)")),
277 CExpr::UnaryOp { op, expr } => {
278 let v = eval_refinement_inner(expr, binding, arg)?;
279 match (op.as_str(), v) {
280 ("not", Value::Bool(b)) => Ok(Value::Bool(!b)),
281 ("-", Value::Int(n)) => Ok(Value::Int(-n)),
282 ("-", Value::Float(n)) => Ok(Value::Float(-n)),
283 (o, v) => Err(format!("unsupported unary `{o}` on {v:?}")),
284 }
285 }
286 CExpr::BinOp { op, lhs, rhs } => {
287 if op == "and" || op == "or" {
290 let l = eval_refinement_inner(lhs, binding, arg)?;
291 let lb = match l {
292 Value::Bool(b) => b,
293 other => return Err(format!("`{op}` on non-bool: {other:?}")),
294 };
295 if op == "and" && !lb { return Ok(Value::Bool(false)); }
296 if op == "or" && lb { return Ok(Value::Bool(true)); }
297 let r = eval_refinement_inner(rhs, binding, arg)?;
298 return match r {
299 Value::Bool(b) => Ok(Value::Bool(b)),
300 other => Err(format!("`{op}` on non-bool: {other:?}")),
301 };
302 }
303 let l = eval_refinement_inner(lhs, binding, arg)?;
304 let r = eval_refinement_inner(rhs, binding, arg)?;
305 apply_refinement_binop(op, &l, &r)
306 }
307 other => Err(format!("unsupported predicate node: {other:?}")),
313 }
314}
315
316fn apply_refinement_binop(op: &str, l: &Value, r: &Value) -> Result<Value, String> {
317 use Value::*;
318 match (op, l, r) {
319 ("+", Int(a), Int(b)) => Ok(Int(a + b)),
320 ("-", Int(a), Int(b)) => Ok(Int(a - b)),
321 ("*", Int(a), Int(b)) => Ok(Int(a * b)),
322 ("/", Int(a), Int(b)) if *b != 0 => Ok(Int(a / b)),
323 ("%", Int(a), Int(b)) if *b != 0 => Ok(Int(a % b)),
324 ("+", Float(a), Float(b)) => Ok(Float(a + b)),
325 ("-", Float(a), Float(b)) => Ok(Float(a - b)),
326 ("*", Float(a), Float(b)) => Ok(Float(a * b)),
327 ("/", Float(a), Float(b)) => Ok(Float(a / b)),
328
329 ("==", a, b) => Ok(Bool(a == b)),
330 ("!=", a, b) => Ok(Bool(a != b)),
331
332 ("<", Int(a), Int(b)) => Ok(Bool(a < b)),
333 ("<=", Int(a), Int(b)) => Ok(Bool(a <= b)),
334 (">", Int(a), Int(b)) => Ok(Bool(a > b)),
335 (">=", Int(a), Int(b)) => Ok(Bool(a >= b)),
336
337 ("<", Float(a), Float(b)) => Ok(Bool(a < b)),
338 ("<=", Float(a), Float(b)) => Ok(Bool(a <= b)),
339 (">", Float(a), Float(b)) => Ok(Bool(a > b)),
340 (">=", Float(a), Float(b)) => Ok(Bool(a >= b)),
341
342 (op, a, b) => Err(format!(
343 "unsupported binop `{op}` on {a:?} and {b:?}")),
344 }
345}
346
347fn const_str(constants: &[Const], idx: u32) -> String {
348 match constants.get(idx as usize) {
349 Some(Const::NodeId(s)) | Some(Const::Str(s)) => s.clone(),
350 _ => String::new(),
351 }
352}
353
354fn compare_sort_keys(a: &Value, b: &Value) -> std::cmp::Ordering {
364 use std::cmp::Ordering;
365 match (a, b) {
366 (Value::Int(x), Value::Int(y)) => x.cmp(y),
367 (Value::Float(x), Value::Float(y)) => x.partial_cmp(y).unwrap_or(Ordering::Equal),
368 (Value::Str(x), Value::Str(y)) => x.cmp(y),
369 _ => Ordering::Equal,
370 }
371}
372
373fn par_max_concurrency() -> usize {
374 let from_env = std::env::var("LEX_PAR_MAX_CONCURRENCY")
375 .ok()
376 .and_then(|s| s.parse::<usize>().ok())
377 .filter(|n| *n > 0);
378 let default = std::thread::available_parallelism()
379 .map(|n| n.get())
380 .unwrap_or(4);
381 from_env.unwrap_or(default).min(64)
382}
383
384fn par_map_run<'a>(
391 program: &'a Program,
392 closure: Value,
393 items: Vec<Value>,
394 worker_handlers: Vec<Box<dyn EffectHandler + Send>>,
395) -> Result<Vec<Value>, VmError> {
396 if items.is_empty() {
397 return Ok(Vec::new());
398 }
399 let n_workers = worker_handlers.len().min(items.len()).max(1);
400 let mut buckets: Vec<Vec<(usize, Value)>> = (0..n_workers).map(|_| Vec::new()).collect();
404 for (i, v) in items.into_iter().enumerate() {
405 buckets[i % n_workers].push((i, v));
406 }
407 let n_total: usize = buckets.iter().map(|b| b.len()).sum();
408 let results: std::sync::Mutex<Vec<Option<Result<Value, String>>>> =
409 std::sync::Mutex::new((0..n_total).map(|_| None).collect());
410
411 let mut worker_handlers = worker_handlers;
415 worker_handlers.truncate(n_workers);
416 type Pair = (Vec<(usize, Value)>, Box<dyn EffectHandler + Send>);
417 let pairs: Vec<Pair> = buckets.into_iter().zip(worker_handlers).collect();
418
419 std::thread::scope(|s| {
420 let mut handles = Vec::with_capacity(pairs.len());
421 for (bucket, handler) in pairs {
422 let closure = closure.clone();
423 let results = &results;
424 handles.push(s.spawn(move || {
425 let handler_for_vm: Box<dyn EffectHandler + 'a> = handler;
430 let mut vm = Vm::with_handler(program, handler_for_vm);
431 for (idx, item) in bucket {
432 let r = vm
433 .invoke_closure_value(closure.clone(), vec![item])
434 .map_err(|e| format!("{e:?}"));
435 results.lock().unwrap()[idx] = Some(r);
436 }
437 }));
438 }
439 for h in handles {
440 h.join().map_err(|_| ()).ok();
441 }
442 });
443
444 let mut out = Vec::with_capacity(n_total);
445 let inner = results.into_inner().unwrap();
446 for r in inner {
447 match r {
448 Some(Ok(v)) => out.push(v),
449 Some(Err(e)) => return Err(VmError::Effect(format!("par_map worker: {e}"))),
450 None => return Err(VmError::Panic("par_map worker did not produce a result".into())),
451 }
452 }
453 Ok(out)
454}
455
456impl<'a> Vm<'a> {
457 pub fn new(program: &'a Program) -> Self {
458 Self::with_handler(program, Box::new(DenyAllEffects))
459 }
460
461 pub fn with_handler(program: &'a Program, handler: Box<dyn EffectHandler + 'a>) -> Self {
462 Self {
463 program,
464 handler,
465 tracer: Box::new(NullTracer),
466 frames: Vec::with_capacity(32),
469 stack: Vec::with_capacity(128),
470 step_limit: 10_000_000,
471 steps: 0,
472 pure_memo: std::collections::HashMap::new(),
473 pure_memo_hits: 0,
474 pure_memo_misses: 0,
475 field_ics: std::collections::HashMap::new(),
476 locals_storage: Vec::with_capacity(256),
479 }
480 }
481
482 pub fn set_tracer(&mut self, tracer: Box<dyn Tracer + 'a>) {
483 self.tracer = tracer;
484 }
485
486 pub fn set_step_limit(&mut self, limit: u64) {
492 self.step_limit = limit;
493 }
494
495 pub fn call(&mut self, name: &str, args: Vec<Value>) -> Result<Value, VmError> {
496 let fn_id = self.program.lookup(name).ok_or_else(|| VmError::Panic(format!("no function `{name}`")))?;
497 self.invoke(fn_id, args)
498 }
499
500 fn run_parser_op(&mut self, args: Vec<Value>) -> Result<Value, String> {
506 let parser = args.first().cloned()
507 .ok_or_else(|| "parser.run: missing parser arg".to_string())?;
508 let input = match args.get(1) {
509 Some(Value::Str(s)) => s.clone(),
510 _ => return Err("parser.run: input must be Str".into()),
511 };
512 match crate::parser_runtime::run_parser(&parser, &input, 0, self) {
513 Ok((value, _pos)) => Ok(Value::Variant {
514 name: "Ok".into(),
515 args: vec![value],
516 }),
517 Err((pos, msg)) => {
518 let mut e = indexmap::IndexMap::new();
519 e.insert("pos".into(), Value::Int(pos as i64));
520 e.insert("message".into(), Value::Str(msg.into()));
521 Ok(Value::Variant {
522 name: "Err".into(),
523 args: vec![Value::Record(e)],
524 })
525 }
526 }
527 }
528
529 fn run_conc_op(&mut self, op: &str, args: Vec<Value>) -> Result<Value, String> {
544 match op {
545 "spawn" => {
546 let mut it = args.into_iter();
547 let init = it.next().unwrap_or(Value::Unit);
548 let handler = it.next().unwrap_or(Value::Unit);
549 if !matches!(handler, Value::Closure { .. }) {
550 return Err(format!(
551 "conc.spawn: handler must be a Closure, got {handler:?}"));
552 }
553 Ok(Value::Actor(Arc::new(Mutex::new(ActorCell {
554 state: init,
555 handler,
556 }))))
557 }
558 "ask" | "tell" => {
559 let mut it = args.into_iter();
560 let actor_val = it.next().unwrap_or(Value::Unit);
561 let msg = it.next().unwrap_or(Value::Unit);
562 let cell = match actor_val {
563 Value::Actor(ref arc) => Arc::clone(arc),
564 other => return Err(format!(
565 "conc.{op}: first arg must be an Actor, got {other:?}")),
566 };
567 let mut guard = cell.lock().map_err(|e| format!("conc.{op}: actor mutex poisoned: {e}"))?;
569 let handler = guard.handler.clone();
570 let state = guard.state.clone();
571 let result = self.invoke_closure_value(handler, vec![state, msg])
573 .map_err(|e| format!("conc.{op}: handler error: {e:?}"))?;
574 match result {
576 Value::Tuple(mut parts) if parts.len() == 2 => {
577 let reply = parts.pop().unwrap();
578 let new_state = parts.pop().unwrap();
579 guard.state = new_state;
580 drop(guard);
581 if op == "ask" { Ok(reply) } else { Ok(Value::Unit) }
582 }
583 other => Err(format!(
584 "conc.{op}: handler must return a 2-tuple (new_state, reply), got {other:?}")),
585 }
586 }
587 other => Err(format!("unknown conc.{other}")),
588 }
589 }
590
591 pub fn invoke_closure_value(
596 &mut self,
597 closure: Value,
598 args: Vec<Value>,
599 ) -> Result<Value, VmError> {
600 let (fn_id, captures) = match closure {
601 Value::Closure { fn_id, captures, .. } => (fn_id, captures),
602 other => return Err(VmError::TypeMismatch(
603 format!("invoke_closure_value: not a closure: {other:?}"))),
604 };
605 let mut combined = captures;
606 combined.extend(args);
607 self.invoke(fn_id, combined)
608 }
609
610 pub fn invoke(&mut self, fn_id: u32, args: Vec<Value>) -> Result<Value, VmError> {
611 let f = &self.program.functions[fn_id as usize];
612 if args.len() != f.arity as usize {
613 return Err(VmError::Panic(format!("arity mismatch calling {}", f.name)));
614 }
615 let f_name = f.name.clone();
621 let refinements = f.refinements.clone();
622 for (i, refinement) in refinements.iter().enumerate() {
623 if let Some(r) = refinement {
624 let arg = args.get(i).cloned().unwrap_or(Value::Unit);
625 match eval_refinement(&r.predicate, &r.binding, &arg) {
626 Ok(true) => {}
627 Ok(false) => return Err(VmError::RefinementFailed {
628 fn_name: f_name,
629 param_index: i,
630 binding: r.binding.clone(),
631 reason: format!("predicate failed for {} = {arg:?}", r.binding),
632 }),
633 Err(reason) => return Err(VmError::RefinementFailed {
634 fn_name: f_name,
635 param_index: i,
636 binding: r.binding.clone(),
637 reason,
638 }),
639 }
640 }
641 }
642 let f = &self.program.functions[fn_id as usize];
643 let locals_start = self.locals_storage.len();
645 let locals_len = f.locals_count.max(f.arity) as usize;
646 self.locals_storage.resize(locals_start + locals_len, Value::Unit);
647 for (i, v) in args.into_iter().enumerate() {
648 self.locals_storage[locals_start + i] = v;
649 }
650 let base_depth = self.frames.len();
654 self.push_frame(Frame {
655 fn_id, pc: 0, locals_start, locals_len,
656 stack_base: self.stack.len(),
657 trace_kind: FrameKind::Entry,
658 memo_key: None,
659 })?;
660 self.run_to(base_depth)
661 }
662
663 fn push_frame(&mut self, frame: Frame) -> Result<(), VmError> {
668 if self.frames.len() as u32 >= MAX_CALL_DEPTH {
669 return Err(VmError::CallStackOverflow(MAX_CALL_DEPTH));
670 }
671 self.frames.push(frame);
672 Ok(())
673 }
674
675 fn run_to(&mut self, base_depth: usize) -> Result<Value, VmError> {
680 loop {
681 if self.steps > self.step_limit {
682 let frame_idx = self.frames.len() - 1;
683 let fn_id = self.frames[frame_idx].fn_id;
684 let fn_name = &self.program.functions[fn_id as usize].name;
685 return Err(VmError::Panic(format!(
686 "step limit exceeded in `{fn_name}` ({} > {})",
687 self.steps, self.step_limit,
688 )));
689 }
690 self.steps += 1;
691 let frame_idx = self.frames.len() - 1;
692 let pc = self.frames[frame_idx].pc;
693 let fn_id = self.frames[frame_idx].fn_id;
694 let code = &self.program.functions[fn_id as usize].code;
695 if pc >= code.len() {
696 let fn_name = &self.program.functions[fn_id as usize].name;
697 return Err(VmError::Panic(format!("ran past end of code in `{fn_name}`")));
698 }
699 let op = code[pc].clone();
700 self.frames[frame_idx].pc = pc + 1;
701
702 match op {
703 Op::PushConst(i) => {
704 let c = &self.program.constants[i as usize];
705 self.stack.push(const_to_value(c));
706 }
707 Op::Pop => { self.pop()?; }
708 Op::Dup => {
709 let v = self.peek()?.clone();
710 self.stack.push(v);
711 }
712 Op::LoadLocal(i) => {
713 let base = self.frames[frame_idx].locals_start;
714 let v = self.locals_storage[base + i as usize].clone();
715 self.stack.push(v);
716 }
717 Op::StoreLocal(i) => {
718 let v = self.pop()?;
719 let base = self.frames[frame_idx].locals_start;
720 self.locals_storage[base + i as usize] = v;
721 }
722 Op::MakeRecord { field_name_indices } => {
723 let n = field_name_indices.len();
724 let mut values: Vec<Value> = (0..n).map(|_| Value::Unit).collect();
725 for i in (0..n).rev() {
726 values[i] = self.pop()?;
727 }
728 let mut rec = IndexMap::new();
729 for (i, val) in values.into_iter().enumerate() {
730 let name = match &self.program.constants[field_name_indices[i] as usize] {
731 Const::FieldName(s) => s.clone(),
732 _ => return Err(VmError::TypeMismatch("expected FieldName const".into())),
733 };
734 rec.insert(name, val);
735 }
736 self.stack.push(Value::Record(rec));
737 }
738 Op::MakeTuple(n) => {
739 let mut items: Vec<Value> = (0..n).map(|_| Value::Unit).collect();
740 for i in (0..n as usize).rev() { items[i] = self.pop()?; }
741 self.stack.push(Value::Tuple(items));
742 }
743 Op::MakeList(n) => {
744 let mut items: Vec<Value> = (0..n).map(|_| Value::Unit).collect();
745 for i in (0..n as usize).rev() { items[i] = self.pop()?; }
746 self.stack.push(Value::List(items.into()));
747 }
748 Op::MakeVariant { name_idx, arity } => {
749 let mut args: Vec<Value> = (0..arity).map(|_| Value::Unit).collect();
750 for i in (0..arity as usize).rev() { args[i] = self.pop()?; }
751 let name = match &self.program.constants[name_idx as usize] {
752 Const::VariantName(s) => s.clone(),
753 _ => return Err(VmError::TypeMismatch("expected VariantName const".into())),
754 };
755 self.stack.push(Value::Variant { name, args });
756 }
757 Op::GetField(i) => {
758 let name_idx = i;
759 let v = self.pop()?;
760 match v {
761 Value::Record(r) => {
762 let site = (fn_id as u64) << 32 | pc as u64;
766 let value = 'ic: {
767 if let Some(&off) = self.field_ics.get(&site) {
768 if let Some((k, val)) = r.get_index(off) {
769 if let Const::FieldName(s) =
770 &self.program.constants[name_idx as usize]
771 {
772 if s == k { break 'ic val.clone(); } }
774 }
775 }
776 let name = match &self.program.constants[name_idx as usize] {
778 Const::FieldName(s) => s.as_str(),
779 _ => return Err(VmError::TypeMismatch(
780 "expected FieldName const".into())),
781 };
782 let (off, _, val) = r.get_full(name)
783 .ok_or_else(|| VmError::TypeMismatch(
784 format!("missing field `{name}`")))?;
785 self.field_ics.insert(site, off);
786 val.clone()
787 };
788 self.stack.push(value);
789 }
790 other => return Err(VmError::TypeMismatch(
791 format!("GetField on non-record: {other:?}"))),
792 }
793 }
794 Op::GetElem(i) => {
795 let v = self.pop()?;
796 match v {
797 Value::Tuple(items) => {
798 let v = items.into_iter().nth(i as usize)
799 .ok_or_else(|| VmError::TypeMismatch(format!("tuple index {i} out of range")))?;
800 self.stack.push(v);
801 }
802 other => return Err(VmError::TypeMismatch(format!("GetElem on non-tuple: {other:?}"))),
803 }
804 }
805 Op::TestVariant(i) => {
806 let name = match &self.program.constants[i as usize] {
807 Const::VariantName(s) => s.clone(),
808 _ => return Err(VmError::TypeMismatch("expected VariantName const".into())),
809 };
810 let v = self.pop()?;
811 match &v {
812 Value::Variant { name: vname, .. } => {
813 self.stack.push(Value::Bool(vname == &name));
814 }
815 other => return Err(VmError::TypeMismatch(format!("TestVariant on non-variant: {other:?}"))),
818 }
819 }
820 Op::GetVariant(_i) => {
821 let v = self.pop()?;
822 match v {
823 Value::Variant { args, .. } => {
824 self.stack.push(Value::Tuple(args));
825 }
826 other => return Err(VmError::TypeMismatch(format!("GetVariant on non-variant: {other:?}"))),
827 }
828 }
829 Op::GetVariantArg(i) => {
830 let v = self.pop()?;
831 match v {
832 Value::Variant { mut args, .. } => {
833 if (i as usize) >= args.len() {
834 return Err(VmError::TypeMismatch("variant arg index oob".into()));
835 }
836 self.stack.push(args.swap_remove(i as usize));
837 }
838 other => return Err(VmError::TypeMismatch(format!("GetVariantArg on non-variant: {other:?}"))),
839 }
840 }
841 Op::GetListLen => {
842 let v = self.pop()?;
843 match v {
844 Value::List(items) => self.stack.push(Value::Int(items.len() as i64)),
845 other => return Err(VmError::TypeMismatch(format!("GetListLen on non-list: {other:?}"))),
846 }
847 }
848 Op::GetListElem(i) => {
849 let v = self.pop()?;
850 match v {
851 Value::List(items) => {
852 let v = items.into_iter().nth(i as usize)
853 .ok_or_else(|| VmError::TypeMismatch("list index oob".into()))?;
854 self.stack.push(v);
855 }
856 other => return Err(VmError::TypeMismatch(format!("GetListElem on non-list: {other:?}"))),
857 }
858 }
859 Op::GetListElemDyn => {
860 let idx = match self.pop()? {
862 Value::Int(n) => n as usize,
863 other => return Err(VmError::TypeMismatch(format!("GetListElemDyn idx: {other:?}"))),
864 };
865 let v = self.pop()?;
866 match v {
867 Value::List(items) => {
868 let v = items.into_iter().nth(idx)
869 .ok_or_else(|| VmError::TypeMismatch("list index oob".into()))?;
870 self.stack.push(v);
871 }
872 other => return Err(VmError::TypeMismatch(format!("GetListElemDyn on non-list: {other:?}"))),
873 }
874 }
875 Op::ListAppend => {
876 let value = self.pop()?;
877 let list = self.pop()?;
878 match list {
879 Value::List(mut items) => {
880 items.push_back(value);
881 self.stack.push(Value::List(items));
882 }
883 other => return Err(VmError::TypeMismatch(format!("ListAppend on non-list: {other:?}"))),
884 }
885 }
886 Op::Jump(off) => {
887 let new_pc = (self.frames[frame_idx].pc as i32 + off) as usize;
888 self.frames[frame_idx].pc = new_pc;
889 }
890 Op::JumpIf(off) => {
891 let v = self.pop()?;
892 if v.as_bool() {
893 let new_pc = (self.frames[frame_idx].pc as i32 + off) as usize;
894 self.frames[frame_idx].pc = new_pc;
895 }
896 }
897 Op::JumpIfNot(off) => {
898 let v = self.pop()?;
899 if !v.as_bool() {
900 let new_pc = (self.frames[frame_idx].pc as i32 + off) as usize;
901 self.frames[frame_idx].pc = new_pc;
902 }
903 }
904 Op::MakeClosure { fn_id, capture_count } => {
905 let n = capture_count as usize;
906 let mut captures: Vec<Value> = (0..n).map(|_| Value::Unit).collect();
907 for i in (0..n).rev() { captures[i] = self.pop()?; }
908 let body_hash = self.program.functions[fn_id as usize].body_hash;
911 self.stack.push(Value::Closure { fn_id, body_hash, captures });
912 }
913 Op::CallClosure { arity, node_id_idx } => {
914 let mut args: Vec<Value> = (0..arity).map(|_| Value::Unit).collect();
915 for i in (0..arity as usize).rev() { args[i] = self.pop()?; }
916 let closure = self.pop()?;
917 let (fn_id, captures) = match closure {
918 Value::Closure { fn_id, captures, .. } => (fn_id, captures),
919 other => return Err(VmError::TypeMismatch(format!("CallClosure on non-closure: {other:?}"))),
920 };
921 let node_id = const_str(&self.program.constants, node_id_idx);
922 let callee_name = self.program.functions[fn_id as usize].name.clone();
923 let budget_cost = call_budget_cost(&self.program.functions[fn_id as usize]);
924 if budget_cost > 0 {
925 self.handler.note_call_budget(budget_cost)
926 .map_err(VmError::Effect)?;
927 }
928 let mut combined = captures;
929 combined.extend(args);
930 self.tracer.enter_call(&node_id, &callee_name, &combined);
931 let f = &self.program.functions[fn_id as usize];
932 let locals_start = self.locals_storage.len();
933 let locals_len = f.locals_count.max(f.arity) as usize;
934 self.locals_storage.resize(locals_start + locals_len, Value::Unit);
935 for (i, v) in combined.into_iter().enumerate() {
936 self.locals_storage[locals_start + i] = v;
937 }
938 self.push_frame(Frame {
939 fn_id, pc: 0, locals_start, locals_len,
940 stack_base: self.stack.len(),
941 trace_kind: FrameKind::Call(node_id),
942 memo_key: None,
947 })?;
948 }
949 Op::SortByKey { node_id_idx: _ } => {
950 let f = self.pop()?;
958 let xs = self.pop()?;
959 let items = match xs {
960 Value::List(v) => v,
961 other => return Err(VmError::TypeMismatch(
962 format!("SortByKey requires a List, got: {other:?}"))),
963 };
964 if !matches!(f, Value::Closure { .. }) {
965 return Err(VmError::TypeMismatch(
966 format!("SortByKey requires a closure, got: {f:?}")));
967 }
968 let mut keyed: Vec<(Value, Value)> = Vec::with_capacity(items.len());
969 for item in items {
970 let key = self.invoke_closure_value(f.clone(), vec![item.clone()])?;
971 keyed.push((key, item));
972 }
973 keyed.sort_by(|(ka, _), (kb, _)| compare_sort_keys(ka, kb));
974 let sorted: VecDeque<Value> = keyed.into_iter().map(|(_, v)| v).collect();
975 self.stack.push(Value::List(sorted));
976 }
977 Op::ParallelMap { node_id_idx: _ } => {
978 let f = self.pop()?;
989 let xs = self.pop()?;
990 let items = match xs {
991 Value::List(v) => v,
992 other => return Err(VmError::TypeMismatch(
993 format!("ParallelMap requires a List, got: {other:?}"))),
994 };
995 if !matches!(f, Value::Closure { .. }) {
996 return Err(VmError::TypeMismatch(
997 format!("ParallelMap requires a closure, got: {f:?}")));
998 }
999 let n_workers = par_max_concurrency().max(1).min(items.len().max(1));
1006 let mut worker_handlers: Vec<Box<dyn EffectHandler + Send>> =
1007 Vec::with_capacity(n_workers);
1008 for _ in 0..n_workers {
1009 worker_handlers.push(
1010 self.handler
1011 .spawn_for_worker()
1012 .unwrap_or_else(|| Box::new(DenyAllEffects)),
1013 );
1014 }
1015 let results = par_map_run(self.program, f, items.into_iter().collect(), worker_handlers)?;
1016 self.stack.push(Value::List(results.into()));
1017 }
1018 Op::Call { fn_id, arity, node_id_idx } => {
1019 let mut args: Vec<Value> = (0..arity).map(|_| Value::Unit).collect();
1020 for i in (0..arity as usize).rev() { args[i] = self.pop()?; }
1021 let node_id = const_str(&self.program.constants, node_id_idx);
1022 let callee_name = self.program.functions[fn_id as usize].name.clone();
1023 let budget_cost = call_budget_cost(&self.program.functions[fn_id as usize]);
1024 if budget_cost > 0 {
1025 self.handler.note_call_budget(budget_cost)
1026 .map_err(VmError::Effect)?;
1027 }
1028 let refinements = self.program.functions[fn_id as usize]
1037 .refinements.clone();
1038 for (i, refinement) in refinements.iter().enumerate() {
1039 if let Some(r) = refinement {
1040 let arg = args.get(i).cloned().unwrap_or(Value::Unit);
1041 match eval_refinement(&r.predicate, &r.binding, &arg) {
1042 Ok(true) => { }
1043 Ok(false) => {
1044 return Err(VmError::RefinementFailed {
1045 fn_name: callee_name.clone(),
1046 param_index: i,
1047 binding: r.binding.clone(),
1048 reason: format!(
1049 "predicate failed for {} = {arg:?}",
1050 r.binding),
1051 });
1052 }
1053 Err(reason) => {
1054 return Err(VmError::RefinementFailed {
1055 fn_name: callee_name.clone(),
1056 param_index: i,
1057 binding: r.binding.clone(),
1058 reason,
1059 });
1060 }
1061 }
1062 }
1063 }
1064 let f = &self.program.functions[fn_id as usize];
1070 let memo_key: Option<(u32, [u8; 16])> = if f.effects.is_empty() {
1071 Some((fn_id, hash_call_args(&args)))
1072 } else {
1073 None
1074 };
1075 if let Some(key) = memo_key {
1076 if let Some(cached) = self.pure_memo.get(&key).cloned() {
1077 self.pure_memo_hits += 1;
1078 self.tracer.enter_call(&node_id, &callee_name, &args);
1079 self.tracer.exit_ok(&cached);
1080 self.stack.push(cached);
1081 continue;
1082 }
1083 self.pure_memo_misses += 1;
1084 }
1085 self.tracer.enter_call(&node_id, &callee_name, &args);
1086 let locals_start = self.locals_storage.len();
1087 let locals_len = f.locals_count.max(f.arity) as usize;
1088 self.locals_storage.resize(locals_start + locals_len, Value::Unit);
1089 for (i, v) in args.into_iter().enumerate() {
1090 self.locals_storage[locals_start + i] = v;
1091 }
1092 self.push_frame(Frame {
1093 fn_id, pc: 0, locals_start, locals_len,
1094 stack_base: self.stack.len(),
1095 trace_kind: FrameKind::Call(node_id),
1096 memo_key,
1097 })?;
1098 }
1099 Op::TailCall { fn_id, arity, node_id_idx } => {
1100 let mut args: Vec<Value> = (0..arity).map(|_| Value::Unit).collect();
1101 for i in (0..arity as usize).rev() { args[i] = self.pop()?; }
1102 let node_id = const_str(&self.program.constants, node_id_idx);
1103 let callee_name = self.program.functions[fn_id as usize].name.clone();
1104 let budget_cost = call_budget_cost(&self.program.functions[fn_id as usize]);
1105 if budget_cost > 0 {
1106 self.handler.note_call_budget(budget_cost)
1107 .map_err(VmError::Effect)?;
1108 }
1109 let refinements = self.program.functions[fn_id as usize]
1112 .refinements.clone();
1113 for (i, refinement) in refinements.iter().enumerate() {
1114 if let Some(r) = refinement {
1115 let arg = args.get(i).cloned().unwrap_or(Value::Unit);
1116 match eval_refinement(&r.predicate, &r.binding, &arg) {
1117 Ok(true) => {}
1118 Ok(false) => return Err(VmError::RefinementFailed {
1119 fn_name: callee_name.clone(),
1120 param_index: i,
1121 binding: r.binding.clone(),
1122 reason: format!(
1123 "predicate failed for {} = {arg:?}",
1124 r.binding),
1125 }),
1126 Err(reason) => return Err(VmError::RefinementFailed {
1127 fn_name: callee_name.clone(),
1128 param_index: i,
1129 binding: r.binding.clone(),
1130 reason,
1131 }),
1132 }
1133 }
1134 }
1135 self.tracer.exit_call_tail();
1139 self.tracer.enter_call(&node_id, &callee_name, &args);
1140 let f = &self.program.functions[fn_id as usize];
1141 let old_locals_start = self.frames.last().unwrap().locals_start;
1146 self.locals_storage.truncate(old_locals_start);
1147 let new_locals_len = f.locals_count.max(f.arity) as usize;
1148 self.locals_storage.resize(old_locals_start + new_locals_len, Value::Unit);
1149 for (i, v) in args.into_iter().enumerate() {
1150 self.locals_storage[old_locals_start + i] = v;
1151 }
1152 let frame = self.frames.last_mut().unwrap();
1153 frame.fn_id = fn_id;
1154 frame.pc = 0;
1155 frame.locals_len = new_locals_len;
1156 frame.trace_kind = FrameKind::Call(node_id);
1157 }
1158 Op::EffectCall { kind_idx, op_idx, arity, node_id_idx } => {
1159 let mut args: Vec<Value> = (0..arity).map(|_| Value::Unit).collect();
1160 for i in (0..arity as usize).rev() { args[i] = self.pop()?; }
1161 let kind = match &self.program.constants[kind_idx as usize] {
1162 Const::Str(s) => s.clone(),
1163 _ => return Err(VmError::TypeMismatch("expected Str const for effect kind".into())),
1164 };
1165 let op_name = match &self.program.constants[op_idx as usize] {
1166 Const::Str(s) => s.clone(),
1167 _ => return Err(VmError::TypeMismatch("expected Str const for effect op".into())),
1168 };
1169 let node_id = const_str(&self.program.constants, node_id_idx);
1170 self.tracer.enter_effect(&node_id, &kind, &op_name, &args);
1171 let result = match self.tracer.override_effect(&node_id) {
1172 Some(v) => Ok(v),
1173 None if (kind.as_str(), op_name.as_str()) == ("parser", "run")
1179 => self.run_parser_op(args),
1180 None if kind.as_str() == "conc"
1185 => self.run_conc_op(op_name.as_str(), args),
1186 None => self.handler.dispatch(&kind, &op_name, args),
1187 };
1188 match result {
1189 Ok(v) => {
1190 self.tracer.exit_ok(&v);
1191 self.stack.push(v);
1192 }
1193 Err(e) => {
1194 self.tracer.exit_err(&e);
1195 return Err(VmError::Effect(e));
1196 }
1197 }
1198 }
1199 Op::Return => {
1200 let v = self.pop()?;
1201 let frame = self.frames.pop().unwrap();
1202 self.stack.truncate(frame.stack_base);
1204 self.locals_storage.truncate(frame.locals_start);
1207 if matches!(frame.trace_kind, FrameKind::Call(_)) {
1208 self.tracer.exit_ok(&v);
1209 }
1210 if let Some(key) = frame.memo_key {
1215 self.pure_memo.insert(key, v.clone());
1216 }
1217 if self.frames.len() <= base_depth {
1222 return Ok(v);
1223 }
1224 self.stack.push(v);
1225 }
1226 Op::Panic(i) => {
1227 let msg = match &self.program.constants[i as usize] {
1228 Const::Str(s) => s.clone(),
1229 _ => "panic".into(),
1230 };
1231 return Err(VmError::Panic(msg));
1232 }
1233 Op::IntAdd => self.bin_int(|a, b| Value::Int(a + b))?,
1235 Op::IntSub => self.bin_int(|a, b| Value::Int(a - b))?,
1236 Op::IntMul => self.bin_int(|a, b| Value::Int(a * b))?,
1237 Op::IntDiv => self.bin_int(|a, b| Value::Int(a / b))?,
1238 Op::IntMod => self.bin_int(|a, b| Value::Int(a % b))?,
1239 Op::IntNeg => {
1240 let a = self.pop()?.as_int();
1241 self.stack.push(Value::Int(-a));
1242 }
1243 Op::IntEq => self.bin_int(|a, b| Value::Bool(a == b))?,
1244 Op::IntLt => self.bin_int(|a, b| Value::Bool(a < b))?,
1245 Op::IntLe => self.bin_int(|a, b| Value::Bool(a <= b))?,
1246 Op::FloatAdd => self.bin_float(|a, b| Value::Float(a + b))?,
1247 Op::FloatSub => self.bin_float(|a, b| Value::Float(a - b))?,
1248 Op::FloatMul => self.bin_float(|a, b| Value::Float(a * b))?,
1249 Op::FloatDiv => self.bin_float(|a, b| Value::Float(a / b))?,
1250 Op::FloatNeg => {
1251 let a = self.pop()?.as_float();
1252 self.stack.push(Value::Float(-a));
1253 }
1254 Op::FloatEq => self.bin_float(|a, b| Value::Bool(a == b))?,
1255 Op::FloatLt => self.bin_float(|a, b| Value::Bool(a < b))?,
1256 Op::FloatLe => self.bin_float(|a, b| Value::Bool(a <= b))?,
1257 Op::NumAdd => {
1258 let b = self.pop()?;
1262 let a = self.pop()?;
1263 match (a, b) {
1264 (Value::Int(x), Value::Int(y)) => self.stack.push(Value::Int(x + y)),
1265 (Value::Float(x), Value::Float(y)) => self.stack.push(Value::Float(x + y)),
1266 (Value::Str(x), Value::Str(y)) => {
1267 let mut s = String::with_capacity(x.len() + y.len());
1269 s.push_str(&x);
1270 s.push_str(&y);
1271 self.stack.push(Value::Str(s.into()));
1272 }
1273 (a, b) => return Err(VmError::TypeMismatch(format!("Num op: {a:?} {b:?}"))),
1274 }
1275 }
1276 Op::NumSub => self.bin_num(|a, b| Value::Int(a - b), |a, b| Value::Float(a - b))?,
1277 Op::NumMul => self.bin_num(|a, b| Value::Int(a * b), |a, b| Value::Float(a * b))?,
1278 Op::NumDiv => self.bin_num(|a, b| Value::Int(a / b), |a, b| Value::Float(a / b))?,
1279 Op::NumMod => self.bin_int(|a, b| Value::Int(a % b))?,
1280 Op::NumNeg => {
1281 let v = self.pop()?;
1282 match v {
1283 Value::Int(n) => self.stack.push(Value::Int(-n)),
1284 Value::Float(f) => self.stack.push(Value::Float(-f)),
1285 other => return Err(VmError::TypeMismatch(format!("NumNeg on {other:?}"))),
1286 }
1287 }
1288 Op::NumEq => self.bin_eq()?,
1289 Op::NumLt => self.bin_ord(|a, b| Value::Bool(a < b), |a, b| Value::Bool(a < b), |a, b| Value::Bool(a < b))?,
1290 Op::NumLe => self.bin_ord(|a, b| Value::Bool(a <= b), |a, b| Value::Bool(a <= b), |a, b| Value::Bool(a <= b))?,
1291 Op::BoolAnd => {
1292 let b = self.pop()?.as_bool();
1293 let a = self.pop()?.as_bool();
1294 self.stack.push(Value::Bool(a && b));
1295 }
1296 Op::BoolOr => {
1297 let b = self.pop()?.as_bool();
1298 let a = self.pop()?.as_bool();
1299 self.stack.push(Value::Bool(a || b));
1300 }
1301 Op::BoolNot => {
1302 let a = self.pop()?.as_bool();
1303 self.stack.push(Value::Bool(!a));
1304 }
1305 Op::StrConcat => {
1306 let b = self.pop()?;
1307 let a = self.pop()?;
1308 let s = format!("{}{}", a.as_str(), b.as_str());
1309 self.stack.push(Value::Str(s.into()));
1310 }
1311 Op::StrLen => {
1312 let v = self.pop()?;
1313 self.stack.push(Value::Int(v.as_str().len() as i64));
1314 }
1315 Op::StrEq => {
1316 let b = self.pop()?;
1317 let a = self.pop()?;
1318 self.stack.push(Value::Bool(a.as_str() == b.as_str()));
1319 }
1320 Op::BytesLen => {
1321 let v = self.pop()?;
1322 match v {
1323 Value::Bytes(b) => self.stack.push(Value::Int(b.len() as i64)),
1324 other => return Err(VmError::TypeMismatch(format!("BytesLen on {other:?}"))),
1325 }
1326 }
1327 Op::BytesEq => {
1328 let b = self.pop()?;
1329 let a = self.pop()?;
1330 let eq = match (a, b) {
1331 (Value::Bytes(x), Value::Bytes(y)) => x == y,
1332 _ => return Err(VmError::TypeMismatch("BytesEq operands".into())),
1333 };
1334 self.stack.push(Value::Bool(eq));
1335 }
1336 }
1337 }
1338 }
1339
1340 fn pop(&mut self) -> Result<Value, VmError> {
1341 self.stack.pop().ok_or(VmError::StackUnderflow)
1342 }
1343 fn peek(&self) -> Result<&Value, VmError> {
1344 self.stack.last().ok_or(VmError::StackUnderflow)
1345 }
1346
1347 fn bin_int(&mut self, f: impl Fn(i64, i64) -> Value) -> Result<(), VmError> {
1348 let b = self.pop()?.as_int();
1349 let a = self.pop()?.as_int();
1350 self.stack.push(f(a, b));
1351 Ok(())
1352 }
1353 fn bin_float(&mut self, f: impl Fn(f64, f64) -> Value) -> Result<(), VmError> {
1354 let b = self.pop()?.as_float();
1355 let a = self.pop()?.as_float();
1356 self.stack.push(f(a, b));
1357 Ok(())
1358 }
1359 fn bin_num(
1360 &mut self,
1361 i: impl Fn(i64, i64) -> Value,
1362 f: impl Fn(f64, f64) -> Value,
1363 ) -> Result<(), VmError> {
1364 let b = self.pop()?;
1365 let a = self.pop()?;
1366 match (a, b) {
1367 (Value::Int(x), Value::Int(y)) => { self.stack.push(i(x, y)); Ok(()) }
1368 (Value::Float(x), Value::Float(y)) => { self.stack.push(f(x, y)); Ok(()) }
1369 (a, b) => Err(VmError::TypeMismatch(format!("Num op: {a:?} {b:?}"))),
1370 }
1371 }
1372
1373 fn bin_ord(
1377 &mut self,
1378 i: impl Fn(i64, i64) -> Value,
1379 f: impl Fn(f64, f64) -> Value,
1380 s: impl Fn(&str, &str) -> Value,
1381 ) -> Result<(), VmError> {
1382 let b = self.pop()?;
1383 let a = self.pop()?;
1384 match (a, b) {
1385 (Value::Int(x), Value::Int(y)) => { self.stack.push(i(x, y)); Ok(()) }
1386 (Value::Float(x), Value::Float(y)) => { self.stack.push(f(x, y)); Ok(()) }
1387 (Value::Str(x), Value::Str(y)) => { self.stack.push(s(&x, &y)); Ok(()) }
1388 (a, b) => Err(VmError::TypeMismatch(format!("Num op: {a:?} {b:?}"))),
1389 }
1390 }
1391 fn bin_eq(&mut self) -> Result<(), VmError> {
1392 let b = self.pop()?;
1393 let a = self.pop()?;
1394 self.stack.push(Value::Bool(a == b));
1395 Ok(())
1396 }
1397}
1398
1399fn const_to_value(c: &Const) -> Value {
1400 match c {
1401 Const::Int(n) => Value::Int(*n),
1402 Const::Float(f) => Value::Float(*f),
1403 Const::Bool(b) => Value::Bool(*b),
1404 Const::Str(s) => Value::Str(s.as_str().into()),
1405 Const::Bytes(b) => Value::Bytes(b.clone()),
1406 Const::Unit => Value::Unit,
1407 Const::FieldName(s) | Const::VariantName(s) | Const::NodeId(s) => Value::Str(s.as_str().into()),
1408 }
1409}