1use crate::Real;
8use crate::context::EvalContext;
9use crate::error::ExprError;
10use crate::eval::context_stack::ContextStack;
11use crate::eval::stack_ops::EvalOp;
12use crate::eval::types::{FunctionCacheEntry, OwnedNativeFunction};
13use crate::types::{AstExpr, FunctionName, HString};
14use crate::types::{TryIntoFunctionName, TryIntoHeaplessString};
15
16use alloc::collections::BTreeMap;
17use alloc::format;
18use alloc::rc::Rc;
19use alloc::string::ToString;
20use alloc::vec::Vec;
21
22const MAX_STACK_DEPTH: usize = 1000;
24
25pub fn eval_iterative<'arena>(
27 ast: &'arena AstExpr<'arena>,
28 ctx: Option<Rc<EvalContext>>,
29 arena: &'arena bumpalo::Bump,
30) -> Result<Real, ExprError> {
31 let mut engine = EvalEngine::new(arena);
32 engine.eval(ast, ctx)
33}
34
35pub struct EvalEngine<'arena> {
37 arena: Option<&'arena bumpalo::Bump>,
39
40 op_stack: bumpalo::collections::Vec<'arena, EvalOp<'arena>>,
42 value_stack: bumpalo::collections::Vec<'arena, Real>,
44 arg_buffer: bumpalo::collections::Vec<'arena, Real>,
46
47 op_stack_hwm: usize,
49 value_stack_hwm: usize,
50 arg_buffer_hwm: usize,
51
52 ctx_stack: ContextStack,
54 func_cache: BTreeMap<HString, Option<FunctionCacheEntry>>,
56 param_overrides: Option<crate::types::BatchParamMap>,
58 local_functions: Option<&'arena core::cell::RefCell<crate::types::ExpressionFunctionMap>>,
60 expr_func_cache: BTreeMap<HString, &'arena AstExpr<'arena>>,
62}
63
64impl<'arena> EvalEngine<'arena> {
67 pub fn new(arena: &'arena bumpalo::Bump) -> Self {
69 Self {
70 arena: Some(arena),
71
72 op_stack: bumpalo::collections::Vec::new_in(arena),
73 value_stack: bumpalo::collections::Vec::new_in(arena),
74 arg_buffer: bumpalo::collections::Vec::new_in(arena),
75
76 op_stack_hwm: 0,
78 value_stack_hwm: 0,
79 arg_buffer_hwm: 0,
80
81 ctx_stack: ContextStack::new(),
83 func_cache: BTreeMap::new(),
84 param_overrides: None,
85 local_functions: None,
86 expr_func_cache: BTreeMap::new(),
87 }
88 }
89
90 pub fn set_local_functions(
92 &mut self,
93 functions: Option<&'arena core::cell::RefCell<crate::types::ExpressionFunctionMap>>,
94 ) {
95 self.local_functions = functions;
96 }
97
98 fn arena_clear_stacks(&mut self) {
101 unsafe {
104 self.op_stack.set_len(0);
105 self.value_stack.set_len(0);
106 self.arg_buffer.set_len(0);
107 }
108 }
109
110 pub fn arena_reset(&mut self) {
113 self.arena_clear_stacks();
114 self.ctx_stack.clear();
115 self.func_cache.clear();
116 self.expr_func_cache.clear();
117
118 self.op_stack_hwm = 0;
120 self.value_stack_hwm = 0;
121 self.arg_buffer_hwm = 0;
122 }
123
124 pub fn eval(
126 &mut self,
127 ast: &'arena AstExpr<'arena>,
128 ctx: Option<Rc<EvalContext>>,
129 ) -> Result<Real, ExprError> {
130 self.arena_clear_stacks();
132 self.ctx_stack.clear();
133 self.func_cache.clear();
134
135 let root_ctx_id = self.ctx_stack.push_context(ctx)?;
137
138 self.op_stack.push(EvalOp::Eval {
140 expr: ast,
141 ctx_id: root_ctx_id,
142 });
143
144 while let Some(op) = self.op_stack.pop() {
146 if self.op_stack.len() > MAX_STACK_DEPTH {
148 return Err(ExprError::RecursionLimit(format!(
149 "Maximum evaluation depth {} exceeded",
150 MAX_STACK_DEPTH
151 )));
152 }
153
154 self.process_operation(op)?;
155 }
156
157 self.value_stack
159 .pop()
160 .ok_or_else(|| ExprError::Other("No result on value stack".to_string()))
161 }
162
163 fn process_operation(&mut self, op: EvalOp<'arena>) -> Result<(), ExprError> {
165 match op {
166 EvalOp::Eval { expr, ctx_id } => {
167 self.process_eval(expr, ctx_id)?;
168 }
169
170 EvalOp::ApplyUnary { op } => {
171 let operand = self.pop_value()?;
172 self.value_stack.push(op.apply(operand));
173 }
174
175 EvalOp::CompleteBinary { op } => {
176 let right = self.pop_value()?;
177 let left = self.pop_value()?;
178 self.value_stack.push(op.apply(left, right));
179 }
180
181 EvalOp::ShortCircuitAnd { right_expr, ctx_id } => {
182 let left_val = self.pop_value()?;
183 if left_val == 0.0 {
184 self.value_stack.push(0.0);
186 } else {
187 self.op_stack.push(EvalOp::CompleteAnd);
189 self.op_stack.push(EvalOp::Eval {
190 expr: right_expr,
191 ctx_id,
192 });
193 }
194 }
195
196 EvalOp::ShortCircuitOr { right_expr, ctx_id } => {
197 let left_val = self.pop_value()?;
198 if left_val != 0.0 {
199 self.value_stack.push(1.0);
201 } else {
202 self.op_stack.push(EvalOp::CompleteOr);
204 self.op_stack.push(EvalOp::Eval {
205 expr: right_expr,
206 ctx_id,
207 });
208 }
209 }
210
211 EvalOp::CompleteAnd => {
212 let right = self.pop_value()?;
213 let left = 1.0; self.value_stack.push(if left != 0.0 && right != 0.0 {
215 1.0
216 } else {
217 0.0
218 });
219 }
220
221 EvalOp::CompleteOr => {
222 let right = self.pop_value()?;
223 let left = 0.0; self.value_stack.push(if left != 0.0 || right != 0.0 {
225 1.0
226 } else {
227 0.0
228 });
229 }
230
231 EvalOp::LookupVariable { name, ctx_id } => {
232 self.process_variable_lookup(name, ctx_id)?;
233 }
234
235 EvalOp::TernaryCondition {
236 true_branch,
237 false_branch,
238 ctx_id,
239 } => {
240 let condition = self.pop_value()?;
241 if condition != 0.0 {
242 self.op_stack.push(EvalOp::Eval {
243 expr: true_branch,
244 ctx_id,
245 });
246 } else {
247 self.op_stack.push(EvalOp::Eval {
248 expr: false_branch,
249 ctx_id,
250 });
251 }
252 }
253
254 EvalOp::AccessArray { array_name, ctx_id } => {
255 let index = self.pop_value()?;
256 self.process_array_access(array_name, index, ctx_id)?;
257 }
258
259 EvalOp::AccessAttribute {
260 object_name,
261 attr_name,
262 ctx_id,
263 } => {
264 self.process_attribute_access(object_name, attr_name, ctx_id)?;
265 }
266
267 EvalOp::ApplyFunction {
268 name,
269 arg_count,
270 ctx_id,
271 } => {
272 self.process_function_call(name, arg_count, ctx_id)?;
273 }
274
275 EvalOp::RestoreFunctionParams { params: _ } => {
276 }
279 }
280
281 Ok(())
282 }
283
284 fn process_eval(
286 &mut self,
287 expr: &'arena AstExpr<'arena>,
288 ctx_id: usize,
289 ) -> Result<(), ExprError> {
290 match expr {
291 AstExpr::Constant(val) => {
292 self.value_stack.push(*val);
293 }
294
295 AstExpr::Variable(name) => {
296 let hname = name.try_into_heapless()?;
297 self.op_stack.push(EvalOp::LookupVariable {
298 name: hname,
299 ctx_id,
300 });
301 }
302
303 AstExpr::LogicalOp { op, left, right } => {
304 use crate::types::LogicalOperator;
306 match op {
307 LogicalOperator::And => {
308 self.op_stack.push(EvalOp::ShortCircuitAnd {
309 right_expr: right,
310 ctx_id,
311 });
312 self.op_stack.push(EvalOp::Eval { expr: left, ctx_id });
313 }
314 LogicalOperator::Or => {
315 self.op_stack.push(EvalOp::ShortCircuitOr {
316 right_expr: right,
317 ctx_id,
318 });
319 self.op_stack.push(EvalOp::Eval { expr: left, ctx_id });
320 }
321 }
322 }
323
324 AstExpr::Conditional {
325 condition,
326 true_branch,
327 false_branch,
328 } => {
329 self.op_stack.push(EvalOp::TernaryCondition {
330 true_branch,
331 false_branch,
332 ctx_id,
333 });
334 self.op_stack.push(EvalOp::Eval {
335 expr: condition,
336 ctx_id,
337 });
338 }
339
340 AstExpr::Function { name, args } => {
341 match (*name, args.len()) {
343 ("&&", 2) => {
344 self.op_stack.push(EvalOp::ShortCircuitAnd {
346 right_expr: &args[1],
347 ctx_id,
348 });
349 self.op_stack.push(EvalOp::Eval {
350 expr: &args[0],
351 ctx_id,
352 });
353 }
354 ("||", 2) => {
355 self.op_stack.push(EvalOp::ShortCircuitOr {
357 right_expr: &args[1],
358 ctx_id,
359 });
360 self.op_stack.push(EvalOp::Eval {
361 expr: &args[0],
362 ctx_id,
363 });
364 }
365 _ => {
366 let fname = name.try_into_function_name()?;
370
371 self.op_stack.push(EvalOp::ApplyFunction {
374 name: fname,
375 arg_count: args.len(),
376 ctx_id,
377 });
378
379 for arg in args.iter().rev() {
382 self.op_stack.push(EvalOp::Eval { expr: arg, ctx_id });
383 }
384 }
385 }
386 }
387
388 AstExpr::Array { name, index } => {
389 let array_name = name.try_into_heapless()?;
390
391 self.op_stack
392 .push(EvalOp::AccessArray { array_name, ctx_id });
393 self.op_stack.push(EvalOp::Eval {
394 expr: index,
395 ctx_id,
396 });
397 }
398
399 AstExpr::Attribute { base, attr } => {
400 let obj_name = base.try_into_heapless()?;
401 let attr_name = attr.try_into_heapless()?;
402
403 self.op_stack.push(EvalOp::AccessAttribute {
404 object_name: obj_name,
405 attr_name,
406 ctx_id,
407 });
408 }
409 }
410
411 Ok(())
412 }
413
414 fn process_variable_lookup(&mut self, name: HString, ctx_id: usize) -> Result<(), ExprError> {
416 for op in self.op_stack.iter().rev() {
418 if let EvalOp::RestoreFunctionParams {
419 params: Some(params),
420 } = op
421 {
422 for (param_name, value) in params.iter() {
423 if param_name == &name {
424 self.value_stack.push(*value);
425 return Ok(());
426 }
427 }
428 }
429 }
430
431 if let Some(ref overrides) = self.param_overrides {
433 if let Some(&value) = overrides.get(&name) {
434 self.value_stack.push(value);
435 return Ok(());
436 }
437 }
438
439 if let Some(value) = self.ctx_stack.lookup_variable(ctx_id, &name) {
441 self.value_stack.push(value);
442 return Ok(());
443 }
444
445 let value = match name.as_str() {
447 "pi" | "PI" => core::f64::consts::PI as Real,
448 "e" | "E" => core::f64::consts::E as Real,
449 "tau" | "TAU" => 2.0 * core::f64::consts::PI as Real,
450 _ => {
451 let is_potential_function_name = match name.as_str() {
453 "sin" | "cos" | "tan" | "asin" | "acos" | "atan" | "atan2" | "sinh"
454 | "cosh" | "tanh" | "exp" | "log" | "log10" | "ln" | "sqrt" | "abs"
455 | "ceil" | "floor" | "pow" | "neg" | "," | "comma" | "+" | "-" | "*" | "/"
456 | "%" | "^" | "max" | "min" | "<" | ">" | "<=" | ">=" | "==" | "!=" => true,
457 _ => false,
458 };
459
460 if is_potential_function_name && name.len() > 1 {
461 return Err(ExprError::Syntax(format!(
462 "Function '{}' used without arguments",
463 name
464 )));
465 }
466
467 return Err(ExprError::UnknownVariable {
468 name: name.to_string(),
469 });
470 }
471 };
472
473 self.value_stack.push(value);
474 Ok(())
475 }
476
477 fn process_array_access(
479 &mut self,
480 array_name: HString,
481 index: Real,
482 ctx_id: usize,
483 ) -> Result<(), ExprError> {
484 let idx = index as usize;
485
486 if let Some(ctx) = self.ctx_stack.get_context(ctx_id) {
487 if let Some(array) = ctx.arrays.get(&array_name) {
488 if idx < array.len() {
489 self.value_stack.push(array[idx]);
490 return Ok(());
491 } else {
492 return Err(ExprError::ArrayIndexOutOfBounds {
493 name: array_name.to_string(),
494 index: idx,
495 len: array.len(),
496 });
497 }
498 }
499 }
500
501 Err(ExprError::UnknownVariable {
502 name: array_name.to_string(),
503 })
504 }
505
506 fn process_attribute_access(
508 &mut self,
509 object_name: HString,
510 attr_name: HString,
511 ctx_id: usize,
512 ) -> Result<(), ExprError> {
513 if let Some(ctx) = self.ctx_stack.get_context(ctx_id) {
514 if let Some(obj_attrs) = ctx.attributes.get(&object_name) {
515 if let Some(&value) = obj_attrs.get(&attr_name) {
516 self.value_stack.push(value);
517 return Ok(());
518 }
519 }
520 }
521
522 Err(ExprError::AttributeNotFound {
523 base: object_name.to_string(),
524 attr: attr_name.to_string(),
525 })
526 }
527
528 fn process_function_call(
530 &mut self,
531 name: FunctionName,
532 arg_count: usize,
533 ctx_id: usize,
534 ) -> Result<(), ExprError> {
535 let args_start = self.value_stack.len().saturating_sub(arg_count);
537
538 let ctx = self
540 .ctx_stack
541 .get_context(ctx_id)
542 .ok_or_else(|| ExprError::Other("Invalid context ID".to_string()))?;
543
544 if let Some(local_funcs) = self.local_functions {
546 if let Some(func) = local_funcs.borrow().get(&name).cloned() {
547 return self.process_expression_function(&func, args_start, arg_count, ctx_id);
548 }
549 }
550
551 if let Some(func) = ctx.get_native_function(&name) {
553 if arg_count != func.arity {
554 return Err(ExprError::InvalidFunctionCall {
555 name: name.to_string(),
556 expected: func.arity,
557 found: arg_count,
558 });
559 }
560
561 let args = &self.value_stack[args_start..];
563 let owned_fn = OwnedNativeFunction::from(func);
564 let result = (owned_fn.implementation)(args);
565
566 self.value_stack.truncate(args_start);
568
569 self.value_stack.push(result);
571 return Ok(());
572 }
573
574 Err(ExprError::UnknownFunction {
575 name: name.to_string(),
576 })
577 }
578
579 fn pop_value(&mut self) -> Result<Real, ExprError> {
581 self.value_stack
582 .pop()
583 .ok_or_else(|| ExprError::Other("Value stack underflow".to_string()))
584 }
585
586 pub fn set_param_overrides(&mut self, params: crate::types::BatchParamMap) {
589 self.param_overrides = Some(params);
590 }
591
592 pub fn clear_param_overrides(&mut self) {
594 self.param_overrides = None;
595 }
596
597 pub fn with_param_overrides<F, R>(&mut self, params: crate::types::BatchParamMap, f: F) -> R
600 where
601 F: FnOnce(&mut Self) -> R,
602 {
603 let old_overrides = self.param_overrides.take();
604 self.param_overrides = Some(params);
605 let result = f(self);
606 self.param_overrides = old_overrides;
607 result
608 }
609
610 fn process_expression_function(
612 &mut self,
613 func: &crate::types::ExpressionFunction,
614 args_start: usize,
615 arg_count: usize,
616 ctx_id: usize,
617 ) -> Result<(), ExprError> {
618 use crate::types::TryIntoHeaplessString;
619
620 if arg_count != func.params.len() {
621 return Err(ExprError::InvalidFunctionCall {
622 name: func.name.to_string(),
623 expected: func.params.len(),
624 found: arg_count,
625 });
626 }
627
628 let params_slice = if let Some(buffer_ptr) = func.param_buffer {
630 let buffer = unsafe { &mut *buffer_ptr };
632
633 for i in 0..func.params.len() {
635 buffer[i].1 = self.value_stack[args_start + i];
636 }
637
638 Some(&*buffer)
639 } else if let Some(arena) = self.arena
640 && !func.params.is_empty()
641 {
642 let params_slice: &mut [(crate::types::HString, crate::Real)] =
644 arena.alloc_slice_fill_default(func.params.len());
645
646 for (i, param) in func.params.iter().enumerate() {
648 let value = self.value_stack[args_start + i];
649 let param_key = param.as_str().try_into_heapless()?;
650 params_slice[i] = (param_key, value);
651 }
652
653 Some(&*params_slice)
654 } else {
655 None
656 };
657
658 self.value_stack.truncate(args_start);
660
661 if let Some(arena) = self.arena {
663 let func_key = func.name.try_into_heapless()?;
665
666 let ast = if let Some(&cached_ast) = self.expr_func_cache.get(&func_key) {
667 cached_ast
668 } else {
669 let param_names: Vec<crate::String> = func.params.clone();
671 let parsed_ast = crate::engine::parse_expression_with_parameters(
672 &func.expression,
673 arena,
674 ¶m_names,
675 )?;
676
677 let arena_ast = arena.alloc(parsed_ast);
679
680 self.expr_func_cache.insert(func_key.clone(), &*arena_ast);
682
683 &*arena_ast
684 };
685
686 self.op_stack.push(EvalOp::RestoreFunctionParams {
688 params: params_slice,
689 });
690 self.op_stack.push(EvalOp::Eval {
691 expr: ast,
692 ctx_id, });
694 Ok(())
695 } else {
696 Err(ExprError::Other(
698 "Expression functions require an arena-enabled evaluator".to_string(),
699 ))
700 }
701 }
702}
703
704pub fn eval_with_engine<'arena>(
736 ast: &'arena AstExpr<'arena>,
737 ctx: Option<Rc<EvalContext>>,
738 engine: &mut EvalEngine<'arena>,
739) -> Result<Real, ExprError> {
740 engine.eval(ast, ctx)
741}