aimx/expressions/
closure.rs

1//! Closure expression parsing and evaluation for the `=>` operator.
2//! Provides parsing of closure syntax and runtime representation used by AIMX.
3
4use crate::{
5    aim::{ContextLike, WriterLike, Writer},
6    expressions::{Conditional, ExpressionLike, Primary, parse_conditional, parse_arc_identifier},
7    values::Value,
8};
9use anyhow::Result;
10use nom::{
11    IResult, Parser,
12    bytes::tag,
13    character::complete::{char, multispace0},
14    sequence::{delimited, separated_pair},
15};
16use std::{
17    fmt,
18    sync::Arc,
19};
20
21/// Closure (anonymous function) expression.
22///
23/// Variants:
24/// - `One(name, body)` – single-parameter closure.
25/// - `Two(name1, name2, body)` – two-parameter closure.
26/// - `Primary(primary)` – primary/conditional expression treated as a closure body.
27#[derive(Debug, Clone, PartialEq)]
28pub enum Closure {
29    One(Arc<str>, Conditional),
30    Two(Arc<str>, Arc<str>, Conditional),
31    /// Primary flattened AST optimization.
32    Primary(Box<Primary>),
33}
34
35impl Closure {
36    /// Evaluate the closure body using parameter bindings already set in `context`.
37    pub fn invoke(&self, context: &mut dyn ContextLike) -> Result<Arc<Value>> {
38        match self {
39            Closure::Two(one, two, conditional) => {
40                context.set_key(0, one.clone());
41                context.set_key(1, two.clone());
42                conditional.evaluate(context)
43            }
44            Closure::One(one, conditional) => {
45                context.set_key(0, one.clone());
46                conditional.evaluate(context)
47            }
48            Closure::Primary(primary) => primary.evaluate(context),
49        }
50    }
51
52    /// Call the closure with argument value(s) using the AIMX calling convention.
53    ///
54    /// - Propagates `Value::Errata` without evaluation.
55    /// - Binds a single value to parameter index `0`.
56    /// - For arrays, binds element `0` to parameter `0` and element `1` (if present) to parameter `1`.
57    /// - Evaluates inside a closure scope (`start_closure` / `end_closure`).
58    pub fn call(&self, context: &mut dyn ContextLike, arg: Arc<Value>) -> Result<Arc<Value>> {
59        if arg.is_error() {
60            return Ok(arg);
61        }
62
63        let stack = context.start_closure();
64
65        match &*arg {
66            Value::Literal(_) | Value::Errata(_) | Value::Empty => {
67                context.set_parameter(0, arg);
68            }
69            Value::Array(array) => {
70                if !array.is_empty() {
71                    context.set_parameter(0, array[0].clone());
72                    if array.len() > 1 {
73                        context.set_parameter(1, array[1].clone());
74                    }
75                }
76            }
77            _ => {}
78        }
79
80        let result = self.invoke(context);
81        context.end_closure(stack);
82        result
83    }
84
85    /// Write the closure in AIMX syntax.
86    pub fn print(&self, writer: &mut Writer) {
87        match self {
88            Closure::One(one, conditional) => {
89                writer.write_str(one);
90                writer.write_str(" => ");
91                conditional.print(writer);
92            }
93            Closure::Two(one, two, conditional) => {
94                writer.write_char('(');
95                writer.write_str(one);
96                writer.write_str(", ");
97                writer.write_str(two);
98                writer.write_str(") => ");
99                conditional.print(writer);
100            }
101            Closure::Primary(primary) => primary.print(writer),
102        }
103    }
104}
105
106/// Parse a single-parameter closure: `identifier => expression`.
107fn single_closure(input: &str) -> IResult<&str, Closure> {
108    let (input, (one, _, conditional)) = (
109        parse_arc_identifier,
110        delimited(multispace0, tag("=>"), multispace0),
111        parse_conditional,
112    ).parse(input)?;
113    Ok((input, Closure::One(one, conditional)))
114}
115
116/// Parse a two-parameter closure: `(identifier, identifier) => expression`.
117fn double_closure(input: &str) -> IResult<&str, Closure> {
118    let (input, ((one, two), _, conditional)) = (delimited(
119        char('('),
120        separated_pair(
121            parse_arc_identifier,
122            delimited(multispace0, char(','), multispace0),
123            parse_arc_identifier,
124        ),
125        char(')'),
126        ),
127        delimited(multispace0, tag("=>"), multispace0),
128        parse_conditional,
129    ).parse(input)?;
130    Ok((input, Closure::Two(one, two, conditional)))
131}
132
133/// Parse a closure expression or fallback conditional body.
134///
135/// If standard closure syntax does not match, falls back to [`parse_closure_conditional`].
136pub fn parse_closure(input: &str) -> IResult<&str, Closure> {
137    if input.starts_with('(') {
138        if let Ok((input, closure)) = double_closure(input) {
139            return Ok((input, closure));
140        }
141    } else if let Ok((input, closure)) = single_closure(input) {
142        return Ok((input, closure));
143    }
144    parse_closure_conditional(input)
145}
146
147/// Parse a closure from a conditional expression as `Closure::Primary`.
148pub fn parse_closure_conditional(input: &str) -> IResult<&str, Closure> {
149    let (input, conditional) = parse_conditional(input)?;
150    let closure = match conditional {
151        Conditional::Primary(primary) => Closure::Primary(primary),
152        _ => Closure::Primary(Box::new(Primary::Conditional(conditional))),
153    };
154    Ok((input, closure))
155}
156
157impl ExpressionLike for Closure {
158    /// Evaluate to a runtime value.
159    ///
160    /// - `Primary` evaluates directly.
161    /// - Other variants return `Value::Closure` for later invocation.
162    fn evaluate(&self, context: &mut dyn ContextLike) -> Result<Arc<Value>> {
163        match self {
164            Closure::Primary(primary) => primary.evaluate(context),
165            // NOTE: CLONE HERE
166            _ => Ok(Arc::new(Value::Closure(Arc::new(self.clone())))),
167        }
168    }
169
170    /// Return the formula-string representation (round-trippable by the parser).
171    fn to_formula(&self) -> String {
172        let mut writer = Writer::formulizer();
173        self.print(&mut writer);
174        writer.finish()
175    }
176}
177
178impl WriterLike for Closure {
179    fn write(&self, writer: &mut Writer) {
180        self.print(writer);
181    }
182}
183
184impl fmt::Display for Closure {
185    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186        write!(f, "{}", self.to_stringized())
187    }
188}