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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
//! Property statement parsing for VB6 CST.
//!
//! This module handles parsing of VB6 Property statements:
//! - Property Get
//! - Property Let
//! - Property Set
use crate::language::Token;
use crate::parsers::SyntaxKind;
use super::Parser;
impl Parser<'_> {
/// Check if the current position is at a compiler conditional Property statement.
///
/// This includes statements like:
/// - `#If ... Then`
/// - `#ElseIf ... Then`
/// - `#Else`
/// - `#End If`
pub(crate) fn is_at_compiler_conditional_property_statement(&self) -> bool {
if !self.at_compiler_directive_keyword(Token::IfKeyword) {
return false;
}
let mut index = self.pos;
while let Some((_, token)) = self.tokens.get(index) {
index += 1;
if *token == Token::Newline {
break;
}
}
while let Some((_, token)) = self.tokens.get(index) {
match token {
Token::Whitespace
| Token::Newline
| Token::PublicKeyword
| Token::PrivateKeyword
| Token::FriendKeyword
| Token::StaticKeyword => {
index += 1;
}
Token::PropertyKeyword => return true,
_ => return false,
}
}
false
}
/// Parse a Property statement (Property Get, Property Let, or Property Set).
///
/// VB6 Property statement syntax:
/// - [Public | Private | Friend] [Static] Property Get name [(arglist)] [As type]
/// - [Public | Private | Friend] [Static] Property Let name ([arglist,] value)
/// - [Public | Private | Friend] [Static] Property Set name ([arglist,] value)
///
/// [Reference](https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/property-get-statement)
/// [Reference](https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/property-let-statement)
/// [Reference](https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/property-set-statement)
pub(crate) fn parse_property_statement(&mut self) {
// if we are now parsing a property statement, we are no longer in the header.
self.parsing_header = false;
self.builder
.start_node(SyntaxKind::PropertyStatement.to_raw());
self.consume_property_signature_line();
// Parse body until "End Property"
self.parse_statement_list(|parser| {
(parser.at_token(Token::EndKeyword)
&& parser.peek_next_keyword() == Some(Token::PropertyKeyword))
|| parser.at_component_declaration_start()
});
self.consume_property_terminator();
self.builder.finish_node(); // PropertyStatement
}
pub(crate) fn parse_compiler_conditional_property_statement(&mut self) {
self.parsing_header = false;
self.builder
.start_node(SyntaxKind::PropertyStatement.to_raw());
self.consume_until_after(Token::Newline);
self.consume_property_signature_line();
while self.at_compiler_directive_keyword(Token::ElseIfKeyword)
|| self.at_compiler_directive_keyword(Token::ElseKeyword)
{
self.consume_until_after(Token::Newline);
self.consume_property_signature_line();
}
if self.at_compiler_end_if_directive() {
self.consume_until_after(Token::Newline);
}
self.parse_statement_list(|parser| {
(parser.at_token(Token::EndKeyword)
&& parser.peek_next_keyword() == Some(Token::PropertyKeyword))
|| parser.at_component_declaration_start()
});
self.consume_property_terminator();
if self.at_compiler_end_if_directive() {
self.consume_compiler_directive_prefix();
self.consume_until_after(Token::Newline);
}
self.builder.finish_node(); // PropertyStatement
}
fn consume_property_signature_line(&mut self) {
// 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 "Property" keyword
self.consume_token();
// Consume any whitespace after "Property"
self.consume_whitespace();
// Consume Get/Let/Set keyword
if self.at_token(Token::GetKeyword)
|| self.at_token(Token::LetKeyword)
|| self.at_token(Token::SetKeyword)
{
self.consume_token();
}
// Consume any whitespace after Get/Let/Set
self.consume_whitespace();
// Consume property name (keywords can be used as property 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 (includes "As Type" if present)
self.consume_until_after(Token::Newline);
}
fn consume_property_terminator(&mut self) {
// Consume "End Property" and trailing tokens, or report mismatch as recovery.
self.consume_expected_end_keyword_terminator(Token::PropertyKeyword, "Property");
}
}