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