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