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}