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