aimx/expressions/
primary.rs

1//! Primary expression parsing for AIMX.
2//!
3//! Provides parsing and evaluation for primary expressions: literals, references,
4//! collections, parentheses, and flattened higher-precedence operators.
5//! Used internally by the expression parser and evaluator.
6
7use crate::{
8    aim::{ContextLike, WriterLike, Writer},
9    expressions::{
10        Additive, Closure, Coalesce, Collection, Conditional, Equality, Expression, ExpressionLike,
11        LogicalAnd, LogicalOr, Multiplicative, Postfix, Reference, Relational, Unary,
12        parse_collection, parse_expression, parse_reference,
13    },
14    literals::{Literal, parse_literal},
15    values::Value,
16};
17use anyhow::Result;
18use nom::{
19    IResult, Parser,
20    branch::alt,
21    character::complete::{char, multispace0},
22    combinator::map, sequence::delimited,
23};
24use std::{
25    fmt,
26    sync::Arc,
27};
28
29/// Primary expression node.
30///
31/// Wraps literals, references, parentheses, collections, and flattened
32/// higher-precedence operators for unified parsing and evaluation.
33/// `Postfix` through `Closure` variants are populated by the parser to keep
34/// the AST shallow and evaluation efficient.
35#[derive(Debug, Clone, PartialEq)]
36pub enum Primary {
37    /// A literal value such as a number, text string, boolean, date, or task
38    Literal(Literal),
39    /// A workflow rule reference
40    Reference(Arc<Reference>),
41    /// A parenthesized expression
42    Parentheses(Expression),
43    // A Collection expression
44    Collection(Collection),
45
46    // Flattened abstract syntax tree
47    Postfix(Postfix),
48    Unary(Unary),
49    Multiplicative(Multiplicative),
50    Additive(Additive),
51    Relational(Relational),
52    Equality(Equality),
53    LogicalAnd(LogicalAnd),
54    LogicalOr(LogicalOr),
55    Coalesce(Coalesce),
56    Conditional(Conditional),
57    Closure(Closure),
58}
59
60impl Primary {
61    /// Returns `true` if this `Primary` is [`Primary::Literal`].
62    pub fn is_literal(&self) -> bool {
63        match self {
64            Primary::Literal(_) => true,
65            _ => false,
66        }
67    }
68
69    /// Returns `true` if this `Primary` is [`Primary::Reference`].
70    pub fn is_reference(&self) -> bool {
71        match self {
72            Primary::Reference(_) => true,
73            _ => false,
74        }
75    }
76
77    /// Write this primary expression to a [`Writer`].
78    pub fn print(&self, writer: &mut Writer) {
79        match self {
80            Primary::Literal(literal) => literal.print(writer),
81            Primary::Reference(reference) => reference.print(writer),
82            Primary::Parentheses(expression) => {
83                writer.write_char('(');
84                expression.print(writer);
85                writer.write_char(')');
86            }
87            Primary::Collection(collection) => collection.print(writer),
88
89            Primary::Postfix(postfix) => postfix.print(writer),
90            Primary::Unary(unary) => unary.print(writer),
91            Primary::Multiplicative(multiplicative) => multiplicative.print(writer),
92            Primary::Additive(additive) => additive.print(writer),
93            Primary::Relational(relational) => relational.print(writer),
94            Primary::Equality(equality) => equality.print(writer),
95            Primary::LogicalAnd(logical_and) => logical_and.print(writer),
96            Primary::LogicalOr(logical_or) => logical_or.print(writer),
97            Primary::Coalesce(coalesce) => coalesce.print(writer),
98            Primary::Conditional(conditional) => conditional.print(writer),
99            Primary::Closure(closure) => closure.print(writer),
100        }
101    }
102}
103
104/// Parse a primary expression.
105///
106/// Recognizes literals, references, collections, and parenthesized expressions,
107/// and emits a [`Primary`] node. Used internally by the expression parser.
108///
109/// # Grammar
110///
111/// The primary expression grammar follows:
112/// ```text
113/// primary = literal | reference | '(' expression ')'
114/// ```
115///
116/// Where:
117/// - `literal` represents any valid literal value (number, text, boolean, date, task)
118/// - `reference` represents a variable or field reference
119/// - `expression` represents any valid AIMX expression
120pub fn parse_primary(input: &str) -> IResult<&str, Primary> {
121    alt((
122        map(parse_literal, Primary::Literal),
123        map(parse_reference, Primary::Reference),
124        map(parse_collection, |collection| {
125            Primary::Collection(collection)
126        }),
127        map(parse_parentheses, |expression| {
128            Primary::Parentheses(expression)
129        }),
130    ))
131    .parse(input)
132}
133
134/// Parse a parenthesized expression.
135///
136/// Accepts `()` as [`Expression::Empty`] and otherwise parses an inner expression
137/// between `(` and `)` with optional surrounding whitespace.
138pub fn parse_parentheses(input: &str) -> IResult<&str, Expression> {
139    alt((
140        // Empty parentheses case
141        (
142            char('('),
143            multispace0,
144            char(')'),
145        ).map(|_| Expression::Empty),
146        
147        // Non-empty parentheses case
148        delimited(
149            char('('),
150            delimited(multispace0, parse_expression, multispace0),
151            char(')'),
152        ),
153    )).parse(input)
154}
155
156impl ExpressionLike for Primary {
157    /// Evaluate this primary expression in the given context.
158    fn evaluate(&self, context: &mut dyn ContextLike) -> Result<Arc<Value>> {
159        match self {
160            Primary::Literal(literal) => {
161                if literal.is_empty() {
162                    Ok(Value::empty())
163                } else {
164                    Ok(Arc::new(Value::Literal(literal.clone())))
165                }
166            }
167            Primary::Reference(reference) => reference.evaluate(context),
168            Primary::Parentheses(expression) => expression.evaluate(context),
169            Primary::Collection(collection) => collection.evaluate(context),
170
171            Primary::Postfix(postfix) => postfix.evaluate(context),
172            Primary::Unary(unary) => unary.evaluate(context),
173            Primary::Multiplicative(multiplicative) => multiplicative.evaluate(context),
174            Primary::Additive(additive) => additive.evaluate(context),
175            Primary::Relational(relational) => relational.evaluate(context),
176            Primary::Equality(equality) => equality.evaluate(context),
177            Primary::LogicalAnd(logical_and) => logical_and.evaluate(context),
178            Primary::LogicalOr(logical_or) => logical_or.evaluate(context),
179            Primary::Coalesce(coalesce) => coalesce.evaluate(context),
180            Primary::Conditional(conditional) => conditional.evaluate(context),
181            Primary::Closure(closure) => closure.evaluate(context),
182        }
183    }
184
185    /// Return the formula-string representation (round-trippable by the parser).
186    fn to_formula(&self) -> String {
187        let mut writer = Writer::formulizer();
188        self.print(&mut writer);
189        writer.finish()
190    }
191}
192
193impl WriterLike for Primary {
194    fn write(&self, writer: &mut Writer) {
195        self.print(writer);
196    }
197}
198
199impl fmt::Display for Primary {
200    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201        write!(f, "{}", self.to_stringized())
202    }
203}