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