changed how special variables are detected
[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   COMMON_TOKEN_ACTION = true;
21 }
22
23 PARSER_BEGIN(PHPParser)
24 package test;
25
26 import org.eclipse.core.resources.IFile;
27 import org.eclipse.core.resources.IMarker;
28 import org.eclipse.core.runtime.CoreException;
29 import org.eclipse.ui.texteditor.MarkerUtilities;
30 import org.eclipse.jface.preference.IPreferenceStore;
31
32 import java.util.Hashtable;
33 import java.util.ArrayList;
34 import java.io.StringReader;
35 import java.io.*;
36 import java.text.MessageFormat;
37
38 import net.sourceforge.phpeclipse.actions.PHPStartApacheAction;
39 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
40 import net.sourceforge.phpdt.internal.compiler.ast.*;
41 import net.sourceforge.phpdt.internal.compiler.parser.OutlineableWithChildren;
42 import net.sourceforge.phpdt.internal.compiler.parser.Outlineable;
43 import net.sourceforge.phpdt.internal.compiler.parser.PHPOutlineInfo;
44 import net.sourceforge.phpdt.internal.corext.Assert;
45
46 /**
47  * A new php parser.
48  * This php parser is inspired by the Java 1.2 grammar example
49  * given with JavaCC. You can get JavaCC at http://www.webgain.com
50  * You can test the parser with the PHPParserTestCase2.java
51  * @author Matthieu Casanova
52  */
53 public final class PHPParser extends PHPParserSuperclass {
54
55   /** The current segment. */
56   private static OutlineableWithChildren 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   static PHPOutlineInfo outlineInfo;
61
62   /** The error level of the current ParseException. */
63   private static int errorLevel = ERROR;
64   /** The message of the current ParseException. If it's null it's because the parse exception wasn't handled */
65   private static String errorMessage;
66
67   private static int errorStart = -1;
68   private static int errorEnd = -1;
69   private static PHPDocument phpDocument;
70
71   private static final String SYNTAX_ERROR_CHAR = "syntax error";
72   /**
73    * The point where html starts.
74    * It will be used by the token manager to create HTMLCode objects
75    */
76   public static int htmlStart;
77
78   //ast stack
79   private final static int AstStackIncrement = 100;
80   /** The stack of node. */
81   private static AstNode[] nodes;
82   /** The cursor in expression stack. */
83   private static int nodePtr;
84
85   private static final boolean PARSER_DEBUG = false;
86
87   public final void setFileToParse(final IFile fileToParse) {
88     PHPParser.fileToParse = fileToParse;
89   }
90
91   public PHPParser() {
92   }
93
94   public PHPParser(final IFile fileToParse) {
95     this(new StringReader(""));
96     PHPParser.fileToParse = fileToParse;
97   }
98
99   public static final void phpParserTester(final String strEval) throws ParseException {
100     final StringReader stream = new StringReader(strEval);
101     if (jj_input_stream == null) {
102       jj_input_stream = new SimpleCharStream(stream, 1, 1);
103     }
104     ReInit(new StringReader(strEval));
105     init();
106     phpDocument = new PHPDocument(null,"_root".toCharArray());
107     currentSegment = phpDocument;
108     outlineInfo = new PHPOutlineInfo(null, currentSegment);
109     PHPParserTokenManager.SwitchTo(PHPParserTokenManager.PHPPARSING);
110     phpTest();
111   }
112
113   public static final void htmlParserTester(final File fileName) throws FileNotFoundException, ParseException {
114     final Reader stream = new FileReader(fileName);
115     if (jj_input_stream == null) {
116       jj_input_stream = new SimpleCharStream(stream, 1, 1);
117     }
118     ReInit(stream);
119     init();
120     phpDocument = new PHPDocument(null,"_root".toCharArray());
121     currentSegment = phpDocument;
122     outlineInfo = new PHPOutlineInfo(null, currentSegment);
123     phpFile();
124   }
125
126   public static final void htmlParserTester(final String strEval) throws ParseException {
127     final StringReader stream = new StringReader(strEval);
128     if (jj_input_stream == null) {
129       jj_input_stream = new SimpleCharStream(stream, 1, 1);
130     }
131     ReInit(stream);
132     init();
133     phpDocument = new PHPDocument(null,"_root".toCharArray());
134     currentSegment = phpDocument;
135     outlineInfo = new PHPOutlineInfo(null, currentSegment);
136     phpFile();
137   }
138
139   /**
140    * Reinitialize the parser.
141    */
142   private static final void init() {
143     nodes = new AstNode[AstStackIncrement];
144     nodePtr = -1;
145     htmlStart = 0;
146   }
147
148   /**
149    * Add an php node on the stack.
150    * @param node the node that will be added to the stack
151    */
152   private static final void pushOnAstNodes(final AstNode node) {
153     try {
154       nodes[++nodePtr] = node;
155     } catch (IndexOutOfBoundsException e) {
156       final int oldStackLength = nodes.length;
157       final AstNode[] oldStack = nodes;
158       nodes = new AstNode[oldStackLength + AstStackIncrement];
159       System.arraycopy(oldStack, 0, nodes, 0, oldStackLength);
160       nodePtr = oldStackLength;
161       nodes[nodePtr] = node;
162     }
163   }
164
165   public final PHPOutlineInfo parseInfo(final Object parent, final String s) {
166     phpDocument = new PHPDocument(parent,"_root".toCharArray());
167     currentSegment = phpDocument;
168     outlineInfo = new PHPOutlineInfo(parent, currentSegment);
169     final StringReader stream = new StringReader(s);
170     if (jj_input_stream == null) {
171       jj_input_stream = new SimpleCharStream(stream, 1, 1);
172     }
173     ReInit(stream);
174     init();
175     try {
176       parse();
177       phpDocument.nodes = new AstNode[nodes.length];
178       System.arraycopy(nodes,0,phpDocument.nodes,0,nodes.length);
179       if (PHPeclipsePlugin.DEBUG) {
180         PHPeclipsePlugin.log(1,phpDocument.toString());
181       }
182     } catch (ParseException e) {
183       processParseException(e);
184     }
185     return outlineInfo;
186   }
187
188   private static void processParseExceptionDebug(final ParseException e) throws ParseException {
189     if (PARSER_DEBUG) {
190       throw e;
191     }
192     processParseException(e);
193   }
194   /**
195    * This method will process the parse exception.
196    * If the error message is null, the parse exception wasn't catched and a trace is written in the log
197    * @param e the ParseException
198    */
199   private static void processParseException(final ParseException e) {
200     if (errorMessage == null) {
201       PHPeclipsePlugin.log(e);
202       errorMessage = "this exception wasn't handled by the parser please tell us how to reproduce it";
203       errorStart = e.currentToken.sourceStart;
204       errorEnd   = e.currentToken.sourceEnd;
205     }
206     setMarker(e);
207     errorMessage = null;
208   //  if (PHPeclipsePlugin.DEBUG) PHPeclipsePlugin.log(e);
209   }
210
211   /**
212    * Create marker for the parse error.
213    * @param e the ParseException
214    */
215   private static void setMarker(final ParseException e) {
216     try {
217       if (errorStart == -1) {
218         setMarker(fileToParse,
219                   errorMessage,
220                   e.currentToken.sourceStart,
221                   e.currentToken.sourceEnd,
222                   errorLevel,
223                   "Line " + e.currentToken.beginLine);
224       } else {
225         setMarker(fileToParse,
226                   errorMessage,
227                   errorStart,
228                   errorEnd,
229                   errorLevel,
230                   "Line " + e.currentToken.beginLine);
231         errorStart = -1;
232         errorEnd = -1;
233       }
234     } catch (CoreException e2) {
235       PHPeclipsePlugin.log(e2);
236     }
237   }
238
239   private static void scanLine(final String output,
240                                final IFile file,
241                                final int indx,
242                                final int brIndx) throws CoreException {
243     String current;
244     final StringBuffer lineNumberBuffer = new StringBuffer(10);
245     char ch;
246     current = output.substring(indx, brIndx);
247
248     if (current.indexOf(PARSE_WARNING_STRING) != -1 || current.indexOf(PARSE_ERROR_STRING) != -1) {
249       final int onLine = current.indexOf("on line <b>");
250       if (onLine != -1) {
251         lineNumberBuffer.delete(0, lineNumberBuffer.length());
252         for (int i = onLine; i < current.length(); i++) {
253           ch = current.charAt(i);
254           if ('0' <= ch && '9' >= ch) {
255             lineNumberBuffer.append(ch);
256           }
257         }
258
259         final int lineNumber = Integer.parseInt(lineNumberBuffer.toString());
260
261         final Hashtable attributes = new Hashtable();
262
263         current = current.replaceAll("\n", "");
264         current = current.replaceAll("<b>", "");
265         current = current.replaceAll("</b>", "");
266         MarkerUtilities.setMessage(attributes, current);
267
268         if (current.indexOf(PARSE_ERROR_STRING) != -1)
269           attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_ERROR));
270         else if (current.indexOf(PARSE_WARNING_STRING) != -1)
271           attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_WARNING));
272         else
273           attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_INFO));
274         MarkerUtilities.setLineNumber(attributes, lineNumber);
275         MarkerUtilities.createMarker(file, attributes, IMarker.PROBLEM);
276       }
277     }
278   }
279
280   public final void parse(final String s) {
281     final StringReader stream = new StringReader(s);
282     if (jj_input_stream == null) {
283       jj_input_stream = new SimpleCharStream(stream, 1, 1);
284     }
285     ReInit(stream);
286     init();
287     try {
288       parse();
289     } catch (ParseException e) {
290       processParseException(e);
291     }
292   }
293
294   /**
295    * Call the php parse command ( php -l -f &lt;filename&gt; )
296    * and create markers according to the external parser output
297    */
298   public static void phpExternalParse(final IFile file) {
299     final IPreferenceStore store = PHPeclipsePlugin.getDefault().getPreferenceStore();
300     final String filename = file.getLocation().toString();
301
302     final String[] arguments = { filename };
303     final MessageFormat form = new MessageFormat(store.getString(PHPeclipsePlugin.EXTERNAL_PARSER_PREF));
304     final String command = form.format(arguments);
305
306     final String parserResult = PHPStartApacheAction.getParserOutput(command, "External parser: ");
307
308     try {
309       // parse the buffer to find the errors and warnings
310       createMarkers(parserResult, file);
311     } catch (CoreException e) {
312       PHPeclipsePlugin.log(e);
313     }
314   }
315
316   /**
317    * Put a new html block in the stack.
318    */
319   public static final void createNewHTMLCode() {
320     final int currentPosition = SimpleCharStream.getPosition();
321     if (currentPosition == htmlStart || currentPosition > SimpleCharStream.currentBuffer.length()) {
322       return;
323     }
324     final char[] chars = SimpleCharStream.currentBuffer.substring(htmlStart,currentPosition+1).toCharArray();
325     pushOnAstNodes(new HTMLCode(chars, htmlStart,currentPosition));
326   }
327
328   /** Create a new task. */
329   public static final void createNewTask() {
330     final int currentPosition = SimpleCharStream.getPosition();
331     final String  todo = SimpleCharStream.currentBuffer.substring(currentPosition-3,
332                                                                   SimpleCharStream.currentBuffer.indexOf("\n",
333                                                                                                          currentPosition)-1);
334     PHPeclipsePlugin.log(1,SimpleCharStream.currentBuffer.toString());
335     try {
336       setMarker(fileToParse,
337                 todo,
338                 SimpleCharStream.getBeginLine(),
339                 TASK,
340                 "Line "+SimpleCharStream.getBeginLine());
341     } catch (CoreException e) {
342       PHPeclipsePlugin.log(e);
343     }
344   }
345
346   private static final void parse() throws ParseException {
347           phpFile();
348   }
349 }
350
351 PARSER_END(PHPParser)
352
353 TOKEN_MGR_DECLS:
354 {
355   // CommonTokenAction: use the begins/ends fields added to the Jack
356   // CharStream class to set corresponding fields in each Token (which was
357   // also extended with new fields). By default Jack doesn't supply absolute
358   // offsets, just line/column offsets
359   static void CommonTokenAction(Token t) {
360     t.sourceStart = input_stream.beginOffset;
361     t.sourceEnd = input_stream.endOffset;
362   } // CommonTokenAction
363 } // TOKEN_MGR_DECLS
364
365 <DEFAULT> TOKEN :
366 {
367   <PHPSTARTSHORT : "<?">    {PHPParser.createNewHTMLCode();} : PHPPARSING
368 | <PHPSTARTLONG  : "<?php"> {PHPParser.createNewHTMLCode();} : PHPPARSING
369 | <PHPECHOSTART  : "<?=">   {PHPParser.createNewHTMLCode();} : PHPPARSING
370 }
371
372 <PHPPARSING, IN_SINGLE_LINE_COMMENT> TOKEN :
373 {
374   <PHPEND :"?>"> {PHPParser.htmlStart = SimpleCharStream.getPosition();} : DEFAULT
375 }
376
377 /* Skip any character if we are not in php mode */
378 <DEFAULT> SKIP :
379 {
380  < ~[] >
381 }
382
383
384 /* WHITE SPACE */
385 <PHPPARSING> SKIP :
386 {
387   " "
388 | "\t"
389 | "\n"
390 | "\r"
391 | "\f"
392 }
393
394 /* COMMENTS */
395 <PHPPARSING> SPECIAL_TOKEN :
396 {
397   "//" : IN_SINGLE_LINE_COMMENT
398 | "#"  : IN_SINGLE_LINE_COMMENT
399 | <"/**" ~["/"]> { input_stream.backup(1); } : IN_FORMAL_COMMENT
400 | "/*" : IN_MULTI_LINE_COMMENT
401 }
402
403 <IN_SINGLE_LINE_COMMENT> SPECIAL_TOKEN :
404 {
405   <SINGLE_LINE_COMMENT: "\n" | "\r" | "\r\n" > : PHPPARSING
406 | < ~[] >
407 }
408
409 <IN_SINGLE_LINE_COMMENT,IN_FORMAL_COMMENT,IN_MULTI_LINE_COMMENT> SPECIAL_TOKEN :
410 {
411  "todo" {PHPParser.createNewTask();}
412 }
413
414 <IN_FORMAL_COMMENT> SPECIAL_TOKEN :
415 {
416   "*/" : PHPPARSING
417 }
418
419 <IN_MULTI_LINE_COMMENT> SPECIAL_TOKEN :
420 {
421   "*/" : PHPPARSING
422 }
423
424 <IN_SINGLE_LINE_COMMENT,IN_FORMAL_COMMENT,IN_MULTI_LINE_COMMENT>
425 MORE :
426 {
427   < ~[] >
428 }
429
430 /* KEYWORDS */
431 <PHPPARSING> TOKEN :
432 {
433   <CLASS    : "class">
434 | <FUNCTION : "function">
435 | <VAR      : "var">
436 | <IF       : "if">
437 | <ELSEIF   : "elseif">
438 | <ELSE     : "else">
439 | <ARRAY    : "array">
440 | <BREAK    : "break">
441 | <LIST     : "list">
442 }
443
444 /* LANGUAGE CONSTRUCT */
445 <PHPPARSING> TOKEN :
446 {
447   <PRINT              : "print">
448 | <ECHO               : "echo">
449 | <INCLUDE            : "include">
450 | <REQUIRE            : "require">
451 | <INCLUDE_ONCE       : "include_once">
452 | <REQUIRE_ONCE       : "require_once">
453 | <GLOBAL             : "global">
454 | <DEFINE             : "define">
455 | <STATIC             : "static">
456 | <CLASSACCESS        : "->">
457 | <STATICCLASSACCESS  : "::">
458 | <ARRAYASSIGN        : "=>">
459 }
460
461 /* RESERVED WORDS AND LITERALS */
462
463 <PHPPARSING> TOKEN :
464 {
465   <CASE     : "case">
466 | <CONST    : "const">
467 | <CONTINUE : "continue">
468 | <_DEFAULT : "default">
469 | <DO       : "do">
470 | <EXTENDS  : "extends">
471 | <FOR      : "for">
472 | <GOTO     : "goto">
473 | <NEW      : "new">
474 | <NULL     : "null">
475 | <RETURN   : "return">
476 | <SUPER    : "super">
477 | <SWITCH   : "switch">
478 | <THIS     : "this">
479 | <TRUE     : "true">
480 | <FALSE    : "false">
481 | <WHILE    : "while">
482 | <ENDWHILE : "endwhile">
483 | <ENDSWITCH: "endswitch">
484 | <ENDIF    : "endif">
485 | <ENDFOR   : "endfor">
486 | <FOREACH  : "foreach">
487 | <AS       : "as" >
488 }
489
490 /* TYPES */
491 <PHPPARSING> TOKEN :
492 {
493   <STRING  : "string">
494 | <OBJECT  : "object">
495 | <BOOL    : "bool">
496 | <BOOLEAN : "boolean">
497 | <REAL    : "real">
498 | <DOUBLE  : "double">
499 | <FLOAT   : "float">
500 | <INT     : "int">
501 | <INTEGER : "integer">
502 }
503
504 //Misc token
505 <PHPPARSING> TOKEN :
506 {
507   <AT                 : "@">
508 | <DOLLAR             : "$">
509 | <BANG               : "!">
510 | <TILDE              : "~">
511 | <HOOK               : "?">
512 | <COLON              : ":">
513 }
514
515 /* OPERATORS */
516 <PHPPARSING> TOKEN :
517 {
518   <OR_OR              : "||">
519 | <AND_AND            : "&&">
520 | <PLUS_PLUS          : "++">
521 | <MINUS_MINUS        : "--">
522 | <PLUS               : "+">
523 | <MINUS              : "-">
524 | <STAR               : "*">
525 | <SLASH              : "/">
526 | <BIT_AND            : "&">
527 | <BIT_OR             : "|">
528 | <XOR                : "^">
529 | <REMAINDER          : "%">
530 | <LSHIFT             : "<<">
531 | <RSIGNEDSHIFT       : ">>">
532 | <RUNSIGNEDSHIFT     : ">>>">
533 | <_ORL               : "OR">
534 | <_ANDL              : "AND">
535 }
536
537 /* LITERALS */
538 <PHPPARSING> TOKEN :
539 {
540   <INTEGER_LITERAL:
541         <DECIMAL_LITERAL> (["l","L"])?
542       | <HEX_LITERAL> (["l","L"])?
543       | <OCTAL_LITERAL> (["l","L"])?
544   >
545 |
546   <#DECIMAL_LITERAL: ["1"-"9"] (["0"-"9"])* >
547 |
548   <#HEX_LITERAL: "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+ >
549 |
550   <#OCTAL_LITERAL: "0" (["0"-"7"])* >
551 |
552   <FLOATING_POINT_LITERAL:
553         (["0"-"9"])+ "." (["0"-"9"])* (<EXPONENT>)? (["f","F","d","D"])?
554       | "." (["0"-"9"])+ (<EXPONENT>)? (["f","F","d","D"])?
555       | (["0"-"9"])+ <EXPONENT> (["f","F","d","D"])?
556       | (["0"-"9"])+ (<EXPONENT>)? ["f","F","d","D"]
557   >
558 |
559   <#EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ >
560 |
561   <STRING_LITERAL: (<STRING_1> | <STRING_2> | <STRING_3>)>
562 |   <STRING_1: "\"" ( ~["\"","\\"] | "\\" ~[] )* "\"">
563 |   <STRING_2: "'"  ( ~["'","\\"]  | "\\" ~[] )* "'">
564 |   <STRING_3: "`"  ( ~["`","\\"]  | "\\" ~[] )* "`">
565 }
566
567 /* IDENTIFIERS */
568
569 <PHPPARSING> TOKEN :
570 {
571   <IDENTIFIER: (<LETTER>|<SPECIAL>) (<LETTER>|<DIGIT>|<SPECIAL>)* >
572 |
573   < #LETTER:
574       ["a"-"z"] | ["A"-"Z"]
575   >
576 |
577   < #DIGIT:
578       ["0"-"9"]
579   >
580 |
581   < #SPECIAL:
582     "_" | ["\u007f"-"\u00ff"]
583   >
584 }
585
586 /* SEPARATORS */
587
588 <PHPPARSING> TOKEN :
589 {
590   <LPAREN    : "(">
591 | <RPAREN    : ")">
592 | <LBRACE    : "{">
593 | <RBRACE    : "}">
594 | <LBRACKET  : "[">
595 | <RBRACKET  : "]">
596 | <SEMICOLON : ";">
597 | <COMMA     : ",">
598 | <DOT       : ".">
599 }
600
601
602 /* COMPARATOR */
603 <PHPPARSING> TOKEN :
604 {
605   <GT                 : ">">
606 | <LT                 : "<">
607 | <EQUAL_EQUAL        : "==">
608 | <LE                 : "<=">
609 | <GE                 : ">=">
610 | <NOT_EQUAL          : "!=">
611 | <DIF                : "<>">
612 | <BANGDOUBLEEQUAL    : "!==">
613 | <TRIPLEEQUAL        : "===">
614 }
615
616 /* ASSIGNATION */
617 <PHPPARSING> TOKEN :
618 {
619   <ASSIGN             : "=">
620 | <PLUSASSIGN         : "+=">
621 | <MINUSASSIGN        : "-=">
622 | <STARASSIGN         : "*=">
623 | <SLASHASSIGN        : "/=">
624 | <ANDASSIGN          : "&=">
625 | <ORASSIGN           : "|=">
626 | <XORASSIGN          : "^=">
627 | <DOTASSIGN          : ".=">
628 | <REMASSIGN          : "%=">
629 | <TILDEEQUAL         : "~=">
630 | <LSHIFTASSIGN       : "<<=">
631 | <RSIGNEDSHIFTASSIGN : ">>=">
632 }
633
634 <PHPPARSING> TOKEN :
635 {
636   <DOLLAR_ID: <DOLLAR> <IDENTIFIER>>
637 }
638
639 void phpTest() :
640 {}
641 {
642   Php()
643   <EOF>
644 }
645
646 void phpFile() :
647 {}
648 {
649   try {
650     (PhpBlock())*
651     {PHPParser.createNewHTMLCode();}
652   } catch (TokenMgrError e) {
653     PHPeclipsePlugin.log(e);
654     errorStart   = SimpleCharStream.getPosition();
655     errorEnd     = errorStart + 1;
656     errorMessage = e.getMessage();
657     errorLevel   = ERROR;
658     throw generateParseException();
659   }
660 }
661
662 /**
663  * A php block is a <?= expression [;]?>
664  * or <?php somephpcode ?>
665  * or <? somephpcode ?>
666  */
667 void PhpBlock() :
668 {
669   final int start = SimpleCharStream.getPosition();
670   final PHPEchoBlock phpEchoBlock;
671 }
672 {
673   phpEchoBlock = phpEchoBlock()
674   {pushOnAstNodes(phpEchoBlock);}
675 |
676   [   <PHPSTARTLONG>
677     | <PHPSTARTSHORT>
678     {try {
679       setMarker(fileToParse,
680                 "You should use '<?php' instead of '<?' it will avoid some problems with XML",
681                 start,
682                 SimpleCharStream.getPosition(),
683                 INFO,
684                 "Line " + token.beginLine);
685     } catch (CoreException e) {
686       PHPeclipsePlugin.log(e);
687     }}
688   ]
689   Php()
690   try {
691     <PHPEND>
692   } catch (ParseException e) {
693     errorMessage = "'?>' expected";
694     errorLevel   = ERROR;
695     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
696     errorEnd   = SimpleCharStream.getPosition() + 1;
697     processParseExceptionDebug(e);
698   }
699 }
700
701 PHPEchoBlock phpEchoBlock() :
702 {
703   final Expression expr;
704   final PHPEchoBlock echoBlock;
705   final Token token, token2;
706 }
707 {
708   token = <PHPECHOSTART> expr = Expression() [ <SEMICOLON> ] token2 = <PHPEND>
709   {
710   echoBlock = new PHPEchoBlock(expr,token.sourceStart,token.sourceEnd);
711   pushOnAstNodes(echoBlock);
712   return echoBlock;}
713 }
714
715 void Php() :
716 {}
717 {
718   (BlockStatement())*
719 }
720
721 ClassDeclaration ClassDeclaration() :
722 {
723   final ClassDeclaration classDeclaration;
724   Token className = null;
725   final Token superclassName, token;
726   String classNameImage = SYNTAX_ERROR_CHAR;
727   String superclassNameImage = null;
728 }
729 {
730   token = <CLASS>
731   try {
732     className = <IDENTIFIER>
733     {classNameImage = className.image;}
734   } catch (ParseException e) {
735     errorMessage = "unexpected token : '"+ e.currentToken.next.image +"', identifier expected";
736     errorLevel   = ERROR;
737     errorStart   = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
738     errorEnd     = SimpleCharStream.getPosition() + 1;
739     processParseExceptionDebug(e);
740   }
741   [
742     <EXTENDS>
743     try {
744       superclassName = <IDENTIFIER>
745       {superclassNameImage = superclassName.image;}
746     } catch (ParseException e) {
747       errorMessage = "unexpected token : '"+ e.currentToken.next.image +"', identifier expected";
748       errorLevel   = ERROR;
749       errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
750       errorEnd   = SimpleCharStream.getPosition() + 1;
751       processParseExceptionDebug(e);
752       superclassNameImage = SYNTAX_ERROR_CHAR;
753     }
754   ]
755   {
756     int start, end;
757     if (className == null) {
758       start = token.sourceStart;
759       end = token.sourceEnd;
760     } else {
761       start = className.sourceStart;
762       end = className.sourceEnd;
763     }
764     if (superclassNameImage == null) {
765
766       classDeclaration = new ClassDeclaration(currentSegment,
767                                               classNameImage,
768                                               start,
769                                               end);
770     } else {
771       classDeclaration = new ClassDeclaration(currentSegment,
772                                               classNameImage,
773                                               superclassNameImage,
774                                               start,
775                                               end);
776     }
777       currentSegment.add(classDeclaration);
778       currentSegment = classDeclaration;
779   }
780   ClassBody(classDeclaration)
781   {currentSegment = (OutlineableWithChildren) currentSegment.getParent();
782    classDeclaration.sourceEnd = SimpleCharStream.getPosition();
783    pushOnAstNodes(classDeclaration);
784    return classDeclaration;}
785 }
786
787 void ClassBody(final ClassDeclaration classDeclaration) :
788 {}
789 {
790   try {
791     <LBRACE>
792   } catch (ParseException e) {
793     errorMessage = "unexpected token : '"+ e.currentToken.next.image + "'. '{' expected";
794     errorLevel   = ERROR;
795     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
796     errorEnd   = SimpleCharStream.getPosition() + 1;
797     processParseExceptionDebug(e);
798   }
799   ( ClassBodyDeclaration(classDeclaration) )*
800   try {
801     <RBRACE>
802   } catch (ParseException e) {
803     errorMessage = "unexpected token : '"+ e.currentToken.next.image +"'. 'var', 'function' or '}' expected";
804     errorLevel   = ERROR;
805     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
806     errorEnd   = SimpleCharStream.getPosition() + 1;
807     processParseExceptionDebug(e);
808   }
809 }
810
811 /**
812  * A class can contain only methods and fields.
813  */
814 void ClassBodyDeclaration(final ClassDeclaration classDeclaration) :
815 {
816   final MethodDeclaration method;
817   final FieldDeclaration field;
818 }
819 {
820   method = MethodDeclaration() {method.analyzeCode();
821                                 classDeclaration.addMethod(method);}
822 | field = FieldDeclaration()   {classDeclaration.addField(field);}
823 }
824
825 /**
826  * A class field declaration : it's var VariableDeclarator() (, VariableDeclarator())*;.
827  * it is only used by ClassBodyDeclaration()
828  */
829 FieldDeclaration FieldDeclaration() :
830 {
831   VariableDeclaration variableDeclaration;
832   final VariableDeclaration[] list;
833   final ArrayList arrayList = new ArrayList();
834   final int pos = SimpleCharStream.getPosition();
835   final Token token;
836   Token token2 = null;
837 }
838 {
839   token = <VAR> variableDeclaration = VariableDeclaratorNoSuffix()
840   {arrayList.add(variableDeclaration);
841    outlineInfo.addVariable(variableDeclaration.name());}
842   (
843     <COMMA> variableDeclaration = VariableDeclaratorNoSuffix()
844       {arrayList.add(variableDeclaration);
845        outlineInfo.addVariable(variableDeclaration.name());}
846   )*
847   try {
848     token2 = <SEMICOLON>
849   } catch (ParseException e) {
850     errorMessage = "unexpected token : '"+ e.currentToken.next.image +"'. A ';' was expected after variable declaration";
851     errorLevel   = ERROR;
852     errorStart   = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
853     errorEnd     = SimpleCharStream.getPosition() + 1;
854     processParseExceptionDebug(e);
855   }
856
857   {list = new VariableDeclaration[arrayList.size()];
858    arrayList.toArray(list);
859    int end;
860    if (token2 == null) {
861      end = list[list.length-1].sourceEnd;
862    } else {
863      end = token2.sourceEnd;
864    }
865    return new FieldDeclaration(list,
866                                token.sourceStart,
867                                end,
868                                currentSegment);}
869 }
870
871 /**
872  * a strict variable declarator : there cannot be a suffix here.
873  * It will be used by fields and formal parameters
874  */
875 VariableDeclaration VariableDeclaratorNoSuffix() :
876 {
877   final Token varName;
878   Expression initializer = null;
879 }
880 {
881   varName = <DOLLAR_ID>
882   [
883     <ASSIGN>
884     try {
885       initializer = VariableInitializer()
886     } catch (ParseException e) {
887       errorMessage = "Literal expression expected in variable initializer";
888       errorLevel   = ERROR;
889       errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
890       errorEnd   = SimpleCharStream.getPosition() + 1;
891       processParseExceptionDebug(e);
892     }
893   ]
894   {
895   if (initializer == null) {
896     return new VariableDeclaration(currentSegment,
897                                    new Variable(varName.image.substring(1),
898                                                 varName.sourceStart,
899                                                 varName.sourceEnd),
900                                    varName.sourceStart,
901                                    varName.sourceEnd);
902   }
903   return new VariableDeclaration(currentSegment,
904                                  new Variable(varName.image.substring(1),
905                                               varName.sourceStart,
906                                               varName.sourceEnd),
907                                  initializer,
908                                  VariableDeclaration.EQUAL,
909                                  varName.sourceStart);
910   }
911 }
912
913 /**
914  * this will be used by static statement
915  */
916 VariableDeclaration VariableDeclarator() :
917 {
918   final AbstractVariable variable;
919   Expression initializer = null;
920 }
921 {
922   variable = VariableDeclaratorId()
923   [
924     <ASSIGN>
925     try {
926       initializer = VariableInitializer()
927     } catch (ParseException e) {
928       errorMessage = "Literal expression expected in variable initializer";
929       errorLevel   = ERROR;
930       errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
931       errorEnd   = SimpleCharStream.getPosition() + 1;
932       processParseExceptionDebug(e);
933     }
934   ]
935   {
936   if (initializer == null) {
937     return new VariableDeclaration(currentSegment,
938                                    variable,
939                                    variable.sourceStart,
940                                    variable.sourceEnd);
941   }
942     return new VariableDeclaration(currentSegment,
943                                    variable,
944                                    initializer,
945                                    VariableDeclaration.EQUAL,
946                                    variable.sourceStart);
947   }
948 }
949
950 /**
951  * A Variable name.
952  * @return the variable name (with suffix)
953  */
954 AbstractVariable VariableDeclaratorId() :
955 {
956   final Variable var;
957   AbstractVariable expression = null;
958   final int pos = SimpleCharStream.getPosition();
959 }
960 {
961   try {
962     var = Variable()
963     (
964       LOOKAHEAD(2)
965       expression = VariableSuffix(var)
966     )*
967     {
968      if (expression == null) {
969        return var;
970      }
971      return expression;
972     }
973   } catch (ParseException e) {
974     errorMessage = "'$' expected for variable identifier";
975     errorLevel   = ERROR;
976     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
977     errorEnd   = SimpleCharStream.getPosition() + 1;
978     throw e;
979   }
980 }
981
982 /**
983  * Return a variablename without the $.
984  * @return a variable name
985  *//*
986 Variable Variable():
987 {
988   final StringBuffer buff;
989   Expression expression = null;
990   final Token token;
991   Variable expr;
992   final int pos;
993 }
994 {
995   token = <DOLLAR_ID>
996   [<LBRACE> expression = Expression() <RBRACE>]
997   {
998     if (expression == null) {
999       return new Variable(token.image.substring(1),
1000                           token.sourceStart,
1001                           token.sourceEnd);
1002     }
1003     String s = expression.toStringExpression();
1004     buff = new StringBuffer(token.image.length()+s.length()+2);
1005     buff.append(token.image);
1006     buff.append("{");
1007     buff.append(s);
1008     buff.append("}");
1009     s = buff.toString();
1010     return new Variable(s,token.sourceStart,token.sourceEnd);
1011   }
1012 |
1013   token = <DOLLAR>
1014   expr = VariableName()
1015   {return new Variable(expr,token.sourceStart,expr.sourceEnd);}
1016 }   */
1017
1018 Variable Variable() :
1019 {
1020   Variable variable = null;
1021   final Token token;
1022 }
1023 {
1024  token = <DOLLAR_ID> [variable = Var(token)]
1025   {
1026     if (variable == null) {
1027       return new Variable(token.image.substring(1),token.sourceStart,token.sourceEnd);
1028     }
1029     final StringBuffer buff = new StringBuffer();
1030     buff.append(token.image.substring(1));
1031     buff.append(variable.toStringExpression());
1032     return new Variable(buff.toString(),token.sourceStart,variable.sourceEnd);
1033   }
1034 |
1035   token = <DOLLAR> variable = Var(token)
1036   {
1037     return new Variable(variable,token.sourceStart,variable.sourceEnd);
1038   }
1039 }
1040
1041 Variable Var(final Token dollar) :
1042 {
1043   Variable variable = null;
1044   final Token token;
1045   ConstantIdentifier constant;
1046 }
1047 {
1048   token = <DOLLAR_ID> [variable = Var(token)]
1049   {if (variable == null) {
1050      return new Variable(token.image.substring(1),token.sourceStart,token.sourceEnd);
1051    }
1052    final StringBuffer buff = new StringBuffer();
1053    buff.append(token.image.substring(1));
1054    buff.append(variable.toStringExpression());
1055    return new Variable(buff.toString(),dollar.sourceStart,variable.sourceEnd);
1056    }
1057 |
1058   LOOKAHEAD(<DOLLAR> <DOLLAR>)
1059   token = <DOLLAR> variable = Var(token)
1060   {return new Variable(variable,dollar.sourceStart,variable.sourceEnd);}
1061 |
1062   constant = VariableName()
1063   {return new Variable(constant.name,dollar.sourceStart,constant.sourceEnd);}
1064 }
1065
1066 /**
1067  * A Variable name (without the $)
1068  * @return a variable name String
1069  */
1070 ConstantIdentifier VariableName():
1071 {
1072   final StringBuffer buff;
1073   String expr;
1074   final Variable var;
1075   Expression expression = null;
1076   final Token token;
1077   Token token2 = null;
1078   int pos;
1079 }
1080 {
1081   token = <LBRACE> expression = Expression() token2 = <RBRACE>
1082   {expr = expression.toStringExpression();
1083    buff = new StringBuffer(expr.length()+2);
1084    buff.append("{");
1085    buff.append(expr);
1086    buff.append("}");
1087    pos = SimpleCharStream.getPosition();
1088    expr = buff.toString();
1089    return new ConstantIdentifier(expr,
1090                                  token.sourceStart,
1091                                  token2.sourceEnd);
1092
1093    }
1094 |
1095   token = <IDENTIFIER>
1096   [<LBRACE> expression = Expression() token2 = <RBRACE>]
1097   {
1098     if (expression == null) {
1099       return new ConstantIdentifier(token.image,
1100                                     token.sourceStart,
1101                                     token.sourceEnd);
1102     }
1103     expr = expression.toStringExpression();
1104     buff = new StringBuffer(token.image.length()+expr.length()+2);
1105     buff.append(token.image);
1106     buff.append("{");
1107     buff.append(expr);
1108     buff.append("}");
1109     expr = buff.toString();
1110     return new ConstantIdentifier(expr,
1111                                   token.sourceStart,
1112                                   token2.sourceEnd);
1113   }
1114 /*|
1115   <DOLLAR>
1116   var = VariableName()
1117   {
1118     return new Variable(var,
1119                         var.sourceStart-1,
1120                         var.sourceEnd);
1121   }
1122 |
1123   token = <DOLLAR_ID>
1124   {
1125   return new Variable(token.image,
1126                       token.sourceStart,
1127                       token.sourceEnd);
1128   } */
1129 }
1130
1131 Expression VariableInitializer() :
1132 {
1133   final Expression expr;
1134   final Token token, token2;
1135 }
1136 {
1137   expr = Literal()
1138   {return expr;}
1139 |
1140   token2 = <MINUS> (token = <INTEGER_LITERAL> | token = <FLOATING_POINT_LITERAL>)
1141   {return new PrefixedUnaryExpression(new NumberLiteral(token),
1142                                       OperatorIds.MINUS,
1143                                       token2.sourceStart);}
1144 |
1145   token2 = <PLUS> (token = <INTEGER_LITERAL> | token = <FLOATING_POINT_LITERAL>)
1146   {return new PrefixedUnaryExpression(new NumberLiteral(token),
1147                                       OperatorIds.PLUS,
1148                                       token2.sourceStart);}
1149 |
1150   expr = ArrayDeclarator()
1151   {return expr;}
1152 |
1153   token = <IDENTIFIER>
1154   {return new ConstantIdentifier(token);}
1155 }
1156
1157 ArrayVariableDeclaration ArrayVariable() :
1158 {
1159 final Expression expr,expr2;
1160 }
1161 {
1162   expr = Expression()
1163   [
1164     <ARRAYASSIGN> expr2 = Expression()
1165     {return new ArrayVariableDeclaration(expr,expr2);}
1166   ]
1167   {return new ArrayVariableDeclaration(expr,SimpleCharStream.getPosition());}
1168 }
1169
1170 ArrayVariableDeclaration[] ArrayInitializer() :
1171 {
1172   ArrayVariableDeclaration expr;
1173   final ArrayList list = new ArrayList();
1174 }
1175 {
1176   <LPAREN>
1177     [
1178       expr = ArrayVariable()
1179       {list.add(expr);}
1180       ( LOOKAHEAD(2) <COMMA> expr = ArrayVariable()
1181       {list.add(expr);}
1182       )*
1183     ]
1184     [
1185       <COMMA> {list.add(null);}
1186     ]
1187   <RPAREN>
1188   {
1189   final ArrayVariableDeclaration[] vars = new ArrayVariableDeclaration[list.size()];
1190   list.toArray(vars);
1191   return vars;}
1192 }
1193
1194 /**
1195  * A Method Declaration.
1196  * <b>function</b> MetodDeclarator() Block()
1197  */
1198 MethodDeclaration MethodDeclaration() :
1199 {
1200   final MethodDeclaration functionDeclaration;
1201   final Block block;
1202   final OutlineableWithChildren seg = currentSegment;
1203   final Token token;
1204 }
1205 {
1206   token = <FUNCTION>
1207   try {
1208     functionDeclaration = MethodDeclarator(token.sourceStart)
1209     {outlineInfo.addVariable(new String(functionDeclaration.name));}
1210   } catch (ParseException e) {
1211     if (errorMessage != null)  throw e;
1212     errorMessage = "unexpected token : '"+ e.currentToken.next.image +"', function identifier expected";
1213     errorLevel   = ERROR;
1214     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
1215     errorEnd   = SimpleCharStream.getPosition() + 1;
1216     throw e;
1217   }
1218   {currentSegment = functionDeclaration;}
1219   block = Block()
1220   {functionDeclaration.statements = block.statements;
1221    currentSegment = seg;
1222    return functionDeclaration;}
1223 }
1224
1225 /**
1226  * A MethodDeclarator.
1227  * [&] IDENTIFIER(parameters ...).
1228  * @return a function description for the outline
1229  */
1230 MethodDeclaration MethodDeclarator(final int start) :
1231 {
1232   Token identifier = null;
1233   Token reference = null;
1234   final Hashtable formalParameters = new Hashtable();
1235   String identifierChar = SYNTAX_ERROR_CHAR;
1236   final int end;
1237 }
1238 {
1239   [reference = <BIT_AND>]
1240   try {
1241     identifier = <IDENTIFIER>
1242     {identifierChar = identifier.image;}
1243   } catch (ParseException e) {
1244     errorMessage = "unexpected token : '"+ e.currentToken.next.image +"', function identifier expected";
1245     errorLevel   = ERROR;
1246     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
1247     errorEnd   = SimpleCharStream.getPosition() + 1;
1248     processParseExceptionDebug(e);
1249   }
1250   end = FormalParameters(formalParameters)
1251   {
1252   int nameStart, nameEnd;
1253   if (identifier == null) {
1254     if (reference == null) {
1255       nameStart = start + 9;
1256       nameEnd = start + 10;
1257     } else {
1258       nameStart = reference.sourceEnd + 1;
1259       nameEnd = reference.sourceEnd + 2;
1260     }
1261   } else {
1262       nameStart = identifier.sourceStart;
1263       nameEnd = identifier.sourceEnd;
1264   }
1265   return new MethodDeclaration(currentSegment,
1266                                identifierChar,
1267                                formalParameters,
1268                                reference != null,
1269                                nameStart,
1270                                nameEnd,
1271                                start,
1272                                end);
1273   }
1274 }
1275
1276 /**
1277  * FormalParameters follows method identifier.
1278  * (FormalParameter())
1279  */
1280 int FormalParameters(final Hashtable parameters) :
1281 {
1282   VariableDeclaration var;
1283   final Token token;
1284   int end;
1285 }
1286 {
1287   try {
1288   <LPAREN>
1289   } catch (ParseException e) {
1290     errorMessage = "unexpected token : '"+ e.currentToken.next.image +"', '(' expected after function identifier";
1291     errorLevel   = ERROR;
1292     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
1293     errorEnd   = SimpleCharStream.getPosition() + 1;
1294     processParseExceptionDebug(e);
1295   }
1296   [
1297     var = FormalParameter()
1298     {parameters.put(new String(var.name()),var);}
1299     (
1300       <COMMA> var = FormalParameter()
1301       {parameters.put(new String(var.name()),var);}
1302     )*
1303   ]
1304   try {
1305     token = <RPAREN>
1306     {end = token.sourceEnd;}
1307   } catch (ParseException e) {
1308     errorMessage = "')' expected";
1309     errorLevel   = ERROR;
1310     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
1311     errorEnd   = SimpleCharStream.getPosition() + 1;
1312     processParseExceptionDebug(e);
1313     end = e.currentToken.sourceStart;
1314   }
1315  {return end;}
1316 }
1317
1318 /**
1319  * A formal parameter.
1320  * $varname[=value] (,$varname[=value])
1321  */
1322 VariableDeclaration FormalParameter() :
1323 {
1324   final VariableDeclaration variableDeclaration;
1325   Token token = null;
1326 }
1327 {
1328   [token = <BIT_AND>] variableDeclaration = VariableDeclaratorNoSuffix()
1329   {
1330     if (token != null) {
1331       variableDeclaration.setReference(true);
1332     }
1333     return variableDeclaration;}
1334 }
1335
1336 ConstantIdentifier Type() :
1337 {final Token token;}
1338 {
1339   token = <STRING>    {return new ConstantIdentifier(token);}
1340 | token = <BOOL>      {return new ConstantIdentifier(token);}
1341 | token = <BOOLEAN>   {return new ConstantIdentifier(token);}
1342 | token = <REAL>      {return new ConstantIdentifier(token);}
1343 | token = <DOUBLE>    {return new ConstantIdentifier(token);}
1344 | token = <FLOAT>     {return new ConstantIdentifier(token);}
1345 | token = <INT>       {return new ConstantIdentifier(token);}
1346 | token = <INTEGER>   {return new ConstantIdentifier(token);}
1347 | token = <OBJECT>    {return new ConstantIdentifier(token);}
1348 }
1349
1350 Expression Expression() :
1351 {
1352   final Expression expr;
1353   Expression initializer = null;
1354   int assignOperator = -1;
1355 }
1356 {
1357   LOOKAHEAD(1)
1358   expr = ConditionalExpression()
1359   [
1360     assignOperator = AssignmentOperator()
1361     try {
1362       initializer = Expression()
1363     } catch (ParseException e) {
1364       if (errorMessage != null) {
1365         throw e;
1366       }
1367       errorMessage = "unexpected token : '"+ e.currentToken.next.image +"', expression expected";
1368       errorLevel   = ERROR;
1369       errorEnd   = SimpleCharStream.getPosition();
1370       throw e;
1371     }
1372   ]
1373   {
1374     if (assignOperator != -1) {// todo : change this, very very bad :(
1375         if (expr instanceof AbstractVariable) {
1376           return new VariableDeclaration(currentSegment,
1377                                          (AbstractVariable) expr,
1378                                          initializer,
1379                                          expr.sourceStart,
1380                                          initializer.sourceEnd);
1381         }
1382         String varName = expr.toStringExpression().substring(1);
1383         return new VariableDeclaration(currentSegment,
1384                                        new Variable(varName,
1385                                                     expr.sourceStart,
1386                                                     expr.sourceEnd),
1387                                        expr.sourceStart,
1388                                        initializer.sourceEnd);
1389     }
1390     return expr;
1391   }
1392 | expr = ExpressionWBang()       {return expr;}
1393 }
1394
1395 Expression ExpressionWBang() :
1396 {
1397   final Expression expr;
1398   final Token token;
1399 }
1400 {
1401   token = <BANG> expr = ExpressionWBang()
1402   {return new PrefixedUnaryExpression(expr,OperatorIds.NOT,token.sourceStart);}
1403 | expr = ExpressionNoBang() {return expr;}
1404 }
1405
1406 Expression ExpressionNoBang() :
1407 {
1408   Expression expr;
1409 }
1410 {
1411   expr = ListExpression()    {return expr;}
1412 |
1413   expr = PrintExpression()   {return expr;}
1414 }
1415
1416 /**
1417  * Any assignement operator.
1418  * @return the assignement operator id
1419  */
1420 int AssignmentOperator() :
1421 {}
1422 {
1423   <ASSIGN>             {return VariableDeclaration.EQUAL;}
1424 | <STARASSIGN>         {return VariableDeclaration.STAR_EQUAL;}
1425 | <SLASHASSIGN>        {return VariableDeclaration.SLASH_EQUAL;}
1426 | <REMASSIGN>          {return VariableDeclaration.REM_EQUAL;}
1427 | <PLUSASSIGN>         {return VariableDeclaration.PLUS_EQUAL;}
1428 | <MINUSASSIGN>        {return VariableDeclaration.MINUS_EQUAL;}
1429 | <LSHIFTASSIGN>       {return VariableDeclaration.LSHIFT_EQUAL;}
1430 | <RSIGNEDSHIFTASSIGN> {return VariableDeclaration.RSIGNEDSHIFT_EQUAL;}
1431 | <ANDASSIGN>          {return VariableDeclaration.AND_EQUAL;}
1432 | <XORASSIGN>          {return VariableDeclaration.XOR_EQUAL;}
1433 | <ORASSIGN>           {return VariableDeclaration.OR_EQUAL;}
1434 | <DOTASSIGN>          {return VariableDeclaration.DOT_EQUAL;}
1435 | <TILDEEQUAL>         {return VariableDeclaration.TILDE_EQUAL;}
1436 }
1437
1438 Expression ConditionalExpression() :
1439 {
1440   final Expression expr;
1441   Expression expr2 = null;
1442   Expression expr3 = null;
1443 }
1444 {
1445   expr = ConditionalOrExpression() [ <HOOK> expr2 = Expression() <COLON> expr3 = ConditionalExpression() ]
1446 {
1447   if (expr3 == null) {
1448     return expr;
1449   }
1450   return new ConditionalExpression(expr,expr2,expr3);
1451 }
1452 }
1453
1454 Expression ConditionalOrExpression() :
1455 {
1456   Expression expr,expr2;
1457   int operator;
1458 }
1459 {
1460   expr = ConditionalAndExpression()
1461   (
1462     (
1463         <OR_OR> {operator = OperatorIds.OR_OR;}
1464       | <_ORL>  {operator = OperatorIds.ORL;}
1465     )
1466     expr2 = ConditionalAndExpression()
1467     {
1468       expr = new BinaryExpression(expr,expr2,operator);
1469     }
1470   )*
1471   {return expr;}
1472 }
1473
1474 Expression ConditionalAndExpression() :
1475 {
1476   Expression expr,expr2;
1477   int operator;
1478 }
1479 {
1480   expr = ConcatExpression()
1481   (
1482   (  <AND_AND> {operator = OperatorIds.AND_AND;}
1483    | <_ANDL>   {operator = OperatorIds.ANDL;})
1484    expr2 = ConcatExpression() {expr = new BinaryExpression(expr,expr2,operator);}
1485   )*
1486   {return expr;}
1487 }
1488
1489 Expression ConcatExpression() :
1490 {
1491   Expression expr,expr2;
1492 }
1493 {
1494   expr = InclusiveOrExpression()
1495   (
1496     <DOT> expr2 = InclusiveOrExpression()
1497     {expr = new BinaryExpression(expr,expr2,OperatorIds.DOT);}
1498   )*
1499   {return expr;}
1500 }
1501
1502 Expression InclusiveOrExpression() :
1503 {
1504   Expression expr,expr2;
1505 }
1506 {
1507   expr = ExclusiveOrExpression()
1508   (<BIT_OR> expr2 = ExclusiveOrExpression()
1509    {expr = new BinaryExpression(expr,expr2,OperatorIds.OR);}
1510   )*
1511   {return expr;}
1512 }
1513
1514 Expression ExclusiveOrExpression() :
1515 {
1516   Expression expr,expr2;
1517 }
1518 {
1519   expr = AndExpression()
1520   (
1521     <XOR> expr2 = AndExpression()
1522     {expr = new BinaryExpression(expr,expr2,OperatorIds.XOR);}
1523   )*
1524   {return expr;}
1525 }
1526
1527 Expression AndExpression() :
1528 {
1529   Expression expr,expr2;
1530 }
1531 {
1532   expr = EqualityExpression()
1533   (
1534     LOOKAHEAD(1)
1535     <BIT_AND> expr2 = EqualityExpression()
1536     {expr = new BinaryExpression(expr,expr2,OperatorIds.AND);}
1537   )*
1538   {return expr;}
1539 }
1540
1541 Expression EqualityExpression() :
1542 {
1543   Expression expr,expr2;
1544   int operator;
1545 }
1546 {
1547   expr = RelationalExpression()
1548   (
1549   (   <EQUAL_EQUAL>      {operator = OperatorIds.EQUAL_EQUAL;}
1550     | <DIF>              {operator = OperatorIds.DIF;}
1551     | <NOT_EQUAL>        {operator = OperatorIds.DIF;}
1552     | <BANGDOUBLEEQUAL>  {operator = OperatorIds.BANG_EQUAL_EQUAL;}
1553     | <TRIPLEEQUAL>      {operator = OperatorIds.EQUAL_EQUAL_EQUAL;}
1554   )
1555   try {
1556     expr2 = RelationalExpression()
1557   } catch (ParseException e) {
1558     if (errorMessage != null) {
1559       throw e;
1560     }
1561     errorMessage = "unexpected token : '"+ e.currentToken.next.image +"', expression expected";
1562     errorLevel   = ERROR;
1563     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
1564     errorEnd   = SimpleCharStream.getPosition() + 1;
1565     throw e;
1566   }
1567   {
1568     expr = new BinaryExpression(expr,expr2,operator);
1569   }
1570   )*
1571   {return expr;}
1572 }
1573
1574 Expression RelationalExpression() :
1575 {
1576   Expression expr,expr2;
1577   int operator;
1578 }
1579 {
1580   expr = ShiftExpression()
1581   (
1582   ( <LT> {operator = OperatorIds.LESS;}
1583   | <GT> {operator = OperatorIds.GREATER;}
1584   | <LE> {operator = OperatorIds.LESS_EQUAL;}
1585   | <GE> {operator = OperatorIds.GREATER_EQUAL;})
1586    expr2 = ShiftExpression()
1587   {expr = new BinaryExpression(expr,expr2,operator);}
1588   )*
1589   {return expr;}
1590 }
1591
1592 Expression ShiftExpression() :
1593 {
1594   Expression expr,expr2;
1595   int operator;
1596 }
1597 {
1598   expr = AdditiveExpression()
1599   (
1600   ( <LSHIFT>         {operator = OperatorIds.LEFT_SHIFT;}
1601   | <RSIGNEDSHIFT>   {operator = OperatorIds.RIGHT_SHIFT;}
1602   | <RUNSIGNEDSHIFT> {operator = OperatorIds.UNSIGNED_RIGHT_SHIFT;})
1603   expr2 = AdditiveExpression()
1604   {expr = new BinaryExpression(expr,expr2,operator);}
1605   )*
1606   {return expr;}
1607 }
1608
1609 Expression AdditiveExpression() :
1610 {
1611   Expression expr,expr2;
1612   int operator;
1613 }
1614 {
1615   expr = MultiplicativeExpression()
1616   (
1617     LOOKAHEAD(1)
1618      ( <PLUS>  {operator = OperatorIds.PLUS;}
1619      | <MINUS> {operator = OperatorIds.MINUS;}
1620   )
1621    expr2 = MultiplicativeExpression()
1622   {expr = new BinaryExpression(expr,expr2,operator);}
1623    )*
1624   {return expr;}
1625 }
1626
1627 Expression MultiplicativeExpression() :
1628 {
1629   Expression expr,expr2;
1630   int operator;
1631 }
1632 {
1633   try {
1634     expr = UnaryExpression()
1635   } catch (ParseException e) {
1636     if (errorMessage != null) throw e;
1637     errorMessage = "unexpected token '"+e.currentToken.next.image+"'";
1638     errorLevel   = ERROR;
1639     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
1640     errorEnd   = SimpleCharStream.getPosition() + 1;
1641     throw e;
1642   }
1643   (
1644    (  <STAR>      {operator = OperatorIds.MULTIPLY;}
1645     | <SLASH>     {operator = OperatorIds.DIVIDE;}
1646     | <REMAINDER> {operator = OperatorIds.REMAINDER;})
1647     expr2 = UnaryExpression()
1648     {expr = new BinaryExpression(expr,expr2,operator);}
1649   )*
1650   {return expr;}
1651 }
1652
1653 /**
1654  * An unary expression starting with @, & or nothing
1655  */
1656 Expression UnaryExpression() :
1657 {
1658   final Expression expr;
1659 }
1660 {
1661  /* <BIT_AND> expr = UnaryExpressionNoPrefix()             //why did I had that ?
1662   {return new PrefixedUnaryExpression(expr,OperatorIds.AND,pos);}
1663 |      */
1664   expr = AtNotUnaryExpression() {return expr;}
1665 }
1666
1667 /**
1668  * An expression prefixed (or not) by one or more @ and !.
1669  * @return the expression
1670  */
1671 Expression AtNotUnaryExpression() :
1672 {
1673   final Expression expr;
1674   final Token token;
1675 }
1676 {
1677   token = <AT>
1678   expr = AtNotUnaryExpression()
1679   {return new PrefixedUnaryExpression(expr,OperatorIds.AT,token.sourceStart);}
1680 |
1681   token = <BANG>
1682   expr = AtNotUnaryExpression()
1683   {return new PrefixedUnaryExpression(expr,OperatorIds.NOT,token.sourceStart);}
1684 |
1685   expr = UnaryExpressionNoPrefix()
1686   {return expr;}
1687 }
1688
1689
1690 Expression UnaryExpressionNoPrefix() :
1691 {
1692   final Expression expr;
1693   final Token token;
1694 }
1695 {
1696   token = <PLUS> expr = AtNotUnaryExpression()   {return new PrefixedUnaryExpression(expr,
1697                                                                                      OperatorIds.PLUS,
1698                                                                                      token.sourceStart);}
1699 |
1700   token = <MINUS> expr = AtNotUnaryExpression()  {return new PrefixedUnaryExpression(expr,
1701                                                                                      OperatorIds.MINUS,
1702                                                                                      token.sourceStart);}
1703 |
1704   expr = PreIncDecExpression()
1705   {return expr;}
1706 |
1707   expr = UnaryExpressionNotPlusMinus()
1708   {return expr;}
1709 }
1710
1711
1712 Expression PreIncDecExpression() :
1713 {
1714 final Expression expr;
1715 final int operator;
1716 final Token token;
1717 }
1718 {
1719   (
1720       token = <PLUS_PLUS>   {operator = OperatorIds.PLUS_PLUS;}
1721     |
1722       token = <MINUS_MINUS> {operator = OperatorIds.MINUS_MINUS;}
1723   )
1724   expr = PrimaryExpression()
1725   {return new PrefixedUnaryExpression(expr,operator,token.sourceStart);}
1726 }
1727
1728 Expression UnaryExpressionNotPlusMinus() :
1729 {
1730   final Expression expr;
1731 }
1732 {
1733   LOOKAHEAD( <LPAREN> (Type() | <ARRAY>) <RPAREN> )
1734   expr = CastExpression()         {return expr;}
1735 | expr = PostfixExpression()      {return expr;}
1736 | expr = Literal()                {return expr;}
1737 | <LPAREN> expr = Expression()
1738   try {
1739     <RPAREN>
1740   } catch (ParseException e) {
1741     errorMessage = "')' expected";
1742     errorLevel   = ERROR;
1743     errorStart   = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
1744     errorEnd     = SimpleCharStream.getPosition() + 1;
1745     throw e;
1746   }
1747   {return expr;}
1748 }
1749
1750 CastExpression CastExpression() :
1751 {
1752 final ConstantIdentifier type;
1753 final Expression expr;
1754 final Token token,token1;
1755 }
1756 {
1757   token1 = <LPAREN>
1758   (
1759       type = Type()
1760     |
1761       token = <ARRAY> {type = new ConstantIdentifier(token);}
1762   )
1763   <RPAREN> expr = UnaryExpression()
1764   {return new CastExpression(type,expr,token1.sourceStart,expr.sourceEnd);}
1765 }
1766
1767 Expression PostfixExpression() :
1768 {
1769   final Expression expr;
1770   int operator = -1;
1771   Token token = null;
1772 }
1773 {
1774   expr = PrimaryExpression()
1775   [
1776       token = <PLUS_PLUS>   {operator = OperatorIds.PLUS_PLUS;}
1777     |
1778       token = <MINUS_MINUS> {operator = OperatorIds.MINUS_MINUS;}
1779   ]
1780   {
1781     if (operator == -1) {
1782       return expr;
1783     }
1784     return new PostfixedUnaryExpression(expr,operator,token.sourceEnd);
1785   }
1786 }
1787
1788 Expression PrimaryExpression() :
1789 {
1790   Expression expr = null;
1791   Token token = null;
1792 }
1793 {
1794   [token = <BIT_AND>] expr = refPrimaryExpression(token)
1795   {return expr;}
1796 |
1797   expr = ArrayDeclarator()
1798   {return expr;}
1799 }
1800
1801 Expression refPrimaryExpression(final Token reference) :
1802 {
1803   Expression expr = null;
1804   Expression expr2 = null;
1805   int assignOperator = -1;
1806   final Token identifier;
1807   final String var;
1808 }
1809 {
1810   identifier = <IDENTIFIER>
1811   {
1812     expr = new ConstantIdentifier(identifier);
1813   }
1814   (
1815     <STATICCLASSACCESS> expr2 = ClassIdentifier()
1816     {expr = new ClassAccess(expr,
1817                             expr2,
1818                             ClassAccess.STATIC);}
1819   )*
1820   [ expr2 = Arguments(expr) ]
1821   {
1822     if (expr2 == null) {
1823       if (reference != null) {
1824         ParseException e = generateParseException();
1825         errorMessage = "you cannot use a constant by reference";
1826         errorLevel   = ERROR;
1827         errorStart   = reference.sourceStart;
1828         errorEnd     = reference.sourceEnd;
1829         processParseExceptionDebug(e);
1830       }
1831       return expr;
1832     }
1833     return expr2;
1834   }
1835 |
1836   expr = VariableDeclaratorId()  //todo use the reference parameter ...
1837   [ expr = Arguments(expr) ]
1838   {return expr;}
1839 |
1840   token = <NEW>
1841   expr = ClassIdentifier()
1842   {
1843     int start;
1844     if (reference == null) {
1845       start = token.sourceStart;
1846     } else {
1847       start = reference.sourceStart;
1848     }
1849     expr = new ClassInstantiation(expr,
1850                                   reference != null,
1851                                   start);
1852   }
1853   [ expr = Arguments(expr) ]
1854   {return expr;}
1855 }
1856
1857 /**
1858  * An array declarator.
1859  * array(vars)
1860  * @return an array
1861  */
1862 ArrayInitializer ArrayDeclarator() :
1863 {
1864   final ArrayVariableDeclaration[] vars;
1865   final Token token;
1866 }
1867 {
1868   token = <ARRAY> vars = ArrayInitializer()
1869   {return new ArrayInitializer(vars,token.sourceStart,SimpleCharStream.getPosition());}
1870 }
1871
1872 Expression ClassIdentifier():
1873 {
1874   final Expression expr;
1875   final Token token;
1876   final ConstantIdentifier type;
1877 }
1878 {
1879   token = <IDENTIFIER>          {return new ConstantIdentifier(token);}
1880 | expr = Type()                 {return expr;}
1881 | expr = VariableDeclaratorId() {return expr;}
1882 }
1883
1884 /**
1885  * Used by Variabledeclaratorid and primarysuffix
1886  */
1887 AbstractVariable VariableSuffix(final AbstractVariable prefix) :
1888 {
1889   Variable expr = null;
1890   final int pos = SimpleCharStream.getPosition();
1891   Expression expression = null;
1892 }
1893 {
1894   <CLASSACCESS>
1895   try {
1896     expression = VariableName()
1897   } catch (ParseException e) {
1898     errorMessage = "unexpected token : '"+ e.currentToken.next.image +"', function call or field access expected";
1899     errorLevel   = ERROR;
1900     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
1901     errorEnd   = SimpleCharStream.getPosition() + 1;
1902     throw e;
1903   }
1904   {return new ClassAccess(prefix,
1905                           expression,
1906                           ClassAccess.NORMAL);}
1907 |
1908   <LBRACKET> [ expression = Expression() | expression = Type() ]  //Not good
1909   try {
1910     <RBRACKET>
1911   } catch (ParseException e) {
1912     errorMessage = "']' expected";
1913     errorLevel   = ERROR;
1914     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
1915     errorEnd   = SimpleCharStream.getPosition() + 1;
1916     throw e;
1917   }
1918   {return new ArrayDeclarator(prefix,expression,SimpleCharStream.getPosition());}
1919 }
1920
1921 Literal Literal() :
1922 {
1923   final Token token;
1924 }
1925 {
1926   token = <INTEGER_LITERAL>        {return new NumberLiteral(token);}
1927 | token = <FLOATING_POINT_LITERAL> {return new NumberLiteral(token);}
1928 | token = <STRING_LITERAL>         {return new StringLiteral(token);}
1929 | token = <TRUE>                   {return new TrueLiteral(token);}
1930 | token = <FALSE>                  {return new FalseLiteral(token);}
1931 | token = <NULL>                   {return new NullLiteral(token);}
1932 }
1933
1934 FunctionCall Arguments(final Expression func) :
1935 {
1936 Expression[] args = null;
1937 final Token token;
1938 }
1939 {
1940   <LPAREN> [ args = ArgumentList() ]
1941   try {
1942     token = <RPAREN>
1943   } catch (ParseException e) {
1944     errorMessage = "unexpected token : '"+ e.currentToken.next.image +"', ')' expected to close the argument list";
1945     errorLevel   = ERROR;
1946     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
1947     errorEnd   = SimpleCharStream.getPosition() + 1;
1948     throw e;
1949   }
1950   {return new FunctionCall(func,args,token.sourceEnd);}
1951 }
1952
1953 /**
1954  * An argument list is a list of arguments separated by comma :
1955  * argumentDeclaration() (, argumentDeclaration)*
1956  * @return an array of arguments
1957  */
1958 Expression[] ArgumentList() :
1959 {
1960 Expression arg;
1961 final ArrayList list = new ArrayList();
1962 }
1963 {
1964   arg = Expression()
1965   {list.add(arg);}
1966   ( <COMMA>
1967       try {
1968         arg = Expression()
1969         {list.add(arg);}
1970       } catch (ParseException e) {
1971         errorMessage = "unexpected token : '"+ e.currentToken.next.image +"'. An expression expected after a comma in argument list";
1972         errorLevel   = ERROR;
1973         errorStart   = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
1974         errorEnd     = SimpleCharStream.getPosition() + 1;
1975         throw e;
1976       }
1977    )*
1978    {
1979    final Expression[] arguments = new Expression[list.size()];
1980    list.toArray(arguments);
1981    return arguments;}
1982 }
1983
1984 /**
1985  * A Statement without break.
1986  * @return a statement
1987  */
1988 Statement StatementNoBreak() :
1989 {
1990   final Statement statement;
1991   Token token = null;
1992 }
1993 {
1994   LOOKAHEAD(2)
1995   statement = expressionStatement()     {return statement;}
1996 | LOOKAHEAD(1)
1997   statement = LabeledStatement()        {return statement;}
1998 | statement = Block()                   {return statement;}
1999 | statement = EmptyStatement()          {return statement;}
2000 | statement = SwitchStatement()         {return statement;}
2001 | statement = IfStatement()             {return statement;}
2002 | statement = WhileStatement()          {return statement;}
2003 | statement = DoStatement()             {return statement;}
2004 | statement = ForStatement()            {return statement;}
2005 | statement = ForeachStatement()        {return statement;}
2006 | statement = ContinueStatement()       {return statement;}
2007 | statement = ReturnStatement()         {return statement;}
2008 | statement = EchoStatement()           {return statement;}
2009 | [token=<AT>] statement = IncludeStatement()
2010   {if (token != null) {
2011     ((InclusionStatement)statement).silent = true;
2012     statement.sourceStart = token.sourceStart;
2013   }
2014   return statement;}
2015 | statement = StaticStatement()         {return statement;}
2016 | statement = GlobalStatement()         {return statement;}
2017 | statement = defineStatement()         {currentSegment.add((Outlineable)statement);return statement;}
2018 }
2019
2020 /**
2021  * A statement expression.
2022  * expression ;
2023  * @return an expression
2024  */
2025 Statement expressionStatement() :
2026 {
2027   final Statement statement;
2028   final Token token;
2029 }
2030 {
2031   statement = Expression()
2032   try {
2033     token = <SEMICOLON>
2034     {statement.sourceEnd = token.sourceEnd;}
2035   } catch (ParseException e) {
2036     if (e.currentToken.next.kind != PHPParserConstants.PHPEND) {
2037       errorMessage = "unexpected token : '"+ e.currentToken.next.image +"'. A ';' was expected";
2038       errorLevel   = ERROR;
2039       errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2040       errorEnd   = SimpleCharStream.getPosition() + 1;
2041       throw e;
2042     }
2043   }
2044   {return statement;}
2045 }
2046
2047 Define defineStatement() :
2048 {
2049   final int start = SimpleCharStream.getPosition();
2050   Expression defineName,defineValue;
2051 }
2052 {
2053   <DEFINE>
2054   try {
2055     <LPAREN>
2056   } catch (ParseException e) {
2057     errorMessage = "unexpected token : '"+ e.currentToken.next.image +"', '(' expected";
2058     errorLevel   = ERROR;
2059     errorStart   = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2060     errorEnd     = SimpleCharStream.getPosition() + 1;
2061     processParseExceptionDebug(e);
2062   }
2063   try {
2064     defineName = Expression()
2065   } catch (ParseException e) {
2066     errorMessage = "unexpected token : '"+ e.currentToken.next.image +"', expression expected";
2067     errorLevel   = ERROR;
2068     errorStart   = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2069     errorEnd     = SimpleCharStream.getPosition() + 1;
2070     throw e;
2071   }
2072   try {
2073     <COMMA>
2074   } catch (ParseException e) {
2075     errorMessage = "unexpected token : '"+ e.currentToken.next.image +"', ',' expected";
2076     errorLevel   = ERROR;
2077     errorStart   = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2078     errorEnd     = SimpleCharStream.getPosition() + 1;
2079     processParseExceptionDebug(e);
2080   }
2081   try {
2082     defineValue = Expression()
2083   } catch (ParseException e) {
2084     errorMessage = "unexpected token : '"+ e.currentToken.next.image +"', expression expected";
2085     errorLevel   = ERROR;
2086     errorStart   = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2087     errorEnd     = SimpleCharStream.getPosition() + 1;
2088     throw e;
2089   }
2090   try {
2091     <RPAREN>
2092   } catch (ParseException e) {
2093     errorMessage = "unexpected token : '"+ e.currentToken.next.image +"', ')' expected";
2094     errorLevel   = ERROR;
2095     errorStart   = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2096     errorEnd     = SimpleCharStream.getPosition() + 1;
2097     processParseExceptionDebug(e);
2098   }
2099   {return new Define(currentSegment,
2100                      defineName,
2101                      defineValue,
2102                      start,
2103                      SimpleCharStream.getPosition());}
2104 }
2105
2106 /**
2107  * A Normal statement.
2108  */
2109 Statement Statement() :
2110 {
2111   final Statement statement;
2112 }
2113 {
2114   statement = StatementNoBreak() {return statement;}
2115 | statement = BreakStatement()   {return statement;}
2116 }
2117
2118 /**
2119  * An html block inside a php syntax.
2120  */
2121 HTMLBlock htmlBlock() :
2122 {
2123   final int startIndex = nodePtr;
2124   final AstNode[] blockNodes;
2125   final int nbNodes;
2126 }
2127 {
2128   <PHPEND> (phpEchoBlock())*
2129   try {
2130     (<PHPSTARTLONG> | <PHPSTARTSHORT>)
2131   } catch (ParseException e) {
2132     errorMessage = "unexpected end of file , '<?php' expected";
2133     errorLevel   = ERROR;
2134     errorStart   = SimpleCharStream.getPosition();
2135     errorEnd     = SimpleCharStream.getPosition();
2136     throw e;
2137   }
2138   {
2139   nbNodes    = nodePtr - startIndex;
2140   blockNodes = new AstNode[nbNodes];
2141   System.arraycopy(nodes,startIndex,blockNodes,0,nbNodes);
2142   nodePtr = startIndex;
2143   return new HTMLBlock(blockNodes);}
2144 }
2145
2146 /**
2147  * An include statement. It's "include" an expression;
2148  */
2149 InclusionStatement IncludeStatement() :
2150 {
2151   final Expression expr;
2152   final int keyword;
2153   final InclusionStatement inclusionStatement;
2154   final Token token, token2;
2155 }
2156 {
2157       (  token = <REQUIRE>      {keyword = InclusionStatement.REQUIRE;}
2158        | token = <REQUIRE_ONCE> {keyword = InclusionStatement.REQUIRE_ONCE;}
2159        | token = <INCLUDE>      {keyword = InclusionStatement.INCLUDE;}
2160        | token = <INCLUDE_ONCE> {keyword = InclusionStatement.INCLUDE_ONCE;})
2161   try {
2162     expr = Expression()
2163   } catch (ParseException e) {
2164     if (errorMessage != null) {
2165       throw e;
2166     }
2167     errorMessage = "unexpected token '"+ e.currentToken.next.image+"', expression expected";
2168     errorLevel   = ERROR;
2169     errorStart   = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2170     errorEnd     = SimpleCharStream.getPosition() + 1;
2171     throw e;
2172   }
2173   {inclusionStatement = new InclusionStatement(currentSegment,
2174                                                keyword,
2175                                                expr,
2176                                                token.sourceStart);
2177    currentSegment.add(inclusionStatement);
2178   }
2179   try {
2180     token2 = <SEMICOLON>
2181   } catch (ParseException e) {
2182     errorMessage = "unexpected token : '"+ e.currentToken.next.image +"'. A ';' was expected";
2183     errorLevel   = ERROR;
2184     errorStart   = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2185     errorEnd     = SimpleCharStream.getPosition() + 1;
2186     throw e;
2187   }
2188   {inclusionStatement.sourceEnd = token2.sourceEnd;
2189   return inclusionStatement;}
2190 }
2191
2192 PrintExpression PrintExpression() :
2193 {
2194   final Expression expr;
2195   final int pos = SimpleCharStream.getPosition();
2196 }
2197 {
2198   <PRINT> expr = Expression() {return new PrintExpression(expr,pos,SimpleCharStream.getPosition());}
2199 }
2200
2201 ListExpression ListExpression() :
2202 {
2203   Expression expr = null;
2204   final Expression expression;
2205   final ArrayList list = new ArrayList();
2206   final int pos = SimpleCharStream.getPosition();
2207 }
2208 {
2209   <LIST>
2210   try {
2211     <LPAREN>
2212   } catch (ParseException e) {
2213     errorMessage = "unexpected token : '"+ e.currentToken.next.image +"', '(' expected";
2214     errorLevel   = ERROR;
2215     errorStart   = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2216     errorEnd     = SimpleCharStream.getPosition() + 1;
2217     throw e;
2218   }
2219   [
2220     expr = VariableDeclaratorId()
2221     {list.add(expr);}
2222   ]
2223   {if (expr == null) list.add(null);}
2224   (
2225     try {
2226       <COMMA>
2227     } catch (ParseException e) {
2228       errorMessage = "unexpected token : '"+ e.currentToken.next.image +"', ',' expected";
2229       errorLevel   = ERROR;
2230       errorStart   = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2231       errorEnd     = SimpleCharStream.getPosition() + 1;
2232       throw e;
2233     }
2234     [expr = VariableDeclaratorId() {list.add(expr);}]
2235   )*
2236   try {
2237     <RPAREN>
2238   } catch (ParseException e) {
2239     errorMessage = "unexpected token : '"+ e.currentToken.next.image +"', ')' expected";
2240     errorLevel   = ERROR;
2241     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2242     errorEnd   = SimpleCharStream.getPosition() + 1;
2243     throw e;
2244   }
2245   [ <ASSIGN> expression = Expression()
2246     {
2247     final Variable[] vars = new Variable[list.size()];
2248     list.toArray(vars);
2249     return new ListExpression(vars,
2250                               expression,
2251                               pos,
2252                               SimpleCharStream.getPosition());}
2253   ]
2254   {
2255     final Variable[] vars = new Variable[list.size()];
2256     list.toArray(vars);
2257     return new ListExpression(vars,pos,SimpleCharStream.getPosition());}
2258 }
2259
2260 /**
2261  * An echo statement.
2262  * echo anyexpression (, otherexpression)*
2263  */
2264 EchoStatement EchoStatement() :
2265 {
2266   final ArrayList expressions = new ArrayList();
2267   Expression expr;
2268   Token token;
2269   Token token2 = null;
2270 }
2271 {
2272   token = <ECHO> expr = Expression()
2273   {expressions.add(expr);}
2274   (
2275     <COMMA> expr = Expression()
2276     {expressions.add(expr);}
2277   )*
2278   try {
2279     token2 = <SEMICOLON>
2280   } catch (ParseException e) {
2281     if (e.currentToken.next.kind != 4) {
2282       errorMessage = "';' expected after 'echo' statement";
2283       errorLevel   = ERROR;
2284       errorStart   = e.currentToken.sourceEnd;
2285       errorEnd     = e.currentToken.sourceEnd;
2286       processParseExceptionDebug(e);
2287     }
2288   }
2289   {
2290    final Expression[] exprs = new Expression[expressions.size()];
2291    expressions.toArray(exprs);
2292    if (token2 == null) {
2293      return new EchoStatement(exprs,token.sourceStart, exprs[exprs.length-1].sourceEnd);
2294    }
2295    return new EchoStatement(exprs,token.sourceStart, token2.sourceEnd);
2296    }
2297 }
2298
2299 GlobalStatement GlobalStatement() :
2300 {
2301    Variable expr;
2302    final ArrayList vars = new ArrayList();
2303    final GlobalStatement global;
2304    final Token token, token2;
2305 }
2306 {
2307   token = <GLOBAL>
2308     expr = Variable()
2309     {vars.add(expr);}
2310   (<COMMA>
2311     expr = Variable()
2312     {vars.add(expr);}
2313   )*
2314   try {
2315     token2 = <SEMICOLON>
2316     {
2317     final Variable[] variables = new Variable[vars.size()];
2318     vars.toArray(variables);
2319     global = new GlobalStatement(currentSegment,
2320                                  variables,
2321                                  token.sourceStart,
2322                                  token2.sourceEnd);
2323     currentSegment.add(global);
2324     return global;}
2325   } catch (ParseException e) {
2326     errorMessage = "unexpected token : '"+ e.currentToken.next.image +"'. a ';' was expected";
2327     errorLevel   = ERROR;
2328     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2329     errorEnd   = SimpleCharStream.getPosition() + 1;
2330     throw e;
2331   }
2332 }
2333
2334 StaticStatement StaticStatement() :
2335 {
2336   final ArrayList vars = new ArrayList();
2337   VariableDeclaration expr;
2338   final Token token, token2;
2339 }
2340 {
2341   token = <STATIC> expr = VariableDeclarator() {vars.add(expr);}
2342   (
2343     <COMMA> expr = VariableDeclarator() {vars.add(expr);}
2344   )*
2345   try {
2346     token2 = <SEMICOLON>
2347     {
2348     final VariableDeclaration[] variables = new VariableDeclaration[vars.size()];
2349     vars.toArray(variables);
2350     return new StaticStatement(variables,
2351                                token.sourceStart,
2352                                token2.sourceEnd);}
2353   } catch (ParseException e) {
2354     errorMessage = "unexpected token : '"+ e.currentToken.next.image +"'. a ';' was expected";
2355     errorLevel   = ERROR;
2356     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2357     errorEnd   = SimpleCharStream.getPosition() + 1;
2358     throw e;
2359   }
2360 }
2361
2362 LabeledStatement LabeledStatement() :
2363 {
2364   final Token label;
2365   final Statement statement;
2366 }
2367 {
2368   label = <IDENTIFIER> <COLON> statement = Statement()
2369   {return new LabeledStatement(label.image,statement,label.sourceStart,statement.sourceEnd);}
2370 }
2371
2372 /**
2373  * A Block is
2374  * {
2375  * statements
2376  * }.
2377  * @return a block
2378  */
2379 Block Block() :
2380 {
2381   final ArrayList list = new ArrayList();
2382   Statement statement;
2383   final Token token, token2;
2384 }
2385 {
2386   try {
2387     token = <LBRACE>
2388   } catch (ParseException e) {
2389     errorMessage = "'{' expected";
2390     errorLevel   = ERROR;
2391     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2392     errorEnd   = SimpleCharStream.getPosition() + 1;
2393     throw e;
2394   }
2395   ( statement = BlockStatement() {list.add(statement);}
2396   | statement = htmlBlock()      {list.add(statement);})*
2397   try {
2398     token2 = <RBRACE>
2399   } catch (ParseException e) {
2400     errorMessage = "unexpected token : '"+ e.currentToken.image +"', '}' expected";
2401     errorLevel   = ERROR;
2402     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2403     errorEnd   = SimpleCharStream.getPosition() + 1;
2404     throw e;
2405   }
2406   {
2407   final Statement[] statements = new Statement[list.size()];
2408   list.toArray(statements);
2409   return new Block(statements,token.sourceStart,token2.sourceEnd);}
2410 }
2411
2412 Statement BlockStatement() :
2413 {
2414   final Statement statement;
2415 }
2416 {
2417   try {
2418     statement = Statement()         {if (phpDocument == currentSegment) pushOnAstNodes(statement);
2419                                      return statement;}
2420   } catch (ParseException e) {
2421     errorMessage = "unexpected token : '"+ e.currentToken.image +"', a statement was expected";
2422     errorLevel   = ERROR;
2423     errorStart = e.currentToken.sourceStart;
2424     errorEnd   = e.currentToken.sourceEnd;
2425     throw e;
2426   }
2427 | statement = ClassDeclaration()  {return statement;}
2428 | statement = MethodDeclaration() {if (phpDocument == currentSegment) pushOnAstNodes(statement);
2429                                    currentSegment.add((MethodDeclaration) statement);
2430                                    ((MethodDeclaration) statement).analyzeCode();
2431                                    return statement;}
2432 }
2433
2434 /**
2435  * A Block statement that will not contain any 'break'
2436  */
2437 Statement BlockStatementNoBreak() :
2438 {
2439   final Statement statement;
2440 }
2441 {
2442   statement = StatementNoBreak()  {return statement;}
2443 | statement = ClassDeclaration()  {return statement;}
2444 | statement = MethodDeclaration() {currentSegment.add((MethodDeclaration) statement);
2445                                    ((MethodDeclaration) statement).analyzeCode();
2446                                    return statement;}
2447 }
2448
2449 /**
2450  * used only by ForInit()
2451  */
2452 VariableDeclaration[] LocalVariableDeclaration() :
2453 {
2454   final ArrayList list = new ArrayList();
2455   VariableDeclaration var;
2456 }
2457 {
2458   var = LocalVariableDeclarator()
2459   {list.add(var);}
2460   ( <COMMA> var = LocalVariableDeclarator() {list.add(var);})*
2461   {
2462     final VariableDeclaration[] vars = new VariableDeclaration[list.size()];
2463     list.toArray(vars);
2464   return vars;}
2465 }
2466
2467 /**
2468  * used only by LocalVariableDeclaration().
2469  */
2470 VariableDeclaration LocalVariableDeclarator() :
2471 {
2472   final Variable varName;
2473   Expression initializer = null;
2474 }
2475 {
2476   varName = Variable() [ <ASSIGN> initializer = Expression() ]
2477   {
2478    if (initializer == null) {
2479     return new VariableDeclaration(currentSegment,
2480                                    varName,
2481                                    varName.sourceStart,
2482                                    varName.sourceEnd);
2483    }
2484     return new VariableDeclaration(currentSegment,
2485                                    varName,
2486                                    initializer,
2487                                    VariableDeclaration.EQUAL,
2488                                    varName.sourceStart);
2489   }
2490 }
2491
2492 EmptyStatement EmptyStatement() :
2493 {
2494   final Token token;
2495 }
2496 {
2497   token = <SEMICOLON>
2498   {return new EmptyStatement(token.sourceStart,token.sourceEnd);}
2499 }
2500
2501 /**
2502  * used only by StatementExpressionList() which is used only by ForInit() and ForStatement()
2503  */
2504 Expression StatementExpression() :
2505 {
2506   final Expression expr,expr2;
2507   final int operator;
2508 }
2509 {
2510   expr = PreIncDecExpression() {return expr;}
2511 |
2512   expr = PrimaryExpression()
2513   [ <PLUS_PLUS> {return new PostfixedUnaryExpression(expr,
2514                                                      OperatorIds.PLUS_PLUS,
2515                                                      SimpleCharStream.getPosition());}
2516   | <MINUS_MINUS> {return new PostfixedUnaryExpression(expr,
2517                                                        OperatorIds.MINUS_MINUS,
2518                                                        SimpleCharStream.getPosition());}
2519   ]
2520   {return expr;}
2521 }
2522
2523 SwitchStatement SwitchStatement() :
2524 {
2525   final Expression variable;
2526   final AbstractCase[] cases;
2527   final int pos = SimpleCharStream.getPosition();
2528 }
2529 {
2530   <SWITCH>
2531   try {
2532     <LPAREN>
2533   } catch (ParseException e) {
2534     errorMessage = "'(' expected after 'switch'";
2535     errorLevel   = ERROR;
2536     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2537     errorEnd   = SimpleCharStream.getPosition() + 1;
2538     throw e;
2539   }
2540   try {
2541     variable = Expression()
2542   } catch (ParseException e) {
2543     if (errorMessage != null) {
2544       throw e;
2545     }
2546     errorMessage = "expression expected";
2547     errorLevel   = ERROR;
2548     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2549     errorEnd   = SimpleCharStream.getPosition() + 1;
2550     throw e;
2551   }
2552   try {
2553     <RPAREN>
2554   } catch (ParseException e) {
2555     errorMessage = "')' expected";
2556     errorLevel   = ERROR;
2557     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2558     errorEnd   = SimpleCharStream.getPosition() + 1;
2559     throw e;
2560   }
2561   (cases = switchStatementBrace() | cases = switchStatementColon(pos, pos + 6))
2562   {return new SwitchStatement(variable,cases,pos,SimpleCharStream.getPosition());}
2563 }
2564
2565 AbstractCase[] switchStatementBrace() :
2566 {
2567   AbstractCase cas;
2568   final ArrayList cases = new ArrayList();
2569 }
2570 {
2571   <LBRACE>
2572  ( cas = switchLabel0() {cases.add(cas);})*
2573   try {
2574     <RBRACE>
2575     {
2576     final AbstractCase[] abcase = new AbstractCase[cases.size()];
2577     cases.toArray(abcase);
2578     return abcase;}
2579   } catch (ParseException e) {
2580     errorMessage = "'}' expected";
2581     errorLevel   = ERROR;
2582     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2583     errorEnd   = SimpleCharStream.getPosition() + 1;
2584     throw e;
2585   }
2586 }
2587 /**
2588  * A Switch statement with : ... endswitch;
2589  * @param start the begin offset of the switch
2590  * @param end the end offset of the switch
2591  */
2592 AbstractCase[] switchStatementColon(final int start, final int end) :
2593 {
2594   AbstractCase cas;
2595   final ArrayList cases = new ArrayList();
2596 }
2597 {
2598   <COLON>
2599   {try {
2600   setMarker(fileToParse,
2601             "Ugly syntax detected, you should switch () {...} instead of switch (): ... enswitch;",
2602             start,
2603             end,
2604             INFO,
2605             "Line " + token.beginLine);
2606   } catch (CoreException e) {
2607     PHPeclipsePlugin.log(e);
2608   }}
2609   ( cas = switchLabel0() {cases.add(cas);})*
2610   try {
2611     <ENDSWITCH>
2612   } catch (ParseException e) {
2613     errorMessage = "'endswitch' expected";
2614     errorLevel   = ERROR;
2615     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2616     errorEnd   = SimpleCharStream.getPosition() + 1;
2617     throw e;
2618   }
2619   try {
2620     <SEMICOLON>
2621     {
2622     final AbstractCase[] abcase = new AbstractCase[cases.size()];
2623     cases.toArray(abcase);
2624     return abcase;}
2625   } catch (ParseException e) {
2626     errorMessage = "';' expected after 'endswitch' keyword";
2627     errorLevel   = ERROR;
2628     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2629     errorEnd   = SimpleCharStream.getPosition() + 1;
2630     throw e;
2631   }
2632 }
2633
2634 AbstractCase switchLabel0() :
2635 {
2636   final Expression expr;
2637   Statement statement;
2638   final ArrayList stmts = new ArrayList();
2639   final int pos = SimpleCharStream.getPosition();
2640 }
2641 {
2642   expr = SwitchLabel()
2643   ( statement = BlockStatementNoBreak() {stmts.add(statement);}
2644   | statement = htmlBlock()             {stmts.add(statement);})*
2645   [ statement = BreakStatement()        {stmts.add(statement);}]
2646   {
2647   final Statement[] stmtsArray = new Statement[stmts.size()];
2648   stmts.toArray(stmtsArray);
2649   if (expr == null) {//it's a default
2650     return new DefaultCase(stmtsArray,pos,SimpleCharStream.getPosition());
2651   }
2652   return new Case(expr,stmtsArray,pos,SimpleCharStream.getPosition());}
2653 }
2654
2655 /**
2656  * A SwitchLabel.
2657  * case Expression() :
2658  * default :
2659  * @return the if it was a case and null if not
2660  */
2661 Expression SwitchLabel() :
2662 {
2663   final Expression expr;
2664 }
2665 {
2666   token = <CASE>
2667   try {
2668     expr = Expression()
2669   } catch (ParseException e) {
2670     if (errorMessage != null) throw e;
2671     errorMessage = "expression expected after 'case' keyword";
2672     errorLevel   = ERROR;
2673     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2674     errorEnd   = SimpleCharStream.getPosition() + 1;
2675     throw e;
2676   }
2677   try {
2678     <COLON>
2679     {return expr;}
2680   } catch (ParseException e) {
2681     errorMessage = "':' expected after case expression";
2682     errorLevel   = ERROR;
2683     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2684     errorEnd   = SimpleCharStream.getPosition() + 1;
2685     throw e;
2686   }
2687 |
2688   token = <_DEFAULT>
2689   try {
2690     <COLON>
2691     {return null;}
2692   } catch (ParseException e) {
2693     errorMessage = "':' expected after 'default' keyword";
2694     errorLevel   = ERROR;
2695     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2696     errorEnd   = SimpleCharStream.getPosition() + 1;
2697     throw e;
2698   }
2699 }
2700
2701 Break BreakStatement() :
2702 {
2703   Expression expression = null;
2704   final Token token, token2;
2705 }
2706 {
2707   token = <BREAK> [ expression = Expression() ]
2708   try {
2709     token2 = <SEMICOLON>
2710   } catch (ParseException e) {
2711     errorMessage = "';' expected after 'break' keyword";
2712     errorLevel   = ERROR;
2713     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2714     errorEnd   = SimpleCharStream.getPosition() + 1;
2715     throw e;
2716   }
2717   {return new Break(expression, token.sourceStart, token2.sourceEnd);}
2718 }
2719
2720 IfStatement IfStatement() :
2721 {
2722   final int pos = SimpleCharStream.getPosition();
2723   final Expression condition;
2724   final IfStatement ifStatement;
2725   Token token;
2726 }
2727 {
2728   token = <IF> condition = Condition("if") ifStatement = IfStatement0(condition,
2729                                                                       token.sourceStart,token.sourceStart+2)
2730   {return ifStatement;}
2731 }
2732
2733
2734 Expression Condition(final String keyword) :
2735 {
2736   final Expression condition;
2737 }
2738 {
2739   try {
2740     <LPAREN>
2741   } catch (ParseException e) {
2742     errorMessage = "'(' expected after " + keyword + " keyword";
2743     errorLevel   = ERROR;
2744     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length();
2745     errorEnd   = errorStart +1;
2746     processParseExceptionDebug(e);
2747   }
2748   condition = Expression()
2749   try {
2750      <RPAREN>
2751   } catch (ParseException e) {
2752     errorMessage = "')' expected after " + keyword + " keyword";
2753     errorLevel   = ERROR;
2754     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2755     errorEnd   = SimpleCharStream.getPosition() + 1;
2756     processParseExceptionDebug(e);
2757   }
2758   {return condition;}
2759 }
2760
2761 IfStatement IfStatement0(final Expression condition, final int start,final int end) :
2762 {
2763   Statement statement;
2764   final Statement stmt;
2765   final Statement[] statementsArray;
2766   ElseIf elseifStatement;
2767   Else elseStatement = null;
2768   final ArrayList stmts;
2769   final ArrayList elseIfList = new ArrayList();
2770   final ElseIf[] elseIfs;
2771   int pos = SimpleCharStream.getPosition();
2772   final int endStatements;
2773 }
2774 {
2775   <COLON>
2776   {stmts = new ArrayList();}
2777   (  statement = Statement() {stmts.add(statement);}
2778    | statement = htmlBlock() {stmts.add(statement);})*
2779    {endStatements = SimpleCharStream.getPosition();}
2780    (elseifStatement = ElseIfStatementColon() {elseIfList.add(elseifStatement);})*
2781    [elseStatement = ElseStatementColon()]
2782
2783   {try {
2784   setMarker(fileToParse,
2785             "Ugly syntax detected, you should if () {...} instead of if (): ... endif;",
2786             start,
2787             end,
2788             INFO,
2789             "Line " + token.beginLine);
2790   } catch (CoreException e) {
2791     PHPeclipsePlugin.log(e);
2792   }}
2793   try {
2794     <ENDIF>
2795   } catch (ParseException e) {
2796     errorMessage = "'endif' expected";
2797     errorLevel   = ERROR;
2798     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2799     errorEnd   = SimpleCharStream.getPosition() + 1;
2800     throw e;
2801   }
2802   try {
2803     <SEMICOLON>
2804   } catch (ParseException e) {
2805     errorMessage = "';' expected after 'endif' keyword";
2806     errorLevel   = ERROR;
2807     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2808     errorEnd   = SimpleCharStream.getPosition() + 1;
2809     throw e;
2810   }
2811     {
2812     elseIfs = new ElseIf[elseIfList.size()];
2813     elseIfList.toArray(elseIfs);
2814     if (stmts.size() == 1) {
2815       return new IfStatement(condition,
2816                              (Statement) stmts.get(0),
2817                               elseIfs,
2818                               elseStatement,
2819                               pos,
2820                               SimpleCharStream.getPosition());
2821     } else {
2822       statementsArray = new Statement[stmts.size()];
2823       stmts.toArray(statementsArray);
2824       return new IfStatement(condition,
2825                              new Block(statementsArray,pos,endStatements),
2826                              elseIfs,
2827                              elseStatement,
2828                              pos,
2829                              SimpleCharStream.getPosition());
2830     }
2831     }
2832
2833 |
2834   (stmt = Statement() | stmt = htmlBlock())
2835   ( LOOKAHEAD(1) elseifStatement = ElseIfStatement() {elseIfList.add(elseifStatement);})*
2836   [ LOOKAHEAD(1)
2837     <ELSE>
2838     try {
2839       {pos = SimpleCharStream.getPosition();}
2840       statement = Statement()
2841       {elseStatement = new Else(statement,pos,SimpleCharStream.getPosition());}
2842     } catch (ParseException e) {
2843       if (errorMessage != null) {
2844         throw e;
2845       }
2846       errorMessage = "unexpected token '"+e.currentToken.next.image+"', a statement was expected";
2847       errorLevel   = ERROR;
2848       errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2849       errorEnd   = SimpleCharStream.getPosition() + 1;
2850       throw e;
2851     }
2852   ]
2853   {
2854     elseIfs = new ElseIf[elseIfList.size()];
2855     elseIfList.toArray(elseIfs);
2856     return new IfStatement(condition,
2857                            stmt,
2858                            elseIfs,
2859                            elseStatement,
2860                            pos,
2861                            SimpleCharStream.getPosition());}
2862 }
2863
2864 ElseIf ElseIfStatementColon() :
2865 {
2866   final Expression condition;
2867   Statement statement;
2868   final ArrayList list = new ArrayList();
2869   final int pos = SimpleCharStream.getPosition();
2870 }
2871 {
2872   <ELSEIF> condition = Condition("elseif")
2873   <COLON> (  statement = Statement() {list.add(statement);}
2874            | statement = htmlBlock() {list.add(statement);})*
2875   {
2876   final Statement[] stmtsArray = new Statement[list.size()];
2877   list.toArray(stmtsArray);
2878   return new ElseIf(condition,stmtsArray ,pos,SimpleCharStream.getPosition());}
2879 }
2880
2881 Else ElseStatementColon() :
2882 {
2883   Statement statement;
2884   final ArrayList list = new ArrayList();
2885   final int pos = SimpleCharStream.getPosition();
2886 }
2887 {
2888   <ELSE> <COLON> (  statement = Statement() {list.add(statement);}
2889                   | statement = htmlBlock() {list.add(statement);})*
2890   {
2891   final Statement[] stmtsArray = new Statement[list.size()];
2892   list.toArray(stmtsArray);
2893   return new Else(stmtsArray,pos,SimpleCharStream.getPosition());}
2894 }
2895
2896 ElseIf ElseIfStatement() :
2897 {
2898   final Expression condition;
2899   final Statement statement;
2900   final ArrayList list = new ArrayList();
2901   final int pos = SimpleCharStream.getPosition();
2902 }
2903 {
2904   <ELSEIF> condition = Condition("elseif") statement = Statement() {list.add(statement);/*todo:do better*/}
2905   {
2906   final Statement[] stmtsArray = new Statement[list.size()];
2907   list.toArray(stmtsArray);
2908   return new ElseIf(condition,stmtsArray,pos,SimpleCharStream.getPosition());}
2909 }
2910
2911 WhileStatement WhileStatement() :
2912 {
2913   final Expression condition;
2914   final Statement action;
2915   final int pos = SimpleCharStream.getPosition();
2916 }
2917 {
2918   <WHILE>
2919     condition = Condition("while")
2920     action    = WhileStatement0(pos,pos + 5)
2921     {return new WhileStatement(condition,action,pos,SimpleCharStream.getPosition());}
2922 }
2923
2924 Statement WhileStatement0(final int start, final int end) :
2925 {
2926   Statement statement;
2927   final ArrayList stmts = new ArrayList();
2928   final int pos = SimpleCharStream.getPosition();
2929 }
2930 {
2931   <COLON> (statement = Statement() {stmts.add(statement);})*
2932   {try {
2933   setMarker(fileToParse,
2934             "Ugly syntax detected, you should while () {...} instead of while (): ... endwhile;",
2935             start,
2936             end,
2937             INFO,
2938             "Line " + token.beginLine);
2939   } catch (CoreException e) {
2940     PHPeclipsePlugin.log(e);
2941   }}
2942   try {
2943     <ENDWHILE>
2944   } catch (ParseException e) {
2945     errorMessage = "'endwhile' expected";
2946     errorLevel   = ERROR;
2947     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2948     errorEnd   = SimpleCharStream.getPosition() + 1;
2949     throw e;
2950   }
2951   try {
2952     <SEMICOLON>
2953     {
2954     final Statement[] stmtsArray = new Statement[stmts.size()];
2955     stmts.toArray(stmtsArray);
2956     return new Block(stmtsArray,pos,SimpleCharStream.getPosition());}
2957   } catch (ParseException e) {
2958     errorMessage = "';' expected after 'endwhile' keyword";
2959     errorLevel   = ERROR;
2960     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2961     errorEnd   = SimpleCharStream.getPosition() + 1;
2962     throw e;
2963   }
2964 |
2965   statement = Statement()
2966   {return statement;}
2967 }
2968
2969 DoStatement DoStatement() :
2970 {
2971   final Statement action;
2972   final Expression condition;
2973   final Token token, token2;
2974 }
2975 {
2976   token = <DO> action = Statement() <WHILE> condition = Condition("while")
2977   try {
2978     token2 = <SEMICOLON>
2979     {return new DoStatement(condition,action,token.sourceStart,token2.sourceEnd);}
2980   } catch (ParseException e) {
2981     errorMessage = "unexpected token : '"+ e.currentToken.next.image +"'. A ';' was expected";
2982     errorLevel   = ERROR;
2983     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
2984     errorEnd   = SimpleCharStream.getPosition() + 1;
2985     throw e;
2986   }
2987 }
2988
2989 ForeachStatement ForeachStatement() :
2990 {
2991   Statement statement;
2992   Expression expression;
2993   ArrayVariableDeclaration variable;
2994   Token token;
2995 }
2996 {
2997   token = <FOREACH>
2998     try {
2999     <LPAREN>
3000   } catch (ParseException e) {
3001     errorMessage = "'(' expected after 'foreach' keyword";
3002     errorLevel   = ERROR;
3003     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
3004     errorEnd   = SimpleCharStream.getPosition() + 1;
3005     throw e;
3006   }
3007   try {
3008     expression = Expression()
3009   } catch (ParseException e) {
3010     errorMessage = "variable expected";
3011     errorLevel   = ERROR;
3012     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
3013     errorEnd   = SimpleCharStream.getPosition() + 1;
3014     throw e;
3015   }
3016   try {
3017     <AS>
3018   } catch (ParseException e) {
3019     errorMessage = "'as' expected";
3020     errorLevel   = ERROR;
3021     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
3022     errorEnd   = SimpleCharStream.getPosition() + 1;
3023     throw e;
3024   }
3025   try {
3026     variable = ArrayVariable()
3027   } catch (ParseException e) {
3028     errorMessage = "variable expected";
3029     errorLevel   = ERROR;
3030     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
3031     errorEnd   = SimpleCharStream.getPosition() + 1;
3032     throw e;
3033   }
3034   try {
3035     <RPAREN>
3036   } catch (ParseException e) {
3037     errorMessage = "')' expected after 'foreach' keyword";
3038     errorLevel   = ERROR;
3039     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
3040     errorEnd   = SimpleCharStream.getPosition() + 1;
3041     throw e;
3042   }
3043   try {
3044     statement = Statement()
3045   } catch (ParseException e) {
3046     if (errorMessage != null) throw e;
3047     errorMessage = "statement expected";
3048     errorLevel   = ERROR;
3049     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
3050     errorEnd   = SimpleCharStream.getPosition() + 1;
3051     throw e;
3052   }
3053   {return new ForeachStatement(expression,
3054                                variable,
3055                                statement,
3056                                token.sourceStart,
3057                                statement.sourceEnd);}
3058
3059 }
3060
3061 ForStatement ForStatement() :
3062 {
3063 final Token token,token2;
3064 final int pos = SimpleCharStream.getPosition();
3065 Expression[] initializations = null;
3066 Expression condition = null;
3067 Expression[] increments = null;
3068 Statement action;
3069 final ArrayList list = new ArrayList();
3070 final int startBlock, endBlock;
3071 }
3072 {
3073   token = <FOR>
3074   try {
3075     <LPAREN>
3076   } catch (ParseException e) {
3077     errorMessage = "'(' expected after 'for' keyword";
3078     errorLevel   = ERROR;
3079     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
3080     errorEnd   = SimpleCharStream.getPosition() + 1;
3081     throw e;
3082   }
3083      [ initializations = ForInit() ] <SEMICOLON>
3084      [ condition = Expression() ] <SEMICOLON>
3085      [ increments = StatementExpressionList() ] <RPAREN>
3086     (
3087       action = Statement()
3088       {return new ForStatement(initializations,
3089                                condition,
3090                                increments,
3091                                action,
3092                                token.sourceStart,
3093                                action.sourceEnd);}
3094     |
3095       <COLON>
3096       {startBlock = SimpleCharStream.getPosition();}
3097       (action = Statement() {list.add(action);})*
3098       {
3099         try {
3100         setMarker(fileToParse,
3101                   "Ugly syntax detected, you should for () {...} instead of for (): ... endfor;",
3102                   pos,
3103                   pos+token.image.length(),
3104                   INFO,
3105                   "Line " + token.beginLine);
3106         } catch (CoreException e) {
3107           PHPeclipsePlugin.log(e);
3108         }
3109       }
3110       {endBlock = SimpleCharStream.getPosition();}
3111       try {
3112         <ENDFOR>
3113       } catch (ParseException e) {
3114         errorMessage = "'endfor' expected";
3115         errorLevel   = ERROR;
3116         errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
3117         errorEnd   = SimpleCharStream.getPosition() + 1;
3118         throw e;
3119       }
3120       try {
3121         token2 = <SEMICOLON>
3122         {
3123         final Statement[] stmtsArray = new Statement[list.size()];
3124         list.toArray(stmtsArray);
3125         return new ForStatement(initializations,
3126                                 condition,
3127                                 increments,
3128                                 new Block(stmtsArray,
3129                                           stmtsArray[0].sourceStart,
3130                                           stmtsArray[stmtsArray.length-1].sourceEnd),
3131                                 token.sourceStart,
3132                                 token2.sourceEnd);}
3133       } catch (ParseException e) {
3134         errorMessage = "';' expected after 'endfor' keyword";
3135         errorLevel   = ERROR;
3136         errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
3137         errorEnd   = SimpleCharStream.getPosition() + 1;
3138         throw e;
3139       }
3140     )
3141 }
3142
3143 Expression[] ForInit() :
3144 {
3145   final Expression[] exprs;
3146 }
3147 {
3148   LOOKAHEAD(LocalVariableDeclaration())
3149   exprs = LocalVariableDeclaration()
3150   {return exprs;}
3151 |
3152   exprs = StatementExpressionList()
3153   {return exprs;}
3154 }
3155
3156 Expression[] StatementExpressionList() :
3157 {
3158   final ArrayList list = new ArrayList();
3159   final Expression expr;
3160 }
3161 {
3162   expr = StatementExpression()   {list.add(expr);}
3163   (<COMMA> StatementExpression() {list.add(expr);})*
3164   {
3165     final Expression[] exprsArray = new Expression[list.size()];
3166     list.toArray(exprsArray);
3167     return exprsArray;
3168   }
3169 }
3170
3171 Continue ContinueStatement() :
3172 {
3173   Expression expr = null;
3174   final Token token,token2;
3175 }
3176 {
3177   token = <CONTINUE> [ expr = Expression() ]
3178   try {
3179     token2 = <SEMICOLON>
3180     {return new Continue(expr,token.sourceStart,token2.sourceEnd);}
3181   } catch (ParseException e) {
3182     errorMessage = "';' expected after 'continue' statement";
3183     errorLevel   = ERROR;
3184     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
3185     errorEnd   = SimpleCharStream.getPosition() + 1;
3186     throw e;
3187   }
3188 }
3189
3190 ReturnStatement ReturnStatement() :
3191 {
3192   Expression expr = null;
3193   final Token token,token2;
3194 }
3195 {
3196   token = <RETURN> [ expr = Expression() ]
3197   try {
3198     token2 = <SEMICOLON>
3199     {return new ReturnStatement(expr,token.sourceStart,token2.sourceEnd);}
3200   } catch (ParseException e) {
3201     errorMessage = "';' expected after 'return' statement";
3202     errorLevel   = ERROR;
3203     errorStart = SimpleCharStream.getPosition() - e.currentToken.next.image.length() + 1;
3204     errorEnd   = SimpleCharStream.getPosition() + 1;
3205     throw e;
3206   }
3207 }