cl_interpret/
convalue.rs

1//! Values in the dynamically typed AST interpreter.
2//!
3//! The most permanent fix is a temporary one.
4use cl_ast::{Expr, Sym, format::FmtAdapter};
5
6use crate::{closure::Closure, env::Place};
7
8use super::{
9    Callable, Environment,
10    builtin::Builtin,
11    error::{Error, IResult},
12    function::Function,
13};
14use std::{collections::HashMap, ops::*, rc::Rc};
15
16/*
17A Value can be:
18- A Primitive (Empty, isize, etc.)
19- A Record (Array, Tuple, Struct)
20- A Variant (discriminant, Value) pair
21
22array [
23    10,     // 0
24    20,     // 1
25]
26
27tuple (
28    10,     // 0
29    20,     // 1
30)
31
32struct {
33    x: 10,  // x => 0
34    y: 20,  // y => 1
35}
36*/
37
38type Integer = isize;
39
40/// A Conlang value stores data in the interpreter
41#[derive(Clone, Debug, Default)]
42pub enum ConValue {
43    /// The empty/unit `()` type
44    #[default]
45    Empty,
46    /// An integer
47    Int(Integer),
48    /// A floating point number
49    Float(f64),
50    /// A boolean
51    Bool(bool),
52    /// A unicode character
53    Char(char),
54    /// A string
55    String(Sym),
56    /// A reference
57    Ref(Place),
58    /// A reference to an array
59    Slice(Place, usize),
60    /// An Array
61    Array(Box<[ConValue]>),
62    /// A tuple
63    Tuple(Box<[ConValue]>),
64    /// A value of a product type
65    Struct(Box<(Sym, HashMap<Sym, ConValue>)>),
66    /// A value of a product type with anonymous members
67    TupleStruct(Box<(&'static str, Box<[ConValue]>)>),
68    /// An entire namespace
69    Module(Box<HashMap<Sym, Option<ConValue>>>),
70    /// A quoted expression
71    Quote(Box<Expr>),
72    /// A callable thing
73    Function(Rc<Function>),
74    /// A closure, capturing by reference
75    Closure(Rc<Closure>),
76    /// A built-in function
77    Builtin(&'static Builtin),
78}
79
80impl ConValue {
81    /// Gets whether the current value is true or false
82    pub fn truthy(&self) -> IResult<bool> {
83        match self {
84            ConValue::Bool(v) => Ok(*v),
85            _ => Err(Error::TypeError())?,
86        }
87    }
88
89    #[allow(non_snake_case)]
90    pub fn TupleStruct(name: Sym, values: Box<[ConValue]>) -> Self {
91        Self::TupleStruct(Box::new((name.to_ref(), values)))
92    }
93    #[allow(non_snake_case)]
94    pub fn Struct(name: Sym, values: HashMap<Sym, ConValue>) -> Self {
95        Self::Struct(Box::new((name, values)))
96    }
97
98    pub fn index(&self, index: &Self, env: &Environment) -> IResult<ConValue> {
99        let Self::Int(index) = index else {
100            Err(Error::TypeError())?
101        };
102        match self {
103            ConValue::String(string) => string
104                .chars()
105                .nth(*index as _)
106                .map(ConValue::Char)
107                .ok_or(Error::OobIndex(*index as usize, string.chars().count())),
108            ConValue::Array(arr) => arr
109                .get(*index as usize)
110                .cloned()
111                .ok_or(Error::OobIndex(*index as usize, arr.len())),
112            ConValue::Slice(id, start) => env
113                .get_id(*id)
114                .ok_or(Error::StackOverflow(*id))?
115                .index(&ConValue::Int((*index as usize + start) as isize), env),
116            _ => Err(Error::TypeError()),
117        }
118    }
119    cmp! {
120        lt: false, <;
121        lt_eq: true, <=;
122        eq: true, ==;
123        neq: false, !=;
124        gt_eq: true, >=;
125        gt: false, >;
126    }
127    assign! {
128        add_assign: +;
129        bitand_assign: &;
130        bitor_assign: |;
131        bitxor_assign: ^;
132        div_assign: /;
133        mul_assign: *;
134        rem_assign: %;
135        shl_assign: <<;
136        shr_assign: >>;
137        sub_assign: -;
138    }
139}
140
141impl Callable for ConValue {
142    fn name(&self) -> Sym {
143        match self {
144            ConValue::Function(func) => func.name(),
145            ConValue::Closure(func) => func.name(),
146            ConValue::Builtin(func) => func.name(),
147            _ => "".into(),
148        }
149    }
150    fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
151        match self {
152            Self::Function(func) => func.call(interpreter, args),
153            Self::Closure(func) => func.call(interpreter, args),
154            Self::Builtin(func) => func.call(interpreter, args),
155            _ => Err(Error::NotCallable(self.clone())),
156        }
157    }
158}
159/// Templates comparison functions for [ConValue]
160macro cmp ($($fn:ident: $empty:literal, $op:tt);*$(;)?) {$(
161    /// TODO: Remove when functions are implemented:
162    ///       Desugar into function calls
163    pub fn $fn(&self, other: &Self) -> IResult<Self> {
164        match (self, other) {
165            (Self::Empty, Self::Empty) => Ok(Self::Bool($empty)),
166            (Self::Int(a), Self::Int(b)) => Ok(Self::Bool(a $op b)),
167            (Self::Float(a), Self::Float(b)) => Ok(Self::Bool(a $op b)),
168            (Self::Bool(a), Self::Bool(b)) => Ok(Self::Bool(a $op b)),
169            (Self::Char(a), Self::Char(b)) => Ok(Self::Bool(a $op b)),
170            (Self::String(a), Self::String(b)) => Ok(Self::Bool(&**a $op &**b)),
171            _ => Err(Error::TypeError())
172        }
173    }
174)*}
175macro assign($( $fn: ident: $op: tt );*$(;)?) {$(
176    pub fn $fn(&mut self, other: Self) -> IResult<()> {
177        *self = (std::mem::take(self) $op other)?;
178        Ok(())
179    }
180)*}
181/// Implements [From] for an enum with 1-tuple variants
182macro from ($($T:ty => $v:expr),*$(,)?) {
183    $(impl From<$T> for ConValue {
184        fn from(value: $T) -> Self { $v(value.into()) }
185    })*
186}
187impl From<&Sym> for ConValue {
188    fn from(value: &Sym) -> Self {
189        ConValue::String(*value)
190    }
191}
192from! {
193    Integer => ConValue::Int,
194    f64 => ConValue::Float,
195    bool => ConValue::Bool,
196    char => ConValue::Char,
197    Sym => ConValue::String,
198    &str => ConValue::String,
199    Expr => ConValue::Quote,
200    String => ConValue::String,
201    Rc<str> => ConValue::String,
202    Function => ConValue::Function,
203    Vec<ConValue> => ConValue::Tuple,
204    &'static Builtin => ConValue::Builtin,
205}
206impl From<()> for ConValue {
207    fn from(_: ()) -> Self {
208        Self::Empty
209    }
210}
211impl From<&[ConValue]> for ConValue {
212    fn from(value: &[ConValue]) -> Self {
213        match value {
214            [] => Self::Empty,
215            [value] => value.clone(),
216            _ => Self::Tuple(value.into()),
217        }
218    }
219}
220
221/// Implements binary [std::ops] traits for [ConValue]
222///
223/// TODO: Desugar operators into function calls
224macro ops($($trait:ty: $fn:ident = [$($match:tt)*])*) {
225    $(impl $trait for ConValue {
226        type Output = IResult<Self>;
227        /// TODO: Desugar operators into function calls
228        fn $fn(self, rhs: Self) -> Self::Output {Ok(match (self, rhs) {$($match)*})}
229    })*
230}
231ops! {
232    Add: add = [
233        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
234        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_add(b)),
235        (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a + b),
236        (ConValue::String(a), ConValue::String(b)) => (a.to_string() + &*b).into(),
237        (ConValue::String(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(c); s.into() }
238        (ConValue::Char(a), ConValue::Char(b)) => {
239            ConValue::String([a, b].into_iter().collect::<String>().into())
240        }
241        _ => Err(Error::TypeError())?
242        ]
243    BitAnd: bitand = [
244        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
245        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
246        (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b),
247        _ => Err(Error::TypeError())?
248    ]
249    BitOr: bitor = [
250        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
251        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
252        (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
253        _ => Err(Error::TypeError())?
254    ]
255    BitXor: bitxor = [
256        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
257        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
258        (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
259        _ => Err(Error::TypeError())?
260    ]
261    Div: div = [
262        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
263        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_div(b).unwrap_or_else(|| {
264            eprintln!("Warning: Divide by zero in {a} / {b}"); a
265        })),
266        (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a / b),
267        _ => Err(Error::TypeError())?
268    ]
269    Mul: mul = [
270        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
271        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_mul(b)),
272        (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a * b),
273        _ => Err(Error::TypeError())?
274    ]
275    Rem: rem = [
276        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
277        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_rem(b).unwrap_or_else(|| {
278            println!("Warning: Divide by zero in {a} % {b}"); a
279        })),
280        (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a % b),
281        _ => Err(Error::TypeError())?
282    ]
283    Shl: shl = [
284        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
285        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_shl(b as _)),
286        _ => Err(Error::TypeError())?
287    ]
288    Shr: shr = [
289        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
290        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_shr(b as _)),
291        _ => Err(Error::TypeError())?
292    ]
293    Sub: sub = [
294        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
295        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_sub(b)),
296        (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a - b),
297        _ => Err(Error::TypeError())?
298    ]
299}
300impl std::fmt::Display for ConValue {
301    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
302        match self {
303            ConValue::Empty => "Empty".fmt(f),
304            ConValue::Int(v) => v.fmt(f),
305            ConValue::Float(v) => v.fmt(f),
306            ConValue::Bool(v) => v.fmt(f),
307            ConValue::Char(v) => v.fmt(f),
308            ConValue::String(v) => v.fmt(f),
309            ConValue::Ref(v) => write!(f, "&<{}>", v),
310            ConValue::Slice(v, len) => write!(f, "&<{v}>[{len}..]"),
311            ConValue::Array(array) => {
312                '['.fmt(f)?;
313                for (idx, element) in array.iter().enumerate() {
314                    if idx > 0 {
315                        ", ".fmt(f)?
316                    }
317                    element.fmt(f)?
318                }
319                ']'.fmt(f)
320            }
321            ConValue::Tuple(tuple) => {
322                '('.fmt(f)?;
323                for (idx, element) in tuple.iter().enumerate() {
324                    if idx > 0 {
325                        ", ".fmt(f)?
326                    }
327                    element.fmt(f)?
328                }
329                ')'.fmt(f)
330            }
331            ConValue::TupleStruct(parts) => {
332                let (name, tuple) = parts.as_ref();
333                if !name.is_empty() {
334                    write!(f, "{name}")?;
335                }
336                '('.fmt(f)?;
337                for (idx, element) in tuple.iter().enumerate() {
338                    if idx > 0 {
339                        ", ".fmt(f)?
340                    }
341                    element.fmt(f)?
342                }
343                ')'.fmt(f)
344            }
345            ConValue::Struct(parts) => {
346                let (name, map) = parts.as_ref();
347                use std::fmt::Write;
348                if !name.is_empty() {
349                    write!(f, "{name} ")?;
350                }
351                let mut f = f.delimit_with("{", "\n}");
352                for (k, v) in map.iter() {
353                    write!(f, "\n{k}: {v},")?;
354                }
355                Ok(())
356            }
357            ConValue::Module(module) => {
358                use std::fmt::Write;
359                let mut f = f.delimit_with("{", "\n}");
360                for (k, v) in module.iter() {
361                    write!(f, "\n{k}: ")?;
362                    match v {
363                        Some(v) => write!(f, "{v},"),
364                        None => write!(f, "_,"),
365                    }?
366                }
367                Ok(())
368            }
369            ConValue::Quote(q) => {
370                write!(f, "`{q}`")
371            }
372            ConValue::Function(func) => {
373                write!(f, "{}", func.decl())
374            }
375            ConValue::Closure(func) => {
376                write!(f, "{}", func.as_ref())
377            }
378            ConValue::Builtin(func) => {
379                write!(f, "{}", func.description())
380            }
381        }
382    }
383}
384
385pub macro cvstruct (
386    $Name:ident {
387        $($member:ident : $expr:expr),*
388    }
389) {{
390    let mut members = HashMap::new();
391    $(members.insert(stringify!($member).into(), ($expr).into());)*
392    ConValue::Struct(Box::new((stringify!($Name).into(), members)))
393}}