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