exp_rs/types.rs
1//! Type definitions for the expression parser and evaluator.
2//!
3//! This module contains the core data structures used throughout the expression parser
4//! and evaluator, including the Abstract Syntax Tree (AST) representation, token types,
5//! function definitions, and other auxiliary types.
6
7extern crate alloc;
8
9#[cfg(test)]
10use crate::Real;
11#[cfg(not(test))]
12use crate::{Box, Real, String, Vec};
13#[cfg(not(test))]
14use alloc::rc::Rc;
15#[cfg(test)]
16use std::rc::Rc;
17#[cfg(test)]
18use std::boxed::Box;
19#[cfg(test)]
20use std::string::String;
21#[cfg(test)]
22use std::vec::Vec;
23
24/// Abstract Syntax Tree (AST) node representing an expression.
25///
26/// The AST is the core data structure used for representing parsed expressions.
27/// Each variant of this enum represents a different type of expression node,
28/// forming a tree structure that can be evaluated to produce a result.
29#[derive(Clone, Debug, PartialEq)]
30pub enum AstExpr {
31 /// A literal numerical value.
32 ///
33 /// Examples: `3.14`, `42`, `-1.5`
34 Constant(Real),
35
36 /// A named variable reference.
37 ///
38 /// Examples: `x`, `temperature`, `result`
39 Variable(String),
40
41 /// A function call with a name and list of argument expressions.
42 ///
43 /// Examples: `sin(x)`, `max(a, b)`, `sqrt(x*x + y*y)`
44 Function {
45 /// The name of the function being called
46 name: String,
47 /// The arguments passed to the function
48 args: Vec<AstExpr>
49 },
50
51 /// An array element access.
52 ///
53 /// Examples: `array[0]`, `values[i+1]`
54 Array {
55 /// The name of the array
56 name: String,
57 /// The expression for the index
58 index: Box<AstExpr>
59 },
60
61 /// An attribute access on an object.
62 ///
63 /// Examples: `point.x`, `settings.value`
64 Attribute {
65 /// The base object name
66 base: String,
67 /// The attribute name
68 attr: String
69 },
70}
71
72impl AstExpr {
73 /// Helper method that raises a constant expression to a power.
74 ///
75 /// This is primarily used in testing to evaluate power operations on constants.
76 /// For non-constant expressions, it returns 0.0 as a default value.
77 ///
78 /// # Parameters
79 ///
80 /// * `exp` - The exponent to raise the constant to
81 ///
82 /// # Returns
83 ///
84 /// The constant raised to the given power, or 0.0 for non-constant expressions
85 pub fn pow(self, exp: Real) -> Real {
86 match self {
87 #[cfg(feature = "f32")]
88 AstExpr::Constant(val) => libm::powf(val, exp),
89 #[cfg(not(feature = "f32"))]
90 AstExpr::Constant(val) => libm::pow(val, exp),
91 _ => 0.0, // Default for non-constant expressions
92 }
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99 use crate::error::ExprError;
100 use crate::eval::eval_ast;
101
102 #[test]
103 fn test_eval_ast_array_and_attribute_errors() {
104 // Array not found
105 let ast = AstExpr::Array {
106 name: "arr".to_string(),
107 index: Box::new(AstExpr::Constant(0.0)),
108 };
109 let err = eval_ast(&ast, None).unwrap_err();
110 match err {
111 ExprError::UnknownVariable { name } => assert_eq!(name, "arr"),
112 _ => panic!("Expected UnknownVariable error"),
113 }
114 // Attribute not found
115 let ast2 = AstExpr::Attribute {
116 base: "foo".to_string(),
117 attr: "bar".to_string(),
118 };
119 let err2 = eval_ast(&ast2, None).unwrap_err();
120 match err2 {
121 ExprError::AttributeNotFound { base, attr } => {
122 assert_eq!(base, "foo");
123 assert_eq!(attr, "bar");
124 }
125 _ => panic!("Expected AttributeNotFound error"),
126 }
127 }
128
129 #[test]
130 fn test_eval_ast_function_wrong_arity() {
131 // sin with 2 args (should be 1)
132 let ast = AstExpr::Function {
133 name: "sin".to_string(),
134 args: vec![AstExpr::Constant(1.0), AstExpr::Constant(2.0)],
135 };
136 let err = eval_ast(&ast, None).unwrap_err();
137 match err {
138 ExprError::InvalidFunctionCall {
139 name,
140 expected,
141 found,
142 } => {
143 assert_eq!(name, "sin");
144 assert_eq!(expected, 1);
145 assert_eq!(found, 2);
146 }
147 _ => panic!("Expected InvalidFunctionCall error"),
148 }
149 }
150
151 #[test]
152 fn test_eval_ast_unknown_function_and_variable() {
153 // Unknown function
154 let ast = AstExpr::Function {
155 name: "notafunc".to_string(),
156 args: vec![AstExpr::Constant(1.0)],
157 };
158 let err = eval_ast(&ast, None).unwrap_err();
159 match err {
160 ExprError::UnknownFunction { name } => assert_eq!(name, "notafunc"),
161 _ => panic!("Expected UnknownFunction error"),
162 }
163 // Unknown variable
164 let ast2 = AstExpr::Variable("notavar".to_string());
165 let err2 = eval_ast(&ast2, None).unwrap_err();
166 match err2 {
167 ExprError::UnknownVariable { name } => assert_eq!(name, "notavar"),
168 _ => panic!("Expected UnknownVariable error"),
169 }
170 }
171}
172
173/// Classifies the kind of expression node in the AST.
174///
175/// This enum is used to categorize expression nodes at a higher level than the specific
176/// AST node variants, making it easier to determine the general type of an expression
177/// without matching on all variants.
178#[derive(Copy, Clone, PartialEq, Eq, Debug)]
179pub enum ExprKind {
180 /// A constant numerical value.
181 Constant,
182
183 /// A variable reference.
184 Variable,
185
186 /// A function call with a specific arity (number of arguments).
187 Function {
188 /// Number of arguments the function takes
189 arity: usize
190 },
191
192 /// An array element access.
193 Array,
194
195 /// An object attribute access.
196 Attribute,
197}
198
199/// Classifies the kind of token produced during lexical analysis.
200///
201/// These token types are used by the lexer to categorize different elements
202/// in the expression string during the parsing phase.
203#[derive(Copy, Clone, PartialEq, Eq, Debug)]
204pub enum TokenKind {
205 /// A numerical literal.
206 Number,
207
208 /// A variable identifier.
209 Variable,
210
211 /// An operator such as +, -, *, /, ^, etc.
212 Operator,
213
214 /// An opening delimiter like '(' or '['.
215 Open,
216
217 /// A closing delimiter like ')' or ']'.
218 Close,
219
220 /// A separator between items, typically a comma.
221 Separator,
222
223 /// End of the expression.
224 End,
225
226 /// An error token representing invalid input.
227 Error,
228
229 /// A null or placeholder token.
230 Null,
231}
232
233/*
234 All legacy bitmasking, ExprType, and OperatorKind have been removed.
235 All parser and evaluator logic now uses AstExpr and enums only.
236 The old Expr struct and related types are no longer present.
237 Next: Update and simplify the test suite to use the new AST parser and evaluator.
238*/
239
240
241/// Represents a native Rust function that can be registered with the evaluation context.
242///
243/// Native functions allow users to extend the expression evaluator with custom
244/// functionality written in Rust. These functions can be called from within expressions
245/// like any built-in function.
246///
247/// # Example
248///
249/// ```
250/// # use exp_rs::{EvalContext, Real};
251/// # use exp_rs::engine::interp;
252/// # use std::rc::Rc;
253/// let mut ctx = EvalContext::new();
254///
255/// // Register a custom function that calculates the hypotenuse
256/// ctx.register_native_function(
257/// "hypotenuse", // Function name
258/// 2, // Takes 2 arguments
259/// |args: &[Real]| { // Implementation
260/// (args[0] * args[0] + args[1] * args[1]).sqrt()
261/// }
262/// );
263///
264/// // Use the function in an expression
265/// let result = interp("hypotenuse(3, 4)", Some(Rc::new(ctx))).unwrap();
266/// assert_eq!(result, 5.0);
267/// ```
268#[derive(Clone)]
269pub struct NativeFunction<'a> {
270 /// Number of arguments the function takes.
271 pub arity: usize,
272
273 /// The actual implementation of the function as a Rust closure.
274 pub implementation: Rc<dyn Fn(&[Real]) -> Real>,
275
276 /// The name of the function as it will be used in expressions.
277 pub name: Cow<'a, str>,
278
279 /// Optional description of what the function does.
280 pub description: Option<String>,
281}
282
283/* We can't derive Clone for NativeFunction because Box<dyn Fn> doesn't implement Clone.
284 Instead, we provide a shallow clone in context.rs for EvalContext, which is safe for read-only use.
285 Do NOT call .clone() on NativeFunction directly. */
286
287use alloc::borrow::Cow;
288
289/// Represents a function defined by an expression string rather than Rust code.
290///
291/// Expression functions allow users to define custom functions using the expression
292/// language itself. These functions are compiled once when registered and can be called
293/// from other expressions. They support parameters and can access variables from the
294/// evaluation context.
295///
296/// # Example
297///
298/// ```
299/// # use exp_rs::{EvalContext, Real};
300/// # use exp_rs::engine::interp;
301/// # use std::rc::Rc;
302/// let mut ctx = EvalContext::new();
303///
304/// // Register a function to calculate the area of a circle
305/// ctx.register_expression_function(
306/// "circle_area", // Function name
307/// &["radius"], // Parameter names
308/// "pi * radius * radius" // Function body as an expression
309/// ).unwrap();
310///
311/// // Use the function in another expression
312/// let result = interp("circle_area(2)", Some(Rc::new(ctx))).unwrap();
313/// assert!(result > 12.56 && result < 12.57); // π * 4 ≈ 12.566
314/// ```
315#[derive(Clone)]
316pub struct ExpressionFunction {
317 /// The name of the function as it will be used in expressions.
318 pub name: String,
319
320 /// The parameter names that the function accepts.
321 pub params: Vec<String>,
322
323 /// The original expression string defining the function body.
324 pub expression: String,
325
326 /// The pre-compiled AST of the expression for faster evaluation.
327 pub compiled_ast: AstExpr,
328
329 /// Optional description of what the function does.
330 pub description: Option<String>,
331}
332
333
334/// Internal representation of a variable in the evaluation system.
335///
336/// This is an implementation detail and should not be used directly by library users.
337/// Variables are normally managed through the `EvalContext` interface.
338#[doc(hidden)]
339#[derive(Debug, Clone)]
340pub struct Variable<'a> {
341 /// The name of the variable.
342 pub name: Cow<'a, str>,
343
344 /// Internal address/identifier for the variable.
345 pub address: i8,
346
347 /// Function associated with the variable (if any).
348 pub function: fn(Real, Real) -> Real,
349
350 /// Context or associated AST nodes.
351 pub context: Vec<AstExpr>,
352}
353
354impl<'a> Variable<'a> {
355 /// Creates a new variable with the given name and default values.
356 pub fn new(name: &'a str) -> Variable<'a> {
357 Variable {
358 name: Cow::Borrowed(name),
359 address: 0,
360 function: crate::functions::dummy,
361 context: Vec::<AstExpr>::new(),
362 }
363 }
364}