Skip to main content

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