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.xtend;
018
019import static eu.cqse.check.framework.scanner.ETokenType.ABSTRACT;
020import static eu.cqse.check.framework.scanner.ETokenType.ANNOTATION;
021import static eu.cqse.check.framework.scanner.ETokenType.AT_OPERATOR;
022import static eu.cqse.check.framework.scanner.ETokenType.BOOLEAN;
023import static eu.cqse.check.framework.scanner.ETokenType.BYTE;
024import static eu.cqse.check.framework.scanner.ETokenType.CASE;
025import static eu.cqse.check.framework.scanner.ETokenType.CATCH;
026import static eu.cqse.check.framework.scanner.ETokenType.CHAR;
027import static eu.cqse.check.framework.scanner.ETokenType.CLASS;
028import static eu.cqse.check.framework.scanner.ETokenType.COLON;
029import static eu.cqse.check.framework.scanner.ETokenType.COMMA;
030import static eu.cqse.check.framework.scanner.ETokenType.CREATE;
031import static eu.cqse.check.framework.scanner.ETokenType.DEF;
032import static eu.cqse.check.framework.scanner.ETokenType.DEFAULT;
033import static eu.cqse.check.framework.scanner.ETokenType.DISPATCH;
034import static eu.cqse.check.framework.scanner.ETokenType.DO;
035import static eu.cqse.check.framework.scanner.ETokenType.DOT;
036import static eu.cqse.check.framework.scanner.ETokenType.DOUBLE;
037import static eu.cqse.check.framework.scanner.ETokenType.DOUBLE_ARROW;
038import static eu.cqse.check.framework.scanner.ETokenType.ELSE;
039import static eu.cqse.check.framework.scanner.ETokenType.ENUM;
040import static eu.cqse.check.framework.scanner.ETokenType.EXTENSION;
041import static eu.cqse.check.framework.scanner.ETokenType.FINAL;
042import static eu.cqse.check.framework.scanner.ETokenType.FINALLY;
043import static eu.cqse.check.framework.scanner.ETokenType.FLOAT;
044import static eu.cqse.check.framework.scanner.ETokenType.FOR;
045import static eu.cqse.check.framework.scanner.ETokenType.GT;
046import static eu.cqse.check.framework.scanner.ETokenType.IDENTIFIER;
047import static eu.cqse.check.framework.scanner.ETokenType.IF;
048import static eu.cqse.check.framework.scanner.ETokenType.IMPORT;
049import static eu.cqse.check.framework.scanner.ETokenType.INT;
050import static eu.cqse.check.framework.scanner.ETokenType.INTERFACE;
051import static eu.cqse.check.framework.scanner.ETokenType.LBRACE;
052import static eu.cqse.check.framework.scanner.ETokenType.LBRACK;
053import static eu.cqse.check.framework.scanner.ETokenType.LONG;
054import static eu.cqse.check.framework.scanner.ETokenType.LPAREN;
055import static eu.cqse.check.framework.scanner.ETokenType.LT;
056import static eu.cqse.check.framework.scanner.ETokenType.MULT;
057import static eu.cqse.check.framework.scanner.ETokenType.NATIVE;
058import static eu.cqse.check.framework.scanner.ETokenType.NEW;
059import static eu.cqse.check.framework.scanner.ETokenType.OR;
060import static eu.cqse.check.framework.scanner.ETokenType.OVERRIDE;
061import static eu.cqse.check.framework.scanner.ETokenType.PACKAGE;
062import static eu.cqse.check.framework.scanner.ETokenType.PRIVATE;
063import static eu.cqse.check.framework.scanner.ETokenType.PROTECTED;
064import static eu.cqse.check.framework.scanner.ETokenType.PUBLIC;
065import static eu.cqse.check.framework.scanner.ETokenType.RBRACE;
066import static eu.cqse.check.framework.scanner.ETokenType.RBRACK;
067import static eu.cqse.check.framework.scanner.ETokenType.RPAREN;
068import static eu.cqse.check.framework.scanner.ETokenType.SEMICOLON;
069import static eu.cqse.check.framework.scanner.ETokenType.SHORT;
070import static eu.cqse.check.framework.scanner.ETokenType.STATIC;
071import static eu.cqse.check.framework.scanner.ETokenType.STRICTFP;
072import static eu.cqse.check.framework.scanner.ETokenType.SWITCH;
073import static eu.cqse.check.framework.scanner.ETokenType.SYNCHRONIZED;
074import static eu.cqse.check.framework.scanner.ETokenType.TEMPLATE_LITERAL;
075import static eu.cqse.check.framework.scanner.ETokenType.THROWS;
076import static eu.cqse.check.framework.scanner.ETokenType.TRANSIENT;
077import static eu.cqse.check.framework.scanner.ETokenType.TRY;
078import static eu.cqse.check.framework.scanner.ETokenType.VAL;
079import static eu.cqse.check.framework.scanner.ETokenType.VAR;
080import static eu.cqse.check.framework.scanner.ETokenType.VOID;
081import static eu.cqse.check.framework.scanner.ETokenType.VOLATILE;
082import static eu.cqse.check.framework.scanner.ETokenType.WHILE;
083import static eu.cqse.check.framework.shallowparser.languages.xtend.XtendShallowParser.EXtendShallowParserState.IN_ENUM;
084import static eu.cqse.check.framework.shallowparser.languages.xtend.XtendShallowParser.EXtendShallowParserState.IN_LAMBDA;
085import static eu.cqse.check.framework.shallowparser.languages.xtend.XtendShallowParser.EXtendShallowParserState.IN_METHOD;
086import static eu.cqse.check.framework.shallowparser.languages.xtend.XtendShallowParser.EXtendShallowParserState.IN_METHOD_WITH_TEMPLATE;
087import static eu.cqse.check.framework.shallowparser.languages.xtend.XtendShallowParser.EXtendShallowParserState.IN_SINGLE_STATEMENT;
088import static eu.cqse.check.framework.shallowparser.languages.xtend.XtendShallowParser.EXtendShallowParserState.IN_TOP_LEVEL;
089import static eu.cqse.check.framework.shallowparser.languages.xtend.XtendShallowParser.EXtendShallowParserState.IN_TYPE;
090
091import java.util.EnumSet;
092
093import org.conqat.lib.commons.region.Region;
094
095import eu.cqse.check.framework.scanner.ETokenType;
096import eu.cqse.check.framework.scanner.ETokenType.ETokenClass;
097import eu.cqse.check.framework.scanner.IToken;
098import eu.cqse.check.framework.shallowparser.SubTypeNames;
099import eu.cqse.check.framework.shallowparser.framework.EShallowEntityType;
100import eu.cqse.check.framework.shallowparser.framework.RecognizerBase;
101import eu.cqse.check.framework.shallowparser.framework.ShallowParserBase;
102import eu.cqse.check.framework.shallowparser.languages.xtend.XtendShallowParser.EXtendShallowParserState;
103
104/**
105 * Shallow parser for Xtend.
106 * <p>
107 * Same features as the Java shallow parser:
108 * <ul>
109 * <li>The parser recognizes types (classes, enums, interfaces, annotations),
110 * methods and attributes, and individual statements.</li>
111 * <li>It recognizes the nesting of statements (e.g. in loops), but does not
112 * parse into the statements. For example, it recognizes an if-statement and
113 * provides the list of sub-statements, but does not provide direct access to
114 * the if-condition.</li>
115 * <li>Import and package statements are parsed as meta information.</li>
116 * <li>Annotations are recognized as meta information, but only annotations at
117 * types and methods. Annotations at parameters are not parsed, as the parser
118 * does not parse into the parameter list of methods.</li>
119 * <li>The parser does not recognize anonymous classes. These are treated as a
120 * single long statement or attribute. Inner classes, however, are parsed
121 * correctly.</li>
122 * <li>The parser recognizes try, switch and if assignments to fields.</li>
123 * <li>The parser can deal with multiple classes in a single file.</li>
124 * </ul>
125 */
126public class XtendShallowParser extends ShallowParserBase<EXtendShallowParserState> {
127
128        /** The states of the xtend parser */
129        public static enum EXtendShallowParserState {
130                /** Top level state (e.g. at the beginning of the file). */
131                IN_TOP_LEVEL,
132
133                /** State inside a type (class, interface or annotation) definition. */
134                IN_TYPE,
135
136                /** State inside a method definition. */
137                IN_METHOD,
138
139                /** State inside a method definition with template literal. */
140                IN_METHOD_WITH_TEMPLATE,
141
142                /** State inside a region, that accepts only a single statement. */
143                IN_SINGLE_STATEMENT,
144
145                /** State inside an enum body. */
146                IN_ENUM,
147
148                /**
149                 * State to match a lambda expression, the content of the lambda
150                 * expression however is treated as IN_METHOD
151                 */
152                IN_LAMBDA
153        }
154
155        /** All modifiers that may be part of a type definition. */
156        private static final EnumSet<ETokenType> TYPE_MODIFIERS = EnumSet.of(PUBLIC, PRIVATE, PROTECTED, PACKAGE, STRICTFP,
157                        ABSTRACT, STATIC);
158
159        /** All possible types of a field. */
160        private static final EnumSet<ETokenType> FIELD_TYPES = EnumSet.of(BOOLEAN, BYTE, CHAR, DOUBLE, FLOAT, INT, LONG,
161                        SHORT, VOID);
162
163        /** All possible modifiers of a field. */
164        private static final EnumSet<ETokenType> FIELD_MODIFIERS = EnumSet.of(PUBLIC, PRIVATE, PROTECTED, PACKAGE, STATIC,
165                        FINAL, EXTENSION, VOLATILE, TRANSIENT);
166
167        /** All possible modifiers of a method. */
168        private static final EnumSet<ETokenType> METHOD_MODIFIERS = EnumSet.of(PUBLIC, PRIVATE, PROTECTED, PACKAGE, STATIC,
169                        ABSTRACT, DISPATCH, FINAL, STRICTFP, NATIVE, SYNCHRONIZED);
170
171        /** Constructor */
172        public XtendShallowParser() {
173                super(EXtendShallowParserState.class, IN_TOP_LEVEL);
174                createMetaRules();
175                createTypeDefRules();
176
177                createEnumBodyRule();
178
179                createMethodDefRule();
180                createConstructorDefRule();
181
182                createLoopRules();
183                createSwitchCaseRules();
184                createContinuationRules();
185
186                createAnonymousBlock();
187                createFieldRules();
188                createSingleStatementRule();
189
190                createLambdaExpressionRule();
191
192                createEmptyStatementRule();
193        }
194
195        /** Creates the meta rules, such as package, annotation and import. */
196        private void createMetaRules() {
197                createImportRule();
198                createPackageRule();
199                createAnnotationRule();
200        }
201
202        /** Creates the rule for imports. */
203        private void createImportRule() {
204                inState(IN_TOP_LEVEL).sequence(IMPORT).optional(STATIC).optional(EXTENSION).markStart().sequence(IDENTIFIER)
205                                .repeated(DOT, EnumSet.of(IDENTIFIER, MULT))
206                                .createNode(EShallowEntityType.META, SubTypeNames.IMPORT, new Region(0, -1)).optional(SEMICOLON)
207                                .endNode();
208        }
209
210        /** Creates the rule for package declarations. */
211        private void createPackageRule() {
212                inState(IN_TOP_LEVEL).sequence(PACKAGE).sequence(IDENTIFIER).repeated(DOT, IDENTIFIER)
213                                .createNode(EShallowEntityType.META, 0, new Region(1, -1)).optional(SEMICOLON).endNode();
214        }
215
216        /** Creates the rule for annotations. */
217        private void createAnnotationRule() {
218                inState(IN_TOP_LEVEL, IN_TYPE, IN_METHOD).sequence(AT_OPERATOR, IDENTIFIER)
219                                .createNode(EShallowEntityType.META, SubTypeNames.ANNOTATION, -1).skipNested(LPAREN, RPAREN).endNode();
220        }
221
222        /** Creates all rules that recognize type definitions. */
223        private void createTypeDefRules() {
224                createTypeDefRule(ENUM, SubTypeNames.ENUM, IN_ENUM, IN_TOP_LEVEL, IN_TYPE);
225                createTypeDefRule(INTERFACE, SubTypeNames.INTERFACE, IN_TYPE, IN_TOP_LEVEL, IN_TYPE);
226                createTypeDefRule(ANNOTATION, SubTypeNames.ANNOTATION, IN_TYPE, IN_TOP_LEVEL, IN_TYPE);
227                createTypeDefRule(CLASS, SubTypeNames.CLASS, IN_TYPE, IN_TOP_LEVEL, IN_TYPE, IN_METHOD);
228        }
229
230        /** Creates a rule that matches a specific type definition. */
231        private void createTypeDefRule(ETokenType type, String subtypeName, EXtendShallowParserState substate,
232                        EXtendShallowParserState... states) {
233                inState(states).repeated(TYPE_MODIFIERS).sequence(type).sequence(IDENTIFIER)
234                                .createNode(EShallowEntityType.TYPE, subtypeName, -1).skipTo(LBRACE).parseUntil(substate)
235                                .sequence(RBRACE).endNode();
236        }
237
238        /** Recognizes an enumeration inside of an enum body. */
239        private void createEnumBodyRule() {
240                inState(IN_ENUM).sequence(IDENTIFIER).sequenceBefore(COMMA)
241                                .createNode(EShallowEntityType.ATTRIBUTE, SubTypeNames.ENUM_LITERAL, 0).endNode();
242
243                inState(IN_ENUM).sequence(IDENTIFIER).sequenceBefore(RBRACE)
244                                .createNode(EShallowEntityType.ATTRIBUTE, SubTypeNames.ENUM_LITERAL, 0).endNode();
245        }
246
247        /** Creates rule that recognizes method definitions. */
248        private void createMethodDefRule() {
249                RecognizerBase<EXtendShallowParserState> baseRecognizer = inState(IN_TYPE).repeated(METHOD_MODIFIERS)
250                                .sequence(EnumSet.of(DEF, OVERRIDE)).repeated(METHOD_MODIFIERS).optional(FIELD_TYPES)
251                                .repeated(METHOD_MODIFIERS);
252
253                RecognizerBase<EXtendShallowParserState> baseWithSimpleCreate = baseRecognizer.optional(IDENTIFIER)
254                                .sequence(CREATE).skipTo(NEW).sequence(IDENTIFIER).skipNested(LPAREN, RPAREN);
255
256                appendMethodNode(baseWithSimpleCreate);
257                appendMethodNode(baseRecognizer);
258
259                inState(IN_METHOD_WITH_TEMPLATE).sequence(TEMPLATE_LITERAL)
260                                .createNode(EShallowEntityType.STATEMENT, SubTypeNames.SIMPLE_STATEMENT, -1).endNode();
261        }
262
263        /** Appends the node for a method. */
264        private static void appendMethodNode(RecognizerBase<EXtendShallowParserState> baseRecognizer) {
265                RecognizerBase<EXtendShallowParserState> newBaseRecognizer = baseRecognizer.skipBefore(LPAREN)
266                                .createNode(EShallowEntityType.METHOD, SubTypeNames.METHOD, -1).skipNested(LPAREN, RPAREN);
267
268                // Method header only with a "throws" clause (in interface declaration)
269                RecognizerBase<EXtendShallowParserState> withThrowsException = newBaseRecognizer.sequence(THROWS)
270                                .subRecognizer(new XtendSkipToEndOfStatementRecognizer(), 0, 1);
271
272                appendRecognizeMethodBody(newBaseRecognizer);
273                appendRecognizeMethodBody(withThrowsException);
274
275                withThrowsException.endNode(); // abstract method def
276                newBaseRecognizer.endNode(); // abstract method def
277        }
278
279        /**
280         * Appends the rule to recognize the method body, which is either a
281         * statement block or a template.
282         */
283        private static void appendRecognizeMethodBody(RecognizerBase<EXtendShallowParserState> baseRecognizer) {
284                baseRecognizer.sequence(LBRACE).parseUntil(IN_METHOD).sequence(RBRACE).endNode();
285                baseRecognizer.sequenceBefore(TEMPLATE_LITERAL).parseOnce(IN_METHOD_WITH_TEMPLATE).endNode();
286        }
287
288        /** Creates rule for constructors. */
289        private void createConstructorDefRule() {
290                inState(IN_TYPE).sequence(NEW).createNode(EShallowEntityType.METHOD, SubTypeNames.CONSTRUCTOR).skipTo(LBRACE)
291                                .parseUntil(IN_METHOD).sequence(RBRACE).endNode();
292        }
293
294        /** Creates all rules that correspond to loops. */
295        private void createLoopRules() {
296                createForRule();
297                createDoWhileRule();
298                createWhileRule();
299        }
300
301        /** Creates rule for for statements. */
302        private void createForRule() {
303                RecognizerBase<EXtendShallowParserState> baseRecognizer = inState(IN_METHOD).sequence(FOR)
304                                .createNode(EShallowEntityType.STATEMENT, SubTypeNames.FOR).skipNested(LPAREN, RPAREN);
305
306                appendEndNodeWithBlockOrSingleStatement(baseRecognizer);
307        }
308
309        /** Creates rule for do while statements. */
310        private void createDoWhileRule() {
311                RecognizerBase<EXtendShallowParserState> baseRecognizer = inState(IN_METHOD).sequence(DO)
312                                .createNode(EShallowEntityType.STATEMENT, SubTypeNames.DO);
313
314                appendWhileRuleInDoWhile(baseRecognizer.sequence(LBRACE).parseUntil(IN_METHOD).sequence(RBRACE));
315                appendWhileRuleInDoWhile(baseRecognizer.parseOnce(IN_SINGLE_STATEMENT));
316        }
317
318        /** Appends the while statement to the rule after the do block. */
319        private static void appendWhileRuleInDoWhile(RecognizerBase<EXtendShallowParserState> baseRecognizer) {
320                baseRecognizer.sequence(WHILE).skipNested(LPAREN, RPAREN).endNode();
321        }
322
323        /** Creates rule for while statement. */
324        private void createWhileRule() {
325                RecognizerBase<EXtendShallowParserState> baseRecognizer = inState(IN_METHOD).sequence(WHILE)
326                                .createNode(EShallowEntityType.STATEMENT, SubTypeNames.WHILE).skipNested(LPAREN, RPAREN);
327                appendEndNodeWithBlockOrSingleStatement(baseRecognizer);
328        }
329
330        /** Creates rule that matches switch case statements. */
331        private void createSwitchCaseRules() {
332                inState(IN_METHOD).sequence(SWITCH).createNode(EShallowEntityType.STATEMENT, SubTypeNames.SWITCH).skipTo(LBRACE)
333                                .parseUntil(IN_METHOD).sequence(RBRACE).endNode();
334                createCaseRules();
335        }
336
337        /** Creates rules that apply within a switch block. */
338        private void createCaseRules() {
339                inState(IN_METHOD).sequence(DEFAULT).sequence(COLON).createNode(EShallowEntityType.META, SubTypeNames.DEFAULT)
340                                .endNode();
341
342                // type guard (with optional case)
343                inState(IN_METHOD).repeated(IDENTIFIER, DOT).sequence(IDENTIFIER).skipNested(LT, GT)
344                                .sequenceBefore(EnumSet.of(COMMA, COLON, CASE)).skipTo(EnumSet.of(COMMA, COLON))
345                                .createNode(EShallowEntityType.META, SubTypeNames.CASE).endNode();
346
347                // case only
348                inState(IN_METHOD).sequence(CASE)
349                                .skipToWithNesting(EnumSet.of(COMMA, COLON), ETokenType.LPAREN, ETokenType.RPAREN)
350                                .createNode(EShallowEntityType.META, SubTypeNames.CASE).endNode();
351        }
352
353        /**
354         * Creates rule for statements with continuation like if-else statements and
355         * try-catch-finally.
356         */
357        private void createContinuationRules() {
358                createRuleWithContinuation(IF, ELSE, ELSE, IF);
359                createRuleWithContinuation(TRY, FINALLY, CATCH);
360        }
361
362        /**
363         * Create one rule for a statement with continuation.
364         */
365        private void createRuleWithContinuation(ETokenType first, ETokenType last, ETokenType... middle) {
366                RecognizerBase<EXtendShallowParserState> baseMid = inState(IN_METHOD).sequence((Object[]) middle)
367                                .createNode(EShallowEntityType.STATEMENT, new Region(0, -1)).skipNested(LPAREN, RPAREN);
368                appendSingleStatementAndBlockForContinuationNode(baseMid, last, middle);
369
370                RecognizerBase<EXtendShallowParserState> baseFirst = inState(IN_METHOD).sequence(EnumSet.of(first, last))
371                                .createNode(EShallowEntityType.STATEMENT, new Region(0, -1)).skipNested(LPAREN, RPAREN);
372                appendSingleStatementAndBlockForContinuationNode(baseFirst, last, middle);
373
374        }
375
376        /**
377         * Appends single statement and blocks of statements rule to continuation
378         * constructs.
379         */
380        private static void appendSingleStatementAndBlockForContinuationNode(
381                        RecognizerBase<EXtendShallowParserState> baseRecognizer, ETokenType last, ETokenType... middle) {
382                RecognizerBase<EXtendShallowParserState> baseBlock = baseRecognizer.sequence(LBRACE).parseUntil(IN_METHOD)
383                                .sequence(RBRACE);
384                RecognizerBase<EXtendShallowParserState> baseSingleStatement = baseRecognizer.parseOnce(IN_SINGLE_STATEMENT);
385                appendEndOfNodeInContinuationConstructs(baseBlock, last, middle);
386                appendEndOfNodeInContinuationConstructs(baseSingleStatement, last, middle);
387        }
388
389        /** Appends the rules for the end of a node in continuation constructs. */
390        private static void appendEndOfNodeInContinuationConstructs(RecognizerBase<EXtendShallowParserState> baseRecognizer,
391                        ETokenType last, ETokenType... middle) {
392                baseRecognizer.sequenceBefore(last).endNodeWithContinuation();
393                baseRecognizer.sequenceBefore((Object[]) middle).endNodeWithContinuation();
394                baseRecognizer.optional(ETokenType.SEMICOLON).endNode();
395        }
396
397        /** Rule for anonymous blocks. */
398        private void createAnonymousBlock() {
399                inAnyState().sequence(LBRACE).createNode(EShallowEntityType.STATEMENT, SubTypeNames.ANONYMOUS_BLOCK)
400                                .parseUntil(IN_METHOD).sequence(RBRACE).endNode();
401        }
402
403        /** Creates all rules that correspond to field definitions. */
404        private void createFieldRules() {
405                createFieldRule(EShallowEntityType.ATTRIBUTE, SubTypeNames.ATTRIBUTE, IN_TYPE);
406                createFieldRule(EShallowEntityType.STATEMENT, SubTypeNames.LOCAL_VARIABLE, IN_METHOD);
407
408                // Addition to attribute, as local variables cannot be defined via
409                // "IDENTIFIER IDENTIFIER"
410                RecognizerBase<EXtendShallowParserState> baseWithIdentifier = inState(IN_TYPE).repeated(FIELD_MODIFIERS)
411                                .sequence(IDENTIFIER).skipNested(LT, GT).repeated(LBRACK, RBRACK).sequence(IDENTIFIER);
412
413                appendFieldNodeAndSkipToEnd(baseWithIdentifier, EShallowEntityType.ATTRIBUTE, SubTypeNames.ATTRIBUTE);
414        }
415
416        /** Create rule for attributes. */
417        private void createFieldRule(EShallowEntityType type, String subtype, EXtendShallowParserState... states) {
418
419                // To declare an attribute or a variable it must include either
420                // val, var or an actual type
421                RecognizerBase<EXtendShallowParserState> baseWithValOrVar = inState(states).repeated(FIELD_MODIFIERS)
422                                .sequence(EnumSet.of(VAL, VAR)).repeated(FIELD_MODIFIERS).optional(FIELD_TYPES).repeated(LBRACK, RBRACK)
423                                .sequence(IDENTIFIER).skipNested(LT, GT).repeated(LBRACK, RBRACK).optional(IDENTIFIER);
424
425                RecognizerBase<EXtendShallowParserState> baseWithFieldType = inState(states).repeated(FIELD_MODIFIERS)
426                                .sequence(FIELD_TYPES).repeated(LBRACK, RBRACK).sequence(IDENTIFIER);
427
428                // Additional rule because constructs like this are valid:
429                // var (String)=>String stringToStringFunction = [ toUpperCase ]
430                RecognizerBase<EXtendShallowParserState> baseWithLambda = inState(states).repeated(FIELD_MODIFIERS)
431                                .sequence(EnumSet.of(VAL, VAR)).skipNested(LPAREN, RPAREN).sequence(DOUBLE_ARROW)
432                                .sequence(EnumSet.of(IDENTIFIER, DOUBLE, FLOAT, BYTE, SHORT, LONG, CHAR, INT, BOOLEAN))
433                                .sequence(IDENTIFIER);
434
435                appendFieldNodeAndSkipToEnd(baseWithValOrVar, type, subtype);
436                appendFieldNodeAndSkipToEnd(baseWithFieldType, type, subtype);
437                appendFieldNodeAndSkipToEnd(baseWithLambda, type, subtype);
438        }
439
440        /**
441         * Creates a node of a given type and subtype and skips to the end of the
442         * statement, the name of the node is defined by the previous token.
443         */
444        private static void appendFieldNodeAndSkipToEnd(RecognizerBase<EXtendShallowParserState> baseRecognizer,
445                        EShallowEntityType type, String subtype) {
446                RecognizerBase<EXtendShallowParserState> alternative = baseRecognizer.createNode(type, subtype, -1);
447                alternative.sequence(ETokenType.EQ).subRecognizer(new XtendSkipToEndOfStatementRecognizer(), 0, 1).endNode();
448                alternative.optional(ETokenType.SEMICOLON).endNode();
449        }
450
451        /**
452         * Factory method for a recognizer that matches a single statement. Should
453         * match nearly everything, that is left.
454         */
455        private void createSingleStatementRule() {
456                RecognizerBase<EXtendShallowParserState> baseClasses = inState(IN_METHOD, IN_SINGLE_STATEMENT).sequence(
457                                EnumSet.of(ETokenClass.KEYWORD, ETokenClass.IDENTIFIER, ETokenClass.LITERAL, ETokenClass.OPERATOR));
458
459                // this is especially needed, because a statement can start with
460                // LPAREN.
461                RecognizerBase<EXtendShallowParserState> baseLparen = inState(IN_METHOD, IN_SINGLE_STATEMENT)
462                                .sequenceBefore(LPAREN);
463
464                appendSingleStatementNode(baseClasses);
465                appendSingleStatementNode(baseLparen);
466        }
467
468        /** Appends the node creation for a simple statement. */
469        private static void appendSingleStatementNode(RecognizerBase<EXtendShallowParserState> baseRecognizer) {
470                baseRecognizer.createNode(EShallowEntityType.STATEMENT, SubTypeNames.SIMPLE_STATEMENT, 0)
471                                .subRecognizer(new XtendSkipToEndOfStatementRecognizer(), 0, 1).endNode();
472        }
473
474        /** Rule to recognize lambda expressions */
475        private void createLambdaExpressionRule() {
476                RecognizerBase<EXtendShallowParserState> alternative = inState(IN_LAMBDA).sequence(LBRACK)
477                                .createNode(EShallowEntityType.METHOD, SubTypeNames.LAMBDA_EXPRESSION);
478                alternative.skipBeforeWithNesting(EnumSet.of(RBRACK, OR), LBRACK, RBRACK).sequence(OR).parseUntil(IN_METHOD)
479                                .sequence(RBRACK).endNode();
480
481                alternative.parseUntil(IN_METHOD).sequence(RBRACK).endNode();
482        }
483
484        /** Matches for single semicolons and adds an empty statement for them. */
485        private void createEmptyStatementRule() {
486                inAnyState().sequence(SEMICOLON).createNode(EShallowEntityType.STATEMENT, SubTypeNames.EMPTY_STATEMENT, 0)
487                                .endNode();
488        }
489
490        /**
491         * Ends a rule with either multiple statements in a block or a single
492         * statement.
493         */
494        private static void appendEndNodeWithBlockOrSingleStatement(
495                        RecognizerBase<EXtendShallowParserState> baseRecognizer) {
496                baseRecognizer.sequence(LBRACE).parseUntil(IN_METHOD).sequence(RBRACE).endNode();
497                baseRecognizer.parseOnce(IN_SINGLE_STATEMENT).endNode();
498        }
499
500        /** {@inheritDoc} */
501        @Override
502        protected boolean isFilteredToken(IToken token, IToken previousToken) {
503                return super.isFilteredToken(token, previousToken) || token.getType() == ETokenType.EOL;
504        }
505}