aimx/expressions/
expression.rs

1//! Top-level AIMX expression AST and parser.
2//!
3//! Provides [`Expression`] as the root AST node plus helpers to parse full
4//! expressions and argument lists used by higher-level APIs.
5use crate::{
6    aim::{ContextLike, Writer, WriterLike},
7    expressions::{
8        Branch, Closure, Conditional, ExpressionLike, Primary, Retry, parse_closure, parse_conditional
9    },
10    values::{Errata, Value},
11};
12use anyhow::Result;
13use nom::{
14    IResult, Parser,
15    character::complete::{char, multispace0},
16    multi::many1, sequence::preceded,
17};
18use std::{
19    fmt,
20    sync::Arc,
21};
22
23/// Root AST node for parsed AIMX expressions.
24///
25/// Variants cover empty input, parse/evaluation errors, single expressions,
26/// comma-separated arrays, function argument lists, control-flow constructs
27/// (`Branch`, `Retry`), and flattened primaries for simple expressions.
28/// Used by higher-level APIs as the evaluatable expression representation.
29#[derive(Debug, Clone, PartialEq)]
30pub enum Expression {
31    Empty,
32    Errata(Errata),
33    Single(Conditional),
34    Array(Vec<Box<Conditional>>),
35    ArgumentList(Vec<Box<Closure>>),
36    Branch(Branch),
37    Retry(Retry),
38    /// Primary flattened AST optimization
39    Primary(Box<Primary>),
40}
41
42impl Expression {
43    pub fn convert(input: &str) -> Self {
44        match parse_expression(input) {
45            Ok((_, expression)) => expression,
46            Err(nom::Err::Incomplete(_)) => {
47                Errata::new_expression(
48                    Arc::from("Incomplete Expression"),
49                    Arc::from(input),
50                )
51            }
52            Err(nom::Err::Error(e)) | Err(nom::Err::Failure(e)) => {
53                Errata::new_expression_location(
54                    Arc::from(format!("Syntax Error ({})", e)),
55                    Arc::from(input),
56                    Arc::from(e.input),
57                )
58            }
59        }
60    }
61    /// Returns `true` if this is [`Expression::Empty`].
62    pub fn is_empty(&self) -> bool {
63        match self {
64            Expression::Empty => true,
65            _ => false,
66        }
67    }
68
69    /// Returns `true` if this is [`Expression::Errata`].
70    pub fn is_error(&self) -> bool {
71        match self {
72            Expression::Errata(_) => true,
73            _ => false,
74        }
75    }
76
77    /// Returns `true` if this is [`Expression::Branch`].
78    pub fn is_branch(&self) -> bool {
79        match self {
80            Expression::Branch(_) => true,
81            _ => false,
82        }
83    }
84
85    /// Returns `true` if this is [`Expression::Retry`].
86    pub fn is_retry(&self) -> bool {
87        match self {
88            Expression::Retry(_) => true,
89            _ => false,
90        }
91    }
92
93    /// Write this expression to a [`Writer`] for formatting output.
94    pub fn print(&self, writer: &mut Writer) {
95        match self {
96            Expression::Empty => {}
97            Expression::Errata(errata) => errata.print(writer),
98            Expression::Single(conditional) => conditional.print(writer),
99            Expression::Array(conditionals) => {
100                if let Some((first, rest)) = conditionals.split_first() {
101                    first.print(writer);
102                    for conditional in rest {
103                        writer.write_str(", ");
104                        conditional.print(writer);
105                    }
106                }
107            }
108            Expression::ArgumentList(closures) => {
109                if let Some((first, rest)) = closures.split_first() {
110                    first.print(writer);
111                    for closure in rest {
112                        writer.write_str(", ");
113                        closure.print(writer);
114                    }
115                }
116            }
117            Expression::Branch(branch) => branch.print(writer),
118            Expression::Retry(retry) => retry.print(writer),
119            Expression::Primary(primary) => primary.print(writer),
120        }
121    }
122}
123
124/// Parse a comma-separated argument list for function calls.
125///
126/// Returns [`Expression::Empty`] for no arguments or
127/// [`Expression::ArgumentList`] containing [`Closure`] nodes.
128/// Accepts raw input without surrounding parentheses.
129///
130/// Input and remaining slice follow `nom`'s [`IResult`] conventions.
131pub fn parse_argument_list(input: &str) -> IResult<&str, Expression> {
132    let input = input.trim();
133    // Expression is empty
134    if input.is_empty() {
135        return Ok((input, Expression::Empty));
136    }
137    let (input, closure) = parse_closure(input)?;
138    let mut array = vec![Box::new(closure)];
139    if let Ok((input, closure_list)) = many1(closure_array).parse(input) {
140        for closure in closure_list {
141            array.push(Box::new(closure));
142        }
143        let (input, _) = multispace0(input)?;
144        Ok((input, Expression::ArgumentList(array)))
145    } else {
146        let (input, _) = multispace0(input)?;
147        Ok((input, Expression::ArgumentList(array)))
148    }
149}
150
151/// Parse a complete AIMX expression string into an [`Expression`].
152///
153/// Entry-point for constructing the AST from source. Trims input, returns
154/// [`Expression::Empty`] for empty input, flattens simple primaries into
155/// [`Expression::Primary`], and otherwise yields `Single`/`Array` as needed.
156///
157/// Input and remaining slice follow `nom`'s [`IResult`] conventions.
158pub fn parse_expression(input: &str) -> IResult<&str, Expression> {
159    let input = input.trim();
160    // Expression is empty
161    if input.is_empty() {
162        return Ok((input, Expression::Empty));
163    }
164    let (input, conditional) = parse_conditional(input)?;
165    if let Ok((input, conditional_list)) = many1(conditional_array).parse(input) {
166        let mut array = vec![Box::new(conditional)];
167        for conditional in conditional_list {
168            array.push(Box::new(conditional));
169        }
170        let (input, _) = multispace0(input)?;
171        Ok((input, Expression::Array(array)))
172    } else {
173        let expression = match conditional {
174            Conditional::Primary(primary) => Expression::Primary(primary),
175            _ => Expression::Single(conditional),
176        };
177        let (input, _) = multispace0(input)?;
178        Ok((input, expression))
179    }
180}
181
182/// Parse a single closure element in an argument list (internal helper).
183fn closure_array(input: &str) -> IResult<&str, Closure> {
184    preceded(
185        (multispace0, char(','), multispace0),
186        parse_closure
187    ).parse(input)
188}
189
190/// Parse a single conditional element in an array expression (internal helper).
191fn conditional_array(input: &str) -> IResult<&str, Conditional> {
192    preceded(
193        (multispace0, char(','), multispace0),
194        parse_conditional
195    ).parse(input)
196}
197
198impl ExpressionLike for Expression {
199    /// Evaluate this expression within the given [`ContextLike`].
200    ///
201    /// Propagates [`Errata`] as [`Value::Errata`], short-circuits on
202    /// error-valued elements in arrays/argument lists, and delegates to
203    /// inner node evaluators for other variants.
204    fn evaluate(&self, context: &mut dyn ContextLike) -> Result<Arc<Value>> {
205        match self {
206            Expression::Empty => Ok(Value::empty()),
207            Expression::Errata(errata) => Ok(Arc::new(Value::Errata(errata.clone()))),
208            Expression::Single(conditional) => conditional.evaluate(context),
209            Expression::Array(array) => {
210                let mut value_array: Vec<Arc<Value>> = Vec::new();
211                for conditional in array {
212                    let value = conditional.evaluate(context)?;
213                    if value.is_error() {
214                        return Ok(value);
215                    }
216                    value_array.push(value);
217                }
218                Ok(Arc::new(Value::Array(Arc::new(value_array))))
219            }
220            Expression::ArgumentList(array) => {
221                let mut value_array: Vec<Arc<Value>> = Vec::new();
222                for closure in array {
223                    let value = closure.evaluate(context)?;
224                    if value.is_error() {
225                        return Ok(value);
226                    }
227                    value_array.push(value);
228                }
229                Ok(Arc::new(Value::Array(Arc::new(value_array))))
230            }
231            Expression::Branch(branch) => branch.evaluate(context),
232            Expression::Retry(retry) => retry.evaluate(context),
233            Expression::Primary(primary) => primary.evaluate(context),
234        }
235    }
236
237    /// Return the formula-string representation (round-trippable by the parser).
238    fn to_formula(&self) -> String {
239        let mut writer = Writer::formulizer();
240        self.print(&mut writer);
241        writer.finish()
242    }
243}
244
245impl WriterLike for Expression {
246    fn write(&self, writer: &mut Writer) {
247        self.print(writer);
248    }
249}
250
251impl fmt::Display for Expression {
252    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253        write!(f, "{}", self.to_stringized())
254    }
255}