Skip to main content

aver/interpreter/
eval.rs

1use super::*;
2
3impl Interpreter {
4    pub fn eval_expr(&mut self, expr: &Expr) -> Result<Value, RuntimeError> {
5        match expr {
6            Expr::Literal(lit) => Ok(self.eval_literal(lit)),
7            Expr::Resolved(slot) => self.lookup_slot(*slot),
8            Expr::Ident(name) => self.lookup(name),
9            Expr::Attr(obj, field) => {
10                // Fast path: `Ident.field` avoids cloning the entire namespace/record
11                if let Expr::Ident(name) = obj.as_ref() {
12                    let rc = self.lookup_rc(name)?;
13                    return match rc.as_ref() {
14                        Value::Namespace { name, members } => {
15                            members.get(field.as_str()).cloned().ok_or_else(|| {
16                                RuntimeError::Error(format!("Unknown member '{}.{}'", name, field))
17                            })
18                        }
19                        Value::Record { fields, .. } => fields
20                            .iter()
21                            .find(|(k, _)| k == field)
22                            .map(|(_, v)| Ok(v.clone()))
23                            .unwrap_or_else(|| {
24                                Err(RuntimeError::Error(format!("Unknown field '{}'", field)))
25                            }),
26                        _ => Err(RuntimeError::Error(format!(
27                            "Field access '{}' is not supported on this value",
28                            field
29                        ))),
30                    };
31                }
32                // General path: computed object expression
33                let obj_val = self.eval_expr(obj)?;
34                match obj_val {
35                    Value::Record { fields, .. } => fields
36                        .into_iter()
37                        .find(|(k, _)| k == field)
38                        .map(|(_, v)| Ok(v))
39                        .unwrap_or_else(|| {
40                            Err(RuntimeError::Error(format!("Unknown field '{}'", field)))
41                        }),
42                    Value::Namespace { name, members } => {
43                        members.get(field).cloned().ok_or_else(|| {
44                            RuntimeError::Error(format!("Unknown member '{}.{}'", name, field))
45                        })
46                    }
47                    _ => Err(RuntimeError::Error(format!(
48                        "Field access '{}' is not supported on this value",
49                        field
50                    ))),
51                }
52            }
53            Expr::FnCall(fn_expr, args) => {
54                let fn_val = self.eval_expr(fn_expr)?;
55                let mut arg_vals = Vec::new();
56                for a in args {
57                    arg_vals.push(self.eval_expr(a)?);
58                }
59                self.call_value(fn_val, arg_vals)
60            }
61            Expr::BinOp(op, left, right) => {
62                let lv = self.eval_expr(left)?;
63                let rv = self.eval_expr(right)?;
64                self.eval_binop(op, lv, rv)
65            }
66            Expr::Match { subject, arms, .. } => {
67                let sv = self.eval_expr(subject)?;
68                self.eval_match(sv, arms)
69            }
70            Expr::Pipe(left, right) => {
71                let left_val = self.eval_expr(left)?;
72                let fn_val = self.eval_expr(right)?;
73                self.call_value(fn_val, vec![left_val])
74            }
75            Expr::Constructor(name, arg) => {
76                let arg_val = match arg {
77                    Some(a) => self.eval_expr(a)?,
78                    None => Value::Unit,
79                };
80                match name.as_str() {
81                    "Ok" => Ok(Value::Ok(Box::new(arg_val))),
82                    "Err" => Ok(Value::Err(Box::new(arg_val))),
83                    "Some" => Ok(Value::Some(Box::new(arg_val))),
84                    "None" => Ok(Value::None),
85                    _ => Err(RuntimeError::Error(format!(
86                        "Unknown constructor: {}",
87                        name
88                    ))),
89                }
90            }
91            Expr::ErrorProp(inner) => {
92                let val = self.eval_expr(inner)?;
93                match val {
94                    Value::Ok(v) => Ok(*v),
95                    Value::Err(e) => Err(RuntimeError::ErrProp(e)),
96                    _ => Err(RuntimeError::Error(
97                        "Operator '?' can only be applied to Result".to_string(),
98                    )),
99                }
100            }
101            Expr::InterpolatedStr(parts) => {
102                let mut result = String::new();
103                for part in parts {
104                    match part {
105                        StrPart::Literal(s) => result.push_str(s),
106                        StrPart::Parsed(expr) => {
107                            let val = self.eval_expr(expr)?;
108                            result.push_str(&aver_repr(&val));
109                        }
110                    }
111                }
112                Ok(Value::Str(result))
113            }
114            Expr::List(elements) => {
115                let mut values = Vec::new();
116                for elem in elements {
117                    values.push(self.eval_expr(elem)?);
118                }
119                Ok(list_from_vec(values))
120            }
121            Expr::Tuple(items) => {
122                let mut values = Vec::with_capacity(items.len());
123                for item in items {
124                    values.push(self.eval_expr(item)?);
125                }
126                Ok(Value::Tuple(values))
127            }
128            Expr::MapLiteral(entries) => self.eval_map_literal(entries),
129            Expr::RecordCreate { type_name, fields } => self.eval_record_create(type_name, fields),
130            Expr::RecordUpdate {
131                type_name,
132                base,
133                updates,
134            } => self.eval_record_update(type_name, base, updates),
135            Expr::TailCall(boxed) => self.eval_tail_call(boxed),
136        }
137    }
138
139    /// Evaluate a TailCall expression — separate fn to keep `eval_expr` frame small.
140    #[inline(never)]
141    pub(super) fn eval_tail_call(
142        &mut self,
143        boxed: &(String, Vec<Expr>),
144    ) -> Result<Value, RuntimeError> {
145        let (target, args) = boxed;
146        let mut vals = Vec::with_capacity(args.len());
147        for a in args {
148            vals.push(self.eval_expr(a)?);
149        }
150        Err(RuntimeError::TailCall(Box::new((target.clone(), vals))))
151    }
152
153    /// Evaluate a record constructor.
154    /// Kept out of `eval_expr` to avoid inflating the recursive eval stack frame.
155    #[inline(never)]
156    fn eval_record_create(
157        &mut self,
158        type_name: &str,
159        fields: &[(String, Expr)],
160    ) -> Result<Value, RuntimeError> {
161        let mut field_vals = Vec::with_capacity(fields.len());
162        let mut seen = HashSet::new();
163        for (name, expr) in fields {
164            if !seen.insert(name.clone()) {
165                return Err(RuntimeError::Error(format!(
166                    "Record '{}' field '{}' provided more than once",
167                    type_name, name
168                )));
169            }
170            let val = self.eval_expr(expr)?;
171            field_vals.push((name.clone(), val));
172        }
173
174        if let Some(schema) = self.record_schemas.get(type_name) {
175            let mut by_name = HashMap::with_capacity(field_vals.len());
176            for (name, val) in field_vals {
177                by_name.insert(name, val);
178            }
179
180            for provided in by_name.keys() {
181                if !schema.iter().any(|f| f == provided) {
182                    return Err(RuntimeError::Error(format!(
183                        "Record '{}' has no field '{}'",
184                        type_name, provided
185                    )));
186                }
187            }
188
189            let mut ordered = Vec::with_capacity(schema.len());
190            for required in schema {
191                let val = by_name.remove(required).ok_or_else(|| {
192                    RuntimeError::Error(format!(
193                        "Record '{}' missing required field '{}'",
194                        type_name, required
195                    ))
196                })?;
197                ordered.push((required.clone(), val));
198            }
199
200            return Ok(Value::Record {
201                type_name: type_name.to_string(),
202                fields: ordered,
203            });
204        }
205
206        Ok(Value::Record {
207            type_name: type_name.to_string(),
208            fields: field_vals,
209        })
210    }
211
212    /// Evaluate a record update: `Type.update(base, field = newVal, ...)`.
213    #[inline(never)]
214    fn eval_record_update(
215        &mut self,
216        type_name: &str,
217        base: &Expr,
218        updates: &[(String, Expr)],
219    ) -> Result<Value, RuntimeError> {
220        let base_val = self.eval_expr(base)?;
221        let (base_type, base_fields) = match base_val {
222            Value::Record {
223                type_name: t,
224                fields,
225            } => (t, fields),
226            _ => {
227                return Err(RuntimeError::Error(format!(
228                    "{}.update: base must be a {} record",
229                    type_name, type_name
230                )));
231            }
232        };
233        if base_type != type_name {
234            return Err(RuntimeError::Error(format!(
235                "{}.update: base is a {} record, expected {}",
236                type_name, base_type, type_name
237            )));
238        }
239
240        let mut update_vals = Vec::with_capacity(updates.len());
241        for (name, expr) in updates {
242            let val = self.eval_expr(expr)?;
243            update_vals.push((name.clone(), val));
244        }
245
246        // Validate update field names against schema
247        if let Some(schema) = self.record_schemas.get(type_name) {
248            for (field_name, _) in &update_vals {
249                if !schema.iter().any(|f| f == field_name) {
250                    return Err(RuntimeError::Error(format!(
251                        "Record '{}' has no field '{}'",
252                        type_name, field_name
253                    )));
254                }
255            }
256        }
257
258        // Clone base fields, apply overrides
259        let mut fields: Vec<(String, Value)> = base_fields;
260        for (upd_name, upd_val) in update_vals {
261            if let Some(field) = fields.iter_mut().find(|(k, _)| k == &upd_name) {
262                field.1 = upd_val;
263            } else {
264                return Err(RuntimeError::Error(format!(
265                    "Record '{}' has no field '{}'",
266                    type_name, upd_name
267                )));
268            }
269        }
270
271        Ok(Value::Record {
272            type_name: type_name.to_string(),
273            fields,
274        })
275    }
276
277    #[inline(never)]
278    fn eval_map_literal(&mut self, entries: &[(Expr, Expr)]) -> Result<Value, RuntimeError> {
279        let mut map = HashMap::with_capacity(entries.len());
280        for (key_expr, value_expr) in entries {
281            let key = self.eval_expr(key_expr)?;
282            if !Self::is_hashable_map_key(&key) {
283                return Err(RuntimeError::Error(
284                    "Map literal key must be Int, Float, String, or Bool".to_string(),
285                ));
286            }
287            let value = self.eval_expr(value_expr)?;
288            map.insert(key, value);
289        }
290        Ok(Value::Map(map))
291    }
292
293    fn is_hashable_map_key(value: &Value) -> bool {
294        matches!(
295            value,
296            Value::Int(_) | Value::Float(_) | Value::Str(_) | Value::Bool(_)
297        )
298    }
299
300    pub(super) fn eval_literal(&self, lit: &Literal) -> Value {
301        match lit {
302            Literal::Int(i) => Value::Int(*i),
303            Literal::Float(f) => Value::Float(*f),
304            Literal::Str(s) => Value::Str(s.clone()),
305            Literal::Bool(b) => Value::Bool(*b),
306        }
307    }
308
309    /// Call a function value (owned). Delegates to `call_fn_ref` for `Value::Fn`.
310    pub(super) fn call_value(
311        &mut self,
312        fn_val: Value,
313        args: Vec<Value>,
314    ) -> Result<Value, RuntimeError> {
315        match &fn_val {
316            Value::Builtin(name) => {
317                self.ensure_effects_allowed(name, Self::builtin_effects(name).iter().copied())?;
318                self.call_builtin(name, &args)
319            }
320            Value::Fn { .. } => self.call_fn_ref(&fn_val, args),
321            _ => Err(RuntimeError::Error(format!(
322                "Cannot call value: {:?}",
323                fn_val
324            ))),
325        }
326    }
327
328    /// Call a `Value::Fn` by reference — avoids cloning name/params/effects.
329    /// Used in map/filter/fold hot loops.
330    /// When the body returns `RuntimeError::TailCall`, delegates to
331    /// `tco_trampoline` to iterate without growing the call stack.
332    pub(super) fn call_fn_ref(
333        &mut self,
334        fn_val: &Value,
335        args: Vec<Value>,
336    ) -> Result<Value, RuntimeError> {
337        let Value::Fn {
338            name,
339            params,
340            effects,
341            body,
342            resolution,
343            memo_eligible,
344            home_globals,
345            ..
346        } = fn_val
347        else {
348            return Err(RuntimeError::Error(format!(
349                "Cannot call value: {:?}",
350                fn_val
351            )));
352        };
353
354        if args.len() != params.len() {
355            return Err(RuntimeError::Error(format!(
356                "Function '{}' expects {} arguments, got {}",
357                name,
358                params.len(),
359                args.len()
360            )));
361        }
362        self.ensure_effects_allowed(name, effects.iter().map(String::as_str))?;
363
364        // Auto-memoization: check cache before executing
365        let is_memo = *memo_eligible;
366        let memo_key = if is_memo {
367            let key = hash_memo_args(&args);
368            if let Some(cached) = self
369                .memo_cache
370                .entry(name.clone())
371                .or_default()
372                .get(key, &args)
373            {
374                return Ok(cached);
375            }
376            Some((key, args.clone()))
377        } else {
378            None
379        };
380
381        self.call_stack.push(CallFrame {
382            name: name.clone(),
383            effects: effects.clone(),
384        });
385
386        let prev_local_slots = self.active_local_slots.take();
387        let saved_frames: Vec<EnvFrame> = self.env.drain(1..).collect();
388        let prev_global = if let Some(home) = home_globals {
389            let global = self
390                .env
391                .first_mut()
392                .ok_or_else(|| RuntimeError::Error("No global scope".to_string()))?;
393            Some(std::mem::replace(global, EnvFrame::Shared(Rc::clone(home))))
394        } else {
395            None
396        };
397
398        let result = if let Some(res) = resolution {
399            // Resolved path: push Slots frame only.
400            // lookup_rc skips Slots frames, so globals at env[0] are visible.
401            let mut slots = vec![Rc::new(Value::Unit); res.local_count as usize];
402            for ((param_name, _), arg_val) in params.iter().zip(args.into_iter()) {
403                if let Some(&slot) = res.local_slots.get(param_name) {
404                    slots[slot as usize] = Rc::new(arg_val);
405                }
406            }
407            self.active_local_slots = Some(res.local_slots.clone());
408            self.push_env(EnvFrame::Slots(slots));
409            let r = match &**body {
410                FnBody::Expr(e) => self.eval_expr(e),
411                FnBody::Block(stmts) => self.exec_body_resolved(stmts, &res.local_slots),
412            };
413            self.pop_env();
414            r
415        } else {
416            // Unresolved path (REPL): env is already isolated to [globals].
417            let mut params_scope = HashMap::new();
418            for ((param_name, _), arg_val) in params.iter().zip(args.into_iter()) {
419                params_scope.insert(param_name.clone(), Rc::new(arg_val));
420            }
421            self.push_env(EnvFrame::Owned(params_scope));
422            let r = match &**body {
423                FnBody::Expr(e) => self.eval_expr(e),
424                FnBody::Block(stmts) => self.exec_body(stmts),
425            };
426            self.pop_env();
427            r
428        };
429
430        // If TailCall, enter trampoline — only then do we clone fn state.
431        let result = match result {
432            Err(RuntimeError::TailCall(boxed)) => {
433                let (target, new_args) = *boxed;
434                self.tco_trampoline(name, params, body, resolution, target, new_args)
435            }
436            other => other,
437        };
438
439        self.active_local_slots = prev_local_slots;
440        if let Some(prev) = prev_global {
441            if let Some(global) = self.env.first_mut() {
442                *global = prev;
443            }
444        }
445        self.env.truncate(1);
446        self.env.extend(saved_frames);
447
448        self.call_stack.pop();
449        let final_result = match result {
450            Ok(v) => Ok(v),
451            Err(RuntimeError::ErrProp(e)) => Ok(Value::Err(e)),
452            Err(e) => Err(e),
453        };
454
455        // Auto-memoization: store result in cache
456        if let (Some((key, memo_args)), Ok(val)) = (memo_key, &final_result) {
457            let fn_name = name.clone();
458            let cache = self.memo_cache.entry(fn_name).or_default();
459            cache.insert(key, memo_args, val.clone(), MEMO_CACHE_CAP_PER_FN);
460        }
461
462        final_result
463    }
464
465    /// Trampoline loop for tail-call optimization.
466    /// Called only when `call_fn_ref` encounters a `TailCall` — keeps the call
467    /// stack flat by re-binding args in a loop instead of recursing.
468    #[inline(never)]
469    pub(super) fn tco_trampoline(
470        &mut self,
471        orig_name: &str,
472        orig_params: &[(String, String)],
473        orig_body: &Rc<FnBody>,
474        orig_resolution: &Option<FnResolution>,
475        first_target: String,
476        first_args: Vec<Value>,
477    ) -> Result<Value, RuntimeError> {
478        let mut cur_name = orig_name.to_string();
479        let mut cur_params: Vec<(String, String)> = orig_params.to_vec();
480        let mut cur_body = Rc::clone(orig_body);
481        let mut cur_resolution = orig_resolution.clone();
482
483        // Handle the initial TailCall
484        let mut target = first_target;
485        let mut new_args = first_args;
486
487        loop {
488            // Switch to target if different from current
489            if target != cur_name {
490                let target_fn = self.lookup(&target)?;
491                match target_fn {
492                    Value::Fn {
493                        name: tn,
494                        params: tp,
495                        effects: te,
496                        body: tb,
497                        resolution: tr,
498                        ..
499                    } => {
500                        cur_name = tn;
501                        cur_params = tp;
502                        cur_body = tb;
503                        cur_resolution = tr;
504                        if let Some(frame) = self.call_stack.last_mut() {
505                            frame.name = cur_name.clone();
506                            frame.effects = te;
507                        }
508                    }
509                    _ => {
510                        return Err(RuntimeError::Error(format!(
511                            "TCO target '{}' is not a function",
512                            target
513                        )));
514                    }
515                }
516            }
517
518            // Execute body with new args
519            let exec_result = if let Some(ref res) = cur_resolution {
520                let mut slots = vec![Rc::new(Value::Unit); res.local_count as usize];
521                for ((param_name, _), arg_val) in cur_params.iter().zip(new_args.into_iter()) {
522                    if let Some(&slot) = res.local_slots.get(param_name) {
523                        slots[slot as usize] = Rc::new(arg_val);
524                    }
525                }
526                self.active_local_slots = Some(res.local_slots.clone());
527                self.push_env(EnvFrame::Slots(slots));
528                let r = match &*cur_body {
529                    FnBody::Expr(e) => self.eval_expr(e),
530                    FnBody::Block(stmts) => self.exec_body_resolved(stmts, &res.local_slots),
531                };
532                self.pop_env();
533                r
534            } else {
535                let mut params_scope = HashMap::new();
536                for ((param_name, _), arg_val) in cur_params.iter().zip(new_args.into_iter()) {
537                    params_scope.insert(param_name.clone(), Rc::new(arg_val));
538                }
539                let saved_frames: Vec<EnvFrame> = self.env.drain(1..).collect();
540                self.push_env(EnvFrame::Owned(params_scope));
541                let r = match &*cur_body {
542                    FnBody::Expr(e) => self.eval_expr(e),
543                    FnBody::Block(stmts) => self.exec_body(stmts),
544                };
545                self.pop_env();
546                self.env.extend(saved_frames);
547                r
548            };
549
550            match exec_result {
551                Err(RuntimeError::TailCall(boxed)) => {
552                    let (next_target, next_args) = *boxed;
553                    target = next_target;
554                    new_args = next_args;
555                    continue;
556                }
557                other => return other,
558            }
559        }
560    }
561}