endbasic_core/
syms.rs

1// EndBASIC
2// Copyright 2021 Julio Merino
3//
4// Licensed under the Apache License, Version 2.0 (the "License"); you may not
5// use this file except in compliance with the License.  You may obtain a copy
6// of the License at:
7//
8//     http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
13// License for the specific language governing permissions and limitations
14// under the License.
15
16//! Symbol definitions and symbols table representation.
17
18use 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/// Command or function execution errors.
33///
34/// These are separate from the more generic `Error` type because they are not annotated with the
35/// specific callable that triggered the error.  We add such annotation once we capture the error
36/// within the evaluation logic.
37#[derive(Debug)]
38pub enum CallError {
39    /// A specific parameter had an invalid value.
40    ArgumentError(LineCol, String),
41
42    /// Error while evaluating input arguments.
43    EvalError(LineCol, String),
44
45    /// Any other error not representable by other values.
46    InternalError(LineCol, String),
47
48    /// I/O error during execution.
49    IoError(io::Error),
50
51    /// Hack to support errors that arise from within a program that is `RUN`.
52    // TODO(jmmv): Consider unifying `CallError` with `exec::Error`.
53    NestedError(String),
54
55    /// General mismatch of parameters given to the function with expectations (different numbers,
56    /// invalid types).
57    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
72/// Result for callable execution return values.
73pub type CallResult = std::result::Result<(), CallError>;
74
75/// The key of a symbol in the symbols table.
76#[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/// Represents a multidimensional array.
92#[derive(Clone, Debug, PartialEq)]
93pub struct Array {
94    /// The type of all elements in the array.
95    subtype: ExprType,
96
97    /// The dimensions of the array.  At least one must be present.
98    dimensions: Vec<usize>,
99
100    /// The values in the array, flattened.  Given dimensions `(N, M, O)`, an element `(i, j, k)` is
101    /// at position `i * (M * O) + j * O + k`.
102    values: Vec<Value>,
103}
104
105impl Array {
106    /// Creates a new array of the given `subtype` and `dimensions`.
107    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    /// Returns the dimensions of the array.
123    pub fn dimensions(&self) -> &[usize] {
124        &self.dimensions
125    }
126
127    /// Returns the type of the elements in this array.
128    pub fn subtype(&self) -> ExprType {
129        self.subtype
130    }
131
132    /// Validates that the subscript `i` is in the `[0,max)` range and converts it to an `usize`.
133    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    /// Computes the index to access the flat `values` array given a list of `subscripts`.
144    ///
145    /// It is an error if `dimensions` and `subscripts` have different sizes, or if the values in
146    /// `subscripts` are negative.
147    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    /// Assings the `value` to the array position indicated by the `subscripts`.
168    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    /// Obtains the value contained in the array position indicated by the `subscripts`.
187    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
195/// Holds the definition of a symbol.
196pub enum Symbol {
197    /// An array definition.
198    Array(Array),
199
200    /// A callable definition.
201    Callable(Rc<dyn Callable>),
202
203    /// A variable definition.
204    Variable(Value),
205}
206
207impl Symbol {
208    /// Returns the type the symbol evaluates as.
209    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    /// Returns the metadata for the symbol, if any.
218    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    /// Returns whether the symbol was defined by the user or not.
227    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
246/// Storage for all symbols that exist at runtime.
247///
248/// Symbols are represented as a two-layer map: the globals map contains all symbols that are
249/// visible by all scopes, and the scope contains all symbols that are only visible within a
250/// given scope.
251///
252/// The collection of symbols that is visible at any given point in time is thus the union of
253/// the global symbols and the symbols in the last scope.
254///
255/// Scopes are represented as a stack in order to support nested function calls.
256pub struct Symbols {
257    /// Map of global symbol names to their definitions.
258    globals: HashMap<SymbolKey, Symbol>,
259
260    /// Map of local symbol names to their definitions.
261    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    /// Constructs a symbols object from a flat map of symbol names to their definitions.
272    #[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    /// Enters a new scope.
281    pub(crate) fn enter_scope(&mut self) {
282        self.scopes.push(HashMap::default());
283    }
284
285    /// Leaves the current scope.
286    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    /// Registers the given builtin callable as a global symbol.
293    ///
294    /// Given that callables cannot be defined at runtime, specifying a non-unique name results in
295    /// a panic.
296    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    /// Returns the mapping of all callables.
303    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    /// Returns the mapping of all symbols in the current scope that are not globals.
314    pub fn locals(&self) -> &HashMap<SymbolKey, Symbol> {
315        self.scopes.last().unwrap()
316    }
317
318    /// Clears all user-defined symbols.
319    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            // TODO(jmmv): Preserving symbols that start with __ is a hack that was added to support
324            // the already-existing GPIO tests when RUN was changed to issue a CLEAR upfront.  This
325            // is undocumented behavior and we should find a nicer way to do this.
326            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    /// Defines a new local variable `key` of type `etype`.  The variable must not yet exist.
336    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    /// Defines a new global variable `key` of type `etype`.  The variable must not yet exist.
345    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    /// Defines a new array `key` of type `subtype` with `dimensions`.  The array must not yet
354    /// exist, and the name may not overlap function or variable names.
355    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    /// Defines a new global array `key` of type `subtype` with `dimensions`.  The array must not yet
364    /// exist, and the name may not overlap function or variable names.
365    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    /// Obtains the value of a symbol or `None` if it is not defined.
374    ///
375    /// This is meant to use by the compiler only.  All other users should call `get` instead
376    /// to do the necessary runtime validity checks.
377    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    /// Obtains the value of a symbol or `None` if it is not defined.
386    ///
387    /// This is meant to use by the compiler only.  All other users should call `get` instead
388    /// to do the necessary runtime validity checks.
389    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    /// Obtains the value of a symbol or `None` if it is not defined.
398    ///
399    /// Returns an error if the type annotation in the symbol reference does not match its type.
400    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    /// Obtains the value of a symbol or `None` if it is not defined.
413    pub fn get_auto(&self, var: &str) -> Option<&Symbol> {
414        let key = SymbolKey::from(var);
415        self.load(&key)
416    }
417
418    /// Obtains the value of a symbol or `None` if it is not defined.
419    ///
420    /// Returns an error if the type annotation in the symbol reference does not match its type.
421    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    /// Obtains the value of a variable.
435    ///
436    /// Returns an error if the variable is not defined, if the referenced symbol is not a variable,
437    /// or if the type annotation in the variable reference does not match the type of the value
438    /// that the variable contains.
439    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    /// Sets the value of a variable without type promotion nor type checking.
448    ///
449    /// This is meant to use by the compiler only.  All other users should call `set_var` instead
450    /// to do the necessary runtime validity checks.
451    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    /// Sets the value of a variable.
474    ///
475    /// If `vref` contains a type annotation, the type of the value must be compatible with that
476    /// type annotation.
477    ///
478    /// If the variable is already defined, then the type of the new value must be compatible with
479    /// the existing variable.  In other words: a variable cannot change types while it's alive.
480    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    /// Unsets the symbol `key` irrespective of its type.
514    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
522/// Builder pattern for a callable's metadata.
523pub 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    /// Constructs a new metadata builder with the minimum information necessary.
534    ///
535    /// All code except tests must populate the whole builder with details.  This is enforced at
536    /// construction time, where we only allow some fields to be missing under the test
537    /// configuration.
538    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    /// Constructs a new metadata builder with the minimum information necessary.
552    ///
553    /// This is the same as `new` but using a dynamically-allocated name, which is necessary for
554    /// user-defined symbols.
555    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    /// Sets the return type of the callable.
567    pub fn with_return_type(mut self, return_type: ExprType) -> Self {
568        self.return_type = Some(return_type);
569        self
570    }
571
572    /// Sets the syntax specifications for this callable.
573    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    /// Sets the syntax specifications for this callable.
599    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    /// Sets the category for this callable.  All callables with the same category name will be
625    /// grouped together in help messages.
626    pub fn with_category(mut self, category: &'static str) -> Self {
627        self.category = Some(category);
628        self
629    }
630
631    /// Sets the description for this callable.  The `description` is a collection of paragraphs
632    /// separated by a single newline character, where the first paragraph is taken as the summary
633    /// of the description.  The summary must be a short sentence that is descriptive enough to be
634    /// understood without further details.  Empty lines (paragraphs) are not allowed.
635    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    /// Generates the final `CallableMetadata` object, ensuring all values are present.
644    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    /// Generates the final `CallableMetadata` object, ensuring the minimal set of values are
657    /// present.  Only useful for testing.
658    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/// Representation of a callable's metadata.
674///
675/// The callable is expected to hold onto an instance of this object within its struct to make
676/// queries fast.
677#[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    /// Gets the callable's name, all in uppercase.
689    pub fn name(&self) -> &str {
690        &self.name
691    }
692
693    /// Gets the callable's return type.
694    pub fn return_type(&self) -> Option<ExprType> {
695        self.return_type
696    }
697
698    /// Gets the callable's syntax specification.
699    pub fn syntax(&self) -> &str {
700        &self.syntax
701    }
702
703    /// Returns the callable's syntax definitions.
704    pub(crate) fn syntaxes(&self) -> &[CallableSyntax] {
705        &self.syntaxes
706    }
707
708    /// Gets the callable's category as a collection of lines.  The first line is the title of the
709    /// category, and any extra lines are additional information for it.
710    pub fn category(&self) -> &'static str {
711        self.category
712    }
713
714    /// Gets the callable's textual description as a collection of lines.  The first line is the
715    /// summary of the callable's purpose.
716    pub fn description(&self) -> Lines<'static> {
717        self.description.lines()
718    }
719
720    /// Returns true if this is a callable that takes no arguments.
721    pub fn is_argless(&self) -> bool {
722        self.syntax.is_empty()
723    }
724
725    /// Returns true if this callable is a function (not a command).
726    pub fn is_function(&self) -> bool {
727        self.return_type.is_some()
728    }
729}
730
731/// A trait to define a callable that is executed by a `Machine`.
732///
733/// The callable themselves are immutable but they can reference mutable state.  Given that
734/// EndBASIC is not threaded, it is sufficient for those references to be behind a `RefCell`
735/// and/or an `Rc`.
736///
737/// Idiomatically, these objects need to provide a `new()` method that returns an `Rc<Callable>`, as
738/// that's the type used throughout the execution engine.
739#[async_trait(?Send)]
740pub trait Callable {
741    /// Returns the metadata for this function.
742    ///
743    /// The return value takes the form of a reference to force the callable to store the metadata
744    /// as a struct field so that calls to this function are guaranteed to be cheap.
745    fn metadata(&self) -> &CallableMetadata;
746
747    /// Executes the function.
748    ///
749    /// `args` contains the arguments to the function call.
750    ///
751    /// `machine` provides mutable access to the current state of the machine invoking the function.
752    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        // First initialize the array with sequential numbers and check that they are valid
850        // immediately after assignment.
851        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        // But then also iterate over them all to ensure none were overwritten.
865        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        // If modifying this test, update the identical test for get_auto() and get_mut().
999        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        // If modifying this test, update the identical test for get_auto() and get_mut().
1057        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        // If modifying this test, update the identical test for get_auto() and get_mut().
1080        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        // If modifying this test, update the identical test for get() and get_auto().
1087        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        // If modifying this test, update the identical test for get() and get_auto().
1148        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        // If modifying this test, update the identical test for get() and get_auto().
1171        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        // If modifying this test, update the identical test for get() and get_mut().
1181        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        // If modifying this test, update the identical test for get() and get_mut().
1212        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        // If modifying this test, update the identical test for get() and get_mut().
1235        let syms = SymbolsBuilder::default().add_var("SOMETHING", Value::Integer(3)).build();
1236        assert!(syms.get_auto("SOME_THIN").is_none());
1237    }
1238
1239    /// Checks that the variable `name` in `syms` has the value in `exp_value`.
1240    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}