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