Skip to main content

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::{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/// The key of a symbol in the symbols table.
31#[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/// Represents a multidimensional array.
47#[derive(Clone, Debug, PartialEq)]
48pub struct Array {
49    /// The type of all elements in the array.
50    subtype: ExprType,
51
52    /// The dimensions of the array.  At least one must be present.
53    dimensions: Vec<usize>,
54
55    /// The values in the array, flattened.  Given dimensions `(N, M, O)`, an element `(i, j, k)` is
56    /// at position `i * (M * O) + j * O + k`.
57    values: Vec<Value>,
58}
59
60impl Array {
61    /// Creates a new array of the given `subtype` and `dimensions`.
62    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    /// Returns the dimensions of the array.
78    pub fn dimensions(&self) -> &[usize] {
79        &self.dimensions
80    }
81
82    /// Returns the type of the elements in this array.
83    pub fn subtype(&self) -> ExprType {
84        self.subtype
85    }
86
87    /// Validates that the subscript `i` is in the `[0,max)` range and converts it to an `usize`.
88    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    /// Computes the index to access the flat `values` array given a list of `subscripts`.
99    ///
100    /// It is an error if `dimensions` and `subscripts` have different sizes, or if the values in
101    /// `subscripts` are negative.
102    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    /// Assings the `value` to the array position indicated by the `subscripts`.
123    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    /// Obtains the value contained in the array position indicated by the `subscripts`.
142    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
150/// Holds the definition of a symbol.
151pub enum Symbol {
152    /// An array definition.
153    Array(Array),
154
155    /// A callable definition.
156    Callable(Rc<dyn Callable>),
157
158    /// A variable definition.
159    Variable(Value),
160}
161
162impl Symbol {
163    /// Returns the type the symbol evaluates as.
164    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    /// Returns the metadata for the symbol, if any.
173    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    /// Returns whether the symbol was defined by the user or not.
182    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
201/// Storage for all symbols that exist at runtime.
202///
203/// Symbols are represented as a two-layer map: the globals map contains all symbols that are
204/// visible by all scopes, and the scope contains all symbols that are only visible within a
205/// given scope.
206///
207/// The collection of symbols that is visible at any given point in time is thus the union of
208/// the global symbols and the symbols in the last scope.
209///
210/// Scopes are represented as a stack in order to support nested function calls.
211pub struct Symbols {
212    /// Map of global symbol names to their definitions.
213    globals: HashMap<SymbolKey, Symbol>,
214
215    /// Map of local symbol names to their definitions.
216    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    /// Constructs a symbols object from a flat map of symbol names to their definitions.
227    #[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    /// Enters a new scope.
236    pub(crate) fn enter_scope(&mut self) {
237        self.scopes.push(HashMap::default());
238    }
239
240    /// Leaves the current scope.
241    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    /// Registers the given builtin callable as a global symbol.
248    ///
249    /// Given that callables cannot be defined at runtime, specifying a non-unique name results in
250    /// a panic.
251    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    /// Returns the mapping of all callables.
258    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    /// Returns the mapping of all symbols in the current scope that are not globals.
269    pub fn locals(&self) -> &HashMap<SymbolKey, Symbol> {
270        self.scopes.last().unwrap()
271    }
272
273    /// Clears all user-defined symbols.
274    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    /// Defines a new local variable `key` of type `etype`.  The variable must not yet exist.
285    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    /// Defines a new global variable `key` of type `etype`.  The variable must not yet exist.
294    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    /// Defines a new array `key` of type `subtype` with `dimensions`.  The array must not yet
303    /// exist, and the name may not overlap function or variable names.
304    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    /// Defines a new global array `key` of type `subtype` with `dimensions`.  The array must not yet
313    /// exist, and the name may not overlap function or variable names.
314    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    /// Obtains the value of a symbol or `None` if it is not defined.
323    ///
324    /// This is meant to use by the compiler only.  All other users should call `get` instead
325    /// to do the necessary runtime validity checks.
326    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    /// Obtains the value of a symbol or `None` if it is not defined.
335    ///
336    /// This is meant to use by the compiler only.  All other users should call `get` instead
337    /// to do the necessary runtime validity checks.
338    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    /// Obtains the value of a symbol or `None` if it is not defined.
347    ///
348    /// Returns an error if the type annotation in the symbol reference does not match its type.
349    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    /// Obtains the value of a symbol or `None` if it is not defined.
365    pub fn get_auto(&self, var: &str) -> Option<&Symbol> {
366        let key = SymbolKey::from(var);
367        self.load(&key)
368    }
369
370    /// Obtains the value of a symbol or `None` if it is not defined.
371    ///
372    /// Returns an error if the type annotation in the symbol reference does not match its type.
373    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    /// Obtains the value of a variable.
390    ///
391    /// Returns an error if the variable is not defined, if the referenced symbol is not a variable,
392    /// or if the type annotation in the variable reference does not match the type of the value
393    /// that the variable contains.
394    #[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    /// Sets the value of a variable without type promotion nor type checking.
404    ///
405    /// This is meant to use by the compiler only.  All other users should call `set_var` instead
406    /// to do the necessary runtime validity checks.
407    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    /// Sets the value of a variable.
430    ///
431    /// If `vref` contains a type annotation, the type of the value must be compatible with that
432    /// type annotation.
433    ///
434    /// If the variable is already defined, then the type of the new value must be compatible with
435    /// the existing variable.  In other words: a variable cannot change types while it's alive.
436    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    /// Unsets the symbol `key` irrespective of its type.
470    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
478/// Builder pattern for a callable's metadata.
479pub 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    /// Constructs a new metadata builder with the minimum information necessary.
489    ///
490    /// All code except tests must populate the whole builder with details.  This is enforced at
491    /// construction time, where we only allow some fields to be missing under the test
492    /// configuration.
493    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    /// Constructs a new metadata builder with the minimum information necessary.
506    ///
507    /// This is the same as `new` but using a dynamically-allocated name, which is necessary for
508    /// user-defined symbols.
509    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    /// Sets the return type of the callable.
520    pub fn with_return_type(mut self, return_type: ExprType) -> Self {
521        self.return_type = Some(return_type);
522        self
523    }
524
525    /// Sets the syntax specifications for this callable.
526    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    /// Sets the syntax specifications for this callable.
538    pub(crate) fn with_syntaxes<S: Into<Vec<CallableSyntax>>>(mut self, syntaxes: S) -> Self {
539        self.syntaxes = syntaxes.into();
540        self
541    }
542
543    /// Sets the syntax specifications for this callable.
544    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    /// Sets the category for this callable.  All callables with the same category name will be
556    /// grouped together in help messages.
557    pub fn with_category(mut self, category: &'static str) -> Self {
558        self.category = Some(category);
559        self
560    }
561
562    /// Sets the description for this callable.  The `description` is a collection of paragraphs
563    /// separated by a single newline character, where the first paragraph is taken as the summary
564    /// of the description.  The summary must be a short sentence that is descriptive enough to be
565    /// understood without further details.  Empty lines (paragraphs) are not allowed.
566    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    /// Generates the final `CallableMetadata` object, ensuring all values are present.
575    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    /// Generates the final `CallableMetadata` object, ensuring the minimal set of values are
587    /// present.  Only useful for testing.
588    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/// Representation of a callable's metadata.
603///
604/// The callable is expected to hold onto an instance of this object within its struct to make
605/// queries fast.
606#[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    /// Gets the callable's name, all in uppercase.
617    pub fn name(&self) -> &str {
618        &self.name
619    }
620
621    /// Gets the callable's return type.
622    pub fn return_type(&self) -> Option<ExprType> {
623        self.return_type
624    }
625
626    /// Gets the callable's syntax specification.
627    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    /// Returns the callable's syntax definitions.
648    pub(crate) fn syntaxes(&self) -> &[CallableSyntax] {
649        &self.syntaxes
650    }
651
652    /// Gets the callable's category as a collection of lines.  The first line is the title of the
653    /// category, and any extra lines are additional information for it.
654    pub fn category(&self) -> &'static str {
655        self.category
656    }
657
658    /// Gets the callable's textual description as a collection of lines.  The first line is the
659    /// summary of the callable's purpose.
660    pub fn description(&self) -> Lines<'static> {
661        self.description.lines()
662    }
663
664    /// Returns true if this is a callable that takes no arguments.
665    pub fn is_argless(&self) -> bool {
666        self.syntaxes.is_empty() || (self.syntaxes.len() == 1 && self.syntaxes[0].is_empty())
667    }
668
669    /// Returns true if this callable is a function (not a command).
670    pub fn is_function(&self) -> bool {
671        self.return_type.is_some()
672    }
673}
674
675/// A trait to define a callable that is executed by a `Machine`.
676///
677/// The callable themselves are immutable but they can reference mutable state.  Given that
678/// EndBASIC is not threaded, it is sufficient for those references to be behind a `RefCell`
679/// and/or an `Rc`.
680///
681/// Idiomatically, these objects need to provide a `new()` method that returns an `Rc<Callable>`, as
682/// that's the type used throughout the execution engine.
683#[async_trait(?Send)]
684pub trait Callable {
685    /// Returns the metadata for this function.
686    ///
687    /// The return value takes the form of a reference to force the callable to store the metadata
688    /// as a struct field so that calls to this function are guaranteed to be cheap.
689    fn metadata(&self) -> &CallableMetadata;
690
691    /// Executes the function.
692    ///
693    /// `args` contains the arguments to the function call.
694    ///
695    /// `machine` provides mutable access to the current state of the machine invoking the function.
696    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        // First initialize the array with sequential numbers and check that they are valid
794        // immediately after assignment.
795        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        // But then also iterate over them all to ensure none were overwritten.
809        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        // If modifying this test, update the identical test for get_auto() and get_mut().
943        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        // If modifying this test, update the identical test for get_auto() and get_mut().
1001        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        // If modifying this test, update the identical test for get_auto() and get_mut().
1024        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        // If modifying this test, update the identical test for get() and get_auto().
1031        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        // If modifying this test, update the identical test for get() and get_auto().
1092        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        // If modifying this test, update the identical test for get() and get_auto().
1115        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        // If modifying this test, update the identical test for get() and get_mut().
1124        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        // If modifying this test, update the identical test for get() and get_mut().
1155        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        // If modifying this test, update the identical test for get() and get_mut().
1178        let syms = SymbolsBuilder::default().add_var("SOMETHING", Value::Integer(3)).build();
1179        assert!(syms.get_auto("SOME_THIN").is_none());
1180    }
1181
1182    /// Checks that the variable `name` in `syms` has the value in `exp_value`.
1183    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}