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