Deleted unicode handling from scanner
[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   expr = RelationalExpression()
1197   {
1198     buff.append(operator.image);
1199     buff.append(expr);
1200   }
1201   )*
1202   {return buff.toString();}
1203 }
1204
1205 String RelationalExpression() :
1206 {
1207   String expr;
1208   Token operator;
1209   final StringBuffer buff = new StringBuffer();
1210 }
1211 {
1212   expr = ShiftExpression()
1213   {buff.append(expr);}
1214   (
1215   ( operator = <LT> | operator = <GT> | operator = <LE> | operator = <GE> ) expr = ShiftExpression()
1216   {buff.append(operator.image).append(expr);}
1217   )*
1218   {return buff.toString();}
1219 }
1220
1221 String ShiftExpression() :
1222 {
1223   String expr;
1224   Token operator;
1225   final StringBuffer buff = new StringBuffer();
1226 }
1227 {
1228   expr = AdditiveExpression()
1229   {buff.append(expr);}
1230   (
1231   (operator = <LSHIFT> | operator = <RSIGNEDSHIFT> | operator = <RUNSIGNEDSHIFT> ) expr = AdditiveExpression()
1232   {
1233     buff.append(operator.image);
1234     buff.append(expr);
1235   }
1236   )*
1237   {return buff.toString();}
1238 }
1239
1240 String AdditiveExpression() :
1241 {
1242   String expr;
1243   Token operator;
1244   final StringBuffer buff = new StringBuffer();
1245 }
1246 {
1247   expr = MultiplicativeExpression()
1248   {buff.append(expr);}
1249   (
1250    ( operator = <PLUS> | operator = <MINUS> ) expr = MultiplicativeExpression()
1251   {
1252     buff.append(operator.image);
1253     buff.append(expr);
1254   }
1255    )*
1256   {return buff.toString();}
1257 }
1258
1259 String MultiplicativeExpression() :
1260 {
1261   String expr;
1262   Token operator;
1263   final StringBuffer buff = new StringBuffer();}
1264 {
1265   expr = UnaryExpression()
1266   {buff.append(expr);}
1267   (
1268   ( operator = <STAR> | operator = <SLASH> | operator = <REM> ) expr = UnaryExpression()
1269   {
1270     buff.append(operator.image);
1271     buff.append(expr);
1272   }
1273   )*
1274   {return buff.toString();}
1275 }
1276
1277 /**
1278  * An unary expression starting with @, & or nothing
1279  */
1280 String UnaryExpression() :
1281 {
1282   final String expr;
1283   final Token token;
1284   final StringBuffer buff = new StringBuffer();
1285 }
1286 {
1287   token = <BIT_AND> expr = UnaryExpressionNoPrefix()
1288   {
1289     if (token == null) {
1290       return expr;
1291     }
1292     return token.image + expr;
1293   }
1294 |
1295   (<AT> {buff.append("@");})* expr = UnaryExpressionNoPrefix()
1296   {return buff.append(expr).toString();}
1297 }
1298
1299 String UnaryExpressionNoPrefix() :
1300 {
1301   final String expr;
1302   final Token token;
1303 }
1304 {
1305   ( token = <PLUS> | token = <MINUS> ) expr = UnaryExpression()
1306   {
1307     return token.image + expr;
1308   }
1309 |
1310   expr = PreIncrementExpression()
1311   {return expr;}
1312 |
1313   expr = PreDecrementExpression()
1314   {return expr;}
1315 |
1316   expr = UnaryExpressionNotPlusMinus()
1317   {return expr;}
1318 }
1319
1320
1321 String PreIncrementExpression() :
1322 {
1323 final String expr;
1324 }
1325 {
1326   <INCR> expr = PrimaryExpression()
1327   {return "++"+expr;}
1328 }
1329
1330 String PreDecrementExpression() :
1331 {
1332 final String expr;
1333 }
1334 {
1335   <DECR> expr = PrimaryExpression()
1336   {return "--"+expr;}
1337 }
1338
1339 String UnaryExpressionNotPlusMinus() :
1340 {
1341   final String expr;
1342 }
1343 {
1344   <BANG> expr = UnaryExpression()
1345   {return "!" + expr;}
1346 |
1347   LOOKAHEAD( <LPAREN> Type() <RPAREN> )
1348   expr = CastExpression()
1349   {return expr;}
1350 |
1351   expr = PostfixExpression()
1352   {return expr;}
1353 |
1354   expr = Literal()
1355   {return expr;}
1356 |
1357   <LPAREN> expr = Expression()
1358   try {
1359     <RPAREN>
1360   } catch (ParseException e) {
1361     errorMessage = "')' expected";
1362     errorLevel   = ERROR;
1363     throw e;
1364   }
1365   {return "("+expr+")";}
1366 }
1367
1368 String CastExpression() :
1369 {
1370 final String type, expr;
1371 }
1372 {
1373   <LPAREN> type = Type() <RPAREN> expr = UnaryExpression()
1374   {return "(" + type + ")" + expr;}
1375 }
1376
1377 String PostfixExpression() :
1378 {
1379   final String expr;
1380   Token operator = null;
1381 }
1382 {
1383   expr = PrimaryExpression() [ operator = <INCR> | operator = <DECR> ]
1384   {
1385     if (operator == null) {
1386       return expr;
1387     }
1388     return expr + operator.image;
1389   }
1390 }
1391
1392 String PrimaryExpression() :
1393 {
1394   final Token identifier;
1395   String expr;
1396   final StringBuffer buff = new StringBuffer();
1397 }
1398 {
1399   LOOKAHEAD(2)
1400   identifier = <IDENTIFIER> <STATICCLASSACCESS> expr = ClassIdentifier()
1401   {buff.append(identifier.image).append("::").append(expr);}
1402   (
1403   expr = PrimarySuffix()
1404   {buff.append(expr);}
1405   )*
1406   {return buff.toString();}
1407 |
1408   expr = PrimaryPrefix()  {buff.append(expr);}
1409   ( expr = PrimarySuffix()  {buff.append(expr);} )*
1410   {return buff.toString();}
1411 |
1412   expr = ArrayDeclarator()
1413   {return "array" + expr;}
1414 }
1415
1416 String ArrayDeclarator() :
1417 {
1418   final String expr;
1419 }
1420 {
1421   <ARRAY> expr = ArrayInitializer()
1422   {return "array" + expr;}
1423 }
1424
1425 String PrimaryPrefix() :
1426 {
1427   final String expr;
1428   final Token token;
1429 }
1430 {
1431   token = <IDENTIFIER>
1432   {return token.image;}
1433 |
1434   <NEW> expr = ClassIdentifier()
1435   {
1436     return "new " + expr;
1437   }
1438 |  
1439   expr = VariableDeclaratorId()
1440   {return expr;}
1441 }
1442
1443 String ClassIdentifier():
1444 {
1445   final String expr;
1446   final Token token;
1447 }
1448 {
1449   token = <IDENTIFIER>
1450   {return token.image;}
1451 |
1452   expr = VariableDeclaratorId()
1453   {return expr;}
1454 }
1455
1456 String PrimarySuffix() :
1457 {
1458   final String expr;
1459 }
1460 {
1461   expr = Arguments()
1462   {return expr;}
1463 |
1464   expr = VariableSuffix()
1465   {return expr;}
1466 }
1467
1468 String VariableSuffix() :
1469 {
1470   String expr = null;
1471 }
1472 {
1473   <CLASSACCESS>
1474   try {
1475     expr = VariableName()
1476   } catch (ParseException e) {
1477     errorMessage = "unexpected token : '"+ e.currentToken.next.image +"', function call or field access expected";
1478     errorLevel   = ERROR;
1479     throw e;
1480   }
1481   {return "->" + expr;}
1482
1483   <LBRACKET> [ expr = Expression() ]
1484   try {
1485     <RBRACKET>
1486   } catch (ParseException e) {
1487     errorMessage = "']' expected";
1488     errorLevel   = ERROR;
1489     throw e;
1490   }
1491   {
1492     if(expr == null) {
1493       return "[]";
1494     }
1495     return "[" + expr + "]";
1496   }
1497 }
1498
1499 String Literal() :
1500 {
1501   final String expr;
1502   final Token token;
1503 }
1504 {
1505   token = <INTEGER_LITERAL>
1506   {return token.image;}
1507 |
1508   token = <FLOATING_POINT_LITERAL>
1509   {return token.image;}
1510 |
1511   token = <STRING_LITERAL>
1512   {return token.image;}
1513 |
1514   expr = BooleanLiteral()
1515   {return expr;}
1516 |
1517   expr = NullLiteral()
1518   {return expr;}
1519 }
1520
1521 String BooleanLiteral() :
1522 {}
1523 {
1524   <TRUE>
1525   {return "true";}
1526 |
1527   <FALSE>
1528   {return "false";}
1529 }
1530
1531 String NullLiteral() :
1532 {}
1533 {
1534   <NULL>
1535   {return "null";}
1536 }
1537
1538 String Arguments() :
1539 {
1540 String expr = null;
1541 }
1542 {
1543   <LPAREN> [ expr = ArgumentList() ]
1544   try {
1545     <RPAREN>
1546   } catch (ParseException e) {
1547     errorMessage = "unexpected token : '"+ e.currentToken.next.image +"', ')' expected to close the argument list";
1548     errorLevel   = ERROR;
1549     throw e;
1550   }
1551   {
1552   if (expr == null) {
1553     return "()";
1554   }
1555   return "(" + expr + ")";
1556   }
1557 }
1558
1559 String ArgumentList() :
1560 {
1561 String expr;
1562 final StringBuffer buff = new StringBuffer();
1563 }
1564 {
1565   expr = Expression()
1566   {buff.append(expr);}
1567   ( <COMMA>
1568       try {
1569         expr = Expression()
1570       } catch (ParseException e) {
1571         errorMessage = "expression expected after a comma in argument list";
1572         errorLevel   = ERROR;
1573         throw e;
1574       }
1575     {
1576       buff.append(",").append(expr);
1577     }
1578    )*
1579    {return buff.toString();}
1580 }
1581
1582 /**
1583  * A Statement without break
1584  */
1585 void StatementNoBreak() :
1586 {}
1587 {
1588   LOOKAHEAD(2)
1589   Expression()
1590   try {
1591     (<SEMICOLON> | <PHPEND>)
1592   } catch (ParseException e) {
1593     errorMessage = "';' expected";
1594     errorLevel   = ERROR;
1595     throw e;
1596   }
1597 |
1598   LOOKAHEAD(2)
1599   LabeledStatement()
1600 |
1601   Block()
1602 |
1603   EmptyStatement()
1604 |
1605   StatementExpression()
1606   try {
1607     <SEMICOLON>
1608   } catch (ParseException e) {
1609     errorMessage = "';' expected after expression";
1610     errorLevel   = ERROR;
1611     throw e;
1612   }
1613 |
1614   SwitchStatement()
1615 |
1616   IfStatement()
1617 |
1618   WhileStatement()
1619 |
1620   DoStatement()
1621 |
1622   ForStatement()
1623 |
1624   ForeachStatement()
1625 |
1626   ContinueStatement()
1627 |
1628   ReturnStatement()
1629 |
1630   EchoStatement()
1631 |
1632   [<AT>] IncludeStatement()
1633 |
1634   StaticStatement()
1635 |
1636   GlobalStatement()
1637 }
1638
1639 /**
1640  * A Normal statement
1641  */
1642 void Statement() :
1643 {}
1644 {
1645   StatementNoBreak()
1646 |
1647   BreakStatement()
1648 }
1649
1650 void IncludeStatement() :
1651 {
1652   final String expr;
1653   final int pos = jj_input_stream.bufpos;
1654 }
1655 {
1656   <REQUIRE>
1657   expr = Expression()
1658   {
1659     if (currentSegment != null) {
1660       currentSegment.add(new PHPReqIncDeclaration(currentSegment, "require",pos,expr));
1661     }
1662   }
1663   try {
1664     (<SEMICOLON> | "?>")
1665   } catch (ParseException e) {
1666     errorMessage = "';' expected";
1667     errorLevel   = ERROR;
1668     throw e;
1669   }
1670 |
1671   <REQUIRE_ONCE>
1672   expr = Expression()
1673   {
1674     if (currentSegment != null) {
1675       currentSegment.add(new PHPReqIncDeclaration(currentSegment, "require_once",pos,expr));
1676     }
1677   }
1678   try {
1679     (<SEMICOLON> | "?>")
1680   } catch (ParseException e) {
1681     errorMessage = "';' expected";
1682     errorLevel   = ERROR;
1683     throw e;
1684   }
1685 |
1686   <INCLUDE>
1687   expr = Expression()
1688   {
1689     if (currentSegment != null) {
1690       currentSegment.add(new PHPReqIncDeclaration(currentSegment, "include",pos,expr));
1691     }
1692   }
1693   try {
1694     (<SEMICOLON> | "?>")
1695   } catch (ParseException e) {
1696     errorMessage = "';' expected";
1697     errorLevel   = ERROR;
1698     throw e;
1699   }
1700 |
1701   <INCLUDE_ONCE>
1702   expr = Expression()
1703   {
1704     if (currentSegment != null) {
1705       currentSegment.add(new PHPReqIncDeclaration(currentSegment, "include_once",pos,expr));
1706     }
1707   }
1708   try {
1709     (<SEMICOLON> | "?>")
1710   } catch (ParseException e) {
1711     errorMessage = "';' expected";
1712     errorLevel   = ERROR;
1713     throw e;
1714   }
1715 }
1716
1717 String PrintExpression() :
1718 {
1719   final StringBuffer buff = new StringBuffer("print ");
1720   final String expr;
1721 }
1722 {
1723   <PRINT> expr = Expression()
1724   {
1725     buff.append(expr);
1726     return buff.toString();
1727   }
1728 }
1729
1730 String ListExpression() :
1731 {
1732   final StringBuffer buff = new StringBuffer("list(");
1733   String expr;
1734 }
1735 {
1736   <LIST>
1737   try {
1738     <LPAREN>
1739   } catch (ParseException e) {
1740     errorMessage = "unexpected token : '"+ e.currentToken.next.image +"', '(' expected";
1741     errorLevel   = ERROR;
1742     throw e;
1743   }
1744   [
1745     expr = VariableDeclaratorId()
1746     {buff.append(expr);}
1747   ]
1748   [
1749     try {
1750       <COMMA>
1751     } catch (ParseException e) {
1752       errorMessage = "unexpected token : '"+ e.currentToken.next.image +"', ',' expected";
1753       errorLevel   = ERROR;
1754       throw e;
1755     }
1756     expr = VariableDeclaratorId()
1757     {buff.append(",").append(expr);}
1758   ]
1759   {buff.append(")");}
1760   try {
1761     <RPAREN>
1762   } catch (ParseException e) {
1763     errorMessage = "unexpected token : '"+ e.currentToken.next.image +"', ')' expected";
1764     errorLevel   = ERROR;
1765     throw e;
1766   }
1767   [ <ASSIGN> expr = Expression() {buff.append("(").append(expr);}]
1768   {return buff.toString();}
1769 }
1770
1771 void EchoStatement() :
1772 {}
1773 {
1774   <ECHO> Expression() (<COMMA> Expression())*
1775   try {
1776     (<SEMICOLON> | "?>")
1777   } catch (ParseException e) {
1778     errorMessage = "';' expected after 'echo' statement";
1779     errorLevel   = ERROR;
1780     throw e;
1781   }
1782 }
1783
1784 void GlobalStatement() :
1785 {}
1786 {
1787   <GLOBAL> VariableDeclaratorId() (<COMMA> VariableDeclaratorId())*
1788   try {
1789     (<SEMICOLON> | "?>")
1790   } catch (ParseException e) {
1791     errorMessage = "';' expected";
1792     errorLevel   = ERROR;
1793     throw e;
1794   }
1795 }
1796
1797 void StaticStatement() :
1798 {}
1799 {
1800   <STATIC> VariableDeclarator() (<COMMA> VariableDeclarator())*
1801   try {
1802     (<SEMICOLON> | "?>")
1803   } catch (ParseException e) {
1804     errorMessage = "';' expected";
1805     errorLevel   = ERROR;
1806     throw e;
1807   }
1808 }
1809
1810 void LabeledStatement() :
1811 {}
1812 {
1813   <IDENTIFIER> <COLON> Statement()
1814 }
1815
1816 void Block() :
1817 {}
1818 {
1819   try {
1820     <LBRACE>
1821   } catch (ParseException e) {
1822     errorMessage = "'{' expected";
1823     errorLevel   = ERROR;
1824     throw e;
1825   }
1826   ( BlockStatement() )*
1827   try {
1828     <RBRACE>
1829   } catch (ParseException e) {
1830     errorMessage = "unexpected token : '"+ e.currentToken.image +"', '}' expected";
1831     errorLevel   = ERROR;
1832     throw e;
1833   }
1834 }
1835
1836 void BlockStatement() :
1837 {}
1838 {
1839   Statement()
1840 |
1841   ClassDeclaration()
1842 |
1843   MethodDeclaration()
1844 }
1845
1846 /**
1847  * A Block statement that will not contain any 'break'
1848  */
1849 void BlockStatementNoBreak() :
1850 {}
1851 {
1852   StatementNoBreak()
1853 |
1854   ClassDeclaration()
1855 |
1856   MethodDeclaration()
1857 }
1858
1859 void LocalVariableDeclaration() :
1860 {}
1861 {
1862   LocalVariableDeclarator() ( <COMMA> LocalVariableDeclarator() )*
1863 }
1864
1865 void LocalVariableDeclarator() :
1866 {}
1867 {
1868   VariableDeclaratorId() [ <ASSIGN> Expression() ]
1869 }
1870
1871 void EmptyStatement() :
1872 {}
1873 {
1874   <SEMICOLON>
1875 }
1876
1877 void StatementExpression() :
1878 {}
1879 {
1880   PreIncrementExpression()
1881 |
1882   PreDecrementExpression()
1883 |
1884   PrimaryExpression()
1885   [
1886    <INCR>
1887   |
1888     <DECR>
1889   |
1890     AssignmentOperator() Expression()
1891   ]
1892 }
1893
1894 void SwitchStatement() :
1895 {
1896   Token breakToken = null;
1897   int line;
1898 }
1899 {
1900   <SWITCH>
1901   try {
1902     <LPAREN>
1903   } catch (ParseException e) {
1904     errorMessage = "'(' expected after 'switch'";
1905     errorLevel   = ERROR;
1906     throw e;
1907   }
1908   Expression()
1909   try {
1910     <RPAREN>
1911   } catch (ParseException e) {
1912     errorMessage = "')' expected";
1913     errorLevel   = ERROR;
1914     throw e;
1915   }
1916   try {
1917   <LBRACE>
1918   } catch (ParseException e) {
1919     errorMessage = "'{' expected";
1920     errorLevel   = ERROR;
1921     throw e;
1922   }
1923     (
1924       line = SwitchLabel()
1925       ( BlockStatementNoBreak() )*
1926       [ breakToken = <BREAK>
1927         try {
1928           <SEMICOLON>
1929         } catch (ParseException e) {
1930           errorMessage = "';' expected after 'break' keyword";
1931           errorLevel   = ERROR;
1932           throw e;
1933         }
1934       ]
1935       {
1936         try {
1937           if (breakToken == null) {
1938             setMarker(fileToParse,
1939                       "You should use put a 'break' at the end of your statement",
1940                       line,
1941                       INFO,
1942                       "Line " + line);
1943           }
1944         } catch (CoreException e) {
1945           PHPeclipsePlugin.log(e);
1946         }
1947       }
1948     )*
1949   try {
1950     <RBRACE>
1951   } catch (ParseException e) {
1952     errorMessage = "'}' expected";
1953     errorLevel   = ERROR;
1954     throw e;
1955   }
1956 }
1957
1958 int SwitchLabel() :
1959 {
1960   final Token token;
1961 }
1962 {
1963   token = <CASE>
1964   try {
1965     Expression()
1966   } catch (ParseException e) {
1967     if (errorMessage != null) throw e;
1968     errorMessage = "expression expected after 'case' keyword";
1969     errorLevel   = ERROR;
1970     throw e;
1971   }
1972   try {
1973     <COLON>
1974   } catch (ParseException e) {
1975     errorMessage = "':' expected after case expression";
1976     errorLevel   = ERROR;
1977     throw e;
1978   }
1979   {return token.beginLine;}
1980 |
1981   token = <_DEFAULT>
1982   try {
1983     <COLON>
1984   } catch (ParseException e) {
1985     errorMessage = "':' expected after 'default' keyword";
1986     errorLevel   = ERROR;
1987     throw e;
1988   }
1989   {return token.beginLine;}
1990 }
1991
1992 void IfStatement() :
1993 {
1994   final Token token;
1995   final int pos = jj_input_stream.bufpos;
1996 }
1997 {
1998   token = <IF> Condition("if") IfStatement0(pos,pos+token.image.length())
1999 }
2000
2001 void Condition(final String keyword) :
2002 {}
2003 {
2004   try {
2005     <LPAREN>
2006   } catch (ParseException e) {
2007     errorMessage = "'(' expected after " + keyword + " keyword";
2008     errorLevel   = ERROR;
2009     throw e;
2010   }
2011   Expression()
2012   try {
2013      <RPAREN>
2014   } catch (ParseException e) {
2015     errorMessage = "')' expected after " + keyword + " keyword";
2016     errorLevel   = ERROR;
2017     throw e;
2018   }
2019 }
2020
2021 void IfStatement0(final int start,final int end) :
2022 {}
2023 {
2024   <COLON> (Statement())* (ElseIfStatementColon())* [ElseStatementColon()]
2025
2026   {try {
2027   setMarker(fileToParse,
2028             "Ugly syntax detected, you should if () {...} instead of if (): ... endif;",
2029             start,
2030             end,
2031             INFO,
2032             "Line " + token.beginLine);
2033   } catch (CoreException e) {
2034     PHPeclipsePlugin.log(e);
2035   }}
2036   try {
2037     <ENDIF>
2038   } catch (ParseException e) {
2039     errorMessage = "'endif' expected";
2040     errorLevel   = ERROR;
2041     throw e;
2042   }
2043   try {
2044     <SEMICOLON>
2045   } catch (ParseException e) {
2046     errorMessage = "';' expected after 'endif' keyword";
2047     errorLevel   = ERROR;
2048     throw e;
2049   }
2050 |
2051   Statement() ( LOOKAHEAD(1) ElseIfStatement() )* [ LOOKAHEAD(1) <ELSE> Statement() ]
2052 }
2053
2054 void ElseIfStatementColon() :
2055 {}
2056 {
2057   <ELSEIF> Condition("elseif") <COLON> (Statement())*
2058 }
2059
2060 void ElseStatementColon() :
2061 {}
2062 {
2063   <ELSE> <COLON> (Statement())*
2064 }
2065
2066 void ElseIfStatement() :
2067 {}
2068 {
2069   <ELSEIF> Condition("elseif") Statement()
2070 }
2071
2072 void WhileStatement() :
2073 {
2074   final Token token;
2075   final int pos = jj_input_stream.bufpos;
2076 }
2077 {
2078   token = <WHILE> Condition("while") WhileStatement0(pos,pos + token.image.length())
2079 }
2080
2081 void WhileStatement0(final int start, final int end) :
2082 {}
2083 {
2084   <COLON> (Statement())*
2085   {try {
2086   setMarker(fileToParse,
2087             "Ugly syntax detected, you should while () {...} instead of while (): ... endwhile;",
2088             start,
2089             end,
2090             INFO,
2091             "Line " + token.beginLine);
2092   } catch (CoreException e) {
2093     PHPeclipsePlugin.log(e);
2094   }}
2095   try {
2096     <ENDWHILE>
2097   } catch (ParseException e) {
2098     errorMessage = "'endwhile' expected";
2099     errorLevel   = ERROR;
2100     throw e;
2101   }
2102   try {
2103     (<SEMICOLON> | "?>")
2104   } catch (ParseException e) {
2105     errorMessage = "';' expected after 'endwhile' keyword";
2106     errorLevel   = ERROR;
2107     throw e;
2108   }
2109 |
2110   Statement()
2111 }
2112
2113 void DoStatement() :
2114 {}
2115 {
2116   <DO> Statement() <WHILE> Condition("while")
2117   try {
2118     (<SEMICOLON> | "?>")
2119   } catch (ParseException e) {
2120     errorMessage = "';' expected";
2121     errorLevel   = ERROR;
2122     throw e;
2123   }
2124 }
2125
2126 void ForeachStatement() :
2127 {}
2128 {
2129   <FOREACH>
2130     try {
2131     <LPAREN>
2132   } catch (ParseException e) {
2133     errorMessage = "'(' expected after 'foreach' keyword";
2134     errorLevel   = ERROR;
2135     throw e;
2136   }
2137   try {
2138     Variable()
2139   } catch (ParseException e) {
2140     errorMessage = "variable expected";
2141     errorLevel   = ERROR;
2142     throw e;
2143   }
2144   [ VariableSuffix() ]
2145   try {
2146     <AS>
2147   } catch (ParseException e) {
2148     errorMessage = "'as' expected";
2149     errorLevel   = ERROR;
2150     throw e;
2151   }
2152   try {
2153     Variable()
2154   } catch (ParseException e) {
2155     errorMessage = "variable expected";
2156     errorLevel   = ERROR;
2157     throw e;
2158   }
2159   [ <ARRAYASSIGN> Expression() ]
2160   try {
2161     <RPAREN>
2162   } catch (ParseException e) {
2163     errorMessage = "')' expected after 'foreach' keyword";
2164     errorLevel   = ERROR;
2165     throw e;
2166   }
2167   try {
2168     Statement()
2169   } catch (ParseException e) {
2170     if (errorMessage != null) throw e;
2171     errorMessage = "statement expected";
2172     errorLevel   = ERROR;
2173     throw e;
2174   }
2175 }
2176
2177 void ForStatement() :
2178 {
2179 final Token token;
2180 final int pos = jj_input_stream.bufpos;
2181 }
2182 {
2183   token = <FOR>
2184   try {
2185     <LPAREN>
2186   } catch (ParseException e) {
2187     errorMessage = "'(' expected after 'for' keyword";
2188     errorLevel   = ERROR;
2189     throw e;
2190   }
2191      [ ForInit() ] <SEMICOLON> [ Expression() ] <SEMICOLON> [ StatementExpressionList() ] <RPAREN>
2192     (
2193       Statement()
2194     |
2195       <COLON> (Statement())*
2196       {
2197         try {
2198         setMarker(fileToParse,
2199                   "Ugly syntax detected, you should for () {...} instead of for (): ... endfor;",
2200                   pos,
2201                   pos+token.image.length(),
2202                   INFO,
2203                   "Line " + token.beginLine);
2204         } catch (CoreException e) {
2205           PHPeclipsePlugin.log(e);
2206         }
2207       }
2208       try {
2209         <ENDFOR>
2210       } catch (ParseException e) {
2211         errorMessage = "'endfor' expected";
2212         errorLevel   = ERROR;
2213         throw e;
2214       }
2215       try {
2216         <SEMICOLON>
2217       } catch (ParseException e) {
2218         errorMessage = "';' expected after 'endfor' keyword";
2219         errorLevel   = ERROR;
2220         throw e;
2221       }
2222     )
2223 }
2224
2225 void ForInit() :
2226 {}
2227 {
2228   LOOKAHEAD(LocalVariableDeclaration())
2229   LocalVariableDeclaration()
2230 |
2231   StatementExpressionList()
2232 }
2233
2234 void StatementExpressionList() :
2235 {}
2236 {
2237   StatementExpression() ( <COMMA> StatementExpression() )*
2238 }
2239
2240 void BreakStatement() :
2241 {}
2242 {
2243   <BREAK> [ <IDENTIFIER> ]
2244   try {
2245     <SEMICOLON>
2246   } catch (ParseException e) {
2247     errorMessage = "';' expected after 'break' statement";
2248     errorLevel   = ERROR;
2249     throw e;
2250   }
2251 }
2252
2253 void ContinueStatement() :
2254 {}
2255 {
2256   <CONTINUE> [ <IDENTIFIER> ]
2257   try {
2258     <SEMICOLON>
2259   } catch (ParseException e) {
2260     errorMessage = "';' expected after 'continue' statement";
2261     errorLevel   = ERROR;
2262     throw e;
2263   }
2264 }
2265
2266 void ReturnStatement() :
2267 {}
2268 {
2269   <RETURN> [ Expression() ]
2270   try {
2271     <SEMICOLON>
2272   } catch (ParseException e) {
2273     errorMessage = "';' expected after 'return' statement";
2274     errorLevel   = ERROR;
2275     throw e;
2276   }
2277 }