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