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" => {
490 let idx = self.arena.push_boxed(inner);
491 Ok(NanValue::new_ok(idx))
492 }
493 "Err" => {
494 let idx = self.arena.push_boxed(inner);
495 Ok(NanValue::new_err(idx))
496 }
497 "Some" => {
498 let idx = self.arena.push_boxed(inner);
499 Ok(NanValue::new_some(idx))
500 }
501 "None" => Err(RuntimeError::Error(
502 "Constructor 'None' does not take an argument".to_string(),
503 )),
504 _ => Err(RuntimeError::Error(format!(
505 "Unknown constructor: {}",
506 name
507 ))),
508 }),
509 Err(err) => EvalState::Apply(Err(err)),
510 },
511 EvalCont::ErrorProp => match result {
512 Ok(value) => EvalState::Apply(if value.is_ok() {
513 let inner = self.arena.get_boxed(value.wrapper_index());
514 Ok(inner)
515 } else if value.is_err() {
516 let inner = self.arena.get_boxed(value.wrapper_index());
517 Err(RuntimeError::ErrProp(inner))
518 } else {
519 Err(RuntimeError::Error(
520 "Operator '?' can only be applied to Result".to_string(),
521 ))
522 }),
523 Err(err) => EvalState::Apply(Err(err)),
524 },
525 EvalCont::InterpolatedStr {
526 lowered,
527 parts,
528 idx,
529 result: mut text,
530 } => match result {
531 Ok(value) => {
532 text.push_str(&value.repr(&self.arena));
533 self.resume_interpolated_str(lowered, parts, idx, text, conts)
534 }
535 Err(err) => EvalState::Apply(Err(err)),
536 },
537 EvalCont::List {
538 lowered,
539 items,
540 idx,
541 mut values,
542 } => match result {
543 Ok(value) => {
544 values.push(value);
545 self.resume_list(lowered, items, idx, values, conts)
546 }
547 Err(err) => EvalState::Apply(Err(err)),
548 },
549 EvalCont::Tuple {
550 lowered,
551 items,
552 idx,
553 mut values,
554 } => match result {
555 Ok(value) => {
556 values.push(value);
557 self.resume_tuple(lowered, items, idx, values, conts)
558 }
559 Err(err) => EvalState::Apply(Err(err)),
560 },
561 EvalCont::MapKey {
562 lowered,
563 entries,
564 idx,
565 map,
566 } => match result {
567 Ok(key) => {
568 if !Self::is_hashable_map_key_nv(key) {
569 return EvalState::Apply(Err(RuntimeError::Error(
570 "Map literal key must be Int, Float, String, or Bool".to_string(),
571 )));
572 }
573 conts.push(EvalCont::MapValue {
574 lowered: Rc::clone(&lowered),
575 entries: Rc::clone(&entries),
576 idx,
577 map,
578 key,
579 });
580 EvalState::Expr {
581 lowered,
582 expr: entries[idx].1,
583 }
584 }
585 Err(err) => EvalState::Apply(Err(err)),
586 },
587 EvalCont::MapValue {
588 lowered,
589 entries,
590 idx,
591 mut map,
592 key,
593 } => match result {
594 Ok(value) => {
595 let hash = key.map_key_hash(&self.arena);
596 map.insert(hash, (key, value));
597 self.resume_map(lowered, entries, idx + 1, map, conts)
598 }
599 Err(err) => EvalState::Apply(Err(err)),
600 },
601 EvalCont::RecordCreate(mut progress) => match result {
602 Ok(value) => {
603 let field_name = progress.fields[progress.idx].0.clone();
604 if !progress.seen.insert(field_name.clone()) {
605 return EvalState::Apply(Err(RuntimeError::Error(format!(
606 "Record '{}' field '{}' provided more than once",
607 progress.type_name, field_name
608 ))));
609 }
610 progress.values.push((field_name, value));
611 progress.idx += 1;
612 self.resume_record_create(progress, conts)
613 }
614 Err(err) => EvalState::Apply(Err(err)),
615 },
616 EvalCont::RecordUpdateBase {
617 lowered,
618 type_name,
619 updates,
620 } => match result {
621 Ok(base_nv) => {
622 if !base_nv.is_record() {
623 return EvalState::Apply(Err(RuntimeError::Error(format!(
624 "{}.update: base must be a {} record",
625 type_name, type_name
626 ))));
627 }
628 let (base_type_id, base_fields) = self.arena.get_record(base_nv.arena_index());
629 let base_type_name = self.arena.get_type_name(base_type_id).to_string();
630 if base_type_name != type_name {
631 return EvalState::Apply(Err(RuntimeError::Error(format!(
632 "{}.update: base is a {} record, expected {}",
633 type_name, base_type_name, type_name
634 ))));
635 }
636 let base_field_names: Vec<String> =
637 self.arena.get_field_names(base_type_id).to_vec();
638 let base_fields_vec: Vec<NanValue> = base_fields.to_vec();
639 self.resume_record_update(
640 RecordUpdateProgress {
641 lowered,
642 type_name,
643 base_type_id,
644 base_fields: base_fields_vec,
645 base_field_names,
646 updates,
647 idx: 0,
648 update_vals: Vec::new(),
649 },
650 conts,
651 )
652 }
653 Err(err) => EvalState::Apply(Err(err)),
654 },
655 EvalCont::RecordUpdateField(mut progress) => match result {
656 Ok(value) => {
657 progress
658 .update_vals
659 .push((progress.updates[progress.idx].0.clone(), value));
660 progress.idx += 1;
661 self.resume_record_update(progress, conts)
662 }
663 Err(err) => EvalState::Apply(Err(err)),
664 },
665 EvalCont::TailCallArgs {
666 lowered,
667 target,
668 args,
669 idx,
670 mut values,
671 } => match result {
672 Ok(value) => {
673 values.push(value);
674 self.resume_tail_call(lowered, target, args, idx + 1, values, conts)
675 }
676 Err(err) => EvalState::Apply(Err(err)),
677 },
678 EvalCont::BodyBinding {
679 name,
680 next_idx,
681 lowered,
682 local_slots,
683 } => match result {
684 Ok(value) => {
685 if let Some(local_slots) = local_slots.as_ref()
686 && let Some(&slot) = local_slots.get(&name)
687 {
688 self.define_slot(slot, value);
689 } else {
690 self.define_nv(name, value);
691 }
692 EvalState::Body {
693 lowered,
694 idx: next_idx,
695 local_slots,
696 last: NanValue::UNIT,
697 }
698 }
699 Err(err) => EvalState::Apply(Err(err)),
700 },
701 EvalCont::BodyExpr {
702 next_idx,
703 lowered,
704 local_slots,
705 } => match result {
706 Ok(value) => EvalState::Body {
707 lowered,
708 idx: next_idx,
709 local_slots,
710 last: value,
711 },
712 Err(err) => EvalState::Apply(Err(err)),
713 },
714 EvalCont::MatchScope => {
715 self.pop_env();
716 EvalState::Apply(result)
717 }
718 EvalCont::FunctionReturn(frame) => self.finish_function_call(frame, result, conts),
719 }
720 }
721
722 fn attr_access_nv(&self, nv: NanValue, field: &str) -> Result<NanValue, RuntimeError> {
724 if nv.is_record() {
725 let (tid, fields) = self.arena.get_record(nv.arena_index());
726 let field_names = self.arena.get_field_names(tid);
727 if let Some(pos) = field_names.iter().position(|n| n == field) {
728 return Ok(fields[pos]);
729 }
730 return Err(RuntimeError::Error(format!("Unknown field '{}'", field)));
731 }
732 if nv.is_namespace() {
733 let (name, members) = self.arena.get_namespace(nv.arena_index());
734 if let Some((_, member_nv)) = members.iter().find(|(k, _)| k.as_ref() == field) {
735 return Ok(*member_nv);
736 }
737 return Err(RuntimeError::Error(format!(
738 "Unknown member '{}.{}'",
739 name, field
740 )));
741 }
742 Err(RuntimeError::Error(format!(
743 "Field access '{}' is not supported on this value",
744 field
745 )))
746 }
747
748 fn dispatch_match(
749 &mut self,
750 lowered: Rc<LoweredFunctionBody>,
751 subject: NanValue,
752 arms: SharedMatchArms,
753 line: usize,
754 conts: &mut Vec<EvalCont>,
755 ) -> EvalState {
756 let arm_count = arms.len();
757 for (arm_idx, arm) in arms.iter().enumerate() {
758 if let Some(bindings) = self.match_pattern_nv(&arm.pattern, subject) {
759 self.note_verify_match_arm(line, arm_count, arm_idx);
760 if bindings.is_empty() {
761 return EvalState::Expr {
762 lowered,
763 expr: arm.body,
764 };
765 }
766
767 if let Some(local_slots) = self.active_local_slots.clone() {
768 let all_slotted = bindings
769 .iter()
770 .all(|(name, _)| local_slots.contains_key(name));
771 if all_slotted {
772 for (name, nv) in bindings {
773 if let Some(&slot) = local_slots.get(&name) {
774 self.define_slot(slot, nv);
775 }
776 }
777 return EvalState::Expr {
778 lowered,
779 expr: arm.body,
780 };
781 }
782 }
783
784 let scope: HashMap<String, NanValue> = bindings.into_iter().collect();
785 self.push_env(EnvFrame::Owned(scope));
786 conts.push(EvalCont::MatchScope);
787 return EvalState::Expr {
788 lowered,
789 expr: arm.body,
790 };
791 }
792 }
793
794 EvalState::Apply(Err(RuntimeError::Error(format!(
795 "No match found for value {}",
796 subject.repr(&self.arena)
797 ))))
798 }
799
800 fn dispatch_call(
801 &mut self,
802 fn_val: NanValue,
803 args: Vec<NanValue>,
804 conts: &mut Vec<EvalCont>,
805 ) -> EvalState {
806 match self.start_call_nv(fn_val, args) {
807 Ok(CallDispatch::Immediate(result)) => EvalState::Apply(result),
808 Ok(CallDispatch::EnterFunction { frame, state }) => {
809 conts.push(EvalCont::FunctionReturn(*frame));
810 state
811 }
812 Err(err) => EvalState::Apply(Err(err)),
813 }
814 }
815
816 fn start_call_nv(
817 &mut self,
818 fn_val: NanValue,
819 args: Vec<NanValue>,
820 ) -> Result<CallDispatch, RuntimeError> {
821 if fn_val.is_builtin() {
822 let name = self.arena.get_builtin(fn_val.arena_index()).to_string();
823 self.ensure_effects_allowed(&name, Self::builtin_effects(&name).iter().copied())?;
824 let result = self.call_builtin_nv(&name, &args);
825 return Ok(CallDispatch::Immediate(result));
826 }
827 if fn_val.is_fn() {
828 let function = Rc::clone(self.arena.get_fn_rc(fn_val.arena_index()));
829 if args.len() != function.params.len() {
830 return Err(RuntimeError::Error(format!(
831 "Function '{}' expects {} arguments, got {}",
832 function.name,
833 function.params.len(),
834 args.len()
835 )));
836 }
837 self.ensure_effects_allowed(
838 function.name.as_str(),
839 function.effects.iter().map(String::as_str),
840 )?;
841
842 let memo_key = if function.memo_eligible {
843 let key = hash_memo_args_nv(&args, &self.arena);
844 let fn_name: &str = function.name.as_ref();
845 if let Some(cache) = self.memo_cache.get_mut(fn_name)
846 && let Some(cached_val) = cache.get_nv_as_value(key, &args, &self.arena)
847 {
848 let cached_nv = NanValue::from_value(&cached_val, &mut self.arena);
849 return Ok(CallDispatch::Immediate(Ok(cached_nv)));
850 }
851 Some((key, args.clone()))
852 } else {
853 None
854 };
855
856 self.call_stack.push(CallFrame {
857 name: Rc::clone(&function.name),
858 effects: Rc::clone(&function.effects),
859 });
860
861 let prev_local_slots = self.active_local_slots.take();
862 let saved_base = self.env_base;
863 self.env_base = self.env.len();
864 let prev_global = if let Some(home) = function.home_globals.as_ref() {
865 let global = self
866 .env
867 .first_mut()
868 .ok_or_else(|| RuntimeError::Error("No global scope".to_string()))?;
869 Some(std::mem::replace(global, EnvFrame::Shared(Rc::clone(home))))
870 } else {
871 None
872 };
873
874 let active = ActiveFunction { function };
875 let frame = FunctionFrame {
876 active,
877 prev_local_slots,
878 saved_base,
879 prev_global,
880 memo_key,
881 };
882 let state = self.enter_function_body_nv(&frame.active, args);
883 return Ok(CallDispatch::EnterFunction {
884 frame: Box::new(frame),
885 state,
886 });
887 }
888 Err(RuntimeError::Error(format!(
889 "Cannot call value: {}",
890 fn_val.repr(&self.arena)
891 )))
892 }
893
894 fn enter_function_body_nv(
895 &mut self,
896 active: &ActiveFunction,
897 args: Vec<NanValue>,
898 ) -> EvalState {
899 if let Some(resolution) = &active.function.resolution {
900 let local_slots = Rc::clone(&resolution.local_slots);
901 let mut slots = Self::empty_slots_nv(resolution.local_count);
902 for ((param_name, _), arg_nv) in active.function.params.iter().zip(args.into_iter()) {
903 if let Some(&slot) = resolution.local_slots.get(param_name) {
904 slots[slot as usize] = arg_nv;
905 }
906 }
907 self.active_local_slots = Some(Rc::clone(&local_slots));
908 self.push_env(EnvFrame::Slots(slots));
909 EvalState::Body {
910 lowered: Rc::clone(&active.function.lowered_body),
911 idx: 0,
912 local_slots: Some(local_slots),
913 last: NanValue::UNIT,
914 }
915 } else {
916 let mut params_scope: HashMap<String, NanValue> = HashMap::new();
917 for ((param_name, _), arg_nv) in active.function.params.iter().zip(args.into_iter()) {
918 params_scope.insert(param_name.clone(), arg_nv);
919 }
920 self.push_env(EnvFrame::Owned(params_scope));
921 EvalState::Body {
922 lowered: Rc::clone(&active.function.lowered_body),
923 idx: 0,
924 local_slots: None,
925 last: NanValue::UNIT,
926 }
927 }
928 }
929
930 fn finish_function_call(
931 &mut self,
932 mut frame: FunctionFrame,
933 result: Result<NanValue, RuntimeError>,
934 conts: &mut Vec<EvalCont>,
935 ) -> EvalState {
936 self.pop_env();
937
938 match result {
939 Err(RuntimeError::TailCall(boxed)) => {
940 let (target, nv_args) = *boxed;
941 let next_active = if target == frame.active.function.name.as_str() {
942 frame.active.clone()
943 } else {
944 let next_function = match self.lookup_nv(&target) {
945 Ok(nv) => {
946 if !nv.is_fn() {
947 return EvalState::Apply(Err(RuntimeError::Error(format!(
948 "TCO target '{}' is not a function: {}",
949 target,
950 nv.repr(&self.arena)
951 ))));
952 }
953 Rc::clone(self.arena.get_fn_rc(nv.arena_index()))
954 }
955 Err(err) => return EvalState::Apply(Err(err)),
956 };
957
958 if let Some(call_frame) = self.call_stack.last_mut() {
959 call_frame.name = Rc::clone(&next_function.name);
960 call_frame.effects = Rc::clone(&next_function.effects);
961 }
962
963 ActiveFunction {
964 function: next_function,
965 }
966 };
967
968 frame.active = next_active;
969 let state = self.enter_function_body_nv(&frame.active, nv_args);
970 conts.push(EvalCont::FunctionReturn(frame));
971 state
972 }
973 other => {
974 self.active_local_slots = frame.prev_local_slots;
975 if let Some(prev) = frame.prev_global
976 && let Some(global) = self.env.first_mut()
977 {
978 *global = prev;
979 }
980 self.env_base = frame.saved_base;
981 self.call_stack.pop();
982
983 let final_result = match other {
984 Ok(value) => Ok(value),
985 Err(RuntimeError::ErrProp(err_nv)) => {
986 let idx = self.arena.push_boxed(err_nv);
987 Ok(NanValue::new_err(idx))
988 }
989 Err(err) => Err(err),
990 };
991
992 if let (Some((key, memo_args)), Ok(value)) = (&frame.memo_key, &final_result) {
993 let fn_name: &str = frame.active.function.name.as_ref();
994 let cache = if let Some(c) = self.memo_cache.get_mut(fn_name) {
995 c
996 } else {
997 self.memo_cache
998 .entry(frame.active.function.name.as_ref().clone())
999 .or_default()
1000 };
1001 cache.insert_nv(
1002 *key,
1003 memo_args.clone(),
1004 *value,
1005 &self.arena,
1006 MEMO_CACHE_CAP_PER_FN,
1007 );
1008 }
1009
1010 EvalState::Apply(final_result)
1011 }
1012 }
1013 }
1014
1015 fn resume_interpolated_str(
1016 &mut self,
1017 lowered: Rc<LoweredFunctionBody>,
1018 parts: SharedStrParts,
1019 mut idx: usize,
1020 mut result: String,
1021 conts: &mut Vec<EvalCont>,
1022 ) -> EvalState {
1023 while idx < parts.len() {
1024 match parts[idx].clone() {
1025 LoweredStrPart::Literal(text) => {
1026 result.push_str(&text);
1027 idx += 1;
1028 }
1029 LoweredStrPart::Parsed(expr) => {
1030 conts.push(EvalCont::InterpolatedStr {
1031 lowered: Rc::clone(&lowered),
1032 parts: Rc::clone(&parts),
1033 idx: idx + 1,
1034 result,
1035 });
1036 return EvalState::Expr { lowered, expr };
1037 }
1038 }
1039 }
1040 let idx = self.arena.push_string(&result);
1041 EvalState::Apply(Ok(NanValue::new_string(idx)))
1042 }
1043
1044 fn resume_list(
1045 &mut self,
1046 lowered: Rc<LoweredFunctionBody>,
1047 items: SharedExprs,
1048 idx: usize,
1049 values: Vec<NanValue>,
1050 conts: &mut Vec<EvalCont>,
1051 ) -> EvalState {
1052 if idx >= items.len() {
1053 let list_idx = self.arena.push_list(values);
1054 return EvalState::Apply(Ok(NanValue::new_list(list_idx)));
1055 }
1056
1057 conts.push(EvalCont::List {
1058 lowered: Rc::clone(&lowered),
1059 items: Rc::clone(&items),
1060 idx: idx + 1,
1061 values,
1062 });
1063 EvalState::Expr {
1064 lowered,
1065 expr: items[idx],
1066 }
1067 }
1068
1069 fn resume_tuple(
1070 &mut self,
1071 lowered: Rc<LoweredFunctionBody>,
1072 items: SharedExprs,
1073 idx: usize,
1074 values: Vec<NanValue>,
1075 conts: &mut Vec<EvalCont>,
1076 ) -> EvalState {
1077 if idx >= items.len() {
1078 let tuple_idx = self.arena.push_tuple(values);
1079 return EvalState::Apply(Ok(NanValue::new_tuple(tuple_idx)));
1080 }
1081
1082 conts.push(EvalCont::Tuple {
1083 lowered: Rc::clone(&lowered),
1084 items: Rc::clone(&items),
1085 idx: idx + 1,
1086 values,
1087 });
1088 EvalState::Expr {
1089 lowered,
1090 expr: items[idx],
1091 }
1092 }
1093
1094 fn resume_map(
1095 &mut self,
1096 lowered: Rc<LoweredFunctionBody>,
1097 entries: SharedMapEntries,
1098 idx: usize,
1099 map: crate::nan_value::PersistentMap,
1100 conts: &mut Vec<EvalCont>,
1101 ) -> EvalState {
1102 if idx >= entries.len() {
1103 let map_idx = self.arena.push_map(map);
1104 return EvalState::Apply(Ok(NanValue::new_map(map_idx)));
1105 }
1106
1107 conts.push(EvalCont::MapKey {
1108 lowered: Rc::clone(&lowered),
1109 entries: Rc::clone(&entries),
1110 idx,
1111 map,
1112 });
1113 EvalState::Expr {
1114 lowered,
1115 expr: entries[idx].0,
1116 }
1117 }
1118
1119 fn resume_record_create(
1120 &mut self,
1121 progress: RecordCreateProgress,
1122 conts: &mut Vec<EvalCont>,
1123 ) -> EvalState {
1124 if progress.idx >= progress.fields.len() {
1125 return EvalState::Apply(
1126 self.build_record_create_nv(&progress.type_name, progress.values),
1127 );
1128 }
1129
1130 let lowered = Rc::clone(&progress.lowered);
1131 let expr = progress.fields[progress.idx].1;
1132 conts.push(EvalCont::RecordCreate(progress));
1133 EvalState::Expr { lowered, expr }
1134 }
1135
1136 fn resume_record_update(
1137 &mut self,
1138 progress: RecordUpdateProgress,
1139 conts: &mut Vec<EvalCont>,
1140 ) -> EvalState {
1141 if progress.idx >= progress.updates.len() {
1142 return EvalState::Apply(self.build_record_update_nv(
1143 &progress.type_name,
1144 progress.base_type_id,
1145 progress.base_fields,
1146 progress.base_field_names,
1147 progress.update_vals,
1148 ));
1149 }
1150
1151 let next_expr = progress.updates[progress.idx].1;
1152 let lowered = Rc::clone(&progress.lowered);
1153 conts.push(EvalCont::RecordUpdateField(progress));
1154 EvalState::Expr {
1155 lowered,
1156 expr: next_expr,
1157 }
1158 }
1159
1160 fn resume_tail_call(
1161 &mut self,
1162 lowered: Rc<LoweredFunctionBody>,
1163 target: String,
1164 args: SharedExprs,
1165 idx: usize,
1166 values: Vec<NanValue>,
1167 conts: &mut Vec<EvalCont>,
1168 ) -> EvalState {
1169 if idx >= args.len() {
1170 return EvalState::Apply(Err(RuntimeError::TailCall(Box::new((target, values)))));
1171 }
1172
1173 conts.push(EvalCont::TailCallArgs {
1174 lowered: Rc::clone(&lowered),
1175 target,
1176 args: Rc::clone(&args),
1177 idx,
1178 values,
1179 });
1180 EvalState::Expr {
1181 lowered,
1182 expr: args[idx],
1183 }
1184 }
1185
1186 fn build_record_create_nv(
1187 &mut self,
1188 type_name: &str,
1189 field_vals: Vec<(String, NanValue)>,
1190 ) -> Result<NanValue, RuntimeError> {
1191 if let Some(schema) = self.record_schemas.get(type_name) {
1192 let schema_clone = schema.clone();
1195 let type_id = self.arena.find_type_id(type_name).unwrap_or_else(|| {
1196 self.arena
1197 .register_record_type(type_name, schema_clone.clone())
1198 });
1199 let mut by_name = HashMap::with_capacity(field_vals.len());
1200 for (name, value) in field_vals {
1201 if by_name.insert(name.clone(), value).is_some() {
1202 return Err(RuntimeError::Error(format!(
1203 "Record '{}' field '{}' provided more than once",
1204 type_name, name
1205 )));
1206 }
1207 }
1208
1209 for provided in by_name.keys() {
1210 if !schema_clone.iter().any(|field| field == provided) {
1211 return Err(RuntimeError::Error(format!(
1212 "Record '{}' has no field '{}'",
1213 type_name, provided
1214 )));
1215 }
1216 }
1217
1218 let mut ordered = Vec::with_capacity(schema_clone.len());
1219 for required in &schema_clone {
1220 let value = by_name.remove(required).ok_or_else(|| {
1221 RuntimeError::Error(format!(
1222 "Record '{}' missing required field '{}'",
1223 type_name, required
1224 ))
1225 })?;
1226 ordered.push(value);
1227 }
1228
1229 let rec_idx = self.arena.push_record(type_id, ordered);
1230 return Ok(NanValue::new_record(rec_idx));
1231 }
1232
1233 let type_id = self.arena.find_type_id(type_name).unwrap_or_else(|| {
1235 let field_names: Vec<String> = field_vals.iter().map(|(n, _)| n.clone()).collect();
1236 self.arena.register_record_type(type_name, field_names)
1237 });
1238 let nv_fields: Vec<NanValue> = field_vals.iter().map(|(_, v)| *v).collect();
1239 let rec_idx = self.arena.push_record(type_id, nv_fields);
1240 Ok(NanValue::new_record(rec_idx))
1241 }
1242
1243 fn build_record_update_nv(
1244 &mut self,
1245 type_name: &str,
1246 base_type_id: u32,
1247 mut base_fields: Vec<NanValue>,
1248 base_field_names: Vec<String>,
1249 update_vals: Vec<(String, NanValue)>,
1250 ) -> Result<NanValue, RuntimeError> {
1251 if let Some(schema) = self.record_schemas.get(type_name) {
1252 for (field_name, _) in &update_vals {
1253 if !schema.iter().any(|field| field == field_name) {
1254 return Err(RuntimeError::Error(format!(
1255 "Record '{}' has no field '{}'",
1256 type_name, field_name
1257 )));
1258 }
1259 }
1260 }
1261
1262 for (update_name, update_val) in update_vals {
1263 if let Some(pos) = base_field_names.iter().position(|n| n == &update_name) {
1264 base_fields[pos] = update_val;
1265 } else {
1266 return Err(RuntimeError::Error(format!(
1267 "Record '{}' has no field '{}'",
1268 type_name, update_name
1269 )));
1270 }
1271 }
1272
1273 let rec_idx = self.arena.push_record(base_type_id, base_fields);
1274 Ok(NanValue::new_record(rec_idx))
1275 }
1276
1277 fn is_hashable_map_key_nv(value: NanValue) -> bool {
1278 value.is_int() || value.is_float() || value.is_string() || value.is_bool()
1279 }
1280
1281 pub(super) fn eval_literal_nv(&mut self, lit: &Literal) -> NanValue {
1282 match lit {
1283 Literal::Int(i) => NanValue::new_int(*i, &mut self.arena),
1284 Literal::Float(f) => NanValue::new_float(*f),
1285 Literal::Str(s) => NanValue::new_string(self.arena.push_string(s)),
1286 Literal::Bool(b) => NanValue::new_bool(*b),
1287 Literal::Unit => NanValue::UNIT,
1288 }
1289 }
1290
1291 #[allow(dead_code)]
1292 pub(super) fn eval_literal(&self, lit: &Literal) -> Value {
1293 match lit {
1294 Literal::Int(i) => Value::Int(*i),
1295 Literal::Float(f) => Value::Float(*f),
1296 Literal::Str(s) => Value::Str(s.clone()),
1297 Literal::Bool(b) => Value::Bool(*b),
1298 Literal::Unit => Value::Unit,
1299 }
1300 }
1301
1302 pub(super) fn call_value(
1303 &mut self,
1304 fn_val: Value,
1305 args: Vec<Value>,
1306 ) -> Result<Value, RuntimeError> {
1307 let fn_nv = NanValue::from_value(&fn_val, &mut self.arena);
1308 let nv_args: Vec<NanValue> = args
1309 .iter()
1310 .map(|v| NanValue::from_value(v, &mut self.arena))
1311 .collect();
1312 match self.start_call_nv(fn_nv, nv_args)? {
1313 CallDispatch::Immediate(result) => result.map(|nv| nv.to_value(&self.arena)),
1314 CallDispatch::EnterFunction { frame, state } => {
1315 let nv = self.eval_loop(state, vec![EvalCont::FunctionReturn(*frame)])?;
1316 Ok(nv.to_value(&self.arena))
1317 }
1318 }
1319 }
1320}
1321
1322fn hash_memo_args_nv(args: &[NanValue], arena: &Arena) -> u64 {
1324 use std::hash::{Hash, Hasher};
1325 let mut hasher = std::collections::hash_map::DefaultHasher::new();
1326 args.len().hash(&mut hasher);
1327 for arg in args {
1328 arg.hash_in(&mut hasher, arena);
1329 }
1330 hasher.finish()
1331}