exp_rs/
error.rs

1//! Error types and handling for the exp-rs crate.
2//!
3//! This module defines the error types used throughout the exp-rs crate for expression parsing
4//! and evaluation. It provides detailed error information to help diagnose issues in expressions.
5
6extern crate alloc;
7use alloc::string::String;
8#[cfg(not(test))]
9use core::num::ParseFloatError;
10#[cfg(test)]
11use std::num::ParseFloatError;
12
13#[cfg(not(test))]
14use core::result;
15#[cfg(test)]
16use std::result;
17
18/// Result type used throughout the crate.
19///
20/// This is a convenience type alias that uses the `ExprError` type for the error variant.
21pub type Result<T> = result::Result<T, ExprError>;
22
23/// Error type for expression parsing and evaluation.
24///
25/// This enum represents all possible errors that can occur during expression parsing,
26/// tokenization, and evaluation. It provides specific error variants with detailed
27/// information to help diagnose and fix issues.
28#[repr(C, align(4))]
29#[derive(Debug, Clone)]
30pub enum ExprError {
31    /// Error when parsing a floating point number.
32    ///
33    /// This occurs when a string cannot be converted to a floating point number.
34    /// For example, "3.a" is not a valid floating point number.
35    Parse(ParseFloatError),
36
37    /// Error during lexical analysis (tokenization).
38    ///
39    /// This occurs when the tokenizer encounters invalid tokens or unknown characters
40    /// that cannot be processed. The string contains a detailed error message.
41    Tokenizer(String),
42
43    /// Error during syntax analysis.
44    ///
45    /// This occurs when the parser encounters unexpected tokens, incorrect expression
46    /// structure, or other syntax issues. The string contains a detailed error message.
47    Syntax(String),
48
49    /// Error for unmatched parentheses in an expression.
50    ///
51    /// This provides the position of the unmatched parenthesis and the specific
52    /// parenthesis character that was found without a matching pair.
53    UnmatchedParenthesis { position: usize, found: String },
54
55    /// Error when a variable referenced in an expression is not defined.
56    ///
57    /// To resolve this error, make sure the variable is registered in the
58    /// evaluation context using `EvalContext::set_parameter`.
59    UnknownVariable { name: String },
60    /// Unknown function error
61    ///
62    /// This error is returned when a function is called that is not registered in the context
63    /// and is not a built-in (if built-ins are enabled). If the `libm` feature is not enabled,
64    /// users must register their own native functions for all required math operations.
65    ///
66    /// To resolve this error, register a native function with `EvalContext::register_native_function`
67    /// or an expression function with `EvalContext::register_expression_function`.
68    UnknownFunction { name: String },
69    /// Error when a function is called with the wrong number of arguments.
70    ///
71    /// This occurs when a function is called with fewer or more arguments than it expects.
72    /// The error includes the function name, the expected number of arguments, and the
73    /// actual number of arguments provided.
74    InvalidFunctionCall {
75        /// Name of the function that was called
76        name: String,
77        /// Expected number of arguments
78        expected: usize,
79        /// Actual number of arguments provided
80        found: usize,
81    },
82    /// Error when an array index is out of bounds.
83    ///
84    /// This occurs when trying to access an array element with an index that exceeds
85    /// the array's length. The error includes the array name, the attempted index,
86    /// and the actual length of the array.
87    ArrayIndexOutOfBounds {
88        /// Name of the array being accessed
89        name: String,
90        /// Index that was attempted to be accessed
91        index: usize,
92        /// Actual length of the array
93        len: usize,
94    },
95
96    /// Error when an attribute access is attempted on an object that doesn't have that attribute.
97    ///
98    /// This occurs when using the dot notation (e.g., `object.attribute`) and the attribute
99    /// does not exist on the specified object.
100    AttributeNotFound {
101        /// The base object name
102        base: String,
103        /// The attribute name that was not found
104        attr: String,
105    },
106
107    /// Error when division by zero is attempted.
108    ///
109    /// This occurs when a division operation has a zero divisor.
110    DivideByZero,
111
112    /// General-purpose error for any other error conditions.
113    ///
114    /// This is used for errors that don't fit into other specific categories.
115    /// The string contains a detailed error message.
116    Other(String),
117
118    /// Error when the recursion limit is exceeded during expression evaluation.
119    ///
120    /// This usually happens with deeply nested expressions or recursive function calls.
121    /// To resolve this, simplify the expression or increase the recursion limit if possible.
122    RecursionLimit(String),
123
124    /// Error when capacity is exceeded for a heapless container.
125    ///
126    /// This occurs when trying to insert into a full heapless container.
127    /// The string indicates which container type exceeded capacity.
128    CapacityExceeded(&'static str),
129
130    /// Error when a string is too long for heapless string buffer.
131    ///
132    /// This occurs when trying to create a heapless string that exceeds
133    /// the maximum string length limit.
134    StringTooLong(String, usize),
135
136    /// Error when attempting to add a parameter with a name that already exists.
137    ///
138    /// This occurs when trying to register a parameter that has already been registered.
139    DuplicateParameter(String),
140
141    /// Error when attempting to access a parameter by an invalid index.
142    ///
143    /// This occurs when the provided index is out of bounds for the parameter list.
144    InvalidParameterIndex(usize),
145}
146
147impl ExprError {
148    /// Convert this error to a numeric error code for FFI
149    pub fn error_code(&self) -> i32 {
150        match self {
151            ExprError::Parse(_) => 1,
152            ExprError::Tokenizer(_) => 2,
153            ExprError::Syntax(_) => 3,
154            ExprError::UnmatchedParenthesis { .. } => 4,
155            ExprError::UnknownVariable { .. } => 5,
156            ExprError::UnknownFunction { .. } => 6,
157            ExprError::InvalidFunctionCall { .. } => 7,
158            ExprError::ArrayIndexOutOfBounds { .. } => 8,
159            ExprError::AttributeNotFound { .. } => 9,
160            ExprError::DivideByZero => 10,
161            ExprError::RecursionLimit(_) => 11,
162            ExprError::CapacityExceeded(_) => 12,
163            ExprError::StringTooLong(_, _) => 13,
164            ExprError::DuplicateParameter(_) => 14,
165            ExprError::InvalidParameterIndex(_) => 15,
166            ExprError::Other(_) => 99,
167        }
168    }
169}
170
171#[cfg(not(test))]
172use core::fmt;
173#[cfg(test)]
174use std::fmt;
175
176impl fmt::Display for ExprError {
177    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178        match self {
179            ExprError::Parse(_) => write!(f, "Parse error"),
180            ExprError::Tokenizer(err) => write!(f, "Tokenizer error: {}", err),
181            ExprError::Syntax(err) => write!(f, "Syntax error: {}", err),
182            ExprError::UnmatchedParenthesis { position, found } => {
183                write!(
184                    f,
185                    "Unmatched parenthesis at position {}: found '{}'",
186                    position, found
187                )
188            }
189            ExprError::UnknownVariable { name } => {
190                write!(f, "Unknown variable: '{}'", name)
191            }
192            ExprError::UnknownFunction { name } => {
193                write!(f, "Unknown function: '{}'", name)
194            }
195            ExprError::InvalidFunctionCall {
196                name,
197                expected,
198                found,
199            } => {
200                write!(
201                    f,
202                    "Invalid function call to '{}': expected {} arguments, found {}",
203                    name, expected, found
204                )
205            }
206            ExprError::ArrayIndexOutOfBounds { name, index, len } => {
207                write!(
208                    f,
209                    "Array index out of bounds: index {} out of bounds for '{}', length {}",
210                    index, name, len
211                )
212            }
213            ExprError::AttributeNotFound { base, attr } => {
214                write!(f, "Attribute not found: '{}' in '{}'", attr, base)
215            }
216            ExprError::DivideByZero => write!(f, "Division by zero"),
217            ExprError::Other(err) => write!(f, "{}", err),
218            ExprError::RecursionLimit(err) => write!(f, "Recursion limit exceeded: {}", err),
219            ExprError::CapacityExceeded(container_type) => {
220                write!(f, "Capacity exceeded for {}", container_type)
221            }
222            ExprError::StringTooLong(s, max_len) => write!(f, "String too long for heapless buffer (max {} chars): '{}'", max_len, s),
223            ExprError::DuplicateParameter(name) => write!(f, "Parameter '{}' already exists", name),
224            ExprError::InvalidParameterIndex(idx) => write!(f, "Invalid parameter index: {}", idx),
225        }
226    }
227}
228
229impl From<String> for ExprError {
230    fn from(err: String) -> ExprError {
231        ExprError::Other(err)
232    }
233}
234
235impl From<ParseFloatError> for ExprError {
236    fn from(err: ParseFloatError) -> ExprError {
237        ExprError::Parse(err)
238    }
239}