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