endbasic_core/
parser.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//! Statement and expression parser for the EndBASIC language.
17
18use crate::ast::*;
19use crate::lexer::{Lexer, PeekableLexer, Token, TokenSpan};
20use crate::reader::LineCol;
21use std::cmp::Ordering;
22use std::io;
23
24/// Parser errors.
25#[derive(Debug, thiserror::Error)]
26pub enum Error {
27    /// Bad syntax in the input program.
28    #[error("{}:{}: {}", .0.line, .0.col, .1)]
29    Bad(LineCol, String),
30
31    /// I/O error while parsing the input program.
32    #[error("read error")]
33    Io(#[from] io::Error),
34}
35
36/// Result for parser return values.
37pub type Result<T> = std::result::Result<T, Error>;
38
39/// Transforms a `VarRef` into an unannotated name.
40///
41/// This is only valid for references that have no annotations in them.
42fn vref_to_unannotated_string(vref: VarRef, pos: LineCol) -> Result<String> {
43    if vref.ref_type().is_some() {
44        return Err(Error::Bad(pos, format!("Type annotation not allowed in {}", vref)));
45    }
46    Ok(vref.take_name())
47}
48
49/// Converts a collection of `ArgSpan`s passed to a function or array reference to a collection
50/// of expressions with proper validation.
51pub(crate) fn argspans_to_exprs(spans: Vec<ArgSpan>) -> Vec<Expr> {
52    let nargs = spans.len();
53    let mut exprs = Vec::with_capacity(spans.len());
54    for (i, span) in spans.into_iter().enumerate() {
55        debug_assert!(
56            (span.sep == ArgSep::End || i < nargs - 1)
57                || (span.sep != ArgSep::End || i == nargs - 1)
58        );
59        match span.expr {
60            Some(expr) => exprs.push(expr),
61            None => unreachable!(),
62        }
63    }
64    exprs
65}
66
67/// Operators that can appear within an expression.
68///
69/// The main difference between this and `lexer::Token` is that, in here, we differentiate the
70/// meaning of a minus sign and separate it in its two variants: the 2-operand `Minus` and the
71/// 1-operand `Negate`.
72///
73/// That said, this type also is the right place to abstract away operator-related logic to
74/// implement the expression parsing algorithm, so it's not completely useless.
75#[derive(Debug, Eq, PartialEq)]
76enum ExprOp {
77    LeftParen,
78
79    Add,
80    Subtract,
81    Multiply,
82    Divide,
83    Modulo,
84    Power,
85    Negate,
86
87    Equal,
88    NotEqual,
89    Less,
90    LessEqual,
91    Greater,
92    GreaterEqual,
93
94    And,
95    Not,
96    Or,
97    Xor,
98
99    ShiftLeft,
100    ShiftRight,
101}
102
103impl ExprOp {
104    /// Constructs a new operator based on a token, which must have a valid correspondence.
105    fn from(t: Token) -> Self {
106        match t {
107            Token::Equal => ExprOp::Equal,
108            Token::NotEqual => ExprOp::NotEqual,
109            Token::Less => ExprOp::Less,
110            Token::LessEqual => ExprOp::LessEqual,
111            Token::Greater => ExprOp::Greater,
112            Token::GreaterEqual => ExprOp::GreaterEqual,
113            Token::Plus => ExprOp::Add,
114            Token::Multiply => ExprOp::Multiply,
115            Token::Divide => ExprOp::Divide,
116            Token::Modulo => ExprOp::Modulo,
117            Token::Exponent => ExprOp::Power,
118            Token::And => ExprOp::And,
119            Token::Or => ExprOp::Or,
120            Token::Xor => ExprOp::Xor,
121            Token::ShiftLeft => ExprOp::ShiftLeft,
122            Token::ShiftRight => ExprOp::ShiftRight,
123            Token::Minus => panic!("Ambiguous token; cannot derive ExprOp"),
124            _ => panic!("Called on an non-operator"),
125        }
126    }
127
128    /// Returns the priority of this operator.  The specific number's meaning is only valid when
129    /// comparing it against other calls to this function.  Higher number imply higher priority.
130    fn priority(&self) -> i8 {
131        match self {
132            ExprOp::LeftParen => 6,
133            ExprOp::Power => 6,
134
135            ExprOp::Negate => 5,
136            ExprOp::Not => 5,
137
138            ExprOp::Multiply => 4,
139            ExprOp::Divide => 4,
140            ExprOp::Modulo => 4,
141
142            ExprOp::Add => 3,
143            ExprOp::Subtract => 3,
144
145            ExprOp::ShiftLeft => 2,
146            ExprOp::ShiftRight => 2,
147
148            ExprOp::Equal => 1,
149            ExprOp::NotEqual => 1,
150            ExprOp::Less => 1,
151            ExprOp::LessEqual => 1,
152            ExprOp::Greater => 1,
153            ExprOp::GreaterEqual => 1,
154
155            ExprOp::And => 0,
156            ExprOp::Or => 0,
157            ExprOp::Xor => 0,
158        }
159    }
160}
161
162/// Wrapper over an `ExprOp` to extend it with its position.
163struct ExprOpSpan {
164    /// The wrapped expression operation.
165    op: ExprOp,
166
167    /// The position where the operation appears in the input.
168    pos: LineCol,
169}
170
171impl ExprOpSpan {
172    /// Creates a new span from its parts.
173    fn new(op: ExprOp, pos: LineCol) -> Self {
174        Self { op, pos }
175    }
176
177    /// Pops operands from the `expr` stack, applies this operation, and pushes the result back.
178    fn apply(&self, exprs: &mut Vec<Expr>) -> Result<()> {
179        fn apply1(
180            exprs: &mut Vec<Expr>,
181            pos: LineCol,
182            f: fn(Box<UnaryOpSpan>) -> Expr,
183        ) -> Result<()> {
184            if exprs.is_empty() {
185                return Err(Error::Bad(pos, "Not enough values to apply operator".to_owned()));
186            }
187            let expr = exprs.pop().unwrap();
188            exprs.push(f(Box::from(UnaryOpSpan { expr, pos })));
189            Ok(())
190        }
191
192        fn apply2(
193            exprs: &mut Vec<Expr>,
194            pos: LineCol,
195            f: fn(Box<BinaryOpSpan>) -> Expr,
196        ) -> Result<()> {
197            if exprs.len() < 2 {
198                return Err(Error::Bad(pos, "Not enough values to apply operator".to_owned()));
199            }
200            let rhs = exprs.pop().unwrap();
201            let lhs = exprs.pop().unwrap();
202            exprs.push(f(Box::from(BinaryOpSpan { lhs, rhs, pos })));
203            Ok(())
204        }
205
206        match self.op {
207            ExprOp::Add => apply2(exprs, self.pos, Expr::Add),
208            ExprOp::Subtract => apply2(exprs, self.pos, Expr::Subtract),
209            ExprOp::Multiply => apply2(exprs, self.pos, Expr::Multiply),
210            ExprOp::Divide => apply2(exprs, self.pos, Expr::Divide),
211            ExprOp::Modulo => apply2(exprs, self.pos, Expr::Modulo),
212            ExprOp::Power => apply2(exprs, self.pos, Expr::Power),
213
214            ExprOp::Equal => apply2(exprs, self.pos, Expr::Equal),
215            ExprOp::NotEqual => apply2(exprs, self.pos, Expr::NotEqual),
216            ExprOp::Less => apply2(exprs, self.pos, Expr::Less),
217            ExprOp::LessEqual => apply2(exprs, self.pos, Expr::LessEqual),
218            ExprOp::Greater => apply2(exprs, self.pos, Expr::Greater),
219            ExprOp::GreaterEqual => apply2(exprs, self.pos, Expr::GreaterEqual),
220
221            ExprOp::And => apply2(exprs, self.pos, Expr::And),
222            ExprOp::Or => apply2(exprs, self.pos, Expr::Or),
223            ExprOp::Xor => apply2(exprs, self.pos, Expr::Xor),
224
225            ExprOp::ShiftLeft => apply2(exprs, self.pos, Expr::ShiftLeft),
226            ExprOp::ShiftRight => apply2(exprs, self.pos, Expr::ShiftRight),
227
228            ExprOp::Negate => apply1(exprs, self.pos, Expr::Negate),
229            ExprOp::Not => apply1(exprs, self.pos, Expr::Not),
230
231            ExprOp::LeftParen => Ok(()),
232        }
233    }
234}
235
236/// Iterator over the statements of the language.
237pub struct Parser<'a> {
238    lexer: PeekableLexer<'a>,
239}
240
241impl<'a> Parser<'a> {
242    /// Creates a new parser from the given readable.
243    fn from(input: &'a mut dyn io::Read) -> Self {
244        Self { lexer: Lexer::from(input).peekable() }
245    }
246
247    /// Expects the peeked token to be `t` and consumes it.  Otherwise, leaves the token in the
248    /// stream and fails with error `err`.
249    fn expect_and_consume<E: Into<String>>(&mut self, t: Token, err: E) -> Result<TokenSpan> {
250        let peeked = self.lexer.peek()?;
251        if peeked.token != t {
252            return Err(Error::Bad(peeked.pos, err.into()));
253        }
254        Ok(self.lexer.consume_peeked())
255    }
256
257    /// Expects the peeked token to be `t` and consumes it.  Otherwise, leaves the token in the
258    /// stream and fails with error `err`, pointing at `pos` as the original location of the
259    /// problem.
260    fn expect_and_consume_with_pos<E: Into<String>>(
261        &mut self,
262        t: Token,
263        pos: LineCol,
264        err: E,
265    ) -> Result<()> {
266        let peeked = self.lexer.peek()?;
267        if peeked.token != t {
268            return Err(Error::Bad(pos, err.into()));
269        }
270        self.lexer.consume_peeked();
271        Ok(())
272    }
273
274    /// Reads statements until the `delim` keyword is found.  The delimiter is not consumed.
275    fn parse_until(&mut self, delim: Token) -> Result<Vec<Statement>> {
276        let mut stmts = vec![];
277        loop {
278            let peeked = self.lexer.peek()?;
279            if peeked.token == delim {
280                break;
281            } else if peeked.token == Token::Eol {
282                self.lexer.consume_peeked();
283                continue;
284            }
285            match self.parse_one_safe()? {
286                Some(stmt) => stmts.push(stmt),
287                None => break,
288            }
289        }
290        Ok(stmts)
291    }
292
293    /// Parses an assignment for the variable reference `vref` already read.
294    fn parse_assignment(&mut self, vref: VarRef, vref_pos: LineCol) -> Result<Statement> {
295        let expr = self.parse_required_expr("Missing expression in assignment")?;
296
297        let next = self.lexer.peek()?;
298        match &next.token {
299            Token::Eof | Token::Eol | Token::Else => (),
300            t => return Err(Error::Bad(next.pos, format!("Unexpected {} in assignment", t))),
301        }
302        Ok(Statement::Assignment(AssignmentSpan { vref, vref_pos, expr }))
303    }
304
305    /// Parses an assignment to the array `varref` with `subscripts`, both of which have already
306    /// been read.
307    fn parse_array_assignment(
308        &mut self,
309        vref: VarRef,
310        vref_pos: LineCol,
311        subscripts: Vec<Expr>,
312    ) -> Result<Statement> {
313        let expr = self.parse_required_expr("Missing expression in array assignment")?;
314
315        let next = self.lexer.peek()?;
316        match &next.token {
317            Token::Eof | Token::Eol | Token::Else => (),
318            t => return Err(Error::Bad(next.pos, format!("Unexpected {} in array assignment", t))),
319        }
320        Ok(Statement::ArrayAssignment(ArrayAssignmentSpan { vref, vref_pos, subscripts, expr }))
321    }
322
323    /// Parses a builtin call (things of the form `INPUT a`).
324    fn parse_builtin_call(
325        &mut self,
326        vref: VarRef,
327        vref_pos: LineCol,
328        mut first: Option<Expr>,
329    ) -> Result<Statement> {
330        let mut name = vref_to_unannotated_string(vref, vref_pos)?;
331        name.make_ascii_uppercase();
332
333        let mut args = vec![];
334        loop {
335            let expr = self.parse_expr(first.take())?;
336
337            let peeked = self.lexer.peek()?;
338            match peeked.token {
339                Token::Eof | Token::Eol | Token::Else => {
340                    if expr.is_some() || !args.is_empty() {
341                        args.push(ArgSpan { expr, sep: ArgSep::End, sep_pos: peeked.pos });
342                    }
343                    break;
344                }
345                Token::Semicolon => {
346                    let peeked = self.lexer.consume_peeked();
347                    args.push(ArgSpan { expr, sep: ArgSep::Short, sep_pos: peeked.pos });
348                }
349                Token::Comma => {
350                    let peeked = self.lexer.consume_peeked();
351                    args.push(ArgSpan { expr, sep: ArgSep::Long, sep_pos: peeked.pos });
352                }
353                Token::As => {
354                    let peeked = self.lexer.consume_peeked();
355                    args.push(ArgSpan { expr, sep: ArgSep::As, sep_pos: peeked.pos });
356                }
357                _ => {
358                    return Err(Error::Bad(
359                        peeked.pos,
360                        "Expected comma, semicolon, or end of statement".to_owned(),
361                    ))
362                }
363            }
364        }
365        Ok(Statement::Call(CallSpan { vref: VarRef::new(name, None), vref_pos, args }))
366    }
367
368    /// Starts processing either an array reference or a builtin call and disambiguates between the
369    /// two.
370    fn parse_array_or_builtin_call(
371        &mut self,
372        vref: VarRef,
373        vref_pos: LineCol,
374    ) -> Result<Statement> {
375        match self.lexer.peek()?.token {
376            Token::LeftParen => {
377                let left_paren = self.lexer.consume_peeked();
378                let spans = self.parse_comma_separated_exprs()?;
379                let mut exprs = spans.into_iter().map(|span| span.expr.unwrap()).collect();
380                match self.lexer.peek()?.token {
381                    Token::Equal => {
382                        self.lexer.consume_peeked();
383                        self.parse_array_assignment(vref, vref_pos, exprs)
384                    }
385                    _ => {
386                        if exprs.len() != 1 {
387                            return Err(Error::Bad(
388                                left_paren.pos,
389                                "Expected expression".to_owned(),
390                            ));
391                        }
392                        self.parse_builtin_call(vref, vref_pos, Some(exprs.remove(0)))
393                    }
394                }
395            }
396            _ => self.parse_builtin_call(vref, vref_pos, None),
397        }
398    }
399
400    /// Parses the type name of an `AS` type definition.
401    ///
402    /// The `AS` token has already been consumed, so all this does is read a literal type name and
403    /// convert it to the corresponding expression type.
404    fn parse_as_type(&mut self) -> Result<(ExprType, LineCol)> {
405        let token_span = self.lexer.read()?;
406        match token_span.token {
407            Token::BooleanName => Ok((ExprType::Boolean, token_span.pos)),
408            Token::DoubleName => Ok((ExprType::Double, token_span.pos)),
409            Token::IntegerName => Ok((ExprType::Integer, token_span.pos)),
410            Token::TextName => Ok((ExprType::Text, token_span.pos)),
411            t => Err(Error::Bad(
412                token_span.pos,
413                format!("Invalid type name {} in AS type definition", t),
414            )),
415        }
416    }
417
418    /// Parses a `DATA` statement.
419    fn parse_data(&mut self) -> Result<Statement> {
420        let mut values = vec![];
421        loop {
422            let peeked = self.lexer.peek()?;
423            match peeked.token {
424                Token::Eof | Token::Eol | Token::Else => {
425                    values.push(None);
426                    break;
427                }
428                _ => (),
429            }
430
431            let token_span = self.lexer.read()?;
432            match token_span.token {
433                Token::Boolean(b) => values.push(Some(Value::Boolean(b))),
434                Token::Double(d) => values.push(Some(Value::Double(d))),
435                Token::Integer(i) => values.push(Some(Value::Integer(i))),
436                Token::Text(t) => values.push(Some(Value::Text(t))),
437
438                Token::Minus => {
439                    let token_span = self.lexer.read()?;
440                    match token_span.token {
441                        Token::Double(d) => values.push(Some(Value::Double(-d))),
442                        Token::Integer(i) => values.push(Some(Value::Integer(-i))),
443                        _ => {
444                            return Err(Error::Bad(
445                                token_span.pos,
446                                "Expected number after -".to_owned(),
447                            ))
448                        }
449                    }
450                }
451
452                Token::Eof | Token::Eol | Token::Else => {
453                    panic!("Should not be consumed here; handled above")
454                }
455
456                Token::Comma => {
457                    values.push(None);
458                    continue;
459                }
460
461                t => {
462                    return Err(Error::Bad(
463                        token_span.pos,
464                        format!("Unexpected {} in DATA statement", t),
465                    ))
466                }
467            }
468
469            let peeked = self.lexer.peek()?;
470            match &peeked.token {
471                Token::Eof | Token::Eol | Token::Else => {
472                    break;
473                }
474
475                Token::Comma => {
476                    self.lexer.consume_peeked();
477                }
478
479                t => {
480                    return Err(Error::Bad(
481                        peeked.pos,
482                        format!("Expected comma after datum but found {}", t),
483                    ))
484                }
485            }
486        }
487        Ok(Statement::Data(DataSpan { values }))
488    }
489
490    /// Parses the `AS typename` clause of a `DIM` statement.  The caller has already consumed the
491    /// `AS` token.
492    fn parse_dim_as(&mut self) -> Result<(ExprType, LineCol)> {
493        let peeked = self.lexer.peek()?;
494        let (vtype, vtype_pos) = match peeked.token {
495            Token::Eof | Token::Eol => (ExprType::Integer, peeked.pos),
496            Token::As => {
497                self.lexer.consume_peeked();
498                self.parse_as_type()?
499            }
500            _ => return Err(Error::Bad(peeked.pos, "Expected AS or end of statement".to_owned())),
501        };
502
503        let next = self.lexer.peek()?;
504        match &next.token {
505            Token::Eof | Token::Eol => (),
506            t => return Err(Error::Bad(next.pos, format!("Unexpected {} in DIM statement", t))),
507        }
508
509        Ok((vtype, vtype_pos))
510    }
511
512    /// Parses a `DIM` statement.
513    fn parse_dim(&mut self) -> Result<Statement> {
514        let peeked = self.lexer.peek()?;
515        let mut shared = false;
516        if peeked.token == Token::Shared {
517            self.lexer.consume_peeked();
518            shared = true;
519        }
520
521        let token_span = self.lexer.read()?;
522        let vref = match token_span.token {
523            Token::Symbol(vref) => vref,
524            _ => {
525                return Err(Error::Bad(
526                    token_span.pos,
527                    "Expected variable name after DIM".to_owned(),
528                ))
529            }
530        };
531        let name = vref_to_unannotated_string(vref, token_span.pos)?;
532        let name_pos = token_span.pos;
533
534        match self.lexer.peek()?.token {
535            Token::LeftParen => {
536                let peeked = self.lexer.consume_peeked();
537                let dimensions = self.parse_comma_separated_exprs()?;
538                if dimensions.is_empty() {
539                    return Err(Error::Bad(
540                        peeked.pos,
541                        "Arrays require at least one dimension".to_owned(),
542                    ));
543                }
544                let (subtype, subtype_pos) = self.parse_dim_as()?;
545                Ok(Statement::DimArray(DimArraySpan {
546                    name,
547                    name_pos,
548                    shared,
549                    dimensions: argspans_to_exprs(dimensions),
550                    subtype,
551                    subtype_pos,
552                }))
553            }
554            _ => {
555                let (vtype, vtype_pos) = self.parse_dim_as()?;
556                Ok(Statement::Dim(DimSpan { name, name_pos, shared, vtype, vtype_pos }))
557            }
558        }
559    }
560
561    /// Parses the `UNTIL` or `WHILE` clause of a `DO` loop.
562    ///
563    /// `part` is a string indicating where the clause is expected (either after `DO` or after
564    /// `LOOP`).
565    ///
566    /// Returns the guard expression and a boolean indicating if this is an `UNTIL` clause.
567    fn parse_do_guard(&mut self, part: &str) -> Result<Option<(Expr, bool)>> {
568        let peeked = self.lexer.peek()?;
569        match peeked.token {
570            Token::Until => {
571                self.lexer.consume_peeked();
572                let expr = self.parse_required_expr("No expression in UNTIL clause")?;
573                Ok(Some((expr, true)))
574            }
575            Token::While => {
576                self.lexer.consume_peeked();
577                let expr = self.parse_required_expr("No expression in WHILE clause")?;
578                Ok(Some((expr, false)))
579            }
580            Token::Eof | Token::Eol => Ok(None),
581            _ => {
582                let token_span = self.lexer.consume_peeked();
583                Err(Error::Bad(
584                    token_span.pos,
585                    format!("Expecting newline, UNTIL or WHILE after {}", part),
586                ))
587            }
588        }
589    }
590
591    /// Parses a `DO` statement.
592    fn parse_do(&mut self, do_pos: LineCol) -> Result<Statement> {
593        let pre_guard = self.parse_do_guard("DO")?;
594        self.expect_and_consume(Token::Eol, "Expecting newline after DO")?;
595
596        let stmts = self.parse_until(Token::Loop)?;
597        self.expect_and_consume_with_pos(Token::Loop, do_pos, "DO without LOOP")?;
598
599        let post_guard = self.parse_do_guard("LOOP")?;
600
601        let guard = match (pre_guard, post_guard) {
602            (None, None) => DoGuard::Infinite,
603            (Some((guard, true)), None) => DoGuard::PreUntil(guard),
604            (Some((guard, false)), None) => DoGuard::PreWhile(guard),
605            (None, Some((guard, true))) => DoGuard::PostUntil(guard),
606            (None, Some((guard, false))) => DoGuard::PostWhile(guard),
607            (Some(_), Some(_)) => {
608                return Err(Error::Bad(
609                    do_pos,
610                    "DO loop cannot have pre and post guards at the same time".to_owned(),
611                ))
612            }
613        };
614
615        Ok(Statement::Do(DoSpan { guard, body: stmts }))
616    }
617
618    /// Advances until the next statement after failing to parse a `DO` statement.
619    fn reset_do(&mut self) -> Result<()> {
620        loop {
621            match self.lexer.peek()?.token {
622                Token::Eof => break,
623                Token::Loop => {
624                    self.lexer.consume_peeked();
625                    loop {
626                        match self.lexer.peek()?.token {
627                            Token::Eof | Token::Eol => break,
628                            _ => {
629                                self.lexer.consume_peeked();
630                            }
631                        }
632                    }
633                    break;
634                }
635                _ => {
636                    self.lexer.consume_peeked();
637                }
638            }
639        }
640        self.reset()
641    }
642
643    /// Parses a potential `END` statement but, if this corresponds to a statement terminator such
644    /// as `END IF`, returns the token that followed `END`.
645    fn maybe_parse_end(&mut self) -> Result<std::result::Result<Statement, Token>> {
646        match self.lexer.peek()?.token {
647            Token::Function => Ok(Err(Token::Function)),
648            Token::If => Ok(Err(Token::If)),
649            Token::Select => Ok(Err(Token::Select)),
650            Token::Sub => Ok(Err(Token::Sub)),
651            _ => {
652                let code = self.parse_expr(None)?;
653                Ok(Ok(Statement::End(EndSpan { code })))
654            }
655        }
656    }
657
658    /// Parses an `END` statement.
659    fn parse_end(&mut self, pos: LineCol) -> Result<Statement> {
660        match self.maybe_parse_end()? {
661            Ok(stmt) => Ok(stmt),
662            Err(token) => Err(Error::Bad(pos, format!("END {} without {}", token, token))),
663        }
664    }
665
666    /// Parses an `EXIT DO` statement.
667    fn parse_exit_do(&mut self, pos: LineCol) -> Result<Statement> {
668        self.expect_and_consume(Token::Do, "Expecting DO after EXIT")?;
669        Ok(Statement::ExitDo(ExitDoSpan { pos }))
670    }
671
672    /// Parses a variable list of comma-separated expressions.  The caller must have consumed the
673    /// open parenthesis and we stop processing when we encounter the terminating parenthesis (and
674    /// consume it).  We expect at least one expression.
675    fn parse_comma_separated_exprs(&mut self) -> Result<Vec<ArgSpan>> {
676        let mut spans = vec![];
677
678        // The first expression is optional to support calls to functions without arguments.
679        let mut is_first = true;
680        let mut prev_expr = self.parse_expr(None)?;
681
682        loop {
683            let peeked = self.lexer.peek()?;
684            let pos = peeked.pos;
685            match &peeked.token {
686                Token::RightParen => {
687                    self.lexer.consume_peeked();
688
689                    if let Some(expr) = prev_expr.take() {
690                        spans.push(ArgSpan { expr: Some(expr), sep: ArgSep::End, sep_pos: pos });
691                    } else {
692                        if !is_first {
693                            return Err(Error::Bad(pos, "Missing expression".to_owned()));
694                        }
695                    }
696
697                    break;
698                }
699                Token::Comma => {
700                    self.lexer.consume_peeked();
701
702                    if let Some(expr) = prev_expr.take() {
703                        // The first expression is optional to support calls to functions without
704                        // arguments.
705                        spans.push(ArgSpan { expr: Some(expr), sep: ArgSep::Long, sep_pos: pos });
706                    } else {
707                        return Err(Error::Bad(pos, "Missing expression".to_owned()));
708                    }
709
710                    prev_expr = self.parse_expr(None)?;
711                }
712                t => return Err(Error::Bad(pos, format!("Unexpected {}", t))),
713            }
714
715            is_first = false;
716        }
717
718        Ok(spans)
719    }
720
721    /// Parses an expression.
722    ///
723    /// Returns `None` if no expression was found.  This is necessary to treat the case of empty
724    /// arguments to statements, as is the case in `PRINT a , , b`.
725    ///
726    /// If the caller has already processed a parenthesized term of an expression like
727    /// `(first) + second`, then that term must be provided in `first`.
728    ///
729    /// This is an implementation of the Shunting Yard Algorithm by Edgar Dijkstra.
730    fn parse_expr(&mut self, first: Option<Expr>) -> Result<Option<Expr>> {
731        let mut exprs: Vec<Expr> = vec![];
732        let mut op_spans: Vec<ExprOpSpan> = vec![];
733
734        let mut need_operand = true; // Also tracks whether an upcoming minus is unary.
735        if let Some(e) = first {
736            exprs.push(e);
737            need_operand = false;
738        }
739
740        loop {
741            let mut handle_operand = |e, pos| {
742                if !need_operand {
743                    return Err(Error::Bad(pos, "Unexpected value in expression".to_owned()));
744                }
745                need_operand = false;
746                exprs.push(e);
747                Ok(())
748            };
749
750            // Stop processing if we encounter an expression separator, but don't consume it because
751            // the caller needs to have access to it.
752            match self.lexer.peek()?.token {
753                Token::Eof
754                | Token::Eol
755                | Token::As
756                | Token::Comma
757                | Token::Else
758                | Token::Semicolon
759                | Token::Then
760                | Token::To
761                | Token::Step => break,
762                Token::RightParen => {
763                    if !op_spans.iter().any(|eos| eos.op == ExprOp::LeftParen) {
764                        // We encountered an unbalanced parenthesis but we don't know if this is
765                        // because we were called from within an argument list (in which case the
766                        // caller consumed the opening parenthesis and is expecting to consume the
767                        // closing parenthesis) or because we really found an invalid expression.
768                        // Only the caller can know, so avoid consuming the token and exit.
769                        break;
770                    }
771                }
772                _ => (),
773            };
774
775            let ts = self.lexer.consume_peeked();
776            match ts.token {
777                Token::Boolean(value) => {
778                    handle_operand(Expr::Boolean(BooleanSpan { value, pos: ts.pos }), ts.pos)?
779                }
780                Token::Double(value) => {
781                    handle_operand(Expr::Double(DoubleSpan { value, pos: ts.pos }), ts.pos)?
782                }
783                Token::Integer(value) => {
784                    handle_operand(Expr::Integer(IntegerSpan { value, pos: ts.pos }), ts.pos)?
785                }
786                Token::Text(value) => {
787                    handle_operand(Expr::Text(TextSpan { value, pos: ts.pos }), ts.pos)?
788                }
789                Token::Symbol(vref) => {
790                    handle_operand(Expr::Symbol(SymbolSpan { vref, pos: ts.pos }), ts.pos)?
791                }
792
793                Token::LeftParen => {
794                    // If the last operand we encountered was a symbol, collapse it and the left
795                    // parenthesis into the beginning of a function call.
796                    match exprs.pop() {
797                        Some(Expr::Symbol(span)) => {
798                            if !need_operand {
799                                exprs.push(Expr::Call(CallSpan {
800                                    vref: span.vref,
801                                    vref_pos: span.pos,
802                                    args: self.parse_comma_separated_exprs()?,
803                                }));
804                                need_operand = false;
805                            } else {
806                                // We popped out the last expression to see if it this left
807                                // parenthesis started a function call... but it did not (it is a
808                                // symbol following a parenthesis) so put both the expression and
809                                // the token back.
810                                op_spans.push(ExprOpSpan::new(ExprOp::LeftParen, ts.pos));
811                                exprs.push(Expr::Symbol(span));
812                                need_operand = true;
813                            }
814                        }
815                        e => {
816                            if let Some(e) = e {
817                                // We popped out the last expression to see if this left
818                                // parenthesis started a function call... but if it didn't, we have
819                                // to put the expression back.
820                                exprs.push(e);
821                            }
822                            if !need_operand {
823                                return Err(Error::Bad(
824                                    ts.pos,
825                                    format!("Unexpected {} in expression", ts.token),
826                                ));
827                            }
828                            op_spans.push(ExprOpSpan::new(ExprOp::LeftParen, ts.pos));
829                            need_operand = true;
830                        }
831                    };
832                }
833                Token::RightParen => {
834                    let mut found = false;
835                    while let Some(eos) = op_spans.pop() {
836                        eos.apply(&mut exprs)?;
837                        if eos.op == ExprOp::LeftParen {
838                            found = true;
839                            break;
840                        }
841                    }
842                    assert!(found, "Unbalanced parenthesis should have been handled above");
843                    need_operand = false;
844                }
845
846                Token::Not => {
847                    op_spans.push(ExprOpSpan::new(ExprOp::Not, ts.pos));
848                    need_operand = true;
849                }
850                Token::Minus => {
851                    let op;
852                    if need_operand {
853                        op = ExprOp::Negate;
854                    } else {
855                        op = ExprOp::Subtract;
856                        while let Some(eos2) = op_spans.last() {
857                            if eos2.op == ExprOp::LeftParen || eos2.op.priority() < op.priority() {
858                                break;
859                            }
860                            let eos2 = op_spans.pop().unwrap();
861                            eos2.apply(&mut exprs)?;
862                        }
863                    }
864                    op_spans.push(ExprOpSpan::new(op, ts.pos));
865                    need_operand = true;
866                }
867
868                Token::Equal
869                | Token::NotEqual
870                | Token::Less
871                | Token::LessEqual
872                | Token::Greater
873                | Token::GreaterEqual
874                | Token::Plus
875                | Token::Multiply
876                | Token::Divide
877                | Token::Modulo
878                | Token::Exponent
879                | Token::And
880                | Token::Or
881                | Token::Xor
882                | Token::ShiftLeft
883                | Token::ShiftRight => {
884                    let op = ExprOp::from(ts.token);
885                    while let Some(eos2) = op_spans.last() {
886                        if eos2.op == ExprOp::LeftParen || eos2.op.priority() < op.priority() {
887                            break;
888                        }
889                        let eos2 = op_spans.pop().unwrap();
890                        eos2.apply(&mut exprs)?;
891                    }
892                    op_spans.push(ExprOpSpan::new(op, ts.pos));
893                    need_operand = true;
894                }
895
896                Token::Bad(e) => return Err(Error::Bad(ts.pos, e)),
897
898                Token::Eof
899                | Token::Eol
900                | Token::As
901                | Token::Comma
902                | Token::Else
903                | Token::Semicolon
904                | Token::Then
905                | Token::To
906                | Token::Step => {
907                    panic!("Field separators handled above")
908                }
909
910                Token::BooleanName
911                | Token::Case
912                | Token::Data
913                | Token::Do
914                | Token::Dim
915                | Token::DoubleName
916                | Token::Elseif
917                | Token::End
918                | Token::Error
919                | Token::Exit
920                | Token::For
921                | Token::Function
922                | Token::Gosub
923                | Token::Goto
924                | Token::If
925                | Token::Is
926                | Token::IntegerName
927                | Token::Label(_)
928                | Token::Loop
929                | Token::Next
930                | Token::On
931                | Token::Resume
932                | Token::Return
933                | Token::Select
934                | Token::Shared
935                | Token::Sub
936                | Token::TextName
937                | Token::Until
938                | Token::Wend
939                | Token::While => {
940                    return Err(Error::Bad(ts.pos, "Unexpected keyword in expression".to_owned()));
941                }
942            };
943        }
944
945        while let Some(eos) = op_spans.pop() {
946            match eos.op {
947                ExprOp::LeftParen => {
948                    return Err(Error::Bad(eos.pos, "Unbalanced parenthesis".to_owned()))
949                }
950                _ => eos.apply(&mut exprs)?,
951            }
952        }
953
954        if let Some(expr) = exprs.pop() {
955            Ok(Some(expr))
956        } else {
957            Ok(None)
958        }
959    }
960
961    /// Wrapper over `parse_expr` that requires an expression to be present and returns an error
962    /// with `msg` otherwise.
963    fn parse_required_expr(&mut self, msg: &'static str) -> Result<Expr> {
964        let next_pos = self.lexer.peek()?.pos;
965        match self.parse_expr(None)? {
966            Some(expr) => Ok(expr),
967            None => Err(Error::Bad(next_pos, msg.to_owned())),
968        }
969    }
970
971    /// Parses a `GOSUB` statement.
972    fn parse_gosub(&mut self) -> Result<Statement> {
973        let token_span = self.lexer.read()?;
974        match token_span.token {
975            Token::Integer(i) => {
976                let target = format!("{}", i);
977                Ok(Statement::Gosub(GotoSpan { target, target_pos: token_span.pos }))
978            }
979            Token::Label(target) => {
980                Ok(Statement::Gosub(GotoSpan { target, target_pos: token_span.pos }))
981            }
982            _ => Err(Error::Bad(token_span.pos, "Expected label name after GOSUB".to_owned())),
983        }
984    }
985
986    /// Parses a `GOTO` statement.
987    fn parse_goto(&mut self) -> Result<Statement> {
988        let token_span = self.lexer.read()?;
989        match token_span.token {
990            Token::Integer(i) => {
991                let target = format!("{}", i);
992                Ok(Statement::Goto(GotoSpan { target, target_pos: token_span.pos }))
993            }
994            Token::Label(target) => {
995                Ok(Statement::Goto(GotoSpan { target, target_pos: token_span.pos }))
996            }
997            _ => Err(Error::Bad(token_span.pos, "Expected label name after GOTO".to_owned())),
998        }
999    }
1000
1001    /// Parses the branches of a uniline `IF` statement.
1002    fn parse_if_uniline(&mut self, branches: &mut Vec<IfBranchSpan>) -> Result<()> {
1003        debug_assert!(!branches.is_empty(), "Caller must populate the guard of the first branch");
1004
1005        let mut has_else = false;
1006        let peeked = self.lexer.peek()?;
1007        match peeked.token {
1008            Token::Else => has_else = true,
1009            _ => {
1010                let stmt = self
1011                    .parse_uniline()?
1012                    .expect("The caller already checked for a non-empty token");
1013                branches[0].body.push(stmt);
1014            }
1015        }
1016
1017        let peeked = self.lexer.peek()?;
1018        has_else |= peeked.token == Token::Else;
1019
1020        if has_else {
1021            let else_span = self.lexer.consume_peeked();
1022            let expr = Expr::Boolean(BooleanSpan { value: true, pos: else_span.pos });
1023            branches.push(IfBranchSpan { guard: expr, body: vec![] });
1024            if let Some(stmt) = self.parse_uniline()? {
1025                branches[1].body.push(stmt);
1026            }
1027        }
1028
1029        Ok(())
1030    }
1031
1032    /// Parses the branches of a multiline `IF` statement.
1033    fn parse_if_multiline(
1034        &mut self,
1035        if_pos: LineCol,
1036        branches: &mut Vec<IfBranchSpan>,
1037    ) -> Result<()> {
1038        debug_assert!(!branches.is_empty(), "Caller must populate the guard of the first branch");
1039
1040        let mut i = 0;
1041        let mut last = false;
1042        loop {
1043            let peeked = self.lexer.peek()?;
1044            match peeked.token {
1045                Token::Eol => {
1046                    self.lexer.consume_peeked();
1047                }
1048
1049                Token::Elseif => {
1050                    if last {
1051                        return Err(Error::Bad(
1052                            peeked.pos,
1053                            "Unexpected ELSEIF after ELSE".to_owned(),
1054                        ));
1055                    }
1056
1057                    self.lexer.consume_peeked();
1058                    let expr = self.parse_required_expr("No expression in ELSEIF statement")?;
1059                    self.expect_and_consume(Token::Then, "No THEN in ELSEIF statement")?;
1060                    self.expect_and_consume(Token::Eol, "Expecting newline after THEN")?;
1061                    branches.push(IfBranchSpan { guard: expr, body: vec![] });
1062                    i += 1;
1063                }
1064
1065                Token::Else => {
1066                    if last {
1067                        return Err(Error::Bad(peeked.pos, "Duplicate ELSE after ELSE".to_owned()));
1068                    }
1069
1070                    let else_span = self.lexer.consume_peeked();
1071                    self.expect_and_consume(Token::Eol, "Expecting newline after ELSE")?;
1072
1073                    let expr = Expr::Boolean(BooleanSpan { value: true, pos: else_span.pos });
1074                    branches.push(IfBranchSpan { guard: expr, body: vec![] });
1075                    i += 1;
1076
1077                    last = true;
1078                }
1079
1080                Token::End => {
1081                    let token_span = self.lexer.consume_peeked();
1082                    match self.maybe_parse_end()? {
1083                        Ok(stmt) => {
1084                            branches[i].body.push(stmt);
1085                        }
1086                        Err(Token::If) => {
1087                            break;
1088                        }
1089                        Err(token) => {
1090                            return Err(Error::Bad(
1091                                token_span.pos,
1092                                format!("END {} without {}", token, token),
1093                            ));
1094                        }
1095                    }
1096                }
1097
1098                _ => match self.parse_one_safe()? {
1099                    Some(stmt) => {
1100                        branches[i].body.push(stmt);
1101                    }
1102                    None => {
1103                        break;
1104                    }
1105                },
1106            }
1107        }
1108
1109        self.expect_and_consume_with_pos(Token::If, if_pos, "IF without END IF")
1110    }
1111
1112    /// Parses an `IF` statement.
1113    fn parse_if(&mut self, if_pos: LineCol) -> Result<Statement> {
1114        let expr = self.parse_required_expr("No expression in IF statement")?;
1115        self.expect_and_consume(Token::Then, "No THEN in IF statement")?;
1116
1117        let mut branches = vec![IfBranchSpan { guard: expr, body: vec![] }];
1118
1119        let peeked = self.lexer.peek()?;
1120        match peeked.token {
1121            Token::Eol | Token::Eof => self.parse_if_multiline(if_pos, &mut branches)?,
1122            _ => self.parse_if_uniline(&mut branches)?,
1123        }
1124
1125        Ok(Statement::If(IfSpan { branches }))
1126    }
1127
1128    /// Advances until the next statement after failing to parse an `IF` statement.
1129    fn reset_if(&mut self, if_pos: LineCol) -> Result<()> {
1130        loop {
1131            match self.lexer.peek()?.token {
1132                Token::Eof => break,
1133                Token::End => {
1134                    self.lexer.consume_peeked();
1135                    self.expect_and_consume_with_pos(Token::If, if_pos, "IF without END IF")?;
1136                    break;
1137                }
1138                _ => {
1139                    self.lexer.consume_peeked();
1140                }
1141            }
1142        }
1143        self.reset()
1144    }
1145
1146    /// Extracts the optional `STEP` part of a `FOR` statement, with a default of 1.
1147    ///
1148    /// Returns the step as an expression, an `Ordering` value representing how the step value
1149    /// compares to zero, and whether the step is a double or not.
1150    fn parse_step(&mut self) -> Result<(Expr, Ordering, bool)> {
1151        let peeked = self.lexer.peek()?;
1152        match peeked.token {
1153            Token::Step => self.lexer.consume_peeked(),
1154            _ => {
1155                // The position we return here for the step isn't truly the right value, but given
1156                // that we know the hardcoded step of 1 is valid, the caller will not error out and
1157                // will not print the slightly invalid position.
1158                return Ok((
1159                    Expr::Integer(IntegerSpan { value: 1, pos: peeked.pos }),
1160                    Ordering::Greater,
1161                    false,
1162                ));
1163            }
1164        };
1165
1166        let peeked = self.lexer.peek()?;
1167        match peeked.token {
1168            Token::Double(d) => {
1169                let peeked = self.lexer.consume_peeked();
1170                let sign = if d == 0.0 { Ordering::Equal } else { Ordering::Greater };
1171                Ok((Expr::Double(DoubleSpan { value: d, pos: peeked.pos }), sign, true))
1172            }
1173            Token::Integer(i) => {
1174                let peeked = self.lexer.consume_peeked();
1175                Ok((Expr::Integer(IntegerSpan { value: i, pos: peeked.pos }), i.cmp(&0), false))
1176            }
1177            Token::Minus => {
1178                self.lexer.consume_peeked();
1179                let peeked = self.lexer.peek()?;
1180                match peeked.token {
1181                    Token::Double(d) => {
1182                        let peeked = self.lexer.consume_peeked();
1183                        let sign = if d == 0.0 { Ordering::Equal } else { Ordering::Less };
1184                        Ok((Expr::Double(DoubleSpan { value: -d, pos: peeked.pos }), sign, true))
1185                    }
1186                    Token::Integer(i) => {
1187                        let peeked = self.lexer.consume_peeked();
1188                        Ok((
1189                            Expr::Integer(IntegerSpan { value: -i, pos: peeked.pos }),
1190                            (-i).cmp(&0),
1191                            false,
1192                        ))
1193                    }
1194                    _ => Err(Error::Bad(peeked.pos, "STEP needs a literal number".to_owned())),
1195                }
1196            }
1197            _ => Err(Error::Bad(peeked.pos, "STEP needs a literal number".to_owned())),
1198        }
1199    }
1200
1201    /// Parses a `FOR` statement.
1202    fn parse_for(&mut self, for_pos: LineCol) -> Result<Statement> {
1203        let token_span = self.lexer.read()?;
1204        let iterator = match token_span.token {
1205            Token::Symbol(iterator) => match iterator.ref_type() {
1206                None | Some(ExprType::Double) | Some(ExprType::Integer) => iterator,
1207                _ => {
1208                    return Err(Error::Bad(
1209                        token_span.pos,
1210                        "Iterator name in FOR statement must be a numeric reference".to_owned(),
1211                    ))
1212                }
1213            },
1214            _ => {
1215                return Err(Error::Bad(
1216                    token_span.pos,
1217                    "No iterator name in FOR statement".to_owned(),
1218                ))
1219            }
1220        };
1221        let iterator_pos = token_span.pos;
1222
1223        self.expect_and_consume(Token::Equal, "No equal sign in FOR statement")?;
1224        let start = self.parse_required_expr("No start expression in FOR statement")?;
1225
1226        let to_span = self.expect_and_consume(Token::To, "No TO in FOR statement")?;
1227        let end = self.parse_required_expr("No end expression in FOR statement")?;
1228
1229        let (step, step_sign, iter_double) = self.parse_step()?;
1230        let end_condition = match step_sign {
1231            Ordering::Greater => Expr::LessEqual(Box::from(BinaryOpSpan {
1232                lhs: Expr::Symbol(SymbolSpan { vref: iterator.clone(), pos: iterator_pos }),
1233                rhs: end,
1234                pos: to_span.pos,
1235            })),
1236            Ordering::Less => Expr::GreaterEqual(Box::from(BinaryOpSpan {
1237                lhs: Expr::Symbol(SymbolSpan { vref: iterator.clone(), pos: iterator_pos }),
1238                rhs: end,
1239                pos: to_span.pos,
1240            })),
1241            Ordering::Equal => {
1242                return Err(Error::Bad(
1243                    step.start_pos(),
1244                    "Infinite FOR loop; STEP cannot be 0".to_owned(),
1245                ))
1246            }
1247        };
1248
1249        let next_value = Expr::Add(Box::from(BinaryOpSpan {
1250            lhs: Expr::Symbol(SymbolSpan { vref: iterator.clone(), pos: iterator_pos }),
1251            rhs: step,
1252            pos: to_span.pos,
1253        }));
1254
1255        self.expect_and_consume(Token::Eol, "Expecting newline after FOR")?;
1256
1257        let stmts = self.parse_until(Token::Next)?;
1258        self.expect_and_consume_with_pos(Token::Next, for_pos, "FOR without NEXT")?;
1259
1260        Ok(Statement::For(ForSpan {
1261            iter: iterator,
1262            iter_pos: iterator_pos,
1263            iter_double,
1264            start,
1265            end: end_condition,
1266            next: next_value,
1267            body: stmts,
1268        }))
1269    }
1270
1271    /// Advances until the next statement after failing to parse a `FOR` statement.
1272    fn reset_for(&mut self) -> Result<()> {
1273        loop {
1274            match self.lexer.peek()?.token {
1275                Token::Eof => break,
1276                Token::Next => {
1277                    self.lexer.consume_peeked();
1278                    break;
1279                }
1280                _ => {
1281                    self.lexer.consume_peeked();
1282                }
1283            }
1284        }
1285        self.reset()
1286    }
1287
1288    /// Parses the optional parameter list that may appear after a `FUNCTION` or `SUB` definition,
1289    /// including the opening and closing parenthesis.
1290    fn parse_callable_args(&mut self) -> Result<Vec<VarRef>> {
1291        let mut params = vec![];
1292        let peeked = self.lexer.peek()?;
1293        if peeked.token == Token::LeftParen {
1294            self.lexer.consume_peeked();
1295
1296            loop {
1297                let token_span = self.lexer.read()?;
1298                match token_span.token {
1299                    Token::Symbol(param) => {
1300                        let peeked = self.lexer.peek()?;
1301                        if peeked.token == Token::As {
1302                            self.lexer.consume_peeked();
1303
1304                            let name = vref_to_unannotated_string(param, token_span.pos)?;
1305                            let (vtype, _pos) = self.parse_as_type()?;
1306                            params.push(VarRef::new(name, Some(vtype)));
1307                        } else {
1308                            params.push(param);
1309                        }
1310                    }
1311                    _ => {
1312                        return Err(Error::Bad(
1313                            token_span.pos,
1314                            "Expected a parameter name".to_owned(),
1315                        ));
1316                    }
1317                }
1318
1319                let token_span = self.lexer.read()?;
1320                match token_span.token {
1321                    Token::Comma => (),
1322                    Token::RightParen => break,
1323                    _ => {
1324                        return Err(Error::Bad(
1325                            token_span.pos,
1326                            "Expected comma, AS, or end of parameters list".to_owned(),
1327                        ));
1328                    }
1329                }
1330            }
1331        }
1332        Ok(params)
1333    }
1334
1335    /// Parses the body of a callable and returns the collection of statements and the position
1336    /// of the end of the body.
1337    fn parse_callable_body(
1338        &mut self,
1339        start_pos: LineCol,
1340        exp_token: Token,
1341    ) -> Result<(Vec<Statement>, LineCol)> {
1342        debug_assert!(matches!(exp_token, Token::Function | Token::Sub));
1343
1344        let mut body = vec![];
1345        let end_pos;
1346        loop {
1347            let peeked = self.lexer.peek()?;
1348            match peeked.token {
1349                Token::Eof => {
1350                    end_pos = peeked.pos;
1351                    break;
1352                }
1353
1354                Token::Eol => {
1355                    self.lexer.consume_peeked();
1356                }
1357
1358                Token::Function | Token::Sub => {
1359                    return Err(Error::Bad(
1360                        peeked.pos,
1361                        "Cannot nest FUNCTION or SUB definitions".to_owned(),
1362                    ));
1363                }
1364
1365                Token::End => {
1366                    let end_span = self.lexer.consume_peeked();
1367                    match self.maybe_parse_end()? {
1368                        Ok(stmt) => {
1369                            body.push(stmt);
1370                        }
1371                        Err(token) if token == exp_token => {
1372                            end_pos = end_span.pos;
1373                            break;
1374                        }
1375                        Err(token) => {
1376                            return Err(Error::Bad(
1377                                end_span.pos,
1378                                format!("END {} without {}", token, token),
1379                            ));
1380                        }
1381                    }
1382                }
1383
1384                // TODO(jmmv): Handle `EXIT FUNCTION` or `EXIT SUB`.
1385                _ => match self.parse_one_safe()? {
1386                    Some(stmt) => body.push(stmt),
1387                    None => {
1388                        return Err(Error::Bad(
1389                            start_pos,
1390                            format!("{} without END {}", exp_token, exp_token),
1391                        ));
1392                    }
1393                },
1394            }
1395        }
1396
1397        self.expect_and_consume_with_pos(
1398            exp_token.clone(),
1399            start_pos,
1400            format!("{} without END {}", exp_token, exp_token),
1401        )?;
1402
1403        Ok((body, end_pos))
1404    }
1405
1406    /// Parses a `FUNCTION` definition.
1407    fn parse_function(&mut self, function_pos: LineCol) -> Result<Statement> {
1408        let token_span = self.lexer.read()?;
1409        let name = match token_span.token {
1410            Token::Symbol(name) => {
1411                if name.ref_type().is_none() {
1412                    VarRef::new(name.take_name(), Some(ExprType::Integer))
1413                } else {
1414                    name
1415                }
1416            }
1417            _ => {
1418                return Err(Error::Bad(
1419                    token_span.pos,
1420                    "Expected a function name after FUNCTION".to_owned(),
1421                ));
1422            }
1423        };
1424        let name_pos = token_span.pos;
1425
1426        let params = self.parse_callable_args()?;
1427        self.expect_and_consume(Token::Eol, "Expected newline after FUNCTION name")?;
1428
1429        let (body, end_pos) = self.parse_callable_body(function_pos, Token::Function)?;
1430
1431        Ok(Statement::Callable(CallableSpan { name, name_pos, params, body, end_pos }))
1432    }
1433
1434    /// Parses a `SUB` definition.
1435    fn parse_sub(&mut self, sub_pos: LineCol) -> Result<Statement> {
1436        let token_span = self.lexer.read()?;
1437        let name = match token_span.token {
1438            Token::Symbol(name) => {
1439                if name.ref_type().is_some() {
1440                    return Err(Error::Bad(
1441                        token_span.pos,
1442                        "SUBs cannot return a value so type annotations are not allowed".to_owned(),
1443                    ));
1444                }
1445                name
1446            }
1447            _ => {
1448                return Err(Error::Bad(
1449                    token_span.pos,
1450                    "Expected a function name after SUB".to_owned(),
1451                ));
1452            }
1453        };
1454        let name_pos = token_span.pos;
1455
1456        let params = self.parse_callable_args()?;
1457        self.expect_and_consume(Token::Eol, "Expected newline after SUB name")?;
1458
1459        let (body, end_pos) = self.parse_callable_body(sub_pos, Token::Sub)?;
1460
1461        Ok(Statement::Callable(CallableSpan { name, name_pos, params, body, end_pos }))
1462    }
1463
1464    /// Advances until the next statement after failing to parse a `FUNCTION` or `SUB` definition.
1465    fn reset_callable(&mut self, exp_token: Token) -> Result<()> {
1466        loop {
1467            match self.lexer.peek()?.token {
1468                Token::Eof => break,
1469                Token::End => {
1470                    self.lexer.consume_peeked();
1471
1472                    let token_span = self.lexer.read()?;
1473                    if token_span.token == exp_token {
1474                        break;
1475                    }
1476                }
1477                _ => {
1478                    self.lexer.consume_peeked();
1479                }
1480            }
1481        }
1482        self.reset()
1483    }
1484
1485    /// Parses an `ON ERROR` statement.  Only `ON` has been consumed so far.
1486    fn parse_on(&mut self) -> Result<Statement> {
1487        self.expect_and_consume(Token::Error, "Expected ERROR after ON")?;
1488
1489        let token_span = self.lexer.read()?;
1490        match token_span.token {
1491            Token::Goto => {
1492                let token_span = self.lexer.read()?;
1493                match token_span.token {
1494                    Token::Integer(0) => Ok(Statement::OnError(OnErrorSpan::Reset)),
1495                    Token::Integer(i) => Ok(Statement::OnError(OnErrorSpan::Goto(GotoSpan {
1496                        target: format!("{}", i),
1497                        target_pos: token_span.pos,
1498                    }))),
1499                    Token::Label(target) => Ok(Statement::OnError(OnErrorSpan::Goto(GotoSpan {
1500                        target,
1501                        target_pos: token_span.pos,
1502                    }))),
1503                    _ => Err(Error::Bad(
1504                        token_span.pos,
1505                        "Expected label name or 0 after ON ERROR GOTO".to_owned(),
1506                    )),
1507                }
1508            }
1509            Token::Resume => {
1510                self.expect_and_consume(Token::Next, "Expected NEXT after ON ERROR RESUME")?;
1511                Ok(Statement::OnError(OnErrorSpan::ResumeNext))
1512            }
1513            _ => {
1514                Err(Error::Bad(token_span.pos, "Expected GOTO or RESUME after ON ERROR".to_owned()))
1515            }
1516        }
1517    }
1518
1519    /// Parses the guards after a `CASE` keyword.
1520    fn parse_case_guards(&mut self) -> Result<Vec<CaseGuardSpan>> {
1521        let mut guards = vec![];
1522
1523        loop {
1524            let peeked = self.lexer.peek()?;
1525            match peeked.token {
1526                Token::Else => {
1527                    let token_span = self.lexer.consume_peeked();
1528
1529                    if !guards.is_empty() {
1530                        return Err(Error::Bad(
1531                            token_span.pos,
1532                            "CASE ELSE must be on its own".to_owned(),
1533                        ));
1534                    }
1535
1536                    let peeked = self.lexer.peek()?;
1537                    if peeked.token != Token::Eol && peeked.token != Token::Eof {
1538                        return Err(Error::Bad(
1539                            peeked.pos,
1540                            "Expected newline after CASE ELSE".to_owned(),
1541                        ));
1542                    }
1543
1544                    break;
1545                }
1546
1547                Token::Is => {
1548                    self.lexer.consume_peeked();
1549
1550                    let token_span = self.lexer.read()?;
1551                    let rel_op = match token_span.token {
1552                        Token::Equal => CaseRelOp::Equal,
1553                        Token::NotEqual => CaseRelOp::NotEqual,
1554                        Token::Less => CaseRelOp::Less,
1555                        Token::LessEqual => CaseRelOp::LessEqual,
1556                        Token::Greater => CaseRelOp::Greater,
1557                        Token::GreaterEqual => CaseRelOp::GreaterEqual,
1558                        _ => {
1559                            return Err(Error::Bad(
1560                                token_span.pos,
1561                                "Expected relational operator".to_owned(),
1562                            ));
1563                        }
1564                    };
1565
1566                    let expr =
1567                        self.parse_required_expr("Missing expression after relational operator")?;
1568                    guards.push(CaseGuardSpan::Is(rel_op, expr));
1569                }
1570
1571                _ => {
1572                    let from_expr = self.parse_required_expr("Missing expression in CASE guard")?;
1573
1574                    let peeked = self.lexer.peek()?;
1575                    match peeked.token {
1576                        Token::Eol | Token::Comma => {
1577                            guards.push(CaseGuardSpan::Is(CaseRelOp::Equal, from_expr));
1578                        }
1579                        Token::To => {
1580                            self.lexer.consume_peeked();
1581                            let to_expr = self
1582                                .parse_required_expr("Missing expression after TO in CASE guard")?;
1583                            guards.push(CaseGuardSpan::To(from_expr, to_expr));
1584                        }
1585                        _ => {
1586                            return Err(Error::Bad(
1587                                peeked.pos,
1588                                "Expected comma, newline, or TO after expression".to_owned(),
1589                            ));
1590                        }
1591                    }
1592                }
1593            }
1594
1595            let peeked = self.lexer.peek()?;
1596            match peeked.token {
1597                Token::Eol => {
1598                    break;
1599                }
1600                Token::Comma => {
1601                    self.lexer.consume_peeked();
1602                }
1603                _ => {
1604                    return Err(Error::Bad(
1605                        peeked.pos,
1606                        "Expected comma, newline, or TO after expression".to_owned(),
1607                    ));
1608                }
1609            }
1610        }
1611
1612        Ok(guards)
1613    }
1614
1615    /// Parses a `SELECT` statement.
1616    fn parse_select(&mut self, select_pos: LineCol) -> Result<Statement> {
1617        self.expect_and_consume(Token::Case, "Expecting CASE after SELECT")?;
1618
1619        let expr = self.parse_required_expr("No expression in SELECT CASE statement")?;
1620        self.expect_and_consume(Token::Eol, "Expecting newline after SELECT CASE")?;
1621
1622        let mut cases = vec![];
1623
1624        let mut i = 0;
1625        let mut last = false;
1626        let end_pos;
1627        loop {
1628            let peeked = self.lexer.peek()?;
1629            match peeked.token {
1630                Token::Eof => {
1631                    end_pos = peeked.pos;
1632                    break;
1633                }
1634
1635                Token::Eol => {
1636                    self.lexer.consume_peeked();
1637                }
1638
1639                Token::Case => {
1640                    let peeked = self.lexer.consume_peeked();
1641                    let guards = self.parse_case_guards()?;
1642                    self.expect_and_consume(Token::Eol, "Expecting newline after CASE")?;
1643
1644                    let is_last = guards.is_empty();
1645                    if last {
1646                        if is_last {
1647                            return Err(Error::Bad(
1648                                peeked.pos,
1649                                "CASE ELSE must be unique".to_owned(),
1650                            ));
1651                        } else {
1652                            return Err(Error::Bad(peeked.pos, "CASE ELSE is not last".to_owned()));
1653                        }
1654                    }
1655                    last |= is_last;
1656
1657                    cases.push(CaseSpan { guards, body: vec![] });
1658                    if cases.len() > 1 {
1659                        i += 1;
1660                    }
1661                }
1662
1663                Token::End => {
1664                    let end_span = self.lexer.consume_peeked();
1665                    match self.maybe_parse_end()? {
1666                        Ok(stmt) => {
1667                            if cases.is_empty() {
1668                                return Err(Error::Bad(
1669                                    end_span.pos,
1670                                    "Expected CASE after SELECT CASE before any statement"
1671                                        .to_owned(),
1672                                ));
1673                            }
1674
1675                            cases[i].body.push(stmt);
1676                        }
1677                        Err(Token::Select) => {
1678                            end_pos = end_span.pos;
1679                            break;
1680                        }
1681                        Err(token) => {
1682                            if cases.is_empty() {
1683                                return Err(Error::Bad(
1684                                    end_span.pos,
1685                                    "Expected CASE after SELECT CASE before any statement"
1686                                        .to_owned(),
1687                                ));
1688                            } else {
1689                                return Err(Error::Bad(
1690                                    end_span.pos,
1691                                    format!("END {} without {}", token, token),
1692                                ));
1693                            }
1694                        }
1695                    }
1696                }
1697
1698                _ => {
1699                    if cases.is_empty() {
1700                        return Err(Error::Bad(
1701                            peeked.pos,
1702                            "Expected CASE after SELECT CASE before any statement".to_owned(),
1703                        ));
1704                    }
1705
1706                    if let Some(stmt) = self.parse_one_safe()? {
1707                        cases[i].body.push(stmt);
1708                    }
1709                }
1710            }
1711        }
1712
1713        self.expect_and_consume_with_pos(Token::Select, select_pos, "SELECT without END SELECT")?;
1714
1715        Ok(Statement::Select(SelectSpan { expr, cases, end_pos }))
1716    }
1717
1718    /// Advances until the next statement after failing to parse a `SELECT` statement.
1719    fn reset_select(&mut self, select_pos: LineCol) -> Result<()> {
1720        loop {
1721            match self.lexer.peek()?.token {
1722                Token::Eof => break,
1723                Token::End => {
1724                    self.lexer.consume_peeked();
1725                    self.expect_and_consume_with_pos(
1726                        Token::Select,
1727                        select_pos,
1728                        "SELECT without END SELECT",
1729                    )?;
1730                    break;
1731                }
1732                _ => {
1733                    self.lexer.consume_peeked();
1734                }
1735            }
1736        }
1737        self.reset()
1738    }
1739
1740    /// Parses a `WHILE` statement.
1741    fn parse_while(&mut self, while_pos: LineCol) -> Result<Statement> {
1742        let expr = self.parse_required_expr("No expression in WHILE statement")?;
1743        self.expect_and_consume(Token::Eol, "Expecting newline after WHILE")?;
1744
1745        let stmts = self.parse_until(Token::Wend)?;
1746        self.expect_and_consume_with_pos(Token::Wend, while_pos, "WHILE without WEND")?;
1747
1748        Ok(Statement::While(WhileSpan { expr, body: stmts }))
1749    }
1750
1751    /// Advances until the next statement after failing to parse a `WHILE` statement.
1752    fn reset_while(&mut self) -> Result<()> {
1753        loop {
1754            match self.lexer.peek()?.token {
1755                Token::Eof => break,
1756                Token::Wend => {
1757                    self.lexer.consume_peeked();
1758                    break;
1759                }
1760                _ => {
1761                    self.lexer.consume_peeked();
1762                }
1763            }
1764        }
1765        self.reset()
1766    }
1767
1768    /// Extracts the next available uniline statement from the input stream, or `None` if none is
1769    /// available.
1770    ///
1771    /// The statement must be specifiable in a single line as part of a uniline `IF` statement, and
1772    /// we currently expect this to only be used while parsing an `IF`.
1773    ///
1774    /// On success, the stream is left in a position where the next statement can be extracted.
1775    /// On failure, the caller must advance the stream to the next statement by calling `reset`.
1776    fn parse_uniline(&mut self) -> Result<Option<Statement>> {
1777        let token_span = self.lexer.read()?;
1778        match token_span.token {
1779            Token::Data => Ok(Some(self.parse_data()?)),
1780            Token::End => Ok(Some(self.parse_end(token_span.pos)?)),
1781            Token::Eof | Token::Eol => Ok(None),
1782            Token::Exit => Ok(Some(self.parse_exit_do(token_span.pos)?)),
1783            Token::Gosub => Ok(Some(self.parse_gosub()?)),
1784            Token::Goto => Ok(Some(self.parse_goto()?)),
1785            Token::On => Ok(Some(self.parse_on()?)),
1786            Token::Return => Ok(Some(Statement::Return(ReturnSpan { pos: token_span.pos }))),
1787            Token::Symbol(vref) => {
1788                let peeked = self.lexer.peek()?;
1789                if peeked.token == Token::Equal {
1790                    self.lexer.consume_peeked();
1791                    Ok(Some(self.parse_assignment(vref, token_span.pos)?))
1792                } else {
1793                    Ok(Some(self.parse_array_or_builtin_call(vref, token_span.pos)?))
1794                }
1795            }
1796            Token::Bad(msg) => Err(Error::Bad(token_span.pos, msg)),
1797            t => Err(Error::Bad(token_span.pos, format!("Unexpected {} in uniline IF branch", t))),
1798        }
1799    }
1800
1801    /// Extracts the next available statement from the input stream, or `None` if none is available.
1802    ///
1803    /// On success, the stream is left in a position where the next statement can be extracted.
1804    /// On failure, the caller must advance the stream to the next statement by calling `reset`.
1805    fn parse_one(&mut self) -> Result<Option<Statement>> {
1806        loop {
1807            match self.lexer.peek()?.token {
1808                Token::Eol => {
1809                    self.lexer.consume_peeked();
1810                }
1811                Token::Eof => return Ok(None),
1812                _ => break,
1813            }
1814        }
1815        let token_span = self.lexer.read()?;
1816        let res = match token_span.token {
1817            Token::Data => Ok(Some(self.parse_data()?)),
1818            Token::Dim => Ok(Some(self.parse_dim()?)),
1819            Token::Do => {
1820                let result = self.parse_do(token_span.pos);
1821                if result.is_err() {
1822                    self.reset_do()?;
1823                }
1824                Ok(Some(result?))
1825            }
1826            Token::End => Ok(Some(self.parse_end(token_span.pos)?)),
1827            Token::Eof => return Ok(None),
1828            Token::Eol => Ok(None),
1829            Token::Exit => Ok(Some(self.parse_exit_do(token_span.pos)?)),
1830            Token::If => {
1831                let result = self.parse_if(token_span.pos);
1832                if result.is_err() {
1833                    self.reset_if(token_span.pos)?;
1834                }
1835                Ok(Some(result?))
1836            }
1837            Token::For => {
1838                let result = self.parse_for(token_span.pos);
1839                if result.is_err() {
1840                    self.reset_for()?;
1841                }
1842                Ok(Some(result?))
1843            }
1844            Token::Function => {
1845                let result = self.parse_function(token_span.pos);
1846                if result.is_err() {
1847                    self.reset_callable(Token::Function)?;
1848                }
1849                Ok(Some(result?))
1850            }
1851            Token::Gosub => {
1852                let result = self.parse_gosub();
1853                Ok(Some(result?))
1854            }
1855            Token::Goto => {
1856                let result = self.parse_goto();
1857                Ok(Some(result?))
1858            }
1859            Token::Integer(i) => {
1860                let name = format!("{}", i);
1861                // When we encounter a line number, we must return early to avoid looking for a line
1862                // ending given that the next statement may start after the label we found.
1863                return Ok(Some(Statement::Label(LabelSpan { name, name_pos: token_span.pos })));
1864            }
1865            Token::Label(name) => {
1866                // When we encounter a label, we must return early to avoid looking for a line
1867                // ending given that the next statement may start after the label we found.
1868                return Ok(Some(Statement::Label(LabelSpan { name, name_pos: token_span.pos })));
1869            }
1870            Token::On => Ok(Some(self.parse_on()?)),
1871            Token::Return => Ok(Some(Statement::Return(ReturnSpan { pos: token_span.pos }))),
1872            Token::Select => {
1873                let result = self.parse_select(token_span.pos);
1874                if result.is_err() {
1875                    self.reset_select(token_span.pos)?;
1876                }
1877                Ok(Some(result?))
1878            }
1879            Token::Sub => {
1880                let result = self.parse_sub(token_span.pos);
1881                if result.is_err() {
1882                    self.reset_callable(Token::Sub)?;
1883                }
1884                Ok(Some(result?))
1885            }
1886            Token::Symbol(vref) => {
1887                let peeked = self.lexer.peek()?;
1888                if peeked.token == Token::Equal {
1889                    self.lexer.consume_peeked();
1890                    Ok(Some(self.parse_assignment(vref, token_span.pos)?))
1891                } else {
1892                    Ok(Some(self.parse_array_or_builtin_call(vref, token_span.pos)?))
1893                }
1894            }
1895            Token::While => {
1896                let result = self.parse_while(token_span.pos);
1897                if result.is_err() {
1898                    self.reset_while()?;
1899                }
1900                Ok(Some(result?))
1901            }
1902            Token::Bad(msg) => return Err(Error::Bad(token_span.pos, msg)),
1903            t => return Err(Error::Bad(token_span.pos, format!("Unexpected {} in statement", t))),
1904        };
1905
1906        let token_span = self.lexer.peek()?;
1907        match token_span.token {
1908            Token::Eof => (),
1909            Token::Eol => {
1910                self.lexer.consume_peeked();
1911            }
1912            _ => {
1913                return Err(Error::Bad(
1914                    token_span.pos,
1915                    format!("Expected newline but found {}", token_span.token),
1916                ))
1917            }
1918        };
1919
1920        res
1921    }
1922
1923    /// Advances until the next statement after failing to parse a single statement.
1924    fn reset(&mut self) -> Result<()> {
1925        loop {
1926            match self.lexer.peek()?.token {
1927                Token::Eof => break,
1928                Token::Eol => {
1929                    self.lexer.consume_peeked();
1930                    break;
1931                }
1932                _ => {
1933                    self.lexer.consume_peeked();
1934                }
1935            }
1936        }
1937        Ok(())
1938    }
1939
1940    /// Extracts the next available statement from the input stream, or `None` if none is available.
1941    ///
1942    /// The stream is always left in a position where the next statement extraction can be tried.
1943    fn parse_one_safe(&mut self) -> Result<Option<Statement>> {
1944        let result = self.parse_one();
1945        if result.is_err() {
1946            self.reset()?;
1947        }
1948        result
1949    }
1950}
1951
1952/// Extracts all statements from the input stream.
1953pub fn parse(input: &mut dyn io::Read) -> Result<Vec<Statement>> {
1954    let mut parser = Parser::from(input);
1955    let mut statements = vec![];
1956    while let Some(statement) = parser.parse_one_safe()? {
1957        statements.push(statement);
1958    }
1959    Ok(statements)
1960}
1961
1962#[cfg(test)]
1963mod tests {
1964    use super::*;
1965    use crate::ast::ExprType;
1966
1967    /// Syntactic sugar to instantiate a `LineCol` for testing.
1968    fn lc(line: usize, col: usize) -> LineCol {
1969        LineCol { line, col }
1970    }
1971
1972    /// Syntactic sugar to instantiate an `Expr::Boolean` for testing.
1973    fn expr_boolean(value: bool, line: usize, col: usize) -> Expr {
1974        Expr::Boolean(BooleanSpan { value, pos: LineCol { line, col } })
1975    }
1976
1977    /// Syntactic sugar to instantiate an `Expr::Double` for testing.
1978    fn expr_double(value: f64, line: usize, col: usize) -> Expr {
1979        Expr::Double(DoubleSpan { value, pos: LineCol { line, col } })
1980    }
1981
1982    /// Syntactic sugar to instantiate an `Expr::Integer` for testing.
1983    fn expr_integer(value: i32, line: usize, col: usize) -> Expr {
1984        Expr::Integer(IntegerSpan { value, pos: LineCol { line, col } })
1985    }
1986
1987    /// Syntactic sugar to instantiate an `Expr::Text` for testing.
1988    fn expr_text<S: Into<String>>(value: S, line: usize, col: usize) -> Expr {
1989        Expr::Text(TextSpan { value: value.into(), pos: LineCol { line, col } })
1990    }
1991
1992    /// Syntactic sugar to instantiate an `Expr::Symbol` for testing.
1993    fn expr_symbol(vref: VarRef, line: usize, col: usize) -> Expr {
1994        Expr::Symbol(SymbolSpan { vref, pos: LineCol { line, col } })
1995    }
1996
1997    #[test]
1998    fn test_varref_to_unannotated_string() {
1999        assert_eq!(
2000            "print",
2001            &vref_to_unannotated_string(VarRef::new("print", None), LineCol { line: 0, col: 0 })
2002                .unwrap()
2003        );
2004
2005        assert_eq!(
2006            "7:6: Type annotation not allowed in print$",
2007            format!(
2008                "{}",
2009                &vref_to_unannotated_string(
2010                    VarRef::new("print", Some(ExprType::Text)),
2011                    LineCol { line: 7, col: 6 }
2012                )
2013                .unwrap_err()
2014            )
2015        );
2016    }
2017
2018    /// Runs the parser on the given `input` and expects the returned statements to match
2019    /// `exp_statements`.
2020    fn do_ok_test(input: &str, exp_statements: &[Statement]) {
2021        let mut input = input.as_bytes();
2022        let statements = parse(&mut input).expect("Parsing failed");
2023        assert_eq!(exp_statements, statements.as_slice());
2024    }
2025
2026    /// Runs the parser on the given `input` and expects the `err` error message.
2027    fn do_error_test(input: &str, expected_err: &str) {
2028        let mut input = input.as_bytes();
2029        let mut parser = Parser::from(&mut input);
2030        assert_eq!(
2031            expected_err,
2032            format!("{}", parser.parse_one_safe().expect_err("Parsing did not fail"))
2033        );
2034        assert!(parser.parse_one_safe().unwrap().is_none());
2035    }
2036
2037    /// Runs the parser on the given `input` and expects the `err` error message.
2038    ///
2039    /// Does not expect the parser to be reset to the next (EOF) statement.
2040    // TODO(jmmv): Need better testing to ensure the parser is reset to something that can be
2041    // parsed next.
2042    fn do_error_test_no_reset(input: &str, expected_err: &str) {
2043        let mut input = input.as_bytes();
2044        assert_eq!(
2045            expected_err,
2046            format!("{}", parse(&mut input).expect_err("Parsing did not fail"))
2047        );
2048    }
2049
2050    #[test]
2051    fn test_empty() {
2052        do_ok_test("", &[]);
2053    }
2054
2055    #[test]
2056    fn test_statement_separators() {
2057        do_ok_test(
2058            "a=1\nb=2:c=3:' A comment: that follows\nd=4",
2059            &[
2060                Statement::Assignment(AssignmentSpan {
2061                    vref: VarRef::new("a", None),
2062                    vref_pos: lc(1, 1),
2063                    expr: expr_integer(1, 1, 3),
2064                }),
2065                Statement::Assignment(AssignmentSpan {
2066                    vref: VarRef::new("b", None),
2067                    vref_pos: lc(2, 1),
2068                    expr: expr_integer(2, 2, 3),
2069                }),
2070                Statement::Assignment(AssignmentSpan {
2071                    vref: VarRef::new("c", None),
2072                    vref_pos: lc(2, 5),
2073                    expr: expr_integer(3, 2, 7),
2074                }),
2075                Statement::Assignment(AssignmentSpan {
2076                    vref: VarRef::new("d", None),
2077                    vref_pos: lc(3, 1),
2078                    expr: expr_integer(4, 3, 3),
2079                }),
2080            ],
2081        );
2082    }
2083
2084    #[test]
2085    fn test_array_assignments() {
2086        do_ok_test(
2087            "a(1)=100\nfoo(2, 3)=\"text\"\nabc$ (5 + z, 6) = TRUE OR FALSE",
2088            &[
2089                Statement::ArrayAssignment(ArrayAssignmentSpan {
2090                    vref: VarRef::new("a", None),
2091                    vref_pos: lc(1, 1),
2092                    subscripts: vec![expr_integer(1, 1, 3)],
2093                    expr: expr_integer(100, 1, 6),
2094                }),
2095                Statement::ArrayAssignment(ArrayAssignmentSpan {
2096                    vref: VarRef::new("foo", None),
2097                    vref_pos: lc(2, 1),
2098                    subscripts: vec![expr_integer(2, 2, 5), expr_integer(3, 2, 8)],
2099                    expr: expr_text("text", 2, 11),
2100                }),
2101                Statement::ArrayAssignment(ArrayAssignmentSpan {
2102                    vref: VarRef::new("abc", Some(ExprType::Text)),
2103                    vref_pos: lc(3, 1),
2104                    subscripts: vec![
2105                        Expr::Add(Box::from(BinaryOpSpan {
2106                            lhs: expr_integer(5, 3, 7),
2107                            rhs: expr_symbol(VarRef::new("z".to_owned(), None), 3, 11),
2108                            pos: lc(3, 9),
2109                        })),
2110                        expr_integer(6, 3, 14),
2111                    ],
2112                    expr: Expr::Or(Box::from(BinaryOpSpan {
2113                        lhs: expr_boolean(true, 3, 19),
2114                        rhs: expr_boolean(false, 3, 27),
2115                        pos: lc(3, 24),
2116                    })),
2117                }),
2118            ],
2119        );
2120    }
2121
2122    #[test]
2123    fn test_array_assignment_errors() {
2124        do_error_test("a(", "1:3: Unexpected <<EOF>>");
2125        do_error_test("a()", "1:2: Expected expression");
2126        do_error_test("a() =", "1:6: Missing expression in array assignment");
2127        do_error_test("a() IF", "1:2: Expected expression");
2128        do_error_test("a() = 3 4", "1:9: Unexpected value in expression");
2129        do_error_test("a() = 3 THEN", "1:9: Unexpected THEN in array assignment");
2130        do_error_test("a(,) = 3", "1:3: Missing expression");
2131        do_error_test("a(2;3) = 3", "1:4: Unexpected ;");
2132        do_error_test("(2) = 3", "1:1: Unexpected ( in statement");
2133    }
2134
2135    #[test]
2136    fn test_assignments() {
2137        do_ok_test(
2138            "a=1\nfoo$ = \"bar\"\nb$ = 3 + z",
2139            &[
2140                Statement::Assignment(AssignmentSpan {
2141                    vref: VarRef::new("a", None),
2142                    vref_pos: lc(1, 1),
2143                    expr: expr_integer(1, 1, 3),
2144                }),
2145                Statement::Assignment(AssignmentSpan {
2146                    vref: VarRef::new("foo", Some(ExprType::Text)),
2147                    vref_pos: lc(2, 1),
2148                    expr: expr_text("bar", 2, 8),
2149                }),
2150                Statement::Assignment(AssignmentSpan {
2151                    vref: VarRef::new("b", Some(ExprType::Text)),
2152                    vref_pos: lc(3, 1),
2153                    expr: Expr::Add(Box::from(BinaryOpSpan {
2154                        lhs: expr_integer(3, 3, 6),
2155                        rhs: expr_symbol(VarRef::new("z", None), 3, 10),
2156                        pos: lc(3, 8),
2157                    })),
2158                }),
2159            ],
2160        );
2161    }
2162
2163    #[test]
2164    fn test_assignment_errors() {
2165        do_error_test("a =", "1:4: Missing expression in assignment");
2166        do_error_test("a = b 3", "1:7: Unexpected value in expression");
2167        do_error_test("a = b, 3", "1:6: Unexpected , in assignment");
2168        do_error_test("a = if 3", "1:5: Unexpected keyword in expression");
2169        do_error_test("true = 1", "1:1: Unexpected TRUE in statement");
2170    }
2171
2172    #[test]
2173    fn test_builtin_calls() {
2174        do_ok_test(
2175            "PRINT a\nPRINT ; 3 , c$\nNOARGS\nNAME 3 AS 4",
2176            &[
2177                Statement::Call(CallSpan {
2178                    vref: VarRef::new("PRINT", None),
2179                    vref_pos: lc(1, 1),
2180                    args: vec![ArgSpan {
2181                        expr: Some(expr_symbol(VarRef::new("a", None), 1, 7)),
2182                        sep: ArgSep::End,
2183                        sep_pos: lc(1, 8),
2184                    }],
2185                }),
2186                Statement::Call(CallSpan {
2187                    vref: VarRef::new("PRINT", None),
2188                    vref_pos: lc(2, 1),
2189                    args: vec![
2190                        ArgSpan { expr: None, sep: ArgSep::Short, sep_pos: lc(2, 7) },
2191                        ArgSpan {
2192                            expr: Some(expr_integer(3, 2, 9)),
2193                            sep: ArgSep::Long,
2194                            sep_pos: lc(2, 11),
2195                        },
2196                        ArgSpan {
2197                            expr: Some(expr_symbol(VarRef::new("c", Some(ExprType::Text)), 2, 13)),
2198                            sep: ArgSep::End,
2199                            sep_pos: lc(2, 15),
2200                        },
2201                    ],
2202                }),
2203                Statement::Call(CallSpan {
2204                    vref: VarRef::new("NOARGS", None),
2205                    vref_pos: lc(3, 1),
2206                    args: vec![],
2207                }),
2208                Statement::Call(CallSpan {
2209                    vref: VarRef::new("NAME", None),
2210                    vref_pos: lc(4, 1),
2211                    args: vec![
2212                        ArgSpan {
2213                            expr: Some(expr_integer(3, 4, 6)),
2214                            sep: ArgSep::As,
2215                            sep_pos: lc(4, 8),
2216                        },
2217                        ArgSpan {
2218                            expr: Some(expr_integer(4, 4, 11)),
2219                            sep: ArgSep::End,
2220                            sep_pos: lc(4, 12),
2221                        },
2222                    ],
2223                }),
2224            ],
2225        );
2226    }
2227
2228    #[test]
2229    fn test_builtin_calls_and_array_references_disambiguation() {
2230        use Expr::*;
2231
2232        do_ok_test(
2233            "PRINT(1)",
2234            &[Statement::Call(CallSpan {
2235                vref: VarRef::new("PRINT", None),
2236                vref_pos: lc(1, 1),
2237                args: vec![ArgSpan {
2238                    expr: Some(expr_integer(1, 1, 7)),
2239                    sep: ArgSep::End,
2240                    sep_pos: lc(1, 9),
2241                }],
2242            })],
2243        );
2244
2245        do_ok_test(
2246            "PRINT(1), 2",
2247            &[Statement::Call(CallSpan {
2248                vref: VarRef::new("PRINT", None),
2249                vref_pos: lc(1, 1),
2250                args: vec![
2251                    ArgSpan {
2252                        expr: Some(expr_integer(1, 1, 7)),
2253                        sep: ArgSep::Long,
2254                        sep_pos: lc(1, 9),
2255                    },
2256                    ArgSpan {
2257                        expr: Some(expr_integer(2, 1, 11)),
2258                        sep: ArgSep::End,
2259                        sep_pos: lc(1, 12),
2260                    },
2261                ],
2262            })],
2263        );
2264
2265        do_ok_test(
2266            "PRINT(1); 2",
2267            &[Statement::Call(CallSpan {
2268                vref: VarRef::new("PRINT", None),
2269                vref_pos: lc(1, 1),
2270                args: vec![
2271                    ArgSpan {
2272                        expr: Some(expr_integer(1, 1, 7)),
2273                        sep: ArgSep::Short,
2274                        sep_pos: lc(1, 9),
2275                    },
2276                    ArgSpan {
2277                        expr: Some(expr_integer(2, 1, 11)),
2278                        sep: ArgSep::End,
2279                        sep_pos: lc(1, 12),
2280                    },
2281                ],
2282            })],
2283        );
2284
2285        do_ok_test(
2286            "PRINT(1);",
2287            &[Statement::Call(CallSpan {
2288                vref: VarRef::new("PRINT", None),
2289                vref_pos: lc(1, 1),
2290                args: vec![
2291                    ArgSpan {
2292                        expr: Some(expr_integer(1, 1, 7)),
2293                        sep: ArgSep::Short,
2294                        sep_pos: lc(1, 9),
2295                    },
2296                    ArgSpan { expr: None, sep: ArgSep::End, sep_pos: lc(1, 10) },
2297                ],
2298            })],
2299        );
2300
2301        do_ok_test(
2302            "PRINT(1) + 2; 3",
2303            &[Statement::Call(CallSpan {
2304                vref: VarRef::new("PRINT", None),
2305                vref_pos: lc(1, 1),
2306                args: vec![
2307                    ArgSpan {
2308                        expr: Some(Add(Box::from(BinaryOpSpan {
2309                            lhs: expr_integer(1, 1, 7),
2310                            rhs: expr_integer(2, 1, 12),
2311                            pos: lc(1, 10),
2312                        }))),
2313                        sep: ArgSep::Short,
2314                        sep_pos: lc(1, 13),
2315                    },
2316                    ArgSpan {
2317                        expr: Some(expr_integer(3, 1, 15)),
2318                        sep: ArgSep::End,
2319                        sep_pos: lc(1, 16),
2320                    },
2321                ],
2322            })],
2323        );
2324    }
2325
2326    #[test]
2327    fn test_builtin_calls_errors() {
2328        do_error_test("FOO 3 5\n", "1:7: Unexpected value in expression");
2329        do_error_test("INPUT$ a\n", "1:1: Type annotation not allowed in INPUT$");
2330        do_error_test("PRINT IF 1\n", "1:7: Unexpected keyword in expression");
2331        do_error_test("PRINT 3, IF 1\n", "1:10: Unexpected keyword in expression");
2332        do_error_test("PRINT 3 THEN\n", "1:9: Expected comma, semicolon, or end of statement");
2333        do_error_test("PRINT ()\n", "1:7: Expected expression");
2334        do_error_test("PRINT (2, 3)\n", "1:7: Expected expression");
2335        do_error_test("PRINT (2, 3); 4\n", "1:7: Expected expression");
2336    }
2337
2338    #[test]
2339    fn test_data() {
2340        do_ok_test("DATA", &[Statement::Data(DataSpan { values: vec![None] })]);
2341
2342        do_ok_test("DATA , ", &[Statement::Data(DataSpan { values: vec![None, None] })]);
2343        do_ok_test(
2344            "DATA , , ,",
2345            &[Statement::Data(DataSpan { values: vec![None, None, None, None] })],
2346        );
2347
2348        do_ok_test(
2349            "DATA 1: DATA 2",
2350            &[
2351                Statement::Data(DataSpan { values: vec![Some(Value::Integer(1))] }),
2352                Statement::Data(DataSpan { values: vec![Some(Value::Integer(2))] }),
2353            ],
2354        );
2355
2356        do_ok_test(
2357            "DATA TRUE, -3, 5.1, \"foo\"",
2358            &[Statement::Data(DataSpan {
2359                values: vec![
2360                    Some(Value::Boolean(true)),
2361                    Some(Value::Integer(-3)),
2362                    Some(Value::Double(5.1)),
2363                    Some(Value::Text("foo".to_owned())),
2364                ],
2365            })],
2366        );
2367
2368        do_ok_test(
2369            "DATA , TRUE, , 3, , 5.1, , \"foo\",",
2370            &[Statement::Data(DataSpan {
2371                values: vec![
2372                    None,
2373                    Some(Value::Boolean(true)),
2374                    None,
2375                    Some(Value::Integer(3)),
2376                    None,
2377                    Some(Value::Double(5.1)),
2378                    None,
2379                    Some(Value::Text("foo".to_owned())),
2380                    None,
2381                ],
2382            })],
2383        );
2384
2385        do_ok_test(
2386            "DATA -3, -5.1",
2387            &[Statement::Data(DataSpan {
2388                values: vec![Some(Value::Integer(-3)), Some(Value::Double(-5.1))],
2389            })],
2390        );
2391    }
2392
2393    #[test]
2394    fn test_data_errors() {
2395        do_error_test("DATA + 2", "1:6: Unexpected + in DATA statement");
2396        do_error_test("DATA ;", "1:6: Unexpected ; in DATA statement");
2397        do_error_test("DATA 5 + 1", "1:8: Expected comma after datum but found +");
2398        do_error_test("DATA 5 ; 1", "1:8: Expected comma after datum but found ;");
2399        do_error_test("DATA -FALSE", "1:7: Expected number after -");
2400        do_error_test("DATA -\"abc\"", "1:7: Expected number after -");
2401        do_error_test("DATA -foo", "1:7: Expected number after -");
2402    }
2403
2404    #[test]
2405    fn test_dim_default_type() {
2406        do_ok_test(
2407            "DIM i",
2408            &[Statement::Dim(DimSpan {
2409                name: "i".to_owned(),
2410                name_pos: lc(1, 5),
2411                shared: false,
2412                vtype: ExprType::Integer,
2413                vtype_pos: lc(1, 6),
2414            })],
2415        );
2416    }
2417
2418    #[test]
2419    fn test_dim_as_simple_types() {
2420        do_ok_test(
2421            "DIM i AS BOOLEAN",
2422            &[Statement::Dim(DimSpan {
2423                name: "i".to_owned(),
2424                name_pos: lc(1, 5),
2425                shared: false,
2426                vtype: ExprType::Boolean,
2427                vtype_pos: lc(1, 10),
2428            })],
2429        );
2430        do_ok_test(
2431            "DIM i AS DOUBLE",
2432            &[Statement::Dim(DimSpan {
2433                name: "i".to_owned(),
2434                name_pos: lc(1, 5),
2435                shared: false,
2436                vtype: ExprType::Double,
2437                vtype_pos: lc(1, 10),
2438            })],
2439        );
2440        do_ok_test(
2441            "DIM i AS INTEGER",
2442            &[Statement::Dim(DimSpan {
2443                name: "i".to_owned(),
2444                name_pos: lc(1, 5),
2445                shared: false,
2446                vtype: ExprType::Integer,
2447                vtype_pos: lc(1, 10),
2448            })],
2449        );
2450        do_ok_test(
2451            "DIM i AS STRING",
2452            &[Statement::Dim(DimSpan {
2453                name: "i".to_owned(),
2454                name_pos: lc(1, 5),
2455                shared: false,
2456                vtype: ExprType::Text,
2457                vtype_pos: lc(1, 10),
2458            })],
2459        );
2460    }
2461
2462    #[test]
2463    fn test_dim_consecutive() {
2464        do_ok_test(
2465            "DIM i\nDIM j AS BOOLEAN\nDIM k",
2466            &[
2467                Statement::Dim(DimSpan {
2468                    name: "i".to_owned(),
2469                    name_pos: lc(1, 5),
2470                    shared: false,
2471                    vtype: ExprType::Integer,
2472                    vtype_pos: lc(1, 6),
2473                }),
2474                Statement::Dim(DimSpan {
2475                    name: "j".to_owned(),
2476                    name_pos: lc(2, 5),
2477                    shared: false,
2478                    vtype: ExprType::Boolean,
2479                    vtype_pos: lc(2, 10),
2480                }),
2481                Statement::Dim(DimSpan {
2482                    name: "k".to_owned(),
2483                    name_pos: lc(3, 5),
2484                    shared: false,
2485                    vtype: ExprType::Integer,
2486                    vtype_pos: lc(3, 6),
2487                }),
2488            ],
2489        );
2490    }
2491
2492    #[test]
2493    fn test_dim_shared() {
2494        do_ok_test(
2495            "DIM SHARED i",
2496            &[Statement::Dim(DimSpan {
2497                name: "i".to_owned(),
2498                name_pos: lc(1, 12),
2499                shared: true,
2500                vtype: ExprType::Integer,
2501                vtype_pos: lc(1, 13),
2502            })],
2503        );
2504        do_ok_test(
2505            "DIM SHARED i AS BOOLEAN",
2506            &[Statement::Dim(DimSpan {
2507                name: "i".to_owned(),
2508                name_pos: lc(1, 12),
2509                shared: true,
2510                vtype: ExprType::Boolean,
2511                vtype_pos: lc(1, 17),
2512            })],
2513        );
2514    }
2515
2516    #[test]
2517    fn test_dim_array() {
2518        use Expr::*;
2519
2520        do_ok_test(
2521            "DIM i(10)",
2522            &[Statement::DimArray(DimArraySpan {
2523                name: "i".to_owned(),
2524                name_pos: lc(1, 5),
2525                shared: false,
2526                dimensions: vec![expr_integer(10, 1, 7)],
2527                subtype: ExprType::Integer,
2528                subtype_pos: lc(1, 10),
2529            })],
2530        );
2531
2532        do_ok_test(
2533            "DIM foo(-5, 0) AS STRING",
2534            &[Statement::DimArray(DimArraySpan {
2535                name: "foo".to_owned(),
2536                name_pos: lc(1, 5),
2537                shared: false,
2538                dimensions: vec![
2539                    Negate(Box::from(UnaryOpSpan { expr: expr_integer(5, 1, 10), pos: lc(1, 9) })),
2540                    expr_integer(0, 1, 13),
2541                ],
2542                subtype: ExprType::Text,
2543                subtype_pos: lc(1, 19),
2544            })],
2545        );
2546
2547        do_ok_test(
2548            "DIM foo(bar$() + 3, 8, -1)",
2549            &[Statement::DimArray(DimArraySpan {
2550                name: "foo".to_owned(),
2551                name_pos: lc(1, 5),
2552                shared: false,
2553                dimensions: vec![
2554                    Add(Box::from(BinaryOpSpan {
2555                        lhs: Call(CallSpan {
2556                            vref: VarRef::new("bar", Some(ExprType::Text)),
2557                            vref_pos: lc(1, 9),
2558                            args: vec![],
2559                        }),
2560                        rhs: expr_integer(3, 1, 18),
2561                        pos: lc(1, 16),
2562                    })),
2563                    expr_integer(8, 1, 21),
2564                    Negate(Box::from(UnaryOpSpan { expr: expr_integer(1, 1, 25), pos: lc(1, 24) })),
2565                ],
2566                subtype: ExprType::Integer,
2567                subtype_pos: lc(1, 27),
2568            })],
2569        );
2570
2571        do_ok_test(
2572            "DIM SHARED i(10)",
2573            &[Statement::DimArray(DimArraySpan {
2574                name: "i".to_owned(),
2575                name_pos: lc(1, 12),
2576                shared: true,
2577                dimensions: vec![expr_integer(10, 1, 14)],
2578                subtype: ExprType::Integer,
2579                subtype_pos: lc(1, 17),
2580            })],
2581        );
2582    }
2583
2584    #[test]
2585    fn test_dim_errors() {
2586        do_error_test("DIM", "1:4: Expected variable name after DIM");
2587        do_error_test("DIM 3", "1:5: Expected variable name after DIM");
2588        do_error_test("DIM AS", "1:5: Expected variable name after DIM");
2589        do_error_test("DIM foo 3", "1:9: Expected AS or end of statement");
2590        do_error_test("DIM a AS", "1:9: Invalid type name <<EOF>> in AS type definition");
2591        do_error_test("DIM a$ AS", "1:5: Type annotation not allowed in a$");
2592        do_error_test("DIM a AS 3", "1:10: Invalid type name 3 in AS type definition");
2593        do_error_test("DIM a AS INTEGER 3", "1:18: Unexpected 3 in DIM statement");
2594
2595        do_error_test("DIM a()", "1:6: Arrays require at least one dimension");
2596        do_error_test("DIM a(,)", "1:7: Missing expression");
2597        do_error_test("DIM a(, 3)", "1:7: Missing expression");
2598        do_error_test("DIM a(3, )", "1:10: Missing expression");
2599        do_error_test("DIM a(3, , 4)", "1:10: Missing expression");
2600        do_error_test("DIM a(1) AS INTEGER 3", "1:21: Unexpected 3 in DIM statement");
2601    }
2602
2603    #[test]
2604    fn test_do_until_empty() {
2605        do_ok_test(
2606            "DO UNTIL TRUE\nLOOP",
2607            &[Statement::Do(DoSpan {
2608                guard: DoGuard::PreUntil(expr_boolean(true, 1, 10)),
2609                body: vec![],
2610            })],
2611        );
2612
2613        do_ok_test(
2614            "DO UNTIL FALSE\nREM foo\nLOOP",
2615            &[Statement::Do(DoSpan {
2616                guard: DoGuard::PreUntil(expr_boolean(false, 1, 10)),
2617                body: vec![],
2618            })],
2619        );
2620    }
2621
2622    #[test]
2623    fn test_do_infinite_empty() {
2624        do_ok_test("DO\nLOOP", &[Statement::Do(DoSpan { guard: DoGuard::Infinite, body: vec![] })]);
2625    }
2626
2627    #[test]
2628    fn test_do_pre_until_loops() {
2629        do_ok_test(
2630            "DO UNTIL TRUE\nA\nB\nLOOP",
2631            &[Statement::Do(DoSpan {
2632                guard: DoGuard::PreUntil(expr_boolean(true, 1, 10)),
2633                body: vec![make_bare_builtin_call("A", 2, 1), make_bare_builtin_call("B", 3, 1)],
2634            })],
2635        );
2636    }
2637
2638    #[test]
2639    fn test_do_pre_while_loops() {
2640        do_ok_test(
2641            "DO WHILE TRUE\nA\nB\nLOOP",
2642            &[Statement::Do(DoSpan {
2643                guard: DoGuard::PreWhile(expr_boolean(true, 1, 10)),
2644                body: vec![make_bare_builtin_call("A", 2, 1), make_bare_builtin_call("B", 3, 1)],
2645            })],
2646        );
2647    }
2648
2649    #[test]
2650    fn test_do_post_until_loops() {
2651        do_ok_test(
2652            "DO\nA\nB\nLOOP UNTIL TRUE",
2653            &[Statement::Do(DoSpan {
2654                guard: DoGuard::PostUntil(expr_boolean(true, 4, 12)),
2655
2656                body: vec![make_bare_builtin_call("A", 2, 1), make_bare_builtin_call("B", 3, 1)],
2657            })],
2658        );
2659    }
2660
2661    #[test]
2662    fn test_do_post_while_loops() {
2663        do_ok_test(
2664            "DO\nA\nB\nLOOP WHILE FALSE",
2665            &[Statement::Do(DoSpan {
2666                guard: DoGuard::PostWhile(expr_boolean(false, 4, 12)),
2667                body: vec![make_bare_builtin_call("A", 2, 1), make_bare_builtin_call("B", 3, 1)],
2668            })],
2669        );
2670    }
2671
2672    #[test]
2673    fn test_do_nested() {
2674        let code = r#"
2675            DO WHILE TRUE
2676                A
2677                DO
2678                    B
2679                LOOP UNTIL FALSE
2680                C
2681            LOOP
2682        "#;
2683        do_ok_test(
2684            code,
2685            &[Statement::Do(DoSpan {
2686                guard: DoGuard::PreWhile(expr_boolean(true, 2, 22)),
2687                body: vec![
2688                    make_bare_builtin_call("A", 3, 17),
2689                    Statement::Do(DoSpan {
2690                        guard: DoGuard::PostUntil(expr_boolean(false, 6, 28)),
2691                        body: vec![make_bare_builtin_call("B", 5, 21)],
2692                    }),
2693                    make_bare_builtin_call("C", 7, 17),
2694                ],
2695            })],
2696        );
2697    }
2698
2699    #[test]
2700    fn test_do_errors() {
2701        do_error_test("DO\n", "1:1: DO without LOOP");
2702        do_error_test("DO FOR\n", "1:4: Expecting newline, UNTIL or WHILE after DO");
2703
2704        do_error_test("\n\nDO UNTIL TRUE\n", "3:1: DO without LOOP");
2705        do_error_test("\n\nDO WHILE TRUE\n", "3:1: DO without LOOP");
2706        do_error_test("DO UNTIL TRUE\nEND", "1:1: DO without LOOP");
2707        do_error_test("DO WHILE TRUE\nEND", "1:1: DO without LOOP");
2708        do_error_test("DO UNTIL TRUE\nEND\n", "1:1: DO without LOOP");
2709        do_error_test("DO WHILE TRUE\nEND\n", "1:1: DO without LOOP");
2710        do_error_test("DO UNTIL TRUE\nEND WHILE\n", "2:5: Unexpected keyword in expression");
2711        do_error_test("DO WHILE TRUE\nEND WHILE\n", "2:5: Unexpected keyword in expression");
2712
2713        do_error_test("DO UNTIL\n", "1:9: No expression in UNTIL clause");
2714        do_error_test("DO WHILE\n", "1:9: No expression in WHILE clause");
2715        do_error_test("DO UNTIL TRUE", "1:14: Expecting newline after DO");
2716        do_error_test("DO WHILE TRUE", "1:14: Expecting newline after DO");
2717
2718        do_error_test("DO\nLOOP UNTIL", "2:11: No expression in UNTIL clause");
2719        do_error_test("DO\nLOOP WHILE\n", "2:11: No expression in WHILE clause");
2720
2721        do_error_test("DO UNTIL ,\nLOOP", "1:10: No expression in UNTIL clause");
2722        do_error_test("DO WHILE ,\nLOOP", "1:10: No expression in WHILE clause");
2723
2724        do_error_test("DO\nLOOP UNTIL ,\n", "2:12: No expression in UNTIL clause");
2725        do_error_test("DO\nLOOP WHILE ,\n", "2:12: No expression in WHILE clause");
2726
2727        do_error_test(
2728            "DO WHILE TRUE\nLOOP UNTIL FALSE",
2729            "1:1: DO loop cannot have pre and post guards at the same time",
2730        );
2731    }
2732
2733    #[test]
2734    fn test_exit_do() {
2735        do_ok_test("  EXIT DO", &[Statement::ExitDo(ExitDoSpan { pos: lc(1, 3) })]);
2736    }
2737
2738    #[test]
2739    fn test_exit_do_errors() {
2740        do_error_test("EXIT", "1:5: Expecting DO after EXIT");
2741        do_error_test("EXIT 5", "1:6: Expecting DO after EXIT");
2742    }
2743
2744    /// Wrapper around `do_ok_test` to parse an expression.  Given that expressions alone are not
2745    /// valid statements, we have to put them in a statement to parse them.  In doing so, we can
2746    /// also put an extra statement after them to ensure we detect their end properly.
2747    fn do_expr_ok_test(input: &str, expr: Expr) {
2748        do_ok_test(
2749            &format!("PRINT {}, 1", input),
2750            &[Statement::Call(CallSpan {
2751                vref: VarRef::new("PRINT", None),
2752                vref_pos: lc(1, 1),
2753                args: vec![
2754                    ArgSpan {
2755                        expr: Some(expr),
2756                        sep: ArgSep::Long,
2757                        sep_pos: lc(1, 7 + input.len()),
2758                    },
2759                    ArgSpan {
2760                        expr: Some(expr_integer(1, 1, 6 + input.len() + 3)),
2761                        sep: ArgSep::End,
2762                        sep_pos: lc(1, 10 + input.len()),
2763                    },
2764                ],
2765            })],
2766        );
2767    }
2768
2769    /// Wrapper around `do_error_test` to parse an expression.  Given that expressions alone are not
2770    /// valid statements, we have to put them in a statement to parse them.  In doing so, we can
2771    /// also put an extra statement after them to ensure we detect their end properly.
2772    fn do_expr_error_test(input: &str, msg: &str) {
2773        do_error_test(&format!("PRINT {}, 1", input), msg)
2774    }
2775
2776    #[test]
2777    fn test_expr_literals() {
2778        do_expr_ok_test("TRUE", expr_boolean(true, 1, 7));
2779        do_expr_ok_test("FALSE", expr_boolean(false, 1, 7));
2780        do_expr_ok_test("5", expr_integer(5, 1, 7));
2781        do_expr_ok_test("\"some text\"", expr_text("some text", 1, 7));
2782    }
2783
2784    #[test]
2785    fn test_expr_symbols() {
2786        do_expr_ok_test("foo", expr_symbol(VarRef::new("foo", None), 1, 7));
2787        do_expr_ok_test("bar$", expr_symbol(VarRef::new("bar", Some(ExprType::Text)), 1, 7));
2788    }
2789
2790    #[test]
2791    fn test_expr_parens() {
2792        use Expr::*;
2793        do_expr_ok_test("(1)", expr_integer(1, 1, 8));
2794        do_expr_ok_test("((1))", expr_integer(1, 1, 9));
2795        do_expr_ok_test(" ( ( 1 ) ) ", expr_integer(1, 1, 12));
2796        do_expr_ok_test(
2797            "3 * (2 + 5)",
2798            Multiply(Box::from(BinaryOpSpan {
2799                lhs: expr_integer(3, 1, 7),
2800                rhs: Add(Box::from(BinaryOpSpan {
2801                    lhs: expr_integer(2, 1, 12),
2802                    rhs: expr_integer(5, 1, 16),
2803                    pos: lc(1, 14),
2804                })),
2805                pos: lc(1, 9),
2806            })),
2807        );
2808        do_expr_ok_test(
2809            "(7) - (1) + (-2)",
2810            Add(Box::from(BinaryOpSpan {
2811                lhs: Subtract(Box::from(BinaryOpSpan {
2812                    lhs: expr_integer(7, 1, 8),
2813                    rhs: expr_integer(1, 1, 14),
2814                    pos: lc(1, 11),
2815                })),
2816                rhs: Negate(Box::from(UnaryOpSpan {
2817                    expr: expr_integer(2, 1, 21),
2818                    pos: lc(1, 20),
2819                })),
2820                pos: lc(1, 17),
2821            })),
2822        );
2823    }
2824
2825    #[test]
2826    fn test_expr_arith_ops() {
2827        use Expr::*;
2828        let span = Box::from(BinaryOpSpan {
2829            lhs: expr_integer(1, 1, 7),
2830            rhs: expr_integer(2, 1, 11),
2831            pos: lc(1, 9),
2832        });
2833        do_expr_ok_test("1 + 2", Add(span.clone()));
2834        do_expr_ok_test("1 - 2", Subtract(span.clone()));
2835        do_expr_ok_test("1 * 2", Multiply(span.clone()));
2836        do_expr_ok_test("1 / 2", Divide(span.clone()));
2837        do_expr_ok_test("1 ^ 2", Power(span));
2838        let span = Box::from(BinaryOpSpan {
2839            lhs: expr_integer(1, 1, 7),
2840            rhs: expr_integer(2, 1, 13),
2841            pos: lc(1, 9),
2842        });
2843        do_expr_ok_test("1 MOD 2", Modulo(span));
2844    }
2845
2846    #[test]
2847    fn test_expr_rel_ops() {
2848        use Expr::*;
2849        let span1 = Box::from(BinaryOpSpan {
2850            lhs: expr_integer(1, 1, 7),
2851            rhs: expr_integer(2, 1, 11),
2852            pos: lc(1, 9),
2853        });
2854        let span2 = Box::from(BinaryOpSpan {
2855            lhs: expr_integer(1, 1, 7),
2856            rhs: expr_integer(2, 1, 12),
2857            pos: lc(1, 9),
2858        });
2859        do_expr_ok_test("1 = 2", Equal(span1.clone()));
2860        do_expr_ok_test("1 <> 2", NotEqual(span2.clone()));
2861        do_expr_ok_test("1 < 2", Less(span1.clone()));
2862        do_expr_ok_test("1 <= 2", LessEqual(span2.clone()));
2863        do_expr_ok_test("1 > 2", Greater(span1));
2864        do_expr_ok_test("1 >= 2", GreaterEqual(span2));
2865    }
2866
2867    #[test]
2868    fn test_expr_logical_ops() {
2869        use Expr::*;
2870        do_expr_ok_test(
2871            "1 AND 2",
2872            And(Box::from(BinaryOpSpan {
2873                lhs: expr_integer(1, 1, 7),
2874                rhs: expr_integer(2, 1, 13),
2875                pos: lc(1, 9),
2876            })),
2877        );
2878        do_expr_ok_test(
2879            "1 OR 2",
2880            Or(Box::from(BinaryOpSpan {
2881                lhs: expr_integer(1, 1, 7),
2882                rhs: expr_integer(2, 1, 12),
2883                pos: lc(1, 9),
2884            })),
2885        );
2886        do_expr_ok_test(
2887            "1 XOR 2",
2888            Xor(Box::from(BinaryOpSpan {
2889                lhs: expr_integer(1, 1, 7),
2890                rhs: expr_integer(2, 1, 13),
2891                pos: lc(1, 9),
2892            })),
2893        );
2894    }
2895
2896    #[test]
2897    fn test_expr_logical_ops_not() {
2898        use Expr::*;
2899        do_expr_ok_test(
2900            "NOT TRUE",
2901            Not(Box::from(UnaryOpSpan { expr: expr_boolean(true, 1, 11), pos: lc(1, 7) })),
2902        );
2903        do_expr_ok_test(
2904            "NOT 6",
2905            Not(Box::from(UnaryOpSpan { expr: expr_integer(6, 1, 11), pos: lc(1, 7) })),
2906        );
2907        do_expr_ok_test(
2908            "NOT NOT TRUE",
2909            Not(Box::from(UnaryOpSpan {
2910                expr: Not(Box::from(UnaryOpSpan {
2911                    expr: expr_boolean(true, 1, 15),
2912                    pos: lc(1, 11),
2913                })),
2914                pos: lc(1, 7),
2915            })),
2916        );
2917        do_expr_ok_test(
2918            "1 - NOT 4",
2919            Subtract(Box::from(BinaryOpSpan {
2920                lhs: expr_integer(1, 1, 7),
2921                rhs: Not(Box::from(UnaryOpSpan { expr: expr_integer(4, 1, 15), pos: lc(1, 11) })),
2922                pos: lc(1, 9),
2923            })),
2924        );
2925    }
2926
2927    #[test]
2928    fn test_expr_bitwise_ops() {
2929        use Expr::*;
2930        do_expr_ok_test(
2931            "1 << 2",
2932            ShiftLeft(Box::from(BinaryOpSpan {
2933                lhs: expr_integer(1, 1, 7),
2934                rhs: expr_integer(2, 1, 12),
2935                pos: lc(1, 9),
2936            })),
2937        );
2938        do_expr_ok_test(
2939            "1 >> 2",
2940            ShiftRight(Box::from(BinaryOpSpan {
2941                lhs: expr_integer(1, 1, 7),
2942                rhs: expr_integer(2, 1, 12),
2943                pos: lc(1, 9),
2944            })),
2945        );
2946    }
2947
2948    #[test]
2949    fn test_expr_op_priorities() {
2950        use Expr::*;
2951        do_expr_ok_test(
2952            "3 * (2 + 5) = (3 + 1 = 2 OR 1 = 3 XOR FALSE * \"a\")",
2953            Equal(Box::from(BinaryOpSpan {
2954                lhs: Multiply(Box::from(BinaryOpSpan {
2955                    lhs: expr_integer(3, 1, 7),
2956                    rhs: Add(Box::from(BinaryOpSpan {
2957                        lhs: expr_integer(2, 1, 12),
2958                        rhs: expr_integer(5, 1, 16),
2959                        pos: lc(1, 14),
2960                    })),
2961                    pos: lc(1, 9),
2962                })),
2963                rhs: Xor(Box::from(BinaryOpSpan {
2964                    lhs: Or(Box::from(BinaryOpSpan {
2965                        lhs: Equal(Box::from(BinaryOpSpan {
2966                            lhs: Add(Box::from(BinaryOpSpan {
2967                                lhs: expr_integer(3, 1, 22),
2968                                rhs: expr_integer(1, 1, 26),
2969                                pos: lc(1, 24),
2970                            })),
2971                            rhs: expr_integer(2, 1, 30),
2972                            pos: lc(1, 28),
2973                        })),
2974                        rhs: Equal(Box::from(BinaryOpSpan {
2975                            lhs: expr_integer(1, 1, 35),
2976                            rhs: expr_integer(3, 1, 39),
2977                            pos: lc(1, 37),
2978                        })),
2979                        pos: lc(1, 32),
2980                    })),
2981                    rhs: Multiply(Box::from(BinaryOpSpan {
2982                        lhs: expr_boolean(false, 1, 45),
2983                        rhs: expr_text("a", 1, 53),
2984                        pos: lc(1, 51),
2985                    })),
2986                    pos: lc(1, 41),
2987                })),
2988                pos: lc(1, 19),
2989            })),
2990        );
2991        do_expr_ok_test(
2992            "-1 ^ 3",
2993            Negate(Box::from(UnaryOpSpan {
2994                expr: Power(Box::from(BinaryOpSpan {
2995                    lhs: expr_integer(1, 1, 8),
2996                    rhs: expr_integer(3, 1, 12),
2997                    pos: lc(1, 10),
2998                })),
2999                pos: lc(1, 7),
3000            })),
3001        );
3002        do_expr_ok_test(
3003            "-(1 ^ 3)",
3004            Negate(Box::from(UnaryOpSpan {
3005                expr: Power(Box::from(BinaryOpSpan {
3006                    lhs: expr_integer(1, 1, 9),
3007                    rhs: expr_integer(3, 1, 13),
3008                    pos: lc(1, 11),
3009                })),
3010                pos: lc(1, 7),
3011            })),
3012        );
3013        do_expr_ok_test(
3014            "(-1) ^ 3",
3015            Power(Box::from(BinaryOpSpan {
3016                lhs: Negate(Box::from(UnaryOpSpan { expr: expr_integer(1, 1, 9), pos: lc(1, 8) })),
3017                rhs: expr_integer(3, 1, 14),
3018                pos: lc(1, 12),
3019            })),
3020        );
3021        do_expr_ok_test(
3022            "1 ^ (-3)",
3023            Power(Box::from(BinaryOpSpan {
3024                lhs: expr_integer(1, 1, 7),
3025                rhs: Negate(Box::from(UnaryOpSpan {
3026                    expr: expr_integer(3, 1, 13),
3027                    pos: lc(1, 12),
3028                })),
3029                pos: lc(1, 9),
3030            })),
3031        );
3032        do_expr_ok_test(
3033            "0 <> 2 >> 1",
3034            NotEqual(Box::from(BinaryOpSpan {
3035                lhs: expr_integer(0, 1, 7),
3036                rhs: ShiftRight(Box::from(BinaryOpSpan {
3037                    lhs: expr_integer(2, 1, 12),
3038                    rhs: expr_integer(1, 1, 17),
3039                    pos: lc(1, 14),
3040                })),
3041                pos: lc(1, 9),
3042            })),
3043        );
3044    }
3045
3046    #[test]
3047    fn test_expr_numeric_signs() {
3048        use Expr::*;
3049
3050        do_expr_ok_test(
3051            "-a",
3052            Negate(Box::from(UnaryOpSpan {
3053                expr: expr_symbol(VarRef::new("a", None), 1, 8),
3054                pos: lc(1, 7),
3055            })),
3056        );
3057
3058        do_expr_ok_test(
3059            "1 - -3",
3060            Subtract(Box::from(BinaryOpSpan {
3061                lhs: expr_integer(1, 1, 7),
3062                rhs: Negate(Box::from(UnaryOpSpan {
3063                    expr: expr_integer(3, 1, 12),
3064                    pos: lc(1, 11),
3065                })),
3066                pos: lc(1, 9),
3067            })),
3068        );
3069        do_expr_ok_test(
3070            "-1 - 3",
3071            Subtract(Box::from(BinaryOpSpan {
3072                lhs: Negate(Box::from(UnaryOpSpan { expr: expr_integer(1, 1, 8), pos: lc(1, 7) })),
3073                rhs: expr_integer(3, 1, 12),
3074                pos: lc(1, 10),
3075            })),
3076        );
3077        do_expr_ok_test(
3078            "5 + -1",
3079            Add(Box::from(BinaryOpSpan {
3080                lhs: expr_integer(5, 1, 7),
3081                rhs: Negate(Box::from(UnaryOpSpan {
3082                    expr: expr_integer(1, 1, 12),
3083                    pos: lc(1, 11),
3084                })),
3085                pos: lc(1, 9),
3086            })),
3087        );
3088        do_expr_ok_test(
3089            "-5 + 1",
3090            Add(Box::from(BinaryOpSpan {
3091                lhs: Negate(Box::from(UnaryOpSpan { expr: expr_integer(5, 1, 8), pos: lc(1, 7) })),
3092                rhs: expr_integer(1, 1, 12),
3093                pos: lc(1, 10),
3094            })),
3095        );
3096        do_expr_ok_test(
3097            "NOT -3",
3098            Not(Box::from(UnaryOpSpan {
3099                expr: Negate(Box::from(UnaryOpSpan {
3100                    expr: expr_integer(3, 1, 12),
3101                    pos: lc(1, 11),
3102                })),
3103                pos: lc(1, 7),
3104            })),
3105        );
3106
3107        do_expr_ok_test(
3108            "1.0 - -3.5",
3109            Subtract(Box::from(BinaryOpSpan {
3110                lhs: expr_double(1.0, 1, 7),
3111                rhs: Negate(Box::from(UnaryOpSpan {
3112                    expr: expr_double(3.5, 1, 14),
3113                    pos: lc(1, 13),
3114                })),
3115                pos: lc(1, 11),
3116            })),
3117        );
3118        do_expr_ok_test(
3119            "5.12 + -0.50",
3120            Add(Box::from(BinaryOpSpan {
3121                lhs: expr_double(5.12, 1, 7),
3122                rhs: Negate(Box::from(UnaryOpSpan {
3123                    expr: expr_double(0.50, 1, 15),
3124                    pos: lc(1, 14),
3125                })),
3126                pos: lc(1, 12),
3127            })),
3128        );
3129        do_expr_ok_test(
3130            "NOT -3",
3131            Not(Box::from(UnaryOpSpan {
3132                expr: Negate(Box::from(UnaryOpSpan {
3133                    expr: expr_integer(3, 1, 12),
3134                    pos: lc(1, 11),
3135                })),
3136                pos: lc(1, 7),
3137            })),
3138        );
3139    }
3140
3141    #[test]
3142    fn test_expr_functions_variadic() {
3143        use Expr::*;
3144        do_expr_ok_test(
3145            "zero()",
3146            Call(CallSpan { vref: VarRef::new("zero", None), vref_pos: lc(1, 7), args: vec![] }),
3147        );
3148        do_expr_ok_test(
3149            "one%(1)",
3150            Call(CallSpan {
3151                vref: VarRef::new("one", Some(ExprType::Integer)),
3152                vref_pos: lc(1, 7),
3153                args: vec![ArgSpan {
3154                    expr: Some(expr_integer(1, 1, 12)),
3155                    sep: ArgSep::End,
3156                    sep_pos: lc(1, 13),
3157                }],
3158            }),
3159        );
3160        do_expr_ok_test(
3161            "many$(3, \"x\", TRUE)",
3162            Call(CallSpan {
3163                vref: VarRef::new("many", Some(ExprType::Text)),
3164                vref_pos: lc(1, 7),
3165                args: vec![
3166                    ArgSpan {
3167                        expr: Some(expr_integer(3, 1, 13)),
3168                        sep: ArgSep::Long,
3169                        sep_pos: lc(1, 14),
3170                    },
3171                    ArgSpan {
3172                        expr: Some(expr_text("x", 1, 16)),
3173                        sep: ArgSep::Long,
3174                        sep_pos: lc(1, 19),
3175                    },
3176                    ArgSpan {
3177                        expr: Some(expr_boolean(true, 1, 21)),
3178                        sep: ArgSep::End,
3179                        sep_pos: lc(1, 25),
3180                    },
3181                ],
3182            }),
3183        );
3184    }
3185
3186    #[test]
3187    fn test_expr_functions_nested() {
3188        use Expr::*;
3189        do_expr_ok_test(
3190            "consecutive(parenthesis())",
3191            Call(CallSpan {
3192                vref: VarRef::new("consecutive", None),
3193                vref_pos: lc(1, 7),
3194                args: vec![ArgSpan {
3195                    expr: Some(Call(CallSpan {
3196                        vref: VarRef::new("parenthesis", None),
3197                        vref_pos: lc(1, 19),
3198                        args: vec![],
3199                    })),
3200                    sep: ArgSep::End,
3201                    sep_pos: lc(1, 32),
3202                }],
3203            }),
3204        );
3205        do_expr_ok_test(
3206            "outer?(1, inner1(2, 3), 4, inner2(), 5)",
3207            Call(CallSpan {
3208                vref: VarRef::new("outer", Some(ExprType::Boolean)),
3209                vref_pos: lc(1, 7),
3210                args: vec![
3211                    ArgSpan {
3212                        expr: Some(expr_integer(1, 1, 14)),
3213                        sep: ArgSep::Long,
3214                        sep_pos: lc(1, 15),
3215                    },
3216                    ArgSpan {
3217                        expr: Some(Call(CallSpan {
3218                            vref: VarRef::new("inner1", None),
3219                            vref_pos: lc(1, 17),
3220                            args: vec![
3221                                ArgSpan {
3222                                    expr: Some(expr_integer(2, 1, 24)),
3223                                    sep: ArgSep::Long,
3224                                    sep_pos: lc(1, 25),
3225                                },
3226                                ArgSpan {
3227                                    expr: Some(expr_integer(3, 1, 27)),
3228                                    sep: ArgSep::End,
3229                                    sep_pos: lc(1, 28),
3230                                },
3231                            ],
3232                        })),
3233                        sep: ArgSep::Long,
3234                        sep_pos: lc(1, 29),
3235                    },
3236                    ArgSpan {
3237                        expr: Some(expr_integer(4, 1, 31)),
3238                        sep: ArgSep::Long,
3239                        sep_pos: lc(1, 32),
3240                    },
3241                    ArgSpan {
3242                        expr: Some(Call(CallSpan {
3243                            vref: VarRef::new("inner2", None),
3244                            vref_pos: lc(1, 34),
3245                            args: vec![],
3246                        })),
3247                        sep: ArgSep::Long,
3248                        sep_pos: lc(1, 42),
3249                    },
3250                    ArgSpan {
3251                        expr: Some(expr_integer(5, 1, 44)),
3252                        sep: ArgSep::End,
3253                        sep_pos: lc(1, 45),
3254                    },
3255                ],
3256            }),
3257        );
3258    }
3259
3260    #[test]
3261    fn test_expr_functions_and_ops() {
3262        use Expr::*;
3263        do_expr_ok_test(
3264            "b AND ask?(34 + 15, ask(1, FALSE), -5)",
3265            And(Box::from(BinaryOpSpan {
3266                lhs: expr_symbol(VarRef::new("b".to_owned(), None), 1, 7),
3267                rhs: Call(CallSpan {
3268                    vref: VarRef::new("ask", Some(ExprType::Boolean)),
3269                    vref_pos: lc(1, 13),
3270                    args: vec![
3271                        ArgSpan {
3272                            expr: Some(Add(Box::from(BinaryOpSpan {
3273                                lhs: expr_integer(34, 1, 18),
3274                                rhs: expr_integer(15, 1, 23),
3275                                pos: lc(1, 21),
3276                            }))),
3277                            sep: ArgSep::Long,
3278                            sep_pos: lc(1, 25),
3279                        },
3280                        ArgSpan {
3281                            expr: Some(Call(CallSpan {
3282                                vref: VarRef::new("ask", None),
3283                                vref_pos: lc(1, 27),
3284                                args: vec![
3285                                    ArgSpan {
3286                                        expr: Some(expr_integer(1, 1, 31)),
3287                                        sep: ArgSep::Long,
3288                                        sep_pos: lc(1, 32),
3289                                    },
3290                                    ArgSpan {
3291                                        expr: Some(expr_boolean(false, 1, 34)),
3292                                        sep: ArgSep::End,
3293                                        sep_pos: lc(1, 39),
3294                                    },
3295                                ],
3296                            })),
3297                            sep: ArgSep::Long,
3298                            sep_pos: lc(1, 40),
3299                        },
3300                        ArgSpan {
3301                            expr: Some(Negate(Box::from(UnaryOpSpan {
3302                                expr: expr_integer(5, 1, 43),
3303                                pos: lc(1, 42),
3304                            }))),
3305                            sep: ArgSep::End,
3306                            sep_pos: lc(1, 44),
3307                        },
3308                    ],
3309                }),
3310                pos: lc(1, 9),
3311            })),
3312        );
3313    }
3314
3315    #[test]
3316    fn test_expr_functions_not_confused_with_symbols() {
3317        use Expr::*;
3318        let iref = VarRef::new("i", None);
3319        let jref = VarRef::new("j", None);
3320        do_expr_ok_test(
3321            "i = 0 OR i = (j - 1)",
3322            Or(Box::from(BinaryOpSpan {
3323                lhs: Equal(Box::from(BinaryOpSpan {
3324                    lhs: expr_symbol(iref.clone(), 1, 7),
3325                    rhs: expr_integer(0, 1, 11),
3326                    pos: lc(1, 9),
3327                })),
3328                rhs: Equal(Box::from(BinaryOpSpan {
3329                    lhs: expr_symbol(iref, 1, 16),
3330                    rhs: Subtract(Box::from(BinaryOpSpan {
3331                        lhs: expr_symbol(jref, 1, 21),
3332                        rhs: expr_integer(1, 1, 25),
3333                        pos: lc(1, 23),
3334                    })),
3335                    pos: lc(1, 18),
3336                })),
3337                pos: lc(1, 13),
3338            })),
3339        );
3340    }
3341
3342    #[test]
3343    fn test_expr_errors() {
3344        // Note that all column numbers in the errors below are offset by 6 (due to "PRINT ") as
3345        // that's what the do_expr_error_test function is prefixing to the given strings.
3346
3347        do_expr_error_test("+3", "1:7: Not enough values to apply operator");
3348        do_expr_error_test("2 + * 3", "1:9: Not enough values to apply operator");
3349        do_expr_error_test("(2(3))", "1:9: Unexpected ( in expression");
3350        do_expr_error_test("((3)2)", "1:11: Unexpected value in expression");
3351        do_expr_error_test("2 3", "1:9: Unexpected value in expression");
3352
3353        do_expr_error_test("(", "1:8: Missing expression");
3354
3355        do_expr_error_test(")", "1:7: Expected comma, semicolon, or end of statement");
3356        do_expr_error_test("(()", "1:10: Missing expression");
3357        do_expr_error_test("())", "1:7: Expected expression");
3358        do_expr_error_test("3 + (2 + 1) + (4 - 5", "1:21: Unbalanced parenthesis");
3359        do_expr_error_test(
3360            "3 + 2 + 1) + (4 - 5)",
3361            "1:16: Expected comma, semicolon, or end of statement",
3362        );
3363
3364        do_expr_error_test("foo(,)", "1:11: Missing expression");
3365        do_expr_error_test("foo(, 3)", "1:11: Missing expression");
3366        do_expr_error_test("foo(3, )", "1:14: Missing expression");
3367        do_expr_error_test("foo(3, , 4)", "1:14: Missing expression");
3368        // TODO(jmmv): These are not the best error messages...
3369        do_expr_error_test("(,)", "1:8: Missing expression");
3370        do_expr_error_test("(3, 4)", "1:7: Expected expression");
3371        do_expr_error_test("((), ())", "1:10: Missing expression");
3372
3373        // TODO(jmmv): This succeeds because `PRINT` is interned as a `Token::Symbol` so the
3374        // expression parser sees it as a variable reference... but this should probably fail.
3375        // Would need to intern builtin call names as a separate token to catch this, but that
3376        // also means that the lexer must be aware of builtin call names upfront.
3377        use Expr::*;
3378        do_expr_ok_test(
3379            "1 + PRINT",
3380            Add(Box::from(BinaryOpSpan {
3381                lhs: expr_integer(1, 1, 7),
3382                rhs: expr_symbol(VarRef::new("PRINT", None), 1, 11),
3383                pos: lc(1, 9),
3384            })),
3385        );
3386    }
3387
3388    #[test]
3389    fn test_expr_errors_due_to_keywords() {
3390        for kw in &[
3391            "BOOLEAN", "CASE", "DATA", "DIM", "DOUBLE", "ELSEIF", "END", "ERROR", "EXIT", "FOR",
3392            "GOSUB", "GOTO", "IF", "IS", "INTEGER", "LOOP", "NEXT", "ON", "RESUME", "RETURN",
3393            "SELECT", "STRING", "UNTIL", "WEND", "WHILE",
3394        ] {
3395            do_expr_error_test(
3396                &format!("2 + {} - 1", kw),
3397                "1:11: Unexpected keyword in expression",
3398            );
3399        }
3400    }
3401
3402    #[test]
3403    fn test_if_empty_branches() {
3404        do_ok_test(
3405            "IF 1 THEN\nEND IF",
3406            &[Statement::If(IfSpan {
3407                branches: vec![IfBranchSpan { guard: expr_integer(1, 1, 4), body: vec![] }],
3408            })],
3409        );
3410        do_ok_test(
3411            "IF 1 THEN\nREM Some comment to skip over\n\nEND IF",
3412            &[Statement::If(IfSpan {
3413                branches: vec![IfBranchSpan { guard: expr_integer(1, 1, 4), body: vec![] }],
3414            })],
3415        );
3416        do_ok_test(
3417            "IF 1 THEN\nELSEIF 2 THEN\nEND IF",
3418            &[Statement::If(IfSpan {
3419                branches: vec![
3420                    IfBranchSpan { guard: expr_integer(1, 1, 4), body: vec![] },
3421                    IfBranchSpan { guard: expr_integer(2, 2, 8), body: vec![] },
3422                ],
3423            })],
3424        );
3425        do_ok_test(
3426            "IF 1 THEN\nELSEIF 2 THEN\nELSE\nEND IF",
3427            &[Statement::If(IfSpan {
3428                branches: vec![
3429                    IfBranchSpan { guard: expr_integer(1, 1, 4), body: vec![] },
3430                    IfBranchSpan { guard: expr_integer(2, 2, 8), body: vec![] },
3431                    IfBranchSpan { guard: expr_boolean(true, 3, 1), body: vec![] },
3432                ],
3433            })],
3434        );
3435        do_ok_test(
3436            "IF 1 THEN\nELSE\nEND IF",
3437            &[Statement::If(IfSpan {
3438                branches: vec![
3439                    IfBranchSpan { guard: expr_integer(1, 1, 4), body: vec![] },
3440                    IfBranchSpan { guard: expr_boolean(true, 2, 1), body: vec![] },
3441                ],
3442            })],
3443        );
3444    }
3445
3446    /// Helper to instantiate a trivial `Statement::BuiltinCall` that has no arguments.
3447    fn make_bare_builtin_call(name: &str, line: usize, col: usize) -> Statement {
3448        Statement::Call(CallSpan {
3449            vref: VarRef::new(name, None),
3450            vref_pos: LineCol { line, col },
3451            args: vec![],
3452        })
3453    }
3454
3455    #[test]
3456    fn test_if_with_one_statement_or_empty_lines() {
3457        do_ok_test(
3458            "IF 1 THEN\nPRINT\nEND IF",
3459            &[Statement::If(IfSpan {
3460                branches: vec![IfBranchSpan {
3461                    guard: expr_integer(1, 1, 4),
3462                    body: vec![make_bare_builtin_call("PRINT", 2, 1)],
3463                }],
3464            })],
3465        );
3466        do_ok_test(
3467            "IF 1 THEN\nREM foo\nELSEIF 2 THEN\nPRINT\nEND IF",
3468            &[Statement::If(IfSpan {
3469                branches: vec![
3470                    IfBranchSpan { guard: expr_integer(1, 1, 4), body: vec![] },
3471                    IfBranchSpan {
3472                        guard: expr_integer(2, 3, 8),
3473                        body: vec![make_bare_builtin_call("PRINT", 4, 1)],
3474                    },
3475                ],
3476            })],
3477        );
3478        do_ok_test(
3479            "IF 1 THEN\nELSEIF 2 THEN\nELSE\n\nPRINT\nEND IF",
3480            &[Statement::If(IfSpan {
3481                branches: vec![
3482                    IfBranchSpan { guard: expr_integer(1, 1, 4), body: vec![] },
3483                    IfBranchSpan { guard: expr_integer(2, 2, 8), body: vec![] },
3484                    IfBranchSpan {
3485                        guard: expr_boolean(true, 3, 1),
3486                        body: vec![make_bare_builtin_call("PRINT", 5, 1)],
3487                    },
3488                ],
3489            })],
3490        );
3491        do_ok_test(
3492            "IF 1 THEN\n\n\nELSE\nPRINT\nEND IF",
3493            &[Statement::If(IfSpan {
3494                branches: vec![
3495                    IfBranchSpan { guard: expr_integer(1, 1, 4), body: vec![] },
3496                    IfBranchSpan {
3497                        guard: expr_boolean(true, 4, 1),
3498                        body: vec![make_bare_builtin_call("PRINT", 5, 1)],
3499                    },
3500                ],
3501            })],
3502        );
3503    }
3504
3505    #[test]
3506    fn test_if_complex() {
3507        let code = r#"
3508            IF 1 THEN 'First branch
3509                A
3510                B
3511            ELSEIF 2 THEN 'Second branch
3512                C
3513                D
3514            ELSEIF 3 THEN 'Third branch
3515                E
3516                F
3517            ELSE 'Last branch
3518                G
3519                H
3520            END IF
3521        "#;
3522        do_ok_test(
3523            code,
3524            &[Statement::If(IfSpan {
3525                branches: vec![
3526                    IfBranchSpan {
3527                        guard: expr_integer(1, 2, 16),
3528                        body: vec![
3529                            make_bare_builtin_call("A", 3, 17),
3530                            make_bare_builtin_call("B", 4, 17),
3531                        ],
3532                    },
3533                    IfBranchSpan {
3534                        guard: expr_integer(2, 5, 20),
3535                        body: vec![
3536                            make_bare_builtin_call("C", 6, 17),
3537                            make_bare_builtin_call("D", 7, 17),
3538                        ],
3539                    },
3540                    IfBranchSpan {
3541                        guard: expr_integer(3, 8, 20),
3542                        body: vec![
3543                            make_bare_builtin_call("E", 9, 17),
3544                            make_bare_builtin_call("F", 10, 17),
3545                        ],
3546                    },
3547                    IfBranchSpan {
3548                        guard: expr_boolean(true, 11, 13),
3549                        body: vec![
3550                            make_bare_builtin_call("G", 12, 17),
3551                            make_bare_builtin_call("H", 13, 17),
3552                        ],
3553                    },
3554                ],
3555            })],
3556        );
3557    }
3558
3559    #[test]
3560    fn test_if_with_interleaved_end_complex() {
3561        let code = r#"
3562            IF 1 THEN 'First branch
3563                A
3564                END
3565                B
3566            ELSEIF 2 THEN 'Second branch
3567                C
3568                END 8
3569                D
3570            ELSEIF 3 THEN 'Third branch
3571                E
3572                END
3573                F
3574            ELSE 'Last branch
3575                G
3576                END 5
3577                H
3578            END IF
3579        "#;
3580        do_ok_test(
3581            code,
3582            &[Statement::If(IfSpan {
3583                branches: vec![
3584                    IfBranchSpan {
3585                        guard: expr_integer(1, 2, 16),
3586                        body: vec![
3587                            make_bare_builtin_call("A", 3, 17),
3588                            Statement::End(EndSpan { code: None }),
3589                            make_bare_builtin_call("B", 5, 17),
3590                        ],
3591                    },
3592                    IfBranchSpan {
3593                        guard: expr_integer(2, 6, 20),
3594                        body: vec![
3595                            make_bare_builtin_call("C", 7, 17),
3596                            Statement::End(EndSpan {
3597                                code: Some(Expr::Integer(IntegerSpan { value: 8, pos: lc(8, 21) })),
3598                            }),
3599                            make_bare_builtin_call("D", 9, 17),
3600                        ],
3601                    },
3602                    IfBranchSpan {
3603                        guard: expr_integer(3, 10, 20),
3604                        body: vec![
3605                            make_bare_builtin_call("E", 11, 17),
3606                            Statement::End(EndSpan { code: None }),
3607                            make_bare_builtin_call("F", 13, 17),
3608                        ],
3609                    },
3610                    IfBranchSpan {
3611                        guard: expr_boolean(true, 14, 13),
3612                        body: vec![
3613                            make_bare_builtin_call("G", 15, 17),
3614                            Statement::End(EndSpan {
3615                                code: Some(Expr::Integer(IntegerSpan {
3616                                    value: 5,
3617                                    pos: lc(16, 21),
3618                                })),
3619                            }),
3620                            make_bare_builtin_call("H", 17, 17),
3621                        ],
3622                    },
3623                ],
3624            })],
3625        );
3626    }
3627
3628    #[test]
3629    fn test_if_nested() {
3630        let code = r#"
3631            IF 1 THEN
3632                A
3633            ELSEIF 2 THEN
3634                IF 3 THEN
3635                    B
3636                END IF
3637            END IF
3638        "#;
3639        do_ok_test(
3640            code,
3641            &[Statement::If(IfSpan {
3642                branches: vec![
3643                    IfBranchSpan {
3644                        guard: expr_integer(1, 2, 16),
3645                        body: vec![make_bare_builtin_call("A", 3, 17)],
3646                    },
3647                    IfBranchSpan {
3648                        guard: expr_integer(2, 4, 20),
3649                        body: vec![Statement::If(IfSpan {
3650                            branches: vec![IfBranchSpan {
3651                                guard: expr_integer(3, 5, 20),
3652                                body: vec![make_bare_builtin_call("B", 6, 21)],
3653                            }],
3654                        })],
3655                    },
3656                ],
3657            })],
3658        );
3659    }
3660
3661    #[test]
3662    fn test_if_errors() {
3663        do_error_test("IF\n", "1:3: No expression in IF statement");
3664        do_error_test("IF 3 + 1", "1:9: No THEN in IF statement");
3665        do_error_test("IF 3 + 1\n", "1:9: No THEN in IF statement");
3666        do_error_test("IF 3 + 1 PRINT foo\n", "1:10: Unexpected value in expression");
3667        do_error_test("IF 3 + 1\nPRINT foo\n", "1:9: No THEN in IF statement");
3668        do_error_test("IF 3 + 1 THEN", "1:1: IF without END IF");
3669
3670        do_error_test("IF 1 THEN\n", "1:1: IF without END IF");
3671        do_error_test("IF 1 THEN\nELSEIF 1 THEN\n", "1:1: IF without END IF");
3672        do_error_test("IF 1 THEN\nELSE\n", "1:1: IF without END IF");
3673        do_error_test("REM\n   IF 1 THEN\n", "2:4: IF without END IF");
3674
3675        do_error_test("IF 1 THEN\nELSEIF\n", "2:7: No expression in ELSEIF statement");
3676        do_error_test("IF 1 THEN\nELSEIF 3 + 1", "2:13: No THEN in ELSEIF statement");
3677        do_error_test("IF 1 THEN\nELSEIF 3 + 1\n", "2:13: No THEN in ELSEIF statement");
3678        do_error_test(
3679            "IF 1 THEN\nELSEIF 3 + 1 PRINT foo\n",
3680            "2:14: Unexpected value in expression",
3681        );
3682        do_error_test("IF 1 THEN\nELSEIF 3 + 1\nPRINT foo\n", "2:13: No THEN in ELSEIF statement");
3683        do_error_test("IF 1 THEN\nELSEIF 3 + 1 THEN", "2:18: Expecting newline after THEN");
3684
3685        do_error_test("IF 1 THEN\nELSE", "2:5: Expecting newline after ELSE");
3686        do_error_test("IF 1 THEN\nELSE foo", "2:6: Expecting newline after ELSE");
3687
3688        do_error_test("IF 1 THEN\nEND", "1:1: IF without END IF");
3689        do_error_test("IF 1 THEN\nEND\n", "1:1: IF without END IF");
3690        do_error_test("IF 1 THEN\nEND IF foo", "2:8: Expected newline but found foo");
3691        do_error_test("IF 1 THEN\nEND SELECT\n", "2:1: END SELECT without SELECT");
3692        do_error_test("IF 1 THEN\nEND SELECT\nEND IF\n", "2:1: END SELECT without SELECT");
3693
3694        do_error_test(
3695            "IF 1 THEN\nELSE\nELSEIF 2 THEN\nEND IF",
3696            "3:1: Unexpected ELSEIF after ELSE",
3697        );
3698        do_error_test("IF 1 THEN\nELSE\nELSE\nEND IF", "3:1: Duplicate ELSE after ELSE");
3699
3700        do_error_test_no_reset("ELSEIF 1 THEN\nEND IF", "1:1: Unexpected ELSEIF in statement");
3701        do_error_test_no_reset("ELSE 1\nEND IF", "1:1: Unexpected ELSE in statement");
3702
3703        do_error_test("IF 1 THEN\nEND 3 IF", "2:7: Unexpected keyword in expression");
3704        do_error_test("END 3 IF", "1:7: Unexpected keyword in expression");
3705        do_error_test("END IF", "1:1: END IF without IF");
3706
3707        do_error_test("IF TRUE THEN PRINT ELSE ELSE", "1:25: Unexpected ELSE in uniline IF branch");
3708    }
3709
3710    #[test]
3711    fn test_if_uniline_then() {
3712        do_ok_test(
3713            "IF 1 THEN A",
3714            &[Statement::If(IfSpan {
3715                branches: vec![IfBranchSpan {
3716                    guard: expr_integer(1, 1, 4),
3717                    body: vec![make_bare_builtin_call("A", 1, 11)],
3718                }],
3719            })],
3720        );
3721    }
3722
3723    #[test]
3724    fn test_if_uniline_then_else() {
3725        do_ok_test(
3726            "IF 1 THEN A ELSE B",
3727            &[Statement::If(IfSpan {
3728                branches: vec![
3729                    IfBranchSpan {
3730                        guard: expr_integer(1, 1, 4),
3731                        body: vec![make_bare_builtin_call("A", 1, 11)],
3732                    },
3733                    IfBranchSpan {
3734                        guard: expr_boolean(true, 1, 13),
3735                        body: vec![make_bare_builtin_call("B", 1, 18)],
3736                    },
3737                ],
3738            })],
3739        );
3740    }
3741
3742    #[test]
3743    fn test_if_uniline_empty_then_else() {
3744        do_ok_test(
3745            "IF 1 THEN ELSE B",
3746            &[Statement::If(IfSpan {
3747                branches: vec![
3748                    IfBranchSpan { guard: expr_integer(1, 1, 4), body: vec![] },
3749                    IfBranchSpan {
3750                        guard: expr_boolean(true, 1, 11),
3751                        body: vec![make_bare_builtin_call("B", 1, 16)],
3752                    },
3753                ],
3754            })],
3755        );
3756    }
3757
3758    #[test]
3759    fn test_if_uniline_then_empty_else() {
3760        do_ok_test(
3761            "IF 1 THEN A ELSE",
3762            &[Statement::If(IfSpan {
3763                branches: vec![
3764                    IfBranchSpan {
3765                        guard: expr_integer(1, 1, 4),
3766                        body: vec![make_bare_builtin_call("A", 1, 11)],
3767                    },
3768                    IfBranchSpan { guard: expr_boolean(true, 1, 13), body: vec![] },
3769                ],
3770            })],
3771        );
3772    }
3773
3774    #[test]
3775    fn test_if_uniline_empty_then_empty_else() {
3776        do_ok_test(
3777            "IF 1 THEN ELSE",
3778            &[Statement::If(IfSpan {
3779                branches: vec![
3780                    IfBranchSpan { guard: expr_integer(1, 1, 4), body: vec![] },
3781                    IfBranchSpan { guard: expr_boolean(true, 1, 11), body: vec![] },
3782                ],
3783            })],
3784        );
3785    }
3786
3787    /// Performs a test of a uniline if statement followed by a specific allowed statement type.
3788    ///
3789    /// `text` is the literal statement to append to the if statement, and `stmt` contains the
3790    /// expected parsed statement.  The expected positions for the parsed statement are line 1 and
3791    /// columns offset by 11 characters.
3792    fn do_if_uniline_allowed_test(text: &str, stmt: Statement) {
3793        do_ok_test(
3794            &format!("IF 1 THEN {}\nZ", text),
3795            &[
3796                Statement::If(IfSpan {
3797                    branches: vec![IfBranchSpan { guard: expr_integer(1, 1, 4), body: vec![stmt] }],
3798                }),
3799                make_bare_builtin_call("Z", 2, 1),
3800            ],
3801        );
3802    }
3803
3804    #[test]
3805    fn test_if_uniline_allowed_data() {
3806        do_if_uniline_allowed_test("DATA", Statement::Data(DataSpan { values: vec![None] }));
3807    }
3808
3809    #[test]
3810    fn test_if_uniline_allowed_end() {
3811        do_if_uniline_allowed_test(
3812            "END 8",
3813            Statement::End(EndSpan { code: Some(expr_integer(8, 1, 15)) }),
3814        );
3815    }
3816
3817    #[test]
3818    fn test_if_uniline_allowed_exit() {
3819        do_if_uniline_allowed_test("EXIT DO", Statement::ExitDo(ExitDoSpan { pos: lc(1, 11) }));
3820
3821        do_error_test("IF 1 THEN EXIT", "1:15: Expecting DO after EXIT");
3822    }
3823
3824    #[test]
3825    fn test_if_uniline_allowed_gosub() {
3826        do_if_uniline_allowed_test(
3827            "GOSUB 10",
3828            Statement::Gosub(GotoSpan { target: "10".to_owned(), target_pos: lc(1, 17) }),
3829        );
3830
3831        do_error_test("IF 1 THEN GOSUB", "1:16: Expected label name after GOSUB");
3832    }
3833
3834    #[test]
3835    fn test_if_uniline_allowed_goto() {
3836        do_if_uniline_allowed_test(
3837            "GOTO 10",
3838            Statement::Goto(GotoSpan { target: "10".to_owned(), target_pos: lc(1, 16) }),
3839        );
3840
3841        do_error_test("IF 1 THEN GOTO", "1:15: Expected label name after GOTO");
3842    }
3843
3844    #[test]
3845    fn test_if_uniline_allowed_on_error() {
3846        do_if_uniline_allowed_test(
3847            "ON ERROR RESUME NEXT",
3848            Statement::OnError(OnErrorSpan::ResumeNext),
3849        );
3850
3851        do_error_test("IF 1 THEN ON", "1:13: Expected ERROR after ON");
3852    }
3853
3854    #[test]
3855    fn test_if_uniline_allowed_return() {
3856        do_if_uniline_allowed_test("RETURN", Statement::Return(ReturnSpan { pos: lc(1, 11) }));
3857    }
3858
3859    #[test]
3860    fn test_if_uniline_allowed_assignment() {
3861        do_if_uniline_allowed_test(
3862            "a = 3",
3863            Statement::Assignment(AssignmentSpan {
3864                vref: VarRef::new("a", None),
3865                vref_pos: lc(1, 11),
3866                expr: expr_integer(3, 1, 15),
3867            }),
3868        );
3869    }
3870
3871    #[test]
3872    fn test_if_uniline_allowed_array_assignment() {
3873        do_if_uniline_allowed_test(
3874            "a(3) = 5",
3875            Statement::ArrayAssignment(ArrayAssignmentSpan {
3876                vref: VarRef::new("a", None),
3877                vref_pos: lc(1, 11),
3878                subscripts: vec![expr_integer(3, 1, 13)],
3879                expr: expr_integer(5, 1, 18),
3880            }),
3881        );
3882    }
3883
3884    #[test]
3885    fn test_if_uniline_allowed_builtin_call() {
3886        do_if_uniline_allowed_test(
3887            "a 0",
3888            Statement::Call(CallSpan {
3889                vref: VarRef::new("A", None),
3890                vref_pos: lc(1, 11),
3891                args: vec![ArgSpan {
3892                    expr: Some(expr_integer(0, 1, 13)),
3893                    sep: ArgSep::End,
3894                    sep_pos: lc(1, 14),
3895                }],
3896            }),
3897        );
3898    }
3899
3900    #[test]
3901    fn test_if_uniline_unallowed_statements() {
3902        for t in ["DIM", "DO", "IF", "FOR", "10", "@label", "SELECT", "WHILE"] {
3903            do_error_test(
3904                &format!("IF 1 THEN {}", t),
3905                &format!("1:11: Unexpected {} in uniline IF branch", t),
3906            );
3907        }
3908    }
3909
3910    #[test]
3911    fn test_for_empty() {
3912        let auto_iter = VarRef::new("i", None);
3913        do_ok_test(
3914            "FOR i = 1 TO 10\nNEXT",
3915            &[Statement::For(ForSpan {
3916                iter: auto_iter.clone(),
3917                iter_pos: lc(1, 5),
3918                iter_double: false,
3919                start: expr_integer(1, 1, 9),
3920                end: Expr::LessEqual(Box::from(BinaryOpSpan {
3921                    lhs: expr_symbol(auto_iter.clone(), 1, 5),
3922                    rhs: expr_integer(10, 1, 14),
3923                    pos: lc(1, 11),
3924                })),
3925                next: Expr::Add(Box::from(BinaryOpSpan {
3926                    lhs: expr_symbol(auto_iter, 1, 5),
3927                    rhs: expr_integer(1, 1, 16),
3928                    pos: lc(1, 11),
3929                })),
3930                body: vec![],
3931            })],
3932        );
3933
3934        let typed_iter = VarRef::new("d", Some(ExprType::Double));
3935        do_ok_test(
3936            "FOR d# = 1.0 TO 10.2\nREM Nothing to do\nNEXT",
3937            &[Statement::For(ForSpan {
3938                iter: typed_iter.clone(),
3939                iter_pos: lc(1, 5),
3940                iter_double: false,
3941                start: expr_double(1.0, 1, 10),
3942                end: Expr::LessEqual(Box::from(BinaryOpSpan {
3943                    lhs: expr_symbol(typed_iter.clone(), 1, 5),
3944                    rhs: expr_double(10.2, 1, 17),
3945                    pos: lc(1, 14),
3946                })),
3947                next: Expr::Add(Box::from(BinaryOpSpan {
3948                    lhs: expr_symbol(typed_iter, 1, 5),
3949                    rhs: expr_integer(1, 1, 21),
3950                    pos: lc(1, 14),
3951                })),
3952                body: vec![],
3953            })],
3954        );
3955    }
3956
3957    #[test]
3958    fn test_for_incrementing() {
3959        let iter = VarRef::new("i", None);
3960        do_ok_test(
3961            "FOR i = 0 TO 5\nA\nB\nNEXT",
3962            &[Statement::For(ForSpan {
3963                iter: iter.clone(),
3964                iter_pos: lc(1, 5),
3965                iter_double: false,
3966                start: expr_integer(0, 1, 9),
3967                end: Expr::LessEqual(Box::from(BinaryOpSpan {
3968                    lhs: expr_symbol(iter.clone(), 1, 5),
3969                    rhs: expr_integer(5, 1, 14),
3970                    pos: lc(1, 11),
3971                })),
3972                next: Expr::Add(Box::from(BinaryOpSpan {
3973                    lhs: expr_symbol(iter, 1, 5),
3974                    rhs: expr_integer(1, 1, 15),
3975                    pos: lc(1, 11),
3976                })),
3977                body: vec![make_bare_builtin_call("A", 2, 1), make_bare_builtin_call("B", 3, 1)],
3978            })],
3979        );
3980    }
3981
3982    #[test]
3983    fn test_for_incrementing_with_step() {
3984        let iter = VarRef::new("i", None);
3985        do_ok_test(
3986            "FOR i = 0 TO 5 STEP 2\nA\nNEXT",
3987            &[Statement::For(ForSpan {
3988                iter: iter.clone(),
3989                iter_pos: lc(1, 5),
3990                iter_double: false,
3991                start: expr_integer(0, 1, 9),
3992                end: Expr::LessEqual(Box::from(BinaryOpSpan {
3993                    lhs: expr_symbol(iter.clone(), 1, 5),
3994                    rhs: expr_integer(5, 1, 14),
3995                    pos: lc(1, 11),
3996                })),
3997                next: Expr::Add(Box::from(BinaryOpSpan {
3998                    lhs: expr_symbol(iter, 1, 5),
3999                    rhs: expr_integer(2, 1, 21),
4000                    pos: lc(1, 11),
4001                })),
4002                body: vec![make_bare_builtin_call("A", 2, 1)],
4003            })],
4004        );
4005
4006        let iter = VarRef::new("i", None);
4007        do_ok_test(
4008            "FOR i = 0 TO 5 STEP 2.5\nA\nNEXT",
4009            &[Statement::For(ForSpan {
4010                iter: iter.clone(),
4011                iter_pos: lc(1, 5),
4012                iter_double: true,
4013                start: expr_integer(0, 1, 9),
4014                end: Expr::LessEqual(Box::from(BinaryOpSpan {
4015                    lhs: expr_symbol(iter.clone(), 1, 5),
4016                    rhs: expr_integer(5, 1, 14),
4017                    pos: lc(1, 11),
4018                })),
4019                next: Expr::Add(Box::from(BinaryOpSpan {
4020                    lhs: expr_symbol(iter, 1, 5),
4021                    rhs: expr_double(2.5, 1, 21),
4022                    pos: lc(1, 11),
4023                })),
4024                body: vec![make_bare_builtin_call("A", 2, 1)],
4025            })],
4026        );
4027    }
4028
4029    #[test]
4030    fn test_for_decrementing_with_step() {
4031        let iter = VarRef::new("i", None);
4032        do_ok_test(
4033            "FOR i = 5 TO 0 STEP -1\nA\nNEXT",
4034            &[Statement::For(ForSpan {
4035                iter: iter.clone(),
4036                iter_pos: lc(1, 5),
4037                iter_double: false,
4038                start: expr_integer(5, 1, 9),
4039                end: Expr::GreaterEqual(Box::from(BinaryOpSpan {
4040                    lhs: expr_symbol(iter.clone(), 1, 5),
4041                    rhs: expr_integer(0, 1, 14),
4042                    pos: lc(1, 11),
4043                })),
4044                next: Expr::Add(Box::from(BinaryOpSpan {
4045                    lhs: expr_symbol(iter, 1, 5),
4046                    rhs: expr_integer(-1, 1, 22),
4047                    pos: lc(1, 11),
4048                })),
4049                body: vec![make_bare_builtin_call("A", 2, 1)],
4050            })],
4051        );
4052
4053        let iter = VarRef::new("i", None);
4054        do_ok_test(
4055            "FOR i = 5 TO 0 STEP -1.2\nA\nNEXT",
4056            &[Statement::For(ForSpan {
4057                iter: iter.clone(),
4058                iter_pos: lc(1, 5),
4059                iter_double: true,
4060                start: expr_integer(5, 1, 9),
4061                end: Expr::GreaterEqual(Box::from(BinaryOpSpan {
4062                    lhs: expr_symbol(iter.clone(), 1, 5),
4063                    rhs: expr_integer(0, 1, 14),
4064                    pos: lc(1, 11),
4065                })),
4066                next: Expr::Add(Box::from(BinaryOpSpan {
4067                    lhs: expr_symbol(iter, 1, 5),
4068                    rhs: expr_double(-1.2, 1, 22),
4069                    pos: lc(1, 11),
4070                })),
4071                body: vec![make_bare_builtin_call("A", 2, 1)],
4072            })],
4073        );
4074    }
4075
4076    #[test]
4077    fn test_for_errors() {
4078        do_error_test("FOR\n", "1:4: No iterator name in FOR statement");
4079        do_error_test("FOR =\n", "1:5: No iterator name in FOR statement");
4080        do_error_test(
4081            "FOR a$\n",
4082            "1:5: Iterator name in FOR statement must be a numeric reference",
4083        );
4084
4085        do_error_test("FOR d#\n", "1:7: No equal sign in FOR statement");
4086        do_error_test("FOR i 3\n", "1:7: No equal sign in FOR statement");
4087        do_error_test("FOR i = TO\n", "1:9: No start expression in FOR statement");
4088        do_error_test("FOR i = NEXT\n", "1:9: Unexpected keyword in expression");
4089
4090        do_error_test("FOR i = 3 STEP\n", "1:11: No TO in FOR statement");
4091        do_error_test("FOR i = 3 TO STEP\n", "1:14: No end expression in FOR statement");
4092        do_error_test("FOR i = 3 TO NEXT\n", "1:14: Unexpected keyword in expression");
4093
4094        do_error_test("FOR i = 3 TO 1 STEP a\n", "1:21: STEP needs a literal number");
4095        do_error_test("FOR i = 3 TO 1 STEP -a\n", "1:22: STEP needs a literal number");
4096        do_error_test("FOR i = 3 TO 1 STEP NEXT\n", "1:21: STEP needs a literal number");
4097        do_error_test("FOR i = 3 TO 1 STEP 0\n", "1:21: Infinite FOR loop; STEP cannot be 0");
4098        do_error_test("FOR i = 3 TO 1 STEP 0.0\n", "1:21: Infinite FOR loop; STEP cannot be 0");
4099
4100        do_error_test("FOR i = 3 TO 1", "1:15: Expecting newline after FOR");
4101        do_error_test("FOR i = 1 TO 3 STEP 1", "1:22: Expecting newline after FOR");
4102        do_error_test("FOR i = 3 TO 1 STEP -1", "1:23: Expecting newline after FOR");
4103
4104        do_error_test("    FOR i = 0 TO 10\nPRINT i\n", "1:5: FOR without NEXT");
4105    }
4106
4107    #[test]
4108    fn test_function_empty() {
4109        do_ok_test(
4110            "FUNCTION foo$\nEND FUNCTION",
4111            &[Statement::Callable(CallableSpan {
4112                name: VarRef::new("foo", Some(ExprType::Text)),
4113                name_pos: lc(1, 10),
4114                params: vec![],
4115                body: vec![],
4116                end_pos: lc(2, 1),
4117            })],
4118        );
4119    }
4120
4121    #[test]
4122    fn test_function_some_content() {
4123        do_ok_test(
4124            r#"
4125                FUNCTION foo$
4126                    A
4127                    END
4128                    END 8
4129                    B
4130                END FUNCTION
4131            "#,
4132            &[Statement::Callable(CallableSpan {
4133                name: VarRef::new("foo", Some(ExprType::Text)),
4134                name_pos: lc(2, 26),
4135                params: vec![],
4136                body: vec![
4137                    make_bare_builtin_call("A", 3, 21),
4138                    Statement::End(EndSpan { code: None }),
4139                    Statement::End(EndSpan {
4140                        code: Some(Expr::Integer(IntegerSpan { value: 8, pos: lc(5, 25) })),
4141                    }),
4142                    make_bare_builtin_call("B", 6, 21),
4143                ],
4144                end_pos: lc(7, 17),
4145            })],
4146        );
4147    }
4148
4149    #[test]
4150    fn test_function_one_param() {
4151        do_ok_test(
4152            "FUNCTION foo$(x)\nEND FUNCTION",
4153            &[Statement::Callable(CallableSpan {
4154                name: VarRef::new("foo", Some(ExprType::Text)),
4155                name_pos: lc(1, 10),
4156                params: vec![VarRef::new("x", None)],
4157                body: vec![],
4158                end_pos: lc(2, 1),
4159            })],
4160        );
4161    }
4162
4163    #[test]
4164    fn test_function_multiple_params() {
4165        do_ok_test(
4166            "FUNCTION foo$(x$, y, z AS BOOLEAN)\nEND FUNCTION",
4167            &[Statement::Callable(CallableSpan {
4168                name: VarRef::new("foo", Some(ExprType::Text)),
4169                name_pos: lc(1, 10),
4170                params: vec![
4171                    VarRef::new("x", Some(ExprType::Text)),
4172                    VarRef::new("y", None),
4173                    VarRef::new("z", Some(ExprType::Boolean)),
4174                ],
4175                body: vec![],
4176                end_pos: lc(2, 1),
4177            })],
4178        );
4179    }
4180
4181    #[test]
4182    fn test_function_errors() {
4183        do_error_test("FUNCTION", "1:9: Expected a function name after FUNCTION");
4184        do_error_test("FUNCTION foo", "1:13: Expected newline after FUNCTION name");
4185        do_error_test("FUNCTION foo 3", "1:14: Expected newline after FUNCTION name");
4186        do_error_test("FUNCTION foo\nEND", "1:1: FUNCTION without END FUNCTION");
4187        do_error_test("FUNCTION foo\nEND IF", "2:1: END IF without IF");
4188        do_error_test("FUNCTION foo\nEND SUB", "2:1: END SUB without SUB");
4189        do_error_test(
4190            "FUNCTION foo\nFUNCTION bar\nEND FUNCTION\nEND FUNCTION",
4191            "2:1: Cannot nest FUNCTION or SUB definitions",
4192        );
4193        do_error_test(
4194            "FUNCTION foo\nSUB bar\nEND SUB\nEND FUNCTION",
4195            "2:1: Cannot nest FUNCTION or SUB definitions",
4196        );
4197        do_error_test("FUNCTION foo (", "1:15: Expected a parameter name");
4198        do_error_test("FUNCTION foo ()", "1:15: Expected a parameter name");
4199        do_error_test("FUNCTION foo (,)", "1:15: Expected a parameter name");
4200        do_error_test("FUNCTION foo (a,)", "1:17: Expected a parameter name");
4201        do_error_test("FUNCTION foo (,b)", "1:15: Expected a parameter name");
4202        do_error_test("FUNCTION foo (a AS)", "1:19: Invalid type name ) in AS type definition");
4203        do_error_test(
4204            "FUNCTION foo (a INTEGER)",
4205            "1:17: Expected comma, AS, or end of parameters list",
4206        );
4207        do_error_test("FUNCTION foo (a? AS BOOLEAN)", "1:15: Type annotation not allowed in a?");
4208    }
4209
4210    #[test]
4211    fn test_gosub_ok() {
4212        do_ok_test(
4213            "GOSUB 10",
4214            &[Statement::Gosub(GotoSpan { target: "10".to_owned(), target_pos: lc(1, 7) })],
4215        );
4216
4217        do_ok_test(
4218            "GOSUB @foo",
4219            &[Statement::Gosub(GotoSpan { target: "foo".to_owned(), target_pos: lc(1, 7) })],
4220        );
4221    }
4222
4223    #[test]
4224    fn test_gosub_errors() {
4225        do_error_test("GOSUB\n", "1:6: Expected label name after GOSUB");
4226        do_error_test("GOSUB foo\n", "1:7: Expected label name after GOSUB");
4227        do_error_test("GOSUB \"foo\"\n", "1:7: Expected label name after GOSUB");
4228        do_error_test("GOSUB @foo, @bar\n", "1:11: Expected newline but found ,");
4229        do_error_test("GOSUB @foo, 3\n", "1:11: Expected newline but found ,");
4230    }
4231
4232    #[test]
4233    fn test_goto_ok() {
4234        do_ok_test(
4235            "GOTO 10",
4236            &[Statement::Goto(GotoSpan { target: "10".to_owned(), target_pos: lc(1, 6) })],
4237        );
4238
4239        do_ok_test(
4240            "GOTO @foo",
4241            &[Statement::Goto(GotoSpan { target: "foo".to_owned(), target_pos: lc(1, 6) })],
4242        );
4243    }
4244
4245    #[test]
4246    fn test_goto_errors() {
4247        do_error_test("GOTO\n", "1:5: Expected label name after GOTO");
4248        do_error_test("GOTO foo\n", "1:6: Expected label name after GOTO");
4249        do_error_test("GOTO \"foo\"\n", "1:6: Expected label name after GOTO");
4250        do_error_test("GOTO @foo, @bar\n", "1:10: Expected newline but found ,");
4251        do_error_test("GOTO @foo, 3\n", "1:10: Expected newline but found ,");
4252    }
4253
4254    #[test]
4255    fn test_label_own_line() {
4256        do_ok_test(
4257            "@foo\nPRINT",
4258            &[
4259                Statement::Label(LabelSpan { name: "foo".to_owned(), name_pos: lc(1, 1) }),
4260                make_bare_builtin_call("PRINT", 2, 1),
4261            ],
4262        );
4263    }
4264
4265    #[test]
4266    fn test_label_before_statement() {
4267        do_ok_test(
4268            "@foo PRINT",
4269            &[
4270                Statement::Label(LabelSpan { name: "foo".to_owned(), name_pos: lc(1, 1) }),
4271                make_bare_builtin_call("PRINT", 1, 6),
4272            ],
4273        );
4274    }
4275
4276    #[test]
4277    fn test_label_multiple_same_line() {
4278        do_ok_test(
4279            "@foo @bar",
4280            &[
4281                Statement::Label(LabelSpan { name: "foo".to_owned(), name_pos: lc(1, 1) }),
4282                Statement::Label(LabelSpan { name: "bar".to_owned(), name_pos: lc(1, 6) }),
4283            ],
4284        );
4285    }
4286
4287    #[test]
4288    fn test_label_errors() {
4289        do_error_test("PRINT @foo", "1:7: Unexpected keyword in expression");
4290    }
4291
4292    #[test]
4293    fn test_parse_on_error_ok() {
4294        do_ok_test("ON ERROR GOTO 0", &[Statement::OnError(OnErrorSpan::Reset)]);
4295
4296        do_ok_test(
4297            "ON ERROR GOTO 10",
4298            &[Statement::OnError(OnErrorSpan::Goto(GotoSpan {
4299                target: "10".to_owned(),
4300                target_pos: lc(1, 15),
4301            }))],
4302        );
4303
4304        do_ok_test(
4305            "ON ERROR GOTO @foo",
4306            &[Statement::OnError(OnErrorSpan::Goto(GotoSpan {
4307                target: "foo".to_owned(),
4308                target_pos: lc(1, 15),
4309            }))],
4310        );
4311
4312        do_ok_test("ON ERROR RESUME NEXT", &[Statement::OnError(OnErrorSpan::ResumeNext)]);
4313    }
4314
4315    #[test]
4316    fn test_parse_on_error_errors() {
4317        do_error_test("ON", "1:3: Expected ERROR after ON");
4318        do_error_test("ON NEXT", "1:4: Expected ERROR after ON");
4319        do_error_test("ON ERROR", "1:9: Expected GOTO or RESUME after ON ERROR");
4320        do_error_test("ON ERROR FOR", "1:10: Expected GOTO or RESUME after ON ERROR");
4321
4322        do_error_test("ON ERROR RESUME", "1:16: Expected NEXT after ON ERROR RESUME");
4323        do_error_test("ON ERROR RESUME 3", "1:17: Expected NEXT after ON ERROR RESUME");
4324        do_error_test("ON ERROR RESUME NEXT 3", "1:22: Expected newline but found 3");
4325
4326        do_error_test("ON ERROR GOTO", "1:14: Expected label name or 0 after ON ERROR GOTO");
4327        do_error_test("ON ERROR GOTO NEXT", "1:15: Expected label name or 0 after ON ERROR GOTO");
4328        do_error_test("ON ERROR GOTO 0 @a", "1:17: Expected newline but found @a");
4329    }
4330
4331    #[test]
4332    fn test_select_empty() {
4333        do_ok_test(
4334            "SELECT CASE 7\nEND SELECT",
4335            &[Statement::Select(SelectSpan {
4336                expr: expr_integer(7, 1, 13),
4337                cases: vec![],
4338                end_pos: lc(2, 1),
4339            })],
4340        );
4341
4342        do_ok_test(
4343            "SELECT CASE 5 - TRUE\n    \nEND SELECT",
4344            &[Statement::Select(SelectSpan {
4345                expr: Expr::Subtract(Box::from(BinaryOpSpan {
4346                    lhs: expr_integer(5, 1, 13),
4347                    rhs: expr_boolean(true, 1, 17),
4348                    pos: lc(1, 15),
4349                })),
4350                cases: vec![],
4351                end_pos: lc(3, 1),
4352            })],
4353        );
4354    }
4355
4356    #[test]
4357    fn test_select_case_else_only() {
4358        do_ok_test(
4359            "SELECT CASE 7\nCASE ELSE\nA\nEND SELECT",
4360            &[Statement::Select(SelectSpan {
4361                expr: expr_integer(7, 1, 13),
4362                cases: vec![CaseSpan {
4363                    guards: vec![],
4364                    body: vec![make_bare_builtin_call("A", 3, 1)],
4365                }],
4366                end_pos: lc(4, 1),
4367            })],
4368        );
4369    }
4370
4371    #[test]
4372    fn test_select_multiple_cases_without_else() {
4373        do_ok_test(
4374            "SELECT CASE 7\nCASE 1\nA\nCASE 2\nB\nEND SELECT",
4375            &[Statement::Select(SelectSpan {
4376                expr: expr_integer(7, 1, 13),
4377                cases: vec![
4378                    CaseSpan {
4379                        guards: vec![CaseGuardSpan::Is(CaseRelOp::Equal, expr_integer(1, 2, 6))],
4380                        body: vec![make_bare_builtin_call("A", 3, 1)],
4381                    },
4382                    CaseSpan {
4383                        guards: vec![CaseGuardSpan::Is(CaseRelOp::Equal, expr_integer(2, 4, 6))],
4384                        body: vec![make_bare_builtin_call("B", 5, 1)],
4385                    },
4386                ],
4387                end_pos: lc(6, 1),
4388            })],
4389        );
4390    }
4391
4392    #[test]
4393    fn test_select_multiple_cases_with_else() {
4394        do_ok_test(
4395            "SELECT CASE 7\nCASE 1\nA\nCASE 2\nB\nCASE ELSE\nC\nEND SELECT",
4396            &[Statement::Select(SelectSpan {
4397                expr: expr_integer(7, 1, 13),
4398                cases: vec![
4399                    CaseSpan {
4400                        guards: vec![CaseGuardSpan::Is(CaseRelOp::Equal, expr_integer(1, 2, 6))],
4401                        body: vec![make_bare_builtin_call("A", 3, 1)],
4402                    },
4403                    CaseSpan {
4404                        guards: vec![CaseGuardSpan::Is(CaseRelOp::Equal, expr_integer(2, 4, 6))],
4405                        body: vec![make_bare_builtin_call("B", 5, 1)],
4406                    },
4407                    CaseSpan { guards: vec![], body: vec![make_bare_builtin_call("C", 7, 1)] },
4408                ],
4409                end_pos: lc(8, 1),
4410            })],
4411        );
4412    }
4413
4414    #[test]
4415    fn test_select_multiple_cases_empty_bodies() {
4416        do_ok_test(
4417            "SELECT CASE 7\nCASE 1\n\nCASE 2\n\nCASE ELSE\n\nEND SELECT",
4418            &[Statement::Select(SelectSpan {
4419                expr: expr_integer(7, 1, 13),
4420                cases: vec![
4421                    CaseSpan {
4422                        guards: vec![CaseGuardSpan::Is(CaseRelOp::Equal, expr_integer(1, 2, 6))],
4423                        body: vec![],
4424                    },
4425                    CaseSpan {
4426                        guards: vec![CaseGuardSpan::Is(CaseRelOp::Equal, expr_integer(2, 4, 6))],
4427                        body: vec![],
4428                    },
4429                    CaseSpan { guards: vec![], body: vec![] },
4430                ],
4431                end_pos: lc(8, 1),
4432            })],
4433        );
4434    }
4435
4436    #[test]
4437    fn test_select_multiple_cases_with_interleaved_end() {
4438        let code = r#"
4439            SELECT CASE 7
4440                CASE 1
4441                    A
4442                    END
4443                    B
4444                CASE 2 ' Second case.
4445                    C
4446                    END 8
4447                    D
4448                CASE ELSE
4449                    E
4450                    END
4451                    F
4452            END SELECT
4453        "#;
4454        do_ok_test(
4455            code,
4456            &[Statement::Select(SelectSpan {
4457                expr: expr_integer(7, 2, 25),
4458                cases: vec![
4459                    CaseSpan {
4460                        guards: vec![CaseGuardSpan::Is(CaseRelOp::Equal, expr_integer(1, 3, 22))],
4461                        body: vec![
4462                            make_bare_builtin_call("A", 4, 21),
4463                            Statement::End(EndSpan { code: None }),
4464                            make_bare_builtin_call("B", 6, 21),
4465                        ],
4466                    },
4467                    CaseSpan {
4468                        guards: vec![CaseGuardSpan::Is(CaseRelOp::Equal, expr_integer(2, 7, 22))],
4469                        body: vec![
4470                            make_bare_builtin_call("C", 8, 21),
4471                            Statement::End(EndSpan {
4472                                code: Some(Expr::Integer(IntegerSpan { value: 8, pos: lc(9, 25) })),
4473                            }),
4474                            make_bare_builtin_call("D", 10, 21),
4475                        ],
4476                    },
4477                    CaseSpan {
4478                        guards: vec![],
4479                        body: vec![
4480                            make_bare_builtin_call("E", 12, 21),
4481                            Statement::End(EndSpan { code: None }),
4482                            make_bare_builtin_call("F", 14, 21),
4483                        ],
4484                    },
4485                ],
4486                end_pos: lc(15, 13),
4487            })],
4488        );
4489    }
4490
4491    #[test]
4492    fn test_select_case_guards_equals() {
4493        do_ok_test(
4494            "SELECT CASE 7: CASE 9, 10, FALSE: END SELECT",
4495            &[Statement::Select(SelectSpan {
4496                expr: expr_integer(7, 1, 13),
4497                cases: vec![CaseSpan {
4498                    guards: vec![
4499                        CaseGuardSpan::Is(CaseRelOp::Equal, expr_integer(9, 1, 21)),
4500                        CaseGuardSpan::Is(CaseRelOp::Equal, expr_integer(10, 1, 24)),
4501                        CaseGuardSpan::Is(CaseRelOp::Equal, expr_boolean(false, 1, 28)),
4502                    ],
4503                    body: vec![],
4504                }],
4505                end_pos: lc(1, 35),
4506            })],
4507        );
4508    }
4509
4510    #[test]
4511    fn test_select_case_guards_is() {
4512        do_ok_test(
4513            "SELECT CASE 7: CASE IS = 1, IS <> 2, IS < 3, IS <= 4, IS > 5, IS >= 6: END SELECT",
4514            &[Statement::Select(SelectSpan {
4515                expr: expr_integer(7, 1, 13),
4516                cases: vec![CaseSpan {
4517                    guards: vec![
4518                        CaseGuardSpan::Is(CaseRelOp::Equal, expr_integer(1, 1, 26)),
4519                        CaseGuardSpan::Is(CaseRelOp::NotEqual, expr_integer(2, 1, 35)),
4520                        CaseGuardSpan::Is(CaseRelOp::Less, expr_integer(3, 1, 43)),
4521                        CaseGuardSpan::Is(CaseRelOp::LessEqual, expr_integer(4, 1, 52)),
4522                        CaseGuardSpan::Is(CaseRelOp::Greater, expr_integer(5, 1, 60)),
4523                        CaseGuardSpan::Is(CaseRelOp::GreaterEqual, expr_integer(6, 1, 69)),
4524                    ],
4525                    body: vec![],
4526                }],
4527                end_pos: lc(1, 72),
4528            })],
4529        );
4530    }
4531
4532    #[test]
4533    fn test_select_case_guards_to() {
4534        do_ok_test(
4535            "SELECT CASE 7: CASE 1 TO 20, 10 TO 1: END SELECT",
4536            &[Statement::Select(SelectSpan {
4537                expr: expr_integer(7, 1, 13),
4538                cases: vec![CaseSpan {
4539                    guards: vec![
4540                        CaseGuardSpan::To(expr_integer(1, 1, 21), expr_integer(20, 1, 26)),
4541                        CaseGuardSpan::To(expr_integer(10, 1, 30), expr_integer(1, 1, 36)),
4542                    ],
4543                    body: vec![],
4544                }],
4545                end_pos: lc(1, 39),
4546            })],
4547        );
4548    }
4549
4550    #[test]
4551    fn test_select_errors() {
4552        do_error_test("SELECT\n", "1:7: Expecting CASE after SELECT");
4553        do_error_test("SELECT CASE\n", "1:12: No expression in SELECT CASE statement");
4554        do_error_test("SELECT CASE 3 + 7", "1:18: Expecting newline after SELECT CASE");
4555        do_error_test("SELECT CASE 3 + 7 ,", "1:19: Expecting newline after SELECT CASE");
4556        do_error_test("SELECT CASE 3 + 7 IF", "1:19: Unexpected keyword in expression");
4557
4558        do_error_test("SELECT CASE 1\n", "1:1: SELECT without END SELECT");
4559
4560        do_error_test(
4561            "SELECT CASE 1\nEND",
4562            "2:1: Expected CASE after SELECT CASE before any statement",
4563        );
4564        do_error_test(
4565            "SELECT CASE 1\nEND IF",
4566            "2:1: Expected CASE after SELECT CASE before any statement",
4567        );
4568        do_error_test(
4569            "SELECT CASE 1\na = 1",
4570            "2:1: Expected CASE after SELECT CASE before any statement",
4571        );
4572
4573        do_error_test(
4574            "SELECT CASE 1\nCASE 1",
4575            "2:7: Expected comma, newline, or TO after expression",
4576        );
4577        do_error_test("SELECT CASE 1\nCASE ELSE", "2:10: Expecting newline after CASE");
4578
4579        do_error_test("SELECT CASE 1\nCASE ELSE\nEND", "1:1: SELECT without END SELECT");
4580        do_error_test("SELECT CASE 1\nCASE ELSE\nEND IF", "3:1: END IF without IF");
4581
4582        do_error_test("SELECT CASE 1\nCASE ELSE\nCASE ELSE\n", "3:1: CASE ELSE must be unique");
4583        do_error_test("SELECT CASE 1\nCASE ELSE\nCASE 1\n", "3:1: CASE ELSE is not last");
4584    }
4585
4586    #[test]
4587    fn test_select_case_errors() {
4588        fn do_case_error_test(cases: &str, exp_error: &str) {
4589            do_error_test(&format!("SELECT CASE 1\nCASE {}\n", cases), exp_error);
4590        }
4591
4592        do_case_error_test("ELSE, ELSE", "2:10: Expected newline after CASE ELSE");
4593        do_case_error_test("ELSE, 7", "2:10: Expected newline after CASE ELSE");
4594        do_case_error_test("7, ELSE", "2:9: CASE ELSE must be on its own");
4595
4596        do_case_error_test("IS 7", "2:9: Expected relational operator");
4597        do_case_error_test("IS AND", "2:9: Expected relational operator");
4598        do_case_error_test("IS END", "2:9: Expected relational operator");
4599
4600        do_case_error_test("IS <>", "2:11: Missing expression after relational operator");
4601        do_case_error_test("IS <> IF", "2:12: Unexpected keyword in expression");
4602
4603        do_case_error_test("", "2:6: Missing expression in CASE guard");
4604        do_case_error_test("2 + 5 TO", "2:14: Missing expression after TO in CASE guard");
4605        do_case_error_test("2 + 5 TO AS", "2:15: Missing expression after TO in CASE guard");
4606        do_case_error_test(
4607            "2 + 5 TO 8 AS",
4608            "2:17: Expected comma, newline, or TO after expression",
4609        );
4610    }
4611
4612    #[test]
4613    fn test_sub_empty() {
4614        do_ok_test(
4615            "SUB foo\nEND SUB",
4616            &[Statement::Callable(CallableSpan {
4617                name: VarRef::new("foo", None),
4618                name_pos: lc(1, 5),
4619                params: vec![],
4620                body: vec![],
4621                end_pos: lc(2, 1),
4622            })],
4623        );
4624    }
4625
4626    #[test]
4627    fn test_sub_some_content() {
4628        do_ok_test(
4629            r#"
4630                SUB foo
4631                    A
4632                    END
4633                    END 8
4634                    B
4635                END SUB
4636            "#,
4637            &[Statement::Callable(CallableSpan {
4638                name: VarRef::new("foo", None),
4639                name_pos: lc(2, 21),
4640                params: vec![],
4641                body: vec![
4642                    make_bare_builtin_call("A", 3, 21),
4643                    Statement::End(EndSpan { code: None }),
4644                    Statement::End(EndSpan {
4645                        code: Some(Expr::Integer(IntegerSpan { value: 8, pos: lc(5, 25) })),
4646                    }),
4647                    make_bare_builtin_call("B", 6, 21),
4648                ],
4649                end_pos: lc(7, 17),
4650            })],
4651        );
4652    }
4653
4654    #[test]
4655    fn test_sub_one_param() {
4656        do_ok_test(
4657            "SUB foo(x)\nEND SUB",
4658            &[Statement::Callable(CallableSpan {
4659                name: VarRef::new("foo", None),
4660                name_pos: lc(1, 5),
4661                params: vec![VarRef::new("x", None)],
4662                body: vec![],
4663                end_pos: lc(2, 1),
4664            })],
4665        );
4666    }
4667
4668    #[test]
4669    fn test_sub_multiple_params() {
4670        do_ok_test(
4671            "SUB foo(x$, y, z AS BOOLEAN)\nEND SUB",
4672            &[Statement::Callable(CallableSpan {
4673                name: VarRef::new("foo", None),
4674                name_pos: lc(1, 5),
4675                params: vec![
4676                    VarRef::new("x", Some(ExprType::Text)),
4677                    VarRef::new("y", None),
4678                    VarRef::new("z", Some(ExprType::Boolean)),
4679                ],
4680                body: vec![],
4681                end_pos: lc(2, 1),
4682            })],
4683        );
4684    }
4685
4686    #[test]
4687    fn test_sub_errors() {
4688        do_error_test("SUB", "1:4: Expected a function name after SUB");
4689        do_error_test("SUB foo", "1:8: Expected newline after SUB name");
4690        do_error_test("SUB foo 3", "1:9: Expected newline after SUB name");
4691        do_error_test("SUB foo\nEND", "1:1: SUB without END SUB");
4692        do_error_test("SUB foo\nEND IF", "2:1: END IF without IF");
4693        do_error_test("SUB foo\nEND FUNCTION", "2:1: END FUNCTION without FUNCTION");
4694        do_error_test(
4695            "SUB foo\nSUB bar\nEND SUB\nEND SUB",
4696            "2:1: Cannot nest FUNCTION or SUB definitions",
4697        );
4698        do_error_test(
4699            "SUB foo\nFUNCTION bar\nEND FUNCTION\nEND SUB",
4700            "2:1: Cannot nest FUNCTION or SUB definitions",
4701        );
4702        do_error_test("SUB foo (", "1:10: Expected a parameter name");
4703        do_error_test("SUB foo ()", "1:10: Expected a parameter name");
4704        do_error_test("SUB foo (,)", "1:10: Expected a parameter name");
4705        do_error_test("SUB foo (a,)", "1:12: Expected a parameter name");
4706        do_error_test("SUB foo (,b)", "1:10: Expected a parameter name");
4707        do_error_test("SUB foo (a AS)", "1:14: Invalid type name ) in AS type definition");
4708        do_error_test("SUB foo (a INTEGER)", "1:12: Expected comma, AS, or end of parameters list");
4709        do_error_test("SUB foo (a? AS BOOLEAN)", "1:10: Type annotation not allowed in a?");
4710        do_error_test(
4711            "SUB foo$",
4712            "1:5: SUBs cannot return a value so type annotations are not allowed",
4713        );
4714        do_error_test(
4715            "SUB foo$\nEND SUB",
4716            "1:5: SUBs cannot return a value so type annotations are not allowed",
4717        );
4718    }
4719
4720    #[test]
4721    fn test_while_empty() {
4722        do_ok_test(
4723            "WHILE 2 + 3\nWEND",
4724            &[Statement::While(WhileSpan {
4725                expr: Expr::Add(Box::from(BinaryOpSpan {
4726                    lhs: expr_integer(2, 1, 7),
4727                    rhs: expr_integer(3, 1, 11),
4728                    pos: lc(1, 9),
4729                })),
4730                body: vec![],
4731            })],
4732        );
4733        do_ok_test(
4734            "WHILE 5\n\nREM foo\n\nWEND\n",
4735            &[Statement::While(WhileSpan { expr: expr_integer(5, 1, 7), body: vec![] })],
4736        );
4737    }
4738
4739    #[test]
4740    fn test_while_loops() {
4741        do_ok_test(
4742            "WHILE TRUE\nA\nB\nWEND",
4743            &[Statement::While(WhileSpan {
4744                expr: expr_boolean(true, 1, 7),
4745                body: vec![make_bare_builtin_call("A", 2, 1), make_bare_builtin_call("B", 3, 1)],
4746            })],
4747        );
4748    }
4749
4750    #[test]
4751    fn test_while_nested() {
4752        let code = r#"
4753            WHILE TRUE
4754                A
4755                WHILE FALSE
4756                    B
4757                WEND
4758                C
4759            WEND
4760        "#;
4761        do_ok_test(
4762            code,
4763            &[Statement::While(WhileSpan {
4764                expr: expr_boolean(true, 2, 19),
4765                body: vec![
4766                    make_bare_builtin_call("A", 3, 17),
4767                    Statement::While(WhileSpan {
4768                        expr: expr_boolean(false, 4, 23),
4769                        body: vec![make_bare_builtin_call("B", 5, 21)],
4770                    }),
4771                    make_bare_builtin_call("C", 7, 17),
4772                ],
4773            })],
4774        );
4775    }
4776
4777    #[test]
4778    fn test_while_errors() {
4779        do_error_test("WHILE\n", "1:6: No expression in WHILE statement");
4780        do_error_test("WHILE TRUE", "1:11: Expecting newline after WHILE");
4781        do_error_test("\n\nWHILE TRUE\n", "3:1: WHILE without WEND");
4782        do_error_test("WHILE TRUE\nEND", "1:1: WHILE without WEND");
4783        do_error_test("WHILE TRUE\nEND\n", "1:1: WHILE without WEND");
4784        do_error_test("WHILE TRUE\nEND WHILE\n", "2:5: Unexpected keyword in expression");
4785
4786        do_error_test("WHILE ,\nWEND", "1:7: No expression in WHILE statement");
4787        do_error_test("WHILE ,\nEND", "1:7: No expression in WHILE statement");
4788    }
4789}