001/*-------------------------------------------------------------------------+ 002| | 003| Copyright 2005-2011 the ConQAT Project | 004| | 005| Licensed under the Apache License, Version 2.0 (the "License"); | 006| you may not use this file except in compliance with the License. | 007| You may obtain a copy of the License at | 008| | 009| http://www.apache.org/licenses/LICENSE-2.0 | 010| | 011| Unless required by applicable law or agreed to in writing, software | 012| distributed under the License is distributed on an "AS IS" BASIS, | 013| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 014| See the License for the specific language governing permissions and | 015| limitations under the License. | 016+-------------------------------------------------------------------------*/ 017package eu.cqse.check.framework.shallowparser.languages.visualbasic; 018 019import static eu.cqse.check.framework.scanner.ETokenType.*; 020import static eu.cqse.check.framework.shallowparser.languages.visualbasic.EVisualBasicParserState.IN_ENUM; 021import static eu.cqse.check.framework.shallowparser.languages.visualbasic.EVisualBasicParserState.IN_EVENT_DECLARATION; 022import static eu.cqse.check.framework.shallowparser.languages.visualbasic.EVisualBasicParserState.IN_INTERFACE; 023import static eu.cqse.check.framework.shallowparser.languages.visualbasic.EVisualBasicParserState.IN_METHOD; 024import static eu.cqse.check.framework.shallowparser.languages.visualbasic.EVisualBasicParserState.IN_SINGLE_LINE_IF; 025import static eu.cqse.check.framework.shallowparser.languages.visualbasic.EVisualBasicParserState.IN_TYPE; 026import static eu.cqse.check.framework.shallowparser.languages.visualbasic.EVisualBasicParserState.TOP_LEVEL; 027 028import java.util.Arrays; 029import java.util.EnumSet; 030import java.util.List; 031 032import eu.cqse.check.framework.scanner.ETokenType; 033import eu.cqse.check.framework.shallowparser.SubTypeNames; 034import eu.cqse.check.framework.shallowparser.framework.EShallowEntityType; 035import eu.cqse.check.framework.shallowparser.framework.PropertyAccessNameResolver; 036import eu.cqse.check.framework.shallowparser.framework.RecognizerBase; 037import eu.cqse.check.framework.shallowparser.framework.ShallowParserBase; 038 039/** 040 * VisualBasic.NET shallow parser. 041 * 042 * The shallow parser follows the 043 * <a href="https://msdn.microsoft.com/en-us/library/aa711636(v=vs.71).aspx">VB. 044 * NET</a> specification at 045 * 046 * 047 * Notes: 048 * <ul> 049 * <li>At the current state the parser should support multiple visual basic 050 * dialects. There is only one exception: The parser can not deal with single 051 * line if statements in other dialects. Expect incomplete entities in those 052 * cases.</li> 053 * <li>Some unreserved keywords are treated like reserved keywords, as they are 054 * only valid in a certain context (e.g. Linq expressions) and we currently do 055 * not have such functionality ( 056 * <a href="https://msdn.microsoft.com/en-us/library/dd409611.aspx">VB.NET 057 * Keywords</a>).</li> 058 * <li>Getter and Setter of properties are reported as e.g. "getter for <name of 059 * property>".</li> 060 * <li>Attribute lists that give additional compiler information to all kinds of 061 * types, methods etc. are reported as meta annotations.</li> 062 * <li>Preprocessor directives are reported as meta entity.</li> 063 * <li>Do-Until loops are reported as do-while loops. Note that do loops in 064 * VB.NET may behave like while loops, depending on the position of the 065 * condition.</li> 066 * <li>The Declare structure is reported as method.</li> 067 * <li>The form meta data is not parsed into.</li> 068 * <li>Lambdas are not distinguished from other variables.</li> 069 * </ul> 070 */ 071public class VisualBasicShallowParser extends ShallowParserBase<EVisualBasicParserState> { 072 073 /** 074 * All token types that open a new scope. 075 */ 076 private static final List<ETokenType> OPENING_TYPES = Arrays.asList(LPAREN, LBRACE, LBRACK); 077 078 /** 079 * All token types that close a scope. 080 */ 081 private static final List<ETokenType> CLOSING_TYPES = Arrays.asList(RPAREN, RBRACE, RBRACK); 082 083 /** 084 * All types that do not indicate a new line. This excludes COLON as it allows 085 * to continue with a new statement in the very same line. 086 */ 087 private static final EnumSet<ETokenType> NO_NEW_LINE = EnumSet.complementOf(EnumSet.of(EOL, COLON)); 088 089 /** 090 * All token types that may modify type, attribute or method declarations. Does 091 * not include 'MUSTOVERRIDE' as this is an indicator for a different rule. 092 */ 093 private static final EnumSet<ETokenType> DECLARATION_MODIFIER = EnumSet.of(PRIVATE, PUBLIC, PROTECTED, MUSTINHERIT, 094 NOTINHERITABLE, OVERLOADS, SHARED, OVERRIDES, SHADOWS, NOTOVERRIDABLE, FRIEND, READONLY, ASYNC, WITHEVENTS, 095 PARTIAL, WIDENING, NARROWING, CONST, STATIC, DEFAULT, DIM); 096 097 /** 098 * All token types a statement can start with. 099 */ 100 private static final EnumSet<ETokenType> STATEMENT_START = EnumSet.of(IDENTIFIER, DIM, RETURN, ME, MYBASE, THROW, 101 AWAIT, RAISEEVENT, REDIM, ADDHANDLER, REMOVEHANDLER, EXIT_DO, EXIT_FOR, EXIT_SUB, EXIT_WHILE, RESUME, 102 ONERROR, STOP, GOTO, YIELD, ERROR, CALL, CONTINUE, END, ERASE, CTYPE); 103 104 /** 105 * Types that continue a statement only if EOL is a preceding token or EOL is a 106 * following token. https://msdn.microsoft.com/en-us/library/865x40k4.aspx 107 */ 108 private static final EnumSet<ETokenType> STATEMENT_CONTINUATION_BOTH_ONLY = EnumSet.of(COMMA, EQ, ANDEQ, PLUSEQ, 109 MINUSEQ, MULTEQ, DIVEQ, INTDIVEQ, POWEREQ, LSHIFTEQ, RSHIFTEQ, PLUS, MINUS, DIV, INTDIV, MULT, MOD, NOT, GT, 110 LTEQ, GTEQ, POWER, LSHIFT, RSHIFT, ANDALSO, OR, ORELSE, LIKE, XOR, IS, IS_NOT, AGGREGATE, GROUPBY, 111 GROUPJOIN, JOIN, LET, ORDERBY, SELECT, SKIP, SKIPWHILE, TAKE, TAKEWHILE, WHERE, IN, INTO, ON, ASCENDING, 112 DESCENDING, FROM, EQUALS); 113 114 /** Types that continue a statement only if EOL is a preceding token. */ 115 private static final EnumSet<ETokenType> STATEMENT_CONTINUATION_PRECEDING_ONLY = EnumSet.of(DISTINCT); 116 117 /** Types that continue a statement only if EOL is a following token. */ 118 private static final EnumSet<ETokenType> STATEMENT_CONTINUATION_FOLLOWING_ONLY = EnumSet.of(DOT, LT); 119 120 /** 121 * Types that interrupt a statement. This is typically EOL. Furthermore it 122 * contains COLON which directly ends the statement and allows the next 123 * statement to start in the same line. Because of single line if statements we 124 * have to add ELSE here. 125 */ 126 private static final EnumSet<ETokenType> STATEMENT_INTERRUPT_TYPES = EnumSet.of(EOL, COLON, ELSE, END_IF, END_WHILE, 127 END_SUB, END_FUNCTION, END_NAMESPACE, END_USING, END_WITH, END_TRY, END_EVENT, END_SYNCLOCK, END_SELECT, 128 END_ENUM, END_CLASS, END_TYPE, END_GET, END_SET, END_OPERATOR, END_INTERFACE, END_STRUCTURE); 129 130 /** 131 * Types that continue a statement if EOL is a preceding token or EOL is a 132 * following token. https://msdn.microsoft.com/en-us/library/865x40k4.aspx 133 */ 134 private static final EnumSet<ETokenType> STATEMENT_CONTINUATION_BOTH = EnumSet 135 .copyOf(STATEMENT_CONTINUATION_BOTH_ONLY); 136 137 /** Types that continue a statement if EOL is a preceding token. */ 138 private static final EnumSet<ETokenType> STATEMENT_CONTINUATION_PRECEDING = EnumSet 139 .copyOf(STATEMENT_CONTINUATION_PRECEDING_ONLY); 140 141 /** Types that continue a statement if EOL is a following token. */ 142 private static final EnumSet<ETokenType> STATEMENT_CONTINUATION_FOLLOWING = EnumSet 143 .copyOf(STATEMENT_CONTINUATION_FOLLOWING_ONLY); 144 /** 145 * Types that indicate a situation where implicit line conversion might be 146 * applicable or should not be applied. 147 */ 148 private static final EnumSet<ETokenType> STATEMENT_CONTINUATION_CANDIDATES = EnumSet 149 .copyOf(STATEMENT_INTERRUPT_TYPES); 150 151 static { 152 STATEMENT_CONTINUATION_CANDIDATES.addAll(STATEMENT_CONTINUATION_FOLLOWING_ONLY); 153 STATEMENT_CONTINUATION_CANDIDATES.addAll(STATEMENT_CONTINUATION_PRECEDING_ONLY); 154 STATEMENT_CONTINUATION_CANDIDATES.addAll(STATEMENT_CONTINUATION_BOTH_ONLY); 155 156 STATEMENT_CONTINUATION_PRECEDING.addAll(STATEMENT_CONTINUATION_BOTH_ONLY); 157 STATEMENT_CONTINUATION_FOLLOWING.addAll(STATEMENT_CONTINUATION_BOTH_ONLY); 158 159 STATEMENT_CONTINUATION_BOTH.addAll(STATEMENT_CONTINUATION_FOLLOWING_ONLY); 160 STATEMENT_CONTINUATION_BOTH.addAll(STATEMENT_CONTINUATION_PRECEDING_ONLY); 161 } 162 163 /** Constructor */ 164 public VisualBasicShallowParser() { 165 super(EVisualBasicParserState.class, TOP_LEVEL); 166 167 createMetaRules(); 168 createNamespaceRule(); 169 createTypeLikeRules(); 170 createMethodRules(); 171 createEventRules(); 172 createPropertyRules(); 173 createAttributeRules(); 174 createEnumRules(); 175 createSelectCaseRules(); 176 createConditionalsRules(); 177 createLoopRules(); 178 createTryCatchFinallyRules(); 179 createComplexStatementRules(); 180 createSimpleStatementRule(); 181 hideLineSeparators(); 182 } 183 184 /** 185 * Hide line separators EOL and COLON by parsing them without creating nodes. 186 */ 187 private void hideLineSeparators() { 188 189 // Does not include IN_SINGLE_LINE_IF, because we require EOL and COLON 190 // as indicators. 191 inState(TOP_LEVEL, IN_TYPE, IN_ENUM, IN_EVENT_DECLARATION, IN_INTERFACE, IN_METHOD) 192 .sequence(EnumSet.of(EOL, COLON)); 193 194 } 195 196 /** 197 * Create rules to match event declarations. 198 */ 199 private void createEventRules() { 200 skipToEndOfStatement(inState(IN_TYPE).repeated(DECLARATION_MODIFIER).sequence(EVENT).markStart() 201 .createNode(EShallowEntityType.ATTRIBUTE, "event", 0)).endNode(); 202 203 RecognizerBase<EVisualBasicParserState> base = skipToEndOfStatement( 204 inState(IN_TYPE).repeated(DECLARATION_MODIFIER).sequence(IDENTIFIER, EVENT).markStart() 205 .createNode(EShallowEntityType.ATTRIBUTE, "event", 0)); 206 base.parseUntil(IN_EVENT_DECLARATION).sequence(END_EVENT).endNode(); 207 208 // Parse the handler definitions inside of the custom event 209 EVisualBasicParserState[] states = { IN_EVENT_DECLARATION }; 210 createComplexStatementRule(states, ADDHANDLER, END, ADDHANDLER); 211 createComplexStatementRule(states, REMOVEHANDLER, END, REMOVEHANDLER); 212 createComplexStatementRule(states, RAISEEVENT, END, RAISEEVENT); 213 } 214 215 /** 216 * Create rules to match visual basics equivalent to switch/case: select/case. 217 */ 218 private void createSelectCaseRules() { 219 skipToEndOfStatement(inState(IN_METHOD, IN_SINGLE_LINE_IF).sequence(SELECTCASE) 220 .createNode(EShallowEntityType.STATEMENT, SubTypeNames.SELECT)).parseUntil(IN_METHOD) 221 .sequence(ETokenType.END_SELECT).endNode(); 222 223 skipToEndOfStatement(inState(IN_METHOD, IN_SINGLE_LINE_IF).sequence(CASE).createNode(EShallowEntityType.META, 224 SubTypeNames.CASE)).optional(ELSE).endNode(); 225 } 226 227 /** 228 * Create rules that match all kinds of loops or the goto statement. 229 */ 230 private void createLoopRules() { 231 // DO-WHILE 232 RecognizerBase<EVisualBasicParserState> baseRecognizerDoWhile = inState(IN_METHOD, IN_SINGLE_LINE_IF) 233 .sequence(DO).optional(EnumSet.of(WHILE, UNTIL)); 234 baseRecognizerDoWhile = skipToEndOfStatement( 235 baseRecognizerDoWhile.createNode(EShallowEntityType.STATEMENT, SubTypeNames.DO)).parseUntil(IN_METHOD) 236 .sequence(LOOP); 237 skipToEndOfStatement(baseRecognizerDoWhile).endNode(); 238 239 // WHILE 240 skipToEndOfStatement(inState(IN_METHOD, IN_SINGLE_LINE_IF).sequence(WHILE) 241 .createNode(EShallowEntityType.STATEMENT, SubTypeNames.WHILE)).parseUntil(IN_METHOD) 242 .sequence(EnumSet.of(END_WHILE, WEND)).endNode(); 243 244 // FOR 245 RecognizerBase<EVisualBasicParserState> baseRecognizerFor = skipToEndOfStatement( 246 inState(IN_METHOD, IN_SINGLE_LINE_IF).sequence(FOR).createNode(EShallowEntityType.STATEMENT, 247 SubTypeNames.FOR)).parseUntil(IN_METHOD).sequence(NEXT); 248 skipToEndOfStatement(baseRecognizerFor).endNode(); 249 250 } 251 252 /** 253 * Create a rule for complex blocks that contain nodes that have to end with 254 * continuation. These are e.g. try/catch/finally or if/elseif/else constructs. 255 */ 256 private void createBlockRuleWithContinuation(EnumSet<ETokenType> possibleStartTypes, 257 EnumSet<ETokenType> continuationIndicators, ETokenType terminationType) { 258 259 RecognizerBase<EVisualBasicParserState> base = inState(IN_METHOD, IN_SINGLE_LINE_IF) 260 .sequence(possibleStartTypes).createNode(EShallowEntityType.STATEMENT, -1); 261 base = skipToEndOfStatement(base).parseUntil(IN_METHOD); 262 263 base.sequenceBefore(continuationIndicators).endNodeWithContinuation(); 264 base.sequence(terminationType).endNode(); 265 } 266 267 /** 268 * Create rules that match try catch finally constructs. 269 */ 270 private void createTryCatchFinallyRules() { 271 createBlockRuleWithContinuation(EnumSet.of(TRY, CATCH, FINALLY), EnumSet.of(CATCH, FINALLY), END_TRY); 272 } 273 274 /** 275 * Create rules for more complex statements like WITH, USING and SYNCLOCK. 276 */ 277 private void createComplexStatementRules() { 278 EVisualBasicParserState[] states = { IN_METHOD, IN_SINGLE_LINE_IF }; 279 createComplexStatementRule(states, USING, END_USING); 280 createComplexStatementRule(states, WITH, END_WITH); 281 createComplexStatementRule(states, SYNCLOCK, END_SYNCLOCK); 282 } 283 284 /** 285 * Create a rule for complex statements such as WITH and USING. 286 */ 287 private void createComplexStatementRule(EVisualBasicParserState[] states, ETokenType startTokenType, 288 Object... endTokenType) { 289 RecognizerBase<EVisualBasicParserState> base = inState(states).sequence(startTokenType) 290 .createNode(EShallowEntityType.STATEMENT, 0, 1); 291 skipToEndOfStatement(base).parseUntil(IN_METHOD).sequence(endTokenType).endNode(); 292 } 293 294 /** 295 * Create the rules for enum declaration and definition. 296 */ 297 private void createEnumRules() { 298 // Enum declaration 299 RecognizerBase<EVisualBasicParserState> base = inState(TOP_LEVEL, IN_TYPE).repeated(DECLARATION_MODIFIER) 300 .sequence(ENUM).createNode(EShallowEntityType.TYPE, SubTypeNames.ENUM, 1); 301 skipToEndOfStatement(base).parseUntil(IN_ENUM).sequence(END_ENUM).endNode(); 302 303 // Enum definition 304 RecognizerBase<EVisualBasicParserState> enumLiteralBase = inState(IN_ENUM).sequence(IDENTIFIER) 305 .createNode(EShallowEntityType.ATTRIBUTE, SubTypeNames.ENUM_LITERAL, 0); 306 skipToEndOfStatement(enumLiteralBase).endNode(); 307 } 308 309 /** 310 * Create rules that match properties. A matched property is reported as the 311 * shallow entity 'ATTRIBUTE' with potential getter and setter methods. 312 */ 313 private void createPropertyRules() { 314 RecognizerBase<EVisualBasicParserState> base = inState(IN_TYPE).repeated(DECLARATION_MODIFIER) 315 .sequence(PROPERTY, IDENTIFIER).createNode(EShallowEntityType.ATTRIBUTE, SubTypeNames.PROPERTY, -1); 316 317 base = skipToEndOfStatement(base); 318 319 // The property is treated as a type if a getter or setter is found 320 // next. 321 base.sequence(EOL).sequenceBefore(EnumSet.of(GET, SET)).parseUntil(IN_TYPE).sequence(END_PROPERTY).endNode(); 322 323 // Otherwise the property could have an automatically created getter and 324 // setter and therefore will end without a body. 325 base.optional(END_PROPERTY).endNode(); 326 } 327 328 /** 329 * Create rules that cover conditionals (If, ElseIf). 330 */ 331 private void createConditionalsRules() { 332 // For single line if statements we cannot use the same method as for 333 // try-catch structures, so we check this case with a precondition. 334 // Refer to Single-Line Syntax for further details: 335 // https://msdn.microsoft.com/en-us/library/752y8abs.aspx 336 337 // Recognize single line if statements 338 RecognizerBase<EVisualBasicParserState> singleLineIfSelector = createRecognizer( 339 start -> start.sequence(IF).skipTo(THEN).repeated(COLON).sequence(NO_NEW_LINE)); 340 341 RecognizerBase<EVisualBasicParserState> base = inState(IN_METHOD, IN_SINGLE_LINE_IF) 342 .preCondition(singleLineIfSelector).sequence(IF).createNode(EShallowEntityType.STATEMENT, -1) 343 .skipTo(THEN).parseUntil(IN_SINGLE_LINE_IF).sequenceBefore(EnumSet.of(EOL, ELSE)); 344 345 base.sequenceBefore(ELSE).endNodeWithContinuation(); 346 base.sequence(EOL).endNode(); 347 348 // Recognize single line else statements 349 RecognizerBase<EVisualBasicParserState> elseRecognizer = createRecognizer( 350 start -> start.sequence(ELSE).repeated(COLON).sequenceBefore(NO_NEW_LINE)); 351 352 inState(IN_METHOD, IN_SINGLE_LINE_IF).preCondition(elseRecognizer).sequence(ELSE) 353 .createNode(EShallowEntityType.STATEMENT, -1).parseUntil(IN_SINGLE_LINE_IF).sequence(EOL) 354 .optional(END_IF).endNode(); 355 356 // Multi line If Statements 357 createBlockRuleWithContinuation(EnumSet.of(IF, ELSEIF, ELSE), EnumSet.of(ELSEIF, ELSE), END_IF); 358 359 } 360 361 /** 362 * Create a rule to identify namespaces. 363 */ 364 private void createNamespaceRule() { 365 RecognizerBase<EVisualBasicParserState> base = inState(TOP_LEVEL).sequence(NAMESPACE) 366 .createNode(EShallowEntityType.MODULE, SubTypeNames.NAMESPACE, 1); 367 skipToEndOfStatement(base).parseUntil(TOP_LEVEL).sequence(END_NAMESPACE).endNode(); 368 } 369 370 /** 371 * Create a rule that matches simple statements. 372 */ 373 private void createSimpleStatementRule() { 374 // Local variables either begin with at least one modifier 375 RecognizerBase<EVisualBasicParserState> localVariable = inState(IN_METHOD, IN_SINGLE_LINE_IF) 376 .sequence(DECLARATION_MODIFIER).repeated(DECLARATION_MODIFIER).markStart() 377 .createNode(EShallowEntityType.STATEMENT, SubTypeNames.LOCAL_VARIABLE, 0); 378 skipToEndOfStatement(localVariable).endNode(); 379 380 // Allow a statement to start with DOT, too, because of its use in a 381 // WITH statement. 382 RecognizerBase<EVisualBasicParserState> simpleStatement = inState(TOP_LEVEL, IN_TYPE, IN_METHOD, 383 IN_SINGLE_LINE_IF).optional(DOT).repeated(DECLARATION_MODIFIER).sequence(STATEMENT_START) 384 .createNode(EShallowEntityType.STATEMENT, SubTypeNames.SIMPLE_STATEMENT, -1); 385 skipToEndOfStatement(simpleStatement).endNode(); 386 } 387 388 /** 389 * Create rules for methods. 390 */ 391 private void createMethodRules() { 392 createMethodRule(FUNCTION, END_FUNCTION, IDENTIFIER, SubTypeNames.METHOD); 393 createMethodRule(SUB, END_SUB, IDENTIFIER, SubTypeNames.METHOD); 394 createMethodRule(SUB, END_SUB, NEW, SubTypeNames.CONSTRUCTOR); 395 396 // Special methods: GET and SET in properties. 397 createGetSetRule(GET, END_GET); 398 createGetSetRule(SET, END_SET); 399 400 // Operator overloading. 401 createOperatorRule(); 402 403 // Declare 404 RecognizerBase<EVisualBasicParserState> baseRecognizerDeclare = inState(TOP_LEVEL, IN_TYPE) 405 .repeated(DECLARATION_MODIFIER).sequence(ETokenType.DECLARE) 406 // IDENTIFIER needed to match "PtrSafe" 407 .optional(EnumSet.of(ANSI, AUTO, UNICODE, IDENTIFIER)).sequence(EnumSet.of(SUB, FUNCTION)).markStart() 408 .createNode(EShallowEntityType.METHOD, SubTypeNames.DECLARATION, 0); 409 skipToEndOfStatement(baseRecognizerDeclare).endNode(); 410 } 411 412 /** 413 * Creates a rule that matches operator overloading structures. They are 414 * reported as methods. 415 */ 416 private void createOperatorRule() { 417 RecognizerBase<EVisualBasicParserState> base = inState(IN_TYPE).repeated(DECLARATION_MODIFIER) 418 .sequence(OPERATOR).markStart().createNode(EShallowEntityType.METHOD, SubTypeNames.OPERATOR, 0); 419 skipToEndOfStatement(base).parseUntil(IN_METHOD).sequence(END_OPERATOR).endNode(); 420 421 } 422 423 /** 424 * Create the rule for a single getter or setter. The rule is matched inside a 425 * type. This means that a property must parse as IN_TYPE. 426 */ 427 private void createGetSetRule(ETokenType startTokenType, ETokenType endTokenType) { 428 RecognizerBase<EVisualBasicParserState> base = inState(IN_TYPE).repeated(DECLARATION_MODIFIER).markStart() 429 .sequence(startTokenType) 430 .createNode(EShallowEntityType.METHOD, -1, new PropertyAccessNameResolver<EVisualBasicParserState>()); 431 skipToEndOfStatement(base).parseUntil(IN_METHOD).sequence(endTokenType).endNode(); 432 433 } 434 435 /** 436 * Create the rule to match a method that is identified by startTokenType and 437 * parses the contents of the method until endTokenType is reached. 438 */ 439 private void createMethodRule(ETokenType startTokenType, ETokenType endTokenType, Object identifierTokenType, 440 String subtypeName) { 441 442 // Classes may define methods with 'MUSTOVERRIDE' that do not have a 443 // method body. 444 RecognizerBase<EVisualBasicParserState> abstractTypeBase = inState(TOP_LEVEL, IN_TYPE) 445 .repeated(DECLARATION_MODIFIER).sequence(MUSTOVERRIDE).repeated(DECLARATION_MODIFIER) 446 .sequence(startTokenType).sequence(identifierTokenType) 447 .createNode(EShallowEntityType.METHOD, subtypeName, -1); 448 skipToEndOfStatement(abstractTypeBase).endNode(); 449 450 // Typical method declarations. 451 RecognizerBase<EVisualBasicParserState> typeBase = inState(TOP_LEVEL, IN_TYPE); 452 typeBase = appendMethodNode(typeBase, startTokenType, identifierTokenType, subtypeName); 453 skipToEndOfStatement(typeBase).parseUntil(IN_METHOD).sequence(endTokenType).endNode(); 454 455 // For interfaces the methods do not have a body. 456 RecognizerBase<EVisualBasicParserState> interfaceBase = inState(IN_INTERFACE); 457 interfaceBase = appendMethodNode(interfaceBase, startTokenType, identifierTokenType, subtypeName); 458 skipToEndOfStatement(interfaceBase).endNode(); 459 460 } 461 462 /** 463 * Create rule to match a method that is identified by startTokenType and parses 464 * the contents of the method until endTokenType is reached. 465 */ 466 private static RecognizerBase<EVisualBasicParserState> appendMethodNode( 467 RecognizerBase<EVisualBasicParserState> base, ETokenType startTokenType, Object identifierTokenType, 468 String subtypeName) { 469 return base.repeated(DECLARATION_MODIFIER).sequence(startTokenType).sequence(identifierTokenType) 470 .createNode(EShallowEntityType.METHOD, subtypeName, -1); 471 } 472 473 /** 474 * Create rules for type attributes. 475 */ 476 private void createAttributeRules() { 477 // Attributes 478 RecognizerBase<EVisualBasicParserState> baseRecognizerAttribute = inState(TOP_LEVEL, IN_TYPE) 479 .repeated(DECLARATION_MODIFIER) 480 // Member variables have to start with DIM, if no other 481 // modifier is present 482 .optional(DIM).sequence(IDENTIFIER) 483 .createNode(EShallowEntityType.ATTRIBUTE, SubTypeNames.ATTRIBUTE, -1); 484 skipToEndOfStatement(baseRecognizerAttribute).endNode(); 485 486 // Delegates 487 RecognizerBase<EVisualBasicParserState> baseRecognizerDelegate = inState(TOP_LEVEL, IN_TYPE) 488 .repeated(DECLARATION_MODIFIER).sequence(DELEGATE).markStart() 489 .createNode(EShallowEntityType.ATTRIBUTE, SubTypeNames.DELEGATE, 1); 490 skipToEndOfStatement(baseRecognizerDelegate).endNode(); 491 } 492 493 /** 494 * Create rules for classes etc. 495 */ 496 private void createTypeLikeRules() { 497 createTypeLikeRule(EShallowEntityType.TYPE, IN_TYPE, CLASS, END_CLASS, SubTypeNames.CLASS); 498 createTypeLikeRule(EShallowEntityType.MODULE, IN_TYPE, MODULE, END_MODULE, SubTypeNames.MODULE); 499 createTypeLikeRule(EShallowEntityType.TYPE, IN_INTERFACE, INTERFACE, END_INTERFACE, SubTypeNames.INTERFACE); 500 createTypeLikeRule(EShallowEntityType.TYPE, IN_TYPE, STRUCTURE, END_STRUCTURE, SubTypeNames.STRUCTURE); 501 createTypeLikeRule(EShallowEntityType.TYPE, IN_TYPE, TYPE, END_TYPE, SubTypeNames.TYPE_ALIAS); 502 } 503 504 /** 505 * Appends a recognizer to the sequence, that repeatedly skips INHERITS and 506 * IMPLEMENTS code parts. This is necessary because they may continue a 507 * statement in the next line. 508 */ 509 private RecognizerBase<EVisualBasicParserState> appendSkipToEndOfTypeRecognizer( 510 RecognizerBase<EVisualBasicParserState> base) { 511 512 RecognizerBase<EVisualBasicParserState> extendedTypeRecognizer = createRecognizer( 513 start -> start.repeated(EOL).sequence(EnumSet.of(INHERITS, IMPLEMENTS))); 514 skipToEndOfStatement(extendedTypeRecognizer); 515 516 return base.repeatedSubRecognizer(extendedTypeRecognizer); 517 } 518 519 /** 520 * Create a rule for the given type. Modules are treated here as well, as they 521 * are syntactically similar to types. 522 */ 523 private void createTypeLikeRule(EShallowEntityType shallowEntityType, EVisualBasicParserState subParseState, 524 ETokenType startTokenType, ETokenType endTokenType, String subtypeName) { 525 526 RecognizerBase<EVisualBasicParserState> base = inState(TOP_LEVEL, IN_TYPE).repeated(DECLARATION_MODIFIER) 527 .sequence(startTokenType, IDENTIFIER).createNode(shallowEntityType, subtypeName, -1); 528 529 appendSkipToEndOfTypeRecognizer(skipToEndOfStatement(base)).parseUntil(subParseState).sequence(endTokenType) 530 .endNode(); 531 } 532 533 /** 534 * Create the rules that recognize meta statements such as imports. 535 */ 536 private void createMetaRules() { 537 538 // Attribute lists 539 inAnyState().sequence(LT).createNode(EShallowEntityType.META, SubTypeNames.ANNOTATION, 1) 540 .skipToWithNesting(GT, OPENING_TYPES, CLOSING_TYPES).endNode(); 541 542 // Matches attribute and version meta data in e.g. *.bas files 543 skipToEndOfStatement(inState(TOP_LEVEL).sequence(IDENTIFIER).createNode(EShallowEntityType.META, -1)).endNode(); 544 545 // Imports 546 RecognizerBase<EVisualBasicParserState> baseRecognizerImports = inState(TOP_LEVEL).sequence(IMPORTS) 547 .createNode(EShallowEntityType.META, SubTypeNames.IMPORT); 548 skipToEndOfStatement(baseRecognizerImports).endNode(); 549 550 // Preprocessor directives 551 inAnyState().sequence(HASH).createNode(EShallowEntityType.META, "preprocessor directive").skipTo(EOL).endNode(); 552 553 // Goto label 554 inState(IN_METHOD, IN_SINGLE_LINE_IF).sequence(IDENTIFIER, COLON) 555 .createNode(EShallowEntityType.META, SubTypeNames.LABEL, -2).endNode(); 556 557 // Options 558 inState(TOP_LEVEL).sequence(OPTION).createNode(EShallowEntityType.META, 0, 1).skipForward(1) 559 // Binary, Text and Off are not scanned as separate keywords. 560 // They are unreserved keywords: 561 // https://msdn.microsoft.com/en-us/library/dd409611.aspx 562 .optional(EnumSet.of(ON, IDENTIFIER)) 563 // People might write all options in one line. 564 .optional(COLON).endNode(); 565 566 // Form meta data encapsulated by BEGIN and END 567 inState(TOP_LEVEL).sequence(BEGIN).createNode(EShallowEntityType.META, SubTypeNames.FORM) 568 .skipToWithNesting(END, BEGIN, END).endNode(); 569 } 570 571 /** 572 * Skip to the end of a statement and return the modified recognizer. 573 */ 574 private RecognizerBase<EVisualBasicParserState> skipToEndOfStatement(RecognizerBase<EVisualBasicParserState> base) { 575 // Statements in VB.NET end with the first EOL that is encountered 576 // except the compiler can perform Implicit Line Conversion: 577 // https://msdn.microsoft.com/en-us/library/865x40k4.aspx 578 579 // Save the reference to the skipBeforeWithNesting recognizer, as 580 // this gives the same result as using the reference to the empty 581 // recognizer. 582 RecognizerBase<EVisualBasicParserState> statementContinuationRecognizer = createRecognizer(start -> { 583 RecognizerBase<EVisualBasicParserState> prefix = start 584 .skipBeforeWithNesting(STATEMENT_CONTINUATION_CANDIDATES, OPENING_TYPES, CLOSING_TYPES); 585 prefix.sequence(STATEMENT_CONTINUATION_FOLLOWING, EOL); 586 prefix.sequence(STATEMENT_CONTINUATION_BOTH); 587 prefix.sequence(EOL).sequenceBefore(STATEMENT_CONTINUATION_PRECEDING); 588 }); 589 590 return base.repeatedSubRecognizer(statementContinuationRecognizer) 591 .skipBeforeWithNesting(STATEMENT_INTERRUPT_TYPES, OPENING_TYPES, CLOSING_TYPES); 592 } 593}