1use crate::ast::{ExprType, Value, VarRef};
19use crate::compiler::{self, CallableSyntax, RepeatedSyntax, SingularArgSyntax};
20use crate::exec::{Machine, Scope};
21use crate::reader::LineCol;
22use crate::value::{Error, Result};
23use async_trait::async_trait;
24use std::borrow::Cow;
25use std::collections::HashMap;
26use std::fmt;
27use std::io;
28use std::mem;
29use std::rc::Rc;
30use std::str::Lines;
31
32#[derive(Debug)]
38pub enum CallError {
39 ArgumentError(LineCol, String),
41
42 EvalError(LineCol, String),
44
45 InternalError(LineCol, String),
47
48 IoError(io::Error),
50
51 NestedError(String),
54
55 SyntaxError,
58}
59
60impl From<io::Error> for CallError {
61 fn from(e: io::Error) -> Self {
62 Self::IoError(e)
63 }
64}
65
66impl From<compiler::Error> for CallError {
67 fn from(value: compiler::Error) -> Self {
68 Self::EvalError(value.pos, value.message)
69 }
70}
71
72pub type CallResult = std::result::Result<(), CallError>;
74
75#[derive(Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
77pub struct SymbolKey(String);
78
79impl<R: AsRef<str>> From<R> for SymbolKey {
80 fn from(value: R) -> Self {
81 Self(value.as_ref().to_ascii_uppercase())
82 }
83}
84
85impl fmt::Display for SymbolKey {
86 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
87 write!(f, "{}", self.0)
88 }
89}
90
91#[derive(Clone, Debug, PartialEq)]
93pub struct Array {
94 subtype: ExprType,
96
97 dimensions: Vec<usize>,
99
100 values: Vec<Value>,
103}
104
105impl Array {
106 pub fn new(subtype: ExprType, dimensions: Vec<usize>) -> Self {
108 assert!(!dimensions.is_empty());
109 let mut n = 1;
110 for dim in &dimensions {
111 assert!(n > 0);
112 n *= dim;
113 }
114 let mut values = Vec::with_capacity(n);
115 let value = subtype.default_value();
116 for _ in 0..n {
117 values.push(value.clone());
118 }
119 Self { subtype, dimensions, values }
120 }
121
122 pub fn dimensions(&self) -> &[usize] {
124 &self.dimensions
125 }
126
127 pub fn subtype(&self) -> ExprType {
129 self.subtype
130 }
131
132 fn validate_subscript(i: i32, max: usize) -> Result<usize> {
134 if i < 0 {
135 Err(Error::new(format!("Subscript {} cannot be negative", i)))
136 } else if (i as usize) >= max {
137 Err(Error::new(format!("Subscript {} exceeds limit of {}", i, max)))
138 } else {
139 Ok(i as usize)
140 }
141 }
142
143 fn native_index(dimensions: &[usize], subscripts: &[i32]) -> Result<usize> {
148 debug_assert_eq!(
149 subscripts.len(),
150 dimensions.len(),
151 "Invalid number of subscripts; guaranteed valid by the compiler"
152 );
153
154 let mut offset = 0;
155 let mut multiplier = 1;
156 let mut k = dimensions.len() - 1;
157 while k > 0 {
158 offset += Array::validate_subscript(subscripts[k], dimensions[k])? * multiplier;
159 debug_assert!(dimensions[k] > 0);
160 multiplier *= dimensions[k];
161 k -= 1;
162 }
163 offset += Array::validate_subscript(subscripts[k], dimensions[k])? * multiplier;
164 Ok(offset)
165 }
166
167 pub fn assign(&mut self, subscripts: &[i32], value: Value) -> Result<()> {
169 debug_assert_eq!(
170 subscripts.len(),
171 self.dimensions.len(),
172 "Invalid number of subscripts; guaranteed valid by the compiler"
173 );
174
175 debug_assert_eq!(
176 value.as_exprtype(),
177 self.subtype,
178 "Invalid types in assignment; guaranteed valid by the compiler"
179 );
180
181 let i = Array::native_index(&self.dimensions, subscripts)?;
182 self.values[i] = value;
183 Ok(())
184 }
185
186 pub fn index(&self, subscripts: &[i32]) -> Result<&Value> {
188 let i = Array::native_index(&self.dimensions, subscripts)?;
189 let value = &self.values[i];
190 debug_assert!(value.as_exprtype() == self.subtype);
191 Ok(value)
192 }
193}
194
195pub enum Symbol {
197 Array(Array),
199
200 Callable(Rc<dyn Callable>),
202
203 Variable(Value),
205}
206
207impl Symbol {
208 fn eval_type(&self) -> Option<ExprType> {
210 match self {
211 Symbol::Array(array) => Some(array.subtype()),
212 Symbol::Callable(callable) => callable.metadata().return_type(),
213 Symbol::Variable(value) => Some(value.as_exprtype()),
214 }
215 }
216
217 pub fn metadata(&self) -> Option<&CallableMetadata> {
219 match self {
220 Symbol::Array(_) => None,
221 Symbol::Callable(callable) => Some(callable.metadata()),
222 Symbol::Variable(_) => None,
223 }
224 }
225
226 fn user_defined(&self) -> bool {
228 match self {
229 Symbol::Array(_) => true,
230 Symbol::Callable(_) => false,
231 Symbol::Variable(_) => true,
232 }
233 }
234}
235
236impl fmt::Debug for Symbol {
237 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
238 match self {
239 Symbol::Array(array) => write!(f, "Array({:?})", array),
240 Symbol::Callable(callable) => write!(f, "Callable({:?})", callable.metadata()),
241 Symbol::Variable(value) => write!(f, "Variable({:?})", value),
242 }
243 }
244}
245
246pub struct Symbols {
257 globals: HashMap<SymbolKey, Symbol>,
259
260 scopes: Vec<HashMap<SymbolKey, Symbol>>,
262}
263
264impl Default for Symbols {
265 fn default() -> Self {
266 Self { globals: HashMap::default(), scopes: vec![HashMap::default()] }
267 }
268}
269
270impl Symbols {
271 #[cfg(test)]
273 pub(crate) fn from(
274 globals: HashMap<SymbolKey, Symbol>,
275 scope: HashMap<SymbolKey, Symbol>,
276 ) -> Self {
277 Self { globals, scopes: vec![scope] }
278 }
279
280 pub(crate) fn enter_scope(&mut self) {
282 self.scopes.push(HashMap::default());
283 }
284
285 pub(crate) fn leave_scope(&mut self) {
287 let last = self.scopes.pop();
288 assert!(last.is_some(), "Must have at least one scope to pop");
289 assert!(!self.scopes.is_empty(), "Cannot pop the global scope");
290 }
291
292 pub fn add_callable(&mut self, callable: Rc<dyn Callable>) {
297 let key = SymbolKey::from(callable.metadata().name());
298 assert!(!self.globals.contains_key(&key));
299 self.globals.insert(key.to_owned(), Symbol::Callable(callable));
300 }
301
302 pub fn callables(&self) -> HashMap<&SymbolKey, Rc<dyn Callable>> {
304 let mut callables = HashMap::with_capacity(self.globals.len());
305 for (key, symbol) in &self.globals {
306 if let Symbol::Callable(c) = symbol {
307 callables.insert(key, c.clone());
308 }
309 }
310 callables
311 }
312
313 pub fn locals(&self) -> &HashMap<SymbolKey, Symbol> {
315 self.scopes.last().unwrap()
316 }
317
318 pub fn clear(&mut self) {
320 fn filter(key: &SymbolKey, symbol: &mut Symbol) -> bool {
321 let is_internal = key.0.starts_with(|c: char| c.is_ascii_digit());
322
323 let is_gpio_hack = key.0.starts_with("__");
327
328 is_internal || is_gpio_hack || !symbol.user_defined()
329 }
330
331 self.globals.retain(filter);
332 self.scopes.last_mut().unwrap().retain(filter);
333 }
334
335 pub fn dim(&mut self, key: SymbolKey, etype: ExprType) {
337 debug_assert!(
338 !self.globals.contains_key(&key) && !self.scopes.last_mut().unwrap().contains_key(&key),
339 "Pre-existence of variables is checked at compilation time"
340 );
341 self.scopes.last_mut().unwrap().insert(key, Symbol::Variable(etype.default_value()));
342 }
343
344 pub fn dim_shared(&mut self, key: SymbolKey, etype: ExprType) {
346 debug_assert!(
347 !self.globals.contains_key(&key) && !self.scopes.last_mut().unwrap().contains_key(&key),
348 "Pre-existence of variables is checked at compilation time"
349 );
350 self.globals.insert(key, Symbol::Variable(etype.default_value()));
351 }
352
353 pub fn dim_array(&mut self, key: SymbolKey, subtype: ExprType, dimensions: Vec<usize>) {
356 debug_assert!(
357 !self.globals.contains_key(&key) && !self.scopes.last_mut().unwrap().contains_key(&key),
358 "Pre-existence of variables is checked at compilation time"
359 );
360 self.scopes.last_mut().unwrap().insert(key, Symbol::Array(Array::new(subtype, dimensions)));
361 }
362
363 pub fn dim_shared_array(&mut self, key: SymbolKey, subtype: ExprType, dimensions: Vec<usize>) {
366 debug_assert!(
367 !self.globals.contains_key(&key) && !self.scopes.last_mut().unwrap().contains_key(&key),
368 "Pre-existence of variables is checked at compilation time"
369 );
370 self.globals.insert(key, Symbol::Array(Array::new(subtype, dimensions)));
371 }
372
373 pub(crate) fn load(&self, key: &SymbolKey) -> Option<&Symbol> {
378 let local = self.scopes.last().unwrap().get(key);
379 if local.is_some() {
380 return local;
381 }
382 self.globals.get(key)
383 }
384
385 pub(crate) fn load_mut(&mut self, key: &SymbolKey) -> Option<&mut Symbol> {
390 let local = self.scopes.last_mut().unwrap().get_mut(key);
391 if local.is_some() {
392 return local;
393 }
394 self.globals.get_mut(key)
395 }
396
397 pub fn get(&self, vref: &VarRef) -> Result<Option<&Symbol>> {
401 let key = SymbolKey::from(vref.name());
402 let symbol = self.load(&key);
403 if let Some(symbol) = symbol {
404 let stype = symbol.eval_type();
405 if !vref.accepts_callable(stype) {
406 return Err(Error::new(format!("Incompatible types in {} reference", vref)));
407 }
408 }
409 Ok(symbol)
410 }
411
412 pub fn get_auto(&self, var: &str) -> Option<&Symbol> {
414 let key = SymbolKey::from(var);
415 self.load(&key)
416 }
417
418 pub fn get_mut(&mut self, vref: &VarRef) -> Result<Option<&mut Symbol>> {
422 match self.load_mut(&vref.as_symbol_key()) {
423 Some(symbol) => {
424 let stype = symbol.eval_type();
425 if !vref.accepts_callable(stype) {
426 return Err(Error::new(format!("Incompatible types in {} reference", vref)));
427 }
428 Ok(Some(symbol))
429 }
430 None => Ok(None),
431 }
432 }
433
434 pub(crate) fn get_var(&self, vref: &VarRef) -> Result<&Value> {
440 match self.get(vref)? {
441 Some(Symbol::Variable(v)) => Ok(v),
442 Some(_) => Err(Error::new(format!("{} is not a variable", vref.name()))),
443 None => Err(Error::new(format!("Undefined variable {}", vref.name()))),
444 }
445 }
446
447 pub(crate) fn assign(&mut self, key: &SymbolKey, value: Value) {
452 let old_value = match self.globals.get_mut(key) {
453 Some(value) => Some(value),
454 None => self.scopes.last_mut().unwrap().get_mut(key),
455 };
456
457 match old_value {
458 Some(Symbol::Variable(old_value)) => {
459 debug_assert_eq!(
460 mem::discriminant(old_value),
461 mem::discriminant(&value),
462 "Type consistency is validated at compilation time"
463 );
464 *old_value = value;
465 }
466 Some(_) => unreachable!("Type consistency is validated at compilation time"),
467 None => {
468 self.scopes.last_mut().unwrap().insert(key.clone(), Symbol::Variable(value));
469 }
470 }
471 }
472
473 pub fn set_var(&mut self, vref: &VarRef, value: Value) -> Result<()> {
481 let key = vref.as_symbol_key();
482 let value = value.maybe_cast(vref.ref_type())?;
483 match self.get_mut(vref)? {
484 Some(Symbol::Variable(old_value)) => {
485 let value = value.maybe_cast(Some(old_value.as_exprtype()))?;
486 if mem::discriminant(&value) != mem::discriminant(old_value) {
487 return Err(Error::new(format!(
488 "Cannot assign value of type {} to variable of type {}",
489 value.as_exprtype(),
490 old_value.as_exprtype(),
491 )));
492 }
493 self.assign(&key, value);
494 Ok(())
495 }
496 Some(_) => Err(Error::new(format!("Cannot redefine {} as a variable", vref))),
497 None => {
498 if let Some(ref_type) = vref.ref_type() {
499 if !vref.accepts(value.as_exprtype()) {
500 return Err(Error::new(format!(
501 "Cannot assign value of type {} to variable of type {}",
502 value.as_exprtype(),
503 ref_type,
504 )));
505 }
506 }
507 self.scopes.last_mut().unwrap().insert(key, Symbol::Variable(value));
508 Ok(())
509 }
510 }
511 }
512
513 pub(crate) fn unset(&mut self, key: &SymbolKey) -> Result<()> {
515 match self.scopes.last_mut().unwrap().remove(key) {
516 Some(_) => Ok(()),
517 None => Err(Error::new(format!("{} is not defined", key))),
518 }
519 }
520}
521
522pub struct CallableMetadataBuilder {
524 name: Cow<'static, str>,
525 return_type: Option<ExprType>,
526 category: Option<&'static str>,
527 syntax: Option<String>,
528 syntaxes: Vec<CallableSyntax>,
529 description: Option<&'static str>,
530}
531
532impl CallableMetadataBuilder {
533 pub fn new(name: &'static str) -> Self {
539 assert!(name == name.to_ascii_uppercase(), "Callable name must be in uppercase");
540
541 Self {
542 name: Cow::Borrowed(name),
543 return_type: None,
544 syntax: None,
545 syntaxes: vec![],
546 category: None,
547 description: None,
548 }
549 }
550
551 pub fn new_dynamic(name: String) -> Self {
556 Self {
557 name: Cow::Owned(name.to_ascii_uppercase()),
558 return_type: None,
559 syntax: None,
560 syntaxes: vec![],
561 category: None,
562 description: None,
563 }
564 }
565
566 pub fn with_return_type(mut self, return_type: ExprType) -> Self {
568 self.return_type = Some(return_type);
569 self
570 }
571
572 pub fn with_syntax(
574 mut self,
575 syntaxes: &'static [(&'static [SingularArgSyntax], Option<&'static RepeatedSyntax>)],
576 ) -> Self {
577 let syntaxes = syntaxes
578 .iter()
579 .map(|s| CallableSyntax::new_static(s.0, s.1))
580 .collect::<Vec<CallableSyntax>>();
581 if syntaxes.is_empty() {
582 self.syntax = Some("no arguments".to_owned());
583 } else if syntaxes.len() == 1 {
584 self.syntax = Some(syntaxes.first().unwrap().describe());
585 } else {
586 self.syntax = Some(
587 syntaxes
588 .iter()
589 .map(|syn| format!("<{}>", syn.describe()))
590 .collect::<Vec<String>>()
591 .join(" | "),
592 );
593 };
594 self.syntaxes = syntaxes;
595 self
596 }
597
598 pub fn with_dynamic_syntax(
600 mut self,
601 syntaxes: Vec<(Vec<SingularArgSyntax>, Option<RepeatedSyntax>)>,
602 ) -> Self {
603 let syntaxes = syntaxes
604 .into_iter()
605 .map(|s| CallableSyntax::new_dynamic(s.0, s.1))
606 .collect::<Vec<CallableSyntax>>();
607 if syntaxes.is_empty() {
608 self.syntax = Some("no arguments".to_owned());
609 } else if syntaxes.len() == 1 {
610 self.syntax = Some(syntaxes.first().unwrap().describe());
611 } else {
612 self.syntax = Some(
613 syntaxes
614 .iter()
615 .map(|syn| format!("<{}>", syn.describe()))
616 .collect::<Vec<String>>()
617 .join(" | "),
618 );
619 };
620 self.syntaxes = syntaxes;
621 self
622 }
623
624 pub fn with_category(mut self, category: &'static str) -> Self {
627 self.category = Some(category);
628 self
629 }
630
631 pub fn with_description(mut self, description: &'static str) -> Self {
636 for l in description.lines() {
637 assert!(!l.is_empty(), "Description cannot contain empty lines");
638 }
639 self.description = Some(description);
640 self
641 }
642
643 pub fn build(self) -> CallableMetadata {
645 assert!(!self.syntaxes.is_empty(), "All callables must specify a syntax");
646 CallableMetadata {
647 name: self.name,
648 return_type: self.return_type,
649 syntax: self.syntax.expect("All callables must specify a syntax"),
650 syntaxes: self.syntaxes,
651 category: self.category.expect("All callables must specify a category"),
652 description: self.description.expect("All callables must specify a description"),
653 }
654 }
655
656 pub fn test_build(mut self) -> CallableMetadata {
659 if self.syntaxes.is_empty() {
660 self.syntaxes.push(CallableSyntax::new_static(&[], None));
661 }
662 CallableMetadata {
663 name: self.name,
664 return_type: self.return_type,
665 syntax: self.syntax.unwrap_or("".to_owned()),
666 syntaxes: self.syntaxes,
667 category: self.category.unwrap_or(""),
668 description: self.description.unwrap_or(""),
669 }
670 }
671}
672
673#[derive(Clone, Debug)]
678pub struct CallableMetadata {
679 name: Cow<'static, str>,
680 return_type: Option<ExprType>,
681 syntax: String,
682 syntaxes: Vec<CallableSyntax>,
683 category: &'static str,
684 description: &'static str,
685}
686
687impl CallableMetadata {
688 pub fn name(&self) -> &str {
690 &self.name
691 }
692
693 pub fn return_type(&self) -> Option<ExprType> {
695 self.return_type
696 }
697
698 pub fn syntax(&self) -> &str {
700 &self.syntax
701 }
702
703 pub(crate) fn syntaxes(&self) -> &[CallableSyntax] {
705 &self.syntaxes
706 }
707
708 pub fn category(&self) -> &'static str {
711 self.category
712 }
713
714 pub fn description(&self) -> Lines<'static> {
717 self.description.lines()
718 }
719
720 pub fn is_argless(&self) -> bool {
722 self.syntax.is_empty()
723 }
724
725 pub fn is_function(&self) -> bool {
727 self.return_type.is_some()
728 }
729}
730
731#[async_trait(?Send)]
740pub trait Callable {
741 fn metadata(&self) -> &CallableMetadata;
746
747 async fn exec(&self, scope: Scope<'_>, machine: &mut Machine) -> CallResult;
753}
754
755#[cfg(test)]
756mod tests {
757 use super::*;
758 use crate::ast::{ExprType, VarRef};
759 use crate::testutils::*;
760 use std::cell::RefCell;
761
762 #[test]
763 fn test_array_unidimensional_ok() {
764 let mut array = Array::new(ExprType::Integer, vec![5]);
765 assert_eq!(ExprType::Integer, array.subtype());
766
767 array.assign(&[1], 5.into()).unwrap();
768 array.assign(&[4], 8.into()).unwrap();
769 assert_eq!(&Value::Integer(0), array.index(&[0]).unwrap());
770 assert_eq!(&Value::Integer(5), array.index(&[1]).unwrap());
771 assert_eq!(&Value::Integer(0), array.index(&[2]).unwrap());
772 assert_eq!(&Value::Integer(0), array.index(&[3]).unwrap());
773 assert_eq!(&Value::Integer(8), array.index(&[4]).unwrap());
774 }
775
776 #[test]
777 fn test_array_unidimensional_errors() {
778 let mut array = Array::new(ExprType::Integer, vec![5]);
779
780 assert_eq!(
781 "Subscript -1 cannot be negative",
782 format!("{}", array.assign(&[-1], Value::Integer(1)).unwrap_err())
783 );
784 assert_eq!(
785 "Subscript -1 cannot be negative",
786 format!("{}", array.index(&[-1]).unwrap_err())
787 );
788
789 assert_eq!(
790 "Subscript 6 exceeds limit of 5",
791 format!("{}", array.assign(&[6], Value::Integer(1)).unwrap_err())
792 );
793 assert_eq!("Subscript 6 exceeds limit of 5", format!("{}", array.index(&[6]).unwrap_err()));
794 }
795
796 #[test]
797 fn test_array_bidimensional_ok() {
798 let mut array = Array::new(ExprType::Double, vec![2, 3]);
799 assert_eq!(ExprType::Double, array.subtype());
800
801 array.assign(&[0, 1], 9.1.into()).unwrap();
802 array.assign(&[1, 0], 8.1.into()).unwrap();
803 array.assign(&[1, 2], 7.1.into()).unwrap();
804 assert_eq!(&Value::Double(0.0), array.index(&[0, 0]).unwrap());
805 assert_eq!(&Value::Double(9.1), array.index(&[0, 1]).unwrap());
806 assert_eq!(&Value::Double(0.0), array.index(&[0, 2]).unwrap());
807 assert_eq!(&Value::Double(8.1), array.index(&[1, 0]).unwrap());
808 assert_eq!(&Value::Double(0.0), array.index(&[1, 1]).unwrap());
809 assert_eq!(&Value::Double(7.1), array.index(&[1, 2]).unwrap());
810 }
811
812 #[test]
813 fn test_array_bidimensional_errors() {
814 let mut array = Array::new(ExprType::Integer, vec![5, 2]);
815
816 assert_eq!(
817 "Subscript -1 cannot be negative",
818 format!("{}", array.assign(&[-1, 1], Value::Integer(1)).unwrap_err())
819 );
820 assert_eq!(
821 "Subscript -1 cannot be negative",
822 format!("{}", array.index(&[-1, 1]).unwrap_err())
823 );
824
825 assert_eq!(
826 "Subscript -1 cannot be negative",
827 format!("{}", array.assign(&[1, -1], Value::Integer(1)).unwrap_err())
828 );
829 assert_eq!(
830 "Subscript -1 cannot be negative",
831 format!("{}", array.index(&[1, -1]).unwrap_err())
832 );
833
834 assert_eq!(
835 "Subscript 2 exceeds limit of 2",
836 format!("{}", array.assign(&[-1, 2], Value::Integer(1)).unwrap_err())
837 );
838 assert_eq!(
839 "Subscript 2 exceeds limit of 2",
840 format!("{}", array.index(&[-1, 2]).unwrap_err())
841 );
842 }
843
844 #[test]
845 fn test_array_multidimensional() {
846 let mut array = Array::new(ExprType::Integer, vec![2, 4, 3, 5]);
847 assert_eq!(ExprType::Integer, array.subtype());
848
849 let mut n = 0;
852 for i in 0..2 {
853 for j in 0..4 {
854 for k in 0..3 {
855 for l in 0..5 {
856 array.assign(&[i, j, k, l], n.into()).unwrap();
857 assert_eq!(&Value::Integer(n), array.index(&[i, j, k, l]).unwrap());
858 n += 1;
859 }
860 }
861 }
862 }
863
864 let mut n = 0;
866 for i in 0..2 {
867 for j in 0..4 {
868 for k in 0..3 {
869 for l in 0..5 {
870 assert_eq!(&Value::Integer(n), array.index(&[i, j, k, l]).unwrap());
871 n += 1;
872 }
873 }
874 }
875 }
876 }
877
878 #[test]
879 fn test_symbols_clear() {
880 let mut syms = SymbolsBuilder::default()
881 .add_array("SOMEARRAY", ExprType::Integer)
882 .add_callable(OutCommand::new(Rc::from(RefCell::from(vec![]))))
883 .add_callable(SumFunction::new())
884 .add_var("SOMEVAR", Value::Boolean(true))
885 .add_var("__SYSTEM_VAR", Value::Integer(42))
886 .add_global_var("GLOBAL_VAR", Value::Integer(43))
887 .add_global_var("__GLOBAL_SYSTEM_VAR", Value::Integer(44))
888 .build();
889
890 assert!(syms.get(&VarRef::new("SOMEARRAY", None)).unwrap().is_some());
891 assert!(syms.get(&VarRef::new("OUT", None)).unwrap().is_some());
892 assert!(syms.get(&VarRef::new("SUM", None)).unwrap().is_some());
893 assert!(syms.get(&VarRef::new("SOMEVAR", None)).unwrap().is_some());
894 assert!(syms.get(&VarRef::new("__SYSTEM_VAR", None)).unwrap().is_some());
895 assert!(syms.get(&VarRef::new("GLOBAL_VAR", None)).unwrap().is_some());
896 assert!(syms.get(&VarRef::new("__GLOBAL_SYSTEM_VAR", None)).unwrap().is_some());
897 syms.clear();
898 assert!(syms.get(&VarRef::new("SOMEARRAY", None)).unwrap().is_none());
899 assert!(syms.get(&VarRef::new("OUT", None)).unwrap().is_some());
900 assert!(syms.get(&VarRef::new("SUM", None)).unwrap().is_some());
901 assert!(syms.get(&VarRef::new("SOMEVAR", None)).unwrap().is_none());
902 assert!(syms.get(&VarRef::new("__SYSTEM_VAR", None)).unwrap().is_some());
903 assert!(syms.get(&VarRef::new("GLOBAL_VAR", None)).unwrap().is_none());
904 assert!(syms.get(&VarRef::new("__GLOBAL_SYSTEM_VAR", None)).unwrap().is_some());
905 }
906
907 #[test]
908 fn test_symbols_dim_ok() {
909 let mut syms = Symbols::default();
910
911 syms.dim(SymbolKey::from("A_Boolean"), ExprType::Boolean);
912 match syms.get(&VarRef::new("a_boolean", None)).unwrap().unwrap() {
913 Symbol::Variable(value) => assert_eq!(&Value::Boolean(false), value),
914 _ => panic!("Got something that is not the variable we asked for"),
915 }
916 }
917
918 #[test]
919 fn test_symbols_dim_shared_ok() {
920 let mut syms = Symbols::default();
921
922 syms.dim_shared(SymbolKey::from("A_Boolean"), ExprType::Boolean);
923 match syms.get(&VarRef::new("a_boolean", None)).unwrap().unwrap() {
924 Symbol::Variable(value) => assert_eq!(&Value::Boolean(false), value),
925 _ => panic!("Got something that is not the variable we asked for"),
926 }
927 }
928
929 #[test]
930 fn test_symbols_scopes_dim() {
931 let mut syms = Symbols::default();
932
933 syms.dim(SymbolKey::from("Local_Var"), ExprType::Boolean);
934 syms.dim(SymbolKey::from("Other_Var"), ExprType::Double);
935
936 syms.enter_scope();
937 syms.dim(SymbolKey::from("Local_Var"), ExprType::Integer);
938 match syms.get(&VarRef::new("local_var", None)).unwrap().unwrap() {
939 Symbol::Variable(value) => assert_eq!(&Value::Integer(0), value),
940 _ => panic!("Got something that is not the variable we asked for"),
941 }
942 assert!(syms.get(&VarRef::new("other_var", None)).unwrap().is_none());
943 syms.leave_scope();
944
945 match syms.get(&VarRef::new("local_var", None)).unwrap().unwrap() {
946 Symbol::Variable(value) => assert_eq!(&Value::Boolean(false), value),
947 _ => panic!("Got something that is not the variable we asked for"),
948 }
949 match syms.get(&VarRef::new("other_var", None)).unwrap().unwrap() {
950 Symbol::Variable(value) => assert_eq!(&Value::Double(0.0), value),
951 _ => panic!("Got something that is not the variable we asked for"),
952 }
953 }
954
955 #[test]
956 fn test_symbols_scopes_dim_shared() {
957 let mut syms = Symbols::default();
958
959 syms.dim_shared(SymbolKey::from("Global_Var"), ExprType::Boolean);
960
961 syms.enter_scope();
962 match syms.get(&VarRef::new("global_var", None)).unwrap().unwrap() {
963 Symbol::Variable(value) => assert_eq!(&Value::Boolean(false), value),
964 _ => panic!("Got something that is not the variable we asked for"),
965 }
966 syms.leave_scope();
967
968 match syms.get(&VarRef::new("global_var", None)).unwrap().unwrap() {
969 Symbol::Variable(value) => assert_eq!(&Value::Boolean(false), value),
970 _ => panic!("Got something that is not the variable we asked for"),
971 }
972 }
973
974 fn assert_same_array_shape(exp_subtype: ExprType, exp_dims: &[usize], symbol: &Symbol) {
975 match symbol {
976 Symbol::Array(array) => {
977 assert_eq!(exp_subtype, array.subtype());
978 assert_eq!(exp_dims, array.dimensions());
979 }
980 _ => panic!("Expected array symbol type, got {:?}", symbol),
981 }
982 }
983
984 #[test]
985 fn test_symbols_dim_array_ok() {
986 let mut syms = Symbols::default();
987
988 syms.dim_array(SymbolKey::from("A_Boolean"), ExprType::Boolean, vec![1]);
989 assert_same_array_shape(
990 ExprType::Boolean,
991 &[1],
992 syms.get(&VarRef::new("a_boolean", None)).unwrap().unwrap(),
993 );
994 }
995
996 #[test]
997 fn test_symbols_get_check_types() {
998 let syms = SymbolsBuilder::default()
1000 .add_array("BOOL_ARRAY", ExprType::Boolean)
1001 .add_callable(OutCommand::new(Rc::from(RefCell::from(vec![]))))
1002 .add_callable(SumFunction::new())
1003 .add_var("STRING_VAR", Value::Text("".to_owned()))
1004 .build();
1005
1006 for ref_type in &[None, Some(ExprType::Boolean)] {
1007 match syms.get(&VarRef::new("bool_array", *ref_type)).unwrap().unwrap() {
1008 Symbol::Array(array) => assert_eq!(ExprType::Boolean, array.subtype()),
1009 _ => panic!("Got something that is not the array we asked for"),
1010 }
1011 }
1012 assert_eq!(
1013 "Incompatible types in bool_array$ reference",
1014 format!("{}", syms.get(&VarRef::new("bool_array", Some(ExprType::Text))).unwrap_err())
1015 );
1016
1017 match syms.get(&VarRef::new("out", None)).unwrap().unwrap() {
1018 Symbol::Callable(c) => assert_eq!(None, c.metadata().return_type()),
1019 _ => panic!("Got something that is not the command we asked for"),
1020 }
1021 assert_eq!(
1022 "Incompatible types in out# reference",
1023 format!("{}", syms.get(&VarRef::new("out", Some(ExprType::Double))).unwrap_err())
1024 );
1025
1026 for ref_type in &[None, Some(ExprType::Integer)] {
1027 match syms.get(&VarRef::new("sum", *ref_type)).unwrap().unwrap() {
1028 Symbol::Callable(f) => {
1029 assert_eq!(Some(ExprType::Integer), f.metadata().return_type())
1030 }
1031 _ => panic!("Got something that is not the function we asked for"),
1032 }
1033 }
1034 assert_eq!(
1035 "Incompatible types in sum# reference",
1036 format!("{}", syms.get(&VarRef::new("sum", Some(ExprType::Double))).unwrap_err())
1037 );
1038
1039 for ref_type in &[None, Some(ExprType::Text)] {
1040 match syms.get(&VarRef::new("string_var", *ref_type)).unwrap().unwrap() {
1041 Symbol::Variable(value) => assert_eq!(ExprType::Text, value.as_exprtype()),
1042 _ => panic!("Got something that is not the variable we asked for"),
1043 }
1044 }
1045 assert_eq!(
1046 "Incompatible types in string_var% reference",
1047 format!(
1048 "{}",
1049 syms.get(&VarRef::new("string_var", Some(ExprType::Integer))).unwrap_err()
1050 )
1051 );
1052 }
1053
1054 #[test]
1055 fn test_symbols_get_case_insensitivity() {
1056 let syms = SymbolsBuilder::default()
1058 .add_array("SOMEARRAY", ExprType::Integer)
1059 .add_callable(OutCommand::new(Rc::from(RefCell::from(vec![]))))
1060 .add_callable(SumFunction::new())
1061 .add_var("SOMEVAR", Value::Boolean(true))
1062 .build();
1063
1064 assert!(syms.get(&VarRef::new("somearray", None)).unwrap().is_some());
1065 assert!(syms.get(&VarRef::new("SomeArray", None)).unwrap().is_some());
1066
1067 assert!(syms.get(&VarRef::new("out", None)).unwrap().is_some());
1068 assert!(syms.get(&VarRef::new("Out", None)).unwrap().is_some());
1069
1070 assert!(syms.get(&VarRef::new("sum", None)).unwrap().is_some());
1071 assert!(syms.get(&VarRef::new("Sum", None)).unwrap().is_some());
1072
1073 assert!(syms.get(&VarRef::new("somevar", None)).unwrap().is_some());
1074 assert!(syms.get(&VarRef::new("SomeVar", None)).unwrap().is_some());
1075 }
1076
1077 #[test]
1078 fn test_symbols_get_undefined() {
1079 let syms = SymbolsBuilder::default().add_var("SOMETHING", Value::Integer(3)).build();
1081 assert!(syms.get(&VarRef::new("SOME_THIN", Some(ExprType::Integer))).unwrap().is_none());
1082 }
1083
1084 #[test]
1085 fn test_symbols_get_mut_check_types() {
1086 let mut syms = SymbolsBuilder::default()
1088 .add_array("BOOL_ARRAY", ExprType::Boolean)
1089 .add_callable(OutCommand::new(Rc::from(RefCell::from(vec![]))))
1090 .add_callable(SumFunction::new())
1091 .add_var("STRING_VAR", Value::Text("".to_owned()))
1092 .build();
1093
1094 for ref_type in &[None, Some(ExprType::Boolean)] {
1095 match syms.get_mut(&VarRef::new("bool_array", *ref_type)).unwrap().unwrap() {
1096 Symbol::Array(array) => assert_eq!(ExprType::Boolean, array.subtype()),
1097 _ => panic!("Got something that is not the array we asked for"),
1098 }
1099 }
1100 assert_eq!(
1101 "Incompatible types in bool_array$ reference",
1102 format!(
1103 "{}",
1104 syms.get_mut(&VarRef::new("bool_array", Some(ExprType::Text))).unwrap_err()
1105 )
1106 );
1107
1108 match syms.get_mut(&VarRef::new("out", None)).unwrap().unwrap() {
1109 Symbol::Callable(c) => assert_eq!(None, c.metadata().return_type()),
1110 _ => panic!("Got something that is not the command we asked for"),
1111 }
1112 assert_eq!(
1113 "Incompatible types in out# reference",
1114 format!("{}", syms.get_mut(&VarRef::new("out", Some(ExprType::Double))).unwrap_err())
1115 );
1116
1117 for ref_type in &[None, Some(ExprType::Integer)] {
1118 match syms.get_mut(&VarRef::new("sum", *ref_type)).unwrap().unwrap() {
1119 Symbol::Callable(f) => {
1120 assert_eq!(Some(ExprType::Integer), f.metadata().return_type())
1121 }
1122 _ => panic!("Got something that is not the function we asked for"),
1123 }
1124 }
1125 assert_eq!(
1126 "Incompatible types in sum# reference",
1127 format!("{}", syms.get_mut(&VarRef::new("sum", Some(ExprType::Double))).unwrap_err())
1128 );
1129
1130 for ref_type in &[None, Some(ExprType::Text)] {
1131 match syms.get_mut(&VarRef::new("string_var", *ref_type)).unwrap().unwrap() {
1132 Symbol::Variable(value) => assert_eq!(ExprType::Text, value.as_exprtype()),
1133 _ => panic!("Got something that is not the variable we asked for"),
1134 }
1135 }
1136 assert_eq!(
1137 "Incompatible types in string_var% reference",
1138 format!(
1139 "{}",
1140 syms.get_mut(&VarRef::new("string_var", Some(ExprType::Integer))).unwrap_err()
1141 )
1142 );
1143 }
1144
1145 #[test]
1146 fn test_symbols_get_mut_case_insensitivity() {
1147 let mut syms = SymbolsBuilder::default()
1149 .add_array("SOMEARRAY", ExprType::Integer)
1150 .add_callable(OutCommand::new(Rc::from(RefCell::from(vec![]))))
1151 .add_callable(SumFunction::new())
1152 .add_var("SOMEVAR", Value::Boolean(true))
1153 .build();
1154
1155 assert!(syms.get_mut(&VarRef::new("somearray", None)).unwrap().is_some());
1156 assert!(syms.get_mut(&VarRef::new("SomeArray", None)).unwrap().is_some());
1157
1158 assert!(syms.get_mut(&VarRef::new("out", None)).unwrap().is_some());
1159 assert!(syms.get_mut(&VarRef::new("Out", None)).unwrap().is_some());
1160
1161 assert!(syms.get_mut(&VarRef::new("sum", None)).unwrap().is_some());
1162 assert!(syms.get_mut(&VarRef::new("Sum", None)).unwrap().is_some());
1163
1164 assert!(syms.get_mut(&VarRef::new("somevar", None)).unwrap().is_some());
1165 assert!(syms.get_mut(&VarRef::new("SomeVar", None)).unwrap().is_some());
1166 }
1167
1168 #[test]
1169 fn test_symbols_get_mut_undefined() {
1170 let mut syms = SymbolsBuilder::default().add_var("SOMETHING", Value::Integer(3)).build();
1172 assert!(syms
1173 .get_mut(&VarRef::new("SOME_THIN", Some(ExprType::Integer)))
1174 .unwrap()
1175 .is_none());
1176 }
1177
1178 #[test]
1179 fn test_symbols_get_auto() {
1180 let syms = SymbolsBuilder::default()
1182 .add_array("BOOL_ARRAY", ExprType::Boolean)
1183 .add_callable(OutCommand::new(Rc::from(RefCell::from(vec![]))))
1184 .add_callable(SumFunction::new())
1185 .add_var("STRING_VAR", Value::Text("".to_owned()))
1186 .build();
1187
1188 match syms.get_auto("bool_array").unwrap() {
1189 Symbol::Array(array) => assert_eq!(ExprType::Boolean, array.subtype()),
1190 _ => panic!("Got something that is not the array we asked for"),
1191 }
1192
1193 match syms.get_auto("out").unwrap() {
1194 Symbol::Callable(c) => assert_eq!(None, c.metadata().return_type()),
1195 _ => panic!("Got something that is not the command we asked for"),
1196 }
1197
1198 match syms.get_auto("sum").unwrap() {
1199 Symbol::Callable(f) => assert_eq!(Some(ExprType::Integer), f.metadata().return_type()),
1200 _ => panic!("Got something that is not the function we asked for"),
1201 }
1202
1203 match syms.get_auto("string_var").unwrap() {
1204 Symbol::Variable(value) => assert_eq!(ExprType::Text, value.as_exprtype()),
1205 _ => panic!("Got something that is not the variable we asked for"),
1206 }
1207 }
1208
1209 #[test]
1210 fn test_symbols_get_auto_case_insensitivity() {
1211 let syms = SymbolsBuilder::default()
1213 .add_array("SOMEARRAY", ExprType::Integer)
1214 .add_callable(OutCommand::new(Rc::from(RefCell::from(vec![]))))
1215 .add_callable(SumFunction::new())
1216 .add_var("SOMEVAR", Value::Boolean(true))
1217 .build();
1218
1219 assert!(syms.get_auto("somearray").is_some());
1220 assert!(syms.get_auto("SomeArray").is_some());
1221
1222 assert!(syms.get_auto("out").is_some());
1223 assert!(syms.get_auto("Out").is_some());
1224
1225 assert!(syms.get_auto("sum").is_some());
1226 assert!(syms.get_auto("Sum").is_some());
1227
1228 assert!(syms.get_auto("somevar").is_some());
1229 assert!(syms.get_auto("SomeVar").is_some());
1230 }
1231
1232 #[test]
1233 fn test_symbols_get_auto_undefined() {
1234 let syms = SymbolsBuilder::default().add_var("SOMETHING", Value::Integer(3)).build();
1236 assert!(syms.get_auto("SOME_THIN").is_none());
1237 }
1238
1239 fn check_var(syms: &Symbols, name: &str, exp_value: Value) {
1241 match syms.get(&VarRef::new(name, None)).unwrap().unwrap() {
1242 Symbol::Variable(value) => assert_eq!(exp_value, *value),
1243 _ => panic!("Got something that is not the variable we asked for"),
1244 }
1245 }
1246
1247 #[test]
1248 fn test_symbols_set_var_new_check_types_ok() {
1249 for value in [
1250 Value::Boolean(true),
1251 Value::Double(3.4),
1252 Value::Integer(5),
1253 Value::Text("a".to_owned()),
1254 ] {
1255 let mut syms = Symbols::default();
1256 syms.set_var(&VarRef::new("a", None), value.clone()).unwrap();
1257 check_var(&syms, "a", value.clone());
1258 syms.set_var(&VarRef::new("v", Some(value.as_exprtype())), value.clone()).unwrap();
1259 check_var(&syms, "v", value);
1260 }
1261 }
1262
1263 #[test]
1264 fn test_symbols_apply_new() {
1265 let mut syms = Symbols::default();
1266 syms.assign(&SymbolKey::from("a"), Value::Integer(5));
1267 check_var(&syms, "a", Value::Integer(5));
1268 }
1269
1270 #[test]
1271 fn test_symbols_apply_existing() {
1272 let mut syms = SymbolsBuilder::default().add_var("A", Value::Double(1.0)).build();
1273 syms.assign(&SymbolKey::from("a"), Value::Double(2.0));
1274 check_var(&syms, "a", Value::Double(2.0));
1275 }
1276
1277 #[test]
1278 fn test_symbols_get_var_apply_case_insensitivity() {
1279 let mut syms = Symbols::default();
1280 syms.assign(&SymbolKey::from("SomeName"), Value::Integer(6));
1281 assert_eq!(Value::Integer(6), *syms.get_var(&VarRef::new("somename", None)).unwrap());
1282 }
1283
1284 #[test]
1285 fn test_symbols_get_var_apply_replace_value() {
1286 let mut syms = Symbols::default();
1287 syms.assign(&SymbolKey::from("the_var"), Value::Integer(100));
1288 assert_eq!(Value::Integer(100), *syms.get_var(&VarRef::new("the_var", None)).unwrap());
1289 syms.assign(&SymbolKey::from("the_var"), Value::Integer(200));
1290 assert_eq!(Value::Integer(200), *syms.get_var(&VarRef::new("the_var", None)).unwrap());
1291 }
1292
1293 #[test]
1294 fn test_symbols_set_var_new_check_types_incompatible() {
1295 let mut syms = Symbols::default();
1296 assert_eq!(
1297 "Cannot assign value of type BOOLEAN to variable of type INTEGER",
1298 format!(
1299 "{}",
1300 syms.set_var(&VarRef::new("v", Some(ExprType::Integer)), Value::Boolean(false))
1301 .unwrap_err()
1302 )
1303 );
1304 }
1305
1306 #[test]
1307 fn test_symbols_set_var_new_integer_to_double() {
1308 let mut syms = Symbols::default();
1309 syms.set_var(&VarRef::new("v", Some(ExprType::Double)), Value::Integer(3)).unwrap();
1310 check_var(&syms, "v", Value::Double(3.0));
1311 }
1312
1313 #[test]
1314 fn test_symbols_set_var_new_double_to_integer() {
1315 for (expected, actual) in [(4, 4.4), (5, 4.5), (5, 4.6)] {
1316 let mut syms = Symbols::default();
1317 syms.set_var(&VarRef::new("v", Some(ExprType::Integer)), Value::Double(actual))
1318 .unwrap();
1319 check_var(&syms, "v", Value::Integer(expected));
1320 }
1321 }
1322
1323 #[test]
1324 fn test_symbols_set_var_existing_check_types_ok() {
1325 for value in [
1326 Value::Boolean(true),
1327 Value::Double(3.4),
1328 Value::Integer(5),
1329 Value::Text("a".to_owned()),
1330 ] {
1331 let mut syms = SymbolsBuilder::default()
1332 .add_var("A", value.clone())
1333 .add_var("V", value.clone())
1334 .build();
1335 syms.set_var(&VarRef::new("a", None), value.clone()).unwrap();
1336 check_var(&syms, "a", value.clone());
1337 syms.set_var(&VarRef::new("v", Some(value.as_exprtype())), value.clone()).unwrap();
1338 check_var(&syms, "v", value);
1339 }
1340 }
1341
1342 #[test]
1343 fn test_symbols_set_var_existing_check_types_incompatible() {
1344 let mut syms = SymbolsBuilder::default().add_var("V", Value::Double(10.0)).build();
1345 assert_eq!(
1346 "Cannot assign value of type BOOLEAN to variable of type DOUBLE",
1347 format!("{}", syms.set_var(&VarRef::new("v", None), Value::Boolean(true)).unwrap_err())
1348 );
1349 }
1350
1351 #[test]
1352 fn test_symbols_set_existing_integer_to_double() {
1353 let mut syms = SymbolsBuilder::default()
1354 .add_var("A", Value::Double(1.0))
1355 .add_var("V", Value::Double(1.0))
1356 .build();
1357 syms.set_var(&VarRef::new("a", None), Value::Integer(3)).unwrap();
1358 syms.set_var(&VarRef::new("v", Some(ExprType::Double)), Value::Integer(3)).unwrap();
1359 check_var(&syms, "a", Value::Double(3.0));
1360 check_var(&syms, "v", Value::Double(3.0));
1361 }
1362
1363 #[test]
1364 fn test_symbols_set_existing_double_to_integer() {
1365 for (expected, actual) in [(4, 4.4), (5, 4.5), (5, 4.6)] {
1366 let mut syms = SymbolsBuilder::default()
1367 .add_var("A", Value::Integer(1))
1368 .add_var("V", Value::Integer(1))
1369 .build();
1370 syms.set_var(&VarRef::new("a", None), Value::Double(actual)).unwrap();
1371 syms.set_var(&VarRef::new("v", Some(ExprType::Integer)), Value::Double(actual))
1372 .unwrap();
1373 check_var(&syms, "a", Value::Integer(expected));
1374 check_var(&syms, "v", Value::Integer(expected));
1375 }
1376 }
1377
1378 #[test]
1379 fn test_symbols_set_var_name_overlap() {
1380 let mut syms = SymbolsBuilder::default()
1381 .add_array("SOMEARRAY", ExprType::Integer)
1382 .add_callable(OutCommand::new(Rc::from(RefCell::from(vec![]))))
1383 .add_callable(SumFunction::new())
1384 .add_var("SOMEVAR", Value::Boolean(true))
1385 .build();
1386
1387 assert_eq!(
1388 "Cannot redefine Out as a variable",
1389 format!("{}", syms.set_var(&VarRef::new("Out", None), Value::Integer(1)).unwrap_err())
1390 );
1391
1392 assert_eq!(
1393 "Cannot redefine Sum% as a variable",
1394 format!(
1395 "{}",
1396 syms.set_var(&VarRef::new("Sum", Some(ExprType::Integer)), Value::Integer(1))
1397 .unwrap_err()
1398 )
1399 );
1400
1401 assert_eq!(
1402 "Cannot redefine SomeArray% as a variable",
1403 format!(
1404 "{}",
1405 syms.set_var(&VarRef::new("SomeArray", Some(ExprType::Integer)), Value::Integer(1))
1406 .unwrap_err()
1407 )
1408 );
1409
1410 assert_eq!(
1411 "Incompatible types in SomeArray$ reference",
1412 format!(
1413 "{}",
1414 syms.set_var(&VarRef::new("SomeArray", Some(ExprType::Text)), Value::Integer(1))
1415 .unwrap_err()
1416 )
1417 );
1418 }
1419
1420 #[test]
1421 fn test_symbols_set_var_global() {
1422 let mut syms = Symbols::default();
1423 syms.dim_shared(SymbolKey::from("global"), ExprType::Integer);
1424 syms.set_var(&VarRef::new("global", None), Value::Integer(5)).unwrap();
1425 check_var(&syms, "global", Value::Integer(5));
1426
1427 syms.enter_scope();
1428 syms.set_var(&VarRef::new("global", None), Value::Integer(7)).unwrap();
1429 check_var(&syms, "global", Value::Integer(7));
1430 syms.leave_scope();
1431
1432 check_var(&syms, "global", Value::Integer(7));
1433 }
1434
1435 #[test]
1436 fn test_symbols_get_var_set_var_case_insensitivity() {
1437 let mut syms = Symbols::default();
1438 syms.set_var(&VarRef::new("SomeName", None), Value::Integer(6)).unwrap();
1439 assert_eq!(Value::Integer(6), *syms.get_var(&VarRef::new("somename", None)).unwrap());
1440 }
1441
1442 #[test]
1443 fn test_symbols_get_var_set_var_replace_value() {
1444 let mut syms = Symbols::default();
1445 syms.set_var(&VarRef::new("the_var", None), Value::Integer(100)).unwrap();
1446 assert_eq!(Value::Integer(100), *syms.get_var(&VarRef::new("the_var", None)).unwrap());
1447 syms.set_var(&VarRef::new("the_var", None), Value::Integer(200)).unwrap();
1448 assert_eq!(Value::Integer(200), *syms.get_var(&VarRef::new("the_var", None)).unwrap());
1449 }
1450
1451 #[test]
1452 fn test_symbols_get_var_undefined_error() {
1453 let syms = SymbolsBuilder::default().add_var("SOMETHING", Value::Integer(3)).build();
1454 assert_eq!(
1455 "Undefined variable SOME_THIN",
1456 format!(
1457 "{}",
1458 syms.get_var(&VarRef::new("SOME_THIN", Some(ExprType::Integer))).unwrap_err()
1459 )
1460 );
1461 }
1462
1463 #[test]
1464 fn test_symbols_unset_ok() {
1465 let mut syms = SymbolsBuilder::default()
1466 .add_array("SOMEARRAY", ExprType::Integer)
1467 .add_callable(OutCommand::new(Rc::from(RefCell::from(vec![]))))
1468 .add_callable(SumFunction::new())
1469 .add_var("SOMEVAR", Value::Boolean(true))
1470 .build();
1471
1472 let mut count = 2;
1473 for name in ["SomeArray", "SomeVar"] {
1474 syms.unset(&SymbolKey::from(name)).unwrap();
1475 count -= 1;
1476 assert_eq!(count, syms.locals().len());
1477 }
1478 assert_eq!(0, count);
1479
1480 syms.unset(&SymbolKey::from("Out")).unwrap_err();
1481 syms.unset(&SymbolKey::from("Sum")).unwrap_err();
1482 assert_eq!(2, syms.callables().len());
1483 }
1484
1485 #[test]
1486 fn test_symbols_unset_undefined() {
1487 let mut syms = SymbolsBuilder::default().add_var("SOMETHING", Value::Integer(3)).build();
1488 syms.unset(&SymbolKey::from("FOO")).unwrap_err();
1489 assert_eq!(1, syms.locals().len());
1490 }
1491}