7c68a468a44835b19d6ea12f369dd78d12ad346f
[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 |    < STRING_2:
407       "'"
408       (   (~["'"]))*
409
410       "'"
411     >
412 |   < STRING_3:
413       "`"
414       (   (~["`"]))*
415       "`"
416     >
417 }
418
419 /* IDENTIFIERS */
420
421 <PHPPARSING> TOKEN :
422 {
423   < IDENTIFIER: (<LETTER>|<SPECIAL>) (<LETTER>|<DIGIT>|<SPECIAL>)* >
424 |
425   < #LETTER:
426       ["a"-"z"] | ["A"-"Z"]
427   >
428 |
429   < #DIGIT:
430       ["0"-"9"]
431   >
432 |
433   < #SPECIAL:
434     "_"
435   >
436 }
437
438 /* SEPARATORS */
439
440 <PHPPARSING> TOKEN :
441 {
442   < LPAREN: "(" >
443 | < RPAREN: ")" >
444 | < LBRACE: "{" >
445 | < RBRACE: "}" >
446 | < LBRACKET: "[" >
447 | < RBRACKET: "]" >
448 | < SEMICOLON: ";" >
449 | < COMMA: "," >
450 | < DOT: "." >
451 }
452
453 /* OPERATORS */
454
455 <PHPPARSING> TOKEN :
456 {
457   <AT     : "@">
458 | <DOLLAR : "$">
459 | < ASSIGN: "=" >
460 | < GT: ">" >
461 | < LT: "<" >
462 | < BANG: "!" >
463 | < HOOK: "?" >
464 | < COLON: ":" >
465 | < EQ: "==" >
466 | < LE: "<=" >
467 | < GE: ">=" >
468 | < NE: "!=" >
469 | < SC_OR: "||" >
470 | < SC_AND: "&&" >
471 | < INCR: "++" >
472 | < DECR: "--" >
473 | < PLUS: "+" >
474 | < MINUS: "-" >
475 | < STAR: "*" >
476 | < SLASH: "/" >
477 | < BIT_AND: "&" >
478 | < BIT_OR: "|" >
479 | < XOR: "^" >
480 | < REM: "%" >
481 | < LSHIFT: "<<" >
482 | < RSIGNEDSHIFT: ">>" >
483 | < RUNSIGNEDSHIFT: ">>>" >
484 | < PLUSASSIGN: "+=" >
485 | < MINUSASSIGN: "-=" >
486 | < STARASSIGN: "*=" >
487 | < SLASHASSIGN: "/=" >
488 | < ANDASSIGN: "&=" >
489 | < ORASSIGN: "|=" >
490 | < XORASSIGN: "^=" >
491 | < DOTASSIGN: ".=" >
492 | < REMASSIGN: "%=" >
493 | < LSHIFTASSIGN: "<<=" >
494 | < RSIGNEDSHIFTASSIGN: ">>=" >
495 | < RUNSIGNEDSHIFTASSIGN: ">>>=" >
496 }
497
498 <PHPPARSING> TOKEN :
499 {
500   < DOLLAR_ID: <DOLLAR> <IDENTIFIER>  >
501 }
502
503 /*****************************************
504  * THE JAVA LANGUAGE GRAMMAR STARTS HERE *
505  *****************************************/
506
507 /*
508  * Program structuring syntax follows.
509  */
510
511 void phpTest() :
512 {}
513 {
514   Php()
515   <EOF>
516 }
517
518 void phpFile() :
519 {}
520 {
521   (<PHPSTART> Php() <PHPEND>)*
522   <EOF>
523 }
524
525 void Php() :
526 {}
527 {
528   (BlockStatement())*
529 }
530
531 void ClassDeclaration() :
532 {
533   PHPClassDeclaration classDeclaration;
534   Token className;
535   int pos = jj_input_stream.bufpos;
536 }
537 {
538   <CLASS> className = <IDENTIFIER> [ <EXTENDS> <IDENTIFIER> ]
539   {
540     classDeclaration = new PHPClassDeclaration(currentSegment,className.image,pos);
541     currentSegment.add(classDeclaration);
542     currentSegment = classDeclaration;
543   }
544   ClassBody()
545   {
546     currentSegment = (PHPSegmentWithChildren) currentSegment.getParent();
547   }
548 }
549
550 void ClassBody() :
551 {}
552 {
553   try {
554     <LBRACE>
555   } catch (ParseException e) {
556     errorMessage = "'{' expected";
557     errorLevel   = ERROR;
558     throw e;
559   }
560   ( ClassBodyDeclaration() )*
561   try {
562     <RBRACE>
563   } catch (ParseException e) {
564     errorMessage = "'var', 'function' or '}' expected";
565     errorLevel   = ERROR;
566     throw e;
567   }
568 }
569
570 void ClassBodyDeclaration() :
571 {}
572 {
573   MethodDeclaration()
574 |
575   FieldDeclaration()
576 }
577
578 void FieldDeclaration() :
579 {
580   PHPVarDeclaration variableDeclaration;
581 }
582 {
583   <VAR> variableDeclaration = VariableDeclarator()
584   {currentSegment.add(variableDeclaration);}
585   ( <COMMA>
586     variableDeclaration = VariableDeclarator()
587     {currentSegment.add(variableDeclaration);}
588   )*
589   try {
590     <SEMICOLON>
591   } catch (ParseException e) {
592     errorMessage = "';' expected after variable declaration";
593     errorLevel   = ERROR;
594     throw e;
595   }
596 }
597
598 PHPVarDeclaration VariableDeclarator() :
599 {
600   String varName;
601   String varValue = null;
602   int pos;
603 }
604 {
605   varName = VariableDeclaratorId()
606   {pos = jj_input_stream.tokenBegin;}
607   [
608     <ASSIGN>
609     try {
610       varValue = VariableInitializer()
611     } catch (ParseException e) {
612       errorMessage = "Literal expression expected in variable initializer";
613       errorLevel   = ERROR;
614       throw e;
615     }
616   ]
617   {
618     if (varValue == null) {
619       return new PHPVarDeclaration(currentSegment,varName,pos);
620     }
621     return new PHPVarDeclaration(currentSegment,varName,pos,varValue);
622   }
623 }
624
625 String VariableDeclaratorId() :
626 {
627   String expr;
628   StringBuffer buff = new StringBuffer();
629 }
630 {
631   try {
632     expr = Variable()
633     {buff.append(expr);}
634     ( LOOKAHEAD(2) expr = VariableSuffix()
635     {buff.append(expr);}
636     )*
637     {return buff.toString();}
638   } catch (ParseException e) {
639     errorMessage = "'$' expected for variable identifier";
640     errorLevel   = ERROR;
641     throw e;
642   }
643 }
644
645 String Variable():
646 {
647   String expr = null;
648   Token token;
649 }
650 {
651   token = <DOLLAR_ID> [<LBRACE> expr = Expression() <RBRACE>]
652   {
653     if (expr == null) {
654       return token.image;
655     }
656     return token + "{" + expr + "}";
657   }
658 |
659   <DOLLAR> expr = VariableName()
660   {return "$" + expr;}
661 }
662
663 String VariableName():
664 {
665 String expr = null;
666 Token token;
667 }
668 {
669   <LBRACE> expr = Expression() <RBRACE>
670   {return "{"+expr+"}";}
671 |
672   token = <IDENTIFIER> [<LBRACE> expr = Expression() <RBRACE>]
673   {
674     if (expr == null) {
675       return token.image;
676     }
677     return token + "{" + expr + "}";
678   }
679 |
680   <DOLLAR> expr = VariableName()
681   {return "$" + expr;}
682 |
683   token = <DOLLAR_ID> [expr = VariableName()]
684   {
685   if (expr == null) {
686     return token.image;
687   }
688   return token.image + expr;
689   }
690 }
691
692 String VariableInitializer() :
693 {
694   String expr;
695 }
696 {
697   expr = Literal()
698   {return expr;}
699 }
700
701 String ArrayVariable() :
702 {
703 String expr;
704 StringBuffer buff = new StringBuffer();
705 }
706 {
707   expr = Expression()
708   {buff.append(expr);}
709    [<ARRAYASSIGN> expr = Expression()
710    {buff.append("=>").append(expr);}]
711   {return buff.toString();}
712 }
713
714 String ArrayInitializer() :
715 {
716 String expr = null;
717 StringBuffer buff = new StringBuffer("(");
718 }
719 {
720   <LPAREN> [ expr = ArrayVariable()
721             {buff.append(expr);}
722             ( LOOKAHEAD(2) <COMMA> expr = ArrayVariable()
723             {buff.append(",").append(expr);}
724             )* ]
725   <RPAREN>
726   {
727     buff.append(")");
728     return buff.toString();
729   }
730 }
731
732 void MethodDeclaration() :
733 {
734   PHPFunctionDeclaration functionDeclaration;
735 }
736 {
737   <FUNCTION> functionDeclaration = MethodDeclarator()
738   {
739     currentSegment.add(functionDeclaration);
740     currentSegment = functionDeclaration;
741   }
742   Block()
743   {
744     currentSegment = (PHPSegmentWithChildren) currentSegment.getParent();
745   }
746 }
747
748 PHPFunctionDeclaration MethodDeclarator() :
749 {
750   Token identifier;
751   StringBuffer methodDeclaration = new StringBuffer();
752   String formalParameters;
753   int pos = jj_input_stream.bufpos;
754 }
755 {
756   [ <BIT_AND> {methodDeclaration.append("&");} ]
757   identifier = <IDENTIFIER>
758   {methodDeclaration.append(identifier);}
759     formalParameters = FormalParameters()
760   {
761     methodDeclaration.append(formalParameters);
762     return new PHPFunctionDeclaration(currentSegment,methodDeclaration.toString(),pos);
763   }
764 }
765
766 String FormalParameters() :
767 {
768   String expr;
769   final StringBuffer buff = new StringBuffer("(");
770 }
771 {
772   try {
773   <LPAREN>
774   } catch (ParseException e) {
775     errorMessage = "Formal parameter expected after function identifier";
776     errorLevel   = ERROR;
777     jj_consume_token(token.kind);
778   }
779             [ expr = FormalParameter()
780               {buff.append(expr);}
781             (
782                 <COMMA> expr = FormalParameter()
783                 {buff.append(",").append(expr);}
784             )*
785             ]
786   try {
787     <RPAREN>
788   } catch (ParseException e) {
789     errorMessage = "')' expected";
790     errorLevel   = ERROR;
791     throw e;
792   }
793  {
794   buff.append(")");
795   return buff.toString();
796  }
797 }
798
799 String FormalParameter() :
800 {
801   PHPVarDeclaration variableDeclaration;
802   StringBuffer buff = new StringBuffer();
803 }
804 {
805   [<BIT_AND> {buff.append("&");}] variableDeclaration = VariableDeclarator()
806   {
807     buff.append(variableDeclaration.toString());
808     return buff.toString();
809   }
810 }
811
812 String Type() :
813 {}
814 {
815   <STRING>
816   {return "string";}
817 |
818   <BOOL>
819   {return "bool";}
820 |
821   <BOOLEAN>
822   {return "boolean";}
823 |
824   <REAL>
825   {return "real";}
826 |
827   <DOUBLE>
828   {return "double";}
829 |
830   <FLOAT>
831   {return "float";}
832 |
833   <INT>
834   {return "int";}
835 |
836   <INTEGER>
837   {return "integer";}
838 }
839
840 String Expression() :
841 {
842   String expr;
843   String assignOperator = null;
844   String expr2 = null;
845 }
846 {
847   expr = PrintExpression()
848   {return expr;}
849 |
850   expr = ConditionalExpression()
851   [
852     assignOperator = AssignmentOperator()
853     expr2 = Expression()
854   ]
855   {
856     if (expr2 == null) {
857       return expr;
858     } else {
859       return expr + assignOperator + expr2;
860     }
861   }
862 }
863
864 String AssignmentOperator() :
865 {
866   Token assignOperator;
867 }
868 {
869   <ASSIGN>
870 {return "=";}
871 | <STARASSIGN>
872 {return "*=";}
873 | <SLASHASSIGN>
874 {return "/=";}
875 | <REMASSIGN>
876 {return "%=";}
877 | <PLUSASSIGN>
878 {return "+=";}
879 | <MINUSASSIGN>
880 {return "-=";}
881 | <LSHIFTASSIGN>
882 {return "<<=";}
883 | <RSIGNEDSHIFTASSIGN>
884 {return ">>=";}
885 | <RUNSIGNEDSHIFTASSIGN>
886 {return ">>>=";}
887 | <ANDASSIGN>
888 {return "&=";}
889 | <XORASSIGN>
890 {return "|=";}
891 | <ORASSIGN>
892 {return "|=";}
893 | <DOTASSIGN>
894 {return ".=";}
895 }
896
897 String ConditionalExpression() :
898 {
899   String expr;
900   String expr2 = null;
901   String expr3 = null;
902 }
903 {
904   expr = ConditionalOrExpression() [ <HOOK> expr2 = Expression() <COLON> expr3 = ConditionalExpression() ]
905 {
906   if (expr3 == null) {
907     return expr;
908   } else {
909     return expr + "?" + expr2 + ":" + expr3;
910   }
911 }
912 }
913
914 String ConditionalOrExpression() :
915 {
916   String expr;
917   Token operator;
918   String expr2 = null;
919   StringBuffer buff = new StringBuffer();
920 }
921 {
922   expr = ConditionalAndExpression()
923   {
924     buff.append(expr);
925   }
926   (
927     (operator = <SC_OR> | operator = <_ORL>) expr2 = ConditionalAndExpression()
928     {
929       buff.append(operator.image);
930       buff.append(expr2);
931     }
932   )*
933   {
934     return buff.toString();
935   }
936 }
937
938 String ConditionalAndExpression() :
939 {
940   String expr;
941   Token operator;
942   String expr2 = null;
943   StringBuffer buff = new StringBuffer();
944 }
945 {
946   expr = ConcatExpression()
947   {
948     buff.append(expr);
949   }
950   (
951   (operator = <SC_AND> | operator = <_ANDL>) expr2 = ConcatExpression()
952     {
953       buff.append(operator.image);
954       buff.append(expr2);
955     }
956   )*
957   {
958     return buff.toString();
959   }
960 }
961
962 String ConcatExpression() :
963 {
964   String expr;
965   String expr2 = null;
966   StringBuffer buff = new StringBuffer();
967 }
968 {
969   expr = InclusiveOrExpression()
970   {
971     buff.append(expr);
972   }
973   (
974   <DOT> expr2 = InclusiveOrExpression()
975   {
976     buff.append(".");
977     buff.append(expr2);
978   }
979   )*
980   {
981     return buff.toString();
982   }
983 }
984
985 String InclusiveOrExpression() :
986 {
987   String expr;
988   String expr2 = null;
989   StringBuffer buff = new StringBuffer();
990 }
991 {
992   expr = ExclusiveOrExpression()
993   {
994     buff.append(expr);
995   }
996   (
997   <BIT_OR> expr2 = ExclusiveOrExpression()
998   {
999     buff.append("|");
1000     buff.append(expr2);
1001   }
1002   )*
1003   {
1004     return buff.toString();
1005   }
1006 }
1007
1008 String ExclusiveOrExpression() :
1009 {
1010   String expr;
1011   String expr2 = null;
1012   StringBuffer buff = new StringBuffer();
1013 }
1014 {
1015   expr = AndExpression()
1016   {
1017     buff.append(expr);
1018   }
1019   (
1020     <XOR> expr2 = AndExpression()
1021   {
1022     buff.append("^");
1023     buff.append(expr2);
1024   }
1025   )*
1026   {
1027     return buff.toString();
1028   }
1029 }
1030
1031 String AndExpression() :
1032 {
1033   String expr;
1034   String expr2 = null;
1035   StringBuffer buff = new StringBuffer();
1036 }
1037 {
1038   expr = EqualityExpression()
1039   {
1040     buff.append(expr);
1041   }
1042   (
1043     <BIT_AND> expr2 = EqualityExpression()
1044   {
1045     buff.append("&");
1046     buff.append(expr2);
1047   }
1048   )*
1049   {
1050     return buff.toString();
1051   }
1052 }
1053
1054 String EqualityExpression() :
1055 {
1056   String expr;
1057   Token operator;
1058   String expr2;
1059   StringBuffer buff = new StringBuffer();
1060 }
1061 {
1062   expr = RelationalExpression()
1063   {buff.append(expr);}
1064   (
1065   ( operator = <EQ> | operator = <NE> ) expr2 = RelationalExpression()
1066   {
1067     buff.append(operator.image);
1068     buff.append(expr2);
1069   }
1070   )*
1071   {return buff.toString();}
1072 }
1073
1074 String RelationalExpression() :
1075 {
1076   String expr;
1077   Token operator;
1078   String expr2;
1079   StringBuffer buff = new StringBuffer();
1080 }
1081 {
1082   expr = ShiftExpression()
1083   {buff.append(expr);}
1084   (
1085   ( operator = <LT> | operator = <GT> | operator = <LE> | operator = <GE> ) expr2 = ShiftExpression()
1086   {
1087     buff.append(operator.image);
1088     buff.append(expr2);
1089   }
1090   )*
1091   {return buff.toString();}
1092 }
1093
1094 String ShiftExpression() :
1095 {
1096   String expr;
1097   Token operator;
1098   String expr2;
1099   StringBuffer buff = new StringBuffer();
1100 }
1101 {
1102   expr = AdditiveExpression()
1103   {buff.append(expr);}
1104   (
1105   (operator = <LSHIFT> | operator = <RSIGNEDSHIFT> | operator = <RUNSIGNEDSHIFT> ) expr2 = AdditiveExpression()
1106   {
1107     buff.append(operator.image);
1108     buff.append(expr2);
1109   }
1110   )*
1111   {return buff.toString();}
1112 }
1113
1114 String AdditiveExpression() :
1115 {
1116   String expr;
1117   Token operator;
1118   String expr2;
1119   StringBuffer buff = new StringBuffer();
1120 }
1121 {
1122   expr = MultiplicativeExpression()
1123   {buff.append(expr);}
1124   (
1125    ( operator = <PLUS> | operator = <MINUS> ) expr2 = MultiplicativeExpression()
1126   {
1127     buff.append(operator.image);
1128     buff.append(expr2);
1129   }
1130    )*
1131   {return buff.toString();}
1132 }
1133
1134 String MultiplicativeExpression() :
1135 {
1136   String expr, expr2;
1137   Token operator;
1138   final StringBuffer buff = new StringBuffer();}
1139 {
1140   expr = UnaryExpression()
1141   {buff.append(expr);}
1142   (
1143   ( operator = <STAR> | operator = <SLASH> | operator = <REM> ) expr2 = UnaryExpression()
1144   {
1145     buff.append(operator.image);
1146     buff.append(expr2);
1147   }
1148   )*
1149   {return buff.toString();}
1150 }
1151
1152 String UnaryExpression() :
1153 {
1154   String expr;
1155   final StringBuffer buff = new StringBuffer();
1156 }
1157 {
1158   <AT> expr = UnaryExpression()
1159   {return "@" + expr;}
1160 |
1161   ( <PLUS> {buff.append("+");}| <MINUS> {buff.append("-");}) expr = UnaryExpression()
1162   {
1163     buff.append(expr);
1164     return buff.toString();
1165   }
1166 |
1167   expr = PreIncrementExpression()
1168   {return expr;}
1169 |
1170   expr = PreDecrementExpression()
1171   {return expr;}
1172 |
1173   expr = UnaryExpressionNotPlusMinus()
1174   {return expr;}
1175 }
1176
1177 String PreIncrementExpression() :
1178 {
1179 String expr;
1180 }
1181 {
1182   <INCR> expr = PrimaryExpression()
1183   {return "++"+expr;}
1184 }
1185
1186 String PreDecrementExpression() :
1187 {
1188 String expr;
1189 }
1190 {
1191   <DECR> expr = PrimaryExpression()
1192   {return "--"+expr;}
1193 }
1194
1195 String UnaryExpressionNotPlusMinus() :
1196 {
1197   String expr;
1198 }
1199 {
1200   <BANG> expr = UnaryExpression()
1201   {return "!" + expr;}
1202 |
1203   LOOKAHEAD( <LPAREN> Type() <RPAREN> )
1204   expr = CastExpression()
1205   {return expr;}
1206 |
1207   expr = PostfixExpression()
1208   {return expr;}
1209 |
1210   expr = Literal()
1211   {return expr;}
1212 |
1213   <LPAREN> expr = Expression()<RPAREN>
1214   {return "("+expr+")";}
1215 }
1216
1217 String CastExpression() :
1218 {
1219 String type;
1220 String expr;
1221 }
1222 {
1223   <LPAREN> type = Type() <RPAREN> expr = UnaryExpression()
1224   {return "(" + type + ")" + expr;}
1225 }
1226
1227 String PostfixExpression() :
1228 {
1229   String expr;
1230   Token operator = null;
1231 }
1232 {
1233   expr = PrimaryExpression() [ operator = <INCR> | operator = <DECR> ]
1234   {
1235     if (operator == null) {
1236       return expr;
1237     }
1238     return expr + operator.image;
1239   }
1240 }
1241
1242 String PrimaryExpression() :
1243 {
1244   Token identifier;
1245   String expr;
1246   final StringBuffer buff = new StringBuffer();
1247 }
1248 {
1249   LOOKAHEAD(2)
1250   identifier = <IDENTIFIER> <STATICCLASSACCESS> expr = ClassIdentifier()
1251   {buff.append(identifier.image).append("::").append(expr);}
1252   (
1253   expr = PrimarySuffix()
1254   {buff.append(expr);}
1255   )*
1256   {return buff.toString();}
1257 |
1258   expr = PrimaryPrefix()  {buff.append(expr);}
1259   ( expr = PrimarySuffix()  {buff.append(expr);} )*
1260   {return buff.toString();}
1261 |
1262   <ARRAY> expr = ArrayInitializer()
1263   {return "array" + expr;}
1264 }
1265
1266 String PrimaryPrefix() :
1267 {
1268   String expr;
1269   Token token = null;
1270 }
1271 {
1272   token = <IDENTIFIER>
1273   {return token.image;}
1274 |
1275   [token = <BIT_AND>] <NEW> expr = ClassIdentifier()
1276   {
1277     if (token == null) {
1278       return "new " + expr;
1279     }
1280     return "new &" + expr;
1281   }
1282 |  
1283   expr = VariableDeclaratorId()
1284   {return expr;}
1285 }
1286
1287 String ClassIdentifier():
1288 {
1289   String expr;
1290   Token token;
1291 }
1292 {
1293   token = <IDENTIFIER>
1294   {return token.image;}
1295 |
1296   expr = VariableDeclaratorId()
1297   {return expr;}
1298 }
1299
1300 String PrimarySuffix() :
1301 {
1302   String expr;
1303 }
1304 {
1305   expr = Arguments()
1306   {return expr;}
1307 |
1308   expr = VariableSuffix()
1309   {return expr;}
1310 }
1311
1312 String VariableSuffix() :
1313 {
1314   String expr = null;
1315 }
1316 {
1317   <CLASSACCESS> expr = VariableName()
1318   {return "->" + expr;}
1319
1320   <LBRACKET> [ expr = Expression() ] <RBRACKET>
1321   {
1322     if(expr == null) {
1323       return "[]";
1324     }
1325     return "[" + expr + "]";
1326   }
1327 }
1328
1329 String Literal() :
1330 {
1331   String expr;
1332   Token token;
1333 }
1334 {
1335   token = <INTEGER_LITERAL>
1336   {return token.image;}
1337 |
1338   token = <FLOATING_POINT_LITERAL>
1339   {return token.image;}
1340 |
1341   try {
1342     token = <STRING_LITERAL>
1343   {return token.image;}
1344   } catch (TokenMgrError e) {
1345     errorMessage = "unterminated string";
1346     errorLevel   = ERROR;
1347     throw generateParseException();
1348   }
1349 |
1350   expr = BooleanLiteral()
1351   {return expr;}
1352 |
1353   expr = NullLiteral()
1354   {return expr;}
1355 }
1356
1357 String BooleanLiteral() :
1358 {}
1359 {
1360   <TRUE>
1361   {return "true";}
1362 |
1363   <FALSE>
1364   {return "false";}
1365 }
1366
1367 String NullLiteral() :
1368 {}
1369 {
1370   <NULL>
1371   {return "null";}
1372 }
1373
1374 String Arguments() :
1375 {
1376 String expr = null;
1377 }
1378 {
1379   <LPAREN> [ expr = ArgumentList() ]
1380   try {
1381     <RPAREN>
1382   } catch (ParseException e) {
1383     errorMessage = "')' expected to close the argument list";
1384     errorLevel   = ERROR;
1385     throw e;
1386   }
1387   {
1388   if (expr == null) {
1389     return "()";
1390   }
1391   return "(" + expr + ")";
1392   }
1393 }
1394
1395 String ArgumentList() :
1396 {
1397 String expr;
1398 StringBuffer buff = new StringBuffer();
1399 }
1400 {
1401   expr = Expression()
1402   {buff.append(expr);}
1403   ( <COMMA>
1404       try {
1405         expr = Expression()
1406       } catch (ParseException e) {
1407         errorMessage = "expression expected after a comma in argument list";
1408         errorLevel   = ERROR;
1409         throw e;
1410       }
1411     {
1412       buff.append(",").append("expr");
1413     }
1414    )*
1415    {return buff.toString();}
1416 }
1417
1418 /*
1419  * Statement syntax follows.
1420  */
1421
1422 void Statement() :
1423 {}
1424 {
1425   LOOKAHEAD(2)
1426   Expression()  (<SEMICOLON> | "?>")
1427 |
1428   LOOKAHEAD(2)
1429   LabeledStatement()
1430 |
1431   Block()
1432 |
1433   EmptyStatement()
1434 |
1435   StatementExpression()
1436   try {
1437     <SEMICOLON>
1438   } catch (ParseException e) {
1439     errorMessage = "';' expected after expression";
1440     errorLevel   = ERROR;
1441     throw e;
1442   }
1443 |
1444   SwitchStatement()
1445 |
1446   IfStatement()
1447 |
1448   WhileStatement()
1449 |
1450   DoStatement()
1451 |
1452   ForStatement()
1453 |
1454   BreakStatement()
1455 |
1456   ContinueStatement()
1457 |
1458   ReturnStatement()
1459 |
1460   EchoStatement()
1461 |
1462   [<AT>] IncludeStatement()
1463 |
1464   StaticStatement()
1465 |
1466   GlobalStatement()
1467 }
1468
1469 void IncludeStatement() :
1470 {
1471   Token token;
1472   String expr;
1473   int pos;
1474 }
1475 {
1476   token = <REQUIRE>
1477   {pos = token.beginLine;}
1478   expr = Expression()
1479   {currentSegment.add(new PHPReqIncDeclaration(currentSegment, "require",pos,expr));}
1480   (<SEMICOLON> | "?>")
1481 |
1482   token = <REQUIRE_ONCE>
1483   {pos = token.beginLine;}
1484   expr = Expression()
1485   {currentSegment.add(new PHPReqIncDeclaration(currentSegment, "require_once",pos,expr));}
1486   (<SEMICOLON> | "?>")
1487 |
1488   token = <INCLUDE>
1489   {pos = token.beginLine;}
1490   expr = Expression()
1491   {currentSegment.add(new PHPReqIncDeclaration(currentSegment, "include",pos,expr));}
1492   (<SEMICOLON> | "?>")
1493 |
1494   token = <INCLUDE_ONCE>
1495   {pos = token.beginLine;}
1496   expr = Expression()
1497   {currentSegment.add(new PHPReqIncDeclaration(currentSegment, "include_once",pos,expr));}
1498   (<SEMICOLON> | "?>")
1499 }
1500
1501 String PrintExpression() :
1502 {
1503   StringBuffer buff = new StringBuffer("print ");
1504   String expr;
1505 }
1506 {
1507   <PRINT> expr = Expression()
1508   {
1509     buff.append(expr);
1510     return buff.toString();
1511   }
1512 }
1513
1514 void EchoStatement() :
1515 {}
1516 {
1517   <ECHO> Expression() (<COMMA> Expression())*
1518   try {
1519     (<SEMICOLON> | "?>")
1520   } catch (ParseException e) {
1521     errorMessage = "';' expected after 'echo' statement";
1522     errorLevel   = ERROR;
1523     throw e;
1524   }
1525 }
1526
1527 void GlobalStatement() :
1528 {}
1529 {
1530   <GLOBAL> VariableDeclaratorId() (<COMMA> VariableDeclaratorId())* (<SEMICOLON> | "?>")
1531 }
1532
1533 void StaticStatement() :
1534 {}
1535 {
1536   <STATIC> VariableDeclarator() (<COMMA> VariableDeclarator())* (<SEMICOLON> | "?>")
1537 }
1538
1539 void LabeledStatement() :
1540 {}
1541 {
1542   <IDENTIFIER> <COLON> Statement()
1543 }
1544
1545 void Block() :
1546 {}
1547 {
1548   try {
1549     <LBRACE>
1550   } catch (ParseException e) {
1551     errorMessage = "'{' expected";
1552     errorLevel   = ERROR;
1553     throw e;
1554   }
1555   ( BlockStatement() )*
1556   <RBRACE>
1557 }
1558
1559 void BlockStatement() :
1560 {}
1561 {
1562   Statement()
1563 |
1564   ClassDeclaration()
1565 |
1566   MethodDeclaration()
1567 }
1568
1569 void LocalVariableDeclaration() :
1570 {}
1571 {
1572   VariableDeclarator() ( <COMMA> VariableDeclarator() )*
1573 }
1574
1575 void EmptyStatement() :
1576 {}
1577 {
1578   <SEMICOLON>
1579 }
1580
1581 void StatementExpression() :
1582 /*
1583  * The last expansion of this production accepts more than the legal
1584  * Java expansions for StatementExpression.  This expansion does not
1585  * use PostfixExpression for performance reasons.
1586  */
1587 {}
1588 {
1589   PreIncrementExpression()
1590 |
1591   PreDecrementExpression()
1592 |
1593   PrimaryExpression()
1594   [
1595    <INCR>
1596   |
1597     <DECR>
1598   |
1599     AssignmentOperator() Expression()
1600   ]
1601 }
1602
1603 void SwitchStatement() :
1604 {}
1605 {
1606   <SWITCH> <LPAREN> Expression() <RPAREN> <LBRACE>
1607     ( SwitchLabel() ( BlockStatement() )* )*
1608   <RBRACE>
1609 }
1610
1611 void SwitchLabel() :
1612 {}
1613 {
1614   <CASE> Expression() <COLON>
1615 |
1616   <_DEFAULT> <COLON>
1617 }
1618
1619 void IfStatement() :
1620 /*
1621  * The disambiguating algorithm of JavaCC automatically binds dangling
1622  * else's to the innermost if statement.  The LOOKAHEAD specification
1623  * is to tell JavaCC that we know what we are doing.
1624  */
1625 {}
1626 {
1627   <IF> Condition("if") Statement() ( LOOKAHEAD(1) ElseIfStatement() )* [ LOOKAHEAD(1) <ELSE> Statement() ]
1628 }
1629
1630 void Condition(String keyword) :
1631 {}
1632 {
1633   try {
1634     <LPAREN>
1635   } catch (ParseException e) {
1636     errorMessage = "'(' expected after " + keyword + " keyword";
1637     errorLevel   = ERROR;
1638     throw e;
1639   }
1640   Expression()
1641   try {
1642      <RPAREN>
1643   } catch (ParseException e) {
1644     errorMessage = "')' expected after " + keyword + " keyword";
1645     errorLevel   = ERROR;
1646     throw e;
1647   }
1648 }
1649
1650 void ElseIfStatement() :
1651 {}
1652 {
1653   <ELSEIF> Condition("elseif") Statement()
1654 }
1655
1656 void WhileStatement() :
1657 {}
1658 {
1659   <WHILE> Condition("while") WhileStatement0()
1660 }
1661
1662 void WhileStatement0() :
1663 {}
1664 {
1665   <COLON> (Statement())* <ENDWHILE> (<SEMICOLON> | "?>")
1666 |
1667   Statement()
1668 }
1669
1670 void DoStatement() :
1671 {}
1672 {
1673   <DO> Statement() <WHILE> Condition("while") (<SEMICOLON> | "?>")
1674 }
1675
1676 void ForStatement() :
1677 {}
1678 {
1679   <FOR> <LPAREN> [ ForInit() ] <SEMICOLON> [ Expression() ] <SEMICOLON> [ ForUpdate() ] <RPAREN> Statement()
1680 }
1681
1682 void ForInit() :
1683 {}
1684 {
1685   LOOKAHEAD(LocalVariableDeclaration())
1686   LocalVariableDeclaration()
1687 |
1688   StatementExpressionList()
1689 }
1690
1691 void StatementExpressionList() :
1692 {}
1693 {
1694   StatementExpression() ( <COMMA> StatementExpression() )*
1695 }
1696
1697 void ForUpdate() :
1698 {}
1699 {
1700   StatementExpressionList()
1701 }
1702
1703 void BreakStatement() :
1704 {}
1705 {
1706   <BREAK> [ <IDENTIFIER> ] <SEMICOLON>
1707 }
1708
1709 void ContinueStatement() :
1710 {}
1711 {
1712   <CONTINUE> [ <IDENTIFIER> ] <SEMICOLON>
1713 }
1714
1715 void ReturnStatement() :
1716 {}
1717 {
1718   <RETURN> [ Expression() ] <SEMICOLON>
1719 }