1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//! Sub statement parsing for VB6 CST.
//!
//! This module handles parsing of VB6 Sub statements with syntax:
//!
//! \[ Public | Private | Friend \] \[ Static \] Sub name \[ ( arglist ) \]
//! \[ statements \]
//! \[ Exit Sub \]
//! \[ statements \]
//! End Sub
//!
//! [Reference](https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/sub-statement)
use crate::language::Token;
use crate::parsers::SyntaxKind;
use super::Parser;
impl Parser<'_> {
/// Parse a Visual Basic 6 subroutine with syntax:
///
/// \[ Public | Private | Friend \] \[ Static \] Sub name \[ ( arglist ) \]
/// \[ statements \]
/// \[ Exit Sub \]
/// \[ statements \]
/// End Sub
///
/// The Sub statement syntax has these parts:
///
/// | Part | Optional / Required | Description |
/// |-------------|---------------------|-------------|
/// | Public | Optional | Indicates that the Sub procedure is accessible to all other procedures in all modules. If used in a module that contains an Option Private statement, the procedure is not available outside the project. |
/// | Private | Optional | Indicates that the Sub procedure is accessible only to other procedures in the module where it is declared. |
/// | Friend | Optional | Used only in a class module. Indicates that the Sub procedure is visible throughout the project, but not visible to a controller of an instance of an object. |
/// | Static | Optional | Indicates that the Sub procedure's local variables are preserved between calls. The Static attribute doesn't affect variables that are declared outside the Sub, even if they are used in the procedure. |
/// | name | Required | Name of the Sub; follows standard variable naming conventions. |
/// | arglist | Optional | List of variables representing arguments that are passed to the Sub procedure when it is called. Multiple variables are separated by commas. |
/// | statements | Optional | Any group of statements to be executed within the Sub procedure.
///
/// The arglist argument has the following syntax and parts:
///
/// \[ Optional \] \[ `ByVal` | `ByRef` \] \[ `ParamArray` \] varname \[ ( ) \] \[ As type \] \[ = defaultvalue \]
///
/// [Reference](https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/sub-statement)
pub(crate) fn parse_sub_statement(&mut self) {
// if we are now parsing a sub statement, we are no longer in the header.
self.parsing_header = false;
self.builder.start_node(SyntaxKind::SubStatement.to_raw());
// Consume any leading whitespace
self.consume_whitespace();
// Consume optional Public/Private/Friend keyword
if self.at_token(Token::PublicKeyword)
|| self.at_token(Token::PrivateKeyword)
|| self.at_token(Token::FriendKeyword)
{
self.consume_token();
// Consume any whitespace after visibility modifier
self.consume_whitespace();
}
// Consume optional Static keyword
if self.at_token(Token::StaticKeyword) {
self.consume_token();
// Consume any whitespace after Static
self.consume_whitespace();
}
// Consume "Sub" keyword
self.consume_token();
// Consume any whitespace after "Sub"
self.consume_whitespace();
// Consume procedure name (keywords can be used as procedure names in VB6)
if self.at_token(Token::Identifier) {
self.consume_token();
} else if self.at_keyword() {
self.consume_token_as_identifier();
}
// Consume any whitespace before parameter list
self.consume_whitespace();
// Parse parameter list if present
if self.at_token(Token::LeftParenthesis) {
self.parse_parameter_list();
}
// Consume everything until newline (preserving all tokens)
self.consume_until_after(Token::Newline);
// Parse body until "End Sub"
self.parse_statement_list(|parser| {
(parser.at_token(Token::EndKeyword)
&& parser.peek_next_keyword() == Some(Token::SubKeyword))
|| parser.at_component_declaration_start()
});
// Consume "End Sub" and trailing tokens, or report mismatch as recovery.
self.consume_expected_end_keyword_terminator(Token::SubKeyword, "Sub");
self.builder.finish_node(); // SubStatement
}
}