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