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}