tan/
expr.rs

1pub mod expr_iter;
2pub mod expr_transform;
3
4use std::{
5    any::Any,
6    collections::{HashMap, HashSet},
7    fmt,
8    hash::{Hash, Hasher},
9    sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard},
10};
11
12use rust_decimal::Decimal;
13
14use crate::{
15    context::Context,
16    error::Error,
17    lexer::comment::CommentKind,
18    module::Module,
19    range::{Position, Range},
20    scope::Scope,
21    util::{expect_lock_read, expect_lock_write, fmt::format_float},
22};
23
24// #todo Introduce separate Sync, Send, Sync+Send versions of Expr?
25
26// #todo #important optimization, implement clone_from!
27
28// #todo Make some Expr variants non annotatable (e.g. U8), what about range?
29
30// #todo introduce Expr::Ref() with an Rc reference to avoid excessive cloning!
31
32// #insight
33// Annotations are only for named bindings, static-time pseudo-annotations for
34// literals are resolved before dynamic-time, Expr::Ann() is useful for that! But,
35// we can probably skip most expr.unpack()s.
36
37// #insight Use structs for enum values, is more ..structured, readable and can have methods.
38
39// #insight Rc/Arc is used instead of Box to support Clone.
40
41// #todo Separate variant for list and apply/call (can this be defined statically?)
42// #todo List, MaybeList, Call
43// #todo Expr::Range()
44
45// #insight
46// AST = Expr = Value = Object
47
48// #insight
49// The use of Vec in the Expr enum, keeps the nested expressions in the heap.
50
51// #insight No need for a Zero/Never/Nothing Expr variant?
52
53// #todo What would be the 'default'? -> the 'Unit'/'One' type, Nil!
54// #todo Consider parsing to 'simple' Expr, only List and Symbols
55// #todo Optimize 'simple' Expr to 'execution' Expr
56// #todo Introduce ForeignValue?
57// #todo ExprFn should get a single Expr? -> nah, it's foreign.
58
59// #todo not all Expr variants really need Ann, maybe the annotation should be internal to Expr?
60
61// #todo consider Visitor pattern instead of enum?
62
63// #todo for ForeignFn
64// #todo consider &mut and & Context, different types!
65// #todo consider version with no Context
66// #todo find a better name for the type-alias
67// #todo add an option that gets a 'self' expression to allow for e.g. assert! implementation (uses self annotations)
68// #todo maybe should pass 'self' to all foreign functions?
69
70// #insight the `+ Send + Sync + 'static` suffix allows Expr to be Sync.
71
72/// A function that accepts a list of Exprs, returns maybe an Expr.
73pub type FnNoContext = dyn Fn(&[Expr]) -> Result<Expr, Error> + Send + Sync + 'static;
74// #insight Context is needed when the foreing func deals with a Tan func.
75pub type FnContext = dyn Fn(&[Expr], &Context) -> Result<Expr, Error> + Send + Sync + 'static;
76pub type FnMutContext =
77    dyn Fn(&[Expr], &mut Context) -> Result<Expr, Error> + Send + Sync + 'static;
78
79#[derive(Clone)]
80pub enum ForeignFnRef {
81    NoContext(&'static FnNoContext),
82    Context(&'static FnContext),
83    MutContext(&'static FnMutContext),
84}
85
86// #todo Use normal structs instead of tuple-structs?
87
88// #todo Add Expr::Date
89// #todo Add Expr::Panic (catched by the runtime, should support unwind)
90
91// #insight Maybe.None == Nil == Unit
92// #insight (Maybe T) = (Or T Nil)
93
94// #todo Probably the Any/Never (i.e. Top/Bottom) types should not be encoded in Expr.
95
96/// A symbolic expression. This is the 'universal' data type in the language,
97/// all values are expressions (and expressions are values). Evaluation is expression
98/// rewriting to a fixed point.
99#[derive(Default, Clone)]
100pub enum Expr {
101    // --- Low-level ---
102    // #todo Any <> Nothing or even Anything <> Nothing, better keep the shorter Any
103    // Any is the Top type.
104    // Any,
105    // `Never` is the Bottom type (Zero). It's the empty type, a type without instances.
106    // #insight Never is the 'zero' in algebraic sense (x+0 = x, x*0 = 0)
107    // #insight In the Curry–Howard correspondence, an empty type corresponds to falsity.
108    // #insight the Bottom type is the dual to the Top type (Any)
109    Never,
110    // `None` is the Unit type (One). It's a type with a single instance, and thus carries no information.
111    // The single instance of `None` is `()` (none).
112    // #insight Python also uses the term `None`.
113    // #insight `()` is used to avoid reserving `nil`.
114    // #insight Nil/Unit/One is the 'one' in algebraic sense (x+1 != 0, x*1 = x)
115    // #insight Unit == One, and it _is_ 'one' in the algebraic sense
116    // #insight None = (N)one
117    // #insight preferred None over nil to play well with Maybe{Some,None}
118    // #insight None is the default Expr value.
119    #[default]
120    None,
121    // #todo Rethink the transient concept.
122    // #insight Add these variants to is_transient.
123    // Transient/Analysis variants {
124    Comment(String, CommentKind), // #todo consider renaming to Remark (REM)
125    TextSeparator,                // for the formatter.
126    Annotation(String),
127    // } Transient/Analysis
128    Bool(bool), // #todo remove?
129    // #todo consider `Byte`, `UInt8`?
130    U8(u8),
131    Int(i64),
132    Float(f64),
133    Dec(Decimal),
134    Symbol(String),    // #todo consider renaming to Expr::Sym
135    KeySymbol(String), // #todo consider renaming to Expr::Key
136    Char(char),
137    String(String),
138    // #todo currently a special String for types.
139    // #todo consider Typ
140    // #todo Make sure types are unpacked as strings, not symbols.
141    Type(String),
142    // #todo better name for 'generic' List, how about `Cons` or `ConsList` or `Cell`?
143    // #todo add 'quoted' List -> Array!
144    // #todo do we really need Vec here? Maybe Arc<[Expr]> is enough?
145    List(Vec<Expr>),
146    Array(Arc<RwLock<Vec<Expr>>>), // #insight 'reference' type
147    // #todo Consider Vec<u8> -> Box<[u8]> to make non-resizable.
148    Buffer(usize, Arc<RwLock<Vec<u8>>>), // #insight 'reference' type
149    // #todo different name?
150    // #todo support Expr as keys?
151    Map(Arc<RwLock<HashMap<String, Expr>>>),
152    Set(Arc<RwLock<HashSet<Expr>>>),
153    // #todo support `start..` and `..end` ranges.
154    // #todo open-ended range with step can look like this: `start../2`
155    // #todo have type render as (Range Int)
156    IntRange(i64, i64, i64), // start, end, step #todo use a struct here,
157    // #todo have type render as (Range Float)
158    FloatRange(f64, f64, f64), // start, end, step #todo use a struct here,
159    // Range(...),
160    // #todo the Func should probably store the Module environment.
161    // #todo maybe should have explicit do block?
162    /// Func(params, body, func_scope, filename)
163    Func(Vec<Expr>, Vec<Expr>, Arc<Scope>, String),
164    // #todo add file_path to Macro
165    // #todo maybe should have explicit do block?
166    /// Macro(params, body)
167    Macro(Vec<Expr>, Vec<Expr>),
168    // #todo add file_path to ForeignFunc
169    // #todo the ForeignFunc should probably store the Module environment.
170    // #todo introduce a ForeignFuncMut for mutating scope? what would be a better name?
171    // #todo #optimization: I could use symbol table for foreing funcs and just put an integer index here!
172    // #todo Instead of passing an enum, we could have 3 ForeignFunc variants.
173    ForeignFunc(ForeignFnRef), // #todo for some reason, Box is not working here!
174    // #todo consider renaming to just `Foreign`,
175    // #todo consider adding type-name field?
176    // #todo to optimize consider using an index into a table of type-names.
177    // #todo support both mutable and immutable foreignStructs
178    // #todo Support non-sync data?
179    // #todo Remove the 'static?
180    Foreign(Arc<dyn Any + Send + Sync>),
181    ForeignMut(Arc<RwLock<dyn Any + Send + Sync>>),
182    Error(String),
183    // --- High-level ---
184    // #todo do should contain the expressions also, pre-parsed!
185    Do,
186    // #todo let should contain the expressions also, pre-parsed!
187    Let,
188    // #todo maybe this 'compound' if prohibits homoiconicity?
189    If(Box<Expr>, Box<Expr>, Option<Box<Expr>>),
190    Annotated(Box<Expr>, HashMap<String, Expr>),
191    // #todo maybe use annotation in Expr for public/exported? no Vec<String> for exported?
192    // #todo convert free-expression into pseudo-function?
193    // Module(HashMap<String, Expr>, Vec<String>, Vec<Expr>), // bindings, public/exported, free-expressions.
194    Module(Arc<Module>),
195}
196
197impl Eq for Expr {}
198
199// #todo think some more about this.
200impl PartialEq for Expr {
201    fn eq(&self, other: &Self) -> bool {
202        match (self, other) {
203            (Self::Comment(l0, l1), Self::Comment(r0, r1)) => l0 == r0 && l1 == r1,
204            (Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
205            (Self::Int(l0), Self::Int(r0)) => l0 == r0,
206            (Self::Float(l0), Self::Float(r0)) => l0 == r0,
207            (Self::Dec(l0), Self::Dec(r0)) => l0 == r0,
208            (Self::Symbol(l0), Self::Symbol(r0)) => l0 == r0,
209            (Self::KeySymbol(l0), Self::KeySymbol(r0)) => l0 == r0,
210            (Self::Type(l0), Self::Type(r0)) => l0 == r0,
211            (Self::Char(l0), Self::Char(r0)) => l0 == r0,
212            (Self::String(l0), Self::String(r0)) => l0 == r0,
213            (Self::List(l0), Self::List(r0)) => l0 == r0,
214            // #todo maybe should leave for the default discriminant case?
215            // #todo equality not supported for Array, due to RwLock.
216            // (Self::Array(..), Self::Array(..)) => false,
217            // (Self::Array(l0), Self::Array(r0)) => l0 == r0,
218            // #todo equality not supported for Map, due to RwLock.
219            // (Self::Map(l0), Self::Map(r0)) => l0 == r0,
220            (Self::IntRange(l0, l1, l2), Self::IntRange(r0, r1, r2)) => {
221                l0 == r0 && l1 == r1 && l2 == r2
222            }
223            (Self::FloatRange(l0, l1, l2), Self::FloatRange(r0, r1, r2)) => {
224                l0 == r0 && l1 == r1 && l2 == r2
225            }
226            (Self::Func(..), Self::Func(..)) => false,
227            (Self::Macro(l0, l1), Self::Macro(r0, r1)) => l0 == r0 && l1 == r1,
228            (Self::ForeignFunc(..), Self::ForeignFunc(..)) => false,
229            (Self::Foreign(..), Self::Foreign(..)) => false,
230            (Self::If(l0, l1, l2), Self::If(r0, r1, r2)) => l0 == r0 && l1 == r1 && l2 == r2,
231            // #todo #think should unpack and ignore annotations?
232            (Self::Annotated(l0, _l1), Self::Annotated(r0, _r1)) => l0.eq(r0),
233            (Self::Module(..), Self::Module(..)) => false,
234            _ => core::mem::discriminant(self) == core::mem::discriminant(other),
235        }
236    }
237}
238
239impl Hash for Expr {
240    fn hash<H: Hasher>(&self, state: &mut H) {
241        match self {
242            Self::Int(n) => {
243                0.hash(state);
244                n.hash(state);
245            }
246            Self::String(s) => {
247                0.hash(state);
248                s.hash(state);
249            }
250            Self::Annotated(inner, _) => inner.hash(state),
251            // Expr::Zero => todo!(),
252            // Expr::One => todo!(),
253            // Expr::Comment(_, _) => todo!(),
254            // Expr::TextSeparator => todo!(),
255            // Expr::Bool(_) => todo!(),
256            // Expr::Int(_) => todo!(),
257            // Expr::Float(_) => todo!(),
258            // Expr::Dec(_) => todo!(),
259            // Expr::Symbol(_) => todo!(),
260            // Expr::KeySymbol(_) => todo!(),
261            // Expr::Char(_) => todo!(),
262            // Expr::String(_) => todo!(),
263            // Expr::Type(_) => todo!(),
264            // Expr::List(_) => todo!(),
265            // Expr::Array(_) => todo!(),
266            // Expr::Map(_) => todo!(),
267            // Expr::Set(_) => todo!(),
268            // Expr::IntRange(_, _, _) => todo!(),
269            // Expr::FloatRange(_, _, _) => todo!(),
270            // Expr::Func(_, _, _, _) => todo!(),
271            // Expr::Macro(_, _) => todo!(),
272            // Expr::ForeignFunc(_) => todo!(),
273            // Expr::ForeignStruct(_) => todo!(),
274            // Expr::Do => todo!(),
275            // Expr::Let => todo!(),
276            // Expr::If(_, _, _) => todo!(),
277            // Expr::Annotated(_, _) => todo!(),
278            // Expr::Module(_) => todo!(),
279            _ => {
280                println!("******** no hash computation: {self}");
281            }
282        }
283    }
284}
285
286// #todo what is the Expr default? One (Unit/Any) or Zero (Noting/Never)
287// #todo
288// use Sexp notation here. actually not really, maybe it's good as it is,
289// it's more a view into the Rust/Foreign wold.
290impl fmt::Debug for Expr {
291    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292        let text = match self {
293            Expr::Never => "⊥".to_owned(), // #todo maybe use an ASCII representation, e.g. `!` or `!!`
294            // #insight `None` is more readable than `()` in the debug context.
295            Expr::None => "None".to_owned(),
296            Expr::Comment(s, _) => format!("Comment({s})"),
297            Expr::TextSeparator => "<TEXT-SEPARATOR>".to_owned(),
298            Expr::Bool(b) => format!("Bool({b})"),
299            Expr::Symbol(s) => format!("'{s}"), // "Symbol(s)"
300            Expr::KeySymbol(s) => format!("KeySymbol({s})"),
301            Expr::Type(s) => format!("Type({s})"),
302            Expr::Char(c) => format!("Char({c})"),
303            Expr::String(s) => format!("String(\"{s}\")"),
304            Expr::U8(num) => format!("U8({num})"),
305            Expr::Int(num) => format!("Int({num})"),
306            Expr::Float(num) => format!("Float({})", format_float(*num)),
307            Expr::Dec(num) => format!("Dec({num})"),
308            Expr::Do => "do".to_owned(),
309            Expr::List(terms) => {
310                format!(
311                    "List(\n{})",
312                    terms
313                        .iter()
314                        .map(|term| format!("{term:?}"))
315                        .collect::<Vec<String>>()
316                        .join(",\n")
317                )
318            }
319            Expr::Buffer(size, v) => format!("Buffer({size}, {v:?})"),
320            Expr::Array(v) => format!("Array({:?})", v.read().expect("poisoned lock")),
321            Expr::Map(d) => format!("Map({d:?})"),
322            Expr::Set(d) => format!("Set({d:?})"),
323            Expr::IntRange(start, end, step) => format!("IntRange({start},{end},{step})"),
324            Expr::FloatRange(start, end, step) => format!("FloatRange({start},{end},{step})"),
325            Expr::Func(..) => "<FUNC>".to_owned(),
326            Expr::Macro(..) => "<MACRO>".to_owned(),
327            Expr::ForeignFunc(..) => "<FOREIGN-FUNC>".to_owned(),
328            Expr::Foreign(..) => "<FOREIGN>".to_owned(),
329            Expr::ForeignMut(..) => "<FOREIGN-MUT>".to_owned(),
330            // #todo find a better name than `reason`.
331            // #todo add support for wrapping upstream errors
332            // #todo add support for wrapping foreign (rust) errors
333            Expr::Error(reason) => format!("Error({reason})"),
334            Expr::Let => "let".to_owned(),
335            // #todo properly format do, let, if, etc.
336            Expr::If(_, _, _) => "if".to_owned(),
337            // #todo uncomment only for debugging purposes!
338            // Expr::Annotated(expr, ann) => format!("ANN({expr:?}, {ann:?})"),
339            // #insight intentionally ignore annotations in formatting the formatting.
340            Expr::Annotated(expr, _ann) => format!("#({expr:?})"), // "Ann({expr:?})"
341            Expr::Annotation(ann) => format!("Annotation(#{ann})"),
342            Expr::Module(module) => format!("Module({})", module.stem),
343        };
344
345        write!(f, "{text}")
346    }
347}
348
349impl fmt::Display for Expr {
350    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
351        // #todo optimize this!
352        f.write_str(
353            (match self {
354                Expr::Never => "⊥".to_owned(),
355                Expr::None => "()".to_owned(),
356                Expr::Comment(s, _) => format!(r#"(rem "{s}")"#), // #todo what would be a good representation?
357                Expr::TextSeparator => "<TS>".to_owned(),
358                Expr::Bool(b) => b.to_string(),
359                Expr::U8(n) => n.to_string(),
360                Expr::Int(n) => n.to_string(),
361                Expr::Float(n) => format_float(*n),
362                Expr::Dec(n) => format!("(Dec {n})"), // #todo 'literal', e.f. 1.23d or #Dec 1.23
363                Expr::Symbol(s) => s.clone(),
364                Expr::KeySymbol(s) => format!(":{s}"),
365                Expr::Type(s) => s.clone(),
366                Expr::Char(c) => format!(r#"(Char "{c}")"#), // #todo no char literal?
367                Expr::String(s) => format!("\"{s}\""),
368                Expr::Error(reason) => format!(r#"(Error "{reason}")"#),
369                Expr::Do => "do".to_owned(),
370                Expr::Let => "let".to_owned(),
371                // #todo properly format if!
372                Expr::If(..) => "if".to_owned(),
373                Expr::List(terms) => {
374                    format!(
375                        "({})",
376                        terms
377                            .iter()
378                            .map(|term| format!("{}", term))
379                            .collect::<Vec<String>>()
380                            .join(" ")
381                    )
382                }
383                Expr::Buffer(_size, bytes) => {
384                    let exprs = expect_lock_read(bytes)
385                        .iter()
386                        .map(|expr| expr.to_string())
387                        .collect::<Vec<String>>()
388                        .join(" ");
389                    format!("[{exprs}]")
390                }
391                Expr::Array(exprs) => {
392                    let exprs = expect_lock_read(exprs)
393                        .iter()
394                        .map(|expr| expr.to_string())
395                        .collect::<Vec<String>>()
396                        .join(" ");
397                    format!("[{exprs}]")
398                }
399                Expr::Map(map) => {
400                    // #todo Map should support arbitrary exprs (or at lease `(Into String)` exprs)
401                    // #todo currently we convert keys to symbol, make this more subtle.
402                    let exprs = expect_lock_read(map)
403                        .iter()
404                        .map(|(k, v)| format!(":{k} {v}"))
405                        .collect::<Vec<String>>()
406                        .join(" ");
407                    format!("{{{exprs}}}")
408                }
409                Expr::Set(set) => {
410                    // #todo Map should support arbitrary exprs (or at lease `(Into String)` exprs)
411                    // #todo currently we convert keys to symbol, make this more subtle.
412                    let exprs = expect_lock_read(set)
413                        .iter()
414                        .map(|v| format!("{v}"))
415                        .collect::<Vec<String>>()
416                        .join(" ");
417                    format!("[{exprs}]")
418                }
419                Expr::IntRange(start, end, step) => {
420                    if *step == 1 {
421                        format!("{start}..{end}")
422                    } else {
423                        // #insight cannot use `/`
424                        // #todo consider using `:` or `,` instead of `|`?
425                        format!("{start}..{end}|{step}")
426                    }
427                }
428                Expr::FloatRange(start, end, step) => {
429                    if *step == 1.0 {
430                        format!("{start}..{end}")
431                    } else {
432                        // #insight cannot use `/`
433                        // #todo consider using `:` or `,` instead of `|`?
434                        format!("{start}..{end}|{step}")
435                    }
436                }
437                // Expr::Func(..) => "#<func>".to_owned(),
438                Expr::Func(params, ..) => format!("<FUNC {:?}>", params),
439                // Expr::Func(params, body, ..) => format!("<FUNC {:?} -> {:?}>", params, body), // #hint Useful for debugging.
440                Expr::Macro(..) => "<MACRO>".to_owned(),
441                Expr::ForeignFunc(..) => "<FOREIGN-FUNC>>".to_owned(),
442                Expr::Foreign(..) => "<FOREIGN>".to_owned(),
443                Expr::ForeignMut(..) => "<FOREIGN-MUT>".to_owned(),
444                // #insight intentionally pass through the formatting.
445                Expr::Annotated(expr, _) => format!("{expr}"),
446                Expr::Annotation(ann) => format!("#{ann}"),
447                Expr::Module(module) => format!("Module({})", module.stem),
448            })
449            .as_str(),
450        )
451    }
452}
453
454impl AsRef<Expr> for Expr {
455    fn as_ref(&self) -> &Expr {
456        self
457    }
458}
459
460impl Expr {
461    pub fn symbol(s: impl Into<String>) -> Self {
462        Expr::Symbol(s.into())
463    }
464
465    pub fn key_symbol(s: impl Into<String>) -> Self {
466        Expr::KeySymbol(s.into())
467    }
468
469    pub fn string(s: impl Into<String>) -> Self {
470        Expr::String(s.into())
471    }
472
473    pub fn error(s: impl Into<String>) -> Self {
474        Expr::Error(s.into())
475    }
476
477    pub fn typ(s: impl Into<String>) -> Self {
478        Expr::Type(s.into())
479    }
480
481    pub fn array(a: impl Into<Vec<Expr>>) -> Self {
482        Expr::Array(Arc::new(RwLock::new(a.into())))
483    }
484
485    pub fn map(m: impl Into<HashMap<String, Expr>>) -> Self {
486        Expr::Map(Arc::new(RwLock::new(m.into())))
487    }
488
489    pub fn set(s: impl Into<HashSet<Expr>>) -> Self {
490        Expr::Set(Arc::new(RwLock::new(s.into())))
491    }
492
493    // #todo Consider adding `_no_context` suffix, or better remove the NoContext in the types.
494    pub fn foreign_func(f: &'static FnNoContext) -> Self {
495        Expr::ForeignFunc(ForeignFnRef::NoContext(f))
496    }
497
498    pub fn foreign_func_mut_context(f: &'static FnMutContext) -> Self {
499        Expr::ForeignFunc(ForeignFnRef::MutContext(f))
500    }
501
502    // #todo Add `foreign_struct` and `foreign_struct_mut` helpers?
503
504    pub fn annotated(expr: Expr, annotations: &HashMap<String, Expr>) -> Self {
505        // #insight don't override existing annotations.
506        let mut expr = expr;
507        if let Some(current_annotations) = expr.annotations_mut() {
508            for (k, v) in annotations {
509                if !current_annotations.contains_key(k) {
510                    current_annotations.insert(k.clone(), v.clone());
511                }
512            }
513            expr
514        } else {
515            // #todo do something about this clone!!
516            Expr::Annotated(Box::new(expr), annotations.clone())
517        }
518    }
519
520    pub fn maybe_annotated(expr: Expr, annotations: Option<&HashMap<String, Expr>>) -> Self {
521        if let Some(annotations) = annotations {
522            Self::annotated(expr, annotations)
523        } else {
524            expr
525        }
526    }
527}
528
529impl Expr {
530    pub fn annotations(&self) -> Option<&HashMap<String, Expr>> {
531        match self {
532            Expr::Annotated(_, ann) => Some(ann),
533            _ => None,
534        }
535    }
536
537    pub fn annotations_mut(&mut self) -> Option<&mut HashMap<String, Expr>> {
538        match self {
539            Expr::Annotated(_, ann) => Some(ann),
540            _ => None,
541        }
542    }
543
544    // #todo name unpack? / project?
545    pub fn extract(&self) -> (&Expr, Option<&HashMap<String, Expr>>) {
546        match self {
547            Expr::Annotated(expr, ann) => (expr, Some(ann)),
548            _ => (self, None),
549        }
550    }
551
552    // #todo name unwrap?
553    // #todo unpack is very dangerous, we need to encode in the typesystem that the expr is unpacked.
554    // #todo unwrap into tuple (expr, ann)
555    // #todo find better name?
556    /// Removes the annotation from an expression.
557    #[inline]
558    pub fn unpack(&self) -> &Self {
559        match self {
560            Expr::Annotated(expr, _) => expr,
561            _ => self,
562        }
563    }
564
565    #[inline]
566    pub fn unpack_consuming(self) -> Self {
567        match self {
568            Expr::Annotated(expr, _) => *expr,
569            _ => self,
570        }
571    }
572
573    pub fn annotation(&self, name: impl Into<String>) -> Option<&Expr> {
574        match self {
575            Expr::Annotated(_, ann) => ann.get(&name.into()),
576            _ => None,
577        }
578    }
579
580    // #todo do we _really_ want Expr::Nil as value/variant?
581    // #todo rename to is_none
582    // #todo is_one/is_unit
583    pub fn is_none(&self) -> bool {
584        matches!(self.unpack(), Expr::None)
585    }
586
587    // #todo do we really need this? or we should always use `is_invocable`?
588    pub fn is_func(&self) -> bool {
589        matches!(self.unpack(), Expr::Func(..))
590    }
591
592    pub fn is_invocable(&self) -> bool {
593        matches!(
594            self.unpack(),
595            Expr::Func(..) | Expr::ForeignFunc(..) | Expr::Type(..)
596        )
597    }
598
599    // #todo remove TextSeparator concept.
600    // #todo find a better name.
601    // Returns true if the expresion is 'transient'/'inept' i.e. it will
602    // be stripped before evaluation. Transient helpers are currently used
603    // for analysis, not evaluation.
604    pub fn is_transient(&self) -> bool {
605        matches!(
606            self.unpack(),
607            Expr::Comment(..) | Expr::TextSeparator | Expr::Annotation(..)
608        )
609    }
610
611    // #insight
612    // We provide is_false() instead of is_true() as in the future we _may_
613    // consider all non-false values as true.
614    pub fn is_false(&self) -> bool {
615        matches!(self.unpack(), Expr::Bool(false))
616    }
617
618    // #todo consider #[inline]
619    pub fn as_int(&self) -> Option<i64> {
620        let Expr::Int(n) = self.unpack() else {
621            return None;
622        };
623        Some(*n)
624    }
625
626    pub fn as_u8(&self) -> Option<u8> {
627        let Expr::U8(n) = self.unpack() else {
628            return None;
629        };
630        Some(*n)
631    }
632
633    pub fn as_float(&self) -> Option<f64> {
634        let Expr::Float(n) = self.unpack() else {
635            return None;
636        };
637        Some(*n)
638    }
639
640    pub fn as_float_range(&self) -> Option<std::ops::Range<f64>> {
641        let Expr::FloatRange(min, max, _) = self.unpack() else {
642            return None;
643        };
644        Some(*min..*max)
645    }
646
647    pub fn as_decimal(&self) -> Option<Decimal> {
648        let Expr::Dec(n) = self.unpack() else {
649            return None;
650        };
651        Some(*n)
652    }
653
654    pub fn as_bool(&self) -> Option<bool> {
655        let Expr::Bool(b) = self.unpack() else {
656            return None;
657        };
658        Some(*b)
659    }
660
661    pub fn as_string(&self) -> Option<&str> {
662        let Expr::String(s) = self.unpack() else {
663            return None;
664        };
665        Some(s)
666    }
667
668    // #todo
669    // pub fn as_string_mut(&self) -> Option<RefMut<'_, &String>> {
670    //     // #todo how to implement this?
671    //     todo!()
672    // }
673
674    // #insight https://en.wiktionary.org/wiki/stringable
675    pub fn as_stringable(&self) -> Option<&str> {
676        // #todo try to optimize away the unpacks.
677        let expr = self.unpack();
678
679        match expr {
680            Expr::Symbol(s) => Some(s),
681            Expr::KeySymbol(s) => Some(s),
682            Expr::String(s) => Some(s),
683            Expr::Type(s) => Some(s),
684            _ => None,
685        }
686    }
687
688    // #insight useful for optimizations.
689    pub fn as_stringable_consuming(self) -> Option<String> {
690        // #todo try to optimize away the unpacks.
691        let expr = self.unpack_consuming();
692
693        match expr {
694            Expr::Symbol(s) => Some(s),
695            Expr::KeySymbol(s) => Some(s),
696            Expr::String(s) => Some(s),
697            Expr::Type(s) => Some(s),
698            _ => None,
699        }
700    }
701
702    // #todo Add more is_XXX helpers!
703
704    pub fn is_symbol(&self) -> bool {
705        matches!(self.unpack(), Expr::Symbol(..))
706    }
707
708    pub fn as_symbol(&self) -> Option<&str> {
709        let Expr::Symbol(s) = self.unpack() else {
710            return None;
711        };
712        Some(s)
713    }
714
715    pub fn as_key_symbol(&self) -> Option<&str> {
716        let Expr::KeySymbol(s) = self.unpack() else {
717            return None;
718        };
719        Some(s)
720    }
721
722    // #todo can just make this the as_symbol impl.
723    /// Tries to extract Symbol or KeySymbol.
724    pub fn as_symbolic(&self) -> Option<&str> {
725        // #todo try to optimize away the unpacks.
726        let expr = self.unpack();
727
728        match expr {
729            Expr::Symbol(s) => Some(s),
730            Expr::KeySymbol(s) => Some(s),
731            Expr::Type(s) => Some(s),
732            _ => None,
733        }
734    }
735
736    pub fn as_type(&self) -> Option<&str> {
737        let Expr::Type(s) = self.unpack() else {
738            return None;
739        };
740        Some(s)
741    }
742
743    // #todo add an extra function to extract all string-
744
745    pub fn as_char(&self) -> Option<char> {
746        let Expr::Char(c) = self.unpack() else {
747            return None;
748        };
749        Some(*c)
750    }
751
752    pub fn as_list(&self) -> Option<&Vec<Expr>> {
753        let Expr::List(v) = self.unpack() else {
754            return None;
755        };
756        Some(v)
757    }
758
759    pub fn as_array(&self) -> Option<RwLockReadGuard<'_, Vec<Expr>>> {
760        let Expr::Array(v) = self.unpack() else {
761            return None;
762        };
763        // #todo what would be a good message?
764        // #todo extract as variable.
765        Some(expect_lock_read(v))
766    }
767
768    pub fn as_array_consuming(self) -> Option<Arc<RwLock<Vec<Expr>>>> {
769        let Expr::Array(v) = self.unpack_consuming() else {
770            return None;
771        };
772        Some(v)
773    }
774
775    // // #todo try to find a better name.
776    // pub fn as_seq(&self) -> Option<&Vec<Expr>> {
777    //     // #todo try to optimize away the unpacks.
778    //     let expr = self.unpack();
779
780    //     match expr {
781    //         Expr::List(v) => Some(v),
782    //         Expr::Array(v) => Some(???), // #todo how to implement this?
783    //         _ => None,
784    //     }
785    // }
786
787    pub fn as_array_mut(&self) -> Option<RwLockWriteGuard<'_, Vec<Expr>>> {
788        let Expr::Array(v) = self.unpack() else {
789            return None;
790        };
791        Some(expect_lock_write(v))
792    }
793
794    pub fn as_buffer(&self) -> Option<RwLockReadGuard<'_, Vec<u8>>> {
795        // #todo what to do with size/length?
796        let Expr::Buffer(_, v) = self.unpack() else {
797            return None;
798        };
799        // #todo what would be a good message?
800        // #todo extract as variable.
801        Some(expect_lock_read(v))
802    }
803
804    // #insight you _always_ need the length/size when mutating a buffer.
805    pub fn as_buffer_mut(&self) -> Option<(usize, RwLockWriteGuard<'_, Vec<u8>>)> {
806        // #todo what to do with size/length?
807        let Expr::Buffer(length, v) = self.unpack() else {
808            return None;
809        };
810        Some((*length, expect_lock_write(v)))
811    }
812
813    pub fn as_map(&self) -> Option<RwLockReadGuard<'_, HashMap<String, Expr>>> {
814        let Expr::Map(map) = self.unpack() else {
815            return None;
816        };
817        Some(expect_lock_read(map))
818    }
819
820    pub fn as_map_mut(&self) -> Option<RwLockWriteGuard<'_, HashMap<String, Expr>>> {
821        let Expr::Map(map) = self.unpack() else {
822            return None;
823        };
824        Some(expect_lock_write(map))
825    }
826
827    pub fn as_set(&self) -> Option<RwLockReadGuard<'_, HashSet<Expr>>> {
828        let Expr::Set(set) = self.unpack() else {
829            return None;
830        };
831        Some(expect_lock_read(set))
832    }
833
834    pub fn as_set_mut(&self) -> Option<RwLockWriteGuard<'_, HashSet<Expr>>> {
835        let Expr::Set(set) = self.unpack() else {
836            return None;
837        };
838        Some(expect_lock_write(set))
839    }
840
841    // // #todo consider #[inline]
842    // pub fn as_func(&self) -> Option<i64> {
843    //     let Expr::Func(params, body, scope, filename) = self.unpack() else {
844    //         return None;
845    //     };
846    //     Some(...)
847    // }
848
849    // // static vs dyn type.
850    // pub fn static_type(&self) -> Expr {
851    //     match self {
852    //         Expr::Int(_) => return Expr::symbol("Int"),
853    //         Expr::Float(_) => return Expr::symbol("Float"),
854    //         _ => return Expr::symbol("Unknown"),
855    //     }
856    // }
857
858    pub fn static_type(&self) -> &Expr {
859        self.annotation("type").unwrap_or(&Expr::None)
860    }
861
862    // #todo we need a version that returns just a string.
863
864    // #todo introduce a version that does not look-through Symbol, no Context required.
865    // #todo how about return &Expr to avoid clones?
866    // #todo alternatively consider key-symbol instead of String
867    // #insight use string for the type to support parameterized types, e.g (Map String Any)
868    // Returns the dynamic (eval-time) type of the expression.
869    pub fn dyn_type(&self, context: &Context) -> Expr {
870        // #todo make constant out of "type".
871        if let Some(typ) = self.annotation("type") {
872            // #todo why is the unpack needed?
873            return typ.unpack().clone();
874        }
875
876        match self.unpack() {
877            Expr::Never => Expr::typ("Never"), // Never, Zero
878            Expr::None => Expr::typ("None"),   // Unit, One, Nil
879            Expr::Bool(_) => Expr::typ("Bool"),
880            Expr::U8(_) => Expr::typ("U8"),
881            Expr::Int(_) => Expr::typ("Int"),
882            Expr::Float(_) => Expr::typ("Float"),
883            Expr::Dec(_) => Expr::typ("Dec"),
884            Expr::Char(_) => Expr::typ("Char"),
885            Expr::String(_) => Expr::typ("String"),
886            Expr::Type(_) => Expr::typ("Type"),
887            Expr::List(_) => Expr::typ("List"), // #todo return parameterized type
888            Expr::Array(_) => Expr::typ("Array"), // #todo return parameterized type
889            Expr::Buffer(..) => Expr::typ("Buffer"), // #todo return parameterized type
890            Expr::Map(_) => Expr::typ("Map"),   // #todo return parameterized type
891            Expr::Set(_) => Expr::typ("Set"),   // #todo return parameterized type
892            // #todo what about quoted Symbol?
893            Expr::Symbol(name) => {
894                // #todo it's weird that we look through symbols.
895                if let Some(value) = context.scope.get(name) {
896                    value.dyn_type(context)
897                } else {
898                    // #todo could use symbol here!
899                    Expr::typ("Unknown")
900                }
901            }
902            Expr::KeySymbol(..) => Expr::typ("KeySymbol"),
903            // #todo keep the Range type parameter as a ...parameter
904            Expr::IntRange(..) => Expr::typ("(Range Int)"),
905            Expr::FloatRange(..) => Expr::typ("(Range Float)"),
906            Expr::Func(..) => Expr::typ("Func"),
907            // #todo consider returning Func?
908            Expr::ForeignFunc(..) => Expr::typ("ForeignFunc"),
909            Expr::Error(..) => Expr::typ("Error"),
910            // #todo add more here!
911            // #todo the wildcard is very error-prone, cover all cases!
912            _ => {
913                eprintln!("WARNING dyn-type unknown ---> {self:?}");
914                Expr::typ("Unknown")
915            }
916        }
917    }
918
919    pub fn range(&self) -> Option<Range> {
920        self.annotation("range").map(expr_to_range)
921    }
922}
923
924impl From<i64> for Expr {
925    fn from(item: i64) -> Self {
926        Expr::Int(item)
927    }
928}
929
930impl From<f64> for Expr {
931    fn from(item: f64) -> Self {
932        Expr::Float(item)
933    }
934}
935
936// #todo impl TryFrom<Expr> for f64, i64, etc.
937
938#[must_use]
939pub fn annotate(mut expr: Expr, name: impl Into<String>, ann_expr: Expr) -> Expr {
940    let name = name.into();
941    match expr {
942        Expr::Annotated(_, ref mut ann) => {
943            ann.insert(name, ann_expr);
944            expr
945        }
946        expr => {
947            let mut ann = HashMap::new();
948            ann.insert(name, ann_expr);
949            Expr::Annotated(Box::new(expr), ann)
950        }
951    }
952}
953
954// #todo use special sigil for implicit/system annotations.
955
956#[must_use]
957pub fn annotate_type(expr: Expr, type_name: impl Into<String>) -> Expr {
958    // #todo String is not good, we need a symbol/key-symbol that supports spaces.
959    annotate(expr, "type", Expr::Type(type_name.into()))
960}
961
962// #insight it checks exclusively for annotation, maybe too error-prone.
963pub fn has_type_annotation(expr: &Expr, type_name: &str) -> bool {
964    // #todo should also check, dyn_type.
965    if let Some(typ) = expr.annotation("type") {
966        if let Some(name) = typ.as_stringable() {
967            name == type_name
968        } else {
969            false
970        }
971    } else {
972        false
973    }
974}
975
976// #todo we need a version without Context, duh!
977pub fn has_dyn_type(expr: &Expr, type_name: &str, context: &Context) -> bool {
978    let typ = expr.dyn_type(context);
979
980    if let Some(name) = typ.as_stringable() {
981        name == type_name
982    } else {
983        false
984    }
985}
986
987#[must_use]
988pub fn annotate_range(expr: Expr, range: Range) -> Expr {
989    annotate(expr, "range", range_to_expr(&range))
990}
991
992// #todo move elsewhere, e.g. api.
993// #todo think where this function is used. (it is used for Map keys, hmm...)
994// #todo this is a confusing name!
995/// Formats the expression as a value.
996/// For example strings are formatted without the quotes and keys without
997/// the `:` prefix.
998pub fn format_value(expr: impl AsRef<Expr>) -> String {
999    let expr = expr.as_ref();
1000    match expr {
1001        Expr::Float(n) => format_float(*n),
1002        Expr::Annotated(expr, _) => format_value(expr),
1003        Expr::String(s) => s.to_string(),
1004        Expr::KeySymbol(s) => s.to_string(),
1005        _ => expr.to_string(),
1006    }
1007}
1008
1009// #todo consider using Arc<Expr> everywhere?
1010// #todo proper name
1011// #todo proper value/reference handling for all types.
1012/// Clones expressions in optimized way, handles ref types.
1013pub fn expr_clone(expr: &Expr) -> Expr {
1014    match expr {
1015        // #insight treat Array and Map as a 'reference' types, Arc.clone is efficient.
1016        Expr::Array(items) => Expr::Array(items.clone()),
1017        Expr::Map(items) => Expr::Map(items.clone()),
1018        _ => expr.clone(),
1019    }
1020}
1021
1022// ---
1023
1024// #todo implement Defer into Expr!
1025
1026// #todo convert to the Expr::Range variant.
1027// #todo convert position to Map Expr.
1028
1029pub fn position_to_expr(position: &Position) -> Expr {
1030    let mut map: HashMap<String, Expr> = HashMap::new();
1031    map.insert("index".to_owned(), Expr::Int(position.index as i64));
1032    map.insert("line".to_owned(), Expr::Int(position.line as i64));
1033    map.insert("col".to_owned(), Expr::Int(position.col as i64));
1034    Expr::map(map)
1035}
1036
1037pub fn expr_to_position(expr: &Expr) -> Position {
1038    if let Some(map) = expr.as_map() {
1039        let Some(Expr::Int(index)) = map.get("index") else {
1040            // #todo fix me!
1041            return Position::default();
1042        };
1043
1044        let Some(Expr::Int(line)) = map.get("line") else {
1045            // #todo fix me!
1046            return Position::default();
1047        };
1048
1049        let Some(Expr::Int(col)) = map.get("col") else {
1050            // #todo fix me!
1051            return Position::default();
1052        };
1053
1054        return Position {
1055            index: *index as usize,
1056            line: *line as usize,
1057            col: *col as usize,
1058        };
1059    }
1060
1061    // #todo fix me!
1062    Position::default()
1063}
1064
1065pub fn range_to_expr(range: &Range) -> Expr {
1066    let start = position_to_expr(&range.start);
1067    let end = position_to_expr(&range.end);
1068
1069    Expr::array(vec![start, end])
1070}
1071
1072// #todo nasty code.
1073pub fn expr_to_range(expr: &Expr) -> Range {
1074    // #todo error checking?
1075    // let Expr::Array(terms) = expr else {
1076    //     // #todo hmm...
1077    //     return Range::default();
1078    // };
1079
1080    let Some(terms) = expr.as_array() else {
1081        // #todo hmm...
1082        return Range::default();
1083    };
1084
1085    Range {
1086        start: expr_to_position(&terms[0]),
1087        end: expr_to_position(&terms[1]),
1088    }
1089}
1090
1091// #insight Considering None = false, helps to simplify the code in many cases,
1092// and also allows for (if (let (Some x)) ...), let can return None in failed
1093// match.
1094/// Consider both Bool and None values for truthiness.
1095pub fn is_truthy(expr: &Expr) -> Option<bool> {
1096    // #insight To be safe, the unpack is required here.
1097    match expr.unpack() {
1098        Expr::Bool(b) => Some(*b),
1099        Expr::None => Some(false),
1100        // #insight Upstream code can use the None to emit errors.
1101        _ => None,
1102    }
1103}
1104
1105// #todo use `.into()` to convert Expr to Annotated<Expr>.
1106
1107#[cfg(test)]
1108mod tests {
1109    use crate::expr::Expr;
1110
1111    #[test]
1112    fn expr_string_display() {
1113        let expr = Expr::string("hello");
1114        assert_eq!("\"hello\"", format!("{expr}"));
1115    }
1116
1117    #[test]
1118    fn expr_float_display() {
1119        let expr = Expr::Float(3.21);
1120        assert_eq!("3.21", format!("{expr}"));
1121    }
1122
1123    #[test]
1124    fn expr_is_false() {
1125        assert!(Expr::Bool(false).is_false());
1126        assert!(!Expr::Bool(true).is_false());
1127    }
1128}