endbasic_core/
ast.rs

1// EndBASIC
2// Copyright 2020 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//! Abstract Syntax Tree (AST) for the EndBASIC language.
17
18use crate::{reader::LineCol, syms::SymbolKey};
19use std::fmt;
20
21/// Components of a boolean literal expression.
22#[derive(Clone, Debug, Eq, PartialEq)]
23pub struct BooleanSpan {
24    /// The boolean literal.
25    pub value: bool,
26
27    /// Starting position of the literal.
28    pub pos: LineCol,
29}
30
31/// Components of a double literal expression.
32#[derive(Clone, Debug, PartialEq)]
33pub struct DoubleSpan {
34    /// The double literal.
35    pub value: f64,
36
37    /// Starting position of the literal.
38    pub pos: LineCol,
39}
40
41/// Components of an integer literal expression.
42#[derive(Clone, Debug, Eq, PartialEq)]
43pub struct IntegerSpan {
44    /// The integer literal.
45    pub value: i32,
46
47    /// Starting position of the literal.
48    pub pos: LineCol,
49}
50
51/// Components of a string literal expression.
52#[derive(Clone, Debug, Eq, PartialEq)]
53pub struct TextSpan {
54    /// The string literal.
55    pub value: String,
56
57    /// Starting position of the literal.
58    pub pos: LineCol,
59}
60
61/// Components of a symbol reference expression.
62#[derive(Clone, Debug, Eq, PartialEq)]
63pub struct SymbolSpan {
64    /// The symbol reference.
65    pub vref: VarRef,
66
67    /// Starting position of the symbol reference.
68    pub pos: LineCol,
69}
70
71/// Components of a unary operation expression.
72#[derive(Clone, Debug, PartialEq)]
73pub struct UnaryOpSpan {
74    /// Expression affected by the operator.
75    pub expr: Expr,
76
77    /// Starting position of the operator.
78    pub pos: LineCol,
79}
80
81/// Components of a binary operation expression.
82#[derive(Clone, Debug, PartialEq)]
83pub struct BinaryOpSpan {
84    /// Expression on the left side of the operator.
85    pub lhs: Expr,
86
87    /// Expression on the right side of the operator.
88    pub rhs: Expr,
89
90    /// Starting position of the operator.
91    pub pos: LineCol,
92}
93
94/// Represents an expression and provides mechanisms to evaluate it.
95#[derive(Clone, Debug, PartialEq)]
96pub enum Expr {
97    /// A literal boolean value.
98    Boolean(BooleanSpan),
99    /// A literal double-precision floating point value.
100    Double(DoubleSpan),
101    /// A literal integer value.
102    Integer(IntegerSpan),
103    /// A literal string value.
104    Text(TextSpan),
105
106    /// A reference to a variable.
107    Symbol(SymbolSpan),
108
109    /// Arithmetic addition of two expressions.
110    Add(Box<BinaryOpSpan>),
111    /// Arithmetic subtraction of two expressions.
112    Subtract(Box<BinaryOpSpan>),
113    /// Arithmetic multiplication of two expressions.
114    Multiply(Box<BinaryOpSpan>),
115    /// Arithmetic division of two expressions.
116    Divide(Box<BinaryOpSpan>),
117    /// Arithmetic modulo operation of two expressions.
118    Modulo(Box<BinaryOpSpan>),
119    /// Arithmetic power operation of two expressions.
120    Power(Box<BinaryOpSpan>),
121    /// Arithmetic sign flip of an expression.
122    Negate(Box<UnaryOpSpan>),
123
124    /// Relational equality comparison of two expressions.
125    Equal(Box<BinaryOpSpan>),
126    /// Relational inequality comparison of two expressions.
127    NotEqual(Box<BinaryOpSpan>),
128    /// Relational less-than comparison of two expressions.
129    Less(Box<BinaryOpSpan>),
130    /// Relational less-than or equal-to comparison of two expressions.
131    LessEqual(Box<BinaryOpSpan>),
132    /// Relational greater-than comparison of two expressions.
133    Greater(Box<BinaryOpSpan>),
134    /// Relational greater-than or equal-to comparison of two expressions.
135    GreaterEqual(Box<BinaryOpSpan>),
136
137    /// Logical and of two expressions.
138    And(Box<BinaryOpSpan>),
139    /// Logical not of an expression.
140    Not(Box<UnaryOpSpan>),
141    /// Logical or of two expressions.
142    Or(Box<BinaryOpSpan>),
143    /// Logical xor of two expressions.
144    Xor(Box<BinaryOpSpan>),
145
146    /// Shift left of a signed integer by a number of bits without rotation.
147    ShiftLeft(Box<BinaryOpSpan>),
148    /// Shift right of a signed integer by a number of bits without rotation.
149    ShiftRight(Box<BinaryOpSpan>),
150
151    /// A function call or an array reference.
152    Call(CallSpan),
153}
154
155impl Expr {
156    /// Returns the start position of the expression.
157    pub fn start_pos(&self) -> LineCol {
158        match self {
159            Expr::Boolean(span) => span.pos,
160            Expr::Double(span) => span.pos,
161            Expr::Integer(span) => span.pos,
162            Expr::Text(span) => span.pos,
163
164            Expr::Symbol(span) => span.pos,
165
166            Expr::And(span) => span.lhs.start_pos(),
167            Expr::Or(span) => span.lhs.start_pos(),
168            Expr::Xor(span) => span.lhs.start_pos(),
169            Expr::Not(span) => span.pos,
170
171            Expr::ShiftLeft(span) => span.lhs.start_pos(),
172            Expr::ShiftRight(span) => span.lhs.start_pos(),
173
174            Expr::Equal(span) => span.lhs.start_pos(),
175            Expr::NotEqual(span) => span.lhs.start_pos(),
176            Expr::Less(span) => span.lhs.start_pos(),
177            Expr::LessEqual(span) => span.lhs.start_pos(),
178            Expr::Greater(span) => span.lhs.start_pos(),
179            Expr::GreaterEqual(span) => span.lhs.start_pos(),
180
181            Expr::Add(span) => span.lhs.start_pos(),
182            Expr::Subtract(span) => span.lhs.start_pos(),
183            Expr::Multiply(span) => span.lhs.start_pos(),
184            Expr::Divide(span) => span.lhs.start_pos(),
185            Expr::Modulo(span) => span.lhs.start_pos(),
186            Expr::Power(span) => span.lhs.start_pos(),
187            Expr::Negate(span) => span.pos,
188
189            Expr::Call(span) => span.vref_pos,
190        }
191    }
192}
193
194/// Represents type of an expression.
195#[derive(Clone, Copy, Debug, Eq, PartialEq)]
196pub enum ExprType {
197    /// Type for an expression that evaluates to a boolean.
198    Boolean,
199
200    /// Type for an expression that evaluates to a double.
201    Double,
202
203    /// Type for an expression that evaluates to an integer.
204    Integer,
205
206    /// Type for an expression that evaluates to a string.
207    Text,
208}
209
210impl ExprType {
211    /// Returns true if this expression type is numerical.
212    pub(crate) fn is_numerical(self) -> bool {
213        self == Self::Double || self == Self::Integer
214    }
215
216    /// Returns the textual representation of the annotation for this type.
217    pub fn annotation(&self) -> char {
218        match self {
219            ExprType::Boolean => '?',
220            ExprType::Double => '#',
221            ExprType::Integer => '%',
222            ExprType::Text => '$',
223        }
224    }
225
226    /// Returns the default value to assign to this type.
227    pub(crate) fn default_value(&self) -> Value {
228        match self {
229            ExprType::Boolean => Value::Boolean(false),
230            ExprType::Double => Value::Double(0.0),
231            ExprType::Integer => Value::Integer(0),
232            ExprType::Text => Value::Text("".to_owned()),
233        }
234    }
235}
236
237impl fmt::Display for ExprType {
238    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239        match self {
240            ExprType::Boolean => write!(f, "BOOLEAN"),
241            ExprType::Double => write!(f, "DOUBLE"),
242            ExprType::Integer => write!(f, "INTEGER"),
243            ExprType::Text => write!(f, "STRING"),
244        }
245    }
246}
247
248/// Represents a reference to a variable (which doesn't have to exist).
249///
250/// Variable references are different from `SymbolKey`s because they maintain the case of the
251/// reference (for error display purposes) and because they carry an optional type annotation.
252#[derive(Clone, Debug, Eq, PartialEq)]
253pub struct VarRef {
254    /// Name of the variable this points to.
255    name: String,
256
257    /// Type of the variable this points to, if explicitly specified.
258    ///
259    /// If `None`, the type of the variable is subject to type inference.
260    ref_type: Option<ExprType>,
261}
262
263impl VarRef {
264    /// Creates a new reference to the variable with `name` and the optional `ref_type` type.
265    pub fn new<T: Into<String>>(name: T, ref_type: Option<ExprType>) -> Self {
266        Self { name: name.into(), ref_type }
267    }
268
269    /// Returns the name of this reference, without any type annotations.
270    pub fn name(&self) -> &str {
271        &self.name
272    }
273
274    /// Returns the name of this reference, without any type annotations, and consumes the
275    /// reference.
276    pub(crate) fn take_name(self) -> String {
277        self.name
278    }
279
280    /// Returns the type of this reference.
281    pub fn ref_type(&self) -> Option<ExprType> {
282        self.ref_type
283    }
284
285    /// Returns true if this reference is compatible with the given type.
286    pub fn accepts(&self, other: ExprType) -> bool {
287        match self.ref_type {
288            None => true,
289            Some(vtype) => vtype == other,
290        }
291    }
292
293    /// Returns true if this reference is compatible with the return type of a callable.
294    pub fn accepts_callable(&self, other: Option<ExprType>) -> bool {
295        match self.ref_type {
296            None => true,
297            Some(vtype) => match other {
298                Some(other) => vtype == other,
299                None => false,
300            },
301        }
302    }
303
304    /// Converts this variable reference to a symbol key.
305    pub(crate) fn as_symbol_key(&self) -> SymbolKey {
306        SymbolKey::from(&self.name)
307    }
308}
309
310impl fmt::Display for VarRef {
311    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
312        match self.ref_type {
313            None => self.name.fmt(f),
314            Some(vtype) => write!(f, "{}{}", self.name, vtype.annotation()),
315        }
316    }
317}
318
319/// Represents an evaluated value.
320#[derive(Clone, Debug, PartialEq)]
321pub enum Value {
322    /// A boolean value.
323    Boolean(bool),
324
325    /// A double-precision floating point value.
326    Double(f64),
327
328    /// An integer value.
329    Integer(i32),
330
331    /// A string value.
332    Text(String), // Should be `String` but would get confusing with the built-in Rust type.
333
334    /// A reference to a variable.
335    VarRef(SymbolKey, ExprType),
336}
337
338impl From<bool> for Value {
339    fn from(b: bool) -> Self {
340        Value::Boolean(b)
341    }
342}
343
344impl From<f64> for Value {
345    fn from(d: f64) -> Self {
346        Value::Double(d)
347    }
348}
349
350impl From<i32> for Value {
351    fn from(i: i32) -> Self {
352        Value::Integer(i)
353    }
354}
355
356impl From<&str> for Value {
357    fn from(s: &str) -> Self {
358        Value::Text(s.to_owned())
359    }
360}
361
362impl fmt::Display for Value {
363    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
364        match self {
365            Value::Boolean(true) => write!(f, "TRUE"),
366            Value::Boolean(false) => write!(f, "FALSE"),
367            Value::Double(d) => {
368                let mut s = format!("{}", d);
369                if d.is_finite() && !d.is_nan() && !s.contains('.') {
370                    s += ".0";
371                }
372                write!(f, "{}", s)
373            }
374            Value::Integer(i) => write!(f, "{}", i),
375            Value::Text(s) => write!(f, "\"{}\"", s),
376            Value::VarRef(key, etype) => write!(f, "{}{}", key, etype),
377        }
378    }
379}
380
381impl Value {
382    /// Returns the type of the value as an `ExprType`.
383    pub fn as_exprtype(&self) -> ExprType {
384        match self {
385            Value::Boolean(_) => ExprType::Boolean,
386            Value::Double(_) => ExprType::Double,
387            Value::Integer(_) => ExprType::Integer,
388            Value::Text(_) => ExprType::Text,
389            Value::VarRef(_key, etype) => *etype,
390        }
391    }
392}
393
394/// Types of separators between arguments to a `BuiltinCall`.
395#[derive(Clone, Copy, Debug, Eq, PartialEq)]
396pub enum ArgSep {
397    /// Filler for the separator in the last argument.
398    End = 0,
399
400    /// Short separator (`;`).
401    Short = 1,
402
403    /// Long separator (`,`).
404    Long = 2,
405
406    /// `AS` separator.
407    As = 3,
408}
409
410impl fmt::Display for ArgSep {
411    // TODO(jmmv): Can this be removed in favor of describe()?
412    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
413        match self {
414            ArgSep::End => write!(f, "<END OF STATEMENT>"),
415            ArgSep::Short => write!(f, ";"),
416            ArgSep::Long => write!(f, ","),
417            ArgSep::As => write!(f, "AS"),
418        }
419    }
420}
421
422impl ArgSep {
423    /// Formats the separator for a syntax specification.
424    ///
425    /// The return value contains the textual representation of the separator and a boolean that
426    /// indicates whether the separator requires a leading space.
427    pub(crate) fn describe(&self) -> (&str, bool) {
428        match self {
429            ArgSep::End => ("", false),
430            ArgSep::Short => (";", false),
431            ArgSep::Long => (",", false),
432            ArgSep::As => ("AS", true),
433        }
434    }
435}
436
437/// Components of an array assignment statement.
438#[derive(Debug, PartialEq)]
439#[cfg_attr(test, derive(Clone))]
440pub struct ArrayAssignmentSpan {
441    /// Reference to the array to modify.
442    pub vref: VarRef,
443
444    /// Position of the `vref`.
445    pub vref_pos: LineCol,
446
447    /// Expressions to compute the subscripts to index the array.
448    pub subscripts: Vec<Expr>,
449
450    /// Expression to compute the value of the modified element.
451    pub expr: Expr,
452}
453
454/// Components of an assignment statement.
455#[derive(Debug, PartialEq)]
456#[cfg_attr(test, derive(Clone))]
457pub struct AssignmentSpan {
458    /// Reference to the variable to set.
459    pub vref: VarRef,
460
461    /// Position of the `vref`.
462    pub vref_pos: LineCol,
463
464    /// Expression to compute the value of the modified variable.
465    pub expr: Expr,
466}
467
468/// Single argument to a builtin call statement.
469#[derive(Clone, Debug, PartialEq)]
470pub struct ArgSpan {
471    /// Expression to compute the argument's value.  This expression is optional to support calls
472    /// of the form `PRINT a, , b` where some arguments are empty.
473    pub expr: Option<Expr>,
474
475    /// Separator between this argument and the *next*.  The last instance of this type in a call
476    /// always carries a value of `ArgSep::End`.
477    pub sep: ArgSep,
478
479    /// Position of the `sep`.
480    pub sep_pos: LineCol,
481}
482
483/// Components of a call statement or expression.
484#[derive(Clone, Debug, PartialEq)]
485pub struct CallSpan {
486    /// Reference to the callable (a command or a function), or the array to index.
487    pub vref: VarRef,
488
489    /// Position of the reference.
490    pub vref_pos: LineCol,
491
492    /// Sequence of arguments to pass to the callable.
493    pub args: Vec<ArgSpan>,
494}
495
496/// Components of a `FUNCTION` or `SUB` definition.
497#[derive(Debug, PartialEq)]
498pub struct CallableSpan {
499    /// Name of the callable, expressed as a variable reference.  For functions, this contains
500    /// a type, and for subroutines, it does not.
501    pub name: VarRef,
502
503    /// Position of the name of the callable.
504    pub name_pos: LineCol,
505
506    /// Definition of the callable parameters.
507    pub params: Vec<VarRef>,
508
509    /// Statements within the callable's body.
510    pub body: Vec<Statement>,
511
512    /// Position of the end of the callable, used when injecting the implicit return.
513    pub end_pos: LineCol,
514}
515
516/// Components of a data statement.
517#[derive(Debug, PartialEq)]
518pub struct DataSpan {
519    /// Collection of optional literal values.
520    pub values: Vec<Option<Value>>,
521}
522
523/// Components of a variable definition.
524///
525/// Given that a definition causes the variable to be initialized to a default value, it is
526/// tempting to model this statement as a simple assignment.  However, we must be able to
527/// detect variable redeclarations at runtime, so we must treat this statement as a separate
528/// type from assignments.
529#[derive(Debug, Eq, PartialEq)]
530#[cfg_attr(test, derive(Clone))]
531pub struct DimSpan {
532    /// Name of the variable to be defined.  Type annotations are not allowed, hence why this is
533    /// not a `VarRef`.
534    pub name: String,
535
536    /// Position of the name.
537    pub name_pos: LineCol,
538
539    /// Whether the variable is global or not.
540    pub shared: bool,
541
542    /// Type of the variable to be defined.
543    pub vtype: ExprType,
544
545    /// Position of the type.
546    pub vtype_pos: LineCol,
547}
548
549/// Components of an array definition.
550#[derive(Debug, PartialEq)]
551#[cfg_attr(test, derive(Clone))]
552pub struct DimArraySpan {
553    /// Name of the array to define.  Type annotations are not allowed, hence why this is not a
554    /// `VarRef`.
555    pub name: String,
556
557    /// Position of the name.
558    pub name_pos: LineCol,
559
560    /// Whether the array is global or not.
561    pub shared: bool,
562
563    /// Expressions to compute the dimensions of the array.
564    pub dimensions: Vec<Expr>,
565
566    /// Type of the array to be defined.
567    pub subtype: ExprType,
568
569    /// Position of the subtype.
570    pub subtype_pos: LineCol,
571}
572
573/// Type of the `DO` loop.
574#[derive(Debug, PartialEq)]
575pub enum DoGuard {
576    /// Represents an infinite loop without guards.
577    Infinite,
578
579    /// Represents a loop with an `UNTIL` guard in the `DO` clause.
580    PreUntil(Expr),
581
582    /// Represents a loop with a `WHILE` guard in the `DO` clause.
583    PreWhile(Expr),
584
585    /// Represents a loop with an `UNTIL` guard in the `LOOP` clause.
586    PostUntil(Expr),
587
588    /// Represents a loop with a `WHILE` guard in the `LOOP` clause.
589    PostWhile(Expr),
590}
591
592/// Components of a `DO` statement.
593#[derive(Debug, PartialEq)]
594pub struct DoSpan {
595    /// Expression to compute whether to execute the loop's body or not and where this appears in
596    /// the `DO` statement.
597    pub guard: DoGuard,
598
599    /// Statements within the loop's body.
600    pub body: Vec<Statement>,
601}
602
603/// Components of an `END` statement.
604#[derive(Debug, PartialEq)]
605pub struct EndSpan {
606    /// Integer expression to compute the return code.
607    pub code: Option<Expr>,
608}
609
610/// Components of an `EXIT DO` statement.
611#[derive(Debug, Eq, PartialEq)]
612pub struct ExitDoSpan {
613    /// Position of the statement.
614    pub pos: LineCol,
615}
616
617/// Components of a branch of an `IF` statement.
618#[derive(Debug, PartialEq)]
619pub struct IfBranchSpan {
620    /// Expression that guards execution of this branch.
621    pub guard: Expr,
622
623    /// Statements within the branch.
624    pub body: Vec<Statement>,
625}
626
627/// Components of an `IF` statement.
628#[derive(Debug, PartialEq)]
629pub struct IfSpan {
630    /// Sequence of the branches in the conditional.
631    ///
632    /// Representation of the conditional branches.  The final `ELSE` branch, if present, is also
633    /// included here and its guard clause is always a true expression.
634    pub branches: Vec<IfBranchSpan>,
635}
636
637/// Components of a `FOR` statement.
638///
639/// Note that we do not store the original end and step values, and instead use expressions to
640/// represent the loop condition and the computation of the next iterator value.  We do this
641/// for run-time efficiency.  The reason this is possible is because we force the step to be an
642/// integer literal at parse time and do not allow it to be an expression.
643#[derive(Debug, PartialEq)]
644pub struct ForSpan {
645    /// Iterator name, expressed as a variable reference that must be either automatic or an
646    /// integer.
647    pub iter: VarRef,
648
649    /// Position of the iterator.
650    pub iter_pos: LineCol,
651
652    /// If true, the iterator computation needs to be performed as a double so that, when the
653    /// iterator variable is not yet defined, it gains the correct type.
654    pub iter_double: bool,
655
656    /// Expression to compute the iterator's initial value.
657    pub start: Expr,
658
659    /// Condition to test after each iteration.
660    pub end: Expr,
661
662    /// Expression to compute the iterator's next value.
663    pub next: Expr,
664
665    /// Statements within the loop's body.
666    pub body: Vec<Statement>,
667}
668
669/// Components of a `GOTO` or a `GOSUB` statement.
670#[derive(Clone, Debug, Eq, PartialEq)]
671pub struct GotoSpan {
672    /// Name of the label to jump to.
673    pub target: String,
674
675    /// Position of the label.
676    pub target_pos: LineCol,
677}
678
679/// Components of a label "statement".
680///
681/// In principle, labels should be just a property of a statement but, for simplicity in the
682/// current model, it's easiest to represent them as their own statement.
683#[derive(Debug, Eq, PartialEq)]
684pub struct LabelSpan {
685    /// Name of the label being defined.
686    pub name: String,
687
688    /// Position of the label.
689    pub name_pos: LineCol,
690}
691
692/// Components of an `ON ERROR` statement.
693#[derive(Debug, Eq, PartialEq)]
694pub enum OnErrorSpan {
695    /// Components of an `ON ERROR GOTO @label` statement.
696    Goto(GotoSpan),
697
698    /// Components of an `ON ERROR GOTO 0` statement.
699    Reset,
700
701    /// Components of an `ON ERROR RESUME NEXT` statement.
702    ResumeNext,
703}
704
705/// Components of a `RETURN` statement.
706#[derive(Debug, Eq, PartialEq)]
707pub struct ReturnSpan {
708    /// Position of the statement.
709    pub pos: LineCol,
710}
711
712/// Collection of relational operators that can appear in a `CASE IS` guard..
713#[derive(Debug, Eq, PartialEq)]
714pub enum CaseRelOp {
715    /// Relational operator for `CASE IS =`.
716    Equal,
717
718    /// Relational operator for `CASE IS <>`.
719    NotEqual,
720
721    /// Relational operator for `CASE IS <`.
722    Less,
723
724    /// Relational operator for `CASE IS <=`.
725    LessEqual,
726
727    /// Relational operator for `CASE IS >`.
728    Greater,
729
730    /// Relational operator for `CASE IS >=`.
731    GreaterEqual,
732}
733
734/// Components of a `CASE` guard.
735#[derive(Debug, PartialEq)]
736pub enum CaseGuardSpan {
737    /// Represents an `IS <op> <expr>` guard or a simpler `<expr>` guard.
738    Is(CaseRelOp, Expr),
739
740    /// Represents an `<expr> TO <expr>` guard.
741    To(Expr, Expr),
742}
743
744/// Components of a branch of a `SELECT` statement.
745#[derive(Debug, PartialEq)]
746pub struct CaseSpan {
747    /// Expressions that guard execution of this case.
748    pub guards: Vec<CaseGuardSpan>,
749
750    /// Statements within the case block.
751    pub body: Vec<Statement>,
752}
753
754/// Components of a `SELECT` statement.
755#[derive(Debug, PartialEq)]
756pub struct SelectSpan {
757    /// Expression to test for.
758    pub expr: Expr,
759
760    /// Representation of the cases to select from.  The final `CASE ELSE`, if present, is also
761    /// included here without any guards.
762    pub cases: Vec<CaseSpan>,
763
764    /// Position of the `END SELECT` statement.
765    pub end_pos: LineCol,
766}
767
768/// Components of a `WHILE` statement.
769#[derive(Debug, PartialEq)]
770pub struct WhileSpan {
771    /// Expression to compute whether to execute the loop's body or not.
772    pub expr: Expr,
773
774    /// Statements within the loop's body.
775    pub body: Vec<Statement>,
776}
777
778/// Represents a statement in the program along all data to execute it.
779#[derive(Debug, PartialEq)]
780pub enum Statement {
781    /// Represents an assignment to an element of an array.
782    ArrayAssignment(ArrayAssignmentSpan),
783
784    /// Represents a variable assignment.
785    Assignment(AssignmentSpan),
786
787    /// Represents a call to a builtin command such as `PRINT`.
788    Call(CallSpan),
789
790    /// Represents a `FUNCTION` or `SUB` definition.  The difference between the two lies in just
791    /// the presence or absence of a return type in the callable.
792    Callable(CallableSpan),
793
794    /// Represents a `DATA` statement.
795    Data(DataSpan),
796
797    /// Represents a variable definition.
798    Dim(DimSpan),
799
800    /// Represents an array definition.
801    DimArray(DimArraySpan),
802
803    /// Represents a `DO` statement.
804    Do(DoSpan),
805
806    /// Represents an `END` statement.
807    End(EndSpan),
808
809    /// Represents an `EXIT DO` statement.
810    ExitDo(ExitDoSpan),
811
812    /// Represents a `FOR` statement.
813    For(ForSpan),
814
815    /// Represents a `GOSUB` statement.
816    Gosub(GotoSpan),
817
818    /// Represents a `GOTO` statement.
819    Goto(GotoSpan),
820
821    /// Represents an `IF` statement.
822    If(IfSpan),
823
824    /// Represents a label "statement".
825    Label(LabelSpan),
826
827    /// Represents an `ON ERROR` statement.
828    OnError(OnErrorSpan),
829
830    /// Represents a `RETURN` statement.
831    Return(ReturnSpan),
832
833    /// Represents a `SELECT` statement.
834    Select(SelectSpan),
835
836    /// Represents a `WHILE` statement.
837    While(WhileSpan),
838}
839
840#[cfg(test)]
841mod tests {
842    use super::*;
843
844    #[test]
845    fn test_varref_display() {
846        assert_eq!("name", format!("{}", VarRef::new("name", None)));
847        assert_eq!("abc?", format!("{}", VarRef::new("abc", Some(ExprType::Boolean))));
848        assert_eq!("cba#", format!("{}", VarRef::new("cba", Some(ExprType::Double))));
849        assert_eq!("def%", format!("{}", VarRef::new("def", Some(ExprType::Integer))));
850        assert_eq!("ghi$", format!("{}", VarRef::new("ghi", Some(ExprType::Text))));
851    }
852
853    #[test]
854    fn test_varref_accepts() {
855        assert!(VarRef::new("a", None).accepts(ExprType::Boolean));
856        assert!(VarRef::new("a", None).accepts(ExprType::Double));
857        assert!(VarRef::new("a", None).accepts(ExprType::Integer));
858        assert!(VarRef::new("a", None).accepts(ExprType::Text));
859
860        assert!(VarRef::new("a", Some(ExprType::Boolean)).accepts(ExprType::Boolean));
861        assert!(!VarRef::new("a", Some(ExprType::Boolean)).accepts(ExprType::Double));
862        assert!(!VarRef::new("a", Some(ExprType::Boolean)).accepts(ExprType::Integer));
863        assert!(!VarRef::new("a", Some(ExprType::Boolean)).accepts(ExprType::Text));
864
865        assert!(!VarRef::new("a", Some(ExprType::Double)).accepts(ExprType::Boolean));
866        assert!(VarRef::new("a", Some(ExprType::Double)).accepts(ExprType::Double));
867        assert!(!VarRef::new("a", Some(ExprType::Double)).accepts(ExprType::Integer));
868        assert!(!VarRef::new("a", Some(ExprType::Double)).accepts(ExprType::Text));
869
870        assert!(!VarRef::new("a", Some(ExprType::Integer)).accepts(ExprType::Boolean));
871        assert!(!VarRef::new("a", Some(ExprType::Integer)).accepts(ExprType::Double));
872        assert!(VarRef::new("a", Some(ExprType::Integer)).accepts(ExprType::Integer));
873        assert!(!VarRef::new("a", Some(ExprType::Integer)).accepts(ExprType::Text));
874
875        assert!(!VarRef::new("a", Some(ExprType::Text)).accepts(ExprType::Boolean));
876        assert!(!VarRef::new("a", Some(ExprType::Text)).accepts(ExprType::Double));
877        assert!(!VarRef::new("a", Some(ExprType::Text)).accepts(ExprType::Integer));
878        assert!(VarRef::new("a", Some(ExprType::Text)).accepts(ExprType::Text));
879    }
880
881    #[test]
882    fn test_value_display() {
883        assert_eq!("TRUE", format!("{}", Value::Boolean(true)));
884        assert_eq!("FALSE", format!("{}", Value::Boolean(false)));
885        assert_eq!("3.0", format!("{}", Value::Double(3.0)));
886        assert_eq!("3.1", format!("{}", Value::Double(3.1)));
887        assert_eq!("0.51", format!("{}", Value::Double(0.51)));
888        assert_eq!("inf", format!("{}", Value::Double(f64::INFINITY)));
889        assert_eq!("-inf", format!("{}", Value::Double(f64::NEG_INFINITY)));
890        assert_eq!("NaN", format!("{}", Value::Double(f64::NAN)));
891        assert_eq!("NaN", format!("{}", Value::Double(-f64::NAN)));
892        assert_eq!("-56", format!("{}", Value::Integer(-56)));
893        assert_eq!("\"some words\"", format!("{}", Value::Text("some words".to_owned())));
894    }
895}