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