1use super::lowered::{
2 self, ExprId, LoweredDirectCallTarget, LoweredExpr, LoweredForwardArg, LoweredFunctionBody,
3 LoweredLeafOp, LoweredMatchArm, LoweredStmt, LoweredStrPart, LoweredTailCallTarget,
4};
5use super::*;
6
7type SharedExprs = Rc<[ExprId]>;
8type SharedStrParts = Rc<[LoweredStrPart]>;
9type SharedMapEntries = Rc<[(ExprId, ExprId)]>;
10type SharedRecordFields = Rc<[(String, ExprId)]>;
11type SharedMatchArms = Rc<[LoweredMatchArm]>;
12
13#[derive(Debug)]
14enum EvalState {
15 Expr {
16 lowered: Rc<LoweredFunctionBody>,
17 expr: ExprId,
18 },
19 Body {
20 lowered: Rc<LoweredFunctionBody>,
21 idx: usize,
22 local_slots: Option<Rc<HashMap<String, u16>>>,
23 last: NanValue,
24 },
25 Apply(Result<NanValue, RuntimeError>),
26}
27
28impl EvalState {
29 fn map_err_line(self, line: usize) -> Self {
31 match self {
32 EvalState::Apply(Err(err)) => EvalState::Apply(Err(err.at_line(line))),
33 other => other,
34 }
35 }
36}
37
38#[derive(Debug, Clone)]
39enum EvalCont {
40 Attr(String, usize),
41 Call {
42 lowered: Rc<LoweredFunctionBody>,
43 args: SharedExprs,
44 idx: usize,
45 fn_val: Option<NanValue>,
46 arg_vals: Vec<NanValue>,
47 call_line: usize,
48 },
49 BinOpLeft {
50 lowered: Rc<LoweredFunctionBody>,
51 op: BinOp,
52 right: ExprId,
53 line: usize,
54 },
55 BinOpRight {
56 op: BinOp,
57 left: NanValue,
58 line: usize,
59 },
60 Match {
61 lowered: Rc<LoweredFunctionBody>,
62 arms: SharedMatchArms,
63 line: usize,
64 },
65 DirectCall {
66 lowered: Rc<LoweredFunctionBody>,
67 target: LoweredDirectCallTarget,
68 idx: usize,
69 args: SharedExprs,
70 arg_vals: Vec<NanValue>,
71 call_line: usize,
72 },
73 Leaf {
74 lowered: Rc<LoweredFunctionBody>,
75 leaf: LoweredLeafOp,
76 idx: usize,
77 values: Vec<NanValue>,
78 },
79 Constructor(String),
80 ErrorProp(usize),
81 InterpolatedStr {
82 lowered: Rc<LoweredFunctionBody>,
83 parts: SharedStrParts,
84 idx: usize,
85 result: String,
86 },
87 List {
88 lowered: Rc<LoweredFunctionBody>,
89 items: SharedExprs,
90 idx: usize,
91 values: Vec<NanValue>,
92 },
93 Tuple {
94 lowered: Rc<LoweredFunctionBody>,
95 items: SharedExprs,
96 idx: usize,
97 values: Vec<NanValue>,
98 },
99 IndependentProduct {
100 lowered: Rc<LoweredFunctionBody>,
101 items: SharedExprs,
102 idx: usize,
103 values: Vec<NanValue>,
104 unwrap: bool,
105 },
106 MapKey {
107 lowered: Rc<LoweredFunctionBody>,
108 entries: SharedMapEntries,
109 idx: usize,
110 map: crate::nan_value::PersistentMap,
111 },
112 MapValue {
113 lowered: Rc<LoweredFunctionBody>,
114 entries: SharedMapEntries,
115 idx: usize,
116 map: crate::nan_value::PersistentMap,
117 key: NanValue,
118 },
119 RecordCreate(RecordCreateProgress),
120 RecordUpdateBase {
121 lowered: Rc<LoweredFunctionBody>,
122 type_name: String,
123 updates: SharedRecordFields,
124 },
125 RecordUpdateField(RecordUpdateProgress),
126 TailCallArgs {
127 lowered: Rc<LoweredFunctionBody>,
128 target: LoweredTailCallTarget,
129 args: SharedExprs,
130 idx: usize,
131 values: Vec<NanValue>,
132 },
133 BodyBinding {
134 name: String,
135 next_idx: usize,
136 lowered: Rc<LoweredFunctionBody>,
137 local_slots: Option<Rc<HashMap<String, u16>>>,
138 },
139 BodyExpr {
140 next_idx: usize,
141 lowered: Rc<LoweredFunctionBody>,
142 local_slots: Option<Rc<HashMap<String, u16>>>,
143 },
144 MatchScope,
145 FunctionReturn(FunctionFrame),
146}
147
148#[derive(Debug, Clone)]
149struct ActiveFunction {
150 function: Rc<crate::value::FunctionValue>,
151}
152
153#[derive(Debug, Clone)]
154struct FunctionFrame {
155 active: ActiveFunction,
156 prev_local_slots: Option<Rc<HashMap<String, u16>>>,
157 saved_base: usize,
160 prev_global: Option<EnvFrame>,
161 memo_key: Option<(u64, Vec<NanValue>)>,
162}
163
164#[derive(Debug, Clone)]
165struct RecordCreateProgress {
166 lowered: Rc<LoweredFunctionBody>,
167 type_name: String,
168 fields: SharedRecordFields,
169 idx: usize,
170 seen: HashSet<String>,
171 values: Vec<(String, NanValue)>,
172}
173
174#[derive(Debug, Clone)]
175struct RecordUpdateProgress {
176 lowered: Rc<LoweredFunctionBody>,
177 type_name: String,
178 base_type_id: u32,
179 base_fields: Vec<NanValue>,
180 base_field_names: Vec<String>,
181 updates: SharedRecordFields,
182 idx: usize,
183 update_vals: Vec<(String, NanValue)>,
184}
185
186enum CallDispatch {
187 Immediate(Result<NanValue, RuntimeError>),
188 EnterFunction {
189 frame: Box<FunctionFrame>,
190 state: EvalState,
191 },
192}
193
194impl Interpreter {
195 fn empty_slots_nv(local_count: u16) -> Vec<NanValue> {
196 vec![NanValue::UNIT; local_count as usize]
197 }
198
199 pub fn eval_expr(&mut self, expr: &Spanned<Expr>) -> Result<Value, RuntimeError> {
201 let nv = self.eval_expr_nv(expr)?;
202 Ok(nv.to_value(&self.arena))
203 }
204
205 pub(super) fn eval_expr_nv(&mut self, expr: &Spanned<Expr>) -> Result<NanValue, RuntimeError> {
207 let ctx = super::ir_bridge::InterpreterLowerCtx::new(self);
208 let (lowered, root) = lowered::lower_expr_root(expr, &ctx);
209 self.eval_loop(
210 EvalState::Expr {
211 lowered,
212 expr: root,
213 },
214 Vec::new(),
215 )
216 }
217
218 fn eval_loop(
219 &mut self,
220 initial: EvalState,
221 mut conts: Vec<EvalCont>,
222 ) -> Result<NanValue, RuntimeError> {
223 let mut state = initial;
224
225 loop {
226 state = match state {
227 EvalState::Expr { lowered, expr } => self.step_expr(lowered, expr, &mut conts),
228 EvalState::Body {
229 lowered,
230 idx,
231 local_slots,
232 last,
233 } => self.step_body(lowered, idx, local_slots, last, &mut conts),
234 EvalState::Apply(result) => {
235 let Some(cont) = conts.pop() else {
236 return result;
237 };
238 self.apply_cont(cont, result, &mut conts)
239 }
240 };
241 }
242 }
243
244 fn step_expr(
245 &mut self,
246 lowered: Rc<LoweredFunctionBody>,
247 expr_id: ExprId,
248 conts: &mut Vec<EvalCont>,
249 ) -> EvalState {
250 match lowered.expr(expr_id) {
251 LoweredExpr::Literal(lit) => EvalState::Apply(Ok(self.eval_literal_nv(lit))),
252 &LoweredExpr::Resolved(slot) => EvalState::Apply(self.lookup_slot(slot)),
253 LoweredExpr::Ident(name) => EvalState::Apply(self.lookup_nv(name)),
254 LoweredExpr::Attr { obj, field, line } => {
255 let obj = *obj;
256 let line = *line;
257 if let LoweredExpr::Ident(name) = lowered.expr(obj) {
258 let nv = match self.lookup_nv(name) {
259 Ok(nv) => nv,
260 Err(err) => return EvalState::Apply(Err(err)),
261 };
262 let result = self.attr_access_nv(nv, field);
263 return EvalState::Apply(result.map_err(|e| e.at_line(line)));
264 }
265
266 let field = field.clone();
267 conts.push(EvalCont::Attr(field, line));
268 EvalState::Expr { lowered, expr: obj }
269 }
270 LoweredExpr::FnCall {
271 fn_expr,
272 args,
273 call_line,
274 } => {
275 let fn_expr = *fn_expr;
276 let args = Rc::clone(args);
277 let call_line = *call_line;
278 conts.push(EvalCont::Call {
279 lowered: Rc::clone(&lowered),
280 idx: 0,
281 fn_val: None,
282 arg_vals: Vec::with_capacity(args.len()),
283 args,
284 call_line,
285 });
286 EvalState::Expr {
287 lowered,
288 expr: fn_expr,
289 }
290 }
291 LoweredExpr::DirectCall {
292 target,
293 args,
294 call_line,
295 } => self.resume_direct_call(
296 Rc::clone(&lowered),
297 target.clone(),
298 Rc::clone(args),
299 0,
300 Vec::with_capacity(args.len()),
301 *call_line,
302 conts,
303 ),
304 LoweredExpr::ForwardCall {
305 target,
306 args,
307 call_line,
308 } => self.dispatch_forward_call(target.clone(), args, *call_line, conts),
309 &LoweredExpr::BinOp {
310 op,
311 left,
312 right,
313 line,
314 } => {
315 conts.push(EvalCont::BinOpLeft {
316 lowered: Rc::clone(&lowered),
317 op,
318 right,
319 line,
320 });
321 EvalState::Expr {
322 lowered,
323 expr: left,
324 }
325 }
326 LoweredExpr::Match {
327 subject,
328 arms,
329 line,
330 } => {
331 let subject = *subject;
332 let line = *line;
333 conts.push(EvalCont::Match {
334 lowered: Rc::clone(&lowered),
335 arms: Rc::clone(arms),
336 line,
337 });
338 EvalState::Expr {
339 lowered,
340 expr: subject,
341 }
342 }
343 LoweredExpr::Leaf(leaf) => {
344 let leaf = leaf.clone();
345 self.resume_leaf(
346 Rc::clone(&lowered),
347 leaf.clone(),
348 0,
349 Vec::with_capacity(leaf.arity()),
350 conts,
351 )
352 }
353 LoweredExpr::Constructor { name, arg } => match arg {
354 Some(inner) => {
355 let inner = *inner;
356 conts.push(EvalCont::Constructor(name.clone()));
357 EvalState::Expr {
358 lowered,
359 expr: inner,
360 }
361 }
362 None => EvalState::Apply(self.apply_runtime_constructor_nv(name, None)),
363 },
364 &LoweredExpr::ErrorProp { inner, line } => {
365 conts.push(EvalCont::ErrorProp(line));
366 EvalState::Expr {
367 lowered,
368 expr: inner,
369 }
370 }
371 LoweredExpr::InterpolatedStr(parts) => {
372 let parts = Rc::clone(parts);
373 self.resume_interpolated_str(lowered, parts, 0, String::new(), conts)
374 }
375 LoweredExpr::List(items) => {
376 let items = Rc::clone(items);
377 self.resume_list(lowered, items, 0, Vec::new(), conts)
378 }
379 LoweredExpr::Tuple(items) => {
380 let cap = items.len();
381 let items = Rc::clone(items);
382 self.resume_tuple(lowered, items, 0, Vec::with_capacity(cap), conts)
383 }
384 LoweredExpr::IndependentProduct { items, unwrap } => {
385 let cap = items.len();
386 let items = Rc::clone(items);
387 let unwrap = *unwrap;
388 self.resume_independent_product(
389 lowered,
390 items,
391 0,
392 Vec::with_capacity(cap),
393 unwrap,
394 conts,
395 )
396 }
397 LoweredExpr::MapLiteral(entries) => {
398 let entries = Rc::clone(entries);
399 self.resume_map(
400 lowered,
401 entries,
402 0,
403 crate::nan_value::PersistentMap::new(),
404 conts,
405 )
406 }
407 LoweredExpr::RecordCreate { type_name, fields } => {
408 let type_name = type_name.clone();
409 let fields = Rc::clone(fields);
410 self.resume_record_create(
411 RecordCreateProgress {
412 lowered,
413 type_name,
414 fields,
415 idx: 0,
416 seen: HashSet::new(),
417 values: Vec::new(),
418 },
419 conts,
420 )
421 }
422 LoweredExpr::RecordUpdate {
423 type_name,
424 base,
425 updates,
426 } => {
427 let base = *base;
428 conts.push(EvalCont::RecordUpdateBase {
429 lowered: Rc::clone(&lowered),
430 type_name: type_name.clone(),
431 updates: Rc::clone(updates),
432 });
433 EvalState::Expr {
434 lowered,
435 expr: base,
436 }
437 }
438 LoweredExpr::TailCall { target, args } => {
439 let target = target.clone();
440 let args = Rc::clone(args);
441 self.resume_tail_call(lowered, target, args, 0, Vec::new(), conts)
442 }
443 }
444 }
445
446 fn step_body(
447 &mut self,
448 lowered: Rc<LoweredFunctionBody>,
449 idx: usize,
450 local_slots: Option<Rc<HashMap<String, u16>>>,
451 last: NanValue,
452 conts: &mut Vec<EvalCont>,
453 ) -> EvalState {
454 let Some(stmt) = lowered.stmt(idx) else {
455 return EvalState::Apply(Ok(last));
456 };
457
458 match stmt {
459 LoweredStmt::Binding(name, expr) => {
460 let expr = *expr;
461 conts.push(EvalCont::BodyBinding {
462 name: name.clone(),
463 next_idx: idx + 1,
464 lowered: Rc::clone(&lowered),
465 local_slots,
466 });
467 EvalState::Expr { lowered, expr }
468 }
469 &LoweredStmt::Expr(expr) => {
470 conts.push(EvalCont::BodyExpr {
471 next_idx: idx + 1,
472 lowered: Rc::clone(&lowered),
473 local_slots,
474 });
475 EvalState::Expr { lowered, expr }
476 }
477 }
478 }
479
480 fn apply_cont(
481 &mut self,
482 cont: EvalCont,
483 result: Result<NanValue, RuntimeError>,
484 conts: &mut Vec<EvalCont>,
485 ) -> EvalState {
486 match cont {
487 EvalCont::Attr(field, line) => match result {
488 Ok(nv) => {
489 EvalState::Apply(self.attr_access_nv(nv, &field).map_err(|e| e.at_line(line)))
490 }
491 Err(err) => EvalState::Apply(Err(err)),
492 },
493 EvalCont::Call {
494 lowered,
495 args,
496 mut idx,
497 mut fn_val,
498 mut arg_vals,
499 call_line,
500 } => match result {
501 Ok(value) => {
502 if fn_val.is_none() {
503 fn_val = Some(value);
504 if args.is_empty() {
505 self.last_call_line = call_line;
506 let state = self.dispatch_call(
507 fn_val.expect("function value set before dispatch"),
508 arg_vals,
509 conts,
510 );
511 return state.map_err_line(call_line);
512 }
513 conts.push(EvalCont::Call {
514 lowered: Rc::clone(&lowered),
515 args: Rc::clone(&args),
516 idx,
517 fn_val,
518 arg_vals,
519 call_line,
520 });
521 return EvalState::Expr {
522 lowered,
523 expr: args[idx],
524 };
525 }
526
527 arg_vals.push(value);
528 idx += 1;
529 if idx < args.len() {
530 conts.push(EvalCont::Call {
531 lowered: Rc::clone(&lowered),
532 args: Rc::clone(&args),
533 idx,
534 fn_val,
535 arg_vals,
536 call_line,
537 });
538 EvalState::Expr {
539 lowered,
540 expr: args[idx],
541 }
542 } else {
543 self.last_call_line = call_line;
544 let state = self.dispatch_call(
545 fn_val.expect("function value present when args are done"),
546 arg_vals,
547 conts,
548 );
549 state.map_err_line(call_line)
550 }
551 }
552 Err(err) => EvalState::Apply(Err(err)),
553 },
554 EvalCont::BinOpLeft {
555 lowered,
556 op,
557 right,
558 line,
559 } => match result {
560 Ok(left) => {
561 conts.push(EvalCont::BinOpRight { op, left, line });
562 EvalState::Expr {
563 lowered,
564 expr: right,
565 }
566 }
567 Err(err) => EvalState::Apply(Err(err)),
568 },
569 EvalCont::BinOpRight { op, left, line } => match result {
570 Ok(right) => EvalState::Apply(
571 self.eval_binop_nv(&op, left, right)
572 .map_err(|e| e.at_line(line)),
573 ),
574 Err(err) => EvalState::Apply(Err(err)),
575 },
576 EvalCont::Match {
577 lowered,
578 arms,
579 line,
580 } => match result {
581 Ok(subject) => self.dispatch_match(lowered, subject, arms, line, conts),
582 Err(err) => EvalState::Apply(Err(err)),
583 },
584 EvalCont::DirectCall {
585 lowered,
586 target,
587 idx,
588 args,
589 mut arg_vals,
590 call_line,
591 } => match result {
592 Ok(value) => {
593 arg_vals.push(value);
594 self.resume_direct_call(
595 lowered,
596 target,
597 args,
598 idx + 1,
599 arg_vals,
600 call_line,
601 conts,
602 )
603 }
604 Err(err) => EvalState::Apply(Err(err)),
605 },
606 EvalCont::Leaf {
607 lowered,
608 leaf,
609 idx,
610 mut values,
611 } => match result {
612 Ok(value) => {
613 values.push(value);
614 self.resume_leaf(lowered, leaf, idx + 1, values, conts)
615 }
616 Err(err) => EvalState::Apply(Err(err)),
617 },
618 EvalCont::Constructor(name) => match result {
619 Ok(inner) => {
620 EvalState::Apply(self.apply_runtime_constructor_nv(&name, Some(inner)))
621 }
622 Err(err) => EvalState::Apply(Err(err)),
623 },
624 EvalCont::ErrorProp(line) => match result {
625 Ok(value) => EvalState::Apply(if value.is_ok() {
626 let inner = value.wrapper_inner(&self.arena);
627 Ok(inner)
628 } else if value.is_err() {
629 let inner = value.wrapper_inner(&self.arena);
630 Err(RuntimeError::ErrProp(inner))
631 } else {
632 Err(RuntimeError::Error(
633 "Operator '?' can only be applied to Result".to_string(),
634 )
635 .at_line(line))
636 }),
637 Err(err) => EvalState::Apply(Err(err)),
638 },
639 EvalCont::InterpolatedStr {
640 lowered,
641 parts,
642 idx,
643 result: mut text,
644 } => match result {
645 Ok(value) => {
646 text.push_str(&value.repr(&self.arena));
647 self.resume_interpolated_str(lowered, parts, idx, text, conts)
648 }
649 Err(err) => EvalState::Apply(Err(err)),
650 },
651 EvalCont::List {
652 lowered,
653 items,
654 idx,
655 mut values,
656 } => match result {
657 Ok(value) => {
658 values.push(value);
659 self.resume_list(lowered, items, idx, values, conts)
660 }
661 Err(err) => EvalState::Apply(Err(err)),
662 },
663 EvalCont::Tuple {
664 lowered,
665 items,
666 idx,
667 mut values,
668 } => match result {
669 Ok(value) => {
670 values.push(value);
671 self.resume_tuple(lowered, items, idx, values, conts)
672 }
673 Err(err) => EvalState::Apply(Err(err)),
674 },
675 EvalCont::IndependentProduct {
676 lowered,
677 items,
678 idx,
679 mut values,
680 unwrap,
681 } => match result {
682 Ok(value) => {
683 values.push(value);
684 self.resume_independent_product(lowered, items, idx, values, unwrap, conts)
685 }
686 Err(err) => EvalState::Apply(Err(err)),
687 },
688 EvalCont::MapKey {
689 lowered,
690 entries,
691 idx,
692 map,
693 } => match result {
694 Ok(key) => {
695 if !Self::is_hashable_map_key_nv(key) {
696 return EvalState::Apply(Err(RuntimeError::Error(
697 "Map literal key must be Int, Float, String, or Bool".to_string(),
698 )));
699 }
700 conts.push(EvalCont::MapValue {
701 lowered: Rc::clone(&lowered),
702 entries: Rc::clone(&entries),
703 idx,
704 map,
705 key,
706 });
707 EvalState::Expr {
708 lowered,
709 expr: entries[idx].1,
710 }
711 }
712 Err(err) => EvalState::Apply(Err(err)),
713 },
714 EvalCont::MapValue {
715 lowered,
716 entries,
717 idx,
718 map,
719 key,
720 } => match result {
721 Ok(value) => {
722 let hash = key.map_key_hash(&self.arena);
723 let map = map.insert(hash, (key, value));
724 self.resume_map(lowered, entries, idx + 1, map, conts)
725 }
726 Err(err) => EvalState::Apply(Err(err)),
727 },
728 EvalCont::RecordCreate(mut progress) => match result {
729 Ok(value) => {
730 let field_name = progress.fields[progress.idx].0.clone();
731 if !progress.seen.insert(field_name.clone()) {
732 return EvalState::Apply(Err(RuntimeError::Error(format!(
733 "Record '{}' field '{}' provided more than once",
734 progress.type_name, field_name
735 ))));
736 }
737 progress.values.push((field_name, value));
738 progress.idx += 1;
739 self.resume_record_create(progress, conts)
740 }
741 Err(err) => EvalState::Apply(Err(err)),
742 },
743 EvalCont::RecordUpdateBase {
744 lowered,
745 type_name,
746 updates,
747 } => match result {
748 Ok(base_nv) => {
749 if !base_nv.is_record() {
750 return EvalState::Apply(Err(RuntimeError::Error(format!(
751 "{}.update: base must be a {} record",
752 type_name, type_name
753 ))));
754 }
755 let (base_type_id, base_fields) = self.arena.get_record(base_nv.arena_index());
756 let base_type_name = self.arena.get_type_name(base_type_id).to_string();
757 if base_type_name != type_name {
758 return EvalState::Apply(Err(RuntimeError::Error(format!(
759 "{}.update: base is a {} record, expected {}",
760 type_name, base_type_name, type_name
761 ))));
762 }
763 let base_field_names: Vec<String> =
764 self.arena.get_field_names(base_type_id).to_vec();
765 let base_fields_vec: Vec<NanValue> = base_fields.to_vec();
766 self.resume_record_update(
767 RecordUpdateProgress {
768 lowered,
769 type_name,
770 base_type_id,
771 base_fields: base_fields_vec,
772 base_field_names,
773 updates,
774 idx: 0,
775 update_vals: Vec::new(),
776 },
777 conts,
778 )
779 }
780 Err(err) => EvalState::Apply(Err(err)),
781 },
782 EvalCont::RecordUpdateField(mut progress) => match result {
783 Ok(value) => {
784 progress
785 .update_vals
786 .push((progress.updates[progress.idx].0.clone(), value));
787 progress.idx += 1;
788 self.resume_record_update(progress, conts)
789 }
790 Err(err) => EvalState::Apply(Err(err)),
791 },
792 EvalCont::TailCallArgs {
793 lowered,
794 target,
795 args,
796 idx,
797 mut values,
798 } => match result {
799 Ok(value) => {
800 values.push(value);
801 self.resume_tail_call(lowered, target, args, idx + 1, values, conts)
802 }
803 Err(err) => EvalState::Apply(Err(err)),
804 },
805 EvalCont::BodyBinding {
806 name,
807 next_idx,
808 lowered,
809 local_slots,
810 } => match result {
811 Ok(value) => {
812 if let Some(local_slots) = local_slots.as_ref()
813 && let Some(&slot) = local_slots.get(&name)
814 {
815 self.define_slot(slot, value);
816 } else {
817 self.define_nv(name, value);
818 }
819 EvalState::Body {
820 lowered,
821 idx: next_idx,
822 local_slots,
823 last: NanValue::UNIT,
824 }
825 }
826 Err(err) => EvalState::Apply(Err(err)),
827 },
828 EvalCont::BodyExpr {
829 next_idx,
830 lowered,
831 local_slots,
832 } => match result {
833 Ok(value) => EvalState::Body {
834 lowered,
835 idx: next_idx,
836 local_slots,
837 last: value,
838 },
839 Err(err) => EvalState::Apply(Err(err)),
840 },
841 EvalCont::MatchScope => {
842 self.pop_env();
843 EvalState::Apply(result)
844 }
845 EvalCont::FunctionReturn(frame) => self.finish_function_call(frame, result, conts),
846 }
847 }
848
849 fn attr_access_nv(&self, nv: NanValue, field: &str) -> Result<NanValue, RuntimeError> {
851 if nv.is_record() {
852 let (tid, fields) = self.arena.get_record(nv.arena_index());
853 let field_names = self.arena.get_field_names(tid);
854 if let Some(pos) = field_names.iter().position(|n| n == field) {
855 return Ok(fields[pos]);
856 }
857 return Err(RuntimeError::Error(format!("Unknown field '{}'", field)));
858 }
859 if nv.is_namespace() {
860 let (name, members) = self.arena.get_namespace(nv.symbol_index());
861 if let Some((_, member_nv)) = members.iter().find(|(k, _)| k.as_ref() == field) {
862 return Ok(*member_nv);
863 }
864 return Err(RuntimeError::Error(format!(
865 "Unknown member '{}.{}'",
866 name, field
867 )));
868 }
869 Err(RuntimeError::Error(format!(
870 "Field access '{}' is not supported on this value",
871 field
872 )))
873 }
874
875 fn dispatch_match(
876 &mut self,
877 lowered: Rc<LoweredFunctionBody>,
878 subject: NanValue,
879 arms: SharedMatchArms,
880 line: usize,
881 conts: &mut Vec<EvalCont>,
882 ) -> EvalState {
883 if let Some((arm_idx, bindings)) = self.try_dispatch_match_plan_nv(subject, arms.as_ref()) {
884 let arm_count = arms.len();
885 let arm = &arms[arm_idx];
886 self.note_verify_match_arm(line, arm_count, arm_idx);
887 if bindings.is_empty() {
888 return EvalState::Expr {
889 lowered,
890 expr: arm.body,
891 };
892 }
893
894 if let Some(local_slots) = self.active_local_slots.clone() {
895 let all_slotted = bindings
896 .iter()
897 .all(|(name, _)| local_slots.contains_key(name));
898 if all_slotted {
899 for (name, nv) in bindings {
900 if let Some(&slot) = local_slots.get(&name) {
901 self.define_slot(slot, nv);
902 }
903 }
904 return EvalState::Expr {
905 lowered,
906 expr: arm.body,
907 };
908 }
909 }
910
911 let scope: HashMap<String, NanValue> = bindings.into_iter().collect();
912 self.push_env(EnvFrame::Owned(scope));
913 conts.push(EvalCont::MatchScope);
914 return EvalState::Expr {
915 lowered,
916 expr: arm.body,
917 };
918 }
919
920 let arm_count = arms.len();
921 for (arm_idx, arm) in arms.iter().enumerate() {
922 if let Some(bindings) = self.match_pattern_nv(&arm.pattern, subject) {
923 self.note_verify_match_arm(line, arm_count, arm_idx);
924 if bindings.is_empty() {
925 return EvalState::Expr {
926 lowered,
927 expr: arm.body,
928 };
929 }
930
931 if let Some(local_slots) = self.active_local_slots.clone() {
932 let all_slotted = bindings
933 .iter()
934 .all(|(name, _)| local_slots.contains_key(name));
935 if all_slotted {
936 for (name, nv) in bindings {
937 if let Some(&slot) = local_slots.get(&name) {
938 self.define_slot(slot, nv);
939 }
940 }
941 return EvalState::Expr {
942 lowered,
943 expr: arm.body,
944 };
945 }
946 }
947
948 let scope: HashMap<String, NanValue> = bindings.into_iter().collect();
949 self.push_env(EnvFrame::Owned(scope));
950 conts.push(EvalCont::MatchScope);
951 return EvalState::Expr {
952 lowered,
953 expr: arm.body,
954 };
955 }
956 }
957
958 EvalState::Apply(Err(RuntimeError::Error(format!(
959 "No match found for value {}",
960 subject.repr(&self.arena)
961 ))))
962 }
963
964 fn dispatch_call(
965 &mut self,
966 fn_val: NanValue,
967 args: Vec<NanValue>,
968 conts: &mut Vec<EvalCont>,
969 ) -> EvalState {
970 match self.start_call_nv(fn_val, args) {
971 Ok(CallDispatch::Immediate(result)) => EvalState::Apply(result),
972 Ok(CallDispatch::EnterFunction { frame, state }) => {
973 conts.push(EvalCont::FunctionReturn(*frame));
974 state
975 }
976 Err(err) => EvalState::Apply(Err(err)),
977 }
978 }
979
980 fn start_call_nv(
981 &mut self,
982 fn_val: NanValue,
983 args: Vec<NanValue>,
984 ) -> Result<CallDispatch, RuntimeError> {
985 if fn_val.is_builtin() {
986 let name = self.arena.get_builtin(fn_val.symbol_index()).to_string();
987 self.ensure_effects_allowed(&name, Self::builtin_effects(&name).iter().copied())?;
988 let result = self.call_builtin_nv(&name, &args);
989 return Ok(CallDispatch::Immediate(result));
990 }
991 if fn_val.is_fn() {
992 let function = Rc::clone(self.arena.get_fn_rc(fn_val.symbol_index()));
993 if args.len() != function.params.len() {
994 return Err(RuntimeError::Error(format!(
995 "Function '{}' expects {} arguments, got {}",
996 function.name,
997 function.params.len(),
998 args.len()
999 )));
1000 }
1001 self.ensure_effects_allowed(
1002 function.name.as_str(),
1003 function.effects.iter().map(String::as_str),
1004 )?;
1005
1006 let memo_key = if function.memo_eligible {
1007 let key = hash_memo_args_nv(&args, &self.arena);
1008 let fn_name: &str = function.name.as_ref();
1009 if let Some(cache) = self.memo_cache.get_mut(fn_name)
1010 && let Some(cached_val) = cache.get_nv_as_value(key, &args, &self.arena)
1011 {
1012 let cached_nv = NanValue::from_value(&cached_val, &mut self.arena);
1013 return Ok(CallDispatch::Immediate(Ok(cached_nv)));
1014 }
1015 Some((key, args.clone()))
1016 } else {
1017 None
1018 };
1019
1020 self.call_stack.push(CallFrame {
1021 name: Rc::clone(&function.name),
1022 effects: Rc::clone(&function.effects),
1023 });
1024
1025 let prev_local_slots = self.active_local_slots.take();
1026 let saved_base = self.env_base;
1027 self.env_base = self.env.len();
1028 let prev_global = if let Some(home) = function.home_globals.as_ref() {
1029 let global = self
1030 .env
1031 .first_mut()
1032 .ok_or_else(|| RuntimeError::Error("No global scope".to_string()))?;
1033 Some(std::mem::replace(global, EnvFrame::Shared(Rc::clone(home))))
1034 } else {
1035 None
1036 };
1037
1038 let active = ActiveFunction { function };
1039 let frame = FunctionFrame {
1040 active,
1041 prev_local_slots,
1042 saved_base,
1043 prev_global,
1044 memo_key,
1045 };
1046 let state = self.enter_function_body_nv(&frame.active, args);
1047 return Ok(CallDispatch::EnterFunction {
1048 frame: Box::new(frame),
1049 state,
1050 });
1051 }
1052 Err(RuntimeError::Error(format!(
1053 "Cannot call value: {}",
1054 fn_val.repr(&self.arena)
1055 )))
1056 }
1057
1058 fn enter_function_body_nv(
1059 &mut self,
1060 active: &ActiveFunction,
1061 args: Vec<NanValue>,
1062 ) -> EvalState {
1063 if let Some(resolution) = &active.function.resolution {
1064 let local_slots = Rc::clone(&resolution.local_slots);
1065 let mut slots = Self::empty_slots_nv(resolution.local_count);
1066 for ((param_name, _), arg_nv) in active.function.params.iter().zip(args.into_iter()) {
1067 if let Some(&slot) = resolution.local_slots.get(param_name) {
1068 slots[slot as usize] = arg_nv;
1069 }
1070 }
1071 self.active_local_slots = Some(Rc::clone(&local_slots));
1072 self.push_env(EnvFrame::Slots(slots));
1073 EvalState::Body {
1074 lowered: Rc::clone(&active.function.lowered_body),
1075 idx: 0,
1076 local_slots: Some(local_slots),
1077 last: NanValue::UNIT,
1078 }
1079 } else {
1080 let mut params_scope: HashMap<String, NanValue> = HashMap::new();
1081 for ((param_name, _), arg_nv) in active.function.params.iter().zip(args.into_iter()) {
1082 params_scope.insert(param_name.clone(), arg_nv);
1083 }
1084 self.push_env(EnvFrame::Owned(params_scope));
1085 EvalState::Body {
1086 lowered: Rc::clone(&active.function.lowered_body),
1087 idx: 0,
1088 local_slots: None,
1089 last: NanValue::UNIT,
1090 }
1091 }
1092 }
1093
1094 fn finish_function_call(
1095 &mut self,
1096 mut frame: FunctionFrame,
1097 result: Result<NanValue, RuntimeError>,
1098 conts: &mut Vec<EvalCont>,
1099 ) -> EvalState {
1100 self.pop_env();
1101
1102 match result {
1103 Err(RuntimeError::TailCall(boxed)) => {
1104 let (target, nv_args) = *boxed;
1105 let next_active = if target == frame.active.function.name.as_str() {
1106 frame.active.clone()
1107 } else {
1108 let next_function = match self.lookup_nv(&target) {
1109 Ok(nv) => {
1110 if !nv.is_fn() {
1111 return EvalState::Apply(Err(RuntimeError::Error(format!(
1112 "TCO target '{}' is not a function: {}",
1113 target,
1114 nv.repr(&self.arena)
1115 ))));
1116 }
1117 Rc::clone(self.arena.get_fn_rc(nv.symbol_index()))
1118 }
1119 Err(err) => return EvalState::Apply(Err(err)),
1120 };
1121
1122 if let Some(call_frame) = self.call_stack.last_mut() {
1123 call_frame.name = Rc::clone(&next_function.name);
1124 call_frame.effects = Rc::clone(&next_function.effects);
1125 }
1126
1127 ActiveFunction {
1128 function: next_function,
1129 }
1130 };
1131
1132 frame.active = next_active;
1133 let state = self.enter_function_body_nv(&frame.active, nv_args);
1134 conts.push(EvalCont::FunctionReturn(frame));
1135 state
1136 }
1137 other => {
1138 self.active_local_slots = frame.prev_local_slots;
1139 if let Some(prev) = frame.prev_global
1140 && let Some(global) = self.env.first_mut()
1141 {
1142 *global = prev;
1143 }
1144 self.env_base = frame.saved_base;
1145 self.call_stack.pop();
1146
1147 let final_result = match other {
1148 Ok(value) => Ok(value),
1149 Err(RuntimeError::ErrProp(err_nv)) => {
1150 Ok(NanValue::new_err_value(err_nv, &mut self.arena))
1151 }
1152 Err(err) => Err(err),
1153 };
1154
1155 if let (Some((key, memo_args)), Ok(value)) = (&frame.memo_key, &final_result) {
1156 let fn_name: &str = frame.active.function.name.as_ref();
1157 let cache = if let Some(c) = self.memo_cache.get_mut(fn_name) {
1158 c
1159 } else {
1160 self.memo_cache
1161 .entry(frame.active.function.name.as_ref().clone())
1162 .or_default()
1163 };
1164 cache.insert_nv(
1165 *key,
1166 memo_args.clone(),
1167 *value,
1168 &self.arena,
1169 MEMO_CACHE_CAP_PER_FN,
1170 );
1171 }
1172
1173 EvalState::Apply(final_result)
1174 }
1175 }
1176 }
1177
1178 fn resume_interpolated_str(
1179 &mut self,
1180 lowered: Rc<LoweredFunctionBody>,
1181 parts: SharedStrParts,
1182 mut idx: usize,
1183 mut result: String,
1184 conts: &mut Vec<EvalCont>,
1185 ) -> EvalState {
1186 while idx < parts.len() {
1187 match parts[idx].clone() {
1188 LoweredStrPart::Literal(text) => {
1189 result.push_str(&text);
1190 idx += 1;
1191 }
1192 LoweredStrPart::Parsed(expr) => {
1193 conts.push(EvalCont::InterpolatedStr {
1194 lowered: Rc::clone(&lowered),
1195 parts: Rc::clone(&parts),
1196 idx: idx + 1,
1197 result,
1198 });
1199 return EvalState::Expr { lowered, expr };
1200 }
1201 }
1202 }
1203 EvalState::Apply(Ok(NanValue::new_string_value(&result, &mut self.arena)))
1204 }
1205
1206 #[allow(clippy::too_many_arguments)]
1207 fn resume_direct_call(
1208 &mut self,
1209 lowered: Rc<LoweredFunctionBody>,
1210 target: LoweredDirectCallTarget,
1211 args: SharedExprs,
1212 idx: usize,
1213 arg_vals: Vec<NanValue>,
1214 call_line: usize,
1215 conts: &mut Vec<EvalCont>,
1216 ) -> EvalState {
1217 if idx >= args.len() {
1218 self.last_call_line = call_line;
1219 return self
1220 .dispatch_direct_call(target, arg_vals, conts)
1221 .map_err_line(call_line);
1222 }
1223
1224 conts.push(EvalCont::DirectCall {
1225 lowered: Rc::clone(&lowered),
1226 target,
1227 idx,
1228 args: Rc::clone(&args),
1229 arg_vals,
1230 call_line,
1231 });
1232 EvalState::Expr {
1233 lowered,
1234 expr: args[idx],
1235 }
1236 }
1237
1238 fn resume_leaf(
1239 &mut self,
1240 lowered: Rc<LoweredFunctionBody>,
1241 leaf: LoweredLeafOp,
1242 idx: usize,
1243 values: Vec<NanValue>,
1244 conts: &mut Vec<EvalCont>,
1245 ) -> EvalState {
1246 if let Some(expr) = leaf.arg_at(idx) {
1247 conts.push(EvalCont::Leaf {
1248 lowered: Rc::clone(&lowered),
1249 leaf,
1250 idx,
1251 values,
1252 });
1253 EvalState::Expr { lowered, expr }
1254 } else {
1255 EvalState::Apply(self.apply_leaf_op_nv(&leaf, &values))
1256 }
1257 }
1258
1259 fn resume_list(
1260 &mut self,
1261 lowered: Rc<LoweredFunctionBody>,
1262 items: SharedExprs,
1263 idx: usize,
1264 values: Vec<NanValue>,
1265 conts: &mut Vec<EvalCont>,
1266 ) -> EvalState {
1267 if idx >= items.len() {
1268 let list_idx = self.arena.push_list(values);
1269 return EvalState::Apply(Ok(NanValue::new_list(list_idx)));
1270 }
1271
1272 conts.push(EvalCont::List {
1273 lowered: Rc::clone(&lowered),
1274 items: Rc::clone(&items),
1275 idx: idx + 1,
1276 values,
1277 });
1278 EvalState::Expr {
1279 lowered,
1280 expr: items[idx],
1281 }
1282 }
1283
1284 fn resume_tuple(
1285 &mut self,
1286 lowered: Rc<LoweredFunctionBody>,
1287 items: SharedExprs,
1288 idx: usize,
1289 values: Vec<NanValue>,
1290 conts: &mut Vec<EvalCont>,
1291 ) -> EvalState {
1292 if idx >= items.len() {
1293 let tuple_idx = self.arena.push_tuple(values);
1294 return EvalState::Apply(Ok(NanValue::new_tuple(tuple_idx)));
1295 }
1296
1297 conts.push(EvalCont::Tuple {
1298 lowered: Rc::clone(&lowered),
1299 items: Rc::clone(&items),
1300 idx: idx + 1,
1301 values,
1302 });
1303 EvalState::Expr {
1304 lowered,
1305 expr: items[idx],
1306 }
1307 }
1308
1309 fn resume_independent_product(
1310 &mut self,
1311 lowered: Rc<LoweredFunctionBody>,
1312 items: SharedExprs,
1313 idx: usize,
1314 values: Vec<NanValue>,
1315 unwrap: bool,
1316 conts: &mut Vec<EvalCont>,
1317 ) -> EvalState {
1318 if idx == 0 {
1320 self.replay_state.enter_group();
1321 }
1322
1323 if idx >= items.len() {
1324 self.replay_state.exit_group();
1326
1327 if unwrap {
1328 let mut unwrapped = Vec::with_capacity(values.len());
1330 for v in &values {
1331 if v.is_ok() {
1332 unwrapped.push(v.wrapper_inner(&self.arena));
1333 } else if v.is_err() {
1334 let inner = v.wrapper_inner(&self.arena);
1335 return EvalState::Apply(Err(RuntimeError::ErrProp(inner)));
1336 } else {
1337 return EvalState::Apply(Err(RuntimeError::Error(
1338 "Independent product with '?' can only contain Result values"
1339 .to_string(),
1340 )));
1341 }
1342 }
1343 let tuple_idx = self.arena.push_tuple(unwrapped);
1344 return EvalState::Apply(Ok(NanValue::new_tuple(tuple_idx)));
1345 } else {
1346 let tuple_idx = self.arena.push_tuple(values);
1347 return EvalState::Apply(Ok(NanValue::new_tuple(tuple_idx)));
1348 }
1349 }
1350
1351 self.replay_state.set_branch(idx as u32);
1352 conts.push(EvalCont::IndependentProduct {
1353 lowered: Rc::clone(&lowered),
1354 items: Rc::clone(&items),
1355 idx: idx + 1,
1356 values,
1357 unwrap,
1358 });
1359 EvalState::Expr {
1360 lowered,
1361 expr: items[idx],
1362 }
1363 }
1364
1365 fn resume_map(
1366 &mut self,
1367 lowered: Rc<LoweredFunctionBody>,
1368 entries: SharedMapEntries,
1369 idx: usize,
1370 map: crate::nan_value::PersistentMap,
1371 conts: &mut Vec<EvalCont>,
1372 ) -> EvalState {
1373 if idx >= entries.len() {
1374 let map_idx = self.arena.push_map(map);
1375 return EvalState::Apply(Ok(NanValue::new_map(map_idx)));
1376 }
1377
1378 conts.push(EvalCont::MapKey {
1379 lowered: Rc::clone(&lowered),
1380 entries: Rc::clone(&entries),
1381 idx,
1382 map,
1383 });
1384 EvalState::Expr {
1385 lowered,
1386 expr: entries[idx].0,
1387 }
1388 }
1389
1390 fn resume_record_create(
1391 &mut self,
1392 progress: RecordCreateProgress,
1393 conts: &mut Vec<EvalCont>,
1394 ) -> EvalState {
1395 if progress.idx >= progress.fields.len() {
1396 return EvalState::Apply(
1397 self.build_record_create_nv(&progress.type_name, progress.values),
1398 );
1399 }
1400
1401 let lowered = Rc::clone(&progress.lowered);
1402 let expr = progress.fields[progress.idx].1;
1403 conts.push(EvalCont::RecordCreate(progress));
1404 EvalState::Expr { lowered, expr }
1405 }
1406
1407 fn resume_record_update(
1408 &mut self,
1409 progress: RecordUpdateProgress,
1410 conts: &mut Vec<EvalCont>,
1411 ) -> EvalState {
1412 if progress.idx >= progress.updates.len() {
1413 return EvalState::Apply(self.build_record_update_nv(
1414 &progress.type_name,
1415 progress.base_type_id,
1416 progress.base_fields,
1417 progress.base_field_names,
1418 progress.update_vals,
1419 ));
1420 }
1421
1422 let next_expr = progress.updates[progress.idx].1;
1423 let lowered = Rc::clone(&progress.lowered);
1424 conts.push(EvalCont::RecordUpdateField(progress));
1425 EvalState::Expr {
1426 lowered,
1427 expr: next_expr,
1428 }
1429 }
1430
1431 fn resume_tail_call(
1432 &mut self,
1433 lowered: Rc<LoweredFunctionBody>,
1434 target: LoweredTailCallTarget,
1435 args: SharedExprs,
1436 idx: usize,
1437 values: Vec<NanValue>,
1438 conts: &mut Vec<EvalCont>,
1439 ) -> EvalState {
1440 if idx >= args.len() {
1441 let resolved_target = match target {
1442 LoweredTailCallTarget::SelfCall => self
1443 .call_stack
1444 .last()
1445 .map(|frame| frame.name.as_ref().clone())
1446 .unwrap_or_default(),
1447 LoweredTailCallTarget::KnownFunction(name)
1448 | LoweredTailCallTarget::Unknown(name) => name,
1449 };
1450 return EvalState::Apply(Err(RuntimeError::TailCall(Box::new((
1451 resolved_target,
1452 values,
1453 )))));
1454 }
1455
1456 conts.push(EvalCont::TailCallArgs {
1457 lowered: Rc::clone(&lowered),
1458 target,
1459 args: Rc::clone(&args),
1460 idx,
1461 values,
1462 });
1463 EvalState::Expr {
1464 lowered,
1465 expr: args[idx],
1466 }
1467 }
1468
1469 fn build_record_create_nv(
1470 &mut self,
1471 type_name: &str,
1472 field_vals: Vec<(String, NanValue)>,
1473 ) -> Result<NanValue, RuntimeError> {
1474 if let Some(schema) = self.record_schemas.get(type_name) {
1475 let schema_clone = schema.clone();
1478 let type_id = self.arena.find_type_id(type_name).unwrap_or_else(|| {
1479 self.arena
1480 .register_record_type(type_name, schema_clone.clone())
1481 });
1482 let mut by_name = HashMap::with_capacity(field_vals.len());
1483 for (name, value) in field_vals {
1484 if by_name.insert(name.clone(), value).is_some() {
1485 return Err(RuntimeError::Error(format!(
1486 "Record '{}' field '{}' provided more than once",
1487 type_name, name
1488 )));
1489 }
1490 }
1491
1492 for provided in by_name.keys() {
1493 if !schema_clone.iter().any(|field| field == provided) {
1494 return Err(RuntimeError::Error(format!(
1495 "Record '{}' has no field '{}'",
1496 type_name, provided
1497 )));
1498 }
1499 }
1500
1501 let mut ordered = Vec::with_capacity(schema_clone.len());
1502 for required in &schema_clone {
1503 let value = by_name.remove(required).ok_or_else(|| {
1504 RuntimeError::Error(format!(
1505 "Record '{}' missing required field '{}'",
1506 type_name, required
1507 ))
1508 })?;
1509 ordered.push(value);
1510 }
1511
1512 let rec_idx = self.arena.push_record(type_id, ordered);
1513 return Ok(NanValue::new_record(rec_idx));
1514 }
1515
1516 let type_id = self.arena.find_type_id(type_name).unwrap_or_else(|| {
1518 let field_names: Vec<String> = field_vals.iter().map(|(n, _)| n.clone()).collect();
1519 self.arena.register_record_type(type_name, field_names)
1520 });
1521 let nv_fields: Vec<NanValue> = field_vals.iter().map(|(_, v)| *v).collect();
1522 let rec_idx = self.arena.push_record(type_id, nv_fields);
1523 Ok(NanValue::new_record(rec_idx))
1524 }
1525
1526 fn apply_leaf_op_nv(
1527 &mut self,
1528 leaf: &LoweredLeafOp,
1529 args: &[NanValue],
1530 ) -> Result<NanValue, RuntimeError> {
1531 match leaf {
1532 LoweredLeafOp::MapGet { .. } => {
1533 map::call_nv("Map.get", args, &mut self.arena).expect("Map.get leaf owned by Map")
1534 }
1535 LoweredLeafOp::MapSet { .. } => {
1536 map::call_nv("Map.set", args, &mut self.arena).expect("Map.set leaf owned by Map")
1537 }
1538 LoweredLeafOp::VectorNew { .. } => vector::call_nv("Vector.new", args, &mut self.arena)
1539 .expect("Vector.new leaf owned by Vector"),
1540 LoweredLeafOp::VectorGetOrDefaultLiteral {
1541 default_literal, ..
1542 } => {
1543 let opt = vector::call_nv("Vector.get", args, &mut self.arena)
1544 .expect("Vector.get leaf owned by Vector")?;
1545 if opt.is_some() {
1546 Ok(opt.wrapper_inner(&self.arena))
1547 } else if opt.is_none() {
1548 Ok(self.eval_literal_nv(default_literal))
1549 } else {
1550 Err(RuntimeError::Error(
1551 "Vector.get leaf expected Option result".to_string(),
1552 ))
1553 }
1554 }
1555 LoweredLeafOp::IntModOrDefaultLiteral {
1556 default_literal, ..
1557 } => {
1558 let a = args[0].as_int(&self.arena);
1559 let b = args[1].as_int(&self.arena);
1560 if b == 0 {
1561 Ok(self.eval_literal_nv(default_literal))
1562 } else {
1563 Ok(NanValue::new_int(a.rem_euclid(b), &mut self.arena))
1564 }
1565 }
1566 }
1567 }
1568
1569 fn dispatch_direct_call(
1570 &mut self,
1571 target: LoweredDirectCallTarget,
1572 args: Vec<NanValue>,
1573 conts: &mut Vec<EvalCont>,
1574 ) -> EvalState {
1575 match target {
1576 LoweredDirectCallTarget::Builtin(name) | LoweredDirectCallTarget::Function(name) => {
1577 match self.lookup_path_nv(&name) {
1578 Ok(fn_val) => self.dispatch_call(fn_val, args, conts),
1579 Err(err) => EvalState::Apply(Err(err)),
1580 }
1581 }
1582 LoweredDirectCallTarget::Wrapper(kind) => match args.as_slice() {
1583 [inner] => {
1584 let value = match kind {
1585 crate::ir::WrapperKind::ResultOk => {
1586 NanValue::new_ok_value(*inner, &mut self.arena)
1587 }
1588 crate::ir::WrapperKind::ResultErr => {
1589 NanValue::new_err_value(*inner, &mut self.arena)
1590 }
1591 crate::ir::WrapperKind::OptionSome => {
1592 NanValue::new_some_value(*inner, &mut self.arena)
1593 }
1594 };
1595 EvalState::Apply(Ok(value))
1596 }
1597 _ => EvalState::Apply(Err(RuntimeError::Error(
1598 "Wrapper constructor expects exactly 1 argument".to_string(),
1599 ))),
1600 },
1601 LoweredDirectCallTarget::NoneValue => {
1602 if args.is_empty() {
1603 EvalState::Apply(Ok(NanValue::NONE))
1604 } else {
1605 EvalState::Apply(Err(RuntimeError::Error(
1606 "Option.None expects 0 arguments".to_string(),
1607 )))
1608 }
1609 }
1610 LoweredDirectCallTarget::TypeConstructor {
1611 qualified_type_name,
1612 variant_name,
1613 } => {
1614 let name = format!("{qualified_type_name}.{variant_name}");
1615 EvalState::Apply(self.apply_runtime_constructor_args_nv(&name, &args))
1616 }
1617 }
1618 }
1619
1620 fn dispatch_forward_call(
1621 &mut self,
1622 target: LoweredDirectCallTarget,
1623 args: &[LoweredForwardArg],
1624 call_line: usize,
1625 conts: &mut Vec<EvalCont>,
1626 ) -> EvalState {
1627 let mut values = Vec::with_capacity(args.len());
1628 for arg in args {
1629 let value = match arg {
1630 LoweredForwardArg::Local(name) => match self.lookup_nv(name) {
1631 Ok(value) => value,
1632 Err(err) => return EvalState::Apply(Err(err)),
1633 },
1634 LoweredForwardArg::Slot(slot) => match self.lookup_slot(*slot) {
1635 Ok(value) => value,
1636 Err(err) => return EvalState::Apply(Err(err)),
1637 },
1638 };
1639 values.push(value);
1640 }
1641 self.last_call_line = call_line;
1642 self.dispatch_direct_call(target, values, conts)
1643 .map_err_line(call_line)
1644 }
1645
1646 fn build_record_update_nv(
1647 &mut self,
1648 type_name: &str,
1649 base_type_id: u32,
1650 mut base_fields: Vec<NanValue>,
1651 base_field_names: Vec<String>,
1652 update_vals: Vec<(String, NanValue)>,
1653 ) -> Result<NanValue, RuntimeError> {
1654 if let Some(schema) = self.record_schemas.get(type_name) {
1655 for (field_name, _) in &update_vals {
1656 if !schema.iter().any(|field| field == field_name) {
1657 return Err(RuntimeError::Error(format!(
1658 "Record '{}' has no field '{}'",
1659 type_name, field_name
1660 )));
1661 }
1662 }
1663 }
1664
1665 for (update_name, update_val) in update_vals {
1666 if let Some(pos) = base_field_names.iter().position(|n| n == &update_name) {
1667 base_fields[pos] = update_val;
1668 } else {
1669 return Err(RuntimeError::Error(format!(
1670 "Record '{}' has no field '{}'",
1671 type_name, update_name
1672 )));
1673 }
1674 }
1675
1676 let rec_idx = self.arena.push_record(base_type_id, base_fields);
1677 Ok(NanValue::new_record(rec_idx))
1678 }
1679
1680 fn is_hashable_map_key_nv(value: NanValue) -> bool {
1681 value.is_int() || value.is_float() || value.is_string() || value.is_bool()
1682 }
1683
1684 pub(super) fn eval_literal_nv(&mut self, lit: &Literal) -> NanValue {
1685 match lit {
1686 Literal::Int(i) => NanValue::new_int(*i, &mut self.arena),
1687 Literal::Float(f) => NanValue::new_float(*f),
1688 Literal::Str(s) => NanValue::new_string_value(s, &mut self.arena),
1689 Literal::Bool(b) => NanValue::new_bool(*b),
1690 Literal::Unit => NanValue::UNIT,
1691 }
1692 }
1693
1694 #[allow(dead_code)]
1695 pub(super) fn eval_literal(&self, lit: &Literal) -> Value {
1696 match lit {
1697 Literal::Int(i) => Value::Int(*i),
1698 Literal::Float(f) => Value::Float(*f),
1699 Literal::Str(s) => Value::Str(s.clone()),
1700 Literal::Bool(b) => Value::Bool(*b),
1701 Literal::Unit => Value::Unit,
1702 }
1703 }
1704
1705 pub(super) fn call_value(
1706 &mut self,
1707 fn_val: Value,
1708 args: Vec<Value>,
1709 ) -> Result<Value, RuntimeError> {
1710 let fn_nv = NanValue::from_value(&fn_val, &mut self.arena);
1711 let nv_args: Vec<NanValue> = args
1712 .iter()
1713 .map(|v| NanValue::from_value(v, &mut self.arena))
1714 .collect();
1715 match self.start_call_nv(fn_nv, nv_args)? {
1716 CallDispatch::Immediate(result) => result.map(|nv| nv.to_value(&self.arena)),
1717 CallDispatch::EnterFunction { frame, state } => {
1718 let nv = self.eval_loop(state, vec![EvalCont::FunctionReturn(*frame)])?;
1719 Ok(nv.to_value(&self.arena))
1720 }
1721 }
1722 }
1723}
1724
1725fn hash_memo_args_nv(args: &[NanValue], arena: &Arena) -> u64 {
1727 use std::hash::{Hash, Hasher};
1728 let mut hasher = std::collections::hash_map::DefaultHasher::new();
1729 args.len().hash(&mut hasher);
1730 for arg in args {
1731 arg.hash_in(&mut hasher, arena);
1732 }
1733 hasher.finish()
1734}