aimx/parser.rs
1//! Main parsing interface for AIMX expressions.
2//!
3//! This module provides the primary entry point for parsing AIM Expressions (AIMX)
4//! strings into evaluatable abstract syntax trees. It serves as a wrapper around the
5//! internal parsing logic with enhanced error handling and validation.
6//!
7//! The parser transforms AIMX source code into an [`Expression`] AST that can be
8//! evaluated within a [`Context`](crate::context::Context). It handles the complete AIMX
9//! grammar including arrays, closures, conditionals, and all operator precedence rules.
10
11use crate::{
12 Expression, parse_expression,
13 values::Errata,
14};
15
16/// Parse an AIMX expression string into an Abstract Syntax Tree (AST).
17///
18/// This is the main entry point for parsing AIM Expressions. It transforms a string
19/// representation of an expression into an [`Expression`] AST that can be evaluated
20/// within a [`Context`](crate::context::Context). The parser handles the complete AIMX grammar
21/// including operator precedence, type annotations, arrays, closures, and conditional expressions.
22///
23/// # Arguments
24///
25/// * `input` - A string slice containing the AIMX expression to parse
26///
27/// # Returns
28///
29/// Returns an [`Expression`] representing the parsed abstract syntax tree. If parsing
30/// succeeds, the returned expression can be evaluated to produce a [`Value`](crate::value::Value).
31/// If parsing fails, the returned expression will contain error information wrapped
32/// in an [`Errata`] variant.
33///
34/// # Grammar Coverage
35///
36/// The parser supports the complete AIMX grammar including:
37/// - Arithmetic expressions: `2 + 3 * 4`
38/// - Boolean logic: `true & false | !flag`
39/// - Conditional expressions: `5 > 3 ? "yes" : "no"`
40/// - Function calls: `sqrt(16) + abs(-5)`
41/// - Array expressions: `(1, 2, 3).sum()`
42/// - Closures: `x => x * 2`
43/// - Type annotations: `(Number) "42"`
44/// - Task definitions: `[x] "Completed task"`
45/// - Variable references: `employee.salary * 0.15`
46///
47/// # Error Handling
48///
49/// The parser returns an [`Expression`] that may contain error information in the
50/// following scenarios:
51///
52/// - **Syntax Errors**: Malformed expressions that violate AIMX grammar rules
53/// - **Incomplete Expressions**: Input that ends before a complete expression is parsed
54/// - **Unexpected Input**: Valid expressions followed by additional unparsed text
55///
56/// Errors are represented as [`Errata`] variants within the returned [`Expression`],
57/// allowing the calling code to handle parsing failures gracefully.
58///
59/// # Examples
60///
61/// Basic usage for parsing and evaluating expressions:
62///
63/// ```rust
64/// use aimx::{aimx_parse, ExpressionLike, Context};
65///
66/// // Parse a simple arithmetic expression
67/// let expression = aimx_parse("2 + 3 * 4");
68/// let mut context = Context::new();
69/// let result = expression.evaluate(&mut context).unwrap();
70/// assert_eq!(result.to_string(), "14");
71///
72/// // Parse a complex expression with functions and arrays
73/// let expression = aimx_parse("sqrt(pow(2, 2) + max((1, 3))).round_to(5)");
74/// let result = expression.evaluate(&mut context).unwrap();
75/// assert_eq!(result.to_string(), "2.64575");
76///
77/// // Or use a simpler example:
78/// let expression = aimx_parse("sqrt(16)");
79/// let result = expression.evaluate(&mut context).unwrap();
80/// assert_eq!(result.to_string(), "4");
81///
82/// // Handle parsing errors gracefully
83/// let expression = aimx_parse("2 + * 3"); // Invalid syntax
84/// if expression.has_error() {
85/// println!("Parsing failed: {}", expression);
86/// }
87/// ```
88///
89/// # Implementation Details
90///
91/// The parser uses a recursive descent approach built on the [`nom`](https://docs.rs/nom) parser combinator
92/// library. It handles operator precedence through a hierarchy of expression types,
93/// with the highest precedence operators being parsed first. The parser is designed
94/// to be efficient and handle the complete AIMX grammar without backtracking.
95///
96/// After successful parsing, the function validates that no input remains unparsed.
97/// If additional text follows a valid expression, it is treated as an error to
98/// prevent ambiguity in expression boundaries.
99///
100/// # Panics
101///
102/// This function does not panic. All parsing errors are captured and returned
103/// as [`Errata`] variants within the [`Expression`] result.
104///
105/// # See Also
106///
107/// - [`Expression`] - The abstract syntax tree returned by the parser
108/// - [`ExpressionLike`](crate::evaluate::ExpressionLike) - Trait for evaluating expressions
109/// - [`Context`](crate::context::Context) - Evaluation context for expression evaluation
110/// - [`Value`](crate::value::Value) - Runtime values produced by expression evaluation
111/// - [`Errata`] - Error information wrapper
112pub fn aimx_parse(input: &str) -> Expression {
113 match parse_expression(input) {
114 Ok((remaining, expression)) => {
115 if remaining.is_empty() {
116 expression
117 } else {
118 // Unexpected input remaining that wasn't parsed
119 Errata::new_reason_expression_location(
120 "Unexpected Input Remaining".to_owned(),
121 input.to_string(),
122 remaining.to_string()
123 )
124 }
125 }
126 Err(nom::Err::Incomplete(_)) => {
127 Errata::new_reason_expression(
128 "Incomplete Expression".to_owned(),
129 input.to_string()
130 )
131 }
132 Err(nom::Err::Error(e)) | Err(nom::Err::Failure(e)) => {
133 Errata::new_reason_expression_location(
134 format!("Syntax Error ({})", e),
135 input.to_string(),
136 e.input.to_string()
137 )
138 }
139 }
140}