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