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}