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}