Skip to main content

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, fmt::FmtAdapter, types::Symbol};
5use cl_structures::intern::interned::Interned;
6
7use crate::{
8    place::Place,
9    typeinfo::{Model, Type, TypeInfo},
10};
11
12use super::{
13    Callable, Environment,
14    builtin::Builtin,
15    error::{Error, IResult},
16    function::Function,
17};
18use std::{collections::HashMap, ops::*, rc::Rc};
19
20/*
21A Value can be:
22- A Primitive (Empty, isize, etc.)
23- A Record (Array, Tuple, Struct)
24- A Variant (discriminant, Value) pair
25
26array [
27    10,     // 0
28    20,     // 1
29]
30
31tuple (
32    10,     // 0
33    20,     // 1
34)
35
36struct {
37    x: 10,  // x => 0
38    y: 20,  // y => 1
39}
40*/
41
42type Integer = i128;
43
44/// A Conlang value stores data in the interpreter
45#[derive(Clone, Debug, Default)]
46pub enum ConValue {
47    /// The empty/unit `()` type
48    #[default]
49    Empty,
50    /// An integer
51    Int(Integer),
52    /// A floating point number
53    Float(f64),
54    /// A boolean
55    Bool(bool),
56    /// A unicode character
57    Char(char),
58    /// A string literal
59    Str(Symbol),
60    /// A dynamic string
61    String(String),
62    /// A reference
63    Ref(Place),
64    /// A reference to an array
65    Slice(Place, usize),
66    /// An Array
67    Array(Box<[ConValue]>),
68    /// A tuple
69    Tuple(Box<[ConValue]>),
70    // TODO: Instead of storing the identifier, store the index of the struct module
71    /// A value of a product type
72    Struct(Type, Box<HashMap<Symbol, ConValue>>),
73    /// A value of a product type with anonymous members
74    TupleStruct(Type, Box<[ConValue]>),
75    /// An entire namespace
76    Module(Box<HashMap<Symbol, ConValue>>),
77    /// A quoted expression
78    Quote(Rc<Expr>),
79    /// A callable thing
80    Function(Rc<Function>),
81    /// A built-in function
82    Builtin(&'static Builtin),
83    /// The definition of a type, by index
84    TypeInfo(Type),
85}
86
87impl ConValue {
88    /// Gets whether the current value is true or false
89    pub fn truthy(&self) -> IResult<bool> {
90        match self {
91            ConValue::Bool(v) => Ok(*v),
92            ConValue::Int(v) => Ok(*v != 0),
93            _ => Err(Error::TypeError("type implements Truth", self.typename()))?,
94        }
95    }
96
97    pub fn take(&mut self) -> Self {
98        std::mem::take(self)
99    }
100
101    pub fn is_cheap_to_copy(&self) -> bool {
102        match self {
103            Self::Empty
104            | Self::Int(_)
105            | Self::Float(_)
106            | Self::Bool(_)
107            | Self::Char(_)
108            | Self::Str(_) => true,
109            Self::Ref(_) => true,
110            Self::Slice(_, _) => true,
111            Self::Quote(_) => true,
112            Self::Function(_) => true,
113            Self::Builtin(_) => true,
114            Self::TypeInfo(_) => true,
115            Self::Module(_)
116            | Self::String(_)
117            | Self::Array(_)
118            | Self::Tuple(_)
119            | Self::Struct(_, _)
120            | Self::TupleStruct(_, _) => false,
121        }
122    }
123
124    pub fn typename(&self) -> &'static str {
125        match self {
126            ConValue::Empty => "Empty",
127            ConValue::Int(_) => "i64",
128            ConValue::Float(_) => "f64",
129            ConValue::Bool(_) => "bool",
130            ConValue::Char(_) => "char",
131            ConValue::Str(_) => "str",
132            ConValue::String(_) => "String",
133            ConValue::Ref(_) => "Ref",
134            ConValue::Slice(_, _) => "Slice",
135            ConValue::Array(_) => "Array",
136            ConValue::Tuple(_) => "Tuple",
137            ConValue::Struct(ty, _) => ty.name(),
138            ConValue::TupleStruct(ty, _) => ty.name(),
139            ConValue::Module(_) => "",
140            ConValue::Quote(_) => "Quote",
141            ConValue::Function(_) => "Fn",
142            ConValue::Builtin(_) => "Fn",
143            ConValue::TypeInfo(ty) => ty.name(),
144        }
145    }
146
147    pub fn cast(self, ty: &TypeInfo) -> Self {
148        match &ty.model {
149            &Model::Integer { signed, size, min, max } => {
150                let i = match self {
151                    Self::Int(v) => v,
152                    Self::Float(v) => v as _,
153                    Self::Bool(v) => v as _,
154                    Self::Char(v) => v as _,
155                    Self::TypeInfo(Interned(TypeInfo { model: Model::Unit(d), .. }, ..)) => *d as _,
156                    _ => return self,
157                };
158                if i == min || i == max {
159                    self
160                } else if signed {
161                    ConValue::Int(i.wrapping_rem(max))
162                } else {
163                    ConValue::Int(i & max)
164                }
165            }
166            Model::Float { size } => {
167                let f = match self {
168                    Self::Float(v) => v,
169                    Self::Int(v) => v as _,
170                    Self::Bool(v) => v as i32 as _,
171                    Self::Char(v) => v as i32 as _,
172                    Self::TypeInfo(Interned(TypeInfo { model: Model::Unit(d), .. }, ..)) => *d as _,
173                    _ => return self,
174                };
175                ConValue::Float(f)
176            }
177            Model::Bool => ConValue::Bool(self.truthy().unwrap_or(true)),
178            Model::Char => {
179                let c = match self {
180                    Self::Int(v) => v as _,
181                    Self::Float(v) => v as _,
182                    Self::Bool(v) => v as _,
183                    Self::Char(v) => return self,
184                    Self::TypeInfo(Interned(TypeInfo { model: Model::Unit(d), .. }, ..)) => *d as _,
185                    _ => return self,
186                };
187                ConValue::Char(char::from_u32(c).unwrap_or('�'))
188            }
189            Model::Unit(_) => ConValue::Empty,
190            Model::Str => ConValue::String(self.to_string()),
191            _ => self,
192        }
193    }
194
195    pub fn dereference_in<'e>(&'e self, env: &'e Environment) -> IResult<&'e Self> {
196        let mut value = self;
197        while let ConValue::Ref(r) = value {
198            value = r.get(env)?;
199        }
200        Ok(value)
201    }
202
203    #[allow(non_snake_case)]
204    pub fn tuple_struct(id: Type, values: impl Into<Box<[ConValue]>>) -> Self {
205        Self::TupleStruct(id, values.into())
206    }
207    #[allow(non_snake_case)]
208    pub fn Struct(id: Type, values: HashMap<Symbol, ConValue>) -> Self {
209        Self::Struct(id, Box::new(values))
210    }
211
212    pub fn index(self, index: &Self, _env: &Environment) -> IResult<ConValue> {
213        let &Self::Int(index) = index else {
214            Err(Error::TypeError("int", index.typename()))?
215        };
216        match self {
217            ConValue::Str(string) => string
218                .chars()
219                .nth(index as _)
220                .map(ConValue::Char)
221                .ok_or(Error::OobIndex(index as usize, string.chars().count())),
222            ConValue::String(string) => string
223                .chars()
224                .nth(index as _)
225                .map(ConValue::Char)
226                .ok_or(Error::OobIndex(index as usize, string.chars().count())),
227            ConValue::Array(arr) => arr
228                .get(index as usize)
229                .cloned()
230                .ok_or(Error::OobIndex(index as usize, arr.len())),
231            ConValue::Slice(place, len) => {
232                if (index.unsigned_abs() as usize) < len {
233                    Ok(ConValue::Ref(place.index(index as _, index < 0)))
234                } else {
235                    Err(Error::OobIndex(index.unsigned_abs() as _, len))
236                }
237            }
238            ConValue::Ref(place) => Ok(ConValue::Ref(
239                place.index(index.unsigned_abs() as _, index < 0),
240            )),
241            other => Err(Error::TypeError("type implements Index", other.typename())),
242        }
243    }
244    cmp! {
245        lt: <;
246        lt_eq: <=;
247        eq: ==;
248        neq: !=;
249        gt_eq: >=;
250        gt: >;
251    }
252    assign! {
253        add_assign: +;
254        bitand_assign: &;
255        bitor_assign: |;
256        bitxor_assign: ^;
257        div_assign: /;
258        mul_assign: *;
259        rem_assign: %;
260        shl_assign: <<;
261        shr_assign: >>;
262        sub_assign: -;
263    }
264}
265
266impl Callable for ConValue {
267    fn name(&self) -> Option<Symbol> {
268        match self {
269            ConValue::Function(func) => func.name(),
270            // ConValue::Closure(func) => func.name(),
271            ConValue::Builtin(func) => func.name(),
272            _ => None,
273        }
274    }
275    fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
276        match self {
277            Self::Function(func) => func.call(env, args),
278            Self::Builtin(func) => func.call(env, args),
279            Self::Module(m) => {
280                if let Some(func) = m.get(&"call".into()) {
281                    func.call(env, args)
282                } else {
283                    Err(Error::NotCallable(self.clone()))
284                }
285            }
286            Self::Ref(ptr) => {
287                // Move onto stack, and call
288                let func = ptr.get(env)?.clone();
289                func.call(env, args)
290            }
291            Self::TypeInfo(idx) => idx.call(env, args),
292            _ => Err(Error::NotCallable(self.clone())),
293        }
294    }
295}
296
297macro into_inner($($fn:ident: $);*$(;)?) {}
298
299/// Templates comparison functions for [ConValue]
300macro cmp ($($fn:ident: $op:tt);*$(;)?) {$(
301    /// TODO: Remove when functions are implemented:
302    ///       Desugar into function calls
303    pub fn $fn(&self, other: &Self) -> IResult<Self> {
304        match (self, other) {
305            (Self::Empty, Self::Empty) => Ok(Self::Bool(() $op ())),
306            (Self::Int(a), Self::Int(b)) => Ok(Self::Bool(a $op b)),
307            (Self::Float(a), Self::Float(b)) => Ok(Self::Bool(a $op b)),
308            (Self::Bool(a), Self::Bool(b)) => Ok(Self::Bool(a $op b)),
309            (Self::Char(a), Self::Char(b)) => Ok(Self::Bool(a $op b)),
310            (Self::Str(a), Self::Str(b)) => Ok(Self::Bool(&**a $op &**b)),
311            (Self::Str(a), Self::String(b)) => Ok(Self::Bool(&**a $op &**b)),
312            (Self::String(a), Self::Str(b)) => Ok(Self::Bool(&**a $op &**b)),
313            (Self::String(a), Self::String(b)) => Ok(Self::Bool(&**a $op &**b)),
314            (Self::TypeInfo(a), Self::TypeInfo(b)) => Ok(Self::Bool(&*a $op &*b)),
315            (a, _) => Err(Error::TypeError("type implements Cmp", a.typename()))?,
316        }
317    }
318)*}
319macro assign($( $fn: ident: $op: tt );*$(;)?) {$(
320    pub fn $fn(&mut self, other: Self) -> IResult<()> {
321        *self = (std::mem::take(self) $op other)?;
322        Ok(())
323    }
324)*}
325/// Implements [From] for an enum with 1-tuple variants
326macro from ($($T:ty => $v:expr),*$(,)?) {
327    $(impl From<$T> for ConValue {
328        fn from(value: $T) -> Self { $v(value.into()) }
329    })*
330}
331impl From<&Symbol> for ConValue {
332    fn from(value: &Symbol) -> Self {
333        ConValue::Str(*value)
334    }
335}
336impl From<Rc<Symbol>> for ConValue {
337    fn from(value: Rc<Symbol>) -> Self {
338        ConValue::Str(value.0.into())
339    }
340}
341from! {
342    Integer => ConValue::Int,
343    f64 => ConValue::Float,
344    bool => ConValue::Bool,
345    char => ConValue::Char,
346    Symbol => ConValue::Str,
347    &str => ConValue::Str,
348    Expr => ConValue::Quote,
349    String => ConValue::String,
350    Function => ConValue::Function,
351    Vec<ConValue> => ConValue::Tuple,
352    &'static Builtin => ConValue::Builtin,
353}
354impl From<()> for ConValue {
355    fn from(_: ()) -> Self {
356        Self::Empty
357    }
358}
359impl From<&[ConValue]> for ConValue {
360    fn from(value: &[ConValue]) -> Self {
361        match value {
362            [] => Self::Empty,
363            [value] => value.clone(),
364            _ => Self::Tuple(value.into()),
365        }
366    }
367}
368
369/// Implements binary [std::ops] traits for [ConValue]
370///
371/// TODO: Desugar operators into function calls
372macro ops($($trait:ty: $fn:ident = [$($match:tt)*])*) {
373    $(impl $trait for ConValue {
374        type Output = IResult<Self>;
375        /// TODO: Desugar operators into function calls
376        fn $fn(self, rhs: Self) -> Self::Output {Ok(match (self, rhs) {$($match)*})}
377    })*
378}
379ops! {
380    Add: add = [
381        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
382        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_add(b)),
383        (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a + b),
384        (ConValue::Str(a), ConValue::Str(b)) => (a.to_string() + &*b).into(),
385        (ConValue::Str(a), ConValue::String(b)) => (a.to_string() + &*b).into(),
386        (ConValue::String(a), ConValue::Str(b)) => (a.to_string() + &*b).into(),
387        (ConValue::String(a), ConValue::String(b)) => (a.to_string() + &*b).into(),
388        (ConValue::Str(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(c); s.into() }
389        (ConValue::String(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(c); s.into() }
390        (ConValue::Char(a), ConValue::Char(b)) => {
391            ConValue::String([a, b].into_iter().collect::<String>())
392        }
393        (a, b) => Err(Error::TypeError(a.typename(), b.typename()))?
394    ]
395    BitAnd: bitand = [
396        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
397        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
398        (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b),
399        (a, b) => Err(Error::TypeError(a.typename(), b.typename()))?
400    ]
401    BitOr: bitor = [
402        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
403        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
404        (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
405        (a, b) => Err(Error::TypeError(a.typename(), b.typename()))?
406    ]
407    BitXor: bitxor = [
408        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
409        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
410        (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
411        (a, b) => Err(Error::TypeError(a.typename(), b.typename()))?
412    ]
413    Div: div = [
414        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
415        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_div(b).unwrap_or_else(|| {
416            eprintln!("Warning: Divide by zero in {a} / {b}"); a
417        })),
418        (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a / b),
419        (a, b) => Err(Error::TypeError(a.typename(), b.typename()))?
420    ]
421    Mul: mul = [
422        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
423        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_mul(b)),
424        (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a * b),
425        (a, b) => Err(Error::TypeError(a.typename(), b.typename()))?
426    ]
427    Rem: rem = [
428        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
429        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_rem(b).unwrap_or_else(|| {
430            println!("Warning: Divide by zero in {a} % {b}"); a
431        })),
432        (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a % b),
433        (a, b) => Err(Error::TypeError(a.typename(), b.typename()))?
434    ]
435    Shl: shl = [
436        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
437        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_shl(b as _)),
438        (a, ConValue::Int(_)) => Err(Error::TypeError("type implements Shl", a.typename()))?,
439        (_, b) => Err(Error::TypeError("int", b.typename()))?
440    ]
441    Shr: shr = [
442        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
443        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_shr(b as _)),
444        (a, ConValue::Int(_)) => Err(Error::TypeError("type implements Shr", a.typename()))?,
445        (_, b) => Err(Error::TypeError("int", b.typename()))?
446    ]
447    Sub: sub = [
448        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
449        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_sub(b)),
450        (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a - b),
451        (a, b) => Err(Error::TypeError(a.typename(), b.typename()))?
452    ]
453}
454impl std::fmt::Display for ConValue {
455    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
456        match self {
457            ConValue::Empty => "Empty".fmt(f),
458            ConValue::Int(v) => v.fmt(f),
459            ConValue::Float(v) => v.fmt(f),
460            ConValue::Bool(v) => v.fmt(f),
461            ConValue::Char(v) => v.fmt(f),
462            ConValue::Str(v) => v.fmt(f),
463            ConValue::String(v) => v.fmt(f),
464            ConValue::Ref(v) => write!(f, "&<{}>", v),
465            ConValue::Slice(id, len) => write!(f, "&<{id}>[{len}..]"),
466            ConValue::Array(array) => f.delimit('[', ']').list(array, ", "),
467            ConValue::Tuple(tuple) => f.delimit('(', ')').list(tuple, ", "),
468            ConValue::TupleStruct(id, tuple) => f
469                .delimit(format_args!("{} (", id.name()), ")")
470                .list(tuple, ", "),
471            ConValue::Struct(id, map) => {
472                use std::fmt::Write;
473                write!(f, "{} ", id.name())?;
474                let mut f = f.delimit_indented("{", "\n}");
475                for (k, v) in map.iter() {
476                    write!(f, "\n{k}: {v},")?;
477                }
478                Ok(())
479            }
480            ConValue::Module(module) => {
481                use std::fmt::Write;
482                let mut f = f.delimit("{", "\n}");
483                for (k, v) in module.iter() {
484                    write!(f, "\n{k}: {v},")?;
485                }
486                Ok(())
487            }
488            ConValue::Quote(q) => write!(f, "`{q}`"),
489            ConValue::Function(func) => func.fmt(f),
490            ConValue::Builtin(func) => func.fmt(f),
491            ConValue::TypeInfo(ty) => ty.fmt(f),
492        }
493    }
494}
495
496pub macro cvstruct (
497    $Name:ident {
498        $($member:ident : $expr:expr),*
499    }
500) {{
501    let mut members = HashMap::new();
502    $(members.insert(stringify!($member).into(), ($expr).into());)*
503    ConValue::Struct(Box::new((stringify!($Name).into(), members)))
504}}