Skip to main content

synoema_eval/
value.rs

1//! Runtime values for the Synoema tree-walking interpreter.
2
3use std::fmt;
4use synoema_parser::{Pat, Expr, Equation};
5
6/// Runtime value
7#[derive(Debug, Clone)]
8pub enum Value {
9    Int(i64),
10    Float(f64),
11    Bool(bool),
12    Str(String),
13    Char(char),
14    /// List of values
15    List(Vec<Value>),
16    /// Constructor with tag and fields: `Just 42`, `None`
17    Con(String, Vec<Value>),
18    /// Closure: captured env + params + body
19    Closure {
20        params: Vec<Pat>,
21        body: Expr,
22        env: Env,
23    },
24    /// Multi-equation function (pattern matching across equations)
25    Func {
26        name: String,
27        equations: Vec<Equation>,
28        env: Env,
29    },
30    /// Built-in function
31    Builtin(String, usize), // name, arity
32    /// Partially applied builtin
33    PartialBuiltin(String, usize, Vec<Value>), // name, remaining arity, accumulated args
34    /// Record value: {name = "Alice", age = 30}
35    Record(Vec<(String, Value)>),
36    /// Unit (void)
37    Unit,
38}
39
40impl PartialEq for Value {
41    fn eq(&self, other: &Self) -> bool {
42        match (self, other) {
43            (Value::Int(a), Value::Int(b)) => a == b,
44            (Value::Float(a), Value::Float(b)) => a == b,
45            (Value::Bool(a), Value::Bool(b)) => a == b,
46            (Value::Str(a), Value::Str(b)) => a == b,
47            (Value::Char(a), Value::Char(b)) => a == b,
48            (Value::List(a), Value::List(b)) => a == b,
49            (Value::Con(na, fa), Value::Con(nb, fb)) => na == nb && fa == fb,
50            (Value::Record(a), Value::Record(b)) => a == b,
51            (Value::Unit, Value::Unit) => true,
52            _ => false,
53        }
54    }
55}
56
57impl PartialOrd for Value {
58    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
59        match (self, other) {
60            (Value::Int(a), Value::Int(b)) => a.partial_cmp(b),
61            (Value::Float(a), Value::Float(b)) => a.partial_cmp(b),
62            (Value::Str(a), Value::Str(b)) => a.partial_cmp(b),
63            (Value::Char(a), Value::Char(b)) => a.partial_cmp(b),
64            (Value::Bool(a), Value::Bool(b)) => a.partial_cmp(b),
65            _ => None,
66        }
67    }
68}
69
70impl fmt::Display for Value {
71    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72        match self {
73            Value::Int(n) => write!(f, "{}", n),
74            Value::Float(n) => {
75                if *n == (*n as i64) as f64 {
76                    write!(f, "{:.1}", n)
77                } else {
78                    write!(f, "{}", n)
79                }
80            }
81            Value::Bool(b) => write!(f, "{}", b),
82            Value::Str(s) => write!(f, "{}", s),
83            Value::Char(c) => write!(f, "{}", c),
84            Value::List(elems) => {
85                write!(f, "[")?;
86                for (i, e) in elems.iter().enumerate() {
87                    if i > 0 { write!(f, " ")?; }
88                    write!(f, "{}", e)?;
89                }
90                write!(f, "]")
91            }
92            Value::Con(name, fields) => {
93                write!(f, "{}", name)?;
94                for fld in fields {
95                    write!(f, " ")?;
96                    match fld {
97                        Value::Con(_, fs) if !fs.is_empty() => write!(f, "({})", fld)?,
98                        _ => write!(f, "{}", fld)?,
99                    }
100                }
101                Ok(())
102            }
103            Value::Record(fields) => {
104                write!(f, "{{")?;
105                for (i, (name, val)) in fields.iter().enumerate() {
106                    if i > 0 { write!(f, ", ")?; }
107                    write!(f, "{} = {}", name, val)?;
108                }
109                write!(f, "}}")
110            }
111            Value::Closure { .. } => write!(f, "<closure>"),
112            Value::Func { name, .. } => write!(f, "<fn {}>", name),
113            Value::Builtin(name, _) => write!(f, "<builtin {}>", name),
114            Value::PartialBuiltin(name, _, _) => write!(f, "<partial {}>", name),
115            Value::Unit => write!(f, "()"),
116        }
117    }
118}
119
120/// Environment: chain of scopes for variable lookup
121#[derive(Debug, Clone)]
122pub struct Env {
123    frames: Vec<std::collections::HashMap<String, Value>>,
124}
125
126impl Env {
127    pub fn new() -> Self {
128        Env { frames: vec![std::collections::HashMap::new()] }
129    }
130
131    pub fn lookup(&self, name: &str) -> Option<&Value> {
132        for frame in self.frames.iter().rev() {
133            if let Some(v) = frame.get(name) {
134                return Some(v);
135            }
136        }
137        None
138    }
139
140    pub fn insert(&mut self, name: String, val: Value) {
141        if let Some(frame) = self.frames.last_mut() {
142            frame.insert(name, val);
143        }
144    }
145
146    pub fn push_scope(&mut self) {
147        self.frames.push(std::collections::HashMap::new());
148    }
149
150    pub fn pop_scope(&mut self) {
151        if self.frames.len() > 1 {
152            self.frames.pop();
153        }
154    }
155
156    /// Create a child environment (for closures)
157    pub fn child(&self) -> Self {
158        let mut e = self.clone();
159        e.push_scope();
160        e
161    }
162
163    /// Iterate over all bindings (most recent scope first)
164    pub fn iter_all(&self) -> Vec<(String, Value)> {
165        let mut seen = std::collections::HashSet::new();
166        let mut result = Vec::new();
167        for frame in self.frames.iter().rev() {
168            for (k, v) in frame {
169                if seen.insert(k.clone()) {
170                    result.push((k.clone(), v.clone()));
171                }
172            }
173        }
174        result
175    }
176}