Skip to main content

swan_core/
evaluator.rs

1//! Evaluator: turns AST nodes into Swan `Value`s.
2//!
3//! # Entry point
4//! [`eval`] takes a `&Node` and a `&Scope` and returns a `Value`. It is
5//! **infallible**: every error (syntax errors, invalid names, type mismatches)
6//! is returned as a `Value::Undefined(...)`, and errors in sub-expressions
7//! propagate as ordinary values. It never panics.
8//!
9//! # What lives where
10//! This file handles literals, identifier lookup, grouping (`[]`, `{}`),
11//! function definition and application, pairing dispatch, mapping, assignment,
12//! labelling, subcontexting, promise chaining (`~>`), logic (`&`, `|`),
13//! conditional (`?`, `;`), composition (`<<`, `>>`) and syntax-error nodes.
14//! Operations with non-trivial logic are delegated to the sub-modules declared
15//! below (unary operators, pairing, assignment, `~>`, arithmetic and comparison).
16//!
17//! # Tuple semantics
18//! Tuples are flat and normalized (`Nothing` items are dropped). Operators are
19//! lifted over tuples by two internal macros:
20//! - `map_values!` for unary operations: `Nothing` gives `Nothing`, a tuple is
21//!   mapped item by item, any other value goes straight to the operation.
22//! - `map_pairs!` for binary operations: tuples are paired element-wise
23//!   (the shorter side is padded with `Nothing`) and the result is normalized.
24//!
25//! The short-circuit operators `&`, `|`, `?` and `;` are not lifted: they look
26//! at their left operand as a whole.
27//!
28//! # Evaluation order
29//! Operands are evaluated eagerly, left then right. The exceptions are the
30//! short-circuit operators, whose right operand `B` is evaluated only when needed:
31//! `A & B` and `A ? B` when `A` is truthy, `A | B` when `A` is falsy, and
32//! `A ; B` when `A` is `Nothing`. `&`, `|` and `?` cast `A` to bool as a whole
33//! (a tuple is truthy if any of its items is), so they are not paired over tuples.
34//! For `->`, `=` and `:` the left operand is not evaluated; it is resolved into
35//! names by `eval_names`.
36//!
37//! # Scopes
38//! - `{X}` evaluates `X` in a new child scope and returns it as a `Namespace`.
39//! - `A -> B` captures the current scope as a closure; applying it evaluates
40//!   the body in a child of that closure scope.
41//! - `ns.X` evaluates `X` inside the scope of the namespace `ns`.
42
43use std::rc::Rc;
44use crate::ast::Node;
45use crate::value::{Scope, Value, Undef, BuiltinFn};
46
47
48// Unary operations
49/// Unary `+X`.
50mod affirm_operation;
51/// Unary `-X` (numeric negation).
52mod negation_operation;
53/// Unary `!X` (logical negation).
54mod logical_negation_operation;
55/// Unary `$X` (lifting: looks up names in the scope).
56mod lifting_operation;
57
58// Binary operations
59/// `X , Y` (tuple construction with flattening and normalization).
60mod pairing_operation;
61/// Binding of names to values; shared by `=`, `:` and function application.
62pub(crate) mod assignment_operation;
63/// `P ~> F` (promise chaining).
64mod then_operation;
65/// `+ - * / % ^` on numbers (and other supported types).
66mod arithmetic_operations;
67/// `== != > >= < <=`.
68mod comparison_operations;
69
70
71
72// ----------------------------------------------------------------------------
73//  MACROS
74// ----------------------------------------------------------------------------
75
76/// Evaluates a binary operation with element-wise tuple broadcasting.
77///
78/// If either operand is a [`Value::Tuple`], pairs the items using [`Value::pair`]
79/// and applies `$eval_fn` element-wise. Otherwise, applies `$eval_fn` directly.
80///
81/// Requires 3 arguments: `($lhv, $rhv, $eval_fn)`.
82/// The `$eval_fn` must be a callable taking `(Value, Value)`.
83/// If both operands are `Nothing` the result is `Nothing`.
84/// A `Nothing` against a non-tuple value is passed through to `$eval_fn` as is.
85/// The result of a tuple broadcast is normalized.
86///
87/// Requires `Value` to be in scope. Both operands are evaluated by the caller before the macro is used.
88macro_rules! map_pairs {
89    ($lhv:expr, $rhv:expr, $eval_fn:expr) => {{
90        let (lhv, rhv) = ($lhv, $rhv);
91        if matches!(lhv, Value::Tuple(_)) || matches!(rhv, Value::Tuple(_)) {
92            Value::tuple(
93                lhv.pair(&rhv)
94                    .map(|(l,r)| $eval_fn(l,r))
95                    .collect(),
96            )
97        } else if lhv == Value::Nothing && rhv == Value::Nothing {
98            Value::Nothing
99        } else {
100            $eval_fn(lhv, rhv)
101        }
102    }};
103}
104
105/// Applies `$eval_fn` (taking one `Value`) to a single operand.
106///
107/// `Nothing` gives `Nothing`.
108/// A `Tuple` is mapped item by item (flattened) and the result is normalized.
109/// Any other value is passed directly to `$eval_fn`.
110///
111/// The `$eval_fn` must be a callable taking one `Value`.
112/// Requires `Value` to be in scope.
113/// Note that `$val` is evaluated once and moved into the macro.
114macro_rules! map_values {
115    ($val:expr, $eval_fn:expr) => {{
116        let val = $val;
117        match val {
118            Value::Nothing => Value::Nothing,
119            Value::Tuple(_) => {
120                Value::tuple(
121                    val.items()
122                    .map($eval_fn)
123                    .collect()
124                )
125            }
126            _ => $eval_fn(val)
127        }
128    }};
129}
130
131
132
133// ----------------------------------------------------------------------------
134//  PUBLIC ENTRY POINT
135// ----------------------------------------------------------------------------
136
137/// Evaluates an AST node within a given `Scope` context and returns a `Value`.
138///
139/// This function is **infallible**: it never panics. All error conditions are
140/// encoded as `Value::Undefined(...)` so that Swan's "errors are values" semantics
141/// are preserved throughout, and sub-expression errors propagate as `Undefined` values.
142///
143/// # Arguments
144/// - `node`: the AST node to evaluate.
145/// - `scope`: the environment used for identifier lookup. Assignment and labelling
146///   write into this scope (not into a parent).
147///
148/// # Error values produced directly here
149/// - `Undef::Termination` for `Node::Unclosed` (missing closing bracket).
150/// - `Undef::Token` for `Node::UnexpectedToken`.
151/// - `Undef::BinaryOperation` named `"function_definition"`, `"assignment"` or
152///   `"labelling"` when the left side is not a valid name or tuple of names
153///   (the `lhv` holds the `Undef::Name` describing the offending node).
154/// - `Undef::BinaryOperation` named `"subcontexting"` when the left side is not
155///   a namespace.
156/// - `Undef::BinaryOperation` named `"application"` (see [`eval_apply`]).
157///
158/// Errors from the delegated sub-modules are documented there.
159///
160/// # Examples
161///
162/// ```
163/// use swan_core::builtins::root_scope;
164/// use swan_core::evaluator::eval;
165/// use swan_core::lexer::Lexer;
166/// use swan_core::parser::Parser;
167/// use swan_core::value::Value;
168///
169/// let scope = root_scope();
170/// let ast = Parser::new(Lexer::new(r#""a" - 1"#)).parse();
171/// assert!(matches!(eval(&ast, &scope), Value::Undefined(_)));
172/// ```
173pub fn eval(node: &Node, scope: &Scope) -> Value {
174    match node {
175
176
177        // ===== Literals / Leafs =====
178
179        // Numeric literal
180        Node::Numb(leaf)       => Value::Numb(leaf.val),
181
182        // String literal
183        Node::Text(leaf)       => Value::Text(leaf.val.as_str().into()),
184
185        // Nothing literal: `()` or empty expression
186        Node::Nothing(_)       => Value::Nothing,
187
188        // Identifier lookup
189        Node::Identifier(leaf) => scope.get(&leaf.val),
190
191
192
193        // ===== Grouping =====
194
195        // List creation: `[X]` - the evaluated operand is flattened into the list items (Nothing is skipped)
196        Node::List(unary) => {
197            let val = eval(&unary.opd, scope);
198            let vec: Vec<_> = val.items().collect();
199            Value::list(vec)
200        }
201
202        // Namespace creation: `{X}` - body is evaluated for side effects (bindings), result is discarded
203        Node::Namespace(unary) => {
204            let child_scope = scope.new_child();
205            eval(&unary.opd, &child_scope);
206            Value::Namespace(child_scope)
207        }
208
209
210
211        // ===== Unary Operations =====
212
213        // Unary affirm operation: `+X`
214        // (not broadcast via map_values!: the sub-module handles any value itself)
215        Node::AffirmOp(unary) => {
216            let val = eval(&unary.opd, scope);
217            affirm_operation::eval(val)
218        }
219
220        // Unary negation operation: `-X`
221        Node::NegateOp(unary) => {
222            let val = eval(&unary.opd, scope);
223            map_values!(val, |v| negation_operation::eval(v, unary.pos))
224        }
225
226        // Unary logical negation operation: `!X`
227        Node::LogicalNegationOp(unary) => {
228            let val = eval(&unary.opd, scope);
229            map_values!(val, logical_negation_operation::eval)
230        }
231
232        // Unary lifting operation: `$X`
233        Node::LiftingOp(unary) => {
234            let names = eval_names(&unary.opd);
235            map_values!(names, |n| lifting_operation::eval(n, scope, unary.pos))
236        }
237
238
239
240        // ===== Core Operations =====
241
242        // Function definition: `A -> B` - LHO is resolved as names, not evaluated. Captures current scope.
243        Node::FunctionDefinitionOp(bin) => {
244            let names = eval_names(&bin.lho);
245            if matches!(names, Value::Undefined(_)) {
246                invalid_names("function_definition", names, bin.pos)
247            } else {
248                Value::Func{
249                    params: Box::new(names),
250                    body: bin.rho.clone(),
251                    closure: scope.clone(),
252                }
253            }
254        }
255
256        // `f x` — function application.
257        Node::ApplicationOp(bin) => {
258            // The left-hand operand must evaluate to a callable (Builtin or Func).
259            // The right-hand operand is the argument (possibly a Tuple for multiple
260            // arguments).
261            // Functions are broadcast: for a tuple of callables each one receives the same whole argument.
262            let func = eval(&bin.lho, scope);
263            let args = eval(&bin.rho, scope);
264            map_values!(func, |f| eval_apply(f, args.clone(), bin.pos))
265        }
266
267        // Pairing: `X , Y` - flattens both sides into a normalized tuple.
268        Node::PairingOp(bin) => {
269            let lhv = eval(&bin.lho, scope);
270            let rhv = eval(&bin.rho, scope);
271            pairing_operation::eval(lhv, rhv)
272        }
273
274        // Mapping: `T => F` - outer `map_values!` iterates over args, inner over funcs. Every arg to every func.
275        Node::MappingOp(bin) => {
276            let args = eval(&bin.lho, scope);
277            let func = eval(&bin.rho, scope);
278            map_values!(args, |a: Value| map_values!(func.clone(), |f| eval_apply(f, a.clone(), bin.pos)))
279        }
280
281        // Promise chaining: `P ~> F` - each promise in `P` (tuples are broadcast) is chained to `F`; the result of each is a new promise.
282        Node::ThenOp(bin) => {
283            let promises = eval(&bin.lho, scope);
284            let func     = eval(&bin.rho, scope);
285            map_values!(promises, |p| then_operation::eval(p, func.clone(), bin.pos))
286        }
287
288        // Assignment: `name = V` - evaluates to Nothing, binds names in scope. Invalid names give an Undefined and nothing is bound.
289        Node::AssignmentOp(bin) => {
290            let names = eval_names(&bin.lho);
291            if matches!(names, Value::Undefined(_)) {
292                return invalid_names("assignment", names, bin.pos);
293            }
294
295            let values = eval(&bin.rho, scope);
296            assignment_operation::eval(scope, &names, &values);
297            Value::Nothing
298        }
299
300        // Labelling: `name : V` - evaluates to V, binds names in scope. Invalid names give an Undefined and nothing is bound.
301        Node::LabellingOp(bin) => {
302            let names = eval_names(&bin.lho);
303            if matches!(names, Value::Undefined(_)) {
304                return invalid_names("labelling", names, bin.pos);
305            }
306
307            let values = eval(&bin.rho, scope);
308            assignment_operation::eval(scope, &names, &values);
309            values
310        }
311
312        // `ns.expression` — namespace sub-contexting
313        Node::SubcontextingOp(bin) => {
314            // The left-hand side must evaluate to a `Value::Namespace`.
315            // The right-hand side may be any expression and is evaluated within the namespace's scope.
316            // A non-namespace left side gives an `Undefined`.
317            let nst = eval(&bin.lho, scope);
318            map_values!(nst, |ns| match ns {
319                Value::Namespace(ns_scope) => {
320                    eval(&bin.rho, &ns_scope)
321                }
322                lhv => Value::undef_binary("subcontexting", lhv, Value::Nothing, bin.pos),
323            })
324        }
325
326
327
328        // ===== Arithmetic Operations =====
329
330        // Sum: `A + B`
331        Node::SumOp(bin) => {
332            let lhv = eval(&bin.lho, scope);
333            let rhv = eval(&bin.rho, scope);
334            map_pairs!(lhv, rhv, |l,r| arithmetic_operations::eval_add(l, r, bin.pos))
335        }
336
337        // Subtraction: `A - B`
338        Node::SubtractionOp(bin) => {
339            let lhv = eval(&bin.lho, scope);
340            let rhv = eval(&bin.rho, scope);
341            map_pairs!(lhv, rhv, |l,r| arithmetic_operations::eval_subtraction(l, r, bin.pos))
342        }
343
344        // Product: `A * B`
345        Node::ProductOp(bin) => {
346            let lhv = eval(&bin.lho, scope);
347            let rhv = eval(&bin.rho, scope);
348            map_pairs!(lhv, rhv, |l,r| arithmetic_operations::eval_product(l, r, bin.pos))
349        }
350
351        // Division: `A / B`
352        Node::DivisionOp(bin) => {
353            let lhv = eval(&bin.lho, scope);
354            let rhv = eval(&bin.rho, scope);
355            map_pairs!(lhv, rhv, |l,r| arithmetic_operations::eval_division(l, r, bin.pos))
356        }
357
358        // Modulo: `A % B`
359        Node::ModuloOp(bin) => {
360            let lhv = eval(&bin.lho, scope);
361            let rhv = eval(&bin.rho, scope);
362            map_pairs!(lhv, rhv, |l,r| arithmetic_operations::eval_modulo(l, r, bin.pos))
363        }
364
365        // Exponentiation: `A ^ B`
366        Node::PowerOp(bin) => {
367            let lhv = eval(&bin.lho, scope);
368            let rhv = eval(&bin.rho, scope);
369            map_pairs!(lhv, rhv, |l,r| arithmetic_operations::eval_power(l, r, bin.pos))
370        }
371
372
373
374        // ===== Comparison Operations =====
375
376        // Equal: `A == B`
377        Node::EqualToOp(bin) => {
378            let lhv = eval(&bin.lho, scope);
379            let rhv = eval(&bin.rho, scope);
380            map_pairs!(lhv, rhv, |l,r| comparison_operations::eval_equal_to(l, r))
381        }
382
383        // Non-equal: `A != B`
384        Node::NotEqualToOp(bin) => {
385            let lhv = eval(&bin.lho, scope);
386            let rhv = eval(&bin.rho, scope);
387            map_pairs!(lhv, rhv, |l,r| comparison_operations::eval_not_equal_to(l, r))
388        }
389
390        // Greater-than: `A > B`
391        Node::GreaterThanOp(bin) => {
392            let lhv = eval(&bin.lho, scope);
393            let rhv = eval(&bin.rho, scope);
394            map_pairs!(lhv, rhv, |l,r| comparison_operations::eval_greater_than(l, r, bin.pos))
395        }
396
397        // Greater-than-or-equal: `A >= B`
398        Node::GreaterThanOrEqualToOp(bin) => {
399            let lhv = eval(&bin.lho, scope);
400            let rhv = eval(&bin.rho, scope);
401            map_pairs!(lhv, rhv, |l,r| comparison_operations::eval_greater_than_or_equal_to(l, r, bin.pos))
402        }
403
404        // Less than: `A < B`
405        Node::LessThanOp(bin) => {
406            let lhv = eval(&bin.lho, scope);
407            let rhv = eval(&bin.rho, scope);
408            map_pairs!(lhv, rhv, |l,r| comparison_operations::eval_less_than(l, r, bin.pos))
409        }
410
411        // Less-than-or-equal: `A <= B`
412        Node::LessThanOrEqualToOp(bin) => {
413            let lhv = eval(&bin.lho, scope);
414            let rhv = eval(&bin.rho, scope);
415            map_pairs!(lhv, rhv, |l,r| comparison_operations::eval_less_than_or_equal_to(l, r, bin.pos))
416        }
417
418
419
420        // ===== Logic Operations =====
421
422        // And: `A & B` - short-circuits: `B` is evaluated only if `A` is truthy.
423        // `A` is cast to bool as a whole (a tuple is truthy if any item is), there is no pairing.
424        Node::AndOp(bin) => {
425            let lhv = eval(&bin.lho, scope);
426            if bool::from(lhv.clone()) { eval(&bin.rho, scope) } else { lhv }
427        }
428
429        // Or: `A | B` - short-circuits: `B` is evaluated only if `A` is falsy.
430        // `A` is cast to bool as a whole, there is no pairing.
431        Node::OrOp(bin) => {
432            let lhv = eval(&bin.lho, scope);
433            if bool::from(lhv.clone()) { lhv } else { eval(&bin.rho, scope) }
434        }
435
436
437
438        // ===== Conditional operations =====
439
440        // Conditional: `A ? B` - short-circuits: `B` is evaluated only if `A` is truthy, else the result is `Nothing`.
441        // `A` is cast to bool as a whole, there is no pairing.
442        Node::ConditionalOp(bin) => {
443            let lhv = eval(&bin.lho, scope);
444            if bool::from(lhv) { eval(&bin.rho, scope) } else { Value::Nothing }
445        }
446
447        // Alternative: `A ; B` - RHO is evaluated lazily, and only if LHO is exactly Nothing (a tuple is never Nothing, so it is returned as is).
448        Node::AlternativeOp(bin) => {
449            let lhv = eval(&bin.lho, scope);
450            match lhv {
451                Value::Nothing => eval(&bin.rho, scope),
452                _ => lhv
453            }
454        }
455
456
457
458        // ===== Composition operations =====
459
460        // Composition: `F << G` - apply `G` first, then `F`.
461        Node::CompositionOp(bin) => {
462            let lhv = eval(&bin.lho, scope);
463            let rhv = eval(&bin.rho, scope);
464            map_pairs!(lhv, rhv, |l,r| eval_composition(l, r, bin.pos))
465        }
466
467        // Reverse composition: `F >> G` - apply `F` first, then `G`. (Swapped operands to eval_composition).
468        Node::ReverseCompositionOp(bin) => {
469            let lhv = eval(&bin.lho, scope);
470            let rhv = eval(&bin.rho, scope);
471            map_pairs!(lhv, rhv, |l,r| eval_composition(r, l, bin.pos))
472        }
473
474
475
476        // ===== Syntax Errors =====
477
478        // Missing closing bracket - turn parse errors into Undefined values
479        Node::Unclosed(leaf) => {
480            Value::Undefined(Undef::Termination{
481                expected: leaf.val,
482                pos: leaf.pos,
483            })
484        }
485
486        // Undefined token - turn parse errors into Undefined values
487        Node::UnexpectedToken(leaf) => {
488            Value::Undefined(Undef::Token{
489                token: leaf.val.clone(),
490                pos: leaf.pos
491            })
492        }
493    }
494}
495
496
497// EVAL HELPERS
498
499/// Builds the `Undefined` value returned when the left-hand side of a binding
500/// operation is not a valid name or tuple of names.
501///
502/// - `op`: the operation name (`"function_definition"`, `"assignment"` or `"labelling"`).
503/// - `names`: the `Undefined(Undef::Name{..})` returned by [`eval_names`].
504/// - `pos`: position of the operator.
505///
506/// The result is `Undef::BinaryOperation { op, lhv: names, rhv: Nothing, pos }`.
507fn invalid_names(op: &'static str, names: Value, pos: usize) -> Value {
508    Value::undef_binary(op, names, Value::Nothing, pos)
509}
510
511/// Resolves an AST node into names, without evaluating it.
512///
513/// Used for the left side of `=`, `:` and `->`, which must be an identifier,
514/// `()`, or a pairing (`,`) of those (parentheses do not matter).
515///
516/// Returns:
517/// - `Value::Name` for an identifier.
518/// - `Value::Nothing` for `()` or an empty expression.
519/// - A normalized tuple of names for pairings.
520/// - `Undefined(Undef::Name{pos})` for any other node, where `pos` is the
521///   position of the offending node. In a pairing, the first `Undefined`
522///   (left side first) is propagated unchanged.
523fn eval_names(node: &Node) -> Value {
524    match node {
525
526        // Nothing literal: `()` or empty expression
527        Node::Nothing(_) => Value::Nothing,
528
529        // Identifier lookup
530        Node::Identifier(leaf) => Value::Name(leaf.val.as_str().into()),
531
532        // Pairing operation: `X , Y`
533        Node::PairingOp(bin) => {
534            let name1 = eval_names(&bin.lho);
535            let name2 = eval_names(&bin.rho);
536
537            // Short-circuit / propagate if either side failed to produce a valid name
538            if matches!(name1, Value::Undefined(_)) {
539                return name1;
540            }
541            if matches!(name2, Value::Undefined(_)) {
542                return name2;
543            }
544
545            pairing_operation::eval(name1, name2)
546        }
547
548
549
550        // Any other value will return and undefined value
551        node => Value::Undefined(Undef::Name{ pos: node.position() }),
552    }
553}
554
555
556
557// ----------------------------------------------------------------------------
558//  APPLICATION OPERATION
559// ----------------------------------------------------------------------------
560
561/// `func arg` - applies a callable or indexable Swan value to an argument.
562///
563/// `pos` is the source position of the application, used in error values.
564///
565/// Dispatches on the type of `func`:
566///
567/// - `Value::Builtin(f)`: calls the native Rust function with the whole `arg`.
568/// - `Value::Func { ... }`: creates a child scope of the closure, binds the
569///   parameters to `arg` (destructuring tuples, see `assignment_operation`),
570///   and evaluates the body there.
571/// - `Value::List`: indexing by a zero-based number. A tuple argument gives a
572///   tuple of results, normalized (so out-of-range indices are dropped).
573/// - `Value::Namespace`: own-only lookup by text key. A tuple argument gives a
574///   normalized tuple of results (missing keys are dropped).
575/// - `Value::Text`: character indexing by a zero-based number. A tuple argument
576///   gives a normalized tuple of results.
577/// - Anything else (including `Nothing` and `Undefined`): returns
578///   `Undef::BinaryOperation { op: "application", lhv: func, rhv: arg, pos }`.
579///
580/// Broadcasting over a tuple of *callables* is not done here; the callers
581/// (`ApplicationOp`, `MappingOp`) do it with `map_values!`.
582pub fn eval_apply(func: Value, arg: Value, pos: usize) -> Value {
583    match func {
584        Value::Builtin(ref f) => f.call(arg),
585
586        Value::Func { params, body, closure } => {
587            let local_scope = closure.new_child();
588            assignment_operation::eval(&local_scope, &params, &arg);
589            eval(&body, &local_scope)
590        }
591
592        // --- List Application ---
593        Value::List(list) => {
594            if matches!(arg, Value::Tuple(_)) {
595                let items: Vec<Value> = arg
596                    .items()
597                    .map(|idx| eval_apply_list(&list, idx, pos))
598                    .collect();
599                Value::tuple(items)
600            } else {
601                eval_apply_list(&list, arg, pos)
602            }
603        }
604
605        // --- Namespace Application ---
606        Value::Namespace(scope) => {
607            if matches!(arg, Value::Tuple(_)) {
608                let items: Vec<Value> = arg
609                    .items()
610                    .map(|key| eval_apply_namespace(&scope, key, pos))
611                    .collect();
612                Value::tuple(items)
613            } else {
614                eval_apply_namespace(&scope, arg, pos)
615            }
616        }
617
618        // --- Text Application ---
619        Value::Text(text) => {
620            if matches!(arg, Value::Tuple(_)) {
621                let items: Vec<Value> = arg
622                    .items()
623                    .map(|idx| eval_apply_text(&text, idx, pos))
624                    .collect();
625                Value::tuple(items)
626            } else {
627                eval_apply_text(&text, arg, pos)
628            }
629        }
630
631        other => Value::undef_binary("application", other, arg, pos),
632    }
633}
634
635/// Applies a List to a single index `arg`.
636///
637/// Negative, fractional or out-of-range indices give `Nothing`.
638/// Non-numbers give `Undefined`. `pos` is used for the `Undefined` returned for a non-number index.
639fn eval_apply_list(list: &[Value], arg: Value, pos: usize) -> Value {
640    match arg {
641        Value::Numb(n) => {
642            if n >= 0.0 && n.fract() == 0.0 {
643                let idx = n as usize;
644                if idx < list.len() {
645                    return list[idx].clone();
646                }
647            }
648            // Index is out of bounds or not a whole integer
649            Value::Nothing
650        }
651        // Argument is not a number
652        other => Value::undef_binary("application", Value::list(list.to_vec()), other, pos),
653    }
654}
655
656/// Applies a Text to a single index `arg`.
657///
658/// Returns the character at position `arg` as a one-character `Text`.
659/// Indexing is by Unicode character (`char`), not by byte, and is zero-based.
660/// Negative, fractional or out-of-range indices give `Nothing`.
661/// Non-numbers give `Undefined`. `pos` is used for the `Undefined` returned for a non-number index.
662fn eval_apply_text(text: &Rc<str>, arg: Value, pos: usize) -> Value {
663    match arg {
664        Value::Numb(n) => {
665            if n >= 0.0 && n.fract() == 0.0 {
666                if let Some(ch) = text.chars().nth(n as usize) {
667                    return Value::Text(ch.to_string().into());
668                }
669            }
670            // Index is out of bounds or not a whole integer
671            Value::Nothing
672        }
673        // Argument is not a number
674        other => Value::undef_binary("application", Value::Text(text.clone()), other, pos),
675    }
676}
677
678/// Applies a Namespace to a single key `arg`.
679///
680/// Own-only lookup: parent scopes are not searched.
681/// A missing key gives `Nothing`; a non-text key gives
682/// `Undef::BinaryOperation { op: "application", .. }`.
683fn eval_apply_namespace(scope: &Scope, arg: Value, pos: usize) -> Value {
684    match arg {
685        Value::Text(t) => {
686            let found = scope.0.borrow().bindings.get(t.as_ref()).cloned();
687            found.unwrap_or(Value::Nothing)
688        }
689        // Argument is not a string
690        other => Value::undef_binary("application", Value::Namespace(scope.clone()), other, pos),
691    }
692}
693
694
695
696// ----------------------------------------------------------------------------
697//  FUNCTION COMPOSITION OPERATION
698// ----------------------------------------------------------------------------
699
700/// `f << g` builds a `Builtin` named `"composed"` that computes `f(g(x))`.
701///
702/// Both functions are applied with [`eval_apply`] and the whole argument `x`
703/// is passed to `g`; its whole result is passed to `f`. Applying errors become
704/// `Undefined` values, as usual. `pos` is the position of the composition
705/// operator and is used for any application error. `>>` calls this with its
706/// operands swapped. Tuple operands are broadcast by the caller using `map_pairs!`.
707fn eval_composition(f: Value, g: Value, pos: usize) -> Value {
708    Value::Builtin(BuiltinFn::new("composed", move |x:Value| {
709        let y = eval_apply(g.clone(), x, pos);    // y = g(x)
710        eval_apply(f.clone(), y, pos)             // z = f(y)
711    }))
712}
713
714
715
716// ----------------------------------------------------------------------------
717//  Tests
718// ----------------------------------------------------------------------------
719
720#[cfg(test)]
721/// Tests for the constructs handled directly in this file. Operations delegated to sub-modules are tested in their own modules.
722mod tests {
723    use super::*;
724    use std::assert_matches;
725    use crate::lexer::Lexer;
726    use crate::parser::Parser;
727
728    /// Parses and evaluates `src` in a fresh scope that has `TRUE` and `FALSE` defined.
729    fn evaluate(src: &str) -> Value {
730        let mut parser = Parser::new(Lexer::new(src));
731        let ast = parser.parse();
732        let scope = Scope::new();
733        scope.set("TRUE", Value::Bool(true));
734        scope.set("FALSE", Value::Bool(false));
735        eval(&ast, &scope)
736    }
737
738    #[test]
739    fn number_literal() {
740        assert_eq!(evaluate("7.2"), Value::Numb(7.2));
741    }
742
743    #[test]
744    fn string_literal() {
745        assert_eq!(evaluate("\"abc\""), Value::Text("abc".into()));
746    }
747
748    #[test]
749    fn nothing_literal() {
750
751        assert_eq!(evaluate(""), Value::Nothing);
752        assert_eq!(evaluate("   "), Value::Nothing);
753        assert_eq!(evaluate("# comment\n"), Value::Nothing);
754        assert_eq!(evaluate("  # comment\n  "), Value::Nothing);
755
756        assert_eq!(evaluate("()"), Value::Nothing);
757        assert_eq!(evaluate("(   )"), Value::Nothing);
758        assert_eq!(evaluate("(\n# comment\n)"), Value::Nothing);
759        assert_eq!(evaluate("(())"), Value::Nothing);
760    }
761
762    #[test]
763    fn identifier() {
764        assert_eq!(evaluate("TRUE"), Value::Bool(true));
765        assert_eq!(evaluate("x = 4, x"), Value::Numb(4.0));
766    }
767
768    #[test]
769    fn list_literal() {
770        assert_eq!(
771            evaluate("[7 , 2 , 9]"), 
772            Value::list(vec![
773                Value::Numb(7.0),
774                Value::Numb(2.0),
775                Value::Numb(9.0)
776            ])
777        );
778
779        assert_eq!(
780            evaluate("[7 , [2,9]]"), 
781            Value::list(vec![
782                Value::Numb(7.0),
783                Value::list(vec![
784                    Value::Numb(2.0),
785                    Value::Numb(9.0)
786                ])
787            ])
788        );
789
790        assert_eq!(evaluate("[]"), Value::list(vec![]));
791    }
792
793    #[test]
794    fn namespace_literal() {
795        match evaluate("{x:10, y:20}") {
796            Value::Namespace(scope) => {
797                assert_eq!(scope.get("x"), Value::Numb(10.0));
798                assert_eq!(scope.get("y"), Value::Numb(20.0));
799
800                // Inherited from parent scope
801                assert_eq!(scope.get("TRUE"), Value::Bool(true));
802            }
803            other => panic!("expected Namespace, got {:?}", other),
804        }
805    }
806
807    #[test]
808    fn function_definition() {
809
810        assert_matches!(evaluate("(x, y) -> x+y"), Value::Func{params:_, body:_, closure:_});
811
812        // Invalid left-hand names.
813        assert_eq!(evaluate("(x, 12) -> 2*x"), invalid_names("function_definition", Value::Undefined(Undef::Name{pos:4}), 8));
814    }
815
816    #[test]
817    fn application() {
818        // --- Func ---
819        assert_eq!(evaluate("f = (a) -> 2*a , f 5"), Value::Numb(10.0));
820        assert_eq!(evaluate("f = (a,b) -> a+b , f(3,7)"), Value::Numb(10.0));
821        assert_eq!(
822            evaluate("f = (a,b,c) -> (a,b)*c , f(2,3,4,5)"), 
823            Value::tuple(vec![Value::Numb(8.0), Value::Numb(15.0)])
824        );
825        assert_eq!(evaluate("(()->3)()"), Value::Numb(3.0));
826        assert_eq!(evaluate("(()->3) 10"), Value::Numb(3.0));
827
828        // Nothing
829        assert_eq!(evaluate("() 5"), Value::Nothing);
830
831        // Tuple
832        assert_eq!(
833            evaluate("(x->2*x , x->3*x) 4"),
834            Value::tuple(vec![Value::Numb(8.0), Value::Numb(12.0)])
835        );
836
837        // --- List ---
838        assert_eq!(evaluate("[10, 20, 30] 1"), Value::Numb(20.0));
839
840        // Out-of-bounds returns Value::Nothing
841        assert_eq!(evaluate("[10, 20] 5"), Value::Nothing);
842
843        // Non-number argument returns Undefined(Undef::BinaryOperation)
844        assert!(matches!(
845            evaluate(r#"[10, 20] "0""#),
846            Value::Undefined(Undef::BinaryOperation { op: "application", .. })
847        ));
848
849        // Applying list to a Tuple
850        assert_eq!(
851            evaluate("[10, 20, 30] (0, 5, 2)"),
852            Value::tuple(vec![Value::Numb(10.0), Value::Numb(30.0)])
853        );
854        assert_eq!(
855            evaluate("([1,2,3] , [10,20,30]) 1"),
856            Value::tuple(vec![Value::Numb(2.0), Value::Numb(20.0)])
857        );
858
859        // --- Namespace ---
860        assert_eq!(evaluate(r#"{x: 10, y: 20} "x""#), Value::Numb(10.0));
861
862        // Missing key returns Value::Nothing
863        assert_eq!(evaluate(r#"{x: 10} "z""#), Value::Nothing);
864
865        // Non-text key returns Undefined(Undef::BinaryOperation)
866        assert!(matches!(
867            evaluate("{x: 10} 1"),
868            Value::Undefined(Undef::BinaryOperation { op: "application", .. })
869        ));
870
871        // Applying namespace to a Tuple
872        assert_eq!(
873            evaluate(r#"{x: 10, y: 20} ("x", "missing")"#),
874            Value::Numb(10.0),
875        );
876
877        assert_eq!(
878            evaluate("({x:1, y:2} , {x:10, y:20}) \"y\""),
879            Value::tuple(vec![Value::Numb(2.0), Value::Numb(20.0)])
880        );
881
882        // --- Text ---
883        assert_eq!(evaluate(r#""hello" 1"#), Value::Text("e".into()));
884        assert_eq!(evaluate(r#""hello" 0"#), Value::Text("h".into()));
885
886        // Indexing is by character, not by byte
887        assert_eq!(evaluate(r#""héllo" 1"#), Value::Text("é".into()));
888
889        // Out-of-bounds, negative and fractional indices return Nothing
890        assert_eq!(evaluate(r#""hello" 5"#), Value::Nothing);
891        assert_eq!(evaluate(r#""hello" (-1)"#), Value::Nothing);
892        assert_eq!(evaluate(r#""hello" 1.5"#), Value::Nothing);
893        assert_eq!(evaluate(r#""" 0"#), Value::Nothing);
894
895        // Non-number argument returns Undefined(Undef::BinaryOperation)
896        match evaluate(r#""abc" "0""#) {
897            Value::Undefined(Undef::BinaryOperation { op, lhv, rhv, .. }) => {
898                assert_eq!(op, "application");
899                assert_eq!(*lhv, Value::Text("abc".into()));
900                assert_eq!(*rhv, Value::Text("0".into()));
901            }
902            other => panic!("expected Undefined::BinaryOperation, got {:?}", other),
903        }
904
905        // Applying text to a Tuple (Nothing is kept for positional alignment)
906        assert_eq!(
907            evaluate(r#""hello" (0, 10, 4)"#),
908            Value::tuple(vec![
909                Value::Text("h".into()),
910                Value::Text("o".into()),
911            ])
912        );
913
914        // Applying a tuple of texts to a number
915        assert_eq!(
916            evaluate(r#"("abc", "xyz") 1"#),
917            Value::tuple(vec![
918                Value::Text("b".into()),
919                Value::Text("y".into()),
920            ])
921        );
922    }
923
924    #[test]
925    fn assignment() {
926        // Assignment evaluates to Nothing but binds the name.
927        assert_eq!(evaluate("x = 5"), Value::Nothing);
928        assert_eq!(evaluate("x = 5, x"), Value::Numb(5.0));
929
930        // Destructuring assignment.
931        assert_eq!(evaluate("(a, b) = (1, 2), a + b"), Value::Numb(3.0));
932
933        // Invalid left-hand names.
934        assert_eq!(evaluate("(x, 12) = (1, 2)"), invalid_names("assignment", Value::Undefined(Undef::Name{pos:4}), 8));
935    }
936
937    #[test]
938    fn labelling() {
939        // `x : 5` evaluates to the assigned value itself (unlike `=`),
940        // *and* binds `x` in scope. Parses as `(x : 5), x`.
941        assert_eq!(evaluate("x : 5"), Value::Numb(5.0));
942
943        assert_eq!(
944            evaluate("x : 5, x"),
945            Value::tuple(vec![Value::Numb(5.0), Value::Numb(5.0)])
946        );
947
948        // Destructuring labelling behaves like destructuring assignment,
949        // but also yields the assigned values as the LabellingOp's result.
950        assert_eq!(
951            evaluate("(a, b) : (1, 2)"),
952            Value::tuple(vec![Value::Numb(1.0), Value::Numb(2.0)])
953        );
954
955        // Invalid left-hand names.
956        assert_eq!(evaluate("(x, 12) : (1, 2)"), invalid_names("labelling", Value::Undefined(Undef::Name{pos:4}), 8));
957    }
958
959    #[test]
960    fn subcontexting() {
961        assert_eq!(evaluate("{x:10}.x"), Value::Numb(10.0));
962        assert_eq!(evaluate("{x:10, y:20}.(x+y)"), Value::Numb(30.0));
963        assert_eq!(evaluate("{x:10, y:20}.TRUE"), Value::Bool(true));
964        match evaluate("x=10, x.TRUE") {
965            Value::Undefined(Undef::BinaryOperation { op, lhv, rhv, pos }) => {
966                assert_eq!(op, "subcontexting");
967                assert_eq!(*lhv, Value::Numb(10.0));
968                assert_eq!(*rhv, Value::Nothing);
969                assert_eq!(pos, 7);
970            }
971            other => panic!("expected Undefined::BinaryOperation, got {:?}", other),
972        }
973    }
974
975    #[test]
976    fn mapping() {
977
978        // Mapping tuple to function
979        assert_eq!(evaluate("f = a -> 2*a , (2,3,4) => f"), Value::tuple(vec![
980            Value::Numb(4.0),
981            Value::Numb(6.0),
982            Value::Numb(8.0),
983        ]));
984
985        // Mapping value to function
986        assert_eq!(evaluate("f = a -> 2*a , 2 => f"), Value::Numb(4.0));
987
988        // Mapping nothing to function
989        assert_eq!(evaluate("f = a -> 2*a , () => f"), Value::Nothing);
990
991        // Normalizing output of mapping
992        assert_eq!(evaluate("f = a -> () , (1,2,3) => f"), Value::Nothing);
993
994        // Mapping tuple to tuple of functions
995        assert_eq!(
996            evaluate("f = a -> 2*a , g = b -> 3*b, (2,5) => (f,g)"),
997            Value::tuple(vec![
998                Value::Numb(4.0),
999                Value::Numb(6.0),
1000                Value::Numb(10.0),
1001                Value::Numb(15.0),
1002            ])
1003        );
1004    }
1005
1006    #[test]
1007    fn conditional() {
1008
1009        // Left side casts to true -> returns right-hand side operand
1010        assert_eq!(evaluate("TRUE ? 10"), Value::Numb(10.0));
1011        assert_eq!(evaluate("1 ? \"yes\""), Value::Text("yes".into()));
1012        assert_eq!(evaluate("[1] ? 42"), Value::Numb(42.0));
1013
1014        // Left side casts to false -> returns Nothing
1015        assert_eq!(evaluate("FALSE ? 10"), Value::Nothing);
1016        assert_eq!(evaluate("0 ? 10"), Value::Nothing);
1017        assert_eq!(evaluate("\"\" ? 10"), Value::Nothing);
1018        assert_eq!(evaluate("() ? 10"), Value::Nothing);
1019        assert_eq!(evaluate("() ? ()"), Value::Nothing);
1020
1021        // A tuple condition is cast to bool as a whole: it is truthy if any item is
1022        assert_eq!(
1023            evaluate("(TRUE, FALSE, TRUE, FALSE) ? (1,2,3,4)"),
1024            Value::tuple(vec![
1025                Value::Numb(1.0),
1026                Value::Numb(2.0),
1027                Value::Numb(3.0),
1028                Value::Numb(4.0),
1029            ])
1030        );
1031        assert_eq!(evaluate("(FALSE, 0, \"\") ? (1,2,3)"), Value::Nothing);
1032
1033        // The whole right-hand side is returned, not just its first item
1034        assert_eq!(evaluate("TRUE ? (1, 2)"), Value::tuple(vec![Value::Numb(1.0), Value::Numb(2.0)]));
1035    }
1036
1037    #[test]
1038    fn alternative() {
1039        assert_eq!(evaluate("() ; 2"), Value::Numb(2.0));
1040        assert_eq!(evaluate("1 ; 2"), Value::Numb(1.0));
1041        assert_eq!(evaluate("FALSE ; 2"), Value::Bool(false));
1042    }
1043
1044    #[test]
1045    fn and_op() {
1046
1047        // True & RHS -> returns RHS
1048        assert_eq!(evaluate("TRUE & 10"), Value::Numb(10.0));
1049        assert_eq!(evaluate("TRUE & FALSE"), Value::Bool(false));
1050        assert_eq!(evaluate("1 & \"hello\""), Value::Text("hello".into()));
1051
1052        // False & RHS -> returns LHS
1053        assert_eq!(evaluate("FALSE & 10"), Value::Bool(false));
1054        assert_eq!(evaluate("0 & 10"), Value::Numb(0.0));
1055        assert_eq!(evaluate("\"\" & 10"), Value::Text("".into()));
1056
1057        // Nothing
1058        assert_eq!(evaluate("() & ()"), Value::Nothing);
1059        assert_eq!(evaluate("() & 10"), Value::Nothing);
1060
1061        // Tuple: the left-hand side is cast to bool as a whole, there is no pairing
1062        assert_eq!(
1063            evaluate("(FALSE, 1) & (10, \"abc\")"),
1064            Value::tuple(vec![
1065                Value::Numb(10.0),
1066                Value::Text("abc".into()),
1067            ])
1068        );
1069        assert_eq!(
1070            evaluate("(FALSE, 0) & 10"),
1071            Value::tuple(vec![Value::Bool(false), Value::Numb(0.0)])
1072        );
1073        assert_eq!(evaluate("TRUE & (10, 1)"), Value::tuple(vec![Value::Numb(10.0), Value::Numb(1.0)]));
1074    }
1075
1076    #[test]
1077    fn or_op() {
1078        // True | RHS -> returns LHS
1079        assert_eq!(evaluate("TRUE | 10"), Value::Bool(true));
1080        assert_eq!(evaluate("1 | 10"), Value::Numb(1.0));
1081        assert_eq!(evaluate("\"yes\" | 10"), Value::Text("yes".into()));
1082
1083        // False | RHS -> returns RHS
1084        assert_eq!(evaluate("FALSE | 10"), Value::Numb(10.0));
1085        assert_eq!(evaluate("0 | \"fallback\""), Value::Text("fallback".into()));
1086        assert_eq!(evaluate("\"\" | TRUE"), Value::Bool(true));
1087
1088        // Nothing
1089        assert_eq!(evaluate("() | 5"), Value::Numb(5.0));
1090
1091        // Tuple: the left-hand side is cast to bool as a whole, there is no pairing
1092        assert_eq!(
1093            evaluate("(FALSE, 1) | (10, \"abc\")"),
1094            Value::tuple(vec![
1095                Value::Bool(false),
1096                Value::Numb(1.0),
1097            ])
1098        );
1099        assert_eq!(evaluate("(FALSE, 0) | 10"), Value::Numb(10.0));
1100        assert_eq!(evaluate("FALSE | (10, 1)"), Value::tuple(vec![Value::Numb(10.0), Value::Numb(1.0)]));
1101    }
1102
1103    #[test]
1104    fn short_circuit() {
1105        // The right-hand side of `&`, `|` and `?` is evaluated only when needed,
1106        // so assignments in it don't happen otherwise
1107        let scope = crate::builtins::root_scope().new_child();
1108        let run = |src: &str| eval(&Parser::new(Lexer::new(src)).parse(), &scope);
1109
1110        run("x = 0");
1111        run("FALSE & (x = 1)");
1112        run("TRUE | (x = 2)");
1113        run("FALSE ? (x = 3)");
1114        run("(FALSE, 0) ? (x = 4)");
1115        run("(FALSE, 1) | (x = 5)");
1116        assert_eq!(run("x"), Value::Numb(0.0));
1117
1118        run("TRUE & (x = 1)");
1119        assert_eq!(run("x"), Value::Numb(1.0));
1120        run("FALSE | (x = 2)");
1121        assert_eq!(run("x"), Value::Numb(2.0));
1122        run("TRUE ? (x = 3)");
1123        assert_eq!(run("x"), Value::Numb(3.0));
1124        run("(FALSE, 1) ? (x = 4)");
1125        assert_eq!(run("x"), Value::Numb(4.0));
1126
1127        // A recursive call on the right of `?`, `&` or `|` stops at the base case
1128        assert_eq!(run("f = n -> n > 1 ? n * f (n - 1) ; 1, f 5"), Value::Numb(120.0));
1129        assert_eq!(run("g = n -> (n <= 1 & 1) | n * g (n - 1), g 5"), Value::Numb(120.0));
1130        assert_eq!(run("h = n -> n > 0 & h (n - 1), h 5"), Value::Bool(false));
1131    }
1132
1133    #[test]
1134    fn composition() {
1135        assert_eq!(evaluate("f = y -> 2*y , g = x -> x+1 , (f<<g)(3)"), Value::Numb(8.0));
1136    }
1137
1138    #[test]
1139    fn reverse_composition() {
1140        assert_eq!(evaluate("f = y -> 2*y , g = x -> x+1 , (f>>g)(3)"), Value::Numb(7.0));
1141    }
1142
1143    #[test]
1144    fn unclosed_bracket() {
1145        assert!(matches!(evaluate("(1, 2"), Value::Undefined(Undef::Termination{ .. })));
1146    }
1147
1148    #[test]
1149    fn unexpected_token() {
1150        assert!(matches!(evaluate(")"), Value::Undefined(Undef::Token{ .. })));
1151    }
1152
1153    #[test]
1154    fn precedence() {
1155        assert_eq!(evaluate("ns = {f: x -> 2*x}, ns.f 3"), Value::Numb(6.0));
1156        assert_eq!(evaluate("ns = {a=10}, f = x -> 2*x, f ns.a"), Value::Numb(20.0));
1157        assert_eq!(evaluate("f = x -> {y:x}, (f 3).y"), Value::Numb(3.0));
1158    }
1159
1160    #[test]
1161    fn promise_as_condition() {
1162        // A promise is FALSE while pending and TRUE once resolved, whatever its value
1163        let scope = crate::builtins::root_scope().new_child();
1164        let run = |src: &str| eval(&Parser::new(Lexer::new(src)).parse(), &scope);
1165
1166        run("p = promise.create()");
1167        assert_eq!(run("p ? 1"), Value::Nothing);
1168        assert_eq!(run("p ? 1 ; 2"), Value::Numb(2.0));
1169        assert_matches!(run("p & 1"), Value::Promise(_));
1170        assert_eq!(run("p | 1"), Value::Numb(1.0));
1171        assert_eq!(run("!p"), Value::Bool(true));
1172        assert_eq!(run("(FALSE, p) ? 1"), Value::Nothing);
1173
1174        run("promise.resolve p FALSE");
1175        assert_eq!(run("p ? 1"), Value::Numb(1.0));
1176        assert_eq!(run("p & 1"), Value::Numb(1.0));
1177        assert_matches!(run("p | 1"), Value::Promise(_));
1178        assert_eq!(run("!p"), Value::Bool(false));
1179        assert_eq!(run("(FALSE, p) ? 1"), Value::Numb(1.0));
1180    }
1181}