*** 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.io.StringReader;
33 import java.text.MessageFormat;
34
35 import net.sourceforge.phpeclipse.actions.PHPStartApacheAction;
36 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
37 import net.sourceforge.phpdt.internal.compiler.parser.PHPOutlineInfo;
38 import net.sourceforge.phpdt.internal.compiler.parser.PHPSegmentWithChildren;
39 import net.sourceforge.phpdt.internal.compiler.parser.PHPFunctionDeclaration;
40 import net.sourceforge.phpdt.internal.compiler.parser.PHPClassDeclaration;
41 import net.sourceforge.phpdt.internal.compiler.parser.PHPVarDeclaration;
42 import net.sourceforge.phpdt.internal.compiler.parser.PHPReqIncDeclaration;
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 class PHPParser extends PHPParserSuperclass {
52
53   private static IFile fileToParse;
54
55   /** The current segment */
56   private static PHPSegmentWithChildren currentSegment;
57
58   private static final String PARSE_ERROR_STRING = "Parse error"; //$NON-NLS-1$
59   private static final String PARSE_WARNING_STRING = "Warning"; //$NON-NLS-1$
60   PHPOutlineInfo outlineInfo;
61   private static int errorLevel = ERROR;
62   private static String errorMessage;
63
64   public PHPParser() {
65   }
66
67   public void setFileToParse(IFile fileToParse) {
68     this.fileToParse = fileToParse;
69   }
70
71   public PHPParser(IFile fileToParse) {
72     this(new StringReader(""));
73     this.fileToParse = fileToParse;
74   }
75
76   public void phpParserTester(String strEval) throws CoreException, ParseException {
77     PHPParserTokenManager.SwitchTo(PHPParserTokenManager.PHPPARSING);
78     StringReader stream = new StringReader(strEval);
79     if (jj_input_stream == null) {
80       jj_input_stream = new SimpleCharStream(stream, 1, 1);
81     }
82     ReInit(new StringReader(strEval));
83     phpTest();
84   }
85
86   public void htmlParserTester(String strEval) throws CoreException, ParseException {
87     StringReader stream = new StringReader(strEval);
88     if (jj_input_stream == null) {
89       jj_input_stream = new SimpleCharStream(stream, 1, 1);
90     }
91     ReInit(stream);
92     phpFile();
93   }
94
95   public PHPOutlineInfo parseInfo(Object parent, String s) {
96     outlineInfo = new PHPOutlineInfo(parent);
97     currentSegment = outlineInfo.getDeclarations();
98     StringReader stream = new StringReader(s);
99     if (jj_input_stream == null) {
100       jj_input_stream = new SimpleCharStream(stream, 1, 1);
101     }
102     ReInit(stream);
103     try {
104       parse();
105     } catch (ParseException e) {
106       processParseException(e);
107     }
108     return outlineInfo;
109   }
110
111   /**
112    * This method will process the parse exception.
113    * If the error message is null, the parse exception wasn't catched and a trace is written in the log
114    * @param e the ParseException
115    */
116   private static void processParseException(final ParseException e) {
117     if (errorMessage == null) {
118       PHPeclipsePlugin.log(e);
119       errorMessage = "this exception wasn't handled by the parser please tell us how to reproduce it";
120     }
121     setMarker(e);
122     errorMessage = null;
123   }
124
125   /**
126    * Create marker for the parse error
127    */
128   private static void setMarker(ParseException e) {
129     try {
130       setMarker(fileToParse, errorMessage, jj_input_stream.tokenBegin,jj_input_stream.tokenBegin+e.currentToken.image.length(), errorLevel);
131     } catch (CoreException e2) {
132       PHPeclipsePlugin.log(e2);
133     }
134   }
135
136   /**
137    * Create markers according to the external parser output
138    */
139   private static void createMarkers(String output, IFile file) throws CoreException {
140     // delete all markers
141     file.deleteMarkers(IMarker.PROBLEM, false, 0);
142
143     int indx = 0;
144     int brIndx = 0;
145     boolean flag = true;
146     while ((brIndx = output.indexOf("<br />", indx)) != -1) {
147       // newer php error output (tested with 4.2.3)
148       scanLine(output, file, indx, brIndx);
149       indx = brIndx + 6;
150       flag = false;
151     }
152     if (flag) {
153       while ((brIndx = output.indexOf("<br>", indx)) != -1) {
154         // older php error output (tested with 4.2.3)
155         scanLine(output, file, indx, brIndx);
156         indx = brIndx + 4;
157       }
158     }
159   }
160
161   private static void scanLine(String output, IFile file, int indx, int brIndx) throws CoreException {
162     String current;
163     StringBuffer lineNumberBuffer = new StringBuffer(10);
164     char ch;
165     current = output.substring(indx, brIndx);
166
167     if (current.indexOf(PARSE_WARNING_STRING) != -1 || current.indexOf(PARSE_ERROR_STRING) != -1) {
168       int onLine = current.indexOf("on line <b>");
169       if (onLine != -1) {
170         lineNumberBuffer.delete(0, lineNumberBuffer.length());
171         for (int i = onLine; i < current.length(); i++) {
172           ch = current.charAt(i);
173           if ('0' <= ch && '9' >= ch) {
174             lineNumberBuffer.append(ch);
175           }
176         }
177
178         int lineNumber = Integer.parseInt(lineNumberBuffer.toString());
179
180         Hashtable attributes = new Hashtable();
181
182         current = current.replaceAll("\n", "");
183         current = current.replaceAll("<b>", "");
184         current = current.replaceAll("</b>", "");
185         MarkerUtilities.setMessage(attributes, current);
186
187         if (current.indexOf(PARSE_ERROR_STRING) != -1)
188           attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_ERROR));
189         else if (current.indexOf(PARSE_WARNING_STRING) != -1)
190           attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_WARNING));
191         else
192           attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_INFO));
193         MarkerUtilities.setLineNumber(attributes, lineNumber);
194         MarkerUtilities.createMarker(file, attributes, IMarker.PROBLEM);
195       }
196     }
197   }
198
199   public void parse(String s) throws CoreException {
200     ReInit(new StringReader(s));
201     try {
202       parse();
203     } catch (ParseException e) {
204       processParseException(e);
205     }
206   }
207
208   /**
209    * Call the php parse command ( php -l -f &lt;filename&gt; )
210    * and create markers according to the external parser output
211    */
212   public static void phpExternalParse(IFile file) {
213     IPreferenceStore store = PHPeclipsePlugin.getDefault().getPreferenceStore();
214     String filename = file.getLocation().toString();
215
216     String[] arguments = { filename };
217     MessageFormat form = new MessageFormat(store.getString(PHPeclipsePlugin.EXTERNAL_PARSER_PREF));
218     String command = form.format(arguments);
219
220     String parserResult = PHPStartApacheAction.getParserOutput(command, "External parser: ");
221
222     try {
223       // parse the buffer to find the errors and warnings
224       createMarkers(parserResult, file);
225     } catch (CoreException e) {
226       PHPeclipsePlugin.log(e);
227     }
228   }
229
230   public void parse() throws ParseException {
231           phpFile();
232   }
233 }
234
235 PARSER_END(PHPParser)
236
237 <DEFAULT> TOKEN :
238 {
239   <PHPSTART : "<?php" | "<?"> : PHPPARSING
240 }
241
242 <PHPPARSING> TOKEN :
243 {
244   <PHPEND :"?>"> : DEFAULT
245 }
246
247 <DEFAULT> SKIP :
248 {
249  < ~[] >
250 }
251
252
253 /* WHITE SPACE */
254
255 <PHPPARSING> SKIP :
256 {
257   " "
258 | "\t"
259 | "\n"
260 | "\r"
261 | "\f"
262 }
263
264 /* COMMENTS */
265
266 <PHPPARSING> MORE :
267 {
268   "//" : IN_SINGLE_LINE_COMMENT
269 |
270   <"/**" ~["/"]> { input_stream.backup(1); } : IN_FORMAL_COMMENT
271 |
272   "/*" : IN_MULTI_LINE_COMMENT
273 }
274
275 <IN_SINGLE_LINE_COMMENT>
276 SPECIAL_TOKEN :
277 {
278   <SINGLE_LINE_COMMENT: "\n" | "\r" | "\r\n" | "?>" > : PHPPARSING
279 }
280
281 <IN_FORMAL_COMMENT>
282 SPECIAL_TOKEN :
283 {
284   <FORMAL_COMMENT: "*/" > : PHPPARSING
285 }
286
287 <IN_MULTI_LINE_COMMENT>
288 SPECIAL_TOKEN :
289 {
290   <MULTI_LINE_COMMENT: "*/" > : PHPPARSING
291 }
292
293 <IN_SINGLE_LINE_COMMENT,IN_FORMAL_COMMENT,IN_MULTI_LINE_COMMENT>
294 MORE :
295 {
296   < ~[] >
297 }
298
299 /* KEYWORDS */
300 <PHPPARSING> TOKEN :
301 {
302   <CLASS    : "class">
303 | <FUNCTION : "function">
304 | <VAR      : "var">
305 | <IF       : "if">
306 | <ELSEIF   : "elseif">
307 | <ELSE     : "else">
308 | <ARRAY    : "array">
309 }
310
311 /* LANGUAGE CONSTRUCT */
312 <PHPPARSING> TOKEN :
313 {
314   <PRINT : "print">
315 | <ECHO : "echo">
316 | <INCLUDE : "include">
317 | <REQUIRE : "require">
318 | <INCLUDE_ONCE : "include_once">
319 | <REQUIRE_ONCE : "require_once">
320 | <GLOBAL : "global">
321 | <STATIC : "static">
322 | <CLASSACCESS: "->">
323 | <STATICCLASSACCESS: "::">
324 | <ARRAYASSIGN: "=>">
325 }
326
327 /* RESERVED WORDS AND LITERALS */
328
329 <PHPPARSING> TOKEN :
330 {
331   < BREAK: "break" >
332 | < CASE: "case" >
333 | < CONST: "const" >
334 | < CONTINUE: "continue" >
335 | < _DEFAULT: "default" >
336 | < DO: "do" >
337 | < EXTENDS: "extends" >
338 | < FALSE: "false" >
339 | < FOR: "for" >
340 | < GOTO: "goto" >
341 | < NEW: "new" >
342 | < NULL: "null" >
343 | < RETURN: "return" >
344 | < SUPER: "super" >
345 | < SWITCH: "switch" >
346 | < THIS: "this" >
347 | < TRUE: "true" >
348 | < WHILE: "while" >
349 | < ENDWHILE : "endwhile" >
350 }
351
352 /* TYPES */
353
354 <PHPPARSING> TOKEN :
355 {
356   <STRING : "string">
357 | <OBJECT : "object">
358 | <BOOL : "bool">
359 | <BOOLEAN : "boolean">
360 | <REAL : "real">
361 | <DOUBLE : "double">
362 | <FLOAT : "float">
363 | <INT : "int">
364 | <INTEGER : "integer">
365 }
366
367 <PHPPARSING> TOKEN :
368 {
369   < _ORL : "OR" >
370 | < _ANDL: "AND">
371 }
372
373 /* LITERALS */
374
375 <PHPPARSING> TOKEN :
376 {
377   < INTEGER_LITERAL:
378         <DECIMAL_LITERAL> (["l","L"])?
379       | <HEX_LITERAL> (["l","L"])?
380       | <OCTAL_LITERAL> (["l","L"])?
381   >
382 |
383   < #DECIMAL_LITERAL: ["1"-"9"] (["0"-"9"])* >
384 |
385   < #HEX_LITERAL: "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+ >
386 |
387   < #OCTAL_LITERAL: "0" (["0"-"7"])* >
388 |
389   < FLOATING_POINT_LITERAL:
390         (["0"-"9"])+ "." (["0"-"9"])* (<EXPONENT>)? (["f","F","d","D"])?
391       | "." (["0"-"9"])+ (<EXPONENT>)? (["f","F","d","D"])?
392       | (["0"-"9"])+ <EXPONENT> (["f","F","d","D"])?
393       | (["0"-"9"])+ (<EXPONENT>)? ["f","F","d","D"]
394   >
395 |
396   < #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ >
397 |
398   < STRING_LITERAL: (<STRING_1> | <STRING_2> | <STRING_3>)>
399 |    < STRING_1:
400       "\""
401       (
402         ~["\""]
403         |
404         "\\\""
405       )*
406       "\""
407     >
408 |    < STRING_2:
409       "'"
410       (
411       ~["'"]
412        |
413        "\\'"
414       )*
415
416       "'"
417     >
418 |   < STRING_3:
419       "`"
420       (
421         ~["`"]
422       |
423         "\\`"
424       )*
425       "`"
426     >
427 }
428
429 /* IDENTIFIERS */
430
431 <PHPPARSING> TOKEN :
432 {
433   < IDENTIFIER: (<LETTER>|<SPECIAL>) (<LETTER>|<DIGIT>|<SPECIAL>)* >
434 |
435   < #LETTER:
436       ["a"-"z"] | ["A"-"Z"]
437   >
438 |
439   < #DIGIT:
440       ["0"-"9"]
441   >
442 |
443   < #SPECIAL:
444     "_"
445   >
446 }
447
448 /* SEPARATORS */
449
450 <PHPPARSING> TOKEN :
451 {
452   < LPAREN: "(" >
453 | < RPAREN: ")" >
454 | < LBRACE: "{" >
455 | < RBRACE: "}" >
456 | < LBRACKET: "[" >
457 | < RBRACKET: "]" >
458 | < SEMICOLON: ";" >
459 | < COMMA: "," >
460 | < DOT: "." >
461 }
462
463 /* OPERATORS */
464
465 <PHPPARSING> TOKEN :
466 {
467   <AT     : "@">
468 | <DOLLAR : "$">
469 | <ASSIGN: "=" >
470 | <GT: ">" >
471 | <LT: "<" >
472 | <BANG: "!" >
473 | <HOOK: "?" >
474 | <COLON: ":" >
475 | <EQ: "==" >
476 | <LE: "<=" >
477 | <GE: ">=" >
478 | <NE: "!=" >
479 | <SC_OR: "||" >
480 | <SC_AND: "&&" >
481 | <INCR: "++" >
482 | <DECR: "--" >
483 | <PLUS: "+" >
484 | <MINUS: "-" >
485 | <STAR: "*" >
486 | <SLASH: "/" >
487 | <BIT_AND: "&" >
488 | <BIT_OR: "|" >
489 | <XOR: "^" >
490 | <REM: "%" >
491 | <LSHIFT: "<<" >
492 | <RSIGNEDSHIFT: ">>" >
493 | <RUNSIGNEDSHIFT: ">>>" >
494 | <PLUSASSIGN: "+=" >
495 | <MINUSASSIGN: "-=" >
496 | <STARASSIGN: "*=" >
497 | <SLASHASSIGN: "/=" >
498 | <ANDASSIGN: "&=" >
499 | <ORASSIGN: "|=" >
500 | <XORASSIGN: "^=" >
501 | <DOTASSIGN: ".=" >
502 | <REMASSIGN: "%=" >
503 | <LSHIFTASSIGN: "<<=" >
504 | <RSIGNEDSHIFTASSIGN: ">>=" >
505 | <RUNSIGNEDSHIFTASSIGN: ">>>=" >
506 }
507
508 <PHPPARSING> TOKEN :
509 {
510   < DOLLAR_ID: <DOLLAR> <IDENTIFIER>  >
511 }
512
513 /*****************************************
514  * THE JAVA LANGUAGE GRAMMAR STARTS HERE *
515  *****************************************/
516
517 /*
518  * Program structuring syntax follows.
519  */
520
521 void phpTest() :
522 {}
523 {
524   Php()
525   <EOF>
526 }
527
528 void phpFile() :
529 {}
530 {
531   (<PHPSTART> Php() <PHPEND>)*
532   <EOF>
533 }
534
535 void Php() :
536 {}
537 {
538   (BlockStatement())*
539 }
540
541 void ClassDeclaration() :
542 {
543   PHPClassDeclaration classDeclaration;
544   Token className;
545   int pos = jj_input_stream.bufpos;
546 }
547 {
548   <CLASS> className = <IDENTIFIER> [ <EXTENDS> <IDENTIFIER> ]
549   {
550     classDeclaration = new PHPClassDeclaration(currentSegment,className.image,pos);
551     currentSegment.add(classDeclaration);
552     currentSegment = classDeclaration;
553   }
554   ClassBody()
555   {
556     currentSegment = (PHPSegmentWithChildren) currentSegment.getParent();
557   }
558 }
559
560 void ClassBody() :
561 {}
562 {
563   try {
564     <LBRACE>
565   } catch (ParseException e) {
566     errorMessage = "'{' expected";
567     errorLevel   = ERROR;
568     throw e;
569   }
570   ( ClassBodyDeclaration() )*
571   try {
572     <RBRACE>
573   } catch (ParseException e) {
574     errorMessage = "'var', 'function' or '}' expected";
575     errorLevel   = ERROR;
576     throw e;
577   }
578 }
579
580 void ClassBodyDeclaration() :
581 {}
582 {
583   MethodDeclaration()
584 |
585   FieldDeclaration()
586 }
587
588 void FieldDeclaration() :
589 {
590   PHPVarDeclaration variableDeclaration;
591 }
592 {
593   <VAR> variableDeclaration = VariableDeclarator()
594   {currentSegment.add(variableDeclaration);}
595   ( <COMMA>
596     variableDeclaration = VariableDeclarator()
597     {currentSegment.add(variableDeclaration);}
598   )*
599   try {
600     <SEMICOLON>
601   } catch (ParseException e) {
602     errorMessage = "';' expected after variable declaration";
603     errorLevel   = ERROR;
604     throw e;
605   }
606 }
607
608 PHPVarDeclaration VariableDeclarator() :
609 {
610   String varName;
611   String varValue = null;
612   int pos = jj_input_stream.bufpos;
613 }
614 {
615   varName = VariableDeclaratorId()
616   [
617     <ASSIGN>
618     try {
619       varValue = VariableInitializer()
620     } catch (ParseException e) {
621       errorMessage = "Literal expression expected in variable initializer";
622       errorLevel   = ERROR;
623       throw e;
624     }
625   ]
626   {
627     if (varValue == null) {
628       return new PHPVarDeclaration(currentSegment,varName,pos);
629     }
630     return new PHPVarDeclaration(currentSegment,varName,pos,varValue);
631   }
632 }
633
634 String VariableDeclaratorId() :
635 {
636   String expr;
637   StringBuffer buff = new StringBuffer();
638 }
639 {
640   try {
641     expr = Variable()
642     {buff.append(expr);}
643     ( LOOKAHEAD(2) expr = VariableSuffix()
644     {buff.append(expr);}
645     )*
646     {return buff.toString();}
647   } catch (ParseException e) {
648     errorMessage = "'$' expected for variable identifier";
649     errorLevel   = ERROR;
650     throw e;
651   }
652 }
653
654 String Variable():
655 {
656   String expr = null;
657   Token token;
658 }
659 {
660   token = <DOLLAR_ID> [<LBRACE> expr = Expression() <RBRACE>]
661   {
662     if (expr == null) {
663       return token.image;
664     }
665     return token + "{" + expr + "}";
666   }
667 |
668   <DOLLAR> expr = VariableName()
669   {return "$" + expr;}
670 }
671
672 String VariableName():
673 {
674 String expr = null;
675 Token token;
676 }
677 {
678   <LBRACE> expr = Expression() <RBRACE>
679   {return "{"+expr+"}";}
680 |
681   token = <IDENTIFIER> [<LBRACE> expr = Expression() <RBRACE>]
682   {
683     if (expr == null) {
684       return token.image;
685     }
686     return token + "{" + expr + "}";
687   }
688 |
689   <DOLLAR> expr = VariableName()
690   {return "$" + expr;}
691 |
692   token = <DOLLAR_ID> [expr = VariableName()]
693   {
694   if (expr == null) {
695     return token.image;
696   }
697   return token.image + expr;
698   }
699 }
700
701 String VariableInitializer() :
702 {
703   String expr;
704 }
705 {
706   expr = Literal()
707   {return expr;}
708 |
709   expr = ArrayDeclarator()
710   {return expr;}
711 }
712
713 String ArrayVariable() :
714 {
715 String expr;
716 StringBuffer buff = new StringBuffer();
717 }
718 {
719   expr = Expression()
720   {buff.append(expr);}
721    [<ARRAYASSIGN> expr = Expression()
722    {buff.append("=>").append(expr);}]
723   {return buff.toString();}
724 }
725
726 String ArrayInitializer() :
727 {
728 String expr = null;
729 StringBuffer buff = new StringBuffer("(");
730 }
731 {
732   <LPAREN> [ expr = ArrayVariable()
733             {buff.append(expr);}
734             ( LOOKAHEAD(2) <COMMA> expr = ArrayVariable()
735             {buff.append(",").append(expr);}
736             )* ]
737   <RPAREN>
738   {
739     buff.append(")");
740     return buff.toString();
741   }
742 }
743
744 void MethodDeclaration() :
745 {
746   PHPFunctionDeclaration functionDeclaration;
747 }
748 {
749   <FUNCTION> functionDeclaration = MethodDeclarator()
750   {
751     currentSegment.add(functionDeclaration);
752     currentSegment = functionDeclaration;
753   }
754   Block()
755   {
756     currentSegment = (PHPSegmentWithChildren) currentSegment.getParent();
757   }
758 }
759
760 PHPFunctionDeclaration MethodDeclarator() :
761 {
762   Token identifier;
763   StringBuffer methodDeclaration = new StringBuffer();
764   String formalParameters;
765   int pos = jj_input_stream.bufpos;
766 }
767 {
768   [ <BIT_AND> {methodDeclaration.append("&");} ]
769   identifier = <IDENTIFIER>
770   {methodDeclaration.append(identifier);}
771     formalParameters = FormalParameters()
772   {
773     methodDeclaration.append(formalParameters);
774     return new PHPFunctionDeclaration(currentSegment,methodDeclaration.toString(),pos);
775   }
776 }
777
778 String FormalParameters() :
779 {
780   String expr;
781   final StringBuffer buff = new StringBuffer("(");
782 }
783 {
784   try {
785   <LPAREN>
786   } catch (ParseException e) {
787     errorMessage = "Formal parameter expected after function identifier";
788     errorLevel   = ERROR;
789     jj_consume_token(token.kind);
790   }
791             [ expr = FormalParameter()
792               {buff.append(expr);}
793             (
794                 <COMMA> expr = FormalParameter()
795                 {buff.append(",").append(expr);}
796             )*
797             ]
798   try {
799     <RPAREN>
800   } catch (ParseException e) {
801     errorMessage = "')' expected";
802     errorLevel   = ERROR;
803     throw e;
804   }
805  {
806   buff.append(")");
807   return buff.toString();
808  }
809 }
810
811 String FormalParameter() :
812 {
813   PHPVarDeclaration variableDeclaration;
814   StringBuffer buff = new StringBuffer();
815 }
816 {
817   [<BIT_AND> {buff.append("&");}] variableDeclaration = VariableDeclarator()
818   {
819     buff.append(variableDeclaration.toString());
820     return buff.toString();
821   }
822 }
823
824 String Type() :
825 {}
826 {
827   <STRING>
828   {return "string";}
829 |
830   <BOOL>
831   {return "bool";}
832 |
833   <BOOLEAN>
834   {return "boolean";}
835 |
836   <REAL>
837   {return "real";}
838 |
839   <DOUBLE>
840   {return "double";}
841 |
842   <FLOAT>
843   {return "float";}
844 |
845   <INT>
846   {return "int";}
847 |
848   <INTEGER>
849   {return "integer";}
850 }
851
852 String Expression() :
853 {
854   String expr;
855   String assignOperator = null;
856   String expr2 = null;
857 }
858 {
859   expr = PrintExpression()
860   {return expr;}
861 |
862   expr = ConditionalExpression()
863   [
864     assignOperator = AssignmentOperator()
865     try {
866       expr2 = Expression()
867     } catch (ParseException e) {
868       errorMessage = "expression expected";
869       errorLevel   = ERROR;
870       throw generateParseException();
871     }
872   ]
873   {
874     if (expr2 == null) {
875       return expr;
876     } else {
877       return expr + assignOperator + expr2;
878     }
879   }
880 }
881
882 String AssignmentOperator() :
883 {}
884 {
885   <ASSIGN>
886 {return "=";}
887 | <STARASSIGN>
888 {return "*=";}
889 | <SLASHASSIGN>
890 {return "/=";}
891 | <REMASSIGN>
892 {return "%=";}
893 | <PLUSASSIGN>
894 {return "+=";}
895 | <MINUSASSIGN>
896 {return "-=";}
897 | <LSHIFTASSIGN>
898 {return "<<=";}
899 | <RSIGNEDSHIFTASSIGN>
900 {return ">>=";}
901 | <RUNSIGNEDSHIFTASSIGN>
902 {return ">>>=";}
903 | <ANDASSIGN>
904 {return "&=";}
905 | <XORASSIGN>
906 {return "|=";}
907 | <ORASSIGN>
908 {return "|=";}
909 | <DOTASSIGN>
910 {return ".=";}
911 }
912
913 String ConditionalExpression() :
914 {
915   String expr;
916   String expr2 = null;
917   String expr3 = null;
918 }
919 {
920   expr = ConditionalOrExpression() [ <HOOK> expr2 = Expression() <COLON> expr3 = ConditionalExpression() ]
921 {
922   if (expr3 == null) {
923     return expr;
924   } else {
925     return expr + "?" + expr2 + ":" + expr3;
926   }
927 }
928 }
929
930 String ConditionalOrExpression() :
931 {
932   String expr;
933   Token operator;
934   String expr2 = null;
935   StringBuffer buff = new StringBuffer();
936 }
937 {
938   expr = ConditionalAndExpression()
939   {
940     buff.append(expr);
941   }
942   (
943     (operator = <SC_OR> | operator = <_ORL>) expr2 = ConditionalAndExpression()
944     {
945       buff.append(operator.image);
946       buff.append(expr2);
947     }
948   )*
949   {
950     return buff.toString();
951   }
952 }
953
954 String ConditionalAndExpression() :
955 {
956   String expr;
957   Token operator;
958   String expr2 = null;
959   StringBuffer buff = new StringBuffer();
960 }
961 {
962   expr = ConcatExpression()
963   {
964     buff.append(expr);
965   }
966   (
967   (operator = <SC_AND> | operator = <_ANDL>) expr2 = ConcatExpression()
968     {
969       buff.append(operator.image);
970       buff.append(expr2);
971     }
972   )*
973   {
974     return buff.toString();
975   }
976 }
977
978 String ConcatExpression() :
979 {
980   String expr;
981   String expr2 = null;
982   StringBuffer buff = new StringBuffer();
983 }
984 {
985   expr = InclusiveOrExpression()
986   {
987     buff.append(expr);
988   }
989   (
990   <DOT> expr2 = InclusiveOrExpression()
991   {
992     buff.append(".");
993     buff.append(expr2);
994   }
995   )*
996   {
997     return buff.toString();
998   }
999 }
1000
1001 String InclusiveOrExpression() :
1002 {
1003   String expr;
1004   String expr2 = null;
1005   StringBuffer buff = new StringBuffer();
1006 }
1007 {
1008   expr = ExclusiveOrExpression()
1009   {
1010     buff.append(expr);
1011   }
1012   (
1013   <BIT_OR> expr2 = ExclusiveOrExpression()
1014   {
1015     buff.append("|");
1016     buff.append(expr2);
1017   }
1018   )*
1019   {
1020     return buff.toString();
1021   }
1022 }
1023
1024 String ExclusiveOrExpression() :
1025 {
1026   String expr;
1027   String expr2 = null;
1028   StringBuffer buff = new StringBuffer();
1029 }
1030 {
1031   expr = AndExpression()
1032   {
1033     buff.append(expr);
1034   }
1035   (
1036     <XOR> expr2 = AndExpression()
1037   {
1038     buff.append("^");
1039     buff.append(expr2);
1040   }
1041   )*
1042   {
1043     return buff.toString();
1044   }
1045 }
1046
1047 String AndExpression() :
1048 {
1049   String expr;
1050   String expr2 = null;
1051   StringBuffer buff = new StringBuffer();
1052 }
1053 {
1054   expr = EqualityExpression()
1055   {
1056     buff.append(expr);
1057   }
1058   (
1059     <BIT_AND> expr2 = EqualityExpression()
1060   {
1061     buff.append("&");
1062     buff.append(expr2);
1063   }
1064   )*
1065   {
1066     return buff.toString();
1067   }
1068 }
1069
1070 String EqualityExpression() :
1071 {
1072   String expr;
1073   Token operator;
1074   String expr2;
1075   StringBuffer buff = new StringBuffer();
1076 }
1077 {
1078   expr = RelationalExpression()
1079   {buff.append(expr);}
1080   (
1081   ( operator = <EQ> | operator = <NE> ) expr2 = RelationalExpression()
1082   {
1083     buff.append(operator.image);
1084     buff.append(expr2);
1085   }
1086   )*
1087   {return buff.toString();}
1088 }
1089
1090 String RelationalExpression() :
1091 {
1092   String expr;
1093   Token operator;
1094   String expr2;
1095   StringBuffer buff = new StringBuffer();
1096 }
1097 {
1098   expr = ShiftExpression()
1099   {buff.append(expr);}
1100   (
1101   ( operator = <LT> | operator = <GT> | operator = <LE> | operator = <GE> ) expr2 = ShiftExpression()
1102   {
1103     buff.append(operator.image);
1104     buff.append(expr2);
1105   }
1106   )*
1107   {return buff.toString();}
1108 }
1109
1110 String ShiftExpression() :
1111 {
1112   String expr;
1113   Token operator;
1114   String expr2;
1115   StringBuffer buff = new StringBuffer();
1116 }
1117 {
1118   expr = AdditiveExpression()
1119   {buff.append(expr);}
1120   (
1121   (operator = <LSHIFT> | operator = <RSIGNEDSHIFT> | operator = <RUNSIGNEDSHIFT> ) expr2 = AdditiveExpression()
1122   {
1123     buff.append(operator.image);
1124     buff.append(expr2);
1125   }
1126   )*
1127   {return buff.toString();}
1128 }
1129
1130 String AdditiveExpression() :
1131 {
1132   String expr;
1133   Token operator;
1134   String expr2;
1135   StringBuffer buff = new StringBuffer();
1136 }
1137 {
1138   expr = MultiplicativeExpression()
1139   {buff.append(expr);}
1140   (
1141    ( operator = <PLUS> | operator = <MINUS> ) expr2 = MultiplicativeExpression()
1142   {
1143     buff.append(operator.image);
1144     buff.append(expr2);
1145   }
1146    )*
1147   {return buff.toString();}
1148 }
1149
1150 String MultiplicativeExpression() :
1151 {
1152   String expr, expr2;
1153   Token operator;
1154   final StringBuffer buff = new StringBuffer();}
1155 {
1156   expr = UnaryExpression()
1157   {buff.append(expr);}
1158   (
1159   ( operator = <STAR> | operator = <SLASH> | operator = <REM> ) expr2 = UnaryExpression()
1160   {
1161     buff.append(operator.image);
1162     buff.append(expr2);
1163   }
1164   )*
1165   {return buff.toString();}
1166 }
1167
1168 String UnaryExpression() :
1169 {
1170   String expr;
1171   Token token;
1172 }
1173 {
1174   <AT> expr = UnaryExpression()
1175   {return "@" + expr;}
1176 |
1177   ( token = <PLUS> | token = <MINUS> ) expr = UnaryExpression()
1178   {
1179     return token.image + expr;
1180   }
1181 |
1182   expr = PreIncrementExpression()
1183   {return expr;}
1184 |
1185   expr = PreDecrementExpression()
1186   {return expr;}
1187 |
1188   expr = UnaryExpressionNotPlusMinus()
1189   {return expr;}
1190 }
1191
1192 String PreIncrementExpression() :
1193 {
1194 String expr;
1195 }
1196 {
1197   <INCR> expr = PrimaryExpression()
1198   {return "++"+expr;}
1199 }
1200
1201 String PreDecrementExpression() :
1202 {
1203 String expr;
1204 }
1205 {
1206   <DECR> expr = PrimaryExpression()
1207   {return "--"+expr;}
1208 }
1209
1210 String UnaryExpressionNotPlusMinus() :
1211 {
1212   String expr;
1213 }
1214 {
1215   <BANG> expr = UnaryExpression()
1216   {return "!" + expr;}
1217 |
1218   LOOKAHEAD( <LPAREN> Type() <RPAREN> )
1219   expr = CastExpression()
1220   {return expr;}
1221 |
1222   expr = PostfixExpression()
1223   {return expr;}
1224 |
1225   expr = Literal()
1226   {return expr;}
1227 |
1228   <LPAREN> expr = Expression()<RPAREN>
1229   {return "("+expr+")";}
1230 }
1231
1232 String CastExpression() :
1233 {
1234 String type;
1235 String expr;
1236 }
1237 {
1238   <LPAREN> type = Type() <RPAREN> expr = UnaryExpression()
1239   {return "(" + type + ")" + expr;}
1240 }
1241
1242 String PostfixExpression() :
1243 {
1244   String expr;
1245   Token operator = null;
1246 }
1247 {
1248   expr = PrimaryExpression() [ operator = <INCR> | operator = <DECR> ]
1249   {
1250     if (operator == null) {
1251       return expr;
1252     }
1253     return expr + operator.image;
1254   }
1255 }
1256
1257 String PrimaryExpression() :
1258 {
1259   Token identifier;
1260   String expr;
1261   final StringBuffer buff = new StringBuffer();
1262 }
1263 {
1264   LOOKAHEAD(2)
1265   identifier = <IDENTIFIER> <STATICCLASSACCESS> expr = ClassIdentifier()
1266   {buff.append(identifier.image).append("::").append(expr);}
1267   (
1268   expr = PrimarySuffix()
1269   {buff.append(expr);}
1270   )*
1271   {return buff.toString();}
1272 |
1273   expr = PrimaryPrefix()  {buff.append(expr);}
1274   ( expr = PrimarySuffix()  {buff.append(expr);} )*
1275   {return buff.toString();}
1276 |
1277   expr = ArrayDeclarator()
1278   {return "array" + expr;}
1279 }
1280
1281 String ArrayDeclarator() :
1282 {
1283   String expr;
1284 }
1285 {
1286   <ARRAY> expr = ArrayInitializer()
1287   {return "array" + expr;}
1288 }
1289
1290 String PrimaryPrefix() :
1291 {
1292   String expr;
1293   Token token = null;
1294 }
1295 {
1296   token = <IDENTIFIER>
1297   {return token.image;}
1298 |
1299   [token = <BIT_AND>] <NEW> expr = ClassIdentifier()
1300   {
1301     if (token == null) {
1302       return "new " + expr;
1303     }
1304     return "new &" + expr;
1305   }
1306 |  
1307   expr = VariableDeclaratorId()
1308   {return expr;}
1309 }
1310
1311 String ClassIdentifier():
1312 {
1313   String expr;
1314   Token token;
1315 }
1316 {
1317   token = <IDENTIFIER>
1318   {return token.image;}
1319 |
1320   expr = VariableDeclaratorId()
1321   {return expr;}
1322 }
1323
1324 String PrimarySuffix() :
1325 {
1326   String expr;
1327 }
1328 {
1329   expr = Arguments()
1330   {return expr;}
1331 |
1332   expr = VariableSuffix()
1333   {return expr;}
1334 }
1335
1336 String VariableSuffix() :
1337 {
1338   String expr = null;
1339 }
1340 {
1341   <CLASSACCESS> expr = VariableName()
1342   {return "->" + expr;}
1343
1344   <LBRACKET> [ expr = Expression() ]
1345   try {
1346     <RBRACKET>
1347   } catch (ParseException e) {
1348     errorMessage = "']' expected";
1349     errorLevel   = ERROR;
1350     throw generateParseException();
1351   }
1352   {
1353     if(expr == null) {
1354       return "[]";
1355     }
1356     return "[" + expr + "]";
1357   }
1358 }
1359
1360 String Literal() :
1361 {
1362   String expr;
1363   Token token;
1364 }
1365 {
1366   token = <INTEGER_LITERAL>
1367   {return token.image;}
1368 |
1369   token = <FLOATING_POINT_LITERAL>
1370   {return token.image;}
1371 |
1372   try {
1373     token = <STRING_LITERAL>
1374   {return token.image;}
1375   } catch (TokenMgrError e) {
1376     errorMessage = "unterminated string";
1377     errorLevel   = ERROR;
1378     throw generateParseException();
1379   }
1380 |
1381   expr = BooleanLiteral()
1382   {return expr;}
1383 |
1384   expr = NullLiteral()
1385   {return expr;}
1386 }
1387
1388 String BooleanLiteral() :
1389 {}
1390 {
1391   <TRUE>
1392   {return "true";}
1393 |
1394   <FALSE>
1395   {return "false";}
1396 }
1397
1398 String NullLiteral() :
1399 {}
1400 {
1401   <NULL>
1402   {return "null";}
1403 }
1404
1405 String Arguments() :
1406 {
1407 String expr = null;
1408 }
1409 {
1410   <LPAREN> [ expr = ArgumentList() ]
1411   try {
1412     <RPAREN>
1413   } catch (ParseException e) {
1414     errorMessage = "')' expected to close the argument list";
1415     errorLevel   = ERROR;
1416     throw e;
1417   }
1418   {
1419   if (expr == null) {
1420     return "()";
1421   }
1422   return "(" + expr + ")";
1423   }
1424 }
1425
1426 String ArgumentList() :
1427 {
1428 String expr;
1429 StringBuffer buff = new StringBuffer();
1430 }
1431 {
1432   expr = Expression()
1433   {buff.append(expr);}
1434   ( <COMMA>
1435       try {
1436         expr = Expression()
1437       } catch (ParseException e) {
1438         errorMessage = "expression expected after a comma in argument list";
1439         errorLevel   = ERROR;
1440         throw e;
1441       }
1442     {
1443       buff.append(",").append("expr");
1444     }
1445    )*
1446    {return buff.toString();}
1447 }
1448
1449 /*
1450  * Statement syntax follows.
1451  */
1452
1453 void Statement() :
1454 {}
1455 {
1456   LOOKAHEAD(2)
1457   Expression()
1458   try {
1459     (<SEMICOLON> | "?>")
1460   } catch (ParseException e) {
1461     errorMessage = "';' expected";
1462     errorLevel   = ERROR;
1463     throw e;
1464   }
1465 |
1466   LOOKAHEAD(2)
1467   LabeledStatement()
1468 |
1469   Block()
1470 |
1471   EmptyStatement()
1472 |
1473   StatementExpression()
1474   try {
1475     <SEMICOLON>
1476   } catch (ParseException e) {
1477     errorMessage = "';' expected after expression";
1478     errorLevel   = ERROR;
1479     throw e;
1480   }
1481 |
1482   SwitchStatement()
1483 |
1484   IfStatement()
1485 |
1486   WhileStatement()
1487 |
1488   DoStatement()
1489 |
1490   ForStatement()
1491 |
1492   BreakStatement()
1493 |
1494   ContinueStatement()
1495 |
1496   ReturnStatement()
1497 |
1498   EchoStatement()
1499 |
1500   [<AT>] IncludeStatement()
1501 |
1502   StaticStatement()
1503 |
1504   GlobalStatement()
1505 }
1506
1507 void IncludeStatement() :
1508 {
1509   String expr;
1510   int pos = jj_input_stream.bufpos;
1511 }
1512 {
1513   <REQUIRE>
1514   expr = Expression()
1515   {currentSegment.add(new PHPReqIncDeclaration(currentSegment, "require",pos,expr));}
1516   try {
1517     (<SEMICOLON> | "?>")
1518   } catch (ParseException e) {
1519     errorMessage = "';' expected";
1520     errorLevel   = ERROR;
1521     throw e;
1522   }
1523 |
1524   <REQUIRE_ONCE>
1525   expr = Expression()
1526   {currentSegment.add(new PHPReqIncDeclaration(currentSegment, "require_once",pos,expr));}
1527   try {
1528     (<SEMICOLON> | "?>")
1529   } catch (ParseException e) {
1530     errorMessage = "';' expected";
1531     errorLevel   = ERROR;
1532     throw e;
1533   }
1534 |
1535   <INCLUDE>
1536   expr = Expression()
1537   {currentSegment.add(new PHPReqIncDeclaration(currentSegment, "include",pos,expr));}
1538   try {
1539     (<SEMICOLON> | "?>")
1540   } catch (ParseException e) {
1541     errorMessage = "';' expected";
1542     errorLevel   = ERROR;
1543     throw e;
1544   }
1545 |
1546   <INCLUDE_ONCE>
1547   expr = Expression()
1548   {currentSegment.add(new PHPReqIncDeclaration(currentSegment, "include_once",pos,expr));}
1549   try {
1550     (<SEMICOLON> | "?>")
1551   } catch (ParseException e) {
1552     errorMessage = "';' expected";
1553     errorLevel   = ERROR;
1554     throw e;
1555   }
1556 }
1557
1558 String PrintExpression() :
1559 {
1560   StringBuffer buff = new StringBuffer("print ");
1561   String expr;
1562 }
1563 {
1564   <PRINT> expr = Expression()
1565   {
1566     buff.append(expr);
1567     return buff.toString();
1568   }
1569 }
1570
1571 void EchoStatement() :
1572 {}
1573 {
1574   <ECHO> Expression() (<COMMA> Expression())*
1575   try {
1576     (<SEMICOLON> | "?>")
1577   } catch (ParseException e) {
1578     errorMessage = "';' expected after 'echo' statement";
1579     errorLevel   = ERROR;
1580     throw e;
1581   }
1582 }
1583
1584 void GlobalStatement() :
1585 {}
1586 {
1587   <GLOBAL> VariableDeclaratorId() (<COMMA> VariableDeclaratorId())*
1588   try {
1589     (<SEMICOLON> | "?>")
1590   } catch (ParseException e) {
1591     errorMessage = "';' expected";
1592     errorLevel   = ERROR;
1593     throw e;
1594   }
1595 }
1596
1597 void StaticStatement() :
1598 {}
1599 {
1600   <STATIC> VariableDeclarator() (<COMMA> VariableDeclarator())*
1601   try {
1602     (<SEMICOLON> | "?>")
1603   } catch (ParseException e) {
1604     errorMessage = "';' expected";
1605     errorLevel   = ERROR;
1606     throw e;
1607   }
1608 }
1609
1610 void LabeledStatement() :
1611 {}
1612 {
1613   <IDENTIFIER> <COLON> Statement()
1614 }
1615
1616 void Block() :
1617 {}
1618 {
1619   try {
1620     <LBRACE>
1621   } catch (ParseException e) {
1622     errorMessage = "'{' expected";
1623     errorLevel   = ERROR;
1624     throw e;
1625   }
1626   ( BlockStatement() )*
1627   <RBRACE>
1628 }
1629
1630 void BlockStatement() :
1631 {}
1632 {
1633   Statement()
1634 |
1635   ClassDeclaration()
1636 |
1637   MethodDeclaration()
1638 }
1639
1640 void LocalVariableDeclaration() :
1641 {}
1642 {
1643   VariableDeclarator() ( <COMMA> VariableDeclarator() )*
1644 }
1645
1646 void EmptyStatement() :
1647 {}
1648 {
1649   <SEMICOLON>
1650 }
1651
1652 void StatementExpression() :
1653 {}
1654 {
1655   PreIncrementExpression()
1656 |
1657   PreDecrementExpression()
1658 |
1659   PrimaryExpression()
1660   [
1661    <INCR>
1662   |
1663     <DECR>
1664   |
1665     AssignmentOperator() Expression()
1666   ]
1667 }
1668
1669 void SwitchStatement() :
1670 {}
1671 {
1672   <SWITCH> <LPAREN> Expression() <RPAREN> <LBRACE>
1673     ( SwitchLabel() ( BlockStatement() )* )*
1674   <RBRACE>
1675 }
1676
1677 void SwitchLabel() :
1678 {}
1679 {
1680   <CASE> Expression() <COLON>
1681 |
1682   <_DEFAULT> <COLON>
1683 }
1684
1685 void IfStatement() :
1686 /*
1687  * The disambiguating algorithm of JavaCC automatically binds dangling
1688  * else's to the innermost if statement.  The LOOKAHEAD specification
1689  * is to tell JavaCC that we know what we are doing.
1690  */
1691 {}
1692 {
1693   <IF> Condition("if") Statement() ( LOOKAHEAD(1) ElseIfStatement() )* [ LOOKAHEAD(1) <ELSE> Statement() ]
1694 }
1695
1696 void Condition(String keyword) :
1697 {}
1698 {
1699   try {
1700     <LPAREN>
1701   } catch (ParseException e) {
1702     errorMessage = "'(' expected after " + keyword + " keyword";
1703     errorLevel   = ERROR;
1704     throw e;
1705   }
1706   Expression()
1707   try {
1708      <RPAREN>
1709   } catch (ParseException e) {
1710     errorMessage = "')' expected after " + keyword + " keyword";
1711     errorLevel   = ERROR;
1712     throw e;
1713   }
1714 }
1715
1716 void ElseIfStatement() :
1717 {}
1718 {
1719   <ELSEIF> Condition("elseif") Statement()
1720 }
1721
1722 void WhileStatement() :
1723 {}
1724 {
1725   <WHILE> Condition("while") WhileStatement0()
1726 }
1727
1728 void WhileStatement0() :
1729 {}
1730 {
1731   <COLON> (Statement())* <ENDWHILE>
1732   try {
1733     (<SEMICOLON> | "?>")
1734   } catch (ParseException e) {
1735     errorMessage = "';' expected";
1736     errorLevel   = ERROR;
1737     throw e;
1738   }
1739 |
1740   Statement()
1741 }
1742
1743 void DoStatement() :
1744 {}
1745 {
1746   <DO> Statement() <WHILE> Condition("while")
1747   try {
1748     (<SEMICOLON> | "?>")
1749   } catch (ParseException e) {
1750     errorMessage = "';' expected";
1751     errorLevel   = ERROR;
1752     throw e;
1753   }
1754 }
1755
1756 void ForStatement() :
1757 {}
1758 {
1759   <FOR> <LPAREN> [ ForInit() ] <SEMICOLON> [ Expression() ] <SEMICOLON> [ ForUpdate() ] <RPAREN> Statement()
1760 }
1761
1762 void ForInit() :
1763 {}
1764 {
1765   LOOKAHEAD(LocalVariableDeclaration())
1766   LocalVariableDeclaration()
1767 |
1768   StatementExpressionList()
1769 }
1770
1771 void StatementExpressionList() :
1772 {}
1773 {
1774   StatementExpression() ( <COMMA> StatementExpression() )*
1775 }
1776
1777 void ForUpdate() :
1778 {}
1779 {
1780   StatementExpressionList()
1781 }
1782
1783 void BreakStatement() :
1784 {}
1785 {
1786   <BREAK> [ <IDENTIFIER> ] <SEMICOLON>
1787 }
1788
1789 void ContinueStatement() :
1790 {}
1791 {
1792   <CONTINUE> [ <IDENTIFIER> ] <SEMICOLON>
1793 }
1794
1795 void ReturnStatement() :
1796 {}
1797 {
1798   <RETURN> [ Expression() ] <SEMICOLON>
1799 }