aimx/
expression.rs

1//! Top-level expression parsing and evaluation for the AIMX language.
2//!
3//! This module defines the [`Expression`] enum that represents the root of the Abstract Syntax Tree (AST)
4//! for parsed AIMX expressions. It provides parsing functions that handle the complete
5//! AIMX grammar hierarchy, including arrays, argument lists, and the full operator precedence chain.
6//!
7//! The module bridges the gap between raw text input and the executable expression tree that can be
8//! evaluated within a [`Context`](crate::Context). It orchestrates the parsing of complex expressions
9//! by delegating to specialized sub-parsers for different operator precedence levels.
10//!
11//! # Expression Structure
12//!
13//! The `Expression` enum represents the top-level AST node that can contain:
14//!
15//! - Empty expressions (no operation)
16//! - Single conditional expressions (the most common case)
17//! - Arrays of expressions separated by commas
18//! - Argument lists for function calls
19//! - Flattened primary expressions (for performance optimization)
20//!
21//! # Usage
22//!
23//! The primary way to parse expressions is through the public [`aimx_parse`](crate::aimx_parse) function:
24//!
25//! ```rust
26//! use aimx::{aimx_parse, ExpressionLike, Context};
27//!
28//! // Parse and evaluate a simple expression
29//! let expression = aimx_parse("2 + 3 * 4");
30//! let mut context = Context::new();
31//! let result = expression.evaluate(&mut context).unwrap();
32//! assert_eq!(result.to_string(), "14");
33//!
34//! // Parse and evaluate array expressions
35//! let expression = aimx_parse("(1, 2, 3, 4, 5)");
36//! // Arrays are evaluated as Value::Array
37//! ```
38//!
39//! For more advanced usage, the parsing functions in this module can be used directly:
40//!
41//! ```rust
42//! use aimx::expression::{parse_expression, parse_argument_list};
43//!
44//! // Direct parsing (returns nom::IResult)
45//! let result = parse_expression("2 + 3 * 4");
46//! assert!(result.is_ok());
47//!
48//! // Parse argument lists for function calls
49//! let result = parse_argument_list("1, 2, 3");
50//! assert!(result.is_ok());
51//! ```
52//!
53//! # See Also
54//!
55//! - [`aimx_parse`](crate::aimx_parse) - Public API function for parsing expressions
56//! - [`ExpressionLike`] - Trait for expression evaluation
57//! - [`expressions`](crate::expressions) - Module containing operator-specific parsers
58
59use nom::{
60    IResult,
61    Parser,
62    character::complete::{char, multispace0},
63    multi::many1,
64};
65use crate::{
66    expressions::{parse_closure, parse_conditional, Closure, Conditional}, values::Errata, ContextLike, ExpressionLike, Primary, Value, Writer
67};
68use std::fmt;
69use anyhow::Result;
70
71/// The root of the Abstract Syntax Tree (AST) for parsed AIMX expressions.
72///
73/// This enum represents all possible types of expressions that can be parsed
74/// and evaluated in the AIMX language. It encompasses the complete grammar
75/// hierarchy from the top-level expression types down to individual literals
76/// and references through the flattened [`Primary`] variant.
77///
78/// The `Expression` enum serves as the entry point for expression evaluation
79/// and provides methods for checking expression properties and invoking evaluation.
80///
81/// # Variants
82///
83/// - [`Empty`](Expression::Empty) - An empty expression representing no operation
84/// - [`Errata`](Expression::Errata) - An expression containing parsing or evaluation errors
85/// - [`Single`](Expression::Single) - A single conditional expression (ternary operator)
86/// - [`Array`](Expression::Array) - An array of conditional expressions separated by commas
87/// - [`ArgumentList`](Expression::ArgumentList) - A list of closure expressions for function arguments
88/// - [`Primary`](Expression::Primary) - A flattened primary expression for optimization
89///
90/// # AST Flattening
91///
92/// The `Primary` variant represents an optimization where expressions that consist
93/// solely of primary expressions (literals, references, parentheses) are flattened
94/// to reduce AST depth and improve evaluation performance. This flattening occurs
95/// during parsing when an expression doesn't contain any higher-level operators.
96///
97/// # Examples
98///
99/// Parsing expressions with the public API:
100///
101/// ```rust
102/// use aimx::{aimx_parse, ExpressionLike, Context};
103///
104/// // Parse a simple literal (gets flattened to Primary)
105/// let expression = aimx_parse("42");
106/// assert!(matches!(expression, aimx::Expression::Primary(_)));
107///
108/// // Parse conditional expressions (not flattened)
109/// let expression = aimx_parse("true ? 1 : 0");
110/// assert!(matches!(expression, aimx::Expression::Single(_)));
111///
112/// // Parse another conditional expression
113/// let expression = aimx_parse("5 > 3 ? \"yes\" : \"no\"");
114/// assert!(matches!(expression, aimx::Expression::Single(_)));
115/// ```
116///
117/// # See Also
118///
119/// - [`parse_expression`] - Function that produces `Expression` values
120/// - [`ExpressionLike`] - Trait for expression evaluation
121/// - [`Primary`] - Flattened primary expression type
122#[derive(Debug, Clone, PartialEq)]
123pub enum Expression {
124    Empty,
125    Errata(Errata),
126    Single(Conditional),
127    Array(Vec<Box<Conditional>>),
128    ArgumentList(Vec<Box<Closure>>),
129    /// Primary flattened AST optimization
130    Primary(Box<Primary>),
131}
132
133impl Expression {
134    /// Check if this expression is empty.
135    ///
136    /// Returns `true` if this expression represents an empty expression
137    /// (no operation), `false` otherwise.
138    ///
139    /// # Examples
140    ///
141    /// ```rust
142    /// use aimx::expression::{parse_expression, Expression};
143    ///
144    /// let (_, empty_expr) = parse_expression("").unwrap();
145    /// assert!(empty_expr.is_empty());
146    ///
147    /// let (_, non_empty_expr) = parse_expression("42").unwrap();
148    /// assert!(!non_empty_expr.is_empty());
149    /// ```
150    pub fn is_empty(&self) -> bool {
151        match self {
152            Expression::Empty => true,
153            _ => false,
154        }
155    }
156
157    /// Check if this expression contains an error.
158    ///
159    /// Returns `true` if this expression represents an error condition
160    /// (parsing failure or evaluation error), `false` otherwise.
161    ///
162    /// # Examples
163    ///
164    /// ```rust
165    /// use aimx::expression::{parse_expression, Expression};
166    /// use aimx::{aimx_parse, ExpressionLike};
167    ///
168    /// // Valid expressions do not have errors
169    /// let expression = aimx_parse("42");
170    /// assert!(!expression.has_error());
171    ///
172    /// // Parsing errors are wrapped in Errata variants
173    /// let expression = aimx_parse("2 + * 3"); // Invalid syntax
174    /// assert!(expression.has_error());
175    /// ```
176    pub fn has_error(&self) -> bool {
177        match self {
178            Expression::Errata{..} => true,
179            _ => false,
180        }
181    }
182
183    /// Evaluate this expression and return a [`Value`], handling errors gracefully.
184    /// Invoke is the evaluation entry point for expression formulas, whereas
185    /// [`Expression::evaluate`] is used within the expression to evaluate nested
186    /// expression instances.
187    ///
188    /// This method provides a simplified evaluation interface that catches
189    /// evaluation errors and converts them into [`Errata`] values.
190    /// Unlike [`evaluate`](ExpressionLike::evaluate), this method never returns
191    /// a [`Result`] error - all errors are captured in the returned [`Value`].
192    ///
193    /// # Arguments
194    ///
195    /// * `context` - The evaluation context providing variable values and function implementations
196    ///
197    /// # Returns
198    ///
199    /// Returns a [`Value`] representing the result of evaluation. If evaluation
200    /// succeeds, returns the computed value. If evaluation fails, returns an
201    /// [`Errata`] value containing error information.
202    ///
203    /// # Examples
204    ///
205    /// ```rust
206    /// use aimx::expression::{parse_expression, Expression};
207    /// use aimx::{Context, ExpressionLike};
208    ///
209    /// // Parse an expression
210    /// let (_, expression) = parse_expression("2 + 2").unwrap();
211    /// let mut context = Context::new();
212    /// let result = expression.invoke(&mut context);
213    /// assert_eq!(result.to_string(), "4");
214    /// ```
215    ///
216    /// # See Also
217    ///
218    /// - [`ExpressionLike::evaluate`] - The underlying evaluation method that returns [`Result`]
219    /// - [`Value`] - The result type returned by evaluation
220    pub fn invoke(&self, context: &mut dyn ContextLike) -> Value {
221        match self {
222            Expression::Empty => Value::Empty,
223            Expression::Errata(errata) => Value::Errata(errata.clone()),
224            _ => match self.evaluate(context) {
225                Ok(value) => value,
226                Err(err) => {
227                    let message = format!("{}", err);
228                    let formula = self.to_formula();
229                    if let Some((reason, location)) = message.rsplit_once('~') {
230                        Errata::new_reason_formula_location(reason.to_string(), formula, location.to_string())
231                    } else {
232                        Errata::new_reason_formula(message, formula)
233                    }
234                }
235            }
236        }
237    }
238}
239
240/// Parse a comma-separated argument list for function calls.
241///
242/// This function handles parsing of argument lists used in function calls,
243/// method calls, and inference calls. Argument lists can be:
244/// - Empty: `()`
245/// - Single argument: `(expression)`
246/// - Multiple arguments: `(expr1, expr2, expr3)`
247///
248/// The function parses arguments as [`Closure`] expressions,
249/// which allows for complex expressions including closures and procedures.
250///
251/// # Arguments
252///
253/// * `input` - The input string containing the argument list to parse
254///
255/// # Returns
256///
257/// Returns an [`IResult`] containing:
258/// - The remaining unparsed input
259/// - An [`Expression`] representing the parsed argument list
260///
261/// # Grammar
262///
263/// The argument list grammar follows:
264/// ```text
265/// argument_list = closure (S? ',' S? closure)*
266/// ```
267///
268/// Where `closure` represents any valid AIMX expression that can be used
269/// as a function argument.
270///
271/// # Examples
272///
273/// ```rust
274/// use aimx::expression::{parse_argument_list, Expression};
275///
276/// // Parse an empty argument list
277/// let (remaining, expr) = parse_argument_list("").unwrap();
278/// assert_eq!(remaining, "");
279/// assert!(matches!(expr, Expression::Empty));
280///
281/// // Parse a single argument
282/// let (remaining, expr) = parse_argument_list("42").unwrap();
283/// assert_eq!(remaining, "");
284/// assert!(matches!(expr, Expression::ArgumentList(_)));
285///
286/// // Parse multiple arguments
287/// let (remaining, expr) = parse_argument_list("1, 2, 3").unwrap();
288/// assert_eq!(remaining, "");
289/// assert!(matches!(expr, Expression::ArgumentList(_)));
290/// ```
291///
292/// # See Also
293///
294/// - [`parse_expression`] - Main expression parsing function
295/// - [`Closure`] - Type used for argument expressions
296/// - [`Expression::ArgumentList`] - The variant returned for argument lists
297pub fn parse_argument_list(input: &str) -> IResult<&str, Expression> {
298    let input = input.trim();
299    // Expression is empty
300    if input.is_empty() {
301        return Ok((input, Expression::Empty));
302    }
303    let (input, closure) = parse_closure(input)?;
304    let mut array = vec![Box::new(closure)];
305    if let Ok((input, closure_list)) = many1(closure_array).parse(input) {
306        for closure in closure_list {
307            array.push(Box::new(closure));
308        }
309        let (input, _) = multispace0(input)?;
310        Ok((input, Expression::ArgumentList(array)))
311    } else {
312        let (input, _) = multispace0(input)?;
313        Ok((input, Expression::ArgumentList(array)))
314    }
315}
316
317/// Parse a complete AIMX expression string into an abstract syntax tree.
318///
319/// This is the main parsing function that handles the top-level grammar rule:
320/// `expression = S? array S?`
321///
322/// The function orchestrates the parsing of complex expressions by delegating
323/// to specialized sub-parsers for different operator precedence levels. It
324/// handles the complete AIMX grammar including arrays, conditionals, and
325/// all operator types.
326///
327/// # Arguments
328///
329/// * `input` - The input string containing the AIMX expression to parse
330///
331/// # Returns
332///
333/// Returns an [`IResult`] containing:
334/// - The remaining unparsed input (should be empty for successful parsing)
335/// - An [`Expression`] representing the parsed abstract syntax tree
336///
337/// # Grammar
338///
339/// The expression grammar follows this hierarchy:
340/// ```text
341/// expression     = S? array S?
342/// array          = conditional (S? ',' S? conditional)*
343/// conditional    = closure ('?' expression ':' expression)?
344/// closure        = logical_or ('=>' procedure)?
345/// logical_or     = logical_and (('|' | '||') logical_and)*
346/// logical_and    = equality (('&' | '&&') equality)*
347/// equality       = relational (('=' | '!=') relational)*
348/// relational     = additive (('<' | '<=' | '>' | '>=') additive)*
349/// additive       = multiplicative (('+' | '-') multiplicative)*
350/// multiplicative = unary (('*' | '/' | '%') unary)*
351/// unary          = ('!' | '+' | '-' | '(' type ')')? postfix
352/// postfix        = primary (('.' method_call) | function_call | indexing)*
353/// primary        = literal | reference | '(' expression ')'
354/// ```
355///
356/// # Examples
357///
358/// Parsing expressions directly with this function:
359///
360/// ```rust
361/// use aimx::expression::{parse_expression, Expression};
362///
363/// // Parse arithmetic expressions
364/// let result = parse_expression("2 + 3 * 4");
365/// assert!(result.is_ok());
366///
367/// // Parse array expressions
368/// let result = parse_expression("(1, 2, 3)");
369/// assert!(result.is_ok());
370///
371/// // Parse conditional expressions
372/// let result = parse_expression("5 > 3 ? 'yes' : 'no'");
373/// assert!(result.is_ok());
374///
375/// // Parse flattened primary expressions
376/// let result = parse_expression("42");
377/// assert!(result.is_ok());
378/// ```
379///
380/// # Error Handling
381///
382/// This function returns [`IResult`] which uses Rust's Result type for error handling.
383/// Parsing errors are captured as [`nom`] error variants. For a more user-friendly
384/// parsing interface, use [`aimx_parse`](crate::aimx_parse) which wraps this function
385/// and provides better error messages.
386///
387/// # See Also
388///
389/// - [`aimx_parse`](crate::aimx_parse) - Public API function for parsing expressions
390/// - [`parse_argument_list`] - Function for parsing function argument lists
391/// - [`Expression`] - The abstract syntax tree returned by this function
392pub fn parse_expression(input: &str) -> IResult<&str, Expression> {
393    let input = input.trim();
394    // Expression is empty
395    if input.is_empty() {
396        return Ok((input, Expression::Empty));
397    }
398    let (input, conditional) = parse_conditional(input)?;
399    if let Ok((input, conditional_list)) = many1(conditional_array).parse(input) {
400        let mut array = vec![Box::new(conditional)];
401        for conditional in conditional_list {
402            array.push(Box::new(conditional));
403        }
404        let (input, _) = multispace0(input)?;
405        Ok((input, Expression::Array(array)))
406    } else {
407        let expression = match conditional {
408            Conditional::Primary(primary) => Expression::Primary(primary),
409            _ => Expression::Single(conditional),
410        };
411        let (input, _) = multispace0(input)?;
412        Ok((input, expression))
413    }
414}
415
416/// Parse a single closure element in an argument list.
417///
418/// This helper function parses a comma-separated closure expression
419/// within an argument list. It handles the whitespace around the comma
420/// separator.
421///
422/// # Grammar
423///
424/// ```text
425/// closure_array = S? ',' S? closure
426/// ```
427fn closure_array(input: &str) -> IResult<&str, Closure> {
428    let (input, _) = multispace0.parse(input)?;
429    let (input, _) = char(',').parse(input)?;
430    let (input, _) = multispace0.parse(input)?;
431    let (input, closure) = parse_closure(input)?;
432    Ok((input, closure))
433}
434
435/// Parse a single conditional element in an array expression.
436///
437/// This helper function parses a comma-separated conditional expression
438/// within an array. It handles the whitespace around the comma separator.
439///
440/// # Grammar
441///
442/// ```text
443/// conditional_array = S? ',' S? conditional
444/// ```
445fn conditional_array(input: &str) -> IResult<&str, Conditional> {
446    let (input, _) = multispace0.parse(input)?;
447    let (input, _) = char(',').parse(input)?;
448    let (input, _) = multispace0.parse(input)?;
449    let (input, conditional) = parse_conditional(input)?;
450    Ok((input, conditional))
451}
452
453impl ExpressionLike for Expression {
454    /// Evaluate this expression within the given context. Evaluate is called within
455    /// an expression formula to evaluate nested expressions.
456    ///
457    /// This method recursively evaluates the expression tree, delegating
458    /// to the appropriate evaluation methods for each expression variant.
459    /// 
460    /// [`Expression::invoke`] is the evaluation entry point for expression formulas.
461    ///
462    /// # Arguments
463    ///
464    /// * `context` - The evaluation context providing variable values and function implementations
465    ///
466    /// # Returns
467    ///
468    /// Returns a [`Result`] containing the evaluated [`Value`] if successful,
469    /// or an error if evaluation fails.
470    ///
471    /// # Evaluation Strategy
472    ///
473    /// - [`Empty`](Expression::Empty) expressions return [`Value::Empty`]
474    /// - [`Errata`](Expression::Errata) expressions return [`Value::Empty`]
475    /// - [`Single`](Expression::Single) expressions delegate to [`Conditional::evaluate`]
476    /// - [`Array`](Expression::Array) expressions evaluate each element and return an array
477    /// - [`ArgumentList`](Expression::ArgumentList) expressions evaluate each closure and return an array
478    /// - [`Primary`](Expression::Primary) expressions delegate to [`Primary::evaluate`]
479    fn evaluate(&self, context: &mut dyn ContextLike) -> Result<Value> {
480        match self {
481            Expression::Empty
482            | Expression::Errata{..} => Ok(Value::Empty),
483            Expression::Single(conditional) => conditional.evaluate(context),
484            Expression::Array(array) => {
485                let mut value_array: Vec<Box<Value>> = Vec::new();
486                for conditional in array {
487                    let value = conditional.evaluate(context)?;
488                    value_array.push(Box::new(value));
489                }
490                Ok(Value::Array(value_array))
491            },
492            Expression::ArgumentList(array) => {
493                let mut value_array: Vec<Box<Value>> = Vec::new();
494                for closure in array {
495                    let value = closure.evaluate(context)?;
496                    value_array.push(Box::new(value));
497                }
498                Ok(Value::Array(value_array))
499            },
500            Expression::Primary(primary) => primary.evaluate(context),
501        }
502    }
503
504    /// Write this expression to a [`Writer`] for formatting.
505    ///
506    /// This method recursively writes the expression tree to the provided
507    /// writer, producing a formatted representation of the expression.
508    ///
509    /// # Arguments
510    ///
511    /// * `writer` - The writer to write the formatted expression to
512    fn write(&self, writer: &mut Writer) {
513        match self {
514            Expression::Empty => {},
515            Expression::Errata(errata) => errata.write(writer),
516            Expression::Single(conditional) => conditional.write(writer),
517            Expression::Array(conditionals) => {
518                if let Some((first, rest)) = conditionals.split_first() {
519                    first.write(writer);
520                    for conditional in rest {
521                        writer.write_str(", ");
522                        conditional.write(writer);
523                    }
524                }
525            },
526            Expression::ArgumentList(closures) => {
527                if let Some((first, rest)) = closures.split_first() {
528                    first.write(writer);
529                    for closure in rest {
530                        writer.write_str(", ");
531                        closure.write(writer);
532                    }
533                }
534            },
535            Expression::Primary(primary) => primary.write(writer),
536        }
537    }
538
539    /// Convert this expression to a sanitized string representation.
540    ///
541    /// Sanitized expressions are suitable for display purposes and may
542    /// omit certain implementation details or sensitive information.
543    fn to_sanitized(&self) -> String {
544        let mut writer = Writer::sanitizer();
545        self.write(&mut writer);
546        writer.finish()
547    }
548
549    /// Convert this expression to its original formula representation.
550    ///
551    /// The formula representation attempts to reconstruct the original
552    /// expression text as it was parsed.
553    fn to_formula(&self) -> String {
554        let mut writer = Writer::formulizer();
555        self.write(&mut writer);
556        writer.finish()
557    }
558}
559
560impl fmt::Display for Expression {
561    /// Format this expression as a string.
562    ///
563    /// This implementation delegates to the [`write`](ExpressionLike::write) method
564    /// using a string-based writer.
565    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
566        let mut writer = Writer::stringizer();
567        self.write(&mut writer);
568        write!(f, "{}", writer.finish())
569    }
570}