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, constructor::Constructor};
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 literal
55    Str(Sym),
56    /// A dynamic string
57    String(String),
58    /// A reference
59    Ref(usize),
60    /// A reference to an array
61    Slice(usize, usize),
62    /// An Array
63    Array(Box<[ConValue]>),
64    /// A tuple
65    Tuple(Box<[ConValue]>),
66    // TODO: Instead of storing the identifier, store the index of the struct module
67    /// A value of a product type
68    Struct(Sym, Box<HashMap<Sym, ConValue>>),
69    /// A value of a product type with anonymous members
70    TupleStruct(Sym, Box<Box<[ConValue]>>),
71    /// An entire namespace
72    Module(Box<HashMap<Sym, ConValue>>),
73    /// A quoted expression
74    Quote(Rc<Expr>),
75    /// A callable thing
76    Function(Rc<Function>),
77    /// A tuple constructor
78    TupleConstructor(Constructor),
79    /// A closure, capturing by reference
80    Closure(Rc<Closure>),
81    /// A built-in function
82    Builtin(&'static Builtin),
83}
84
85impl ConValue {
86    /// Gets whether the current value is true or false
87    pub fn truthy(&self) -> IResult<bool> {
88        match self {
89            ConValue::Bool(v) => Ok(*v),
90            _ => Err(Error::TypeError())?,
91        }
92    }
93
94    pub fn typename(&self) -> &'static str {
95        match self {
96            ConValue::Empty => "Empty",
97            ConValue::Int(_) => "i64",
98            ConValue::Float(_) => "f64",
99            ConValue::Bool(_) => "bool",
100            ConValue::Char(_) => "char",
101            ConValue::Str(_) => "str",
102            ConValue::String(_) => "String",
103            ConValue::Ref(_) => "Ref",
104            ConValue::Slice(_, _) => "Slice",
105            ConValue::Array(_) => "Array",
106            ConValue::Tuple(_) => "Tuple",
107            ConValue::Struct(_, _) => "Struct",
108            ConValue::TupleStruct(_, _) => "TupleStruct",
109            ConValue::Module(_) => "",
110            ConValue::Quote(_) => "Quote",
111            ConValue::Function(_) => "Fn",
112            ConValue::TupleConstructor(_) => "Fn",
113            ConValue::Closure(_) => "Fn",
114            ConValue::Builtin(_) => "Fn",
115        }
116    }
117
118    #[allow(non_snake_case)]
119    pub fn TupleStruct(id: Sym, values: Box<[ConValue]>) -> Self {
120        Self::TupleStruct(id, Box::new(values))
121    }
122    #[allow(non_snake_case)]
123    pub fn Struct(id: Sym, values: HashMap<Sym, ConValue>) -> Self {
124        Self::Struct(id, Box::new(values))
125    }
126
127    pub fn index(&self, index: &Self, _env: &Environment) -> IResult<ConValue> {
128        let &Self::Int(index) = index else {
129            Err(Error::TypeError())?
130        };
131        match self {
132            ConValue::Str(string) => string
133                .chars()
134                .nth(index as _)
135                .map(ConValue::Char)
136                .ok_or(Error::OobIndex(index as usize, string.chars().count())),
137            ConValue::String(string) => string
138                .chars()
139                .nth(index as _)
140                .map(ConValue::Char)
141                .ok_or(Error::OobIndex(index as usize, string.chars().count())),
142            ConValue::Array(arr) => arr
143                .get(index as usize)
144                .cloned()
145                .ok_or(Error::OobIndex(index as usize, arr.len())),
146            &ConValue::Slice(id, len) => {
147                let index = if index < 0 {
148                    len.wrapping_add_signed(index)
149                } else {
150                    index as usize
151                };
152                if index < len {
153                    Ok(ConValue::Ref(id + index))
154                } else {
155                    Err(Error::OobIndex(index, len))
156                }
157            }
158            _ => Err(Error::TypeError()),
159        }
160    }
161    cmp! {
162        lt: false, <;
163        lt_eq: true, <=;
164        eq: true, ==;
165        neq: false, !=;
166        gt_eq: true, >=;
167        gt: false, >;
168    }
169    assign! {
170        add_assign: +;
171        bitand_assign: &;
172        bitor_assign: |;
173        bitxor_assign: ^;
174        div_assign: /;
175        mul_assign: *;
176        rem_assign: %;
177        shl_assign: <<;
178        shr_assign: >>;
179        sub_assign: -;
180    }
181}
182
183impl Callable for ConValue {
184    fn name(&self) -> Sym {
185        match self {
186            ConValue::Function(func) => func.name(),
187            ConValue::Closure(func) => func.name(),
188            ConValue::Builtin(func) => func.name(),
189            _ => "".into(),
190        }
191    }
192    fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
193        match self {
194            Self::Function(func) => func.call(env, args),
195            Self::TupleConstructor(func) => func.call(env, args),
196            Self::Closure(func) => func.call(env, args),
197            Self::Builtin(func) => func.call(env, args),
198            Self::Module(m) => {
199                if let Some(func) = m.get(&"call".into()) {
200                    func.call(env, args)
201                } else {
202                    Err(Error::NotCallable(self.clone()))
203                }
204            }
205            &Self::Ref(ptr) => {
206                // Move onto stack, and call
207                let func = env.get_id(ptr).ok_or(Error::StackOverflow(ptr))?.clone();
208                func.call(env, args)
209            }
210            _ => Err(Error::NotCallable(self.clone())),
211        }
212    }
213}
214/// Templates comparison functions for [ConValue]
215macro cmp ($($fn:ident: $empty:literal, $op:tt);*$(;)?) {$(
216    /// TODO: Remove when functions are implemented:
217    ///       Desugar into function calls
218    pub fn $fn(&self, other: &Self) -> IResult<Self> {
219        match (self, other) {
220            (Self::Empty, Self::Empty) => Ok(Self::Bool($empty)),
221            (Self::Int(a), Self::Int(b)) => Ok(Self::Bool(a $op b)),
222            (Self::Float(a), Self::Float(b)) => Ok(Self::Bool(a $op b)),
223            (Self::Bool(a), Self::Bool(b)) => Ok(Self::Bool(a $op b)),
224            (Self::Char(a), Self::Char(b)) => Ok(Self::Bool(a $op b)),
225            (Self::Str(a), Self::Str(b)) => Ok(Self::Bool(&**a $op &**b)),
226            (Self::Str(a), Self::String(b)) => Ok(Self::Bool(&**a $op &**b)),
227            (Self::String(a), Self::Str(b)) => Ok(Self::Bool(&**a $op &**b)),
228            (Self::String(a), Self::String(b)) => Ok(Self::Bool(&**a $op &**b)),
229            _ => Err(Error::TypeError())
230        }
231    }
232)*}
233macro assign($( $fn: ident: $op: tt );*$(;)?) {$(
234    pub fn $fn(&mut self, other: Self) -> IResult<()> {
235        *self = (std::mem::take(self) $op other)?;
236        Ok(())
237    }
238)*}
239/// Implements [From] for an enum with 1-tuple variants
240macro from ($($T:ty => $v:expr),*$(,)?) {
241    $(impl From<$T> for ConValue {
242        fn from(value: $T) -> Self { $v(value.into()) }
243    })*
244}
245impl From<&Sym> for ConValue {
246    fn from(value: &Sym) -> Self {
247        ConValue::Str(*value)
248    }
249}
250from! {
251    Integer => ConValue::Int,
252    f64 => ConValue::Float,
253    bool => ConValue::Bool,
254    char => ConValue::Char,
255    Sym => ConValue::Str,
256    &str => ConValue::Str,
257    Expr => ConValue::Quote,
258    String => ConValue::String,
259    Rc<str> => ConValue::Str,
260    Function => ConValue::Function,
261    Vec<ConValue> => ConValue::Tuple,
262    &'static Builtin => ConValue::Builtin,
263}
264impl From<()> for ConValue {
265    fn from(_: ()) -> Self {
266        Self::Empty
267    }
268}
269impl From<&[ConValue]> for ConValue {
270    fn from(value: &[ConValue]) -> Self {
271        match value {
272            [] => Self::Empty,
273            [value] => value.clone(),
274            _ => Self::Tuple(value.into()),
275        }
276    }
277}
278
279/// Implements binary [std::ops] traits for [ConValue]
280///
281/// TODO: Desugar operators into function calls
282macro ops($($trait:ty: $fn:ident = [$($match:tt)*])*) {
283    $(impl $trait for ConValue {
284        type Output = IResult<Self>;
285        /// TODO: Desugar operators into function calls
286        fn $fn(self, rhs: Self) -> Self::Output {Ok(match (self, rhs) {$($match)*})}
287    })*
288}
289ops! {
290    Add: add = [
291        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
292        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_add(b)),
293        (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a + b),
294        (ConValue::Str(a), ConValue::Str(b)) => (a.to_string() + &*b).into(),
295        (ConValue::Str(a), ConValue::String(b)) => (a.to_string() + &*b).into(),
296        (ConValue::String(a), ConValue::Str(b)) => (a.to_string() + &*b).into(),
297        (ConValue::String(a), ConValue::String(b)) => (a.to_string() + &*b).into(),
298        (ConValue::Str(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(c); s.into() }
299        (ConValue::String(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(c); s.into() }
300        (ConValue::Char(a), ConValue::Char(b)) => {
301            ConValue::String([a, b].into_iter().collect::<String>())
302        }
303        _ => Err(Error::TypeError())?
304        ]
305    BitAnd: bitand = [
306        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
307        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
308        (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b),
309        _ => Err(Error::TypeError())?
310    ]
311    BitOr: bitor = [
312        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
313        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
314        (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
315        _ => Err(Error::TypeError())?
316    ]
317    BitXor: bitxor = [
318        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
319        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
320        (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
321        _ => Err(Error::TypeError())?
322    ]
323    Div: div = [
324        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
325        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_div(b).unwrap_or_else(|| {
326            eprintln!("Warning: Divide by zero in {a} / {b}"); a
327        })),
328        (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a / b),
329        _ => Err(Error::TypeError())?
330    ]
331    Mul: mul = [
332        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
333        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_mul(b)),
334        (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a * b),
335        _ => Err(Error::TypeError())?
336    ]
337    Rem: rem = [
338        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
339        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_rem(b).unwrap_or_else(|| {
340            println!("Warning: Divide by zero in {a} % {b}"); a
341        })),
342        (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a % b),
343        _ => Err(Error::TypeError())?
344    ]
345    Shl: shl = [
346        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
347        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_shl(b as _)),
348        _ => Err(Error::TypeError())?
349    ]
350    Shr: shr = [
351        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
352        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_shr(b as _)),
353        _ => Err(Error::TypeError())?
354    ]
355    Sub: sub = [
356        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
357        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_sub(b)),
358        (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a - b),
359        _ => Err(Error::TypeError())?
360    ]
361}
362impl std::fmt::Display for ConValue {
363    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
364        match self {
365            ConValue::Empty => "Empty".fmt(f),
366            ConValue::Int(v) => v.fmt(f),
367            ConValue::Float(v) => v.fmt(f),
368            ConValue::Bool(v) => v.fmt(f),
369            ConValue::Char(v) => v.fmt(f),
370            ConValue::Str(v) => v.fmt(f),
371            ConValue::String(v) => v.fmt(f),
372            ConValue::Ref(v) => write!(f, "&<{}>", v),
373            ConValue::Slice(id, len) => write!(f, "&<{id}>[{len}..]"),
374            ConValue::Array(array) => {
375                '['.fmt(f)?;
376                for (idx, element) in array.iter().enumerate() {
377                    if idx > 0 {
378                        ", ".fmt(f)?
379                    }
380                    element.fmt(f)?
381                }
382                ']'.fmt(f)
383            }
384            ConValue::Tuple(tuple) => {
385                '('.fmt(f)?;
386                for (idx, element) in tuple.iter().enumerate() {
387                    if idx > 0 {
388                        ", ".fmt(f)?
389                    }
390                    element.fmt(f)?
391                }
392                ')'.fmt(f)
393            }
394            ConValue::TupleStruct(id, tuple) => {
395                write!(f, "{id}")?;
396                '('.fmt(f)?;
397                for (idx, element) in tuple.iter().enumerate() {
398                    if idx > 0 {
399                        ", ".fmt(f)?
400                    }
401                    element.fmt(f)?
402                }
403                ')'.fmt(f)
404            }
405            ConValue::Struct(id, map) => {
406                use std::fmt::Write;
407                write!(f, "{id} ")?;
408                let mut f = f.delimit_with("{", "\n}");
409                for (k, v) in map.iter() {
410                    write!(f, "\n{k}: {v},")?;
411                }
412                Ok(())
413            }
414            ConValue::Module(module) => {
415                use std::fmt::Write;
416                let mut f = f.delimit_with("{", "\n}");
417                for (k, v) in module.iter() {
418                    write!(f, "\n{k}: {v},")?;
419                }
420                Ok(())
421            }
422            ConValue::Quote(q) => {
423                write!(f, "`{q}`")
424            }
425            ConValue::Function(func) => {
426                write!(f, "{}", func.decl())
427            }
428            ConValue::TupleConstructor(Constructor { name: index, arity }) => {
429                write!(f, "{index}(..{arity})")
430            }
431            ConValue::Closure(func) => {
432                write!(f, "{}", func.as_ref())
433            }
434            ConValue::Builtin(func) => {
435                write!(f, "{}", func)
436            }
437        }
438    }
439}
440
441pub macro cvstruct (
442    $Name:ident {
443        $($member:ident : $expr:expr),*
444    }
445) {{
446    let mut members = HashMap::new();
447    $(members.insert(stringify!($member).into(), ($expr).into());)*
448    ConValue::Struct(Box::new((stringify!($Name).into(), members)))
449}}