aimx/expressions/
multiplicative.rs

1//! Multiplicative expression parsing and evaluation.
2//!
3//! This module handles parsing and evaluating multiplicative expressions using
4//! the `*` (multiplication), `/` (division), and `%` (modulus) operators.
5//! Multiplicative operators have left associativity and higher precedence
6//! than additive operators (`+`, `-`).
7//!
8//! Multiplicative expressions are a fundamental part of the AIMX grammar hierarchy 
9//! and provide the building blocks for arithmetic expressions. The module implements 
10//! the core parsing logic and evaluation behavior for these operations, including 
11//! type promotion and error handling.
12//!
13//! # Grammar
14//!
15//! Multiplicative expressions are parsed according to the following grammar:
16//!
17//! ```text
18//! multiplicative := unary (S? ('*' | '/' | '%') S? unary)*
19//! ```
20//!
21//! Where `S` represents optional whitespace. The parser builds an AST with
22//! left associativity, ensuring operations are grouped from left to right.
23//!
24//! # Operator Precedence
25//!
26//! Multiplicative operators have the following precedence characteristics:
27//!
28//! - **Higher precedence than**: additive (`+`, `-`), relational (`<`, `<=`, `>`, `>=`), 
29//!   equality (`=`, `!=`), logical (`&`, `|`), conditional (`?`, `:`), 
30//!   closure (`=>`), procedure (`;`)
31//! - **Lower precedence than**: unary (`!`, `+`, `-`, casts), 
32//!   postfix (`.`, `()`, `[]`, `$`), primary (literals, references, parentheses)
33//!
34//! # Examples
35//!
36//! ```text
37//! 2 * 3                    // 6
38//! 10 / 2                   // 5
39//! 17 % 5                   // 2
40//! 2 * 3 / 4 * 5            // 7.5 (left associative: ((2 * 3) / 4) * 5)
41//! 2 + 3 * 4                // 14 (multiplicative precedence: 2 + (3 * 4))
42//! (2 + 3) * 4              // 20 (parentheses override precedence: (2 + 3) * 4)
43//! ```
44//!
45//! # Type Behavior
46//!
47//! Multiplicative operations require numeric operands and support type promotion:
48//!
49//! - **Multiplication (`*`)**: `2 * 3` → `6`
50//! - **Division (`/`)**: `10 / 2` → `5`
51//! - **Modulus (`%`)**: `17 % 5` → `2`
52//! - **Type Promotion**: When operands have different types, the right operand
53//!   is promoted to match the left operand's type using AIMX's type promotion system
54//!
55//! # Error Handling
56//!
57//! During evaluation, multiplicative operations:
58//! - Require numeric operands (return error for non-numeric types)
59//! - Check for division by zero (return error for `/ 0` or `% 0`)
60//! - Use type promotion via [`evaluate_and_promote`]
61//! - Provide detailed error messages with type information
62//!
63//! # AST Structure
64//!
65//! The [`Multiplicative`] enum represents the AST structure, which supports
66//! both operations and flattened [`Primary`] expressions for optimization.
67//!
68//! # Examples
69//!
70//! ```rust
71//! use aimx::{aimx_parse, ExpressionLike, Context};
72//!
73//! // Parse and evaluate a simple multiplication
74//! let expression = aimx_parse("123 * 456");
75//! let mut context = Context::new();
76//! let result = expression.evaluate(&mut context).unwrap();
77//! assert_eq!(result.to_string(), "56088");
78//!
79//! // Parse chained operations (left associative)
80//! let expression = aimx_parse("100 * 50 / 25 % 5");
81//! // Parsed as ((100 * 50) / 25) % 5 = 0
82//! let result = expression.evaluate(&mut context).unwrap();
83//! assert_eq!(result.to_string(), "0");
84//! ```
85//!
86//! # Related Modules
87//!
88//! - [`unary`](crate::expressions::unary) - Higher precedence unary operations
89//! - [`additive`](crate::expressions::additive) - Lower precedence additive operations
90//! - [`expression`](crate::expression) - Top-level expression parsing
91//! - [`evaluate`](crate::evaluate) - Core evaluation traits
92
93use nom::{
94    IResult,
95    Parser,
96    branch::alt,
97    character::complete::{char, multispace0},
98    multi::many0,
99};
100use crate::{
101    Literal,
102    ContextLike,
103    evaluate_and_promote,
104    ExpressionLike,
105    expressions::{Unary, parse_unary},
106    Primary,
107    Value,
108    Writer,
109};
110use std::fmt;
111use anyhow::{anyhow, Result};
112
113/// A multiplicative expression node in the abstract syntax tree.
114///
115/// Represents a multiplicative operation (`*`, `/`, `%`) or a
116/// lower-precedence expression that has been flattened in the AST.
117/// This enum implements the [`ExpressionLike`] trait, allowing it
118/// to be evaluated within a context.
119///
120/// # Variants
121///
122/// - `Multiply(Box<Multiplicative>, Unary)` - Multiplication operation
123/// - `Divide(Box<Multiplicative>, Unary)` - Division operation
124/// - `Modulus(Box<Multiplicative>, Unary)` - Modulus operation
125/// - `Primary(Primary)` - Flattened variants from lower precedence levels
126///
127/// # AST Optimization
128///
129/// The `Primary` variant is used for AST flattening optimization, where
130/// expressions that don't contain multiplicative operators are stored
131/// directly rather than wrapped in unnecessary operation nodes.
132///
133/// # Error Handling
134///
135/// During evaluation, multiplicative operations:
136/// - Require numeric operands (return error for non-numeric types)
137/// - Check for division by zero (return error for `/ 0` or `% 0`)
138/// - Use type promotion via [`evaluate_and_promote`]
139///
140/// # Examples
141///
142/// ```rust
143/// use aimx::expressions::multiplicative::{Multiplicative, parse_multiplicative};
144/// use aimx::{aimx_parse, Literal};
145///
146/// // Parse a multiplication expression
147/// let (_, expr) = parse_multiplicative("2 * 3").unwrap();
148/// assert!(matches!(expr, Multiplicative::Multiply(_, _)));
149///
150/// // Parse a division expression
151/// let (_, expr) = parse_multiplicative("10 / 2").unwrap();
152/// assert!(matches!(expr, Multiplicative::Divide(_, _)));
153/// ```
154#[derive(Debug, Clone, PartialEq)]
155pub enum Multiplicative {
156    Multiply(Box<Multiplicative>, Unary),
157    Divide(Box<Multiplicative>, Unary),
158    Modulus(Box<Multiplicative>, Unary),
159    /// Primary flattened AST optimization
160    Primary(Box<Primary>),
161}
162
163/// Parse a multiplicative expression.
164///
165/// Parses expressions with multiplicative operators (`*`, `/`, `%`) according to left associativity.
166/// The parser consumes unary expressions as operands and builds an AST node
167/// representing the multiplicative operation.
168///
169/// # Arguments
170///
171/// * `input` - The input string slice to parse
172///
173/// # Returns
174///
175/// A `IResult` containing the remaining input and parsed `Multiplicative` expression.
176///
177/// # Grammar
178///
179/// ```text
180/// multiplicative := unary (S? ('*' | '/' | '%') S? unary)*
181/// ```
182///
183/// Where `S` represents optional whitespace.
184///
185/// # Examples
186///
187/// ```rust
188/// use aimx::expressions::multiplicative::parse_multiplicative;
189///
190/// // Simple multiplication
191/// let result = parse_multiplicative("2 * 3");
192/// assert!(result.is_ok());
193///
194/// // Chained operations (left associative)
195/// let result = parse_multiplicative("10 / 2 * 3");
196/// assert!(result.is_ok());
197/// // Parsed as (10 / 2) * 3 = 15
198///
199/// // With whitespace
200/// let result = parse_multiplicative("2   *   3");
201/// assert!(result.is_ok());
202///
203/// // Mixed operators
204/// let result = parse_multiplicative("2 * 3 % 4 / 5");
205/// assert!(result.is_ok());
206/// // Parsed as ((2 * 3) % 4) / 5 = 0.4
207/// ```
208pub fn parse_multiplicative(input: &str) -> IResult<&str, Multiplicative> {
209    let (input, first) = parse_unary(input)?;
210    let (input, rest) = many0((
211        multispace0, 
212        alt((char('*'), char('/'), char('%'))), 
213        multispace0, 
214        parse_unary
215    )).parse(input)?;
216    
217    // Build the result by folding from left to right
218    let result = rest.into_iter().fold(
219        match first {
220            Unary::Primary(primary) => Multiplicative::Primary(primary),
221            _ => Multiplicative::Primary(Box::new(Primary::Unary(first))),
222        },
223        |acc, (_, op, _, next)| {
224            match op {
225                '*' => Multiplicative::Multiply(Box::new(acc), next),
226                '/' => Multiplicative::Divide(Box::new(acc), next),
227                '%' => Multiplicative::Modulus(Box::new(acc), next),
228                _ => unreachable!(),
229            }
230        }
231    );
232    
233    Ok((input, result))
234}
235
236impl ExpressionLike for Multiplicative {
237    fn evaluate(&self, context: &mut dyn ContextLike) -> Result<Value> {
238        match self {
239            Multiplicative::Multiply(left, right) => {
240                let (left_val, right_val) =
241                    evaluate_and_promote(context, left.as_ref(), right)?;
242                // For multiplication, we need numeric operands
243                match (&left_val, &right_val) {
244                    (Value::Literal(Literal::Number(l)), Value::Literal(Literal::Number(r))) => Ok(Value::Literal(Literal::Number(l * r))),
245                    _ => Err(anyhow!(
246                        "Expected Number, found {} * {}~{}",
247                        left_val.type_as_string(),
248                        right_val.type_as_string(),
249                        self.to_formula(),
250                    )),
251                }
252            },
253            Multiplicative::Divide(left, right) => {
254                let (left_val, right_val) =
255                    evaluate_and_promote(context, left.as_ref(), right)?;
256                // For division, we need numeric operands
257                match (&left_val, &right_val) {
258                    (Value::Literal(Literal::Number(l)), Value::Literal(Literal::Number(r))) => {
259                        if *r == 0.0 {
260                            Err(anyhow!("Division by zero ~{}", self.to_formula()))
261                        } else {
262                            Ok(Value::Literal(Literal::Number(l / r)))
263                        }
264                    },
265                    _ => Err(anyhow!(
266                        "Expected Number, found {} / {}~{}",
267                        left_val.type_as_string(),
268                        right_val.type_as_string(),
269                        self.to_formula(),
270                    )),
271                }
272            },
273            Multiplicative::Modulus(left, right) => {
274                let (left_val, right_val) =
275                    evaluate_and_promote(context, left.as_ref(), right)?;
276                // For modulus, we need numeric operands
277                match (&left_val, &right_val) {
278                    (Value::Literal(Literal::Number(l)), Value::Literal(Literal::Number(r))) => {
279                        if *r == 0.0 {
280                            Err(anyhow!("Division by zero (modulus) ~{}", self.to_formula()))
281                        } else {
282                            Ok(Value::Literal(Literal::Number(l % r)))
283                        }
284                    },
285                    _ => Err(anyhow!(
286                        "Expected Number, found {} % {}~{}",
287                        left_val.type_as_string(),
288                        right_val.type_as_string(),
289                        self.to_formula(),
290                    )),
291                }
292            },
293            Multiplicative::Primary(primary) => primary.evaluate(context),
294        }
295    }
296
297    fn write(&self, writer: &mut Writer) {
298        match self {
299            Multiplicative::Multiply(left, right) => {
300                writer.write_binary_op(left.as_ref(), " * ", right);
301            },
302            Multiplicative::Divide(left, right) => {
303                writer.write_binary_op(left.as_ref(), " / ", right);
304            },
305            Multiplicative::Modulus(left, right) => {
306                writer.write_binary_op(left.as_ref(), " % ", right);
307            },
308            Multiplicative::Primary(primary) => primary.write(writer),
309        }
310    }
311    fn to_sanitized(&self) -> String {
312        let mut writer = Writer::sanitizer();
313        self.write(&mut writer);
314        writer.finish()
315    }
316    fn to_formula(&self) -> String {
317        let mut writer = Writer::formulizer();
318        self.write(&mut writer);
319        writer.finish()
320    }
321}
322
323impl fmt::Display for Multiplicative {
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}