aimx/expressions/
coalesce.rs

1//! Coalescing (`??`) expression.
2//!
3//! Represents `a ?? b`, which evaluates `a` and returns it unless it is
4//! `Value::Errata(_)` or `Value::Empty`, in which case `b` is evaluated and used.
5//! Left-associative for chained fallback.
6
7use crate::{
8    aim::{ContextLike, WriterLike, Writer},
9    expressions::{ExpressionLike, LogicalOr, Primary, parse_or},
10    values::Value,
11};
12use anyhow::Result;
13use nom::{IResult, Parser, bytes::complete::tag, character::complete::multispace0, multi::many0, sequence::delimited};
14use std::{
15    fmt,
16    sync::Arc,
17};
18
19/// A `??` coalescing expression in the AST.
20#[derive(Debug, Clone, PartialEq)]
21pub enum Coalesce {
22    /// `left ?? right`
23    Coalesce(Box<Coalesce>, LogicalOr),
24    /// Primary flattened AST optimization for expressions without `??`.
25    Primary(Box<Primary>),
26}
27
28impl Coalesce {
29    pub fn print(&self, writer: &mut Writer) {
30        match self {
31            Coalesce::Primary(primary) => primary.print(writer),
32            Coalesce::Coalesce(left, right) => {
33                left.print(writer);
34                writer.write_str(" ?? ");
35                right.print(writer);
36            }
37        }
38    }
39}
40
41fn coalesce_op(input: &str) -> IResult<&str, LogicalOr> {
42    let (input, _) = delimited(
43        multispace0, 
44        tag("??"), 
45        multispace0
46    ).parse(input)?;
47    parse_or(input)
48}
49
50/// Parse a coalescing expression.
51///
52/// Grammar:
53/// ```text
54/// coalesce := or (S? '??' S? or)*
55/// ```
56pub fn parse_coalesce(input: &str) -> IResult<&str, Coalesce> {
57    let (input, first) = parse_or(input)?;
58    let (input, chain) = many0(coalesce_op).parse(input)?;
59
60    let node = chain.into_iter().fold(
61        match first {
62            LogicalOr::Primary(primary) => Coalesce::Primary(primary),
63            _ => Coalesce::Primary(Box::new(Primary::LogicalOr(first))),
64        },
65        |left, rhs| Coalesce::Coalesce(Box::new(left), rhs),
66    );
67
68    Ok((input, node))
69}
70
71impl ExpressionLike for Coalesce {
72    fn evaluate(&self, context: &mut dyn ContextLike) -> Result<Arc<Value>> {
73        match self {
74            Coalesce::Primary(primary) => primary.evaluate(context),
75            Coalesce::Coalesce(left, right) => {
76                let left_val = left.evaluate(context)?;
77                if left_val.is_error() || left_val.is_empty() {
78                    let right_val = right.evaluate(context)?;
79                    Ok(right_val)
80                } else {
81                    Ok(left_val)
82                }
83            }
84        }
85    }
86
87    /// Return the formula-string representation (round-trippable by the parser).
88    fn to_formula(&self) -> String {
89        let mut writer = Writer::formulizer();
90        self.print(&mut writer);
91        writer.finish()
92    }
93}  
94
95impl WriterLike for Coalesce {
96    fn write(&self, writer: &mut Writer) {
97        self.print(writer);
98    }
99}
100
101impl fmt::Display for Coalesce {
102    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103        write!(f, "{}", self.to_stringized())
104    }
105}