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.ident.to_ref(),
138            ConValue::TupleStruct(ty, _) => ty.ident.to_ref(),
139            ConValue::Module(_) => "",
140            ConValue::Quote(_) => "Quote",
141            ConValue::Function(_) => "Fn",
142            ConValue::Builtin(_) => "Fn",
143            ConValue::TypeInfo(ty) => ty.ident.to_ref(),
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    #[allow(non_snake_case)]
196    pub fn tuple_struct(id: Type, values: impl Into<Box<[ConValue]>>) -> Self {
197        Self::TupleStruct(id, values.into())
198    }
199    #[allow(non_snake_case)]
200    pub fn Struct(id: Type, values: HashMap<Symbol, ConValue>) -> Self {
201        Self::Struct(id, Box::new(values))
202    }
203
204    pub fn index(self, index: &Self, _env: &Environment) -> IResult<ConValue> {
205        let &Self::Int(index) = index else {
206            Err(Error::TypeError("int", index.typename()))?
207        };
208        match self {
209            ConValue::Str(string) => string
210                .chars()
211                .nth(index as _)
212                .map(ConValue::Char)
213                .ok_or(Error::OobIndex(index as usize, string.chars().count())),
214            ConValue::String(string) => string
215                .chars()
216                .nth(index as _)
217                .map(ConValue::Char)
218                .ok_or(Error::OobIndex(index as usize, string.chars().count())),
219            ConValue::Array(arr) => arr
220                .get(index as usize)
221                .cloned()
222                .ok_or(Error::OobIndex(index as usize, arr.len())),
223            ConValue::Slice(place, len) => {
224                if (index.unsigned_abs() as usize) < len {
225                    Ok(ConValue::Ref(place.index(index as _, index < 0)))
226                } else {
227                    Err(Error::OobIndex(index.unsigned_abs() as _, len))
228                }
229            }
230            ConValue::Ref(place) => Ok(ConValue::Ref(
231                place.index(index.unsigned_abs() as _, index < 0),
232            )),
233            other => Err(Error::TypeError("type implements Index", other.typename())),
234        }
235    }
236    cmp! {
237        lt: false, <;
238        lt_eq: true, <=;
239        eq: true, ==;
240        neq: false, !=;
241        gt_eq: true, >=;
242        gt: false, >;
243    }
244    assign! {
245        add_assign: +;
246        bitand_assign: &;
247        bitor_assign: |;
248        bitxor_assign: ^;
249        div_assign: /;
250        mul_assign: *;
251        rem_assign: %;
252        shl_assign: <<;
253        shr_assign: >>;
254        sub_assign: -;
255    }
256}
257
258impl Callable for ConValue {
259    fn name(&self) -> Option<Symbol> {
260        match self {
261            ConValue::Function(func) => func.name(),
262            // ConValue::Closure(func) => func.name(),
263            ConValue::Builtin(func) => func.name(),
264            _ => None,
265        }
266    }
267    fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
268        match self {
269            Self::Function(func) => func.call(env, args),
270            Self::Builtin(func) => func.call(env, args),
271            Self::Module(m) => {
272                if let Some(func) = m.get(&"call".into()) {
273                    func.call(env, args)
274                } else {
275                    Err(Error::NotCallable(self.clone()))
276                }
277            }
278            Self::Ref(ptr) => {
279                // Move onto stack, and call
280                let func = ptr.get(env)?.clone();
281                func.call(env, args)
282            }
283            Self::TypeInfo(idx) => idx.call(env, args),
284            _ => Err(Error::NotCallable(self.clone())),
285        }
286    }
287}
288/// Templates comparison functions for [ConValue]
289macro cmp ($($fn:ident: $empty:literal, $op:tt);*$(;)?) {$(
290    /// TODO: Remove when functions are implemented:
291    ///       Desugar into function calls
292    pub fn $fn(&self, other: &Self) -> IResult<Self> {
293        match (self, other) {
294            (Self::Empty, Self::Empty) => Ok(Self::Bool($empty)),
295            (Self::Int(a), Self::Int(b)) => Ok(Self::Bool(a $op b)),
296            (Self::Float(a), Self::Float(b)) => Ok(Self::Bool(a $op b)),
297            (Self::Bool(a), Self::Bool(b)) => Ok(Self::Bool(a $op b)),
298            (Self::Char(a), Self::Char(b)) => Ok(Self::Bool(a $op b)),
299            (Self::Str(a), Self::Str(b)) => Ok(Self::Bool(&**a $op &**b)),
300            (Self::Str(a), Self::String(b)) => Ok(Self::Bool(&**a $op &**b)),
301            (Self::String(a), Self::Str(b)) => Ok(Self::Bool(&**a $op &**b)),
302            (Self::String(a), Self::String(b)) => Ok(Self::Bool(&**a $op &**b)),
303            (Self::TypeInfo(a), Self::TypeInfo(b)) => Ok(Self::Bool(&*a $op &*b)),
304            (a, _) => Err(Error::TypeError("type implements Cmp", a.typename()))?,
305        }
306    }
307)*}
308macro assign($( $fn: ident: $op: tt );*$(;)?) {$(
309    pub fn $fn(&mut self, other: Self) -> IResult<()> {
310        *self = (std::mem::take(self) $op other)?;
311        Ok(())
312    }
313)*}
314/// Implements [From] for an enum with 1-tuple variants
315macro from ($($T:ty => $v:expr),*$(,)?) {
316    $(impl From<$T> for ConValue {
317        fn from(value: $T) -> Self { $v(value.into()) }
318    })*
319}
320impl From<&Symbol> for ConValue {
321    fn from(value: &Symbol) -> Self {
322        ConValue::Str(*value)
323    }
324}
325impl From<Rc<Symbol>> for ConValue {
326    fn from(value: Rc<Symbol>) -> Self {
327        ConValue::Str(value.0.into())
328    }
329}
330from! {
331    Integer => ConValue::Int,
332    f64 => ConValue::Float,
333    bool => ConValue::Bool,
334    char => ConValue::Char,
335    Symbol => ConValue::Str,
336    &str => ConValue::Str,
337    Expr => ConValue::Quote,
338    String => ConValue::String,
339    Function => ConValue::Function,
340    Vec<ConValue> => ConValue::Tuple,
341    &'static Builtin => ConValue::Builtin,
342}
343impl From<()> for ConValue {
344    fn from(_: ()) -> Self {
345        Self::Empty
346    }
347}
348impl From<&[ConValue]> for ConValue {
349    fn from(value: &[ConValue]) -> Self {
350        match value {
351            [] => Self::Empty,
352            [value] => value.clone(),
353            _ => Self::Tuple(value.into()),
354        }
355    }
356}
357
358/// Implements binary [std::ops] traits for [ConValue]
359///
360/// TODO: Desugar operators into function calls
361macro ops($($trait:ty: $fn:ident = [$($match:tt)*])*) {
362    $(impl $trait for ConValue {
363        type Output = IResult<Self>;
364        /// TODO: Desugar operators into function calls
365        fn $fn(self, rhs: Self) -> Self::Output {Ok(match (self, rhs) {$($match)*})}
366    })*
367}
368ops! {
369    Add: add = [
370        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
371        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_add(b)),
372        (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a + b),
373        (ConValue::Str(a), ConValue::Str(b)) => (a.to_string() + &*b).into(),
374        (ConValue::Str(a), ConValue::String(b)) => (a.to_string() + &*b).into(),
375        (ConValue::String(a), ConValue::Str(b)) => (a.to_string() + &*b).into(),
376        (ConValue::String(a), ConValue::String(b)) => (a.to_string() + &*b).into(),
377        (ConValue::Str(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(c); s.into() }
378        (ConValue::String(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(c); s.into() }
379        (ConValue::Char(a), ConValue::Char(b)) => {
380            ConValue::String([a, b].into_iter().collect::<String>())
381        }
382        (a, b) => Err(Error::TypeError(a.typename(), b.typename()))?
383    ]
384    BitAnd: bitand = [
385        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
386        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
387        (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b),
388        (a, b) => Err(Error::TypeError(a.typename(), b.typename()))?
389    ]
390    BitOr: bitor = [
391        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
392        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
393        (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
394        (a, b) => Err(Error::TypeError(a.typename(), b.typename()))?
395    ]
396    BitXor: bitxor = [
397        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
398        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
399        (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
400        (a, b) => Err(Error::TypeError(a.typename(), b.typename()))?
401    ]
402    Div: div = [
403        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
404        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_div(b).unwrap_or_else(|| {
405            eprintln!("Warning: Divide by zero in {a} / {b}"); a
406        })),
407        (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a / b),
408        (a, b) => Err(Error::TypeError(a.typename(), b.typename()))?
409    ]
410    Mul: mul = [
411        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
412        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_mul(b)),
413        (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a * b),
414        (a, b) => Err(Error::TypeError(a.typename(), b.typename()))?
415    ]
416    Rem: rem = [
417        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
418        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_rem(b).unwrap_or_else(|| {
419            println!("Warning: Divide by zero in {a} % {b}"); a
420        })),
421        (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a % b),
422        (a, b) => Err(Error::TypeError(a.typename(), b.typename()))?
423    ]
424    Shl: shl = [
425        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
426        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_shl(b as _)),
427        (a, ConValue::Int(_)) => Err(Error::TypeError("type implements Shl", a.typename()))?,
428        (_, b) => Err(Error::TypeError("int", b.typename()))?
429    ]
430    Shr: shr = [
431        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
432        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_shr(b as _)),
433        (a, ConValue::Int(_)) => Err(Error::TypeError("type implements Shr", a.typename()))?,
434        (_, b) => Err(Error::TypeError("int", b.typename()))?
435    ]
436    Sub: sub = [
437        (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
438        (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_sub(b)),
439        (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a - b),
440        (a, b) => Err(Error::TypeError(a.typename(), b.typename()))?
441    ]
442}
443impl std::fmt::Display for ConValue {
444    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
445        match self {
446            ConValue::Empty => "Empty".fmt(f),
447            ConValue::Int(v) => v.fmt(f),
448            ConValue::Float(v) => v.fmt(f),
449            ConValue::Bool(v) => v.fmt(f),
450            ConValue::Char(v) => v.fmt(f),
451            ConValue::Str(v) => v.fmt(f),
452            ConValue::String(v) => v.fmt(f),
453            ConValue::Ref(v) => write!(f, "&<{}>", v),
454            ConValue::Slice(id, len) => write!(f, "&<{id}>[{len}..]"),
455            ConValue::Array(array) => f.delimit('[', ']').list(array, ", "),
456            ConValue::Tuple(tuple) => f.delimit('(', ')').list(tuple, ", "),
457            ConValue::TupleStruct(id, tuple) => f
458                .delimit(format_args!("{} (", id.ident), ")")
459                .list(tuple, ", "),
460            ConValue::Struct(id, map) => {
461                use std::fmt::Write;
462                write!(f, "{} ", id.ident)?;
463                let mut f = f.delimit_indented("{", "\n}");
464                for (k, v) in map.iter() {
465                    write!(f, "\n{k}: {v},")?;
466                }
467                Ok(())
468            }
469            ConValue::Module(module) => {
470                use std::fmt::Write;
471                let mut f = f.delimit("{", "\n}");
472                for (k, v) in module.iter() {
473                    write!(f, "\n{k}: {v},")?;
474                }
475                Ok(())
476            }
477            ConValue::Quote(q) => write!(f, "`{q}`"),
478            ConValue::Function(func) => func.fmt(f),
479            ConValue::Builtin(func) => func.fmt(f),
480            ConValue::TypeInfo(ty) => ty.fmt(f),
481        }
482    }
483}
484
485pub macro cvstruct (
486    $Name:ident {
487        $($member:ident : $expr:expr),*
488    }
489) {{
490    let mut members = HashMap::new();
491    $(members.insert(stringify!($member).into(), ($expr).into());)*
492    ConValue::Struct(Box::new((stringify!($Name).into(), members)))
493}}