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