use crate::language::Token;
use crate::parsers::cst::Parser;
use crate::parsers::SyntaxKind;
impl Parser<'_> {
pub(crate) fn parse_on_error_statement(&mut self) {
self.parsing_header = false;
self.builder
.start_node(SyntaxKind::OnErrorStatement.to_raw());
self.consume_whitespace();
self.consume_token();
if self.at_token(Token::ErrorKeyword) {
self.consume_token();
}
self.consume_until_after(Token::Newline);
self.builder.finish_node(); }
pub(crate) fn parse_on_goto_statement(&mut self) {
self.parsing_header = false;
self.builder
.start_node(SyntaxKind::OnGoToStatement.to_raw());
self.consume_whitespace();
self.consume_token();
self.consume_until_after(Token::Newline);
self.builder.finish_node(); }
pub(crate) fn parse_on_gosub_statement(&mut self) {
self.parsing_header = false;
self.builder
.start_node(SyntaxKind::OnGoSubStatement.to_raw());
self.consume_whitespace();
self.consume_token();
self.consume_until_after(Token::Newline);
self.builder.finish_node(); }
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn on_error_goto_label() {
let source = r"
Sub Test()
On Error GoTo ErrorHandler
' Code that might error
Exit Sub
ErrorHandler:
MsgBox Err.Description
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/syntax/statements/control_flow/on_statements",
);
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn on_error_resume_next() {
let source = r#"
Sub Test()
On Error Resume Next
MkDir "C:\Temp"
MkDir "C:\Data"
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/syntax/statements/control_flow/on_statements",
);
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn on_error_goto_0() {
let source = r"
Sub Test()
On Error GoTo 0
' Error handling disabled
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/syntax/statements/control_flow/on_statements",
);
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn on_error_at_module_level() {
let source = r"On Error GoTo ErrorHandler";
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/syntax/statements/control_flow/on_statements",
);
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn on_error_with_whitespace() {
let source = "On Error GoTo Handler\n";
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/syntax/statements/control_flow/on_statements",
);
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn on_error_with_comment() {
let source = r"
Sub Test()
On Error GoTo ErrorHandler ' Setup error handling
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/syntax/statements/control_flow/on_statements",
);
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn on_error_in_if_statement() {
let source = r"
Sub Test()
If needsErrorHandling Then
On Error GoTo ErrorHandler
End If
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/syntax/statements/control_flow/on_statements",
);
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn on_error_inline_if() {
let source = r"
Sub Test()
If debug Then On Error GoTo 0
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/syntax/statements/control_flow/on_statements",
);
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn multiple_on_error_statements() {
let source = r"
Sub Test()
On Error GoTo ErrorHandler
' Do something
On Error GoTo 0
' Disable error handling
On Error Resume Next
' Continue on error
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/syntax/statements/control_flow/on_statements",
);
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn on_error_complete_pattern() {
let source = r#"
Sub ProcessFile(filePath As String)
On Error GoTo ErrorHandler
Open filePath For Input As #1
' Process file
Close #1
Exit Sub
ErrorHandler:
If Err.Number <> 0 Then
MsgBox "Error: " & Err.Description
End If
Resume 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/syntax/statements/control_flow/on_statements",
);
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn on_error_numeric_label() {
let source = r#"
Sub Test()
On Error GoTo 100
' Code
Exit Sub
100:
MsgBox "Error"
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/syntax/statements/control_flow/on_statements",
);
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn on_error_nested_procedures() {
let source = r#"
Sub Outer()
On Error GoTo OuterError
Inner
Exit Sub
OuterError:
MsgBox "Outer error"
End Sub
Sub Inner()
On Error Resume Next
' Code
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/syntax/statements/control_flow/on_statements",
);
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn on_goto_simple() {
let source = r#"
Sub Test()
Dim choice As Integer
choice = 2
On choice GoTo Label1, Label2, Label3
Exit Sub
Label1:
MsgBox "Choice 1"
Exit Sub
Label2:
MsgBox "Choice 2"
Exit Sub
Label3:
MsgBox "Choice 3"
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/syntax/statements/control_flow/on_statements",
);
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn on_goto_with_expression() {
let source = r"
Sub Test()
On x + 1 GoTo First, Second, Third
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/syntax/statements/control_flow/on_statements",
);
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn on_goto_single_label() {
let source = r#"
Sub Test()
On errorCode GoTo ErrorHandler
ErrorHandler:
MsgBox "Error"
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/syntax/statements/control_flow/on_statements",
);
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn on_goto_numeric_labels() {
let source = r"
Sub Test()
On choice GoTo 100, 200, 300
Exit Sub
100:
x = 1
Exit Sub
200:
x = 2
Exit Sub
300:
x = 3
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/syntax/statements/control_flow/on_statements",
);
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn on_goto_preserves_whitespace() {
let source = r"
Sub Test()
On choice GoTo Label1 , Label2
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/syntax/statements/control_flow/on_statements",
);
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn on_gosub_simple() {
let source = r#"
Sub Test()
Dim menuChoice As Integer
menuChoice = 1
On menuChoice GoSub Menu1, Menu2, Menu3
Exit Sub
Menu1:
MsgBox "Menu 1 selected"
Return
Menu2:
MsgBox "Menu 2 selected"
Return
Menu3:
MsgBox "Menu 3 selected"
Return
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/syntax/statements/control_flow/on_statements",
);
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn on_gosub_with_expression() {
let source = r"
Sub Test()
On x Mod 3 GoSub First, Second, Third
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/syntax/statements/control_flow/on_statements",
);
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn on_gosub_single_label() {
let source = r#"
Sub Test()
On flag GoSub Handler
Exit Sub
Handler:
Debug.Print "Called"
Return
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/syntax/statements/control_flow/on_statements",
);
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn on_gosub_numeric_labels() {
let source = r#"
Sub Test()
On choice GoSub 100, 200, 300
Exit Sub
100:
Debug.Print "100"
Return
200:
Debug.Print "200"
Return
300:
Debug.Print "300"
Return
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/syntax/statements/control_flow/on_statements",
);
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn on_gosub_preserves_whitespace() {
let source = r"
Sub Test()
On choice GoSub Sub1 , Sub2
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/syntax/statements/control_flow/on_statements",
);
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
}