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}