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