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