Skip to main content

aver/
value.rs

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