1use crate::ast::*;
19use crate::lexer::{Lexer, PeekableLexer, Token, TokenSpan};
20use crate::reader::LineCol;
21use std::cmp::Ordering;
22use std::io;
23
24#[derive(Debug, thiserror::Error)]
26pub enum Error {
27 #[error("{}: {}", .0, .1)]
29 Bad(LineCol, String),
30
31 #[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
42pub type Result<T> = std::result::Result<T, Error>;
44
45fn 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
55pub(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#[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 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 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
168struct ExprOpSpan {
170 op: ExprOp,
172
173 pos: LineCol,
175}
176
177impl ExprOpSpan {
178 fn new(op: ExprOp, pos: LineCol) -> Self {
180 Self { op, pos }
181 }
182
183 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
242pub struct Parser<'a> {
244 lexer: PeekableLexer<'a>,
245}
246
247impl<'a> Parser<'a> {
248 fn from(input: &'a mut dyn io::Read) -> Self {
250 Self { lexer: Lexer::from(input).peekable() }
251 }
252
253 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 fn parse_comma_separated_exprs(&mut self) -> Result<Vec<ArgSpan>> {
711 let mut spans = vec![];
712
713 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 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 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; 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 return Ok(Some(Statement::Label(LabelSpan { name, name_pos: token_span.pos })));
1892 }
1893 Token::Label(name) => {
1894 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 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 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
1992pub(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 fn lc(line: usize, col: usize) -> LineCol {
2004 LineCol { line, col }
2005 }
2006
2007 fn expr_boolean(value: bool, line: usize, col: usize) -> Expr {
2009 Expr::Boolean(BooleanSpan { value, pos: LineCol { line, col } })
2010 }
2011
2012 fn expr_double(value: f64, line: usize, col: usize) -> Expr {
2014 Expr::Double(DoubleSpan { value, pos: LineCol { line, col } })
2015 }
2016
2017 fn expr_integer(value: i32, line: usize, col: usize) -> Expr {
2019 Expr::Integer(IntegerSpan { value, pos: LineCol { line, col } })
2020 }
2021
2022 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 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 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 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 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 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 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 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 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 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 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 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}