Skip to main content

aver/interpreter/
eval.rs

1use super::*;
2
3impl Interpreter {
4    fn empty_slots(local_count: u16) -> Vec<Rc<Value>> {
5        let unit = Rc::new(Value::Unit);
6        vec![unit; local_count as usize]
7    }
8
9    pub fn eval_expr(&mut self, expr: &Expr) -> Result<Value, RuntimeError> {
10        match expr {
11            Expr::Literal(lit) => Ok(self.eval_literal(lit)),
12            Expr::Resolved(slot) => self.lookup_slot(*slot),
13            Expr::Ident(name) => self.lookup(name),
14            Expr::Attr(obj, field) => {
15                // Fast path: `Ident.field` avoids cloning the entire namespace/record
16                if let Expr::Ident(name) = obj.as_ref() {
17                    let rc = self.lookup_rc(name)?;
18                    return match rc.as_ref() {
19                        Value::Namespace { name, members } => {
20                            members.get(field.as_str()).cloned().ok_or_else(|| {
21                                RuntimeError::Error(format!("Unknown member '{}.{}'", name, field))
22                            })
23                        }
24                        Value::Record { fields, .. } => fields
25                            .iter()
26                            .find(|(k, _)| k == field)
27                            .map(|(_, v)| Ok(v.clone()))
28                            .unwrap_or_else(|| {
29                                Err(RuntimeError::Error(format!("Unknown field '{}'", field)))
30                            }),
31                        _ => Err(RuntimeError::Error(format!(
32                            "Field access '{}' is not supported on this value",
33                            field
34                        ))),
35                    };
36                }
37                // General path: computed object expression
38                let obj_val = self.eval_expr(obj)?;
39                match obj_val {
40                    Value::Record { fields, .. } => fields
41                        .into_iter()
42                        .find(|(k, _)| k == field)
43                        .map(|(_, v)| Ok(v))
44                        .unwrap_or_else(|| {
45                            Err(RuntimeError::Error(format!("Unknown field '{}'", field)))
46                        }),
47                    Value::Namespace { name, members } => {
48                        members.get(field).cloned().ok_or_else(|| {
49                            RuntimeError::Error(format!("Unknown member '{}.{}'", name, field))
50                        })
51                    }
52                    _ => Err(RuntimeError::Error(format!(
53                        "Field access '{}' is not supported on this value",
54                        field
55                    ))),
56                }
57            }
58            Expr::FnCall(fn_expr, args) => {
59                let fn_val = self.eval_expr(fn_expr)?;
60                let mut arg_vals = Vec::new();
61                for a in args {
62                    arg_vals.push(self.eval_expr(a)?);
63                }
64                self.call_value(fn_val, arg_vals)
65            }
66            Expr::BinOp(op, left, right) => {
67                let lv = self.eval_expr(left)?;
68                let rv = self.eval_expr(right)?;
69                self.eval_binop(op, lv, rv)
70            }
71            Expr::Match {
72                subject,
73                arms,
74                line,
75            } => {
76                let sv = self.eval_expr(subject)?;
77                self.eval_match(sv, arms, *line)
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 = Self::empty_slots(res.local_count);
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 = self.exec_body_resolved(body.stmts(), &res.local_slots);
414            self.pop_env();
415            r
416        } else {
417            // Unresolved path (REPL): env is already isolated to [globals].
418            let mut params_scope = HashMap::new();
419            for ((param_name, _), arg_val) in params.iter().zip(args.into_iter()) {
420                params_scope.insert(param_name.clone(), Rc::new(arg_val));
421            }
422            self.push_env(EnvFrame::Owned(params_scope));
423            let r = self.exec_body(body.stmts());
424            self.pop_env();
425            r
426        };
427
428        // If TailCall, enter trampoline — only then do we clone fn state.
429        let result = match result {
430            Err(RuntimeError::TailCall(boxed)) => {
431                let (target, new_args) = *boxed;
432                self.tco_trampoline(name, params, body, resolution, target, new_args)
433            }
434            other => other,
435        };
436
437        self.active_local_slots = prev_local_slots;
438        if let Some(prev) = prev_global
439            && let Some(global) = self.env.first_mut()
440        {
441            *global = prev;
442        }
443        self.env.truncate(1);
444        self.env.extend(saved_frames);
445
446        self.call_stack.pop();
447        let final_result = match result {
448            Ok(v) => Ok(v),
449            Err(RuntimeError::ErrProp(e)) => Ok(Value::Err(e)),
450            Err(e) => Err(e),
451        };
452
453        // Auto-memoization: store result in cache
454        if let (Some((key, memo_args)), Ok(val)) = (memo_key, &final_result) {
455            let fn_name = name.clone();
456            let cache = self.memo_cache.entry(fn_name).or_default();
457            cache.insert(key, memo_args, val.clone(), MEMO_CACHE_CAP_PER_FN);
458        }
459
460        final_result
461    }
462
463    /// Trampoline loop for tail-call optimization.
464    /// Called only when `call_fn_ref` encounters a `TailCall` — keeps the call
465    /// stack flat by re-binding args in a loop instead of recursing.
466    #[inline(never)]
467    pub(super) fn tco_trampoline(
468        &mut self,
469        orig_name: &str,
470        orig_params: &[(String, String)],
471        orig_body: &Rc<FnBody>,
472        orig_resolution: &Option<FnResolution>,
473        first_target: String,
474        first_args: Vec<Value>,
475    ) -> Result<Value, RuntimeError> {
476        let mut cur_name = orig_name.to_string();
477        let mut cur_params: Vec<(String, String)> = orig_params.to_vec();
478        let mut cur_body = Rc::clone(orig_body);
479        let mut cur_resolution = orig_resolution.clone();
480
481        // Handle the initial TailCall
482        let mut target = first_target;
483        let mut new_args = first_args;
484
485        loop {
486            // Switch to target if different from current
487            if target != cur_name {
488                let target_fn = self.lookup(&target)?;
489                match target_fn {
490                    Value::Fn {
491                        name: tn,
492                        params: tp,
493                        effects: te,
494                        body: tb,
495                        resolution: tr,
496                        ..
497                    } => {
498                        cur_name = tn;
499                        cur_params = tp;
500                        cur_body = tb;
501                        cur_resolution = tr;
502                        if let Some(frame) = self.call_stack.last_mut() {
503                            frame.name = cur_name.clone();
504                            frame.effects = te;
505                        }
506                    }
507                    _ => {
508                        return Err(RuntimeError::Error(format!(
509                            "TCO target '{}' is not a function",
510                            target
511                        )));
512                    }
513                }
514            }
515
516            // Execute body with new args
517            let exec_result = if let Some(ref res) = cur_resolution {
518                let mut slots = Self::empty_slots(res.local_count);
519                for ((param_name, _), arg_val) in cur_params.iter().zip(new_args.into_iter()) {
520                    if let Some(&slot) = res.local_slots.get(param_name) {
521                        slots[slot as usize] = Rc::new(arg_val);
522                    }
523                }
524                self.active_local_slots = Some(res.local_slots.clone());
525                self.push_env(EnvFrame::Slots(slots));
526                let r = self.exec_body_resolved(cur_body.stmts(), &res.local_slots);
527                self.pop_env();
528                r
529            } else {
530                let mut params_scope = HashMap::new();
531                for ((param_name, _), arg_val) in cur_params.iter().zip(new_args.into_iter()) {
532                    params_scope.insert(param_name.clone(), Rc::new(arg_val));
533                }
534                let saved_frames: Vec<EnvFrame> = self.env.drain(1..).collect();
535                self.push_env(EnvFrame::Owned(params_scope));
536                let r = self.exec_body(cur_body.stmts());
537                self.pop_env();
538                self.env.extend(saved_frames);
539                r
540            };
541
542            match exec_result {
543                Err(RuntimeError::TailCall(boxed)) => {
544                    let (next_target, next_args) = *boxed;
545                    target = next_target;
546                    new_args = next_args;
547                    continue;
548                }
549                other => return other,
550            }
551        }
552    }
553}