aimx/expressions/
closure.rs

1
2//! # Closure expression parsing and invocation.
3//!
4//! This module provides parsing and evaluation support for closure expressions in the AIMX language.
5//! Closures are anonymous functions that can capture variables from their surrounding scope and are
6//! denoted by the `=>` operator.
7//!
8//! ## Syntax
9//!
10//! AIMX supports two forms of closures:
11//! - Single parameter: `param => expression`
12//! - Double parameter: `(param1, param2) => expression`
13//!
14//! Closures are first-class values that can be stored in variables, passed as arguments to functions,
15//! and returned from functions.
16//!
17//! ## Examples
18//!
19//! ```text
20//! // Simple closure with single parameter
21//! x => x * 2
22//!
23//! // Closure with two parameters
24//! (x, y) => x + y
25//!
26//! // Using closures with array operations
27//! nums.filter(x => x > 5)
28//! nums.map((item, index) => item * index)
29//! ```
30
31use nom::{
32    IResult,
33    Parser,
34    character::complete::{char, multispace0},
35    bytes::tag,
36};
37use crate::{
38    ContextLike,
39    ExpressionLike,
40    expressions::{Conditional, parse_conditional, parse_procedure, parse_identifier, Procedure},
41    Primary,
42    Value,
43    Writer
44};
45use std::fmt;
46use anyhow::Result;
47
48/// Represents a closure (anonymous function) expression in the AIMX language.
49///
50/// Closures capture their environment and can be used as first-class values.
51/// They are typically created using the `=>` operator and can have one or two parameters.
52///
53/// # Variants
54///
55/// - `One(String, Procedure)`: A closure with a single parameter
56/// - `Two(String, String, Procedure)`: A closure with two parameters  
57/// - `Primary(Box<Primary>)`: An optimized representation when the closure is a simple primary expression
58///
59/// # Examples
60///
61/// ```text
62/// // Parses to Closure::One("x", procedure)
63/// x => x + 1
64///
65/// // Parses to Closure::Two("x", "y", procedure)
66/// (x, y) => x + y
67/// ```
68#[derive(Debug, Clone, PartialEq)]
69pub enum Closure {
70    One(String, Procedure),
71    Two(String, String, Procedure),  
72    /// Primary flattened AST optimization - used when the closure body is a simple primary expression
73    Primary(Box<Primary>),
74}
75
76impl Closure {
77    /// Invokes the closure with the current context.
78    ///
79    /// This method evaluates the closure body after setting up the parameter bindings
80    /// in the context. For closures with parameters, the parameters are bound to
81    /// special context keys (0 for single parameter, 0 and 1 for double parameters).
82    ///
83    /// # Arguments
84    ///
85    /// * `context` - The evaluation context
86    ///
87    /// # Returns
88    ///
89    /// Returns `Result<Value>` containing the result of evaluating the closure body.
90    ///
91    /// # Examples
92    ///
93    /// ```rust
94    /// # use aimx::{expressions::Closure, context::{Context, ContextLike}, expressions::Procedure};
95    /// # use aimx::Primary;
96    /// # use anyhow::Result;
97    /// # fn example() -> Result<()> {
98    /// let mut context = Context::new();
99    /// let closure = Closure::One("x".to_string(), Procedure::Primary(Box::new(Primary::Literal(aimx::Literal::Number(42.0)))));
100    /// 
101    /// // Parameter values are set using the context's key mechanism
102    /// let param_name = "x".to_string();
103    /// context.set_key(0, &param_name);
104    /// let result = closure.invoke(&mut context)?;
105    /// // result should be Value::Literal(Literal::Number(42.0))
106    /// # Ok(())
107    /// # }
108    /// ```
109    pub fn invoke(&self, context: &mut dyn ContextLike) -> Result<Value> {
110        match self {
111            Closure::Two(one, two, conditional) => {
112                context.set_key(0, one);
113                context.set_key(1, two);
114                conditional.evaluate(context)
115            }
116            Closure::One(one, conditional) => {
117                context.set_key(0, one);
118                conditional.evaluate(context)
119            }
120            Closure::Primary(primary) => primary.evaluate(context),       
121        } 
122    }
123}
124
125/// Parse a single-parameter closure expression.
126///
127/// Handles the syntax: `identifier => procedure`
128fn single_closure(input: &str) -> IResult<&str, Closure> {
129    let (input, one) = parse_identifier(input)?;
130    let (input, _) = multispace0.parse(input)?;
131    let (input, _) = tag("=>").parse(input)?;
132    let (input, _) = multispace0.parse(input)?;
133    let (input, procedure) = parse_procedure(input)?;
134    Ok((input, Closure::One(one, procedure)))
135}
136
137/// Parse a two-parameter closure expression.
138///
139/// Handles the syntax: `(identifier, identifier) => procedure`
140fn double_closure(input: &str) -> IResult<&str, Closure> {
141    let (input, _) = char('(').parse(input)?;
142    let (input, one) = parse_identifier(input)?;
143    let (input, _) = multispace0.parse(input)?;
144    let (input, _) = char(',').parse(input)?;
145    let (input, _) = multispace0.parse(input)?;
146    let (input, two) = parse_identifier(input)?;
147    let (input, _) = multispace0.parse(input)?;
148    let (input, _) = char(')').parse(input)?;
149    let (input, _) = multispace0.parse(input)?;
150    let (input, _) = tag("=>").parse(input)?;
151    let (input, _) = multispace0.parse(input)?;
152    let (input, procedure) = parse_procedure(input)?;
153    Ok((input, Closure::Two(one, two, procedure)))
154}
155
156/// Parses a closure expression from the input string.
157///
158/// This is the main entry point for parsing closures. It attempts to parse
159/// both single-parameter and two-parameter closures, falling back to
160/// `parse_closure_conditional` if neither form matches.
161///
162/// # Arguments
163///
164/// * `input` - The input string to parse
165///
166/// # Returns
167///
168/// Returns `IResult<&str, Closure>` containing the remaining unparsed input
169/// and the parsed closure.
170///
171/// # Examples
172///
173/// ```rust
174/// # use aimx::expressions::closure::parse_closure;
175/// let result = parse_closure("x => x * 2");
176/// assert!(result.is_ok());
177/// 
178/// let result = parse_closure("(a, b) => a + b");
179/// assert!(result.is_ok());
180/// 
181/// let result = parse_closure("simple_expression");
182/// assert!(result.is_ok()); // Falls back to conditional parsing
183/// ```
184pub fn parse_closure(input: &str) -> IResult<&str, Closure> {
185    if input.starts_with('(') {
186        if let Ok((input, closure)) = double_closure(input) {
187            return Ok((input, closure));
188        }
189    } else if let Ok((input, closure)) = single_closure(input) {
190        return Ok((input, closure));
191    }
192    parse_closure_conditional(input)
193}
194
195/// Parses a closure that falls back to conditional expression parsing.
196///
197/// This function is used when the input doesn't match the standard closure
198/// syntax. It parses the input as a conditional expression and wraps it
199/// in a `Closure::Primary` variant.
200///
201/// # Arguments
202///
203/// * `input` - The input string to parse
204///
205/// # Returns
206///
207/// Returns `IResult<&str, Closure>` containing the remaining unparsed input
208/// and the parsed closure.
209pub fn parse_closure_conditional(input: &str) -> IResult<&str, Closure> {
210    let (input, conditional) = parse_conditional(input)?;
211    let closure = match conditional {
212        Conditional::Primary(primary) => Closure::Primary(primary),
213        _ => Closure::Primary(Box::new(Primary::Conditional(conditional))),
214    };
215    Ok((input, closure))
216}
217
218impl ExpressionLike for Closure {
219    /// Evaluates the closure expression.
220    ///
221    /// For primary closures (optimized simple expressions), this evaluates the expression directly.
222    /// For parameterized closures, this returns a `Value::Closure` containing the closure itself,
223    /// since closures with parameters need to be invoked with `invoke()` to evaluate them.
224    ///
225    /// # Arguments
226    ///
227    /// * `context` - The evaluation context
228    ///
229    /// # Returns
230    ///
231    /// Returns `Result<Value>` containing either the evaluated result (for primary closures)
232    /// or the closure itself as a value (for parameterized closures).
233    ///
234    /// # Examples
235    ///
236    /// ```rust
237    /// # use aimx::{Context, ExpressionLike};
238    /// # use aimx::expressions::{closure::Closure, primary::Primary};
239    /// # use anyhow::Result;
240    /// # fn example() -> Result<()> {
241    /// let mut context = Context::new();
242    /// let closure = Closure::Primary(Box::new(Primary::Literal(aimx::Literal::Number(42.0))));
243    /// let result = closure.evaluate(&mut context)?;
244    /// // result should be Value::Literal(Literal::Number(42.0))
245    /// # Ok(())
246    /// # }
247    /// ```
248    fn evaluate(&self, context: &mut dyn ContextLike) -> Result<Value> {
249        match self {
250            Closure::Primary(primary) => primary.evaluate(context),
251            _ => Ok(Value::Closure(self.clone())),
252        }
253    }
254
255    /// Writes the closure expression to a writer.
256    ///
257    /// This method formats the closure according to its variant and writes it
258    /// to the provided writer. The output matches the AIMX syntax for closures.
259    ///
260    /// # Arguments
261    ///
262    /// * `writer` - The writer to write the formatted closure to
263    fn write(&self, writer: &mut Writer) {
264        match self {
265            Closure::One(one, conditional) => {
266                writer.write_str(one);
267                writer.write_str(" => ");
268                conditional.write(writer);
269            },
270            Closure::Two(one, two, conditional) => {
271                writer.write_char('(');
272                writer.write_str(one);
273                writer.write_str(", ");
274                writer.write_str(two);
275                writer.write_str(") => ");
276                conditional.write(writer);
277            },
278            Closure::Primary(primary) => primary.write(writer),
279        }
280    }
281    
282    /// Returns a sanitized string representation of the closure.
283    ///
284    /// Sanitized output is suitable for display to users and may include
285    /// formatting that makes the expression easier to read.
286    ///
287    /// # Returns
288    ///
289    /// Returns a `String` containing the sanitized closure representation.
290    fn to_sanitized(&self) -> String {
291        let mut writer = Writer::sanitizer();
292        self.write(&mut writer);
293        writer.finish()
294    }
295    
296    /// Returns a formula string representation of the closure.
297    ///
298    /// Formula output is suitable for use as input to the parser and matches
299    /// the exact AIMX syntax.
300    ///
301    /// # Returns
302    ///
303    /// Returns a `String` containing the formula representation.
304    fn to_formula(&self) -> String {
305        let mut writer = Writer::formulizer();
306        self.write(&mut writer);
307        writer.finish()
308    }
309}
310
311impl fmt::Display for Closure {
312    /// Formats the closure for display.
313    ///
314    /// This implementation uses the `Writable` trait to format the closure
315    /// as a string suitable for user display.
316    ///
317    /// # Arguments
318    ///
319    /// * `f` - The formatter to write to
320    ///
321    /// # Returns
322    ///
323    /// Returns `fmt::Result` indicating success or failure.
324    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
325        let mut writer = Writer::stringizer();
326        self.write(&mut writer);
327        write!(f, "{}", writer.finish())
328    }
329}