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.php; 018 019import static eu.cqse.check.framework.scanner.ETokenType.ABSTRACT; 020import static eu.cqse.check.framework.scanner.ETokenType.AND; 021import static eu.cqse.check.framework.scanner.ETokenType.AT; 022import static eu.cqse.check.framework.scanner.ETokenType.BACKSLASH; 023import static eu.cqse.check.framework.scanner.ETokenType.BREAK; 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.CLONE; 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.CONST; 031import static eu.cqse.check.framework.scanner.ETokenType.CONSTRUCTOR; 032import static eu.cqse.check.framework.scanner.ETokenType.CONTINUE; 033import static eu.cqse.check.framework.scanner.ETokenType.DECLARE; 034import static eu.cqse.check.framework.scanner.ETokenType.DEFAULT; 035import static eu.cqse.check.framework.scanner.ETokenType.DESTRUCTOR; 036import static eu.cqse.check.framework.scanner.ETokenType.ECHO; 037import static eu.cqse.check.framework.scanner.ETokenType.ELSE; 038import static eu.cqse.check.framework.scanner.ETokenType.ELSEIF; 039import static eu.cqse.check.framework.scanner.ETokenType.ENDDECLARE; 040import static eu.cqse.check.framework.scanner.ETokenType.ENDFOR; 041import static eu.cqse.check.framework.scanner.ETokenType.ENDFOREACH; 042import static eu.cqse.check.framework.scanner.ETokenType.ENDIF; 043import static eu.cqse.check.framework.scanner.ETokenType.ENDSWITCH; 044import static eu.cqse.check.framework.scanner.ETokenType.ENDWHILE; 045import static eu.cqse.check.framework.scanner.ETokenType.EQ; 046import static eu.cqse.check.framework.scanner.ETokenType.EXTENDS; 047import static eu.cqse.check.framework.scanner.ETokenType.FINAL; 048import static eu.cqse.check.framework.scanner.ETokenType.FINALLY; 049import static eu.cqse.check.framework.scanner.ETokenType.FOR; 050import static eu.cqse.check.framework.scanner.ETokenType.FOREACH; 051import static eu.cqse.check.framework.scanner.ETokenType.FUNCTION; 052import static eu.cqse.check.framework.scanner.ETokenType.GLOBAL; 053import static eu.cqse.check.framework.scanner.ETokenType.GOTO; 054import static eu.cqse.check.framework.scanner.ETokenType.IDENTIFIER; 055import static eu.cqse.check.framework.scanner.ETokenType.IF; 056import static eu.cqse.check.framework.scanner.ETokenType.INCLUDE; 057import static eu.cqse.check.framework.scanner.ETokenType.INCLUDE_ONCE; 058import static eu.cqse.check.framework.scanner.ETokenType.INTEGER_LITERAL; 059import static eu.cqse.check.framework.scanner.ETokenType.INTERFACE; 060import static eu.cqse.check.framework.scanner.ETokenType.LBRACE; 061import static eu.cqse.check.framework.scanner.ETokenType.LPAREN; 062import static eu.cqse.check.framework.scanner.ETokenType.MINUSMINUS; 063import static eu.cqse.check.framework.scanner.ETokenType.NAMESPACE; 064import static eu.cqse.check.framework.scanner.ETokenType.NEW; 065import static eu.cqse.check.framework.scanner.ETokenType.PLUSPLUS; 066import static eu.cqse.check.framework.scanner.ETokenType.PRINT; 067import static eu.cqse.check.framework.scanner.ETokenType.PRIVATE; 068import static eu.cqse.check.framework.scanner.ETokenType.PROTECTED; 069import static eu.cqse.check.framework.scanner.ETokenType.PUBLIC; 070import static eu.cqse.check.framework.scanner.ETokenType.RBRACE; 071import static eu.cqse.check.framework.scanner.ETokenType.REQUIRE; 072import static eu.cqse.check.framework.scanner.ETokenType.REQUIRE_ONCE; 073import static eu.cqse.check.framework.scanner.ETokenType.RETURN; 074import static eu.cqse.check.framework.scanner.ETokenType.RPAREN; 075import static eu.cqse.check.framework.scanner.ETokenType.SEMICOLON; 076import static eu.cqse.check.framework.scanner.ETokenType.STATIC; 077import static eu.cqse.check.framework.scanner.ETokenType.SWITCH; 078import static eu.cqse.check.framework.scanner.ETokenType.TEMPLATE_CODE_END; 079import static eu.cqse.check.framework.scanner.ETokenType.THIS; 080import static eu.cqse.check.framework.scanner.ETokenType.THROW; 081import static eu.cqse.check.framework.scanner.ETokenType.TRAIT; 082import static eu.cqse.check.framework.scanner.ETokenType.TRY; 083import static eu.cqse.check.framework.scanner.ETokenType.USE; 084import static eu.cqse.check.framework.scanner.ETokenType.VAR; 085import static eu.cqse.check.framework.scanner.ETokenType.WHILE; 086import static eu.cqse.check.framework.scanner.ETokenType.YIELD; 087import static eu.cqse.check.framework.shallowparser.SubTypeNames.ELSE_IF; 088import static eu.cqse.check.framework.shallowparser.languages.base.EGenericParserStates.IN_EXPRESSION; 089import static eu.cqse.check.framework.shallowparser.languages.base.EGenericParserStates.IN_METHOD; 090import static eu.cqse.check.framework.shallowparser.languages.base.EGenericParserStates.IN_TYPE; 091import static eu.cqse.check.framework.shallowparser.languages.base.EGenericParserStates.TOP_LEVEL; 092 093import java.util.EnumSet; 094 095import org.conqat.lib.commons.region.Region; 096 097import eu.cqse.check.framework.scanner.ETokenType; 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.base.EGenericParserStates; 103 104/** 105 * PHP shallow parser. 106 * 107 * Some special cases for this parser: 108 * <ul> 109 * <li>Beware, that in most cases field definitions / variable declarations are 110 * recognized as simple statements.</li> 111 * <li>As of PHP 5.4.0, writing CONTINUE without a semicolon raises a compile 112 * error. However this parser accepts CONTINUE from the prior versions as well. 113 * </li> 114 * </ul> 115 */ 116public class PhpShallowParser extends ShallowParserBase<EGenericParserStates> { 117 118 /** All token types that may stand at the beginning of new statement. */ 119 private static final EnumSet<ETokenType> STATEMENT_START_TOKENS = EnumSet.of(BACKSLASH, BREAK, CLONE, ECHO, GOTO, 120 IDENTIFIER, MINUSMINUS, NAMESPACE, NEW, PLUSPLUS, PRINT, RETURN, STATIC, THIS, THROW, YIELD, AT); 121 /** A set of access modifiers. */ 122 private static final EnumSet<ETokenType> ACCESS_MODIFIERS = EnumSet.of(PRIVATE, PUBLIC, PROTECTED, STATIC, GLOBAL, 123 CONST, ABSTRACT, FINAL, VAR); 124 125 /** Valid identifier token types. */ 126 private static final EnumSet<ETokenType> VALID_IDENTIFIERS = EnumSet.of(IDENTIFIER, CLONE); 127 128 /** Constructor. */ 129 public PhpShallowParser() { 130 super(EGenericParserStates.class, TOP_LEVEL); 131 createInExpressionRules(); 132 createConstructorRule(); 133 createMethodRule(); 134 createTypeRule(); 135 createIfElseRule(); 136 createTryCatchRule(); 137 createSwitchCaseRule(); 138 createLoopRules(); 139 createNamespaceRule(); 140 createMetaRules(); 141 createVariableRule(); 142 createAnonymousBlockRule(); 143 createSingleStatementRule(); 144 createEmptyStatementRule(); 145 } 146 147 /** 148 * Creates the rule for anonymous inner classes and anonymous functions. They 149 * are typically found within other statements. 150 */ 151 private void createInExpressionRules() { 152 153 // Anonymous inner classes 154 RecognizerBase<EGenericParserStates> innerClassBase = inState(IN_EXPRESSION).sequence(NEW, CLASS) 155 .createNode(EShallowEntityType.TYPE, SubTypeNames.ANONYMOUS_CLASS, 1).skipBefore(LBRACE); 156 endTypeRule(innerClassBase); 157 158 // Anonymous functions 159 RecognizerBase<EGenericParserStates> closureBase = inState(IN_EXPRESSION).sequence(FUNCTION) 160 .createNode(EShallowEntityType.STATEMENT, SubTypeNames.ANONYMOUS_FUNCTION) 161 .skipNested(LPAREN, RPAREN, createSubexpressionRecognizer()); 162 163 RecognizerBase<EGenericParserStates> closureBaseWithUse = closureBase.sequence(USE).skipNested(LPAREN, RPAREN, 164 createSubexpressionRecognizer()); 165 166 endMethodRule(closureBaseWithUse); 167 endMethodRule(closureBase); 168 } 169 170 /** 171 * Create a rule that matches try catch finally statements. 172 */ 173 private void createTryCatchRule() { 174 RecognizerBase<EGenericParserStates> tryBase = inState(TOP_LEVEL, IN_METHOD).sequence(TRY) 175 .createNode(EShallowEntityType.STATEMENT, SubTypeNames.TRY); 176 177 RecognizerBase<EGenericParserStates> catchBase = inState(TOP_LEVEL, IN_METHOD).sequence(CATCH) 178 .createNode(EShallowEntityType.STATEMENT, SubTypeNames.CATCH).skipNested(LPAREN, RPAREN); 179 180 RecognizerBase<EGenericParserStates> finallyBase = inState(TOP_LEVEL, IN_METHOD).sequence(FINALLY) 181 .createNode(EShallowEntityType.STATEMENT, SubTypeNames.FINALLY); 182 183 continueTryCatchRule(tryBase); 184 continueTryCatchRule(catchBase); 185 continueTryCatchRule(finallyBase); 186 187 } 188 189 /** 190 * Continue the try catch finally rule. 191 */ 192 private static void continueTryCatchRule(RecognizerBase<EGenericParserStates> base) { 193 194 RecognizerBase<EGenericParserStates> multilineBase = base.sequence(LBRACE).parseUntil(IN_METHOD) 195 .sequence(RBRACE); 196 RecognizerBase<EGenericParserStates> singlelineBase = base.parseOnce(IN_METHOD); 197 198 endTryCatchRule(multilineBase); 199 endTryCatchRule(singlelineBase); 200 } 201 202 /** 203 * End the try catch finally rule. 204 */ 205 private static void endTryCatchRule(RecognizerBase<EGenericParserStates> base) { 206 base.sequenceBefore(EnumSet.of(CATCH, FINALLY)).endNodeWithContinuation(); 207 base.endNode(); 208 } 209 210 /** 211 * Create a rule that matches constructors and destructors. 212 */ 213 private void createConstructorRule() { 214 createConstructorRule(CONSTRUCTOR, SubTypeNames.CONSTRUCTOR); 215 createConstructorRule(DESTRUCTOR, SubTypeNames.DESTRUCTOR); 216 } 217 218 /** 219 * Create a rule that matches constructors or destructors. 220 */ 221 private void createConstructorRule(ETokenType tokenType, String subtypeName) { 222 RecognizerBase<EGenericParserStates> base = inState(IN_TYPE).repeated(ACCESS_MODIFIERS).sequence(FUNCTION) 223 .sequence(tokenType).createNode(EShallowEntityType.METHOD, subtypeName).skipNested(LPAREN, RPAREN); 224 base.sequence(LBRACE).parseUntil(IN_METHOD).sequence(RBRACE).endNode(); 225 base.sequence(EnumSet.of(SEMICOLON, TEMPLATE_CODE_END)).endNode(); 226 } 227 228 /** 229 * Create a rule that matches modules (e.g. namespaces). 230 */ 231 private void createNamespaceRule() { 232 233 RecognizerBase<EGenericParserStates> base = inState(TOP_LEVEL).sequence(NAMESPACE) 234 // We have to make sure to not match the sequence NAMESPACE - 235 // BACKSLASH here. These can be used to call functions etc. 236 .sequenceBefore(EnumSet.complementOf(EnumSet.of(BACKSLASH))) 237 .skipBefore(EnumSet.of(SEMICOLON, TEMPLATE_CODE_END, LBRACE)) 238 .createNode(EShallowEntityType.MODULE, SubTypeNames.NAMESPACE, new Region(1, -1)); 239 240 base.sequence(EnumSet.of(SEMICOLON, TEMPLATE_CODE_END)).endNode(); 241 base.sequence(LBRACE).parseUntil(TOP_LEVEL).sequence(RBRACE).endNode(); 242 } 243 244 /** 245 * Create a rule that matches an empty statement (the semicolon). 246 */ 247 private void createEmptyStatementRule() { 248 inAnyState().sequence(SEMICOLON).createNode(EShallowEntityType.STATEMENT, SubTypeNames.EMPTY_STATEMENT) 249 .endNode(); 250 251 } 252 253 /** 254 * Create a rule that matches anonymous blocks. 255 */ 256 private void createAnonymousBlockRule() { 257 inState(TOP_LEVEL, IN_METHOD).sequence(LBRACE) 258 .createNode(EShallowEntityType.STATEMENT, SubTypeNames.ANONYMOUS_BLOCK).parseUntil(IN_METHOD) 259 .sequence(RBRACE).endNode(); 260 } 261 262 /** 263 * Create rules that match labels, use and declare statements. 264 */ 265 private void createMetaRules() { 266 // Declare 267 RecognizerBase<EGenericParserStates> baseDeclare = inState(TOP_LEVEL, IN_METHOD).sequence(DECLARE) 268 .createNode(EShallowEntityType.STATEMENT, SubTypeNames.DECLARATION).skipNested(LPAREN, RPAREN); 269 baseDeclare.sequence(EnumSet.of(SEMICOLON, TEMPLATE_CODE_END)).endNode(); 270 endControlFlowStatement(baseDeclare, ENDDECLARE); 271 272 // Use 273 RecognizerBase<EGenericParserStates> baseUse = inAnyState().sequence(USE) 274 .createNode(EShallowEntityType.META, SubTypeNames.USE, new Region(1, -1)) 275 .skipBefore(EnumSet.of(TEMPLATE_CODE_END, SEMICOLON, LBRACE)); 276 277 baseUse.sequence(EnumSet.of(SEMICOLON, TEMPLATE_CODE_END)).endNode(); 278 baseUse.sequence(LBRACE).parseUntil(IN_METHOD).sequence(RBRACE).endNode(); 279 280 createRequireRule(REQUIRE, SubTypeNames.REQUIRE); 281 createRequireRule(REQUIRE_ONCE, SubTypeNames.REQUIRE_ONCE); 282 createRequireRule(INCLUDE, SubTypeNames.INCLUDE); 283 createRequireRule(INCLUDE_ONCE, SubTypeNames.INCLUDE_ONCE); 284 285 // Label 286 inAnyState().sequence(VALID_IDENTIFIERS, COLON).createNode(EShallowEntityType.META, SubTypeNames.LABEL, 0) 287 .endNode(); 288 } 289 290 /** Creates a rule for include/require */ 291 private void createRequireRule(ETokenType requireToken, String subtype) { 292 inAnyState().sequence(requireToken).createNode(EShallowEntityType.META, subtype, new Region(1, -1)) 293 .skipTo(EnumSet.of(TEMPLATE_CODE_END, SEMICOLON)).endNode(); 294 } 295 296 /** 297 * Create a rule that matches switch case. 298 */ 299 private void createSwitchCaseRule() { 300 inState(IN_METHOD).sequence(EnumSet.of(CASE, DEFAULT)).createNode(EShallowEntityType.META, 0) 301 .optional(VALID_IDENTIFIERS).repeated(COLON, COLON, VALID_IDENTIFIERS) 302 // "case 1;" is allowed. 303 .skipTo(EnumSet.of(COLON, SEMICOLON, TEMPLATE_CODE_END)).endNode(); 304 305 RecognizerBase<EGenericParserStates> base = inState(TOP_LEVEL, IN_METHOD).sequence(SWITCH) 306 .createNode(EShallowEntityType.STATEMENT, SubTypeNames.SWITCH) 307 .skipNested(LPAREN, RPAREN, createSubexpressionRecognizer()); 308 endControlFlowStatement(base, ENDSWITCH); 309 } 310 311 /** 312 * Create a rule that matches type declarations. 313 */ 314 private void createTypeRule() { 315 createTypeRule(CLASS, SubTypeNames.CLASS); 316 createTypeRule(TRAIT, SubTypeNames.TRAIT); 317 createInterfaceRule(); 318 } 319 320 /** 321 * Create a rule that matches interface declarations. 322 */ 323 private void createInterfaceRule() { 324 RecognizerBase<EGenericParserStates> interfaceRule = beginTypeRule(INTERFACE, SubTypeNames.INTERFACE); 325 326 // This rule is necessary for types extending qualified identifiers. 327 // E.g., "interface Foo extends Namespace1\Namespace2\A" 328 endInterfaceRule(interfaceRule.sequence(EXTENDS).skipBefore(LBRACE)); 329 // This rule captures interfaces without body (even without open/closing 330 // braces) 331 endInterfaceRule(interfaceRule.sequence(EXTENDS, VALID_IDENTIFIERS).repeated(COMMA, VALID_IDENTIFIERS)); 332 endInterfaceRule(interfaceRule); 333 } 334 335 /** End a interface declaration rule. */ 336 private static void endInterfaceRule(RecognizerBase<EGenericParserStates> interfaceRule) { 337 endTypeRule(interfaceRule.sequenceBefore(LBRACE)); 338 interfaceRule.endNode(); 339 } 340 341 /** 342 * Create a rule that matches a specific type declaration. 343 */ 344 private void createTypeRule(ETokenType type, String subtypeName) { 345 endTypeRule(beginTypeRule(type, subtypeName).skipBefore(LBRACE)); 346 } 347 348 /** Begin a type rule with the given token type and sub type. */ 349 private RecognizerBase<EGenericParserStates> beginTypeRule(ETokenType type, String subtypeName) { 350 return inState(TOP_LEVEL, IN_METHOD).repeated(ACCESS_MODIFIERS).sequence(type).markStart() 351 .sequence(VALID_IDENTIFIERS).createNode(EShallowEntityType.TYPE, subtypeName, 0); 352 } 353 354 /** 355 * End type rule creation with a parse within IN_TYPE surrounded by braces. 356 */ 357 private static void endTypeRule(RecognizerBase<EGenericParserStates> base) { 358 base.sequence(LBRACE).parseUntil(IN_TYPE).sequence(RBRACE).endNode(); 359 } 360 361 /** 362 * Create a rule that matches variables and attributes. 363 */ 364 private void createVariableRule() { 365 inState(TOP_LEVEL, IN_METHOD).repeated(ACCESS_MODIFIERS).markStart().sequence(VALID_IDENTIFIERS) 366 .sequence(EnumSet.of(SEMICOLON, TEMPLATE_CODE_END)) 367 .createNode(EShallowEntityType.STATEMENT, SubTypeNames.LOCAL_VARIABLE, 0).endNode(); 368 369 inState(TOP_LEVEL, IN_METHOD).sequence(ACCESS_MODIFIERS).repeated(ACCESS_MODIFIERS).markStart() 370 .sequence(VALID_IDENTIFIERS).createNode(EShallowEntityType.STATEMENT, SubTypeNames.LOCAL_VARIABLE, 0) 371 .skipToWithNesting(EnumSet.of(SEMICOLON, TEMPLATE_CODE_END), LPAREN, RPAREN, 372 createSubexpressionRecognizer()) 373 .endNode(); 374 375 inState(IN_TYPE).repeated(ACCESS_MODIFIERS).markStart().sequence(VALID_IDENTIFIERS).sequenceBefore(EQ) 376 .createNode(EShallowEntityType.ATTRIBUTE, SubTypeNames.ATTRIBUTE, 0) 377 .skipToWithNesting(EnumSet.of(SEMICOLON, TEMPLATE_CODE_END), LPAREN, RPAREN, 378 createSubexpressionRecognizer()) 379 .endNode(); 380 381 inState(IN_TYPE).repeated(ACCESS_MODIFIERS).markStart() 382 .sequence(VALID_IDENTIFIERS, EnumSet.of(SEMICOLON, TEMPLATE_CODE_END)) 383 .createNode(EShallowEntityType.ATTRIBUTE, SubTypeNames.ATTRIBUTE, 0).endNode(); 384 } 385 386 /** 387 * Create rules that match all kinds of loop statements. 388 */ 389 private void createLoopRules() { 390 createLoopRule(FOREACH, ENDFOREACH); 391 createLoopRule(FOR, ENDFOR); 392 createLoopRule(WHILE, ENDWHILE); 393 394 // Do-While 395 RecognizerBase<EGenericParserStates> base = inState(IN_METHOD, TOP_LEVEL).sequence(ETokenType.DO) 396 .createNode(EShallowEntityType.STATEMENT, SubTypeNames.DO); 397 base.sequence(LBRACE).parseUntil(IN_METHOD).sequence(RBRACE, WHILE) 398 .skipNested(LPAREN, RPAREN, createSubexpressionRecognizer()).optional(SEMICOLON).endNode(); 399 base.parseOnce(IN_METHOD).sequence(WHILE).skipNested(LPAREN, RPAREN, createSubexpressionRecognizer()) 400 .optional(SEMICOLON).endNode(); 401 } 402 403 /** 404 * Create a rule that matches the normal version as well as the alternative 405 * versions of loop statements. 406 */ 407 private void createLoopRule(ETokenType startToken, ETokenType endToken) { 408 RecognizerBase<EGenericParserStates> base = inState(IN_METHOD, TOP_LEVEL).sequence(startToken) 409 .skipNested(LPAREN, RPAREN, createSubexpressionRecognizer()) 410 .createNode(EShallowEntityType.STATEMENT, 0); 411 endControlFlowStatement(base, endToken); 412 } 413 414 /** 415 * Create a rule that matches if statements. 416 */ 417 private void createIfElseRule() { 418 RecognizerBase<EGenericParserStates> base = inState(IN_METHOD, TOP_LEVEL).sequence(EnumSet.of(IF, ELSEIF)) 419 .createNode(EShallowEntityType.STATEMENT, 0) 420 .skipNested(LPAREN, RPAREN, createSubexpressionRecognizer()); 421 continueIfElseRule(base, true); 422 423 RecognizerBase<EGenericParserStates> elseIfbase = inState(IN_METHOD, TOP_LEVEL).sequence(ELSE, IF) 424 .createNode(EShallowEntityType.STATEMENT, ELSE_IF) 425 .skipNested(LPAREN, RPAREN, createSubexpressionRecognizer()); 426 continueIfElseRule(elseIfbase, true); 427 428 RecognizerBase<EGenericParserStates> elseBase = inState(IN_METHOD, TOP_LEVEL).sequence(ELSE) 429 .createNode(EShallowEntityType.STATEMENT, 0); 430 continueIfElseRule(elseBase, false); 431 } 432 433 /** 434 * Continues an if else rule possibly with continuation. 435 */ 436 private static void continueIfElseRule(RecognizerBase<EGenericParserStates> base, boolean allowContinuation) { 437 RecognizerBase<EGenericParserStates> multiLineBase = base.sequence(LBRACE).parseUntil(IN_METHOD) 438 .sequence(RBRACE); 439 440 RecognizerBase<EGenericParserStates> multiLineBaseAlternative = base.sequence(COLON).parseUntil(IN_METHOD) 441 .sequenceBefore(EnumSet.of(ELSE, ELSEIF, ENDIF)); 442 443 RecognizerBase<EGenericParserStates> singleLineBase = base.parseOnce(IN_METHOD); 444 445 endIfElseRule(singleLineBase, false, allowContinuation); 446 endIfElseRule(multiLineBase, false, allowContinuation); 447 endIfElseRule(multiLineBaseAlternative, true, allowContinuation); 448 } 449 450 /** 451 * Ends an if else rule possibly with continuation. 452 */ 453 private static void endIfElseRule(RecognizerBase<EGenericParserStates> base, boolean expectsEndif, 454 boolean allowContinuation) { 455 if (allowContinuation) { 456 base.sequenceBefore(EnumSet.of(ELSE, ELSEIF)).endNodeWithContinuation(); 457 } 458 459 if (expectsEndif) { 460 base.optional(ENDIF, SEMICOLON).endNode(); 461 base.optional(ENDIF).endNode(); 462 } else { 463 base.endNode(); 464 } 465 } 466 467 /** 468 * Create a rule that matches method declarations. In PHP there is the concept 469 * of conditional functions, therefore we recognize functions within IN_METHOD, 470 * too. 471 */ 472 private void createMethodRule() { 473 RecognizerBase<EGenericParserStates> base = inState(TOP_LEVEL, IN_TYPE, IN_METHOD).repeated(ACCESS_MODIFIERS) 474 .sequence(FUNCTION).optional(AND).sequence(VALID_IDENTIFIERS) 475 .createNode(EShallowEntityType.METHOD, SubTypeNames.METHOD, -1) 476 // we need this skip because of strict typing 477 .skipBeforeWithNesting(EnumSet.of(LBRACE, SEMICOLON, TEMPLATE_CODE_END), LPAREN, RPAREN); 478 479 endMethodRule(base); 480 // abstract method in e.g. interfaces. 481 base.sequence(EnumSet.of(SEMICOLON, TEMPLATE_CODE_END)).endNode(); 482 } 483 484 /** 485 * Ends a standard method rule with a parse within IN_METHOD surrounded by 486 * braces. 487 */ 488 private static void endMethodRule(RecognizerBase<EGenericParserStates> base) { 489 base.sequence(LBRACE).parseUntil(IN_METHOD).sequence(RBRACE).endNode(); 490 } 491 492 /** 493 * Create a rule that matches single statements. 494 */ 495 private void createSingleStatementRule() { 496 inState(TOP_LEVEL, IN_METHOD).sequenceBefore(STATEMENT_START_TOKENS) 497 .createNode(EShallowEntityType.STATEMENT, SubTypeNames.SIMPLE_STATEMENT, 0) 498 .skipToWithNesting(EnumSet.of(SEMICOLON, TEMPLATE_CODE_END), LPAREN, RPAREN, 499 createSubexpressionRecognizer()) 500 .endNode(); 501 502 // Statements that are enclosed in parenthesis. 503 inState(TOP_LEVEL, IN_METHOD).sequenceBefore(LPAREN) 504 .createNode(EShallowEntityType.STATEMENT, SubTypeNames.SIMPLE_STATEMENT, 0) 505 .skipToWithNesting(EnumSet.of(SEMICOLON, TEMPLATE_CODE_END), LPAREN, RPAREN, 506 createSubexpressionRecognizer()) 507 .endNode(); 508 509 // Continue is a special case, as one could omit the 510 // semicolon prior to PHP 5.4.0. 511 RecognizerBase<EGenericParserStates> continueBase = inState(TOP_LEVEL, IN_METHOD).sequence(CONTINUE) 512 .createNode(EShallowEntityType.STATEMENT, SubTypeNames.SIMPLE_STATEMENT, 0); 513 514 EnumSet<ETokenType> continueTypes = EnumSet.copyOf(VALID_IDENTIFIERS); 515 continueTypes.add(INTEGER_LITERAL); 516 continueBase.sequence(continueTypes, SEMICOLON).endNode(); 517 continueBase.optional(SEMICOLON).endNode(); 518 } 519 520 /** 521 * Ends a typical control flow statement with. 522 */ 523 private static void endControlFlowStatement(RecognizerBase<EGenericParserStates> base, 524 ETokenType alternativeEndToken) { 525 base.sequence(COLON).parseUntil(IN_METHOD).sequence(alternativeEndToken).optional(SEMICOLON).endNode(); 526 base.sequence(LBRACE).parseUntil(IN_METHOD).sequence(RBRACE).optional(SEMICOLON).endNode(); 527 base.sequence(SEMICOLON).endNode(); 528 base.parseOnce(IN_METHOD).optional(SEMICOLON).endNode(); 529 } 530 531 /** 532 * Create a new sub expression recognizer. 533 */ 534 public static RecognizerBase<EGenericParserStates> createSubexpressionRecognizer() { 535 return new PhpSubExpressionRecognizer(); 536 } 537}