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}