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