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