use crate::language::Token;
use crate::parsers::SyntaxKind;
use super::Parser;
impl Parser<'_> {
pub(super) fn parse_for_statement(&mut self) {
self.parsing_header = false;
self.builder.start_node(SyntaxKind::ForStatement.to_raw());
self.consume_whitespace();
self.consume_token();
self.parse_lvalue();
self.consume_whitespace();
if self.at_token(Token::EqualityOperator) {
self.consume_token();
}
self.consume_whitespace();
self.parse_expression();
self.consume_whitespace();
if self.at_token(Token::ToKeyword) {
self.consume_token();
self.consume_whitespace();
self.parse_expression();
self.consume_whitespace();
if self.at_token(Token::StepKeyword) {
self.consume_token();
self.consume_whitespace();
self.parse_expression();
}
}
self.consume_until_after(Token::Newline);
self.parse_statement_list(|parser| parser.at_token(Token::NextKeyword));
if self.at_token(Token::NextKeyword) {
self.consume_token();
self.consume_until_after(Token::Newline);
}
self.builder.finish_node(); }
pub(super) fn parse_for_each_statement(&mut self) {
self.parsing_header = false;
self.builder
.start_node(SyntaxKind::ForEachStatement.to_raw());
self.consume_whitespace();
self.consume_token();
self.consume_whitespace();
if self.at_token(Token::EachKeyword) {
self.consume_token();
}
while !self.is_at_end()
&& !self.at_token(Token::InKeyword)
&& !self.at_token(Token::Newline)
{
self.consume_token();
}
if self.at_token(Token::InKeyword) {
self.consume_token();
self.consume_until(Token::Newline);
}
self.consume_until_after(Token::Newline);
self.parse_statement_list(|parser| parser.at_token(Token::NextKeyword));
if self.at_token(Token::NextKeyword) {
self.consume_token();
self.consume_until_after(Token::Newline);
}
self.builder.finish_node(); }
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn simple_for_loop() {
let source = r"
Sub TestSub()
For i = 1 To 10
Debug.Print i
Next i
End Sub
";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/parsers/cst/for_statements");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn for_loop_with_step() {
let source = r"
Sub TestSub()
For i = 1 To 100 Step 5
Debug.Print i
Next i
End Sub
";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/parsers/cst/for_statements");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn for_loop_with_negative_step() {
let source = r"
Sub TestSub()
For i = 10 To 1 Step -1
Debug.Print i
Next i
End Sub
";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/parsers/cst/for_statements");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn for_loop_without_counter_after_next() {
let source = r"
Sub TestSub()
For i = 1 To 10
Debug.Print i
Next
End Sub
";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/parsers/cst/for_statements");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn nested_for_loops() {
let source = r"
Sub TestSub()
For i = 1 To 5
For j = 1 To 5
Debug.Print i * j
Next j
Next i
End Sub
";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/parsers/cst/for_statements");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn for_loop_with_function_calls() {
let source = r"
Sub TestSub()
For i = GetStart() To GetEnd() Step GetStep()
Debug.Print i
Next i
End Sub
";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/parsers/cst/for_statements");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn for_loop_preserves_whitespace() {
let source = r"
Sub TestSub()
For i = 1 To 10 Step 2
Debug.Print i
Next i
End Sub
";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/parsers/cst/for_statements");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn multiple_for_loops_in_sequence() {
let source = r#"
Sub TestSub()
For i = 1 To 5
Debug.Print "First: " & i
Next i
For j = 10 To 20 Step 2
Debug.Print "Second: " & j
Next j
End Sub
"#;
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/parsers/cst/for_statements");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn for_each_loop_simple() {
let source = r"
Sub TestSub()
For Each item In collection
Debug.Print item
Next item
End Sub
";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/parsers/cst/for_statements");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn for_each_loop_without_variable_after_next() {
let source = r"
Sub TestSub()
For Each element In myArray
Debug.Print element
Next
End Sub
";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/parsers/cst/for_statements");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn nested_for_and_for_each() {
let source = r"
Sub TestSub()
For i = 1 To 10
For Each item In items(i)
Debug.Print item
Next item
Next i
End Sub
";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/parsers/cst/for_statements");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
}