Skip to main content

aver/
value.rs

1/// Core Aver runtime value type and associated utilities.
2///
3/// Lives in its own module so both the interpreter and the service
4/// implementations (`services::*`) can import it without circular
5/// dependencies.
6use std::collections::HashMap;
7use std::rc::Rc;
8use thiserror::Error;
9
10use crate::ast::FnBody;
11
12// ---------------------------------------------------------------------------
13// RuntimeError
14// ---------------------------------------------------------------------------
15
16#[derive(Debug, Error)]
17pub enum RuntimeError {
18    #[error("Runtime error: {0}")]
19    Error(String),
20    /// Internal signal: `?` operator encountered Err — caught by call_value to
21    /// do early return. Never surfaces to the user (type checker prevents
22    /// top-level use).
23    #[error("Error propagation")]
24    ErrProp(Box<Value>),
25    /// Internal signal: tail-call — caught by the trampoline in call_fn_ref.
26    /// Never surfaces to the user. Boxed to keep RuntimeError small.
27    #[error("Tail call")]
28    TailCall(Box<(String, Vec<Value>)>),
29    #[error("Replay mismatch at seq {seq}: expected '{expected}', got '{got}'")]
30    ReplayMismatch {
31        seq: u32,
32        expected: String,
33        got: String,
34    },
35    #[error(
36        "Replay args mismatch at seq {seq} for '{effect_type}': expected {expected}, got {got}"
37    )]
38    ReplayArgsMismatch {
39        seq: u32,
40        effect_type: String,
41        expected: String,
42        got: String,
43    },
44    #[error("Replay exhausted at position {position}: no recorded effect for call '{effect_type}'")]
45    ReplayExhausted {
46        effect_type: String,
47        position: usize,
48    },
49    #[error("Replay has {remaining} unconsumed effect(s)")]
50    ReplayUnconsumed { remaining: usize },
51    #[error("Replay serialization error: {0}")]
52    ReplaySerialization(String),
53}
54
55// ---------------------------------------------------------------------------
56// Value
57// ---------------------------------------------------------------------------
58
59#[derive(Debug, Clone)]
60pub enum Value {
61    Int(i64),
62    Float(f64),
63    Str(String),
64    Bool(bool),
65    Unit,
66    Ok(Box<Value>),
67    Err(Box<Value>),
68    Some(Box<Value>),
69    None,
70    List(Vec<Value>),
71    Tuple(Vec<Value>),
72    Map(HashMap<Value, Value>),
73    /// Shared list view (`items[start..]`) to avoid O(n^2) tail copying in
74    /// recursive list processing.
75    ListSlice {
76        items: Rc<Vec<Value>>,
77        start: usize,
78    },
79    Fn {
80        name: String,
81        params: Vec<(String, String)>,
82        effects: Vec<String>,
83        body: Rc<FnBody>,
84        /// Compile-time resolution metadata (slot layout for locals).
85        resolution: Option<crate::ast::FnResolution>,
86        /// True only for functions selected by `compute_memo_fns` in the
87        /// interpreter that defined them.
88        memo_eligible: bool,
89        /// Optional function-specific global scope (used by imported module
90        /// functions so they resolve names in their home module).
91        home_globals: Option<Rc<HashMap<String, Rc<Value>>>>,
92    },
93    Builtin(String),
94    /// User-defined sum type variant, e.g. `Shape.Circle(3.14)`
95    Variant {
96        type_name: String,
97        variant: String,
98        fields: Vec<Value>,
99    },
100    /// User-defined product type (record), e.g. `User(name = "Alice", age = 30)`
101    Record {
102        type_name: String,
103        fields: Vec<(String, Value)>,
104    },
105    /// Type namespace: `Shape` — provides `Shape.Circle`, `Shape.Rect`, etc.
106    Namespace {
107        name: String,
108        members: HashMap<String, Value>,
109    },
110}
111
112impl PartialEq for Value {
113    fn eq(&self, other: &Self) -> bool {
114        match (list_slice(self), list_slice(other)) {
115            (Some(xs), Some(ys)) => return xs == ys,
116            (Some(_), None) | (None, Some(_)) => return false,
117            (None, None) => {}
118        }
119
120        match (self, other) {
121            (Value::Int(a), Value::Int(b)) => a == b,
122            (Value::Float(a), Value::Float(b)) => {
123                if a.is_nan() || b.is_nan() {
124                    a.to_bits() == b.to_bits()
125                } else {
126                    a == b
127                }
128            }
129            (Value::Str(a), Value::Str(b)) => a == b,
130            (Value::Bool(a), Value::Bool(b)) => a == b,
131            (Value::Unit, Value::Unit) => true,
132            (Value::Ok(a), Value::Ok(b)) => a == b,
133            (Value::Err(a), Value::Err(b)) => a == b,
134            (Value::Some(a), Value::Some(b)) => a == b,
135            (Value::None, Value::None) => true,
136            (Value::Tuple(a), Value::Tuple(b)) => a == b,
137            (Value::Map(a), Value::Map(b)) => a == b,
138            (
139                Value::Fn {
140                    name: n1,
141                    params: p1,
142                    effects: e1,
143                    body: b1,
144                    ..
145                },
146                Value::Fn {
147                    name: n2,
148                    params: p2,
149                    effects: e2,
150                    body: b2,
151                    ..
152                },
153            ) => n1 == n2 && p1 == p2 && e1 == e2 && b1 == b2,
154            (Value::Builtin(a), Value::Builtin(b)) => a == b,
155            (
156                Value::Variant {
157                    type_name: t1,
158                    variant: v1,
159                    fields: f1,
160                },
161                Value::Variant {
162                    type_name: t2,
163                    variant: v2,
164                    fields: f2,
165                },
166            ) => t1 == t2 && v1 == v2 && f1 == f2,
167            (
168                Value::Record {
169                    type_name: t1,
170                    fields: f1,
171                },
172                Value::Record {
173                    type_name: t2,
174                    fields: f2,
175                },
176            ) => t1 == t2 && f1 == f2,
177            (
178                Value::Namespace {
179                    name: n1,
180                    members: m1,
181                },
182                Value::Namespace {
183                    name: n2,
184                    members: m2,
185                },
186            ) => n1 == n2 && m1 == m2,
187            _ => false,
188        }
189    }
190}
191
192impl Eq for Value {}
193
194impl std::hash::Hash for Value {
195    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
196        if let Some(items) = list_slice(self) {
197            8u8.hash(state);
198            items.len().hash(state);
199            for item in items {
200                item.hash(state);
201            }
202            return;
203        }
204
205        match self {
206            Value::Int(i) => {
207                0u8.hash(state);
208                i.hash(state);
209            }
210            Value::Float(f) => {
211                1u8.hash(state);
212                let bits = if *f == 0.0 {
213                    0.0f64.to_bits()
214                } else {
215                    f.to_bits()
216                };
217                bits.hash(state);
218            }
219            Value::Str(s) => {
220                2u8.hash(state);
221                s.hash(state);
222            }
223            Value::Bool(b) => {
224                3u8.hash(state);
225                b.hash(state);
226            }
227            Value::Unit => {
228                4u8.hash(state);
229            }
230            Value::Ok(v) => {
231                5u8.hash(state);
232                v.hash(state);
233            }
234            Value::Err(v) => {
235                6u8.hash(state);
236                v.hash(state);
237            }
238            Value::Some(v) => {
239                7u8.hash(state);
240                v.hash(state);
241            }
242            Value::None => {
243                9u8.hash(state);
244            }
245            Value::Map(map) => {
246                10u8.hash(state);
247                let mut entries = map.iter().collect::<Vec<_>>();
248                entries.sort_by(|(k1, _), (k2, _)| aver_repr(k1).cmp(&aver_repr(k2)));
249                for (k, v) in entries {
250                    k.hash(state);
251                    v.hash(state);
252                }
253            }
254            Value::Tuple(items) => {
255                16u8.hash(state);
256                items.hash(state);
257            }
258            Value::Fn {
259                name,
260                params,
261                effects,
262                body,
263                ..
264            } => {
265                11u8.hash(state);
266                name.hash(state);
267                params.hash(state);
268                effects.hash(state);
269                format!("{:?}", body).hash(state);
270            }
271            Value::Builtin(name) => {
272                12u8.hash(state);
273                name.hash(state);
274            }
275            Value::Variant {
276                type_name,
277                variant,
278                fields,
279            } => {
280                13u8.hash(state);
281                type_name.hash(state);
282                variant.hash(state);
283                fields.hash(state);
284            }
285            Value::Record { type_name, fields } => {
286                14u8.hash(state);
287                type_name.hash(state);
288                fields.hash(state);
289            }
290            Value::Namespace { name, members } => {
291                15u8.hash(state);
292                name.hash(state);
293                let mut keys = members.keys().collect::<Vec<_>>();
294                keys.sort();
295                for key in keys {
296                    key.hash(state);
297                    if let Some(value) = members.get(key) {
298                        value.hash(state);
299                    }
300                }
301            }
302            Value::List(_) | Value::ListSlice { .. } => unreachable!("list hashed above"),
303        }
304    }
305}
306
307// ---------------------------------------------------------------------------
308// Environment
309// ---------------------------------------------------------------------------
310
311#[derive(Debug, Clone)]
312pub enum EnvFrame {
313    Owned(HashMap<String, Rc<Value>>),
314    Shared(Rc<HashMap<String, Rc<Value>>>),
315    /// Slot-indexed frame for resolved function bodies — O(1) lookup.
316    Slots(Vec<Rc<Value>>),
317}
318
319/// Scope stack: innermost scope last.
320pub type Env = Vec<EnvFrame>;
321
322// ---------------------------------------------------------------------------
323// List helpers
324// ---------------------------------------------------------------------------
325
326pub fn list_slice(value: &Value) -> Option<&[Value]> {
327    match value {
328        Value::List(items) => Some(items.as_slice()),
329        Value::ListSlice { items, start } => Some(items.get(*start..).unwrap_or(&[])),
330        _ => None,
331    }
332}
333
334pub fn list_from_vec(items: Vec<Value>) -> Value {
335    Value::ListSlice {
336        items: Rc::new(items),
337        start: 0,
338    }
339}
340
341pub fn list_to_vec(value: &Value) -> Option<Vec<Value>> {
342    list_slice(value).map(|items| items.to_vec())
343}
344
345pub fn list_len(value: &Value) -> Option<usize> {
346    list_slice(value).map(|items| items.len())
347}
348
349pub fn list_head(value: &Value) -> Option<Value> {
350    list_slice(value).and_then(|items| items.first().cloned())
351}
352
353pub fn list_tail_view(value: &Value) -> Option<Value> {
354    match value {
355        Value::List(items) => {
356            if items.is_empty() {
357                None
358            } else {
359                Some(Value::ListSlice {
360                    items: Rc::new(items.clone()),
361                    start: 1,
362                })
363            }
364        }
365        Value::ListSlice { items, start } => {
366            if *start >= items.len() {
367                None
368            } else {
369                Some(Value::ListSlice {
370                    items: Rc::clone(items),
371                    start: start + 1,
372                })
373            }
374        }
375        _ => None,
376    }
377}
378
379// ---------------------------------------------------------------------------
380// Display helpers
381// ---------------------------------------------------------------------------
382
383/// Human-readable representation of a value (used by `str()` and `:env`).
384pub fn aver_repr(val: &Value) -> String {
385    match val {
386        Value::Int(i) => i.to_string(),
387        Value::Float(f) => f.to_string(),
388        Value::Str(s) => s.clone(),
389        Value::Bool(b) => if *b { "true" } else { "false" }.to_string(),
390        Value::Unit => "()".to_string(),
391        Value::Ok(v) => format!("Result.Ok({})", aver_repr_inner(v)),
392        Value::Err(v) => format!("Result.Err({})", aver_repr_inner(v)),
393        Value::Some(v) => format!("Option.Some({})", aver_repr_inner(v)),
394        Value::None => "Option.None".to_string(),
395        Value::List(_) | Value::ListSlice { .. } => {
396            let items = list_slice(val).expect("list variants must have a slice view");
397            let parts: Vec<String> = items.iter().map(aver_repr_inner).collect();
398            format!("[{}]", parts.join(", "))
399        }
400        Value::Tuple(items) => {
401            let parts: Vec<String> = items.iter().map(aver_repr_inner).collect();
402            format!("({})", parts.join(", "))
403        }
404        Value::Map(entries) => {
405            let mut pairs = entries
406                .iter()
407                .map(|(k, v)| (aver_repr_inner(k), aver_repr_inner(v)))
408                .collect::<Vec<_>>();
409            pairs.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
410            let parts = pairs
411                .into_iter()
412                .map(|(k, v)| format!("{}: {}", k, v))
413                .collect::<Vec<_>>();
414            format!("{{{}}}", parts.join(", "))
415        }
416        Value::Fn { name, .. } => format!("<fn {}>", name),
417        Value::Builtin(name) => format!("<builtin {}>", name),
418        Value::Variant {
419            variant, fields, ..
420        } => {
421            if fields.is_empty() {
422                variant.clone()
423            } else {
424                let parts: Vec<String> = fields.iter().map(aver_repr_inner).collect();
425                format!("{}({})", variant, parts.join(", "))
426            }
427        }
428        Value::Record { type_name, fields } => {
429            let parts: Vec<String> = fields
430                .iter()
431                .map(|(k, v)| format!("{}: {}", k, aver_repr_inner(v)))
432                .collect();
433            format!("{}({})", type_name, parts.join(", "))
434        }
435        Value::Namespace { name, .. } => format!("<type {}>", name),
436    }
437}
438
439/// Like `aver_repr` but strings get quoted — used inside constructors and lists.
440fn aver_repr_inner(val: &Value) -> String {
441    match val {
442        Value::Str(s) => format!("\"{}\"", s),
443        Value::Tuple(items) => {
444            let parts: Vec<String> = items.iter().map(aver_repr_inner).collect();
445            format!("({})", parts.join(", "))
446        }
447        Value::List(_) | Value::ListSlice { .. } => {
448            let items = list_slice(val).expect("list variants must have a slice view");
449            let parts: Vec<String> = items.iter().map(aver_repr_inner).collect();
450            format!("[{}]", parts.join(", "))
451        }
452        other => aver_repr(other),
453    }
454}
455
456/// Returns the display string for `print()` — `None` for `Unit` (silent).
457pub fn aver_display(val: &Value) -> Option<String> {
458    match val {
459        Value::Unit => None,
460        other => Some(aver_repr(other)),
461    }
462}
463
464// ---------------------------------------------------------------------------
465// Memo hashing — only for memo-safe (scalar + record/variant of scalar) values
466// ---------------------------------------------------------------------------
467
468/// Hash a slice of memo-safe argument values into a single u64.
469pub fn hash_memo_args(args: &[Value]) -> u64 {
470    use std::hash::Hasher;
471    let mut hasher = std::collections::hash_map::DefaultHasher::new();
472    for arg in args {
473        hash_value(arg, &mut hasher);
474    }
475    hasher.finish()
476}
477
478fn hash_value(val: &Value, hasher: &mut impl std::hash::Hasher) {
479    use std::hash::Hash;
480    match val {
481        Value::Int(i) => {
482            0u8.hash(hasher);
483            i.hash(hasher);
484        }
485        Value::Float(f) => {
486            1u8.hash(hasher);
487            f.to_bits().hash(hasher);
488        }
489        Value::Str(s) => {
490            2u8.hash(hasher);
491            s.hash(hasher);
492        }
493        Value::Bool(b) => {
494            3u8.hash(hasher);
495            b.hash(hasher);
496        }
497        Value::Unit => {
498            4u8.hash(hasher);
499        }
500        Value::Tuple(items) => {
501            7u8.hash(hasher);
502            items.len().hash(hasher);
503            for item in items {
504                hash_value(item, hasher);
505            }
506        }
507        Value::Record { type_name, fields } => {
508            5u8.hash(hasher);
509            type_name.hash(hasher);
510            for (k, v) in fields {
511                k.hash(hasher);
512                hash_value(v, hasher);
513            }
514        }
515        Value::Variant {
516            type_name,
517            variant,
518            fields,
519        } => {
520            6u8.hash(hasher);
521            type_name.hash(hasher);
522            variant.hash(hasher);
523            for v in fields {
524                hash_value(v, hasher);
525            }
526        }
527        // Should not reach here for non-memo-safe values; no-op to avoid panic.
528        _ => {
529            255u8.hash(hasher);
530        }
531    }
532}