Skip to main content

somni_expr/
lib.rs

1//! # Somni expression evaluation Library
2//!
3//! This crate implements the expression evaluation subset of the Somnni language and VM. The crate
4//! can be used by itself, to evaluate simple expressions or even to run complete Somni programs, although
5//! slower than the Somni VM would.
6//!
7//! ## Overview
8//!
9//! Expressions are a subset of the Somni language:
10//!
11//! The expression language includes:
12//!
13//! - Literals: integers, floats, booleans, strings.
14//! - Variables
15//! - A basic set of operators
16//! - Function calls
17//!
18//! The expression language does not include:
19//!
20//! - Declaring new variables. You can assign to existing variables.
21//! - Control flow (if, loops, etc.)
22//! - Complex data structures (arrays, objects, etc.)
23//! - Defining functions and variables (these are provided by the context)
24//!
25//! ## Operators
26//!
27//! The following binary operators are supported, in order of precedence:
28//!
29//! - `=`: assign a value to an existing variable
30//! - `||`: logical OR, short-circuiting
31//! - `&&`: logical AND, short-circuiting
32//! - `<`, `<=`, `>`, `>=`, `==`, `!=`: comparison operators
33//! - `|`: bitwise OR
34//! - `^`: bitwise XOR
35//! - `&`: bitwise AND
36//! - `<<`, `>>`: bitwise shift
37//! - `+`, `-`: addition and subtraction
38//! - `*`, `/`: multiplication and division
39//!
40//! Unary operators include:
41//! - `&`: taking the address of a variable
42//! - `*`: dereferencing an address to a variable
43//! - `!`: logical NOT
44//! - `-`: negation
45//!
46//! For the full specification of the grammar, see the [`parser`] module's documentation.
47//!
48//! ## Numeric types
49//!
50//! The Somni language supports three numeric types:
51//!
52//! - Integers
53//! - Signed integers
54//! - Floats
55//!
56//! By default, the library uses the [`DefaultTypeSet`], which uses `u64`, `i64`, and `f64` for
57//! these types. You can use other type sets like [`TypeSet32`] or [`TypeSet128`] to use
58//! 32-bit or 128-bit integers and floats. You need to specify the type set when creating
59//! the context.
60//!
61//! Numeric integer literals can be either signed or unsigned integers. Their type is inferred from the usage.
62//!
63//! ## Usage
64//!
65//! To evaluate an expression, you need to create a [`Context`] first. You can assign
66//! variables and define functions in this context, and then you can use this context
67//! to evaluate expressions.
68//!
69//! ```rust
70//! use somni_expr::Context;
71//!
72//! let mut context = Context::new();
73//!
74//! // Define a variable
75//! context.add_variable::<u64>("x", 42);
76//! context.add_function("add_one", |x: u64| { x + 1 });
77//! context.add_function("floor", |x: f64| { x.floor() as u64 });
78//!
79//! // Evaluate an expression - we expect it to evaluate
80//! // to a number, which is u64 in the default type set.
81//! let result = context.evaluate::<u64>("add_one(x + floor(1.2))");
82//!
83//! assert_eq!(result, Ok(44));
84//! ```
85//!
86//! The context may also include a complete Somni program. The program may use the entirety
87//! of the Somni language, not just the expression language.
88//!
89//! ```rust
90//! use somni_expr::Context;
91//!
92//! let mut context = Context::parse("fn double(x: int) -> int { return x * 2; }").unwrap();
93//!
94//! // Evaluate an expression by calling the function defined by the program:
95//! let result = context.evaluate::<u64>("double(4)");
96//!
97//! assert_eq!(result, Ok(8));
98//! ```
99#![warn(missing_docs)]
100
101macro_rules! for_each {
102    // Any parenthesized set of choices, allows multiple matchers in the pattern
103    ($(($pattern:tt) in [$( ($($choice:tt)*) ),*] => $code:tt;)*) => {
104        $(
105            macro_rules! inner { $pattern => $code; }
106
107            $(
108                inner!( $($choice)* );
109            )*
110        )*
111    };
112    // Single type, single matcher
113    ($($pattern:tt in [$($choice:ty),*] => $code:tt;)*) => {
114        $(
115            macro_rules! inner { $pattern => $code; }
116
117            $(
118                inner!($choice);
119            )*
120        )*
121    };
122}
123
124pub mod error;
125pub mod function;
126pub mod iter;
127pub mod value;
128mod visitor;
129
130pub use function::{DynFunction, FunctionCallError};
131pub use iter::{SomniIterator, WithIterator};
132pub use value::TypedValue;
133pub use visitor::ExpressionVisitor;
134
135use std::{
136    cell::RefCell,
137    collections::HashMap,
138    fmt::{Debug, Display},
139    rc::Rc,
140};
141
142use somni_parser::{
143    ast::{self, Expression, Function, Item, Program},
144    parser::{self, parse, TypeSet as ParserTypeSet},
145    Location,
146};
147
148use crate::{
149    error::MarkInSource,
150    function::ExprFn,
151    value::{LoadOwned, LoadStore, ValueType},
152};
153
154pub use somni_parser::parser::{DefaultTypeSet, TypeSet128, TypeSet32};
155
156/// Defines the backing types for Somni types.
157///
158/// The [`LoadStore`] and [`LoadOwned`] traits can be used to convert between Rust and Somni types.
159pub trait TypeSet: Sized + Default + Debug + 'static {
160    /// The typeset that will be used to parse source code.
161    type Parser: ParserTypeSet<Integer = Self::Integer, Float = Self::Float>;
162
163    /// The type of unsigned integers in this type set.
164    type Integer: Copy + ValueType<NegateOutput: LoadStore<Self>> + LoadStore<Self>;
165
166    /// The type of signed integers in this type set.
167    type SignedInteger: Copy + ValueType<NegateOutput: LoadStore<Self>> + LoadStore<Self>;
168
169    /// The type of floating point numbers in this type set.
170    type Float: Copy + ValueType<NegateOutput: LoadStore<Self>> + LoadStore<Self>;
171
172    /// The type of a string in this type set.
173    type String: ValueType<NegateOutput: LoadStore<Self>> + LoadStore<Self>;
174
175    /// The type of an iterator value in this type set.
176    ///
177    /// This is the payload carried directly by [`TypedValue::Iter`]. Type sets that
178    /// do not support iteration use the uninhabited [`NoIterator`], making it
179    /// impossible to construct an iterator value.
180    type Iterator: Clone + PartialEq + Debug;
181
182    /// Converts an unsigned integer into a signed integer.
183    fn to_signed(v: Self::Integer) -> Result<Self::SignedInteger, OperatorError>;
184
185    /// Converts an unsigned integer into a Rust usize.
186    fn to_usize(v: Self::Integer) -> Result<usize, OperatorError>;
187
188    /// Converts the given Rust usize to an integer.
189    fn int_from_usize(v: usize) -> Self::Integer;
190
191    /// Loads a string.
192    fn load_string<'s>(&'s self, str: &'s Self::String) -> &'s str;
193
194    /// Stores a string.
195    fn store_string(&mut self, str: &str) -> Self::String;
196
197    /// Returns whether the given iterator can yield another value.
198    fn iter_has_next(&self, iter: &Self::Iterator) -> bool;
199
200    /// Advances the given iterator, returning its next value, or `None` if the
201    /// iterator is exhausted.
202    fn iter_next(&self, iter: &Self::Iterator) -> Option<TypedValue<Self>>;
203}
204
205/// The iterator type used by type sets that do not support iteration.
206///
207/// This type is uninhabited, so such type sets can never construct a
208/// [`TypedValue::Iter`] value.
209#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
210pub enum NoIterator {}
211
212for_each! {
213    (($name:ident, $signed:ty)) in [(DefaultTypeSet, i64), (TypeSet32, i32), (TypeSet128, i128)] => {
214        impl TypeSet for $name {
215            type Parser = Self;
216
217            type Integer = <Self::Parser as ParserTypeSet>::Integer;
218            type SignedInteger = $signed;
219            type Float = <Self::Parser as ParserTypeSet>::Float;
220            type String = Box<str>;
221            type Iterator = NoIterator;
222
223            fn to_signed(v: Self::Integer) -> Result<Self::SignedInteger, OperatorError> {
224                <$signed>::try_from(v).map_err(|_| OperatorError::RuntimeError)
225            }
226
227            fn to_usize(v: Self::Integer) -> Result<usize, OperatorError> {
228                usize::try_from(v).map_err(|_| OperatorError::RuntimeError)
229            }
230
231            fn int_from_usize(v: usize) -> Self::Integer {
232                Self::Integer::try_from(v).unwrap()
233            }
234
235            fn load_string<'s>(&'s self, str: &'s Self::String) -> &'s str {
236                str
237            }
238
239            fn store_string(&mut self, str: &str) -> Self::String {
240                str.to_string().into_boxed_str()
241            }
242
243            fn iter_has_next(&self, iter: &Self::Iterator) -> bool {
244                match *iter {}
245            }
246
247            fn iter_next(&self, iter: &Self::Iterator) -> Option<TypedValue<Self>> {
248                match *iter {}
249            }
250        }
251    };
252}
253
254/// Represents an error that can occur during operator evaluation.
255#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
256pub enum OperatorError {
257    /// A type error occurred.
258    TypeError,
259    /// A runtime error occurred.
260    RuntimeError,
261}
262
263impl Display for OperatorError {
264    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
265        let message = match self {
266            OperatorError::TypeError => "Type error",
267            OperatorError::RuntimeError => "Runtime error",
268        };
269
270        f.write_str(message)
271    }
272}
273
274macro_rules! dispatch_binary {
275    ($method:ident) => {
276        pub(crate) fn $method(ctx: &mut T, lhs: Self, rhs: Self) -> Result<Self, OperatorError> {
277            let result = match (lhs, rhs) {
278                (Self::Bool(value), Self::Bool(other)) => {
279                    ValueType::$method(value, other)?.store(ctx)
280                }
281                (Self::Int(value), Self::Int(other)) => {
282                    ValueType::$method(value, other)?.store(ctx)
283                }
284                (Self::SignedInt(value), Self::SignedInt(other)) => {
285                    ValueType::$method(value, other)?.store(ctx)
286                }
287                (Self::MaybeSignedInt(value), Self::MaybeSignedInt(other)) => {
288                    match ValueType::$method(value, other)?.store(ctx) {
289                        Self::Int(v) => Self::MaybeSignedInt(v),
290                        other => other,
291                    }
292                }
293                (Self::Float(value), Self::Float(other)) => {
294                    ValueType::$method(value, other)?.store(ctx)
295                }
296                (Self::String(value), Self::String(other)) => {
297                    ValueType::$method(value, other)?.store(ctx)
298                }
299                (Self::Int(value), Self::MaybeSignedInt(other)) => {
300                    ValueType::$method(value, other)?.store(ctx)
301                }
302                (Self::MaybeSignedInt(value), Self::Int(other)) => {
303                    ValueType::$method(value, other)?.store(ctx)
304                }
305                (Self::SignedInt(value), Self::MaybeSignedInt(other)) => {
306                    ValueType::$method(value, T::to_signed(other)?)?.store(ctx)
307                }
308                (Self::MaybeSignedInt(value), Self::SignedInt(other)) => {
309                    ValueType::$method(T::to_signed(value)?, other)?.store(ctx)
310                }
311                _ => return Err(OperatorError::TypeError),
312            };
313
314            Ok(result)
315        }
316    };
317}
318
319macro_rules! dispatch_unary {
320    ($method:ident) => {
321        pub(crate) fn $method(ctx: &mut T, operand: Self) -> Result<Self, OperatorError> {
322            match operand {
323                Self::Bool(value) => Ok(ValueType::$method(value)?.store(ctx)),
324                Self::Int(value) | Self::MaybeSignedInt(value) => {
325                    Ok(ValueType::$method(value)?.store(ctx))
326                }
327                Self::SignedInt(value) => Ok(ValueType::$method(value)?.store(ctx)),
328                Self::Float(value) => Ok(ValueType::$method(value)?.store(ctx)),
329                Self::String(value) => Ok(ValueType::$method(value)?.store(ctx)),
330                _ => return Err(OperatorError::TypeError),
331            }
332        }
333    };
334}
335
336impl<T> TypedValue<T>
337where
338    T: TypeSet,
339{
340    dispatch_binary!(equals);
341    dispatch_binary!(less_than);
342    dispatch_binary!(less_than_or_equal);
343    dispatch_binary!(not_equals);
344    dispatch_binary!(bitwise_or);
345    dispatch_binary!(bitwise_xor);
346    dispatch_binary!(bitwise_and);
347    dispatch_binary!(shift_left);
348    dispatch_binary!(shift_right);
349    dispatch_binary!(add);
350    dispatch_binary!(subtract);
351    dispatch_binary!(multiply);
352    dispatch_binary!(divide);
353    dispatch_binary!(modulo);
354    dispatch_unary!(not);
355    dispatch_unary!(negate);
356}
357
358/// An expression context that provides the necessary environment for evaluating expressions.
359pub trait ExprContext<T = DefaultTypeSet>
360where
361    T: TypeSet,
362{
363    /// Returns a reference to the `TypeSet`.
364    fn type_context(&mut self) -> &mut T;
365
366    /// Attempts to load a variable from the context.
367    fn try_load_variable(&mut self, variable: &str) -> Option<TypedValue<T>>;
368
369    /// Declares a variable in the context.
370    fn declare(&mut self, variable: &str, value: TypedValue<T>);
371
372    /// Assigns a new value to a variable in the context.
373    fn assign_variable(&mut self, variable: &str, value: &TypedValue<T>) -> Result<(), Box<str>>;
374
375    /// Returns a value from the given address.
376    fn at_address(&mut self, address: TypedValue<T>) -> Result<TypedValue<T>, Box<str>>;
377
378    /// Assigns a new value to a variable in the context.
379    fn assign_address(
380        &mut self,
381        address: TypedValue<T>,
382        value: &TypedValue<T>,
383    ) -> Result<(), Box<str>>;
384
385    /// Returns the address of a variable in the context.
386    fn address_of(&mut self, variable: &str) -> TypedValue<T>;
387
388    /// Opens a new scope in the current stack frame.
389    fn open_scope(&mut self);
390
391    /// Closes the last scope in the current stack frame.
392    fn close_scope(&mut self);
393
394    /// Calls a function in the context.
395    fn call_function(
396        &mut self,
397        function_name: &str,
398        args: &[TypedValue<T>],
399    ) -> Result<TypedValue<T>, FunctionCallError>;
400}
401
402/// An error that occurs during evaluation of an expression.
403#[derive(Clone, Debug, PartialEq)]
404pub struct EvalError {
405    /// The error message.
406    pub message: Box<str>,
407    /// The location in the source code where the error occurred.
408    pub location: Location,
409}
410
411impl Display for EvalError {
412    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
413        write!(f, "Evaluation error: {}", self.message)
414    }
415}
416
417/// An error that occurs during evaluation.
418///
419/// Printing this error will show the error message and the location in the source code.
420///
421/// ```rust
422/// use somni_expr::{Context, TypeSet32};
423/// let mut ctx = Context::<TypeSet32>::new_with_types();
424///
425/// let error = ctx.evaluate::<u32>("true + 1").unwrap_err();
426///
427/// println!("{error:?}");
428///
429/// // Output:
430/// //
431/// // Evaluation error
432/// // ---> at line 1 column 1
433/// //   |
434/// // 1 | true + 1
435/// //   | ^^^^^^^^ Failed to evaluate expression: Type error
436/// ```
437#[derive(Clone, PartialEq)]
438pub struct ExpressionError<'s> {
439    error: EvalError,
440    source: &'s str,
441}
442
443impl ExpressionError<'_> {
444    /// Returns the inner [`EvalError`].
445    pub fn into_inner(self) -> EvalError {
446        self.error
447    }
448}
449
450impl Debug for ExpressionError<'_> {
451    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
452        let marked = MarkInSource(
453            self.source,
454            self.error.location,
455            "Evaluation error",
456            &self.error.message,
457        );
458        marked.fmt(f)
459    }
460}
461
462/// A type in the Somni language.
463#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
464pub enum Type {
465    /// Represents no value, used for e.g. functions that do not return a value.
466    Void,
467    /// Represents integer that may be signed or unsigned.
468    MaybeSignedInt,
469    /// Represents an unsigned integer.
470    Int,
471    /// Represents a signed integer.
472    SignedInt,
473    /// Represents a floating point number.
474    Float,
475    /// Represents a boolean value.
476    Bool,
477    /// Represents a string value.
478    String,
479    /// Represents an iterator handle. The element type is not part of the type;
480    /// it is checked at runtime when a value is produced.
481    Iter,
482}
483impl Type {
484    fn from_name(source: &str) -> Result<Self, Box<str>> {
485        match source {
486            "int" => Ok(Type::Int),
487            "signed" => Ok(Type::SignedInt),
488            "float" => Ok(Type::Float),
489            "bool" => Ok(Type::Bool),
490            "string" => Ok(Type::String),
491            "iter" => Ok(Type::Iter),
492            other => Err(format!("Unknown type `{other}`").into_boxed_str()),
493        }
494    }
495}
496
497impl Display for Type {
498    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
499        match self {
500            Type::Void => write!(f, "void"),
501            Type::MaybeSignedInt => write!(f, "{{int/signed}}"),
502            Type::Int => write!(f, "int"),
503            Type::SignedInt => write!(f, "signed"),
504            Type::Bool => write!(f, "bool"),
505            Type::String => write!(f, "string"),
506            Type::Float => write!(f, "float"),
507            Type::Iter => write!(f, "iter"),
508        }
509    }
510}
511
512/// State of an unevaluated global.
513enum InitializerState {
514    /// Untouched. Contains the item index of the global
515    Unevaluated(usize),
516    /// The global is being evaluated. This state is used to detect cycles.
517    Evaluating,
518}
519
520struct StackFrame<T: TypeSet> {
521    start_addr: usize,
522    variables: Vec<TypedValue<T>>,
523    scopes: Vec<HashMap<String, usize>>,
524}
525
526impl<T: TypeSet> StackFrame<T> {
527    fn new() -> StackFrame<T> {
528        StackFrame {
529            start_addr: 0,
530            variables: vec![],
531            scopes: vec![HashMap::new()],
532        }
533    }
534
535    fn next_call_frame(&self) -> StackFrame<T> {
536        StackFrame {
537            start_addr: self.start_addr + self.variables.len(),
538            variables: vec![],
539            scopes: vec![HashMap::new()],
540        }
541    }
542
543    fn declare(&mut self, variable: &str, value: TypedValue<T>) -> usize {
544        let index = self.variables.len();
545        self.variables.push(value);
546        self.scopes
547            .last_mut()
548            .unwrap()
549            .insert(variable.to_string(), index);
550        index + self.start_addr
551    }
552
553    fn lookup_index(&self, name: &str) -> Option<usize> {
554        for scope in self.scopes.iter().rev() {
555            if let Some(idx) = scope.get(name) {
556                return Some(*idx);
557            }
558        }
559        None
560    }
561
562    fn store(&mut self, variable: &str, value: &TypedValue<T>) -> bool {
563        if let Some(idx) = self.lookup_index(variable) {
564            self.variables.get_mut(idx).unwrap().clone_from(value);
565            true
566        } else {
567            false
568        }
569    }
570
571    fn lookup_by_address(&mut self, address: usize) -> Result<&mut TypedValue<T>, Box<str>> {
572        self.variables
573            .get_mut(address - self.start_addr)
574            .ok_or_else(|| format!("Invalid address {address}").into_boxed_str())
575    }
576
577    fn lookup_by_name<'s>(&'s mut self, variable: &str) -> Option<(usize, &'s mut TypedValue<T>)> {
578        let index = self.lookup_index(variable)?;
579        let address = index + self.start_addr;
580
581        Some((address, self.variables.get_mut(index).unwrap()))
582    }
583
584    fn open_scope(&mut self) {
585        self.scopes.push(HashMap::new());
586    }
587
588    fn close_scope(&mut self) {
589        self.scopes.pop().unwrap();
590    }
591}
592
593struct ProgramData<'ctx, T: TypeSet> {
594    source: &'ctx str,
595    program: Program<T::Parser>,
596    program_functions: HashMap<&'ctx str, usize>,
597    // User-registered functions
598    functions: RefCell<HashMap<&'ctx str, ExprFn<'ctx, T>>>,
599}
600
601impl<'ctx> Default for Context<'ctx, DefaultTypeSet> {
602    fn default() -> Self {
603        Self::new()
604    }
605}
606
607/// The expression context, which holds variables, functions, and other state needed for evaluation.
608pub struct Context<'ctx, T = DefaultTypeSet>
609where
610    T: TypeSet,
611{
612    program: Rc<ProgramData<'ctx, T>>,
613    // Program state
614    // ----
615    /// Variable stack. Element 0 is the global scope.
616    stack: Vec<StackFrame<T>>,
617    // unevaluated globals
618    initializers: HashMap<&'ctx str, InitializerState>,
619    type_context: T,
620}
621
622impl<'ctx> Context<'ctx, DefaultTypeSet> {
623    /// Creates a new context with [default types][DefaultTypeSet].
624    pub fn new() -> Self {
625        Self::new_with_types()
626    }
627
628    /// Loads the given program into a new context with [default types][DefaultTypeSet].
629    pub fn parse(source: &'ctx str) -> Result<Self, ExpressionError<'ctx>> {
630        Self::parse_with_types(source)
631    }
632}
633
634const GLOBAL_VARIABLE: usize = usize::MAX - usize::MAX / 2;
635
636impl<'ctx, T> Context<'ctx, T>
637where
638    T: TypeSet,
639{
640    /// Creates a new context. The type set must be specified when using this function.
641    ///
642    /// ```rust
643    /// use somni_expr::{Context, TypeSet32};
644    /// let mut ctx = Context::<TypeSet32>::new_with_types();
645    /// ```
646    pub fn new_with_types() -> Self {
647        Self::new_from_program("", Program { items: vec![] })
648    }
649
650    /// Parses the given program into a new context. The type set must be specified when using this function.
651    ///
652    /// ```rust
653    /// use somni_expr::{Context, TypeSet32};
654    /// let mut ctx = Context::<TypeSet32>::parse_with_types("// program source comes here").unwrap();
655    /// ```
656    pub fn parse_with_types(source: &'ctx str) -> Result<Self, ExpressionError<'ctx>> {
657        let program = parse::<T::Parser>(source).map_err(|e| ExpressionError {
658            error: EvalError {
659                message: format!("Failed to parse program: {e}").into_boxed_str(),
660                location: e.location,
661            },
662            source,
663        })?;
664
665        Ok(Self::new_from_program(source, program))
666    }
667
668    /// Loads the given program into a new context.
669    pub fn new_from_program(source: &'ctx str, program: Program<T::Parser>) -> Self {
670        let mut program_functions = HashMap::new();
671        let mut initializers = HashMap::new();
672        // Extract data for O(1) function/initializer lookup
673        for (idx, item) in program.items.iter().enumerate() {
674            match item {
675                ast::Item::Function(function) => {
676                    program_functions.insert(function.name.source(source), idx);
677                }
678                ast::Item::GlobalVariable(global_variable) => {
679                    initializers.insert(
680                        global_variable.identifier.source(source),
681                        InitializerState::Unevaluated(idx),
682                    );
683                }
684                ast::Item::ExternFunction(_) => {}
685            }
686        }
687        Self {
688            program: Rc::new(ProgramData {
689                source,
690                program,
691                program_functions,
692                functions: RefCell::new(HashMap::new()),
693            }),
694            stack: vec![StackFrame::new()],
695            type_context: T::default(),
696            initializers,
697        }
698    }
699
700    fn evaluate_any_function_impl(
701        &mut self,
702        function_name: &Function<T::Parser>,
703        args: &[TypedValue<T>],
704    ) -> Result<TypedValue<T>, EvalError> {
705        let source = self.program.clone().source;
706
707        let stack_frame = self
708            .stack
709            .last()
710            .expect("The global scope must always be present")
711            .next_call_frame();
712        self.stack.push(stack_frame);
713
714        let mut visitor = ExpressionVisitor::<Self, T> {
715            context: self,
716            source,
717            _marker: std::marker::PhantomData,
718        };
719
720        let result = visitor.visit_function(function_name, args);
721
722        self.stack.pop();
723
724        result
725    }
726
727    /// Parses and evaluates an expression and returns the result as a specific value type.
728    ///
729    /// This function will attempt to convert the result of the expression to the specified type `V`.
730    /// If the conversion fails, it will return an `ExpressionError`.
731    ///
732    /// ```rust
733    /// use somni_expr::{Context, TypedValue};
734    ///
735    /// let mut context = Context::new();
736    ///
737    /// assert_eq!(context.evaluate::<u64>("1 + 2"), Ok(3));
738    /// assert_eq!(context.evaluate::<TypedValue>("1 + 2"), Ok(TypedValue::Int(3)));
739    /// ```
740    pub fn evaluate<'s, V>(&'s mut self, source: &'s str) -> Result<V::Output, ExpressionError<'s>>
741    where
742        V: LoadOwned<T>,
743    {
744        let expression =
745            parser::parse_expression::<T::Parser>(source).map_err(|e| ExpressionError {
746                error: EvalError {
747                    message: format!("Parser error: {e}").into_boxed_str(),
748                    location: e.location,
749                },
750                source,
751            })?;
752
753        self.evaluate_parsed::<V>(source, &expression)
754    }
755
756    /// Evaluates a pre-parsed expression and returns the result as a specific value type.
757    ///
758    /// This function will attempt to convert the result of the expression to the specified type `V`.
759    /// If the conversion fails, it will return an `ExpressionError`.
760    ///
761    /// ```rust
762    /// use somni_expr::{Context, TypedValue};
763    ///
764    /// let mut context = Context::new();
765    ///
766    /// let source = "1 + 2";
767    /// let expr = somni_parser::parser::parse_expression(source).unwrap();
768    ///
769    /// assert_eq!(context.evaluate_parsed::<u64>(source, &expr), Ok(3));
770    /// assert_eq!(context.evaluate_parsed::<TypedValue>(source, &expr), Ok(TypedValue::Int(3)));
771    /// ```
772    pub fn evaluate_parsed<'s, V>(
773        &'s mut self,
774        source: &'s str,
775        expression: &Expression<T::Parser>,
776    ) -> Result<V::Output, ExpressionError<'s>>
777    where
778        V: LoadOwned<T>,
779    {
780        self.evaluate_impl::<V>(source, expression)
781            .map_err(|error| ExpressionError { error, source })
782    }
783
784    fn evaluate_impl<V>(
785        &mut self,
786        source: &str,
787        expression: &Expression<T::Parser>,
788    ) -> Result<V::Output, EvalError>
789    where
790        V: LoadOwned<T>,
791    {
792        let mut visitor = ExpressionVisitor::<Self, T> {
793            context: self,
794            source,
795            _marker: std::marker::PhantomData,
796        };
797        let result = visitor.visit_expression(expression)?;
798        let result_ty = result.type_of();
799        V::load_owned(self.type_context(), &result).ok_or_else(|| EvalError {
800            message: format!(
801                "Expression evaluates to {result_ty}, which cannot be converted to {}",
802                std::any::type_name::<V>()
803            )
804            .into_boxed_str(),
805            location: expression.location(),
806        })
807    }
808
809    /// Defines a new variable in the context.
810    ///
811    /// The variable can be any type from the current [`TypeSet`], even [`TypedValue`].
812    ///
813    /// The variable will act as a global variable in the context of the program. Its
814    /// value can be changed by expressions.
815    ///
816    /// ```rust
817    /// use somni_expr::{Context, TypedValue};
818    ///
819    /// let mut context = Context::new();
820    ///
821    /// // Variable does not exist, it can't be assigned:
822    /// assert!(context.evaluate::<()>("counter = 0").is_err());
823    ///
824    /// context.add_variable::<u64>("counter", 0);
825    ///
826    /// // Variable exists now, so we can use it:
827    /// assert_eq!(context.evaluate::<()>("counter = counter + 1"), Ok(()));
828    /// assert_eq!(context.evaluate::<u64>("counter"), Ok(1));
829    /// ```
830    pub fn add_variable<V>(&mut self, name: &'ctx str, value: V)
831    where
832        V: LoadStore<T>,
833    {
834        let stored = value.store(self.type_context());
835        self.stack[0].declare(name, stored);
836    }
837
838    /// Adds a new function to the context.
839    ///
840    /// ```rust
841    /// use somni_expr::{Context, TypedValue};
842    ///
843    /// let mut context = Context::new();
844    ///
845    /// context.add_function("plus_one", |x: u64| x + 1);
846    ///
847    /// assert_eq!(context.evaluate::<u64>("plus_one(2)"), Ok(3));
848    /// ```
849    pub fn add_function<F, A>(&mut self, name: &'ctx str, func: F)
850    where
851        F: DynFunction<A, T> + 'ctx,
852    {
853        self.program
854            .functions
855            .borrow_mut()
856            .insert(name, ExprFn::new(func));
857    }
858
859    fn lookup(&mut self, variable: &str) -> Option<(usize, TypedValue<T>)> {
860        if self.stack.len() > 1 {
861            let frame = self.stack.last_mut().unwrap();
862            if let Some((index, var)) = frame.lookup_by_name(variable) {
863                // Already evaluated / user provided
864                return Some((index, var.clone()));
865            }
866        }
867
868        {
869            let global_frame = &mut self.stack[0];
870            if let Some((index, var)) = global_frame.lookup_by_name(variable) {
871                // Already evaluated / user provided
872                return Some((index | GLOBAL_VARIABLE, var.clone()));
873            }
874        }
875
876        // Mark as "initializing" to detect potential cycles
877        let state = self.initializers.get_mut(variable)?;
878        let InitializerState::Unevaluated(idx) =
879            std::mem::replace(state, InitializerState::Evaluating)
880        else {
881            return None;
882        };
883
884        // Get a reference to the initializer
885        let program = self.program.clone();
886        let Some(Item::GlobalVariable(global)) = program.program.items.get(idx) else {
887            return None;
888        };
889
890        let value = self
891            .evaluate_parsed::<TypedValue<T>>(self.program.source, &global.initializer)
892            .ok()?;
893
894        let global_frame = &mut self.stack[0];
895        let index = global_frame.declare(variable, value.clone());
896
897        Some((index | GLOBAL_VARIABLE, value))
898    }
899
900    fn lookup_address(&mut self, address: TypedValue<T>) -> Result<&mut TypedValue<T>, Box<str>> {
901        let TypedValue::Int(address) = address else {
902            return Err(format!("Expected address, got {address:?}").into_boxed_str());
903        };
904
905        let address = T::to_usize(address)
906            .map_err(|_| format!("Invalid address: {address:?}").into_boxed_str())?;
907
908        if address & GLOBAL_VARIABLE != 0 {
909            return self.stack[0].lookup_by_address(address & !GLOBAL_VARIABLE);
910        }
911
912        for frame in self.stack.iter_mut().rev() {
913            if frame.start_addr <= address {
914                return frame.lookup_by_address(address);
915            }
916        }
917
918        Err(format!("Not a valid memory address: {address}").into_boxed_str())
919    }
920}
921
922impl<T> ExprContext<T> for Context<'_, T>
923where
924    T: TypeSet,
925{
926    fn type_context(&mut self) -> &mut T {
927        &mut self.type_context
928    }
929
930    // TODO: return Result
931    fn try_load_variable(&mut self, variable: &str) -> Option<TypedValue<T>> {
932        self.lookup(variable).map(|(_idx, var)| var)
933    }
934
935    fn address_of(&mut self, variable: &str) -> TypedValue<T> {
936        let address = self
937            .lookup(variable)
938            .map(|(address, _var)| address)
939            .unwrap();
940        TypedValue::Int(T::int_from_usize(address))
941    }
942
943    /// Declares a variable in the context.
944    fn declare(&mut self, variable: &str, value: TypedValue<T>) {
945        self.stack.last_mut().unwrap().declare(variable, value);
946    }
947
948    /// Assigns a new value to a variable in the context.
949    fn assign_variable(&mut self, variable: &str, value: &TypedValue<T>) -> Result<(), Box<str>> {
950        if self.stack.last_mut().unwrap().store(variable, value) {
951            return Ok(());
952        }
953        if self.stack[0].store(variable, value) {
954            return Ok(());
955        }
956
957        Err(format!("Variable not found: {variable}").into_boxed_str())
958    }
959
960    fn at_address(&mut self, address: TypedValue<T>) -> Result<TypedValue<T>, Box<str>> {
961        self.lookup_address(address).cloned()
962    }
963
964    fn assign_address(
965        &mut self,
966        address: TypedValue<T>,
967        value: &TypedValue<T>,
968    ) -> Result<(), Box<str>> {
969        let v = self.lookup_address(address)?;
970        v.clone_from(value);
971        Ok(())
972    }
973
974    fn call_function(
975        &mut self,
976        function_name: &str,
977        args: &[TypedValue<T>],
978    ) -> Result<TypedValue<T>, FunctionCallError> {
979        let program = self.program.clone();
980        let Some(fn_item) = self.program.program_functions.get(function_name) else {
981            // Call out to a Rust function
982            return match program.functions.borrow().get(function_name) {
983                Some(func) => func.call(self.type_context(), args),
984                None => Err(FunctionCallError::FunctionNotFound),
985            };
986        };
987
988        // Call a Somni function
989        let Some(ast::Item::Function(function)) = program.program.items.get(*fn_item) else {
990            return Err(FunctionCallError::FunctionNotFound);
991        };
992        self.evaluate_any_function_impl(function, args)
993            .map_err(|err| {
994                FunctionCallError::Other(
995                    format!(
996                        "{:?}",
997                        ExpressionError {
998                            source: self.program.source,
999                            error: err,
1000                        }
1001                    )
1002                    .into_boxed_str(),
1003                )
1004            })
1005    }
1006
1007    /// Opens a new scope in the current stack frame.
1008    fn open_scope(&mut self) {
1009        // TODO: error handling
1010        self.stack.last_mut().unwrap().open_scope();
1011    }
1012
1013    /// Closes the last scope in the current stack frame.
1014    fn close_scope(&mut self) {
1015        // TODO: error handling
1016        self.stack.last_mut().unwrap().close_scope();
1017    }
1018}
1019
1020#[macro_export]
1021#[doc(hidden)]
1022macro_rules! for_all_tuples {
1023    ($pat:tt => $code:tt;) => {
1024        macro_rules! inner { $pat => $code; }
1025
1026        inner!();
1027        inner!(V1);
1028        inner!(V1, V2);
1029        inner!(V1, V2, V3);
1030        inner!(V1, V2, V3, V4);
1031        inner!(V1, V2, V3, V4, V5);
1032        inner!(V1, V2, V3, V4, V5, V6);
1033        inner!(V1, V2, V3, V4, V5, V6, V7);
1034        inner!(V1, V2, V3, V4, V5, V6, V7, V8);
1035        inner!(V1, V2, V3, V4, V5, V6, V7, V8, V9);
1036        inner!(V1, V2, V3, V4, V5, V6, V7, V8, V9, V10);
1037    };
1038}
1039
1040#[cfg(test)]
1041mod test {
1042    use std::path::Path;
1043
1044    use super::*;
1045
1046    fn strip_ansi(s: impl AsRef<str>) -> String {
1047        use ansi_parser::AnsiParser;
1048        fn text_block(output: ansi_parser::Output<'_>) -> Option<&str> {
1049            match output {
1050                ansi_parser::Output::TextBlock(text) => Some(text),
1051                _ => None,
1052            }
1053        }
1054
1055        s.as_ref()
1056            .ansi_parse()
1057            .filter_map(text_block)
1058            .collect::<String>()
1059    }
1060
1061    #[test]
1062    fn test_evaluating_exprs() {
1063        let mut ctx = Context::new();
1064
1065        ctx.add_variable::<i64>("signed", 30);
1066        ctx.add_variable::<u64>("value", 30);
1067        ctx.add_function("func", |v: u64| 2 * v);
1068        ctx.add_function("func2", |v1: u64, v2: u64| v1 + v2);
1069        ctx.add_function("five", || "five");
1070        ctx.add_function("is_five", |num: &str| num == "five");
1071        ctx.add_function("concatenate", |a: &str, b: &str| format!("{a}{b}"));
1072
1073        assert_eq!(ctx.evaluate::<bool>("value / 5 == 6"), Ok(true));
1074        assert_eq!(ctx.evaluate::<bool>("five() == \"five\""), Ok(true));
1075        assert_eq!(
1076            ctx.evaluate::<bool>("is_five(five()) != is_five(\"six\")"),
1077            Ok(true)
1078        );
1079        assert_eq!(ctx.evaluate::<u64>("func(20) / 5"), Ok(8));
1080        assert_eq!(
1081            ctx.evaluate::<TypedValue>("func(20) / 5"),
1082            Ok(TypedValue::Int(8))
1083        );
1084        assert_eq!(ctx.evaluate::<u64>("func2(20, 20) / 5"), Ok(8));
1085        assert_eq!(ctx.evaluate::<bool>("true & false"), Ok(false));
1086        assert_eq!(ctx.evaluate::<bool>("!true"), Ok(false));
1087        assert_eq!(ctx.evaluate::<bool>("false | false"), Ok(false));
1088        assert_eq!(ctx.evaluate::<bool>("true ^ true"), Ok(false));
1089        assert_eq!(ctx.evaluate::<u64>("!0x1111"), Ok(0xFFFF_FFFF_FFFF_EEEE));
1090        assert_eq!(
1091            ctx.evaluate::<String>("concatenate(five(), \"six\")"),
1092            Ok(String::from("fivesix"))
1093        );
1094        assert_eq!(ctx.evaluate::<bool>("signed * 2 == 60"), Ok(true));
1095        assert_eq!(ctx.evaluate::<i64>("*&signed"), Ok(30));
1096    }
1097
1098    #[test]
1099    fn test_context_is_mutable() {
1100        let mut ctx = Context::new();
1101
1102        ctx.add_variable::<u64>("value", 30);
1103
1104        ctx.evaluate::<()>("value = 5").unwrap();
1105        assert_eq!(ctx.evaluate::<bool>("value == 5"), Ok(true));
1106    }
1107
1108    #[test]
1109    fn test_evaluating_exprs_with_u32() {
1110        let mut ctx = Context::<TypeSet32>::new_with_types();
1111
1112        ctx.add_variable::<u32>("value", 30);
1113        ctx.add_function("func", |v: u32| 2 * v);
1114        ctx.add_function("func2", |v1: u32, v2: u32| v1 + v2);
1115
1116        assert_eq!(ctx.evaluate::<bool>("value / 5 == 6"), Ok(true));
1117        assert_eq!(ctx.evaluate::<u32>("func(20) / 5"), Ok(8));
1118        assert_eq!(ctx.evaluate::<u32>("func2(20, 20) / 5"), Ok(8));
1119    }
1120
1121    #[test]
1122    fn test_evaluating_exprs_with_u128() {
1123        let mut ctx = Context::<TypeSet128>::new_with_types();
1124
1125        ctx.add_variable::<u128>("value", 30);
1126        ctx.add_function("func", |v: u128| 2 * v);
1127        ctx.add_function("func2", |v1: u128, v2: u128| v1 + v2);
1128
1129        assert_eq!(ctx.evaluate::<bool>("value / 5 == 6"), Ok(true));
1130        assert_eq!(ctx.evaluate::<u128>("func(20) / 5"), Ok(8));
1131        assert_eq!(ctx.evaluate::<u128>("func2(20, 20) / 5"), Ok(8));
1132    }
1133
1134    #[test]
1135    fn test_evaluate_function() {
1136        let mut ctx =
1137            Context::parse("fn multiply_with_global(a: int) -> int { return a * global; }")
1138                .unwrap();
1139
1140        ctx.add_variable::<u64>("global", 3);
1141
1142        assert_eq!(
1143            ctx.evaluate::<bool>("multiply_with_global(2) == 6"),
1144            Ok(true)
1145        );
1146        assert!(ctx
1147            .evaluate::<bool>("multiply_with_global(\"2\") == 6")
1148            .is_err());
1149    }
1150
1151    #[test]
1152    fn run_eval_tests() {
1153        fn filter(path: &Path) -> bool {
1154            let Ok(env) = std::env::var("TEST_FILTER") else {
1155                // No filter set, walk folders and somni source files.
1156                return path.is_dir() || path.extension().map_or(false, |ext| ext == "sm");
1157            };
1158
1159            Path::new(&env) == path
1160        }
1161
1162        fn walk(dir: &Path, on_file: &impl Fn(&Path)) {
1163            for entry in std::fs::read_dir(dir)
1164                .unwrap_or_else(|_| panic!("Folder not found: {}", dir.display()))
1165                .flatten()
1166            {
1167                let path = entry.path();
1168
1169                if !filter(&path) {
1170                    continue;
1171                }
1172
1173                if path.is_file() {
1174                    on_file(&path);
1175                } else {
1176                    walk(&path, on_file);
1177                }
1178            }
1179        }
1180
1181        fn run_eval_test(path: &Path) {
1182            type Types = WithIterator<DefaultTypeSet>;
1183
1184            fn parse(source: &str) -> Context<'_, Types> {
1185                let mut context = Context::<Types>::parse_with_types(source).unwrap();
1186
1187                context.add_function("add_from_rust", |a: u64, b: u64| -> i64 { (a + b) as i64 });
1188                context.add_function("assert", |a: bool| a); // No-op to test calling Rust functions from expressions
1189                context.add_function("reverse", |s: &str| s.chars().rev().collect::<String>());
1190                context.add_function("range", |a: u64, b: u64| {
1191                    SomniIterator::new((a..b).map(TypedValue::<DefaultTypeSet>::Int))
1192                });
1193
1194                context
1195            }
1196
1197            let test_name = path.file_stem().unwrap();
1198            let parent = path.parent().unwrap().canonicalize().unwrap();
1199            let vm_error = parent.join(test_name).join("stderr");
1200            let expr_error = parent.join(test_name).join("stderr_expr");
1201            let source = std::fs::read_to_string(path).unwrap();
1202
1203            let expressions = source
1204                .lines()
1205                .filter_map(|line| line.trim().strip_prefix("//@"))
1206                .collect::<Vec<_>>();
1207
1208            let mut context = parse(&source);
1209            let fail_expected = std::fs::exists(&expr_error).unwrap_or(false)
1210                || std::fs::exists(&vm_error).unwrap_or(false);
1211
1212            let blessed = std::env::var("BLESS").as_deref() == Ok("1");
1213
1214            for expression in &expressions {
1215                let expression = if let Some(e) = expression.strip_prefix('+') {
1216                    // `//@+` preserves VM state (like changes to globals)
1217                    e.trim()
1218                } else {
1219                    // `//@` resets VM state (like changes to globals)
1220                    context = parse(&source);
1221                    expression
1222                };
1223                println!("Running `{expression}`");
1224                match context.evaluate::<TypedValue<Types>>(expression) {
1225                    Ok(_) if fail_expected => {
1226                        panic!(
1227                            "Expected {} to fail evaluating, but it succeeded",
1228                            path.display()
1229                        )
1230                    }
1231                    Ok(value) => assert_eq!(
1232                        value,
1233                        TypedValue::Bool(true),
1234                        "{}: Expression `{expression}` evaluated to {value:?}",
1235                        path.display()
1236                    ),
1237                    Err(e) if fail_expected => {
1238                        let error = strip_ansi(format!("{e:?}"));
1239                        if blessed {
1240                            std::fs::write(&expr_error, error).unwrap();
1241                        } else {
1242                            let expected_error = std::fs::read_to_string(&expr_error).unwrap();
1243                            pretty_assertions::assert_eq!(strip_ansi(expected_error), error);
1244                        }
1245                    }
1246                    Err(e) => panic!("{}: {e:?}", path.display()),
1247                };
1248            }
1249        }
1250
1251        walk("../tests/eval".as_ref(), &|path| {
1252            run_eval_test(path);
1253        });
1254    }
1255
1256    #[test]
1257    fn test_eval_error() {
1258        let mut ctx = Context::new();
1259
1260        ctx.add_function("func", |v1: u64, v2: u64| v1 + v2);
1261
1262        let err = ctx
1263            .evaluate::<u64>("func(20, true)")
1264            .expect_err("Expected expression to return an error");
1265
1266        pretty_assertions::assert_eq!(
1267            strip_ansi(format!("\n{err:?}")),
1268            r#"
1269Evaluation error
1270 ---> at line 1 column 10
1271  |
12721 | func(20, true)
1273  |          ^^^^ func expects argument 1 to be u64, got bool"#,
1274        );
1275    }
1276}