Skip to main content

aver/interpreter/
eval.rs

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    /// The env_base of the caller — restored on return so the caller's
123    /// frames become visible to lookup_ref again.
124    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    /// Public eval_expr — returns Value for external callers.
165    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    /// Internal eval_expr — returns NanValue natively.
171    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    /// Attribute access on a NanValue.
723    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            // When we have a schema, register the type using schema field order
1193            // (which is the declaration order from the record definition).
1194            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        // No schema — just use field order as-is
1234        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
1322/// Hash NanValue args for memo cache — uses arena for content-based hashing.
1323fn 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}