1extern crate alloc;
2
3#[cfg(test)]
4use crate::Real;
5#[cfg(not(test))]
6use crate::{Real, String, ToString, Vec};
7#[cfg(not(test))]
8use alloc::rc::Rc;
9use crate::types::{TryIntoFunctionName, TryIntoHeaplessString};
11#[cfg(test)]
12use std::rc::Rc;
13#[cfg(test)]
14use std::string::{String, ToString};
15#[cfg(test)]
16use std::vec::Vec;
17
18#[allow(dead_code)]
27#[derive(Default, Clone)]
28pub struct FunctionRegistry {
29 pub native_functions: crate::types::NativeFunctionMap,
31 pub expression_functions: crate::types::ExpressionFunctionMap,
33}
34
35pub struct EvalContext {
83 pub variables: crate::types::VariableMap,
85 pub constants: crate::types::ConstantMap,
87 pub arrays: crate::types::ArrayMap,
89 pub attributes: crate::types::AttributeMap,
91 pub nested_arrays: crate::types::NestedArrayMap,
93 pub native_functions: Rc<crate::types::NativeFunctionMap>,
95 pub parent: Option<Rc<EvalContext>>,
97}
98
99impl EvalContext {
100 pub fn new() -> Self {
114 let mut ctx = Self {
115 variables: crate::types::VariableMap::new(),
116 constants: crate::types::ConstantMap::new(),
117 arrays: crate::types::ArrayMap::new(),
118 attributes: crate::types::AttributeMap::new(),
119 nested_arrays: crate::types::NestedArrayMap::new(),
120 native_functions: Rc::new(crate::types::NativeFunctionMap::new()),
121 parent: None,
122 };
123
124 ctx.register_default_math_functions();
127
128 ctx
129 }
130
131 pub fn with_default_functions() -> Self {
139 Self::new()
141 }
142
143 pub fn empty() -> Self {
161 Self {
162 variables: crate::types::VariableMap::new(),
163 constants: crate::types::ConstantMap::new(),
164 arrays: crate::types::ArrayMap::new(),
165 attributes: crate::types::AttributeMap::new(),
166 nested_arrays: crate::types::NestedArrayMap::new(),
167 native_functions: Rc::new(crate::types::NativeFunctionMap::new()),
168 parent: None,
169 }
170 }
171
172 pub fn set_parameter(
201 &mut self,
202 name: &str,
203 value: Real,
204 ) -> Result<Option<Real>, crate::error::ExprError> {
205 let key = name.try_into_heapless()?;
206 match self.variables.insert(key, value) {
207 Ok(old_value) => Ok(old_value),
208 Err(_) => Err(crate::error::ExprError::CapacityExceeded("variables")),
209 }
210 }
211
212 pub fn register_native_function<F>(
261 &mut self,
262 name: &str,
263 arity: usize,
264 implementation: F,
265 ) -> Result<(), crate::error::ExprError>
266 where
267 F: Fn(&[Real]) -> Real + 'static,
268 {
269 let key = name.try_into_function_name()?;
270 let function = crate::types::NativeFunction {
271 arity,
272 implementation: Rc::new(implementation),
273 name: key.clone(),
274 description: None,
275 };
276
277 match Rc::make_mut(&mut self.native_functions).insert(key, function) {
278 Ok(_) => Ok(()),
279 Err(_) => Err(crate::error::ExprError::CapacityExceeded(
280 "native_functions",
281 )),
282 }
283 }
284
285 pub fn enable_default_functions(&mut self) {
335 self.register_default_math_functions();
336 }
337
338 pub fn register_default_math_functions(&mut self) {
340 let _ = self.register_native_function("+", 2, |args| args[0] + args[1]);
342 let _ = self.register_native_function("-", 2, |args| args[0] - args[1]);
343 let _ = self.register_native_function("*", 2, |args| args[0] * args[1]);
344 let _ = self.register_native_function("/", 2, |args| args[0] / args[1]);
345 let _ = self.register_native_function("%", 2, |args| args[0] % args[1]);
346
347 let _ =
349 self.register_native_function("<", 2, |args| if args[0] < args[1] { 1.0 } else { 0.0 });
350 let _ =
351 self.register_native_function(">", 2, |args| if args[0] > args[1] { 1.0 } else { 0.0 });
352 let _ = self.register_native_function(
353 "<=",
354 2,
355 |args| if args[0] <= args[1] { 1.0 } else { 0.0 },
356 );
357 let _ = self.register_native_function(
358 ">=",
359 2,
360 |args| if args[0] >= args[1] { 1.0 } else { 0.0 },
361 );
362 let _ = self.register_native_function(
363 "==",
364 2,
365 |args| if args[0] == args[1] { 1.0 } else { 0.0 },
366 );
367 let _ = self.register_native_function(
368 "!=",
369 2,
370 |args| if args[0] != args[1] { 1.0 } else { 0.0 },
371 );
372
373 let _ = self.register_native_function("&&", 2, |args| {
375 if args[0] != 0.0 && args[1] != 0.0 {
376 1.0
377 } else {
378 0.0
379 }
380 });
381 let _ = self.register_native_function("||", 2, |args| {
382 if args[0] != 0.0 || args[1] != 0.0 {
383 1.0
384 } else {
385 0.0
386 }
387 });
388
389 let _ = self.register_native_function("add", 2, |args| args[0] + args[1]);
391 let _ = self.register_native_function("sub", 2, |args| args[0] - args[1]);
392 let _ = self.register_native_function("mul", 2, |args| args[0] * args[1]);
393 let _ = self.register_native_function("div", 2, |args| args[0] / args[1]);
394 let _ = self.register_native_function("fmod", 2, |args| args[0] % args[1]);
395 let _ = self.register_native_function("neg", 1, |args| -args[0]);
396
397 let _ = self.register_native_function(",", 2, |args| args[1]); let _ = self.register_native_function("comma", 2, |args| args[1]); let _ = self.register_native_function("abs", 1, |args| args[0].abs());
403 let _ = self.register_native_function("max", 2, |args| args[0].max(args[1]));
404 let _ = self.register_native_function("min", 2, |args| args[0].min(args[1]));
405 let _ = self.register_native_function("sign", 1, |args| {
406 if args[0] > 0.0 {
407 1.0
408 } else if args[0] < 0.0 {
409 -1.0
410 } else {
411 0.0
412 }
413 });
414
415 #[cfg(feature = "f32")]
417 let _ = self.register_native_function("e", 0, |_| core::f32::consts::E);
418 #[cfg(not(feature = "f32"))]
419 let _ = self.register_native_function("e", 0, |_| core::f64::consts::E);
420
421 #[cfg(feature = "f32")]
422 let _ = self.register_native_function("pi", 0, |_| core::f32::consts::PI);
423 #[cfg(not(feature = "f32"))]
424 let _ = self.register_native_function("pi", 0, |_| core::f64::consts::PI);
425
426 #[cfg(feature = "libm")]
428 {
429 let _ = self
430 .register_native_function("acos", 1, |args| crate::functions::acos(args[0], 0.0));
431 let _ = self
432 .register_native_function("asin", 1, |args| crate::functions::asin(args[0], 0.0));
433 let _ = self
434 .register_native_function("atan", 1, |args| crate::functions::atan(args[0], 0.0));
435 let _ = self.register_native_function("atan2", 2, |args| {
436 crate::functions::atan2(args[0], args[1])
437 });
438 let _ = self
439 .register_native_function("ceil", 1, |args| crate::functions::ceil(args[0], 0.0));
440 let _ =
441 self.register_native_function("cos", 1, |args| crate::functions::cos(args[0], 0.0));
442 let _ = self
443 .register_native_function("cosh", 1, |args| crate::functions::cosh(args[0], 0.0));
444 let _ =
445 self.register_native_function("exp", 1, |args| crate::functions::exp(args[0], 0.0));
446 let _ = self
447 .register_native_function("floor", 1, |args| crate::functions::floor(args[0], 0.0));
448 let _ = self
449 .register_native_function("round", 1, |args| crate::functions::round(args[0], 0.0));
450 let _ =
451 self.register_native_function("ln", 1, |args| crate::functions::ln(args[0], 0.0));
452 let _ =
453 self.register_native_function("log", 1, |args| crate::functions::log(args[0], 0.0));
454 let _ = self
455 .register_native_function("log10", 1, |args| crate::functions::log10(args[0], 0.0));
456 let _ = self
457 .register_native_function("pow", 2, |args| crate::functions::pow(args[0], args[1]));
458 let _ = self
459 .register_native_function("^", 2, |args| crate::functions::pow(args[0], args[1]));
460 let _ =
461 self.register_native_function("sin", 1, |args| crate::functions::sin(args[0], 0.0));
462 let _ = self
463 .register_native_function("sinh", 1, |args| crate::functions::sinh(args[0], 0.0));
464 let _ = self
465 .register_native_function("sqrt", 1, |args| crate::functions::sqrt(args[0], 0.0));
466 let _ =
467 self.register_native_function("tan", 1, |args| crate::functions::tan(args[0], 0.0));
468 let _ = self
469 .register_native_function("tanh", 1, |args| crate::functions::tanh(args[0], 0.0));
470 }
471
472 #[cfg(all(not(feature = "libm"), test))]
474 {
475 let _ = self.register_native_function("acos", 1, |args| args[0].acos());
476 let _ = self.register_native_function("asin", 1, |args| args[0].asin());
477 let _ = self.register_native_function("atan", 1, |args| args[0].atan());
478 let _ = self.register_native_function("atan2", 2, |args| args[0].atan2(args[1]));
479 let _ = self.register_native_function("ceil", 1, |args| args[0].ceil());
480 let _ = self.register_native_function("cos", 1, |args| args[0].cos());
481 let _ = self.register_native_function("cosh", 1, |args| args[0].cosh());
482 let _ = self.register_native_function("exp", 1, |args| args[0].exp());
483 let _ = self.register_native_function("floor", 1, |args| args[0].floor());
484 let _ = self.register_native_function("round", 1, |args| args[0].round());
485 let _ = self.register_native_function("ln", 1, |args| args[0].ln());
486 let _ = self.register_native_function("log", 1, |args| args[0].log10());
487 let _ = self.register_native_function("log10", 1, |args| args[0].log10());
488 let _ = self.register_native_function("pow", 2, |args| args[0].powf(args[1]));
489 let _ = self.register_native_function("^", 2, |args| args[0].powf(args[1]));
490 let _ = self.register_native_function("sin", 1, |args| args[0].sin());
491 let _ = self.register_native_function("sinh", 1, |args| args[0].sinh());
492 let _ = self.register_native_function("sqrt", 1, |args| args[0].sqrt());
493 let _ = self.register_native_function("tan", 1, |args| args[0].tan());
494 let _ = self.register_native_function("tanh", 1, |args| args[0].tanh());
495 }
496
497 }
500
501 pub fn get_variable(&self, name: &str) -> Option<Real> {
524 if let Ok(key) = name.try_into_heapless() {
525 if let Some(val) = self.variables.get(&key) {
526 return Some(*val);
527 }
528 }
529
530 if let Some(parent) = &self.parent {
531 parent.get_variable(name)
532 } else {
533 None
534 }
535 }
536
537 pub fn get_constant(&self, name: &str) -> Option<Real> {
538 if let Ok(key) = name.try_into_heapless() {
539 if let Some(val) = self.constants.get(&key) {
540 return Some(*val);
541 }
542 }
543
544 if let Some(parent) = &self.parent {
545 parent.get_constant(name)
546 } else {
547 None
548 }
549 }
550
551 pub fn get_array(&self, name: &str) -> Option<&alloc::vec::Vec<crate::Real>> {
552 if let Ok(key) = name.try_into_heapless() {
553 if let Some(arr) = self.arrays.get(&key) {
554 return Some(arr);
555 }
556 }
557
558 if let Some(parent) = &self.parent {
559 parent.get_array(name)
560 } else {
561 None
562 }
563 }
564
565 pub fn set_attribute(
567 &mut self,
568 object_name: &str,
569 attr_name: &str,
570 value: Real,
571 ) -> Result<Option<Real>, crate::error::ExprError> {
572 let obj_key = object_name.try_into_heapless()?;
573 let attr_key = attr_name.try_into_heapless()?;
574
575 if !self.attributes.contains_key(&obj_key) {
577 let attr_map = heapless::FnvIndexMap::<
578 crate::types::HString,
579 Real,
580 { crate::types::EXP_RS_MAX_ATTR_KEYS },
581 >::new();
582 self.attributes
583 .insert(obj_key.clone(), attr_map)
584 .map_err(|_| crate::error::ExprError::CapacityExceeded("attributes"))?;
585 }
586
587 if let Some(attr_map) = self.attributes.get_mut(&obj_key) {
589 attr_map
590 .insert(attr_key, value)
591 .map_err(|_| crate::error::ExprError::CapacityExceeded("object attributes"))
592 } else {
593 unreachable!("Just inserted the object")
594 }
595 }
596
597 pub fn get_attribute_map(
598 &self,
599 base: &str,
600 ) -> Option<
601 &heapless::FnvIndexMap<crate::types::HString, Real, { crate::types::EXP_RS_MAX_ATTR_KEYS }>,
602 > {
603 if let Ok(key) = base.try_into_heapless() {
604 if let Some(attr_map) = self.attributes.get(&key) {
605 return Some(attr_map);
606 }
607 }
608
609 if let Some(parent) = &self.parent {
610 parent.get_attribute_map(base)
611 } else {
612 None
613 }
614 }
615
616 pub fn get_native_function(&self, name: &str) -> Option<&crate::types::NativeFunction> {
617 if let Ok(key) = name.try_into_function_name() {
618 if let Some(f) = self.native_functions.get(&key) {
619 return Some(f);
620 }
621 }
622
623 if let Some(parent) = &self.parent {
624 parent.get_native_function(name)
625 } else {
626 None
627 }
628 }
629
630 pub fn list_native_functions(&self) -> Vec<String> {
632 let mut functions = Vec::new();
633 let mut seen = alloc::collections::BTreeSet::new();
634
635 for (name, _) in self.native_functions.iter() {
637 let name_str = name.to_string();
638 if seen.insert(name_str.clone()) {
639 functions.push(name_str);
640 }
641 }
642
643 if let Some(parent) = &self.parent {
645 for name in parent.list_native_functions() {
646 if seen.insert(name.clone()) {
647 functions.push(name);
648 }
649 }
650 }
651
652 functions.sort();
653 functions
654 }
655
656 pub fn list_expression_functions(&self) -> Vec<String> {
658 let mut functions = Vec::new();
659 let mut seen = alloc::collections::BTreeSet::new();
660
661 if let Some(parent) = &self.parent {
666 for name in parent.list_expression_functions() {
667 if seen.insert(name.clone()) {
668 functions.push(name);
669 }
670 }
671 }
672
673 functions.sort();
674 functions
675 }
676}
677
678impl Clone for EvalContext {
679 fn clone(&self) -> Self {
680 Self {
681 variables: self.variables.clone(),
682 constants: self.constants.clone(),
683 arrays: self.arrays.clone(),
684 attributes: self.attributes.clone(),
685 nested_arrays: self.nested_arrays.clone(),
686 native_functions: self.native_functions.clone(),
687 parent: self.parent.clone(),
688 }
689 }
690}
691
692impl Default for EvalContext {
693 fn default() -> Self {
696 EvalContext::new()
697 }
713}
714
715#[cfg(test)]
718mod tests {
719 use super::*;
720 use crate::engine;
721 use crate::types::AstExpr;
722 use crate::types::TryIntoHeaplessString;
723 use std::rc::Rc;
724
725 #[test]
726 fn test_get_variable_parent_chain() {
727 let mut parent_ctx = EvalContext::new();
729 let _ = parent_ctx.set_parameter("parent_only", 1.0);
730 let _ = parent_ctx.set_parameter("shadowed", 2.0);
731
732 let mut child_ctx = EvalContext::new();
734 let _ = child_ctx.set_parameter("child_only", 3.0);
735 let _ = child_ctx.set_parameter("shadowed", 4.0); child_ctx.parent = Some(Rc::new(parent_ctx));
737
738 assert_eq!(child_ctx.get_variable("parent_only"), Some(1.0));
740
741 assert_eq!(child_ctx.get_variable("child_only"), Some(3.0));
743
744 assert_eq!(child_ctx.get_variable("shadowed"), Some(4.0));
746
747 assert_eq!(child_ctx.get_variable("nonexistent"), None);
749 }
750
751 #[test]
752 fn test_get_variable_deep_chain() {
753 let mut grandparent_ctx = EvalContext::new();
755 let _ = grandparent_ctx.set_parameter("grandparent_var", 1.0);
756 let _ = grandparent_ctx.set_parameter("shadowed", 2.0);
757
758 let mut parent_ctx = EvalContext::new();
760 let _ = parent_ctx.set_parameter("parent_var", 3.0);
761 let _ = parent_ctx.set_parameter("shadowed", 4.0);
762 parent_ctx.parent = Some(Rc::new(grandparent_ctx));
763
764 let mut child_ctx = EvalContext::new();
766 let _ = child_ctx.set_parameter("child_var", 5.0);
767 let _ = child_ctx.set_parameter("shadowed", 6.0);
768 child_ctx.parent = Some(Rc::new(parent_ctx));
769
770 assert_eq!(child_ctx.get_variable("child_var"), Some(5.0));
772 assert_eq!(child_ctx.get_variable("parent_var"), Some(3.0));
773 assert_eq!(child_ctx.get_variable("grandparent_var"), Some(1.0));
774
775 assert_eq!(child_ctx.get_variable("shadowed"), Some(6.0));
777 }
778
779 #[test]
780 fn test_get_variable_null_parent() {
781 let mut ctx = EvalContext::new();
782 let _ = ctx.set_parameter("x", 1.0);
783 ctx.parent = None;
784
785 assert_eq!(ctx.get_variable("x"), Some(1.0));
786 assert_eq!(ctx.get_variable("nonexistent"), None);
787 }
788
789 #[test]
790 fn test_get_variable_cyclic_reference_safety() {
791 let mut ctx1 = EvalContext::new();
793 let mut ctx2 = EvalContext::new();
794
795 let _ = ctx1.set_parameter("var1", 1.0);
796 let _ = ctx2.set_parameter("var2", 2.0);
797
798 let ctx1_rc = Rc::new(ctx1);
801 ctx2.parent = Some(Rc::clone(&ctx1_rc));
802
803 assert_eq!(ctx2.get_variable("var2"), Some(2.0));
805 assert_eq!(ctx2.get_variable("var1"), Some(1.0));
806 }
807
808 #[test]
809 fn test_get_variable_in_function_scope() {
810 let mut ctx = EvalContext::new();
811
812 let _ = ctx.set_parameter("x", 100.0);
814
815 let mut func_ctx = EvalContext::new();
817 let _ = func_ctx.set_parameter("x", 5.0); func_ctx.parent = Some(Rc::new(ctx.clone()));
819
820 assert_eq!(
822 func_ctx.get_variable("x"),
823 Some(5.0),
824 "Function parameter should shadow parent variable"
825 );
826
827 println!("Parent context x = {:?}", ctx.get_variable("x"));
829 println!("Function context x = {:?}", func_ctx.get_variable("x"));
830 println!("Function context variables: {:?}", func_ctx.variables);
831 println!(
832 "Function context parent variables: {:?}",
833 func_ctx.parent.as_ref().map(|p| &p.variables)
834 );
835 }
836
837 #[test]
838 fn test_get_variable_nested_scopes() {
839 let mut root_ctx = EvalContext::new();
840 let _ = root_ctx.set_parameter("x", 1.0);
841 let _ = root_ctx.set_parameter("y", 1.0);
842
843 let mut mid_ctx = EvalContext::new();
844 let _ = mid_ctx.set_parameter("x", 2.0);
845 mid_ctx.parent = Some(Rc::new(root_ctx));
846
847 let mut leaf_ctx = EvalContext::new();
848 let _ = leaf_ctx.set_parameter("x", 3.0);
849 leaf_ctx.parent = Some(Rc::new(mid_ctx));
850
851 assert_eq!(
853 leaf_ctx.get_variable("x"),
854 Some(3.0),
855 "Should get leaf context value"
856 );
857 assert_eq!(
858 leaf_ctx.get_variable("y"),
859 Some(1.0),
860 "Should get root context value when not shadowed"
861 );
862
863 println!("Variable lookup in nested scopes:");
864 println!("leaf x = {:?}", leaf_ctx.get_variable("x"));
865 println!("leaf y = {:?}", leaf_ctx.get_variable("y"));
866 println!("leaf variables: {:?}", leaf_ctx.variables);
867 println!(
868 "mid variables: {:?}",
869 leaf_ctx.parent.as_ref().map(|p| &p.variables)
870 );
871 println!(
872 "root variables: {:?}",
873 leaf_ctx
874 .parent
875 .as_ref()
876 .and_then(|p| p.parent.as_ref())
877 .map(|p| &p.variables)
878 );
879 }
880
881 #[test]
882 fn test_get_variable_function_parameter_precedence() {
883 let mut ctx = EvalContext::new();
884 let arena = bumpalo::Bump::new();
885 let mut batch = crate::expression::Expression::new(&arena);
886
887 batch
889 .register_expression_function("f", &["x"], "x * 2")
890 .unwrap();
891
892 let _ = ctx.set_parameter("x", 100.0);
894
895 let mut func_ctx = EvalContext::new();
897 let _ = func_ctx.set_parameter("x", 5.0); func_ctx.parent = Some(Rc::new(ctx));
899
900 println!("Function parameter context:");
901 println!("func_ctx x = {:?}", func_ctx.get_variable("x"));
902 println!("func_ctx variables: {:?}", func_ctx.variables);
903 println!(
904 "parent variables: {:?}",
905 func_ctx.parent.as_ref().map(|p| &p.variables)
906 );
907
908 assert_eq!(
909 func_ctx.get_variable("x"),
910 Some(5.0),
911 "Function parameter should take precedence over global x"
912 );
913 }
914
915 #[test]
916 fn test_get_variable_temporary_scope() {
917 let mut ctx = EvalContext::new();
918 let _ = ctx.set_parameter("x", 1.0);
919
920 let mut temp_ctx = EvalContext::new();
922 temp_ctx.parent = Some(Rc::new(ctx));
923
924 assert_eq!(
926 temp_ctx.get_variable("x"),
927 Some(1.0),
928 "Should find variable in parent scope"
929 );
930
931 let _ = temp_ctx.set_parameter("x", 2.0);
933
934 assert_eq!(
936 temp_ctx.get_variable("x"),
937 Some(2.0),
938 "Should find shadowed variable in local scope"
939 );
940
941 println!("Temporary scope variable lookup:");
942 println!("temp x = {:?}", temp_ctx.get_variable("x"));
943 println!("temp variables: {:?}", temp_ctx.variables);
944 println!(
945 "parent variables: {:?}",
946 temp_ctx.parent.as_ref().map(|p| &p.variables)
947 );
948 }
949
950 #[test]
951 fn test_native_function() {
952 let mut ctx = EvalContext::new();
953
954 ctx.register_native_function("add_all", 3, |args| args.iter().sum())
955 .unwrap();
956
957 let val = engine::interp("add_all(1, 2, 3)", Some(Rc::new(ctx))).unwrap();
958 assert_eq!(val, 6.0);
959 }
960
961 #[test]
962 fn test_array_access() {
963 let mut ctx = EvalContext::new();
964 ctx.arrays
965 .insert(
966 "climb_wave_wait_time".try_into_heapless().unwrap(),
967 vec![10.0, 20.0, 30.0],
968 )
969 .expect("Failed to insert array");
970 let val = engine::interp("climb_wave_wait_time[1]", Some(Rc::new(ctx.clone()))).unwrap();
971 assert_eq!(val, 20.0);
972 }
973
974 #[test]
975 fn test_array_access_ast_structure() {
976 let mut ctx = EvalContext::new();
977 ctx.arrays
978 .insert(
979 "climb_wave_wait_time".try_into_heapless().unwrap(),
980 vec![10.0, 20.0, 30.0],
981 )
982 .expect("Failed to insert array");
983 use bumpalo::Bump;
984 let arena = Bump::new();
985 let ast = engine::parse_expression("climb_wave_wait_time[1]", &arena).unwrap();
986 match ast {
987 AstExpr::Array { name, index } => {
988 assert_eq!(name, "climb_wave_wait_time");
989 match *index {
990 AstExpr::Constant(val) => assert_eq!(val, 1.0),
991 _ => panic!("Expected constant index"),
992 }
993 }
994 _ => panic!("Expected array AST node"),
995 }
996 }
997
998 #[test]
999 fn test_attribute_access() {
1000 let mut ctx = EvalContext::new();
1001 let mut foo_map = heapless::FnvIndexMap::<
1002 crate::types::HString,
1003 crate::Real,
1004 { crate::types::EXP_RS_MAX_ATTR_KEYS },
1005 >::new();
1006 foo_map
1007 .insert("bar".try_into_heapless().unwrap(), 42.0)
1008 .unwrap();
1009 ctx.attributes
1010 .insert("foo".try_into_heapless().unwrap(), foo_map)
1011 .unwrap();
1012
1013 use bumpalo::Bump;
1014 let arena = Bump::new();
1015 let ast = engine::parse_expression("foo.bar", &arena).unwrap();
1016 println!("AST for foo.bar: {:?}", ast);
1017
1018 let ctx_copy = ctx.clone();
1019 let eval_result = crate::eval::eval_ast(&ast, Some(Rc::new(ctx_copy)), &arena);
1020 println!("Direct eval_ast result: {:?}", eval_result);
1021
1022 let ctx_copy2 = ctx.clone();
1023 let val = engine::interp("foo.bar", Some(Rc::new(ctx_copy2))).unwrap();
1024 assert_eq!(val, 42.0);
1025
1026 let ctx_copy3 = ctx.clone();
1027 let err = engine::interp("foo.baz", Some(Rc::new(ctx_copy3))).unwrap_err();
1028 println!("Error for foo.baz: {:?}", err);
1029
1030 let ctx_copy4 = ctx.clone();
1031 let err2 = engine::interp("nope.bar", Some(Rc::new(ctx_copy4))).unwrap_err();
1032 println!("Error for nope.bar: {:?}", err2);
1033
1034 let err3 = engine::interp("foo.bar", None).unwrap_err();
1035 println!("Error for foo.bar with None context: {:?}", err3);
1036 }
1037
1038 #[test]
1039 fn test_set_parameter() {
1040 let mut ctx = EvalContext::new();
1041
1042 let prev = ctx.set_parameter("x", 10.0);
1043 assert_eq!(prev.unwrap(), None);
1044
1045 let val = engine::interp("x", Some(Rc::new(ctx.clone()))).unwrap();
1046 assert_eq!(val, 10.0);
1047
1048 let prev = ctx.set_parameter("x", 20.0);
1049 assert_eq!(prev.unwrap(), Some(10.0));
1050
1051 let val = engine::interp("x", Some(Rc::new(ctx.clone()))).unwrap();
1052 assert_eq!(val, 20.0);
1053
1054 let val = engine::interp("x * 2", Some(Rc::new(ctx.clone()))).unwrap();
1055 assert_eq!(val, 40.0);
1056 }
1057}