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.gosu;
018
019import static eu.cqse.check.framework.scanner.ETokenType.ABSTRACT;
020import static eu.cqse.check.framework.scanner.ETokenType.ARROW;
021import static eu.cqse.check.framework.scanner.ETokenType.AT;
022import static eu.cqse.check.framework.scanner.ETokenType.AT_OPERATOR;
023import static eu.cqse.check.framework.scanner.ETokenType.BACKSLASH;
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.CLASS;
027import static eu.cqse.check.framework.scanner.ETokenType.COLON;
028import static eu.cqse.check.framework.scanner.ETokenType.COMMA;
029import static eu.cqse.check.framework.scanner.ETokenType.CONSTRUCT;
030import static eu.cqse.check.framework.scanner.ETokenType.DEFAULT;
031import static eu.cqse.check.framework.scanner.ETokenType.DOT;
032import static eu.cqse.check.framework.scanner.ETokenType.ELSE;
033import static eu.cqse.check.framework.scanner.ETokenType.ENHANCEMENT;
034import static eu.cqse.check.framework.scanner.ETokenType.ENUM;
035import static eu.cqse.check.framework.scanner.ETokenType.EQ;
036import static eu.cqse.check.framework.scanner.ETokenType.FINAL;
037import static eu.cqse.check.framework.scanner.ETokenType.FINALLY;
038import static eu.cqse.check.framework.scanner.ETokenType.FOR;
039import static eu.cqse.check.framework.scanner.ETokenType.FUNCTION;
040import static eu.cqse.check.framework.scanner.ETokenType.GET;
041import static eu.cqse.check.framework.scanner.ETokenType.GT;
042import static eu.cqse.check.framework.scanner.ETokenType.IDENTIFIER;
043import static eu.cqse.check.framework.scanner.ETokenType.IDENTIFIERS;
044import static eu.cqse.check.framework.scanner.ETokenType.IF;
045import static eu.cqse.check.framework.scanner.ETokenType.INTERFACE;
046import static eu.cqse.check.framework.scanner.ETokenType.INTERNAL;
047import static eu.cqse.check.framework.scanner.ETokenType.LBRACE;
048import static eu.cqse.check.framework.scanner.ETokenType.LBRACK;
049import static eu.cqse.check.framework.scanner.ETokenType.LITERALS;
050import static eu.cqse.check.framework.scanner.ETokenType.LPAREN;
051import static eu.cqse.check.framework.scanner.ETokenType.LT;
052import static eu.cqse.check.framework.scanner.ETokenType.OVERRIDE;
053import static eu.cqse.check.framework.scanner.ETokenType.PACKAGE;
054import static eu.cqse.check.framework.scanner.ETokenType.PRIVATE;
055import static eu.cqse.check.framework.scanner.ETokenType.PROPERTY;
056import static eu.cqse.check.framework.scanner.ETokenType.PROTECTED;
057import static eu.cqse.check.framework.scanner.ETokenType.PUBLIC;
058import static eu.cqse.check.framework.scanner.ETokenType.RBRACE;
059import static eu.cqse.check.framework.scanner.ETokenType.RBRACK;
060import static eu.cqse.check.framework.scanner.ETokenType.RPAREN;
061import static eu.cqse.check.framework.scanner.ETokenType.SEMICOLON;
062import static eu.cqse.check.framework.scanner.ETokenType.SET;
063import static eu.cqse.check.framework.scanner.ETokenType.STATIC;
064import static eu.cqse.check.framework.scanner.ETokenType.STRUCTURE;
065import static eu.cqse.check.framework.scanner.ETokenType.SWITCH;
066import static eu.cqse.check.framework.scanner.ETokenType.TRANSIENT;
067import static eu.cqse.check.framework.scanner.ETokenType.TRY;
068import static eu.cqse.check.framework.scanner.ETokenType.USES;
069import static eu.cqse.check.framework.scanner.ETokenType.VAR;
070import static eu.cqse.check.framework.scanner.ETokenType.WHILE;
071import static eu.cqse.check.framework.scanner.ETokenType.WITH;
072import static eu.cqse.check.framework.scanner.ETokenType.ETokenClass.KEYWORD;
073import static eu.cqse.check.framework.scanner.ETokenType.ETokenClass.LITERAL;
074import static eu.cqse.check.framework.scanner.ETokenType.ETokenClass.OPERATOR;
075import static eu.cqse.check.framework.shallowparser.SubTypeNames.ENUM_LITERAL;
076import static eu.cqse.check.framework.shallowparser.SubTypeNames.LAMBDA;
077import static eu.cqse.check.framework.shallowparser.languages.gosu.GosuShallowParser.EGosuParserStates.IN_EXPRESSION;
078import static eu.cqse.check.framework.shallowparser.languages.gosu.GosuShallowParser.EGosuParserStates.IN_INTERFACE;
079import static eu.cqse.check.framework.shallowparser.languages.gosu.GosuShallowParser.EGosuParserStates.IN_METHOD;
080import static eu.cqse.check.framework.shallowparser.languages.gosu.GosuShallowParser.EGosuParserStates.IN_TYPE;
081import static eu.cqse.check.framework.shallowparser.languages.gosu.GosuShallowParser.EGosuParserStates.TOP_LEVEL;
082
083import java.util.Arrays;
084import java.util.Collections;
085import java.util.EnumSet;
086import java.util.List;
087import java.util.Set;
088
089import org.conqat.lib.commons.collections.CollectionUtils;
090import org.conqat.lib.commons.region.Region;
091
092import eu.cqse.check.framework.scanner.ETokenType;
093import eu.cqse.check.framework.scanner.ETokenType.ETokenClass;
094import eu.cqse.check.framework.shallowparser.SubTypeNames;
095import eu.cqse.check.framework.shallowparser.framework.EShallowEntityType;
096import eu.cqse.check.framework.shallowparser.framework.RecognizerBase;
097import eu.cqse.check.framework.shallowparser.framework.ShallowParserBase;
098import eu.cqse.check.framework.shallowparser.languages.gosu.GosuShallowParser.EGosuParserStates;
099
100/**
101 * Shallow parser for Gosu.
102 * <p>
103 * Started this parser from the JavaScriptShallowParser since Gosu seems to be a
104 * simple Java variant without semicolons. Gosu grammar:
105 * https://gosu-lang.github.io/grammar.html
106 */
107public class GosuShallowParser extends ShallowParserBase<EGosuParserStates> {
108
109        /** The states used in this parser. */
110        public enum EGosuParserStates {
111
112                /** Top-level parsing state. */
113                TOP_LEVEL,
114
115                /** Parsing within a type, such as a class. */
116                IN_TYPE,
117
118                /** Parsing in an expression */
119                IN_INTERFACE,
120
121                /** Parsing within a method. */
122                IN_METHOD,
123
124                /** Parsing in an expression */
125                IN_EXPRESSION
126        }
127
128        /** Valid modifiers in Gosu */
129        static final EnumSet<ETokenType> MODIFIERS = EnumSet.of(PRIVATE, INTERNAL, PROTECTED, PUBLIC, STATIC, ABSTRACT,
130                        OVERRIDE, FINAL, TRANSIENT, OVERRIDE);
131
132        /** All tokens that mark the termination of an interface member entity. */
133        private static final EnumSet<ETokenType> MEMBER_TERMINATOR_TOKENS = EnumSet.of(
134                        // the interface-closing bracket
135                        RBRACE,
136                        // the keywords starting new interface members
137                        FUNCTION, PROPERTY, VAR, CLASS, INTERFACE, ENUM, ENHANCEMENT, STRUCTURE, CONSTRUCT,
138                        // the modifiers
139                        AT, PRIVATE, INTERNAL, PROTECTED, PUBLIC, STATIC, ABSTRACT, OVERRIDE, FINAL, TRANSIENT, OVERRIDE);
140
141        /**
142         * All opening parenthesis types that are allowed e.g., in type definitions
143         */
144        private static final List<ETokenType> ALL_OPEN_PAREN = Arrays.asList(LPAREN, LBRACE, LT, LBRACK);
145        /**
146         * All closing parenthesis types that are allowed e.g., in type definitions
147         */
148        private static final List<ETokenType> ALL_CLOSE_PAREN = Arrays.asList(RPAREN, RBRACE, GT, RBRACK);
149
150        /** All token types that can be used as literal or identifier in Gosu */
151        private static final Set<ETokenType> LITERALS_AND_IDENTIFIERS = CollectionUtils.unionSet(LITERALS, IDENTIFIERS);
152
153        /** Constructor. */
154        public GosuShallowParser() {
155                super(EGosuParserStates.class, TOP_LEVEL);
156                // top-level statements (using, ...)
157                createMetaRules();
158                // types (classes, interfaces, ...)
159                createModuleRules();
160                createEnumRules();
161                // entities in types
162                createTypeMemberWithBodyRules();
163                createAbstractTypeMemberRules();
164                createVariableRules();
165                // expressions
166                createSubExpressionRules();
167                createGlobalFunctionRules();
168                // must be last as it contains the simple statement matcher
169                createStatementRules();
170                createAnonymousBlockRule();
171        }
172
173        /** Creates the rule covering the occurrences of anonymous blocks. */
174        private void createAnonymousBlockRule() {
175                inAnyState().sequence(LBRACE).createNode(EShallowEntityType.STATEMENT, SubTypeNames.ANONYMOUS_BLOCK)
176                                .parseUntil(IN_METHOD).sequence(RBRACE).endNode();
177        }
178
179        /** Creates the rule for methods which are outside of any classes */
180        private void createGlobalFunctionRules() {
181                inState(TOP_LEVEL).sequence(FUNCTION, IDENTIFIER).skipTo(LPAREN).skipToWithNesting(RPAREN, LPAREN, RPAREN)
182                                .createNode(EShallowEntityType.METHOD, SubTypeNames.GLOBAL_FUNCTION, 1).sequence(LBRACE)
183                                .parseUntil(IN_METHOD).sequence(RBRACE).endNode();
184        }
185
186        /** Creates parsing rules for meta elements. */
187        private void createMetaRules() {
188                inState(TOP_LEVEL).sequence(PACKAGE, IDENTIFIER).repeated(DOT, IDENTIFIER)
189                                .createNode(EShallowEntityType.META, SubTypeNames.PACKAGE, new Region(1, -1)).optional(SEMICOLON)
190                                .endNode();
191                inState(TOP_LEVEL).sequence(USES, IDENTIFIER).repeated(DOT, IDENTIFIER)
192                                .createNode(EShallowEntityType.META, SubTypeNames.USES, new Region(1, -1)).optional(SEMICOLON)
193                                .endNode();
194        }
195
196        /**
197         * Creates rules for parsing namespace/module constructs.
198         */
199        private void createModuleRules() {
200                // class and enum can occur top-level or in a type
201                inStateSkipModifiers(TOP_LEVEL, IN_TYPE).markStart().sequence(EnumSet.of(CLASS, ENUM), IDENTIFIER)
202                                .skipTo(LBRACE).createNode(EShallowEntityType.TYPE, 0, 1).parseUntil(IN_TYPE).sequence(RBRACE)
203                                .endNode();
204                // interface and structures have members without body. They need special
205                // handling since it is hard to match when they terminate (no braces)
206                inStateSkipModifiers(TOP_LEVEL, IN_TYPE, IN_INTERFACE).markStart()
207                                .sequence(EnumSet.of(INTERFACE, STRUCTURE), IDENTIFIER).skipTo(LBRACE)
208                                .createNode(EShallowEntityType.TYPE, 0, 1).parseUntil(EGosuParserStates.IN_INTERFACE).sequence(RBRACE)
209                                .endNode();
210                // enhancement (can't occur in a type)
211                inStateSkipModifiers(TOP_LEVEL).markStart().sequence(ENHANCEMENT, IDENTIFIER).skipTo(LBRACE)
212                                .createNode(EShallowEntityType.TYPE, 0, 1).parseUntil(IN_TYPE).sequence(RBRACE).endNode();
213        }
214
215        /**
216         * Creates a RecognizerBase that starts in the given ParserStates and skips
217         * {@link #MODIFIERS}.
218         */
219        private RecognizerBase<EGosuParserStates> inStateSkipModifiers(EGosuParserStates... states) {
220                return inState(states).repeated(MODIFIERS);
221        }
222
223        /**
224         * Rules for variables
225         */
226        private void createVariableRules() {
227                Set<ETokenType> typeMemberTerminatorsAndEq = CollectionUtils.unionSet(MEMBER_TERMINATOR_TOKENS,
228                                Collections.singleton(EQ));
229                RecognizerBase<EGosuParserStates> varStart = inStateSkipModifiers(IN_TYPE, IN_INTERFACE).markStart()
230                                .sequence(VAR, IDENTIFIER)
231                                .skipBeforeWithNesting(typeMemberTerminatorsAndEq, ALL_OPEN_PAREN, ALL_CLOSE_PAREN);
232                /*
233                 * This got a little complicated. After the EQ sign, we might want a subparse,
234                 * but it could also be a simple literal ("foo"). Therefore, we use an optional
235                 * sub parser and skip to the next member terminator afterwards
236                 */
237                // var declaration with EQ and sub expression
238                varStart.sequence(EQ).createNode(EShallowEntityType.ATTRIBUTE, SubTypeNames.FIELD, 1)
239                                .optionalSubRecognizer(createRecognizer(start -> start.parseOnce(IN_EXPRESSION)))
240                                .skipBeforeWithNesting(MEMBER_TERMINATOR_TOKENS, ALL_OPEN_PAREN, ALL_CLOSE_PAREN).endNode();
241
242                // var without EQ
243                varStart.sequenceBefore(MEMBER_TERMINATOR_TOKENS)
244                                .createNode(EShallowEntityType.ATTRIBUTE, SubTypeNames.FIELD, 1).endNode();
245        }
246
247        /**
248         * Members of types that have no bodies (abstract and in-interface)
249         */
250        private void createAbstractTypeMemberRules() {
251                // function, property, ... without block (in interface or in abstract
252                // class)
253                interfaceMemberSkipBeforeNextMember(FUNCTION, IDENTIFIER)
254                                .createNode(EShallowEntityType.METHOD, SubTypeNames.FUNCTION, 1).endNode();
255
256                interfaceMemberSkipBeforeNextMember(CONSTRUCT)
257                                .createNode(EShallowEntityType.METHOD, SubTypeNames.CONSTRUCTOR, 0).endNode();
258
259                interfaceMemberSkipBeforeNextMember(PROPERTY, EnumSet.of(GET, SET), IDENTIFIER)
260                                .createNode(EShallowEntityType.METHOD, SubTypeNames.PROPERTY, 2).endNode();
261        }
262
263        /**
264         * Creates a RecognizerBase that matches an interface or type member that has
265         * the given match terms after the modifiers and before the first LPAREN. The
266         * RecognizerBase to before the next Member or to the end of the interface/type.
267         */
268        private RecognizerBase<EGosuParserStates> interfaceMemberSkipBeforeNextMember(Object... memberMatchTerms) {
269                return inStateSkipModifiers(IN_INTERFACE, IN_TYPE).markStart().sequence(memberMatchTerms).skipTo(LPAREN)
270                                .skipToWithNesting(RPAREN, LPAREN, RPAREN)
271                                .skipBeforeWithNesting(MEMBER_TERMINATOR_TOKENS, ALL_OPEN_PAREN, ALL_CLOSE_PAREN);
272        }
273
274        /**
275         * Non-abstract members of Types
276         */
277        private void createTypeMemberWithBodyRules() {
278                // function
279                skipToTypeMemberBody(inStateSkipModifiers(IN_TYPE).markStart().sequence(FUNCTION, IDENTIFIER).skipTo(LPAREN)
280                                .skipToWithNesting(RPAREN, LPAREN, RPAREN))
281                                                .createNode(EShallowEntityType.METHOD, SubTypeNames.FUNCTION, 1).parseUntil(IN_METHOD)
282                                                .sequence(RBRACE).endNode();
283                // construct
284                skipToTypeMemberBody(inStateSkipModifiers(IN_TYPE).markStart().sequence(CONSTRUCT).skipTo(LPAREN)
285                                .skipToWithNesting(RPAREN, LPAREN, RPAREN))
286                                                .createNode(EShallowEntityType.METHOD, SubTypeNames.CONSTRUCTOR, 0).parseUntil(IN_METHOD)
287                                                .sequence(RBRACE).endNode();
288                // property
289                skipToTypeMemberBody(
290                                inStateSkipModifiers(IN_TYPE).markStart().sequence(PROPERTY).sequence(EnumSet.of(GET, SET), IDENTIFIER))
291                                                .createNode(EShallowEntityType.METHOD, SubTypeNames.PROPERTY, 1).parseUntil(IN_METHOD)
292                                                .sequence(RBRACE).endNode();
293        }
294
295        /** */
296        private void createEnumRules() {
297                RecognizerBase<EGosuParserStates> enumLiteral = inState(IN_TYPE).sequence(IDENTIFIER)
298                                .sequenceBefore(EnumSet.of(SEMICOLON, COMMA, LBRACE, RBRACE))
299                                .createNode(EShallowEntityType.ATTRIBUTE, ENUM_LITERAL, 0);
300                enumLiteral.sequence(LBRACE).parseUntil(IN_TYPE).sequence(RBRACE).optional(EnumSet.of(SEMICOLON, COMMA))
301                                .endNode();
302                enumLiteral.optional(EnumSet.of(SEMICOLON, COMMA)).endNode();
303
304                RecognizerBase<EGosuParserStates> constructorOrEnum = inState(IN_TYPE)
305                                .optional(EnumSet.of(PUBLIC, PRIVATE, PROTECTED)).markStart().sequence(IDENTIFIER, LPAREN)
306                                .skipToWithNesting(RPAREN, LPAREN, RPAREN);
307                constructorOrEnum.sequence(EnumSet.of(SEMICOLON, COMMA))
308                                .createNode(EShallowEntityType.ATTRIBUTE, ENUM_LITERAL, 0).endNode();
309                constructorOrEnum.skipTo(LBRACE).sequenceBefore(EnumSet.of(AT_OPERATOR, PUBLIC, PRIVATE, PROTECTED))
310                                .createNode(EShallowEntityType.ATTRIBUTE, ENUM_LITERAL, 0).parseUntil(IN_TYPE).sequence(RBRACE)
311                                .sequence(EnumSet.of(SEMICOLON, COMMA)).endNode();
312        }
313
314        /**
315         * Skips anything before the LBRACE starting the body. If there is a
316         * {@link #MEMBER_TERMINATOR_TOKENS} before, this does not match.
317         */
318        private static RecognizerBase<EGosuParserStates> skipToTypeMemberBody(
319                        RecognizerBase<EGosuParserStates> recognizerBase) {
320                Set<ETokenType> typeMemberTerminatorsAndLbrace = CollectionUtils.unionSet(MEMBER_TERMINATOR_TOKENS,
321                                Collections.singleton(LBRACE));
322                return recognizerBase.skipBefore(typeMemberTerminatorsAndLbrace).sequence(LBRACE);
323        }
324
325        /** Creates parsing rules for statements. */
326        private void createStatementRules() {
327                // empty statement
328                inAnyState().sequence(SEMICOLON).createNode(EShallowEntityType.STATEMENT, SubTypeNames.EMPTY_STATEMENT)
329                                .endNode();
330
331                createElseIfRule();
332                createSimpleBlockRules();
333                createSwitchCaseRules();
334
335                // var statement
336                RecognizerBase<EGosuParserStates> varStatementRecognizer = createRecognizer(
337                                start -> start.repeated(MODIFIERS).sequence(VAR, IDENTIFIER));
338                inState(IN_METHOD, IN_TYPE).preCondition(varStatementRecognizer)
339                                .subRecognizer(
340                                                new GosuSimpleStatementRecognizer(EShallowEntityType.STATEMENT, SubTypeNames.LOCAL_VARIABLE), 1,
341                                                1)
342                                .endNode();
343                inState(TOP_LEVEL).preCondition(varStatementRecognizer)
344                                .subRecognizer(
345                                                new GosuSimpleStatementRecognizer(EShallowEntityType.STATEMENT, SubTypeNames.GLOBAL_VARIABLE),
346                                                1, 1)
347                                .endNode();
348
349                // simple statement
350                inState(IN_METHOD, TOP_LEVEL, IN_TYPE).sequenceBefore(LPAREN)
351                                .subRecognizer(
352                                                new GosuSimpleStatementRecognizer(EShallowEntityType.STATEMENT, SubTypeNames.SIMPLE_STATEMENT),
353                                                1, 1)
354                                .endNode();
355
356                inState(IN_METHOD, TOP_LEVEL, IN_TYPE)
357                                .sequenceBefore(EnumSet.of(OPERATOR, LITERAL, ETokenClass.IDENTIFIER, KEYWORD))
358                                .subRecognizer(
359                                                new GosuSimpleStatementRecognizer(EShallowEntityType.STATEMENT, SubTypeNames.SIMPLE_STATEMENT),
360                                                1, 1)
361                                .endNode();
362
363        }
364
365        /** Creates the rule for "else if" construct. */
366        private void createElseIfRule() {
367                RecognizerBase<EGosuParserStates> elseIfAlternative = inState(IN_METHOD, TOP_LEVEL).sequence(ELSE, IF)
368                                .createNode(EShallowEntityType.STATEMENT, SubTypeNames.ELSE_IF)
369                                .skipNested(LPAREN, RPAREN, getSubExpressionRecognizer());
370                endWithPossibleContinuation(elseIfAlternative.sequence(LBRACE).parseUntil(IN_METHOD).sequence(RBRACE),
371                                EnumSet.of(ELSE));
372                endWithPossibleContinuation(elseIfAlternative.parseOnce(IN_METHOD), EnumSet.of(ELSE));
373        }
374
375        /** Creates simple block rules. */
376        private void createSimpleBlockRules() {
377                createBlockRuleWithContinuation(EnumSet.of(WHILE, FOR, SWITCH, WITH), null, true);
378                createBlockRuleWithContinuation(EnumSet.of(ELSE, FINALLY), null, false);
379                createBlockRuleWithContinuation(EnumSet.of(IF), EnumSet.of(ELSE), true);
380                createBlockRuleWithContinuation(EnumSet.of(TRY, CATCH), EnumSet.of(CATCH, FINALLY), true);
381        }
382
383        /** Creates rules for switch/case. */
384        private void createSwitchCaseRules() {
385                inState(IN_METHOD).sequence(CASE, LITERALS_AND_IDENTIFIERS, COLON)
386                                .createNode(EShallowEntityType.META, SubTypeNames.CASE, 1).endNode();
387                inState(IN_METHOD).sequence(CASE).skipToWithNesting(COLON, LPAREN, RPAREN)
388                                .createNode(EShallowEntityType.META, 0).endNode();
389                inState(IN_METHOD).sequence(DEFAULT, COLON).createNode(EShallowEntityType.META, SubTypeNames.DEFAULT, 0)
390                                .endNode();
391        }
392
393        /**
394         * Creates a rule for recognizing a statement starting with a single keyword,
395         * optionally followed by an expression in parentheses, and followed by a block
396         * or a single statement.
397         *
398         * @param continuationTokens
399         *            list of tokens that indicate a continued statement if encountered
400         *            after the block. May be null.
401         */
402        private void createBlockRuleWithContinuation(EnumSet<ETokenType> startTokens,
403                        EnumSet<ETokenType> continuationTokens, boolean canBeFollowedByParentheses) {
404                RecognizerBase<EGosuParserStates> alternative = inAnyState().sequence(startTokens)
405                                .createNode(EShallowEntityType.STATEMENT, 0);
406                if (canBeFollowedByParentheses) {
407                        alternative = alternative.skipNested(LPAREN, RPAREN, getSubExpressionRecognizer());
408                }
409
410                endWithPossibleContinuation(alternative.sequence(LBRACE).parseUntil(IN_METHOD).sequence(RBRACE),
411                                continuationTokens);
412                endWithPossibleContinuation(alternative.parseOnce(IN_METHOD), continuationTokens);
413        }
414
415        /**
416         * Create rules for shallow entities that can occur in expressions e.g.,
417         * lambdas.
418         */
419        private void createSubExpressionRules() {
420                // lambda expressions
421                RecognizerBase<EGosuParserStates> lambdaStart = inState(IN_EXPRESSION).sequence(BACKSLASH).skipTo(ARROW)
422                                .createNode(EShallowEntityType.METHOD, LAMBDA);
423                lambdaStart.sequence(LBRACE).parseUntil(IN_METHOD).sequence(RBRACE).endNode();
424
425                // lambda without any braces
426                lambdaStart.parseOnce(IN_METHOD).endNode();
427        }
428
429        /** Returns a sub expression recognizer */
430        private RecognizerBase<EGosuParserStates> getSubExpressionRecognizer() {
431                // the BACKSLASH should trigger the lambda parsing rules
432                return createRecognizer(start -> start.sequenceBefore(BACKSLASH).parseOnce(EGosuParserStates.IN_EXPRESSION));
433        }
434}