ugly syntax supported (endif, endwhile endfor)
[phpeclipse.git] / net.sourceforge.phpeclipse / src / test / PHPParser.jj
1 options {
2   LOOKAHEAD = 1;
3   CHOICE_AMBIGUITY_CHECK = 2;
4   OTHER_AMBIGUITY_CHECK = 1;
5   STATIC = true;
6   DEBUG_PARSER = false;
7   DEBUG_LOOKAHEAD = false;
8   DEBUG_TOKEN_MANAGER = false;
9   OPTIMIZE_TOKEN_MANAGER = false;
10   ERROR_REPORTING = true;
11   JAVA_UNICODE_ESCAPE = false;
12   UNICODE_INPUT = false;
13   IGNORE_CASE = true;
14   USER_TOKEN_MANAGER = false;
15   USER_CHAR_STREAM = false;
16   BUILD_PARSER = true;
17   BUILD_TOKEN_MANAGER = true;
18   SANITY_CHECK = true;
19   FORCE_LA_CHECK = false;
20 }
21
22 PARSER_BEGIN(PHPParser)
23 package test;
24
25 import org.eclipse.core.resources.IFile;
26 import org.eclipse.core.resources.IMarker;
27 import org.eclipse.core.runtime.CoreException;
28 import org.eclipse.ui.texteditor.MarkerUtilities;
29 import org.eclipse.jface.preference.IPreferenceStore;
30
31 import java.util.Hashtable;
32 import java.io.StringReader;
33 import java.text.MessageFormat;
34
35 import net.sourceforge.phpeclipse.actions.PHPStartApacheAction;
36 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
37 import net.sourceforge.phpdt.internal.compiler.parser.PHPOutlineInfo;
38 import net.sourceforge.phpdt.internal.compiler.parser.PHPSegmentWithChildren;
39 import net.sourceforge.phpdt.internal.compiler.parser.PHPFunctionDeclaration;
40 import net.sourceforge.phpdt.internal.compiler.parser.PHPClassDeclaration;
41 import net.sourceforge.phpdt.internal.compiler.parser.PHPVarDeclaration;
42 import net.sourceforge.phpdt.internal.compiler.parser.PHPReqIncDeclaration;
43
44 /**
45  * A new php parser.
46  * This php parser is inspired by the Java 1.2 grammar example 
47  * given with JavaCC. You can get JavaCC at http://www.webgain.com
48  * You can test the parser with the PHPParserTestCase2.java
49  * @author Matthieu Casanova
50  */
51 public class PHPParser extends PHPParserSuperclass {
52
53   private static IFile fileToParse;
54
55   /** The current segment */
56   private static PHPSegmentWithChildren currentSegment;
57
58   private static final String PARSE_ERROR_STRING = "Parse error"; //$NON-NLS-1$
59   private static final String PARSE_WARNING_STRING = "Warning"; //$NON-NLS-1$
60   PHPOutlineInfo outlineInfo;
61   private static int errorLevel = ERROR;
62   private static String errorMessage;
63
64   public PHPParser() {
65   }
66
67   public void setFileToParse(IFile fileToParse) {
68     this.fileToParse = fileToParse;
69   }
70
71   public PHPParser(IFile fileToParse) {
72     this(new StringReader(""));
73     this.fileToParse = fileToParse;
74   }
75
76   public void phpParserTester(String strEval) throws CoreException, ParseException {
77     PHPParserTokenManager.SwitchTo(PHPParserTokenManager.PHPPARSING);
78     StringReader stream = new StringReader(strEval);
79     if (jj_input_stream == null) {
80       jj_input_stream = new SimpleCharStream(stream, 1, 1);
81     }
82     ReInit(new StringReader(strEval));
83     phpTest();
84   }
85
86   public void htmlParserTester(String strEval) throws CoreException, ParseException {
87     StringReader stream = new StringReader(strEval);
88     if (jj_input_stream == null) {
89       jj_input_stream = new SimpleCharStream(stream, 1, 1);
90     }
91     ReInit(stream);
92     phpFile();
93   }
94
95   public PHPOutlineInfo parseInfo(Object parent, String s) {
96     outlineInfo = new PHPOutlineInfo(parent);
97     currentSegment = outlineInfo.getDeclarations();
98     StringReader stream = new StringReader(s);
99     if (jj_input_stream == null) {
100       jj_input_stream = new SimpleCharStream(stream, 1, 1);
101     }
102     ReInit(stream);
103     try {
104       parse();
105     } catch (ParseException e) {
106       processParseException(e);
107     }
108     return outlineInfo;
109   }
110
111   /**
112    * This method will process the parse exception.
113    * If the error message is null, the parse exception wasn't catched and a trace is written in the log
114    * @param e the ParseException
115    */
116   private static void processParseException(final ParseException e) {
117     if (errorMessage == null) {
118       PHPeclipsePlugin.log(e);
119       errorMessage = "this exception wasn't handled by the parser please tell us how to reproduce it";
120     }
121     setMarker(e);
122     errorMessage = null;
123   }
124
125   /**
126    * Create marker for the parse error
127    */
128   private static void setMarker(ParseException e) {
129     try {
130       setMarker(fileToParse,
131                 errorMessage,
132                 jj_input_stream.tokenBegin,
133                 jj_input_stream.tokenBegin + e.currentToken.image.length(),
134                 errorLevel,
135                 "Line " + e.currentToken.beginLine);
136     } catch (CoreException e2) {
137       PHPeclipsePlugin.log(e2);
138     }
139   }
140
141   /**
142    * Create markers according to the external parser output
143    */
144   private static void createMarkers(String output, IFile file) throws CoreException {
145     // delete all markers
146     file.deleteMarkers(IMarker.PROBLEM, false, 0);
147
148     int indx = 0;
149     int brIndx = 0;
150     boolean flag = true;
151     while ((brIndx = output.indexOf("<br />", indx)) != -1) {
152       // newer php error output (tested with 4.2.3)
153       scanLine(output, file, indx, brIndx);
154       indx = brIndx + 6;
155       flag = false;
156     }
157     if (flag) {
158       while ((brIndx = output.indexOf("<br>", indx)) != -1) {
159         // older php error output (tested with 4.2.3)
160         scanLine(output, file, indx, brIndx);
161         indx = brIndx + 4;
162       }
163     }
164   }
165
166   private static void scanLine(String output, IFile file, int indx, int brIndx) throws CoreException {
167     String current;
168     StringBuffer lineNumberBuffer = new StringBuffer(10);
169     char ch;
170     current = output.substring(indx, brIndx);
171
172     if (current.indexOf(PARSE_WARNING_STRING) != -1 || current.indexOf(PARSE_ERROR_STRING) != -1) {
173       int onLine = current.indexOf("on line <b>");
174       if (onLine != -1) {
175         lineNumberBuffer.delete(0, lineNumberBuffer.length());
176         for (int i = onLine; i < current.length(); i++) {
177           ch = current.charAt(i);
178           if ('0' <= ch && '9' >= ch) {
179             lineNumberBuffer.append(ch);
180           }
181         }
182
183         int lineNumber = Integer.parseInt(lineNumberBuffer.toString());
184
185         Hashtable attributes = new Hashtable();
186
187         current = current.replaceAll("\n", "");
188         current = current.replaceAll("<b>", "");
189         current = current.replaceAll("</b>", "");
190         MarkerUtilities.setMessage(attributes, current);
191
192         if (current.indexOf(PARSE_ERROR_STRING) != -1)
193           attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_ERROR));
194         else if (current.indexOf(PARSE_WARNING_STRING) != -1)
195           attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_WARNING));
196         else
197           attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_INFO));
198         MarkerUtilities.setLineNumber(attributes, lineNumber);
199         MarkerUtilities.createMarker(file, attributes, IMarker.PROBLEM);
200       }
201     }
202   }
203
204   public void parse(String s) throws CoreException {
205     StringReader stream = new StringReader(s);
206     if (jj_input_stream == null) {
207       jj_input_stream = new SimpleCharStream(stream, 1, 1);
208     }
209     ReInit(stream);
210     try {
211       parse();
212     } catch (ParseException e) {
213       processParseException(e);
214     }
215   }
216
217   /**
218    * Call the php parse command ( php -l -f &lt;filename&gt; )
219    * and create markers according to the external parser output
220    */
221   public static void phpExternalParse(IFile file) {
222     IPreferenceStore store = PHPeclipsePlugin.getDefault().getPreferenceStore();
223     String filename = file.getLocation().toString();
224
225     String[] arguments = { filename };
226     MessageFormat form = new MessageFormat(store.getString(PHPeclipsePlugin.EXTERNAL_PARSER_PREF));
227     String command = form.format(arguments);
228
229     String parserResult = PHPStartApacheAction.getParserOutput(command, "External parser: ");
230
231     try {
232       // parse the buffer to find the errors and warnings
233       createMarkers(parserResult, file);
234     } catch (CoreException e) {
235       PHPeclipsePlugin.log(e);
236     }
237   }
238
239   public void parse() throws ParseException {
240           phpFile();
241   }
242 }
243
244 PARSER_END(PHPParser)
245
246 <DEFAULT> TOKEN :
247 {
248   <PHPSTART : "<?php" | "<?"> : PHPPARSING
249 }
250
251 <PHPPARSING> TOKEN :
252 {
253   <PHPEND :"?>"> : DEFAULT
254 }
255
256 <DEFAULT> SKIP :
257 {
258  < ~[] >
259 }
260
261
262 /* WHITE SPACE */
263
264 <PHPPARSING> SKIP :
265 {
266   " "
267 | "\t"
268 | "\n"
269 | "\r"
270 | "\f"
271 }
272
273 /* COMMENTS */
274
275 <PHPPARSING> MORE :
276 {
277   "//" : IN_SINGLE_LINE_COMMENT
278 |
279   <"/**" ~["/"]> { input_stream.backup(1); } : IN_FORMAL_COMMENT
280 |
281   "/*" : IN_MULTI_LINE_COMMENT
282 }
283
284 <IN_SINGLE_LINE_COMMENT> SPECIAL_TOKEN :
285 {
286   <SINGLE_LINE_COMMENT: "\n" | "\r" | "\r\n" > : PHPPARSING
287 }
288
289 <IN_SINGLE_LINE_COMMENT> TOKEN :
290 {
291   <SINGLE_LINE_COMMENT_PHPEND : "?>" > : DEFAULT
292 }
293
294 <IN_FORMAL_COMMENT>
295 SPECIAL_TOKEN :
296 {
297   <FORMAL_COMMENT: "*/" > : PHPPARSING
298 }
299
300 <IN_MULTI_LINE_COMMENT>
301 SPECIAL_TOKEN :
302 {
303   <MULTI_LINE_COMMENT: "*/" > : PHPPARSING
304 }
305
306 <IN_SINGLE_LINE_COMMENT,IN_FORMAL_COMMENT,IN_MULTI_LINE_COMMENT>
307 MORE :
308 {
309   < ~[] >
310 }
311
312 /* KEYWORDS */
313 <PHPPARSING> TOKEN :
314 {
315   <CLASS    : "class">
316 | <FUNCTION : "function">
317 | <VAR      : "var">
318 | <IF       : "if">
319 | <ELSEIF   : "elseif">
320 | <ELSE     : "else">
321 | <ARRAY    : "array">
322 }
323
324 /* LANGUAGE CONSTRUCT */
325 <PHPPARSING> TOKEN :
326 {
327   <PRINT : "print">
328 | <ECHO : "echo">
329 | <INCLUDE : "include">
330 | <REQUIRE : "require">
331 | <INCLUDE_ONCE : "include_once">
332 | <REQUIRE_ONCE : "require_once">
333 | <GLOBAL : "global">
334 | <STATIC : "static">
335 | <CLASSACCESS: "->">
336 | <STATICCLASSACCESS: "::">
337 | <ARRAYASSIGN: "=>">
338 }
339
340 /* RESERVED WORDS AND LITERALS */
341
342 <PHPPARSING> TOKEN :
343 {
344   < BREAK: "break" >
345 | < CASE: "case" >
346 | < CONST: "const" >
347 | < CONTINUE: "continue" >
348 | < _DEFAULT: "default" >
349 | < DO: "do" >
350 | < EXTENDS: "extends" >
351 | < FALSE: "false" >
352 | < FOR: "for" >
353 | < GOTO: "goto" >
354 | < NEW: "new" >
355 | < NULL: "null" >
356 | < RETURN: "return" >
357 | < SUPER: "super" >
358 | < SWITCH: "switch" >
359 | < THIS: "this" >
360 | < TRUE: "true" >
361 | < WHILE: "while" >
362 | < ENDWHILE : "endwhile" >
363 | <ENDIF : "endif" >
364 | <ENDFOR : "endfor" >
365 | <FOREACH : "foreach" >
366 | <AS : "as" >
367 }
368
369 /* TYPES */
370
371 <PHPPARSING> TOKEN :
372 {
373   <STRING : "string">
374 | <OBJECT : "object">
375 | <BOOL : "bool">
376 | <BOOLEAN : "boolean">
377 | <REAL : "real">
378 | <DOUBLE : "double">
379 | <FLOAT : "float">
380 | <INT : "int">
381 | <INTEGER : "integer">
382 }
383
384 <PHPPARSING> TOKEN :
385 {
386   < _ORL : "OR" >
387 | < _ANDL: "AND">
388 }
389
390 /* LITERALS */
391
392 <PHPPARSING> TOKEN :
393 {
394   < INTEGER_LITERAL:
395         <DECIMAL_LITERAL> (["l","L"])?
396       | <HEX_LITERAL> (["l","L"])?
397       | <OCTAL_LITERAL> (["l","L"])?
398   >
399 |
400   < #DECIMAL_LITERAL: ["1"-"9"] (["0"-"9"])* >
401 |
402   < #HEX_LITERAL: "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+ >
403 |
404   < #OCTAL_LITERAL: "0" (["0"-"7"])* >
405 |
406   < FLOATING_POINT_LITERAL:
407         (["0"-"9"])+ "." (["0"-"9"])* (<EXPONENT>)? (["f","F","d","D"])?
408       | "." (["0"-"9"])+ (<EXPONENT>)? (["f","F","d","D"])?
409       | (["0"-"9"])+ <EXPONENT> (["f","F","d","D"])?
410       | (["0"-"9"])+ (<EXPONENT>)? ["f","F","d","D"]
411   >
412 |
413   < #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ >
414 |
415   < STRING_LITERAL: (<STRING_1> | <STRING_2> | <STRING_3>)>
416 |    < STRING_1:
417       "\""
418       (
419         ~["\""]
420         |
421         "\\\""
422       )*
423       "\""
424     >
425 |    < STRING_2:
426       "'"
427       (
428       ~["'"]
429        |
430        "\\'"
431       )*
432
433       "'"
434     >
435 |   < STRING_3:
436       "`"
437       (
438         ~["`"]
439       |
440         "\\`"
441       )*
442       "`"
443     >
444 }
445
446 /* IDENTIFIERS */
447
448 <PHPPARSING> TOKEN :
449 {
450   < IDENTIFIER: (<LETTER>|<SPECIAL>) (<LETTER>|<DIGIT>|<SPECIAL>)* >
451 |
452   < #LETTER:
453       ["a"-"z"] | ["A"-"Z"]
454   >
455 |
456   < #DIGIT:
457       ["0"-"9"]
458   >
459 |
460   < #SPECIAL:
461     "_" | ["\u007f"-"\u00ff"]
462   >
463 }
464
465 /* SEPARATORS */
466
467 <PHPPARSING> TOKEN :
468 {
469   < LPAREN: "(" >
470 | < RPAREN: ")" >
471 | < LBRACE: "{" >
472 | < RBRACE: "}" >
473 | < LBRACKET: "[" >
474 | < RBRACKET: "]" >
475 | < SEMICOLON: ";" >
476 | < COMMA: "," >
477 | < DOT: "." >
478 }
479
480 /* OPERATORS */
481
482 <PHPPARSING> TOKEN :
483 {
484   <AT     : "@">
485 | <DOLLAR : "$">
486 | <ASSIGN: "=" >
487 | <GT: ">" >
488 | <LT: "<" >
489 | <BANG: "!" >
490 | <HOOK: "?" >
491 | <COLON: ":" >
492 | <EQ: "==" >
493 | <LE: "<=" >
494 | <GE: ">=" >
495 | <NE: "!=" >
496 | <DIF: "<>" >
497 | <SC_OR: "||" >
498 | <SC_AND: "&&" >
499 | <INCR: "++" >
500 | <DECR: "--" >
501 | <PLUS: "+" >
502 | <MINUS: "-" >
503 | <STAR: "*" >
504 | <SLASH: "/" >
505 | <BIT_AND: "&" >
506 | <BIT_OR: "|" >
507 | <XOR: "^" >
508 | <REM: "%" >
509 | <LSHIFT: "<<" >
510 | <RSIGNEDSHIFT: ">>" >
511 | <RUNSIGNEDSHIFT: ">>>" >
512 | <PLUSASSIGN: "+=" >
513 | <MINUSASSIGN: "-=" >
514 | <STARASSIGN: "*=" >
515 | <SLASHASSIGN: "/=" >
516 | <ANDASSIGN: "&=" >
517 | <ORASSIGN: "|=" >
518 | <XORASSIGN: "^=" >
519 | <DOTASSIGN: ".=" >
520 | <REMASSIGN: "%=" >
521 | <LSHIFTASSIGN: "<<=" >
522 | <RSIGNEDSHIFTASSIGN: ">>=" >
523 | <BANGDOUBLEEQUAL: "!==" >
524 | <TRIPLEEQUAL: "===" >
525 | <TILDEEQUAL: "~=" >
526 }
527
528 <PHPPARSING> TOKEN :
529 {
530   < DOLLAR_ID: <DOLLAR> <IDENTIFIER>  >
531 }
532
533 /*****************************************
534  * THE JAVA LANGUAGE GRAMMAR STARTS HERE *
535  *****************************************/
536
537 /*
538  * Program structuring syntax follows.
539  */
540
541 void phpTest() :
542 {}
543 {
544   Php()
545   <EOF>
546 }
547
548 void phpFile() :
549 {}
550 {
551   try {
552   (<PHPSTART> Php() <PHPEND>)*
553   <EOF>
554   } catch (TokenMgrError e) {
555     errorMessage = e.getMessage();
556     errorLevel   = ERROR;
557     throw generateParseException();
558   }
559 }
560
561 void Php() :
562 {}
563 {
564   (BlockStatement())*
565 }
566
567 void ClassDeclaration() :
568 {
569   PHPClassDeclaration classDeclaration;
570   Token className;
571   int pos = jj_input_stream.bufpos;
572 }
573 {
574   <CLASS> className = <IDENTIFIER> [ <EXTENDS> <IDENTIFIER> ]
575   {
576     if (currentSegment != null) {
577       classDeclaration = new PHPClassDeclaration(currentSegment,className.image,pos);
578       currentSegment.add(classDeclaration);
579       currentSegment = classDeclaration;
580     }
581   }
582   ClassBody()
583   {
584     if (currentSegment != null) {
585       currentSegment = (PHPSegmentWithChildren) currentSegment.getParent();
586     }
587   }
588 }
589
590 void ClassBody() :
591 {}
592 {
593   try {
594     <LBRACE>
595   } catch (ParseException e) {
596     errorMessage = "'{' expected";
597     errorLevel   = ERROR;
598     throw e;
599   }
600   ( ClassBodyDeclaration() )*
601   try {
602     <RBRACE>
603   } catch (ParseException e) {
604     errorMessage = "'var', 'function' or '}' expected";
605     errorLevel   = ERROR;
606     throw e;
607   }
608 }
609
610 void ClassBodyDeclaration() :
611 {}
612 {
613   MethodDeclaration()
614 |
615   FieldDeclaration()
616 }
617
618 void FieldDeclaration() :
619 {
620   PHPVarDeclaration variableDeclaration;
621 }
622 {
623   <VAR> variableDeclaration = VariableDeclarator()
624   {
625     if (currentSegment != null) {
626       currentSegment.add(variableDeclaration);
627     }
628   }
629   ( <COMMA>
630       variableDeclaration = VariableDeclarator()
631       {
632       if (currentSegment != null) {
633         currentSegment.add(variableDeclaration);
634       }
635       }
636   )*
637   try {
638     <SEMICOLON>
639   } catch (ParseException e) {
640     errorMessage = "';' expected after variable declaration";
641     errorLevel   = ERROR;
642     throw e;
643   }
644 }
645
646 PHPVarDeclaration VariableDeclarator() :
647 {
648   String varName;
649   String varValue = null;
650   int pos = jj_input_stream.bufpos;
651 }
652 {
653   varName = VariableDeclaratorId()
654   [
655     <ASSIGN>
656     try {
657       varValue = VariableInitializer()
658     } catch (ParseException e) {
659       errorMessage = "Literal expression expected in variable initializer";
660       errorLevel   = ERROR;
661       throw e;
662     }
663   ]
664   {
665     if (varValue == null) {
666       return new PHPVarDeclaration(currentSegment,varName,pos);
667     }
668     return new PHPVarDeclaration(currentSegment,varName,pos,varValue);
669   }
670 }
671
672 String VariableDeclaratorId() :
673 {
674   String expr;
675   StringBuffer buff = new StringBuffer();
676 }
677 {
678   try {
679     expr = Variable()
680     {buff.append(expr);}
681     ( LOOKAHEAD(2) expr = VariableSuffix()
682     {buff.append(expr);}
683     )*
684     {return buff.toString();}
685   } catch (ParseException e) {
686     errorMessage = "'$' expected for variable identifier";
687     errorLevel   = ERROR;
688     throw e;
689   }
690 }
691
692 String Variable():
693 {
694   String expr = null;
695   Token token;
696 }
697 {
698   token = <DOLLAR_ID> [<LBRACE> expr = Expression() <RBRACE>]
699   {
700     if (expr == null) {
701       return token.image;
702     }
703     return token + "{" + expr + "}";
704   }
705 |
706   <DOLLAR> expr = VariableName()
707   {return "$" + expr;}
708 }
709
710 String VariableName():
711 {
712 String expr = null;
713 Token token;
714 }
715 {
716   <LBRACE> expr = Expression() <RBRACE>
717   {return "{"+expr+"}";}
718 |
719   token = <IDENTIFIER> [<LBRACE> expr = Expression() <RBRACE>]
720   {
721     if (expr == null) {
722       return token.image;
723     }
724     return token + "{" + expr + "}";
725   }
726 |
727   <DOLLAR> expr = VariableName()
728   {return "$" + expr;}
729 |
730   token = <DOLLAR_ID> [expr = VariableName()]
731   {
732   if (expr == null) {
733     return token.image;
734   }
735   return token.image + expr;
736   }
737 }
738
739 String VariableInitializer() :
740 {
741   String expr;
742   Token token;
743 }
744 {
745   expr = Literal()
746   {return expr;}
747 |
748   <MINUS> (token = <INTEGER_LITERAL> | token = <FLOATING_POINT_LITERAL>)
749   {return "-" + token.image;}
750 |
751   <PLUS> (token = <INTEGER_LITERAL> | token = <FLOATING_POINT_LITERAL>)
752   {return "+" + token.image;}
753 |
754   expr = ArrayDeclarator()
755   {return expr;}
756 |
757   token = <IDENTIFIER>
758   {return token.image;}
759 }
760
761 String ArrayVariable() :
762 {
763 String expr;
764 StringBuffer buff = new StringBuffer();
765 }
766 {
767   expr = Expression()
768   {buff.append(expr);}
769    [<ARRAYASSIGN> expr = Expression()
770    {buff.append("=>").append(expr);}]
771   {return buff.toString();}
772 }
773
774 String ArrayInitializer() :
775 {
776 String expr = null;
777 StringBuffer buff = new StringBuffer("(");
778 }
779 {
780   <LPAREN> [ expr = ArrayVariable()
781             {buff.append(expr);}
782             ( LOOKAHEAD(2) <COMMA> expr = ArrayVariable()
783             {buff.append(",").append(expr);}
784             )* ]
785   <RPAREN>
786   {
787     buff.append(")");
788     return buff.toString();
789   }
790 }
791
792 void MethodDeclaration() :
793 {
794   PHPFunctionDeclaration functionDeclaration;
795 }
796 {
797   <FUNCTION> functionDeclaration = MethodDeclarator()
798   {
799     if (currentSegment != null) {
800       currentSegment.add(functionDeclaration);
801       currentSegment = functionDeclaration;
802     }
803   }
804   Block()
805   {
806     if (currentSegment != null) {
807       currentSegment = (PHPSegmentWithChildren) currentSegment.getParent();
808     }
809   }
810 }
811
812 PHPFunctionDeclaration MethodDeclarator() :
813 {
814   Token identifier;
815   StringBuffer methodDeclaration = new StringBuffer();
816   String formalParameters;
817   int pos = jj_input_stream.bufpos;
818 }
819 {
820   [ <BIT_AND> {methodDeclaration.append("&");} ]
821   identifier = <IDENTIFIER>
822   {methodDeclaration.append(identifier);}
823     formalParameters = FormalParameters()
824   {
825     methodDeclaration.append(formalParameters);
826     return new PHPFunctionDeclaration(currentSegment,methodDeclaration.toString(),pos);
827   }
828 }
829
830 String FormalParameters() :
831 {
832   String expr;
833   final StringBuffer buff = new StringBuffer("(");
834 }
835 {
836   try {
837   <LPAREN>
838   } catch (ParseException e) {
839     errorMessage = "Formal parameter expected after function identifier";
840     errorLevel   = ERROR;
841     jj_consume_token(token.kind);
842   }
843             [ expr = FormalParameter()
844               {buff.append(expr);}
845             (
846                 <COMMA> expr = FormalParameter()
847                 {buff.append(",").append(expr);}
848             )*
849             ]
850   try {
851     <RPAREN>
852   } catch (ParseException e) {
853     errorMessage = "')' expected";
854     errorLevel   = ERROR;
855     throw e;
856   }
857  {
858   buff.append(")");
859   return buff.toString();
860  }
861 }
862
863 String FormalParameter() :
864 {
865   PHPVarDeclaration variableDeclaration;
866   StringBuffer buff = new StringBuffer();
867 }
868 {
869   [<BIT_AND> {buff.append("&");}] variableDeclaration = VariableDeclarator()
870   {
871     buff.append(variableDeclaration.toString());
872     return buff.toString();
873   }
874 }
875
876 String Type() :
877 {}
878 {
879   <STRING>
880   {return "string";}
881 |
882   <BOOL>
883   {return "bool";}
884 |
885   <BOOLEAN>
886   {return "boolean";}
887 |
888   <REAL>
889   {return "real";}
890 |
891   <DOUBLE>
892   {return "double";}
893 |
894   <FLOAT>
895   {return "float";}
896 |
897   <INT>
898   {return "int";}
899 |
900   <INTEGER>
901   {return "integer";}
902 |
903   <OBJECT>
904   {return "object";}
905 }
906
907 String Expression() :
908 {
909   String expr;
910   String assignOperator = null;
911   String expr2 = null;
912 }
913 {
914   expr = PrintExpression()
915   {return expr;}
916 |
917   expr = ConditionalExpression()
918   [
919     assignOperator = AssignmentOperator()
920     try {
921       expr2 = Expression()
922     } catch (ParseException e) {
923       errorMessage = "expression expected";
924       errorLevel   = ERROR;
925       throw e;
926     }
927   ]
928   {
929     if (expr2 == null) {
930       return expr;
931     } else {
932       return expr + assignOperator + expr2;
933     }
934   }
935 }
936
937 String AssignmentOperator() :
938 {}
939 {
940   <ASSIGN>
941 {return "=";}
942 | <STARASSIGN>
943 {return "*=";}
944 | <SLASHASSIGN>
945 {return "/=";}
946 | <REMASSIGN>
947 {return "%=";}
948 | <PLUSASSIGN>
949 {return "+=";}
950 | <MINUSASSIGN>
951 {return "-=";}
952 | <LSHIFTASSIGN>
953 {return "<<=";}
954 | <RSIGNEDSHIFTASSIGN>
955 {return ">>=";}
956 | <ANDASSIGN>
957 {return "&=";}
958 | <XORASSIGN>
959 {return "|=";}
960 | <ORASSIGN>
961 {return "|=";}
962 | <DOTASSIGN>
963 {return ".=";}
964 | <TILDEEQUAL>
965 {return "~=";}
966 }
967
968 String ConditionalExpression() :
969 {
970   String expr;
971   String expr2 = null;
972   String expr3 = null;
973 }
974 {
975   expr = ConditionalOrExpression() [ <HOOK> expr2 = Expression() <COLON> expr3 = ConditionalExpression() ]
976 {
977   if (expr3 == null) {
978     return expr;
979   } else {
980     return expr + "?" + expr2 + ":" + expr3;
981   }
982 }
983 }
984
985 String ConditionalOrExpression() :
986 {
987   String expr;
988   Token operator;
989   String expr2 = null;
990   StringBuffer buff = new StringBuffer();
991 }
992 {
993   expr = ConditionalAndExpression()
994   {
995     buff.append(expr);
996   }
997   (
998     (operator = <SC_OR> | operator = <_ORL>) expr2 = ConditionalAndExpression()
999     {
1000       buff.append(operator.image);
1001       buff.append(expr2);
1002     }
1003   )*
1004   {
1005     return buff.toString();
1006   }
1007 }
1008
1009 String ConditionalAndExpression() :
1010 {
1011   String expr;
1012   Token operator;
1013   String expr2 = null;
1014   StringBuffer buff = new StringBuffer();
1015 }
1016 {
1017   expr = ConcatExpression()
1018   {
1019     buff.append(expr);
1020   }
1021   (
1022   (operator = <SC_AND> | operator = <_ANDL>) expr2 = ConcatExpression()
1023     {
1024       buff.append(operator.image);
1025       buff.append(expr2);
1026     }
1027   )*
1028   {
1029     return buff.toString();
1030   }
1031 }
1032
1033 String ConcatExpression() :
1034 {
1035   String expr;
1036   String expr2 = null;
1037   StringBuffer buff = new StringBuffer();
1038 }
1039 {
1040   expr = InclusiveOrExpression()
1041   {
1042     buff.append(expr);
1043   }
1044   (
1045   <DOT> expr2 = InclusiveOrExpression()
1046   {
1047     buff.append(".");
1048     buff.append(expr2);
1049   }
1050   )*
1051   {
1052     return buff.toString();
1053   }
1054 }
1055
1056 String InclusiveOrExpression() :
1057 {
1058   String expr;
1059   String expr2 = null;
1060   StringBuffer buff = new StringBuffer();
1061 }
1062 {
1063   expr = ExclusiveOrExpression()
1064   {
1065     buff.append(expr);
1066   }
1067   (
1068   <BIT_OR> expr2 = ExclusiveOrExpression()
1069   {
1070     buff.append("|");
1071     buff.append(expr2);
1072   }
1073   )*
1074   {
1075     return buff.toString();
1076   }
1077 }
1078
1079 String ExclusiveOrExpression() :
1080 {
1081   String expr;
1082   String expr2 = null;
1083   StringBuffer buff = new StringBuffer();
1084 }
1085 {
1086   expr = AndExpression()
1087   {
1088     buff.append(expr);
1089   }
1090   (
1091     <XOR> expr2 = AndExpression()
1092   {
1093     buff.append("^");
1094     buff.append(expr2);
1095   }
1096   )*
1097   {
1098     return buff.toString();
1099   }
1100 }
1101
1102 String AndExpression() :
1103 {
1104   String expr;
1105   String expr2 = null;
1106   StringBuffer buff = new StringBuffer();
1107 }
1108 {
1109   expr = EqualityExpression()
1110   {
1111     buff.append(expr);
1112   }
1113   (
1114     <BIT_AND> expr2 = EqualityExpression()
1115   {
1116     buff.append("&");
1117     buff.append(expr2);
1118   }
1119   )*
1120   {
1121     return buff.toString();
1122   }
1123 }
1124
1125 String EqualityExpression() :
1126 {
1127   String expr;
1128   Token operator;
1129   String expr2;
1130   StringBuffer buff = new StringBuffer();
1131 }
1132 {
1133   expr = RelationalExpression()
1134   {buff.append(expr);}
1135   (
1136   (   operator = <EQ>
1137     | operator = <DIF>
1138     | operator = <NE>
1139     | operator = <BANGDOUBLEEQUAL>
1140     | operator = <TRIPLEEQUAL>
1141   )
1142   expr2 = RelationalExpression()
1143   {
1144     buff.append(operator.image);
1145     buff.append(expr2);
1146   }
1147   )*
1148   {return buff.toString();}
1149 }
1150
1151 String RelationalExpression() :
1152 {
1153   String expr;
1154   Token operator;
1155   String expr2;
1156   StringBuffer buff = new StringBuffer();
1157 }
1158 {
1159   expr = ShiftExpression()
1160   {buff.append(expr);}
1161   (
1162   ( operator = <LT> | operator = <GT> | operator = <LE> | operator = <GE> ) expr2 = ShiftExpression()
1163   {
1164     buff.append(operator.image);
1165     buff.append(expr2);
1166   }
1167   )*
1168   {return buff.toString();}
1169 }
1170
1171 String ShiftExpression() :
1172 {
1173   String expr;
1174   Token operator;
1175   String expr2;
1176   StringBuffer buff = new StringBuffer();
1177 }
1178 {
1179   expr = AdditiveExpression()
1180   {buff.append(expr);}
1181   (
1182   (operator = <LSHIFT> | operator = <RSIGNEDSHIFT> | operator = <RUNSIGNEDSHIFT> ) expr2 = AdditiveExpression()
1183   {
1184     buff.append(operator.image);
1185     buff.append(expr2);
1186   }
1187   )*
1188   {return buff.toString();}
1189 }
1190
1191 String AdditiveExpression() :
1192 {
1193   String expr;
1194   Token operator;
1195   String expr2;
1196   StringBuffer buff = new StringBuffer();
1197 }
1198 {
1199   expr = MultiplicativeExpression()
1200   {buff.append(expr);}
1201   (
1202    ( operator = <PLUS> | operator = <MINUS> ) expr2 = MultiplicativeExpression()
1203   {
1204     buff.append(operator.image);
1205     buff.append(expr2);
1206   }
1207    )*
1208   {return buff.toString();}
1209 }
1210
1211 String MultiplicativeExpression() :
1212 {
1213   String expr, expr2;
1214   Token operator;
1215   final StringBuffer buff = new StringBuffer();}
1216 {
1217   expr = UnaryExpression()
1218   {buff.append(expr);}
1219   (
1220   ( operator = <STAR> | operator = <SLASH> | operator = <REM> ) expr2 = UnaryExpression()
1221   {
1222     buff.append(operator.image);
1223     buff.append(expr2);
1224   }
1225   )*
1226   {return buff.toString();}
1227 }
1228
1229 /**
1230  * An unary expression starting with @, & or nothing
1231  */
1232 String UnaryExpression() :
1233 {
1234   String expr;
1235   Token token;
1236   final StringBuffer buff = new StringBuffer();
1237 }
1238 {
1239   token = <BIT_AND> expr = UnaryExpressionNoPrefix()
1240   {
1241     if (token == null) {
1242       return expr;
1243     }
1244     return token.image + expr;
1245   }
1246 |
1247   (<AT> {buff.append("@");})* expr = UnaryExpressionNoPrefix()
1248   {return buff.append(expr).toString();}
1249 }
1250
1251 String UnaryExpressionNoPrefix() :
1252 {
1253   String expr;
1254   Token token;
1255 }
1256 {
1257   ( token = <PLUS> | token = <MINUS> ) expr = UnaryExpression()
1258   {
1259     return token.image + expr;
1260   }
1261 |
1262   expr = PreIncrementExpression()
1263   {return expr;}
1264 |
1265   expr = PreDecrementExpression()
1266   {return expr;}
1267 |
1268   expr = UnaryExpressionNotPlusMinus()
1269   {return expr;}
1270 }
1271
1272
1273 String PreIncrementExpression() :
1274 {
1275 String expr;
1276 }
1277 {
1278   <INCR> expr = PrimaryExpression()
1279   {return "++"+expr;}
1280 }
1281
1282 String PreDecrementExpression() :
1283 {
1284 String expr;
1285 }
1286 {
1287   <DECR> expr = PrimaryExpression()
1288   {return "--"+expr;}
1289 }
1290
1291 String UnaryExpressionNotPlusMinus() :
1292 {
1293   String expr;
1294 }
1295 {
1296   <BANG> expr = UnaryExpression()
1297   {return "!" + expr;}
1298 |
1299   LOOKAHEAD( <LPAREN> Type() <RPAREN> )
1300   expr = CastExpression()
1301   {return expr;}
1302 |
1303   expr = PostfixExpression()
1304   {return expr;}
1305 |
1306   expr = Literal()
1307   {return expr;}
1308 |
1309   <LPAREN> expr = Expression()<RPAREN>
1310   {return "("+expr+")";}
1311 }
1312
1313 String CastExpression() :
1314 {
1315 String type;
1316 String expr;
1317 }
1318 {
1319   <LPAREN> type = Type() <RPAREN> expr = UnaryExpression()
1320   {return "(" + type + ")" + expr;}
1321 }
1322
1323 String PostfixExpression() :
1324 {
1325   String expr;
1326   Token operator = null;
1327 }
1328 {
1329   expr = PrimaryExpression() [ operator = <INCR> | operator = <DECR> ]
1330   {
1331     if (operator == null) {
1332       return expr;
1333     }
1334     return expr + operator.image;
1335   }
1336 }
1337
1338 String PrimaryExpression() :
1339 {
1340   Token identifier;
1341   String expr;
1342   final StringBuffer buff = new StringBuffer();
1343 }
1344 {
1345   LOOKAHEAD(2)
1346   identifier = <IDENTIFIER> <STATICCLASSACCESS> expr = ClassIdentifier()
1347   {buff.append(identifier.image).append("::").append(expr);}
1348   (
1349   expr = PrimarySuffix()
1350   {buff.append(expr);}
1351   )*
1352   {return buff.toString();}
1353 |
1354   expr = PrimaryPrefix()  {buff.append(expr);}
1355   ( expr = PrimarySuffix()  {buff.append(expr);} )*
1356   {return buff.toString();}
1357 |
1358   expr = ArrayDeclarator()
1359   {return "array" + expr;}
1360 }
1361
1362 String ArrayDeclarator() :
1363 {
1364   String expr;
1365 }
1366 {
1367   <ARRAY> expr = ArrayInitializer()
1368   {return "array" + expr;}
1369 }
1370
1371 String PrimaryPrefix() :
1372 {
1373   String expr;
1374   Token token = null;
1375 }
1376 {
1377   token = <IDENTIFIER>
1378   {return token.image;}
1379 |
1380   <NEW> expr = ClassIdentifier()
1381   {
1382     return "new " + expr;
1383   }
1384 |  
1385   expr = VariableDeclaratorId()
1386   {return expr;}
1387 }
1388
1389 String ClassIdentifier():
1390 {
1391   String expr;
1392   Token token;
1393 }
1394 {
1395   token = <IDENTIFIER>
1396   {return token.image;}
1397 |
1398   expr = VariableDeclaratorId()
1399   {return expr;}
1400 }
1401
1402 String PrimarySuffix() :
1403 {
1404   String expr;
1405 }
1406 {
1407   expr = Arguments()
1408   {return expr;}
1409 |
1410   expr = VariableSuffix()
1411   {return expr;}
1412 }
1413
1414 String VariableSuffix() :
1415 {
1416   String expr = null;
1417 }
1418 {
1419   <CLASSACCESS> expr = VariableName()
1420   {return "->" + expr;}
1421
1422   <LBRACKET> [ expr = Expression() ]
1423   try {
1424     <RBRACKET>
1425   } catch (ParseException e) {
1426     errorMessage = "']' expected";
1427     errorLevel   = ERROR;
1428     throw e;
1429   }
1430   {
1431     if(expr == null) {
1432       return "[]";
1433     }
1434     return "[" + expr + "]";
1435   }
1436 }
1437
1438 String Literal() :
1439 {
1440   String expr;
1441   Token token;
1442 }
1443 {
1444   token = <INTEGER_LITERAL>
1445   {return token.image;}
1446 |
1447   token = <FLOATING_POINT_LITERAL>
1448   {return token.image;}
1449 |
1450   token = <STRING_LITERAL>
1451   {return token.image;}
1452 |
1453   expr = BooleanLiteral()
1454   {return expr;}
1455 |
1456   expr = NullLiteral()
1457   {return expr;}
1458 }
1459
1460 String BooleanLiteral() :
1461 {}
1462 {
1463   <TRUE>
1464   {return "true";}
1465 |
1466   <FALSE>
1467   {return "false";}
1468 }
1469
1470 String NullLiteral() :
1471 {}
1472 {
1473   <NULL>
1474   {return "null";}
1475 }
1476
1477 String Arguments() :
1478 {
1479 String expr = null;
1480 }
1481 {
1482   <LPAREN> [ expr = ArgumentList() ]
1483   try {
1484     <RPAREN>
1485   } catch (ParseException e) {
1486     errorMessage = "')' expected to close the argument list";
1487     errorLevel   = ERROR;
1488     throw e;
1489   }
1490   {
1491   if (expr == null) {
1492     return "()";
1493   }
1494   return "(" + expr + ")";
1495   }
1496 }
1497
1498 String ArgumentList() :
1499 {
1500 String expr;
1501 StringBuffer buff = new StringBuffer();
1502 }
1503 {
1504   expr = Expression()
1505   {buff.append(expr);}
1506   ( <COMMA>
1507       try {
1508         expr = Expression()
1509       } catch (ParseException e) {
1510         errorMessage = "expression expected after a comma in argument list";
1511         errorLevel   = ERROR;
1512         throw e;
1513       }
1514     {
1515       buff.append(",").append("expr");
1516     }
1517    )*
1518    {return buff.toString();}
1519 }
1520
1521 /*
1522  * Statement syntax follows.
1523  */
1524
1525 void Statement() :
1526 {}
1527 {
1528   LOOKAHEAD(2)
1529   Expression()
1530   try {
1531     (<SEMICOLON> | "?>")
1532   } catch (ParseException e) {
1533     errorMessage = "';' expected";
1534     errorLevel   = ERROR;
1535     throw e;
1536   }
1537 |
1538   LOOKAHEAD(2)
1539   LabeledStatement()
1540 |
1541   Block()
1542 |
1543   EmptyStatement()
1544 |
1545   StatementExpression()
1546   try {
1547     <SEMICOLON>
1548   } catch (ParseException e) {
1549     errorMessage = "';' expected after expression";
1550     errorLevel   = ERROR;
1551     throw e;
1552   }
1553 |
1554   SwitchStatement()
1555 |
1556   IfStatement()
1557 |
1558   WhileStatement()
1559 |
1560   DoStatement()
1561 |
1562   ForStatement()
1563 |
1564   ForeachStatement()
1565 |
1566   BreakStatement()
1567 |
1568   ContinueStatement()
1569 |
1570   ReturnStatement()
1571 |
1572   EchoStatement()
1573 |
1574   [<AT>] IncludeStatement()
1575 |
1576   StaticStatement()
1577 |
1578   GlobalStatement()
1579 }
1580
1581 void IncludeStatement() :
1582 {
1583   String expr;
1584   int pos = jj_input_stream.bufpos;
1585 }
1586 {
1587   <REQUIRE>
1588   expr = Expression()
1589   {
1590     if (currentSegment != null) {
1591       currentSegment.add(new PHPReqIncDeclaration(currentSegment, "require",pos,expr));
1592     }
1593   }
1594   try {
1595     (<SEMICOLON> | "?>")
1596   } catch (ParseException e) {
1597     errorMessage = "';' expected";
1598     errorLevel   = ERROR;
1599     throw e;
1600   }
1601 |
1602   <REQUIRE_ONCE>
1603   expr = Expression()
1604   {
1605     if (currentSegment != null) {
1606       currentSegment.add(new PHPReqIncDeclaration(currentSegment, "require_once",pos,expr));
1607     }
1608   }
1609   try {
1610     (<SEMICOLON> | "?>")
1611   } catch (ParseException e) {
1612     errorMessage = "';' expected";
1613     errorLevel   = ERROR;
1614     throw e;
1615   }
1616 |
1617   <INCLUDE>
1618   expr = Expression()
1619   {
1620     if (currentSegment != null) {
1621       currentSegment.add(new PHPReqIncDeclaration(currentSegment, "include",pos,expr));
1622     }
1623   }
1624   try {
1625     (<SEMICOLON> | "?>")
1626   } catch (ParseException e) {
1627     errorMessage = "';' expected";
1628     errorLevel   = ERROR;
1629     throw e;
1630   }
1631 |
1632   <INCLUDE_ONCE>
1633   expr = Expression()
1634   {
1635     if (currentSegment != null) {
1636       currentSegment.add(new PHPReqIncDeclaration(currentSegment, "include_once",pos,expr));
1637     }
1638   }
1639   try {
1640     (<SEMICOLON> | "?>")
1641   } catch (ParseException e) {
1642     errorMessage = "';' expected";
1643     errorLevel   = ERROR;
1644     throw e;
1645   }
1646 }
1647
1648 String PrintExpression() :
1649 {
1650   StringBuffer buff = new StringBuffer("print ");
1651   String expr;
1652 }
1653 {
1654   <PRINT> expr = Expression()
1655   {
1656     buff.append(expr);
1657     return buff.toString();
1658   }
1659 }
1660
1661 void EchoStatement() :
1662 {}
1663 {
1664   <ECHO> Expression() (<COMMA> Expression())*
1665   try {
1666     (<SEMICOLON> | "?>")
1667   } catch (ParseException e) {
1668     errorMessage = "';' expected after 'echo' statement";
1669     errorLevel   = ERROR;
1670     throw e;
1671   }
1672 }
1673
1674 void GlobalStatement() :
1675 {}
1676 {
1677   <GLOBAL> VariableDeclaratorId() (<COMMA> VariableDeclaratorId())*
1678   try {
1679     (<SEMICOLON> | "?>")
1680   } catch (ParseException e) {
1681     errorMessage = "';' expected";
1682     errorLevel   = ERROR;
1683     throw e;
1684   }
1685 }
1686
1687 void StaticStatement() :
1688 {}
1689 {
1690   <STATIC> VariableDeclarator() (<COMMA> VariableDeclarator())*
1691   try {
1692     (<SEMICOLON> | "?>")
1693   } catch (ParseException e) {
1694     errorMessage = "';' expected";
1695     errorLevel   = ERROR;
1696     throw e;
1697   }
1698 }
1699
1700 void LabeledStatement() :
1701 {}
1702 {
1703   <IDENTIFIER> <COLON> Statement()
1704 }
1705
1706 void Block() :
1707 {}
1708 {
1709   try {
1710     <LBRACE>
1711   } catch (ParseException e) {
1712     errorMessage = "'{' expected";
1713     errorLevel   = ERROR;
1714     throw e;
1715   }
1716   ( BlockStatement() )*
1717   <RBRACE>
1718 }
1719
1720 void BlockStatement() :
1721 {}
1722 {
1723   Statement()
1724 |
1725   ClassDeclaration()
1726 |
1727   MethodDeclaration()
1728 }
1729
1730 void LocalVariableDeclaration() :
1731 {}
1732 {
1733   VariableDeclarator() ( <COMMA> VariableDeclarator() )*
1734 }
1735
1736 void EmptyStatement() :
1737 {}
1738 {
1739   <SEMICOLON>
1740 }
1741
1742 void StatementExpression() :
1743 {}
1744 {
1745   PreIncrementExpression()
1746 |
1747   PreDecrementExpression()
1748 |
1749   PrimaryExpression()
1750   [
1751    <INCR>
1752   |
1753     <DECR>
1754   |
1755     AssignmentOperator() Expression()
1756   ]
1757 }
1758
1759 void SwitchStatement() :
1760 {}
1761 {
1762   <SWITCH> <LPAREN> Expression() <RPAREN> <LBRACE>
1763     ( SwitchLabel() ( BlockStatement() )* )*
1764   <RBRACE>
1765 }
1766
1767 void SwitchLabel() :
1768 {}
1769 {
1770   <CASE> Expression() <COLON>
1771 |
1772   <_DEFAULT> <COLON>
1773 }
1774
1775 void IfStatement() :
1776 /*
1777  * The disambiguating algorithm of JavaCC automatically binds dangling
1778  * else's to the innermost if statement.  The LOOKAHEAD specification
1779  * is to tell JavaCC that we know what we are doing.
1780  */
1781 {}
1782 {
1783   <IF> Condition("if") IfStatement0()
1784 }
1785
1786 void Condition(String keyword) :
1787 {}
1788 {
1789   try {
1790     <LPAREN>
1791   } catch (ParseException e) {
1792     errorMessage = "'(' expected after " + keyword + " keyword";
1793     errorLevel   = ERROR;
1794     throw e;
1795   }
1796   Expression()
1797   try {
1798      <RPAREN>
1799   } catch (ParseException e) {
1800     errorMessage = "')' expected after " + keyword + " keyword";
1801     errorLevel   = ERROR;
1802     throw e;
1803   }
1804 }
1805
1806 void IfStatement0() :
1807 {}
1808 {
1809   <COLON> (Statement())* (ElseIfStatementColon())* [ElseStatementColon()] <ENDIF>
1810   try {
1811     <SEMICOLON>
1812   } catch (ParseException e) {
1813     errorMessage = "';' expected 'endif' keyword";
1814     errorLevel   = ERROR;
1815     throw e;
1816   }
1817 |
1818   Statement() ( LOOKAHEAD(1) ElseIfStatement() )* [ LOOKAHEAD(1) <ELSE> Statement() ]
1819 }
1820
1821 void ElseIfStatementColon() :
1822 {}
1823 {
1824   <ELSEIF> Condition("elseif") <COLON> (Statement())*
1825 }
1826
1827 void ElseStatement() :
1828 {}
1829 {
1830   <ELSE> Statement()
1831 }
1832
1833 void ElseStatementColon() :
1834 {}
1835 {
1836   <ELSE> <COLON> (Statement())*
1837 }
1838
1839 void ElseIfStatement() :
1840 {}
1841 {
1842   <ELSEIF> Condition("elseif") Statement()
1843 }
1844
1845 void WhileStatement() :
1846 {}
1847 {
1848   <WHILE> Condition("while") WhileStatement0()
1849 }
1850
1851 void WhileStatement0() :
1852 {}
1853 {
1854   <COLON> (Statement())* <ENDWHILE>
1855   try {
1856     (<SEMICOLON> | "?>")
1857   } catch (ParseException e) {
1858     errorMessage = "';' expected after 'endwhile' keyword";
1859     errorLevel   = ERROR;
1860     throw e;
1861   }
1862 |
1863   Statement()
1864 }
1865
1866 void DoStatement() :
1867 {}
1868 {
1869   <DO> Statement() <WHILE> Condition("while")
1870   try {
1871     (<SEMICOLON> | "?>")
1872   } catch (ParseException e) {
1873     errorMessage = "';' expected";
1874     errorLevel   = ERROR;
1875     throw e;
1876   }
1877 }
1878
1879 void ForeachStatement() :
1880 {}
1881 {
1882   <FOREACH> <LPAREN> Variable() <AS> Variable() [ <ARRAYASSIGN> Expression() ] <RPAREN> Statement()
1883 }
1884
1885 void ForStatement() :
1886 {}
1887 {
1888   <FOR> <LPAREN> [ ForInit() ] <SEMICOLON> [ Expression() ] <SEMICOLON> [ ForUpdate() ] <RPAREN>
1889     (
1890       Statement()
1891     |
1892       <COLON> (Statement())* <ENDFOR>
1893       try {
1894         <SEMICOLON>
1895       } catch (ParseException e) {
1896         errorMessage = "';' expected 'endfor' keyword";
1897         errorLevel   = ERROR;
1898         throw e;
1899       }
1900     )
1901 }
1902
1903 void ForInit() :
1904 {}
1905 {
1906   LOOKAHEAD(LocalVariableDeclaration())
1907   LocalVariableDeclaration()
1908 |
1909   StatementExpressionList()
1910 }
1911
1912 void StatementExpressionList() :
1913 {}
1914 {
1915   StatementExpression() ( <COMMA> StatementExpression() )*
1916 }
1917
1918 void ForUpdate() :
1919 {}
1920 {
1921   StatementExpressionList()
1922 }
1923
1924 void BreakStatement() :
1925 {}
1926 {
1927   <BREAK> [ <IDENTIFIER> ] <SEMICOLON>
1928 }
1929
1930 void ContinueStatement() :
1931 {}
1932 {
1933   <CONTINUE> [ <IDENTIFIER> ] <SEMICOLON>
1934 }
1935
1936 void ReturnStatement() :
1937 {}
1938 {
1939   <RETURN> [ Expression() ] <SEMICOLON>
1940 }