981cd7f042d4c7c199c0e29d512a82ffc5ece5c6
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / phpeditor / phpparser / PHPParser.java
1 /**********************************************************************
2 Copyright (c) 2002 Klaus Hartlage - www.eclipseproject.de
3 All rights reserved. This program and the accompanying materials
4 are made available under the terms of the Common Public License v1.0
5 which accompanies this distribution, and is available at
6 http://www.eclipse.org/legal/cpl-v10.html
7
8 Contributors:
9     Klaus Hartlage - www.eclipseproject.de
10 **********************************************************************/
11 package net.sourceforge.phpeclipse.phpeditor.phpparser;
12
13 import java.text.MessageFormat;
14 import java.util.ArrayList;
15 import java.util.HashMap;
16 import java.util.Hashtable;
17
18 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
19 import net.sourceforge.phpeclipse.actions.PHPStartApacheAction;
20 import net.sourceforge.phpeclipse.phpeditor.PHPString;
21 import net.sourceforge.phpeclipse.phpeditor.php.PHPKeywords;
22 import org.eclipse.core.resources.IFile;
23 import org.eclipse.core.resources.IMarker;
24 import org.eclipse.core.runtime.CoreException;
25 import org.eclipse.core.runtime.IPath;
26 import org.eclipse.jface.preference.IPreferenceStore;
27 import org.eclipse.ui.texteditor.MarkerUtilities;
28
29 public class PHPParser extends PHPKeywords {
30   // strings for external parser call
31   private static final String PARSE_ERROR_STRING = "Parse error"; //$NON-NLS-1$
32   private static final String PARSE_WARNING_STRING = "Warning"; //$NON-NLS-1$
33
34   public static final int ERROR = 2;
35   public static final int WARNING = 1;
36   public static final int INFO = 0;
37   private IFile fileToParse;
38   private ArrayList phpList;
39
40   private int currentPHPString;
41   private boolean phpEnd;
42
43   private static HashMap keywordMap = null;
44   private String str;
45
46   // current character
47   char ch;
48   // current token
49   int token;
50
51   // row counter for syntax errors:
52   int rowCount;
53   // column counter for syntax errors:
54   int columnCount;
55
56   int chIndx;
57
58   // current identifier
59   String identifier;
60
61   Long longNumber;
62   Double doubleNumber;
63   private boolean phpMode;
64
65   final static int TT_EOF = 0;
66   final static int TT_UNDEFINED = 1;
67   final static int TT_HTML = 2;
68
69   final static int TT_MOD = 30;
70   final static int TT_NOT = 31;
71   final static int TT_DOT = 32;
72   final static int TT_POW = 33;
73   final static int TT_DIV = 34;
74   final static int TT_MULTIPLY = 35;
75   final static int TT_SUBTRACT = 36;
76   final static int TT_ADD = 37;
77   final static int TT_EQUAL = 38;
78   final static int TT_UNEQUAL = 39;
79   final static int TT_GREATER = 40;
80   final static int TT_GREATEREQUAL = 41;
81   final static int TT_LESS = 42;
82   final static int TT_LESSEQUAL = 43;
83   final static int TT_AND = 44;
84   final static int TT_OR = 45;
85   final static int TT_HASH = 46;
86   final static int TT_DDOT = 47;
87   final static int TT_DOTASSIGN = 48;
88
89   final static int TT_ASSIGN = 49;
90   final static int TT_REF = 50;
91   final static int TT_FOREACH = 51;
92   final static int TT_AMPERSAND = 52;
93   final static int TT_DOLLARLISTOPEN = 53;
94   final static int TT_TILDE = 54;
95   final static int TT_TILDEASSIGN = 55;
96   final static int TT_MODASSIGN = 56;
97   final static int TT_POWASSIGN = 57;
98   final static int TT_RSHIFTASSIGN = 58;
99   final static int TT_LSHIFTASSIGN = 59;
100   final static int TT_ANDASSIGN = 60;
101   final static int TT_QUESTIONMARK = 61;
102   final static int TT_DDOT2 = 62;
103   final static int TT_AT = 63;
104   // final static int TT_HEREDOC = 64;
105
106   final static int TT_DOLLAROPEN = 127;
107   final static int TT_ARGOPEN = 128;
108   final static int TT_ARGCLOSE = 129;
109   final static int TT_LISTOPEN = 130;
110   final static int TT_LISTCLOSE = 131;
111   final static int TT_PARTOPEN = 132;
112   final static int TT_PARTCLOSE = 133;
113   final static int TT_COMMA = 134;
114
115   final static int TT_STRING = 136;
116   final static int TT_IDENTIFIER = 138;
117   final static int TT_DIGIT = 139;
118   final static int TT_SEMICOLON = 140;
119   final static int TT_SLOT = 141;
120   final static int TT_SLOTSEQUENCE = 142;
121   final static int TT_DECREMENT = 144;
122   final static int TT_INCREMENT = 145;
123   final static int TT_ADDTO = 146;
124   final static int TT_DIVIDEBY = 147;
125   final static int TT_SUBTRACTFROM = 148;
126   final static int TT_TIMESBY = 149;
127   final static int TT_VARIABLE = 150;
128   final static int TT_INT_NUMBER = 151;
129   final static int TT_DOUBLE_NUMBER = 152;
130   final static int TT_INTERPOLATED_STRING = 153;
131   final static int TT_STRING_CONSTANT = 154;
132
133   final static int TT_LSHIFT = 155;
134   final static int TT_RSHIFT = 156;
135   final static int TT_EX_EQUAL = 157;
136   final static int TT_EX_UNEQUAL = 158;
137   final static int TT_LINE = 159;
138   //  final static int TT_AT = 153; // @
139   /**
140    *  Class Constructor.
141    *
142    *@param  s
143    *@param  sess  Description of Parameter
144    *@see
145    */
146   public PHPParser(IFile fileToParse) {
147     if (keywordMap == null) {
148       keywordMap = new HashMap();
149       for (int i = 0; i < PHP_KEYWORS.length; i++) {
150         keywordMap.put(PHP_KEYWORS[i], new Integer(PHP_KEYWORD_TOKEN[i]));
151       }
152     }
153     this.currentPHPString = 0;
154     this.fileToParse = fileToParse;
155     this.phpList = null;
156     this.str = "";
157     this.token = TT_EOF;
158     this.chIndx = 0;
159     this.rowCount = 1;
160     this.columnCount = 0;
161     this.phpEnd = false;
162
163     //   getNextToken();
164   }
165
166   /**
167    * Create marker for the parse error
168    */
169   private void setMarker(String message, int lineNumber, int errorLevel) throws CoreException {
170     setMarker(fileToParse, message, lineNumber, errorLevel);
171   }
172
173   public static void setMarker(IFile file, String message, int lineNumber, int errorLevel) throws CoreException {
174
175     Hashtable attributes = new Hashtable();
176     MarkerUtilities.setMessage(attributes, message);
177     switch (errorLevel) {
178       case ERROR :
179         attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_ERROR));
180         break;
181       case WARNING :
182         attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_WARNING));
183         break;
184       case INFO :
185         attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_INFO));
186         break;
187     }
188     MarkerUtilities.setLineNumber(attributes, lineNumber);
189     MarkerUtilities.createMarker(file, attributes, IMarker.PROBLEM);
190   }
191
192   private void throwSyntaxError(String error) {
193
194     if (str.length() < chIndx) {
195       chIndx--;
196     }
197     // read until end-of-line
198     int eol = chIndx;
199     while (str.length() > eol) {
200       ch = str.charAt(eol++);
201       if (ch == '\n') {
202         eol--;
203         break;
204       }
205     }
206     throw new SyntaxError(rowCount, chIndx - columnCount + 1, str.substring(columnCount, eol), error);
207   }
208
209   private void throwSyntaxError(String error, int startRow) {
210
211     throw new SyntaxError(startRow, 0, " ", error);
212   }
213
214   /**
215    *  Method Declaration.
216    *
217    *@see
218    */
219   private void getChar() {
220     if (str.length() > chIndx) {
221       ch = str.charAt(chIndx++);
222
223       return;
224     }
225
226     chIndx = str.length() + 1;
227     ch = ' ';
228     //  token = TT_EOF;
229     phpEnd = true;
230   }
231
232   private void getNextToken_OldVersion() throws CoreException {
233     phpEnd = false;
234
235     while (str.length() > chIndx) {
236       ch = str.charAt(chIndx++);
237       token = TT_UNDEFINED;
238       if (ch == '\n') {
239         rowCount++;
240         columnCount = chIndx;
241         continue; // while loop
242       }
243       if (str.length() == chIndx) {
244         phpEnd = true;
245       }
246       if (!Character.isWhitespace(ch)) {
247         if (ch == '$') {
248           if (str.length() > chIndx) {
249             if (str.charAt(chIndx) == '{') {
250               chIndx++;
251               token = TT_DOLLAROPEN;
252               return;
253             }
254           }
255           getIdentifier();
256           return;
257         }
258         if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch == '_') || (ch == '$')) {
259           getIdentifier();
260           return;
261         }
262         if (ch >= '0' && ch <= '9') {
263           getNumber();
264           return;
265         }
266         if (ch == '/') {
267           if (str.length() > chIndx) {
268             if (str.charAt(chIndx) == '/') {
269               chIndx++;
270               // read comment until end of line:
271               while ((str.length() > chIndx) && (str.charAt(chIndx) != '\n')) {
272                 chIndx++;
273               }
274               continue;
275             } else if (str.charAt(chIndx) == '*') {
276               chIndx++;
277               // multi line comment:
278               while (str.length() > chIndx) {
279                 if (str.charAt(chIndx) == '*' && (str.length() > (chIndx + 1)) && str.charAt(chIndx + 1) == '/') {
280                   chIndx += 2;
281                   break;
282                 }
283                 ch = str.charAt(chIndx++);
284                 if (ch == '\n') {
285                   rowCount++;
286                   columnCount = chIndx;
287                 }
288               }
289               continue;
290             }
291           }
292         } else if (ch == '#') {
293           // read comment until end of line:
294           while ((str.length() > chIndx) && (str.charAt(chIndx) != '\n')) {
295             chIndx++;
296           }
297           continue;
298         } else if (ch == '"') {
299           // read string until end
300           boolean openString = true;
301           while (str.length() > chIndx) {
302             ch = str.charAt(chIndx++);
303             if (ch == '\\') {
304               if (str.length() > chIndx) {
305                 ch = str.charAt(chIndx++);
306               }
307             } else if (ch == '"') {
308               openString = false;
309               break;
310             } else if (ch == '\n') {
311               rowCount++;
312               columnCount = chIndx;
313             }
314           }
315           if (openString) {
316             throwSyntaxError("Open string character '\"' at end of file.");
317           }
318           token = TT_INTERPOLATED_STRING;
319           return;
320         } else if (ch == '\'') {
321           // read string until end
322           boolean openString = true;
323           int startRow = rowCount;
324           while (str.length() > chIndx) {
325             ch = str.charAt(chIndx++);
326             if (ch == '\\') {
327               if (str.length() > chIndx) {
328                 ch = str.charAt(chIndx++);
329               }
330             } else if (ch == '\'') {
331               openString = false;
332               break;
333             } else if (ch == '\n') {
334               rowCount++;
335               columnCount = chIndx;
336             }
337           }
338           if (openString) {
339             throwSyntaxError("Open string character \"'\" at end of file.", startRow);
340           }
341           token = TT_STRING_CONSTANT;
342           return;
343         } else if (ch == '`') {
344           // read string until end
345           boolean openString = true;
346           int startRow = rowCount;
347           while (str.length() > chIndx) {
348             ch = str.charAt(chIndx++);
349             if (ch == '\\') {
350               if (str.length() > chIndx) {
351                 ch = str.charAt(chIndx++);
352               }
353             } else if (ch == '`') {
354               openString = false;
355               break;
356             } else if (ch == '\n') {
357               rowCount++;
358               columnCount = chIndx;
359             }
360           }
361           if (openString) {
362             throwSyntaxError("Open string character \"`\" at end of file.", startRow);
363           }
364           setMarker("Other string delimiters prefered (found \"`\").", rowCount, PHPParser.INFO);
365           token = TT_STRING_CONSTANT;
366           return;
367         }
368
369         switch (ch) {
370
371           case '(' :
372             token = TT_ARGOPEN;
373
374             break;
375           case ')' :
376             token = TT_ARGCLOSE;
377
378             break;
379           case '{' :
380             token = TT_LISTOPEN;
381
382             break;
383           case '}' :
384             token = TT_LISTCLOSE;
385
386             break;
387           case '[' :
388             token = TT_PARTOPEN;
389
390             break;
391           case ']' :
392             token = TT_PARTCLOSE;
393
394             break;
395           case ',' :
396             token = TT_COMMA;
397
398             break;
399           case '?' :
400             token = TT_QUESTIONMARK;
401             break;
402           case '@' :
403             token = TT_AT;
404             break;
405           case '~' :
406             token = TT_TILDE;
407             if (str.length() > chIndx) {
408               if (str.charAt(chIndx) == '=') {
409                 chIndx++;
410                 token = TT_TILDEASSIGN;
411
412                 break;
413               }
414             }
415             break;
416           case '.' :
417             token = TT_DOT;
418             if (str.length() > chIndx) {
419               if (str.charAt(chIndx) == '=') {
420                 chIndx++;
421                 token = TT_DOTASSIGN;
422
423                 break;
424               }
425             }
426
427             break;
428           case '"' :
429             token = TT_STRING;
430
431             break;
432           case '%' :
433             token = TT_MOD;
434             if (str.length() > chIndx) {
435               if (str.charAt(chIndx) == '=') {
436                 chIndx++;
437                 token = TT_MODASSIGN;
438
439                 break;
440               }
441             }
442             break;
443           case ';' :
444             token = TT_SEMICOLON;
445
446             break;
447           case '^' :
448             token = TT_POW;
449             if (str.length() > chIndx) {
450               if (str.charAt(chIndx) == '=') {
451                 chIndx++;
452                 token = TT_POWASSIGN;
453
454                 break;
455               }
456             }
457             break;
458           case '/' :
459             token = TT_DIV;
460
461             if (str.length() > chIndx) {
462               if (str.charAt(chIndx) == '=') {
463                 chIndx++;
464                 token = TT_DIVIDEBY;
465
466                 break;
467               }
468             }
469
470             break;
471           case '*' :
472             token = TT_MULTIPLY;
473             if (str.length() > chIndx) {
474               if (str.charAt(chIndx) == '*') {
475                 chIndx++;
476                 token = TT_POW;
477
478                 break;
479               }
480               if (str.charAt(chIndx) == '=') {
481                 chIndx++;
482                 token = TT_TIMESBY;
483
484                 break;
485               }
486             }
487
488             break;
489           case '+' :
490             token = TT_ADD;
491             if (str.length() > chIndx) {
492               if (str.charAt(chIndx) == '+') {
493                 chIndx++;
494                 token = TT_INCREMENT;
495
496                 break;
497               }
498               if (str.charAt(chIndx) == '=') {
499                 chIndx++;
500                 token = TT_ADDTO;
501
502                 break;
503               }
504             }
505             break;
506           case '-' :
507             token = TT_SUBTRACT;
508             if (str.length() > chIndx) {
509               if (str.charAt(chIndx) == '-') {
510                 chIndx++;
511                 token = TT_DECREMENT;
512
513                 break;
514               }
515               if (str.charAt(chIndx) == '=') {
516                 chIndx++;
517                 token = TT_SUBTRACTFROM;
518
519                 break;
520               }
521               if (str.charAt(chIndx) == '>') {
522                 chIndx++;
523                 token = TT_REF;
524
525                 break;
526               }
527             }
528
529             break;
530           case '=' :
531             token = TT_ASSIGN;
532
533             if (str.length() > chIndx) {
534               ch = str.charAt(chIndx);
535
536               if (ch == '=') {
537                 chIndx++;
538                 token = TT_EQUAL;
539                 if (str.length() > chIndx) {
540                   ch = str.charAt(chIndx);
541
542                   if (ch == '=') {
543                     chIndx++;
544                     token = TT_EX_EQUAL;
545                   }
546                 }
547                 break;
548               }
549               if (ch == '>') {
550                 chIndx++;
551                 token = TT_FOREACH;
552
553                 break;
554               }
555             }
556
557             break;
558           case '!' :
559             token = TT_NOT;
560
561             if (str.length() > chIndx) {
562               if (str.charAt(chIndx) == '=') {
563                 chIndx++;
564                 token = TT_UNEQUAL;
565                 if (str.length() > chIndx) {
566                   ch = str.charAt(chIndx);
567
568                   if (ch == '=') {
569                     chIndx++;
570                     token = TT_EX_UNEQUAL;
571                   }
572                 }
573                 break;
574               }
575             }
576
577             break;
578           case '>' :
579             token = TT_GREATER;
580
581             if (str.length() > chIndx) {
582               if (str.charAt(chIndx) == '=') {
583                 chIndx++;
584                 token = TT_GREATEREQUAL;
585                 break;
586               }
587               if (str.charAt(chIndx) == '>') {
588                 chIndx++;
589                 token = TT_RSHIFT;
590                 if (str.length() > chIndx) {
591                   if (str.charAt(chIndx) == '=') {
592                     chIndx++;
593                     token = TT_RSHIFTASSIGN;
594                     break;
595                   }
596                 }
597                 break;
598               }
599             }
600
601             break;
602           case '<' :
603             token = TT_LESS;
604
605             if (str.length() > chIndx) {
606               if (str.charAt(chIndx) == '=') {
607                 chIndx++;
608                 token = TT_LESSEQUAL;
609
610                 break;
611               }
612               if (str.charAt(chIndx) == '<') {
613                 chIndx++;
614                 token = TT_LSHIFT;
615                 if (str.charAt(chIndx) == '<') {
616                   // heredoc
617                   int startRow = rowCount;
618                   if (str.length() > chIndx) {
619
620                     ch = str.charAt(++chIndx);
621                     if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch == '_')) {
622                       chIndx++;
623                       getIdentifier();
624                       token = TT_STRING_CONSTANT;
625                       while (str.length() > chIndx) {
626                         ch = str.charAt(chIndx++);
627                         if (ch == '\n') {
628                           if (str.length() >= chIndx + identifier.length()) {
629                             if (str.substring(chIndx, chIndx + identifier.length()).equals(identifier)) {
630                               chIndx += identifier.length();
631                               return;
632                             }
633                           }
634                         }
635                       }
636                     }
637                   }
638                   throwSyntaxError("Open heredoc syntax after operator '<<<'.", startRow);
639                 } else if (str.charAt(chIndx) == '=') {
640                   chIndx++;
641                   token = TT_LSHIFTASSIGN;
642                   break;
643                 }
644                 break;
645               }
646             }
647
648             break;
649
650           case '|' :
651             token = TT_LINE;
652
653             if (str.length() > chIndx) {
654               if (str.charAt(chIndx) == '|') {
655                 chIndx++;
656                 token = TT_OR;
657
658                 break;
659               }
660             }
661
662             break;
663           case '&' :
664             token = TT_AMPERSAND;
665             if (str.length() > chIndx) {
666               if (str.charAt(chIndx) == '&') {
667                 chIndx++;
668                 token = TT_AND;
669                 break;
670               }
671               if (str.charAt(chIndx) == '=') {
672                 chIndx++;
673                 token = TT_ANDASSIGN;
674                 break;
675               }
676               break;
677             }
678
679             break;
680           case ':' :
681             token = TT_DDOT;
682             if (str.length() > chIndx) {
683               if (str.charAt(chIndx) == ':') {
684                 chIndx++;
685                 token = TT_DDOT2;
686               }
687             }
688             break;
689           case '#' :
690             token = TT_HASH;
691
692             break;
693             //          case '@' :
694             //            token = TT_AT;
695             //
696             //            break;
697           default :
698             throwSyntaxError("unexpected character: '" + ch + "'");
699         }
700
701         if (token == TT_UNDEFINED) {
702           throwSyntaxError("token not found");
703         }
704
705         return;
706       }
707     }
708
709     chIndx = str.length() + 1;
710     ch = ' ';
711     token = TT_EOF;
712     phpEnd = true;
713     PHPString temp;
714     if (phpList != null) {
715       if (currentPHPString < phpList.size()) {
716         token = TT_UNDEFINED;
717         temp = (PHPString) phpList.get(currentPHPString++);
718         this.str = temp.getPHPString();
719         this.token = TT_EOF;
720         this.chIndx = 0;
721         this.rowCount = temp.getLineNumber();
722         this.columnCount = 0;
723         getNextToken();
724         phpEnd = true;
725       } else {
726         token = TT_UNDEFINED;
727         return;
728       }
729     }
730   }
731   /**
732    * gets the next token from input
733    */
734   private void getNextToken() throws CoreException {
735     boolean phpFound = false;
736     char ch2;
737
738     phpEnd = false;
739     try {
740       if (!phpMode) {
741
742         while (str.length() > chIndx) {
743           token = TT_UNDEFINED;
744           ch = str.charAt(chIndx++);
745
746           if (ch == '\n') {
747             rowCount++;
748           }
749           if (ch == '<') {
750             ch2 = str.charAt(chIndx++);
751             if (ch2 == '?') {
752               ch2 = str.charAt(chIndx++);
753               if (Character.isWhitespace(ch2)) {
754                 // php start 
755                 phpMode = true;
756                 phpFound = true;
757                 break;
758               } else if (ch2 == 'p') {
759                 ch2 = str.charAt(chIndx++);
760                 if (ch2 == 'h') {
761                   ch2 = str.charAt(chIndx++);
762                   if (ch2 == 'p') {
763                     phpMode = true;
764                     phpFound = true;
765                     break;
766                   }
767                   chIndx--;
768                 }
769                 chIndx--;
770               } else if (ch2 == 'P') {
771                 ch2 = str.charAt(chIndx++);
772                 if (ch2 == 'H') {
773                   ch2 = str.charAt(chIndx++);
774                   if (ch2 == 'P') {
775                     phpMode = true;
776                     phpFound = true;
777                     break;
778                   }
779                   chIndx--;
780                 }
781                 chIndx--;
782               }
783               chIndx--;
784             }
785             chIndx--;
786           }
787         }
788
789       }
790
791       if (phpMode) {
792         while (str.length() > chIndx) {
793           ch = str.charAt(chIndx++);
794           token = TT_UNDEFINED;
795           if (ch == '\n') {
796             rowCount++;
797             columnCount = chIndx;
798             continue; // while loop
799           }
800           if (str.length() == chIndx) {
801             phpEnd = true;
802           }
803           if (!Character.isWhitespace(ch)) {
804             if (ch == '$') {
805               if (str.length() > chIndx) {
806                 if (str.charAt(chIndx) == '{') {
807                   chIndx++;
808                   token = TT_DOLLAROPEN;
809                   return;
810                 }
811               }
812               getIdentifier();
813               return;
814             }
815             if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch == '_') || (ch == '$')) {
816               getIdentifier();
817               return;
818             }
819             if (ch >= '0' && ch <= '9') {
820               getNumber();
821               return;
822             }
823             if (ch == '/') {
824               if (str.length() > chIndx) {
825                 if (str.charAt(chIndx) == '/') {
826                   ch = '/';
827                   chIndx++;
828                   // read comment until end of line:
829                   while ((str.length() > chIndx) && (ch != '\n')) {
830                     ch = str.charAt(chIndx++);
831                     if (ch == '?') {
832                       ch2 = str.charAt(chIndx);
833                       if (ch2 == '>') {
834                         chIndx++;
835                         token = TT_HTML;
836                         // php end
837                         phpMode = false;
838                         phpEnd = true;
839                         return;
840                       }
841                     }
842                   }
843                   rowCount++;
844                   continue;
845
846                 } else if (str.charAt(chIndx) == '*') {
847                   chIndx++;
848                   // multi line comment:
849                   while (str.length() > chIndx) {
850                     if (str.charAt(chIndx) == '*' && (str.length() > (chIndx + 1)) && str.charAt(chIndx + 1) == '/') {
851                       chIndx += 2;
852                       break;
853                     }
854                     ch = str.charAt(chIndx++);
855                     if (ch == '\n') {
856                       rowCount++;
857                       columnCount = chIndx;
858                     }
859                   }
860                   continue;
861                 }
862               }
863             } else if (ch == '#') {
864               // read comment until end of line:
865               while ((str.length() > chIndx) && (ch != '\n')) {
866                 ch = str.charAt(chIndx++);
867                 if (ch == '?') {
868                   ch2 = str.charAt(chIndx);
869                   if (ch2 == '>') {
870                     chIndx++;
871                     token = TT_HTML;
872                     // php end
873                     phpMode = false;
874                     phpEnd = true;
875                     return;
876                   }
877                 }
878               }
879               rowCount++;
880               continue;
881
882             } else if (ch == '"') {
883               // read string until end
884               boolean openString = true;
885               while (str.length() > chIndx) {
886                 ch = str.charAt(chIndx++);
887                 if (ch == '\\') {
888                   if (str.length() > chIndx) {
889                     ch = str.charAt(chIndx++);
890                   }
891                 } else if (ch == '"') {
892                   openString = false;
893                   break;
894                 } else if (ch == '\n') {
895                   rowCount++;
896                   columnCount = chIndx;
897                 }
898               }
899               if (openString) {
900                 throwSyntaxError("Open string character '\"' at end of file.");
901               }
902               token = TT_INTERPOLATED_STRING;
903               return;
904             } else if (ch == '\'') {
905               // read string until end
906               boolean openString = true;
907               int startRow = rowCount;
908               while (str.length() > chIndx) {
909                 ch = str.charAt(chIndx++);
910                 if (ch == '\\') {
911                   if (str.length() > chIndx) {
912                     ch = str.charAt(chIndx++);
913                   }
914                 } else if (ch == '\'') {
915                   openString = false;
916                   break;
917                 } else if (ch == '\n') {
918                   rowCount++;
919                   columnCount = chIndx;
920                 }
921               }
922               if (openString) {
923                 throwSyntaxError("Open string character \"'\" at end of file.", startRow);
924               }
925               token = TT_STRING_CONSTANT;
926               return;
927             } else if (ch == '`') {
928               // read string until end
929               boolean openString = true;
930               int startRow = rowCount;
931               while (str.length() > chIndx) {
932                 ch = str.charAt(chIndx++);
933                 if (ch == '\\') {
934                   if (str.length() > chIndx) {
935                     ch = str.charAt(chIndx++);
936                   }
937                 } else if (ch == '`') {
938                   openString = false;
939                   break;
940                 } else if (ch == '\n') {
941                   rowCount++;
942                   columnCount = chIndx;
943                 }
944               }
945               if (openString) {
946                 throwSyntaxError("Open string character \"`\" at end of file.", startRow);
947               }
948               setMarker("Other string delimiters prefered (found \"`\").", rowCount, PHPParser.INFO);
949               token = TT_STRING_CONSTANT;
950               return;
951             }
952
953             switch (ch) {
954
955               case '(' :
956                 token = TT_ARGOPEN;
957
958                 break;
959               case ')' :
960                 token = TT_ARGCLOSE;
961
962                 break;
963               case '{' :
964                 token = TT_LISTOPEN;
965
966                 break;
967               case '}' :
968                 token = TT_LISTCLOSE;
969
970                 break;
971               case '[' :
972                 token = TT_PARTOPEN;
973
974                 break;
975               case ']' :
976                 token = TT_PARTCLOSE;
977
978                 break;
979               case ',' :
980                 token = TT_COMMA;
981
982                 break;
983               case '?' :
984                 token = TT_QUESTIONMARK;
985                 if (str.length() > chIndx) {
986                   if (str.charAt(chIndx) == '>') {
987                     chIndx++;
988                     token = TT_HTML;
989                     // php end
990                     phpMode = false;
991                     phpEnd = true;
992                     break;
993                   }
994                 }
995
996                 break;
997               case '@' :
998                 token = TT_AT;
999                 break;
1000               case '~' :
1001                 token = TT_TILDE;
1002                 if (str.length() > chIndx) {
1003                   if (str.charAt(chIndx) == '=') {
1004                     chIndx++;
1005                     token = TT_TILDEASSIGN;
1006
1007                     break;
1008                   }
1009                 }
1010                 break;
1011               case '.' :
1012                 token = TT_DOT;
1013                 if (str.length() > chIndx) {
1014                   if (str.charAt(chIndx) == '=') {
1015                     chIndx++;
1016                     token = TT_DOTASSIGN;
1017
1018                     break;
1019                   }
1020                 }
1021
1022                 break;
1023               case '"' :
1024                 token = TT_STRING;
1025
1026                 break;
1027               case '%' :
1028                 token = TT_MOD;
1029                 if (str.length() > chIndx) {
1030                   if (str.charAt(chIndx) == '=') {
1031                     chIndx++;
1032                     token = TT_MODASSIGN;
1033
1034                     break;
1035                   }
1036                 }
1037                 break;
1038               case ';' :
1039                 token = TT_SEMICOLON;
1040
1041                 break;
1042               case '^' :
1043                 token = TT_POW;
1044                 if (str.length() > chIndx) {
1045                   if (str.charAt(chIndx) == '=') {
1046                     chIndx++;
1047                     token = TT_POWASSIGN;
1048
1049                     break;
1050                   }
1051                 }
1052                 break;
1053               case '/' :
1054                 token = TT_DIV;
1055
1056                 if (str.length() > chIndx) {
1057                   if (str.charAt(chIndx) == '=') {
1058                     chIndx++;
1059                     token = TT_DIVIDEBY;
1060
1061                     break;
1062                   }
1063                 }
1064
1065                 break;
1066               case '*' :
1067                 token = TT_MULTIPLY;
1068                 if (str.length() > chIndx) {
1069                   if (str.charAt(chIndx) == '*') {
1070                     chIndx++;
1071                     token = TT_POW;
1072
1073                     break;
1074                   }
1075                   if (str.charAt(chIndx) == '=') {
1076                     chIndx++;
1077                     token = TT_TIMESBY;
1078
1079                     break;
1080                   }
1081                 }
1082
1083                 break;
1084               case '+' :
1085                 token = TT_ADD;
1086                 if (str.length() > chIndx) {
1087                   if (str.charAt(chIndx) == '+') {
1088                     chIndx++;
1089                     token = TT_INCREMENT;
1090
1091                     break;
1092                   }
1093                   if (str.charAt(chIndx) == '=') {
1094                     chIndx++;
1095                     token = TT_ADDTO;
1096
1097                     break;
1098                   }
1099                 }
1100                 break;
1101               case '-' :
1102                 token = TT_SUBTRACT;
1103                 if (str.length() > chIndx) {
1104                   if (str.charAt(chIndx) == '-') {
1105                     chIndx++;
1106                     token = TT_DECREMENT;
1107
1108                     break;
1109                   }
1110                   if (str.charAt(chIndx) == '=') {
1111                     chIndx++;
1112                     token = TT_SUBTRACTFROM;
1113
1114                     break;
1115                   }
1116                   if (str.charAt(chIndx) == '>') {
1117                     chIndx++;
1118                     token = TT_REF;
1119
1120                     break;
1121                   }
1122                 }
1123
1124                 break;
1125               case '=' :
1126                 token = TT_ASSIGN;
1127
1128                 if (str.length() > chIndx) {
1129                   ch = str.charAt(chIndx);
1130
1131                   if (ch == '=') {
1132                     chIndx++;
1133                     token = TT_EQUAL;
1134                     if (str.length() > chIndx) {
1135                       ch = str.charAt(chIndx);
1136
1137                       if (ch == '=') {
1138                         chIndx++;
1139                         token = TT_EX_EQUAL;
1140                       }
1141                     }
1142                     break;
1143                   }
1144                   if (ch == '>') {
1145                     chIndx++;
1146                     token = TT_FOREACH;
1147
1148                     break;
1149                   }
1150                 }
1151
1152                 break;
1153               case '!' :
1154                 token = TT_NOT;
1155
1156                 if (str.length() > chIndx) {
1157                   if (str.charAt(chIndx) == '=') {
1158                     chIndx++;
1159                     token = TT_UNEQUAL;
1160                     if (str.length() > chIndx) {
1161                       ch = str.charAt(chIndx);
1162
1163                       if (ch == '=') {
1164                         chIndx++;
1165                         token = TT_EX_UNEQUAL;
1166                       }
1167                     }
1168                     break;
1169                   }
1170                 }
1171
1172                 break;
1173               case '>' :
1174                 token = TT_GREATER;
1175
1176                 if (str.length() > chIndx) {
1177                   if (str.charAt(chIndx) == '=') {
1178                     chIndx++;
1179                     token = TT_GREATEREQUAL;
1180                     break;
1181                   }
1182                   if (str.charAt(chIndx) == '>') {
1183                     chIndx++;
1184                     token = TT_RSHIFT;
1185                     if (str.length() > chIndx) {
1186                       if (str.charAt(chIndx) == '=') {
1187                         chIndx++;
1188                         token = TT_RSHIFTASSIGN;
1189                         break;
1190                       }
1191                     }
1192                     break;
1193                   }
1194                 }
1195
1196                 break;
1197               case '<' :
1198                 token = TT_LESS;
1199
1200                 if (str.length() > chIndx) {
1201                   if (str.charAt(chIndx) == '=') {
1202                     chIndx++;
1203                     token = TT_LESSEQUAL;
1204
1205                     break;
1206                   }
1207                   if (str.charAt(chIndx) == '<') {
1208                     chIndx++;
1209                     token = TT_LSHIFT;
1210                     if (str.charAt(chIndx) == '<') {
1211                       // heredoc
1212                       int startRow = rowCount;
1213                       if (str.length() > chIndx) {
1214
1215                         ch = str.charAt(++chIndx);
1216                         if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch == '_')) {
1217                           chIndx++;
1218                           getIdentifier();
1219                           token = TT_STRING_CONSTANT;
1220                           while (str.length() > chIndx) {
1221                             ch = str.charAt(chIndx++);
1222                             if (ch == '\n') {
1223                               if (str.length() >= chIndx + identifier.length()) {
1224                                 if (str.substring(chIndx, chIndx + identifier.length()).equals(identifier)) {
1225                                   chIndx += identifier.length();
1226                                   return;
1227                                 }
1228                               }
1229                             }
1230                           }
1231                         }
1232                       }
1233                       throwSyntaxError("Open heredoc syntax after operator '<<<'.", startRow);
1234                     } else if (str.charAt(chIndx) == '=') {
1235                       chIndx++;
1236                       token = TT_LSHIFTASSIGN;
1237                       break;
1238                     }
1239                     break;
1240                   }
1241                 }
1242
1243                 break;
1244
1245               case '|' :
1246                 token = TT_LINE;
1247
1248                 if (str.length() > chIndx) {
1249                   if (str.charAt(chIndx) == '|') {
1250                     chIndx++;
1251                     token = TT_OR;
1252
1253                     break;
1254                   }
1255                 }
1256
1257                 break;
1258               case '&' :
1259                 token = TT_AMPERSAND;
1260                 if (str.length() > chIndx) {
1261                   if (str.charAt(chIndx) == '&') {
1262                     chIndx++;
1263                     token = TT_AND;
1264                     break;
1265                   }
1266                   if (str.charAt(chIndx) == '=') {
1267                     chIndx++;
1268                     token = TT_ANDASSIGN;
1269                     break;
1270                   }
1271                   break;
1272                 }
1273
1274                 break;
1275               case ':' :
1276                 token = TT_DDOT;
1277                 if (str.length() > chIndx) {
1278                   if (str.charAt(chIndx) == ':') {
1279                     chIndx++;
1280                     token = TT_DDOT2;
1281                   }
1282                 }
1283                 break;
1284               case '#' :
1285                 token = TT_HASH;
1286
1287                 break;
1288                 //          case '@' :
1289                 //            token = TT_AT;
1290                 //
1291                 //            break;
1292               default :
1293                 throwSyntaxError("unexpected character: '" + ch + "'");
1294             }
1295
1296             if (token == TT_UNDEFINED) {
1297               throwSyntaxError("token not found");
1298             }
1299
1300             return;
1301           }
1302         }
1303       }
1304     } catch (StringIndexOutOfBoundsException e) {
1305       // catched from charAt
1306     }
1307
1308     chIndx = str.length() + 1;
1309     ch = ' ';
1310     token = TT_EOF;
1311     phpEnd = true;
1312     //PHPString temp;
1313     //    if (phpList != null) {
1314     //      if (currentPHPString < phpList.size()) {
1315     //        token = TT_UNDEFINED;
1316     //        temp = (PHPString) phpList.get(currentPHPString++);
1317     //        this.str = temp.getPHPString();
1318     //        this.token = TT_EOF;
1319     //        this.chIndx = 0;
1320     //        this.rowCount = temp.getLineNumber();
1321     //        this.columnCount = 0;
1322     //        getNextToken();
1323     //        phpEnd = true;
1324     //      } else {
1325     //        token = TT_UNDEFINED;
1326     //        return;
1327     //      }
1328     //    }
1329   }
1330
1331   private void getIdentifier() {
1332     StringBuffer ident = new StringBuffer();
1333
1334     ident.append(ch);
1335     if (ch == '$') {
1336       token = TT_VARIABLE;
1337     } else {
1338       token = TT_IDENTIFIER;
1339     }
1340     getChar();
1341     while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || (ch == '_')) {
1342       ident.append(ch);
1343       getChar();
1344     }
1345     identifier = ident.toString();
1346     chIndx--;
1347
1348     Integer i = (Integer) keywordMap.get(identifier.toLowerCase());
1349     if (i != null) {
1350       token = i.intValue();
1351     }
1352   }
1353
1354   private void getNumber() {
1355     StringBuffer inum = new StringBuffer();
1356     char dFlag = ' ';
1357     int numFormat = 10;
1358
1359     // save first digit
1360     char firstCh = ch;
1361     inum.append(ch);
1362
1363     getChar();
1364     // determine number conversions:
1365     if (firstCh == '0') {
1366       switch (ch) {
1367         case 'b' :
1368           numFormat = 2;
1369           getChar();
1370           break;
1371         case 'B' :
1372           numFormat = 2;
1373           getChar();
1374           break;
1375         case 'o' :
1376           numFormat = 8;
1377           getChar();
1378           break;
1379         case 'O' :
1380           numFormat = 8;
1381           getChar();
1382           break;
1383         case 'x' :
1384           numFormat = 16;
1385           getChar();
1386           break;
1387         case 'X' :
1388           numFormat = 16;
1389           getChar();
1390           break;
1391       }
1392     }
1393
1394     if (numFormat == 16) {
1395       while ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) {
1396         inum.append(ch);
1397         getChar();
1398       }
1399     } else {
1400       while ((ch >= '0' && ch <= '9') || (ch == '.') || (ch == 'E') || (ch == 'e')) {
1401         if ((ch == '.') || (ch == 'E') || (ch == 'e')) {
1402           if (ch == '.' && dFlag != ' ') {
1403             break;
1404           }
1405           if ((dFlag == 'E') || (dFlag == 'e')) {
1406             break;
1407           }
1408           dFlag = ch;
1409           inum.append(ch);
1410           getChar();
1411           if ((ch == '-') || (ch == '+')) {
1412             inum.append(ch);
1413             getChar();
1414           }
1415         } else {
1416           inum.append(ch);
1417           getChar();
1418         }
1419       }
1420     }
1421     chIndx--;
1422
1423     try {
1424       if (dFlag != ' ') {
1425         doubleNumber = new Double(inum.toString());
1426         token = TT_DOUBLE_NUMBER;
1427         return;
1428       } else {
1429         longNumber = Long.valueOf(inum.toString(), numFormat);
1430         token = TT_INT_NUMBER;
1431         return;
1432       }
1433
1434     } catch (Throwable e) {
1435       throwSyntaxError("Number format error: " + inum.toString());
1436     }
1437   }
1438
1439   public void htmlParserTester(String input) {
1440     int lineNumber = 1;
1441     int startLineNumber = 1;
1442     int startIndex = 0;
1443     char ch;
1444     char ch2;
1445     boolean phpMode = false;
1446     boolean phpFound = false;
1447
1448     phpList = new ArrayList();
1449     currentPHPString = 0;
1450
1451     try {
1452       int i = 0;
1453       while (i < input.length()) {
1454         ch = input.charAt(i++);
1455         if (ch == '\n') {
1456           lineNumber++;
1457         }
1458         if ((!phpMode) && ch == '<') {
1459           ch2 = input.charAt(i++);
1460           if (ch2 == '?') {
1461             ch2 = input.charAt(i++);
1462             if (Character.isWhitespace(ch2)) {
1463               // php start 
1464               phpMode = true;
1465               phpFound = true;
1466               startIndex = i;
1467               startLineNumber = lineNumber;
1468               continue;
1469             } else if (ch2 == 'p') {
1470               ch2 = input.charAt(i++);
1471               if (ch2 == 'h') {
1472                 ch2 = input.charAt(i++);
1473                 if (ch2 == 'p') {
1474                   phpMode = true;
1475                   phpFound = true;
1476                   startIndex = i;
1477                   startLineNumber = lineNumber;
1478                   continue;
1479                 }
1480                 i--;
1481               }
1482               i--;
1483             } else if (ch2 == 'P') {
1484               ch2 = input.charAt(i++);
1485               if (ch2 == 'H') {
1486                 ch2 = input.charAt(i++);
1487                 if (ch2 == 'P') {
1488                   phpMode = true;
1489                   phpFound = true;
1490                   startIndex = i;
1491                   startLineNumber = lineNumber;
1492                   continue;
1493                 }
1494                 i--;
1495               }
1496               i--;
1497             }
1498             i--;
1499           }
1500           i--;
1501         }
1502
1503         if (phpMode) {
1504           if (ch == '/' && i < input.length()) {
1505             ch2 = input.charAt(i++);
1506             if (ch2 == '/') {
1507               while (i < input.length()) {
1508                 ch = input.charAt(i++);
1509                 if (ch == '?' && i < input.length()) {
1510                   ch2 = input.charAt(i++);
1511                   if (ch2 == '>') {
1512                     // php end
1513                     phpMode = false;
1514                     phpList.add(new PHPString(input.substring(startIndex, i - 2), startLineNumber));
1515                     continue;
1516                   }
1517                   i--;
1518                 } else if (ch == '\n') {
1519                   lineNumber++;
1520                   break;
1521                 }
1522               }
1523               continue;
1524             } else if (ch2 == '*') {
1525               // multi-line comment
1526               while (i < input.length()) {
1527                 ch = input.charAt(i++);
1528                 if (ch == '\n') {
1529                   lineNumber++;
1530                 } else if (ch == '*' && i < input.length()) {
1531                   ch2 = input.charAt(i++);
1532                   if (ch2 == '/') {
1533                     break;
1534                   }
1535                   i--;
1536                 }
1537               }
1538               continue;
1539             } else {
1540               i--;
1541             }
1542           } else if (ch == '#') {
1543             while (i < input.length()) {
1544               ch = input.charAt(i++);
1545               if (ch == '?' && i < input.length()) {
1546                 ch2 = input.charAt(i++);
1547                 if (ch2 == '>') {
1548                   // php end
1549                   phpMode = false;
1550                   phpList.add(new PHPString(input.substring(startIndex, i - 2), startLineNumber));
1551                   continue;
1552                 }
1553                 i--;
1554               } else if (ch == '\n') {
1555                 lineNumber++;
1556                 break;
1557               }
1558             }
1559             continue;
1560           } else if (ch == '"') {
1561             ch = ' ';
1562             while (i < input.length()) {
1563               ch = input.charAt(i++);
1564               if (ch == '\n') {
1565                 lineNumber++;
1566               } else if (ch == '\\' && i < input.length()) { // escape
1567                 i++;
1568               } else if (ch == '"') {
1569                 break;
1570               }
1571             }
1572             continue;
1573           } else if (ch == '\'') {
1574             ch = ' ';
1575             while (i < input.length()) {
1576               ch = input.charAt(i++);
1577               if (ch == '\n') {
1578                 lineNumber++;
1579               } else if (ch == '\\' && i < input.length()) { // escape
1580                 i++;
1581               } else if (ch == '\'') {
1582                 break;
1583               }
1584             }
1585             continue;
1586           }
1587
1588           if (ch == '?' && i < input.length()) {
1589             ch2 = input.charAt(i++);
1590             if (ch2 == '>') {
1591               // php end
1592               phpMode = false;
1593               phpList.add(new PHPString(input.substring(startIndex, i - 2), startLineNumber));
1594               continue;
1595             }
1596             i--;
1597           }
1598         }
1599       }
1600
1601       if (!phpFound) {
1602         setMarker("No PHP source code found.", lineNumber, PHPParser.INFO);
1603       } else {
1604         if (phpMode) {
1605           setMarker("Open PHP tag at end of file.", lineNumber, PHPParser.INFO);
1606           phpList.add(new PHPString(input.substring(startIndex, i - 2), startLineNumber));
1607         }
1608         //        for (int j=0;j<phpList.size();j++) {
1609         //          String temp = ((PHPString)phpList.get(j)).getPHPString();
1610         //          int startIndx = temp.length()-10;
1611         //          if (startIndx<0) {
1612         //            startIndx = 0; 
1613         //          }
1614         //          System.out.println(temp.substring(startIndx)+"?>");
1615         //        }
1616         phpParserTester(null, 1);
1617         //        PHPString temp;
1618         //        for(int j=0;j<phpList.size();j++) {
1619         //          temp = (PHPString) phpList.get(j);
1620         //          parser.start(temp.getPHPString(), temp.getLineNumber());
1621         //        } 
1622       }
1623     } catch (CoreException e) {
1624     }
1625   }
1626
1627   public void phpParserTester(String s, int rowCount) throws CoreException {
1628     this.str = s;
1629     if (s == null) {
1630       if (phpList.size() != 0) {
1631         this.str = ((PHPString) phpList.get(currentPHPString++)).getPHPString();
1632       }
1633     }
1634     this.token = TT_EOF;
1635     this.chIndx = 0;
1636     this.rowCount = rowCount;
1637     this.columnCount = 0;
1638     this.phpEnd = false;
1639     this.phpMode = true;
1640     getNextToken();
1641     do {
1642       try {
1643         if (token != TT_EOF && token != TT_UNDEFINED) {
1644           statementList();
1645         }
1646         if (token != TT_EOF && token != TT_UNDEFINED) {
1647           if (token == TT_ARGCLOSE) {
1648             throwSyntaxError("Too many closing ')'; end-of-file not reached.");
1649           }
1650           if (token == TT_LISTCLOSE) {
1651             throwSyntaxError("Too many closing '}'; end-of-file not reached.");
1652           }
1653           if (token == TT_PARTCLOSE) {
1654             throwSyntaxError("Too many closing ']'; end-of-file not reached.");
1655           }
1656
1657           if (token == TT_ARGOPEN) {
1658             throwSyntaxError("Read character '('; end-of-file not reached.");
1659           }
1660           if (token == TT_LISTOPEN) {
1661             throwSyntaxError("Read character '{';  end-of-file not reached.");
1662           }
1663           if (token == TT_PARTOPEN) {
1664             throwSyntaxError("Read character '[';  end-of-file not reached.");
1665           }
1666
1667           throwSyntaxError("End-of-file not reached.");
1668         }
1669         return;
1670       } catch (SyntaxError err) {
1671         if (s != null) {
1672           throw err;
1673         } else {
1674           setMarker(err.getMessage(), err.getLine(), ERROR);
1675         }
1676         // if an error occured, 
1677         // try to find keywords 'class' or 'function'
1678         // to parse the rest of the string
1679         while (token != TT_EOF && token != TT_UNDEFINED) {
1680           if (token == TT_class || token == TT_function) {
1681             break;
1682           }
1683           getNextToken();
1684         }
1685         if (token == TT_EOF || token == TT_UNDEFINED) {
1686           return;
1687         }
1688       }
1689     }
1690     while (true);
1691   }
1692
1693   /**
1694    * Parses a string with php tAGS
1695    * i.e. '&lt;body&gt; &lt;?php phpinfo() ?&gt; &lt;/body&gt;'
1696    */
1697   public void parse(String s) throws CoreException {
1698     this.str = s;
1699     this.token = TT_EOF;
1700     this.chIndx = 0;
1701     this.rowCount = 1;
1702     this.columnCount = 0;
1703     this.phpEnd = false;
1704     this.phpMode = false;
1705     getNextToken();
1706     do {
1707       try {
1708         if (token != TT_EOF && token != TT_UNDEFINED) {
1709           statementList();
1710         }
1711         if (token != TT_EOF && token != TT_UNDEFINED) {
1712           if (token == TT_ARGCLOSE) {
1713             throwSyntaxError("Too many closing ')'; end-of-file not reached.");
1714           }
1715           if (token == TT_LISTCLOSE) {
1716             throwSyntaxError("Too many closing '}'; end-of-file not reached.");
1717           }
1718           if (token == TT_PARTCLOSE) {
1719             throwSyntaxError("Too many closing ']'; end-of-file not reached.");
1720           }
1721
1722           if (token == TT_ARGOPEN) {
1723             throwSyntaxError("Read character '('; end-of-file not reached.");
1724           }
1725           if (token == TT_LISTOPEN) {
1726             throwSyntaxError("Read character '{';  end-of-file not reached.");
1727           }
1728           if (token == TT_PARTOPEN) {
1729             throwSyntaxError("Read character '[';  end-of-file not reached.");
1730           }
1731
1732           throwSyntaxError("End-of-file not reached.");
1733         }
1734         return;
1735       } catch (SyntaxError sytaxErr1) {
1736         setMarker(sytaxErr1.getMessage(), sytaxErr1.getLine(), ERROR);
1737         try {
1738           // if an error occured, 
1739           // try to find keywords 'class' or 'function'
1740           // to parse the rest of the string
1741           while (token != TT_EOF && token != TT_UNDEFINED) {
1742             if (token == TT_class || token == TT_function) {
1743               break;
1744             }
1745             getNextToken();
1746           }
1747           if (token == TT_EOF || token == TT_UNDEFINED) {
1748             return;
1749           }
1750         } catch (SyntaxError sytaxErr2) {
1751           setMarker(sytaxErr2.getMessage(), sytaxErr2.getLine(), ERROR);
1752           return;
1753         }
1754       }
1755     }
1756     while (true);
1757   }
1758
1759   private void statementList() throws CoreException {
1760     do {
1761       statement();
1762       if ((token == TT_LISTCLOSE)
1763         || (token == TT_case)
1764         || (token == TT_default)
1765         || (token == TT_elseif)
1766         || (token == TT_endif)
1767         || (token == TT_endfor)
1768         || (token == TT_endforeach)
1769         || (token == TT_endwhile)
1770         || (token == TT_endswitch)
1771         || (token == TT_EOF)
1772         || (token == TT_UNDEFINED)) {
1773         return;
1774       }
1775     } while (true);
1776   }
1777
1778   private void compoundStatement() throws CoreException {
1779     // '{' [statement-list] '}'
1780     if (token == TT_LISTOPEN) {
1781       getNextToken();
1782     } else {
1783       throwSyntaxError("'{' expected in compound-statement.");
1784     }
1785     if (token != TT_LISTCLOSE) {
1786       statementList();
1787     }
1788     if (token == TT_LISTCLOSE) {
1789       getNextToken();
1790     } else {
1791       throwSyntaxError("'}' expected in compound-statement.");
1792     }
1793   }
1794
1795   private void statement() throws CoreException {
1796     //   if (token > TT_KEYWORD && token != TT_list && token != TT_new) {
1797     String keyword = identifier;
1798     if (token == TT_include || token == TT_include_once) {
1799       getNextToken();
1800       expression();
1801       if (token == TT_SEMICOLON) {
1802         getNextToken();
1803       } else {
1804         if (!phpEnd) {
1805           throwSyntaxError("';' character after 'include' or 'include_once' expected.");
1806         }
1807         getNextToken();
1808       }
1809       return;
1810     } else if (token == TT_require || token == TT_require_once) {
1811       getNextToken();
1812       //constant();
1813       expression();
1814       if (token == TT_SEMICOLON) {
1815         getNextToken();
1816       } else {
1817         if (!phpEnd) {
1818           throwSyntaxError("';' character after 'require' or 'require_once' expected.");
1819         }
1820         getNextToken();
1821       }
1822       return;
1823     } else if (token == TT_if) {
1824       getNextToken();
1825       if (token == TT_ARGOPEN) {
1826         getNextToken();
1827       } else {
1828         throwSyntaxError("'(' expected after 'if' keyword.");
1829       }
1830       expression();
1831       if (token == TT_ARGCLOSE) {
1832         getNextToken();
1833       } else {
1834         throwSyntaxError("')' expected after 'if' condition.");
1835       }
1836       ifStatement();
1837       return;
1838
1839     } else if (token == TT_switch) {
1840       getNextToken();
1841       if (token == TT_ARGOPEN) {
1842         getNextToken();
1843       } else {
1844         throwSyntaxError("'(' expected after 'switch' keyword.");
1845       }
1846       expression();
1847       if (token == TT_ARGCLOSE) {
1848         getNextToken();
1849       } else {
1850         throwSyntaxError("')' expected after 'switch' condition.");
1851       }
1852       switchStatement();
1853       return;
1854     } else if (token == TT_for) {
1855       getNextToken();
1856       if (token == TT_ARGOPEN) {
1857         getNextToken();
1858       } else {
1859         throwSyntaxError("'(' expected after 'for' keyword.");
1860       }
1861       if (token == TT_SEMICOLON) {
1862         getNextToken();
1863       } else {
1864         expressionList();
1865         if (token == TT_SEMICOLON) {
1866           getNextToken();
1867         } else {
1868           throwSyntaxError("';' expected after 'for'.");
1869         }
1870       }
1871       if (token == TT_SEMICOLON) {
1872         getNextToken();
1873       } else {
1874         expressionList();
1875         if (token == TT_SEMICOLON) {
1876           getNextToken();
1877         } else {
1878           throwSyntaxError("';' expected after 'for'.");
1879         }
1880       }
1881       if (token == TT_ARGCLOSE) {
1882         getNextToken();
1883       } else {
1884         expressionList();
1885         if (token == TT_ARGCLOSE) {
1886           getNextToken();
1887         } else {
1888           throwSyntaxError("')' expected after 'for'.");
1889         }
1890       }
1891       forStatement();
1892       return;
1893     } else if (token == TT_while) {
1894       getNextToken();
1895       if (token == TT_ARGOPEN) {
1896         getNextToken();
1897       } else {
1898         throwSyntaxError("'(' expected after 'while' keyword.");
1899       }
1900       expression();
1901       if (token == TT_ARGCLOSE) {
1902         getNextToken();
1903       } else {
1904         throwSyntaxError("')' expected after 'while' condition.");
1905       }
1906       whileStatement();
1907       return;
1908     } else if (token == TT_do) {
1909       getNextToken();
1910       if (token == TT_LISTOPEN) {
1911         getNextToken();
1912       } else {
1913         throwSyntaxError("'{' expected after 'do' keyword.");
1914       }
1915       if (token != TT_LISTCLOSE) {
1916         statementList();
1917       }
1918       if (token == TT_LISTCLOSE) {
1919         getNextToken();
1920       } else {
1921         throwSyntaxError("'}' expected after 'do' keyword.");
1922       }
1923       if (token == TT_while) {
1924         getNextToken();
1925         if (token == TT_ARGOPEN) {
1926           getNextToken();
1927         } else {
1928           throwSyntaxError("'(' expected after 'while' keyword.");
1929         }
1930         expression();
1931         if (token == TT_ARGCLOSE) {
1932           getNextToken();
1933         } else {
1934           throwSyntaxError("')' expected after 'while' condition.");
1935         }
1936       } else {
1937         throwSyntaxError("'while' expected after 'do' keyword.");
1938       }
1939       if (token == TT_SEMICOLON) {
1940         getNextToken();
1941       } else {
1942         if (!phpEnd) {
1943           throwSyntaxError("';' expected after do-while statement.");
1944         }
1945         getNextToken();
1946       }
1947       return;
1948     } else if (token == TT_foreach) {
1949       getNextToken();
1950       if (token == TT_ARGOPEN) {
1951         getNextToken();
1952       } else {
1953         throwSyntaxError("'(' expected after 'foreach' keyword.");
1954       }
1955       expression();
1956       if (token == TT_as) {
1957         getNextToken();
1958       } else {
1959         throwSyntaxError("'as' expected after 'foreach' exxpression.");
1960       }
1961       variable();
1962       if (token == TT_FOREACH) {
1963         getNextToken();
1964         variable();
1965       }
1966       if (token == TT_ARGCLOSE) {
1967         getNextToken();
1968       } else {
1969         throwSyntaxError("')' expected after 'foreach' expression.");
1970       }
1971       foreachStatement();
1972       return;
1973
1974     } else if (token == TT_continue || token == TT_break || token == TT_return) {
1975       getNextToken();
1976       if (token != TT_SEMICOLON) {
1977         expression();
1978       }
1979       if (token == TT_SEMICOLON) {
1980         getNextToken();
1981       } else {
1982         if (!phpEnd) {
1983           throwSyntaxError("';' expected after 'continue', 'break' or 'return'.");
1984         }
1985         getNextToken();
1986       }
1987       return;
1988
1989     } else if (token == TT_echo) {
1990       getNextToken();
1991       expressionList();
1992       if (token == TT_SEMICOLON) {
1993         getNextToken();
1994       } else {
1995         if (!phpEnd) {
1996           throwSyntaxError("';' expected after 'echo' statement.");
1997         }
1998         getNextToken();
1999       }
2000       return;
2001       //    } else if (token == TT_print) {
2002       //      getNextToken();
2003       //      expression();
2004       //      if (token == TT_SEMICOLON) {
2005       //        getNextToken();
2006       //      } else {
2007       //        if (!phpEnd) {
2008       //          throwSyntaxError("';' expected after 'print' statement.");
2009       //        }
2010       //        getNextToken();
2011       //      }
2012       //      return;
2013
2014     } else if (token == TT_global || token == TT_static) {
2015       getNextToken();
2016       variableList();
2017       if (token == TT_SEMICOLON) {
2018         getNextToken();
2019       } else {
2020         if (!phpEnd) {
2021           throwSyntaxError("';' expected after 'global' or 'static' statement.");
2022         }
2023         getNextToken();
2024       }
2025       return;
2026
2027       //      } else if (token == TT_unset) {
2028       //        getNextToken();
2029       //        if (token == TT_ARGOPEN) {
2030       //          getNextToken();
2031       //        } else {
2032       //          throwSyntaxError("'(' expected after 'unset' keyword.");
2033       //        }
2034       //        variableList();
2035       //        if (token == TT_ARGCLOSE) {
2036       //          getNextToken();
2037       //        } else {
2038       //          throwSyntaxError("')' expected after 'unset' statement.");
2039       //        }
2040       //        if (token == TT_SEMICOLON) {
2041       //          getNextToken();
2042       //        } else {
2043       //          if (!phpEnd) {
2044       //            throwSyntaxError("';' expected after 'unset' statement.");
2045       //          }
2046       //          getNextToken();
2047       //        }
2048       //        return;
2049
2050       //      } else if (token == TT_exit || token == TT_die) {
2051       //        getNextToken();
2052       //        if (token != TT_SEMICOLON) {
2053       //          exitStatus();
2054       //        }
2055       //        if (token == TT_SEMICOLON) {
2056       //          getNextToken();
2057       //        } else {
2058       //          if (!phpEnd) {
2059       //            throwSyntaxError("';' expected after 'exit' or 'die' statement.");
2060       //          }
2061       //          getNextToken();
2062       //        }
2063       //        return;
2064
2065     } else if (token == TT_define) {
2066       getNextToken();
2067       if (token == TT_ARGOPEN) {
2068         getNextToken();
2069       } else {
2070         throwSyntaxError("'(' expected after 'define' keyword.");
2071       }
2072       expression();
2073       if (token == TT_COMMA) {
2074         getNextToken();
2075       } else {
2076         throwSyntaxError("',' expected after first 'define' constant.");
2077       }
2078       expression();
2079       if (token == TT_COMMA) {
2080         getNextToken();
2081         expression();
2082       }
2083       if (token == TT_ARGCLOSE) {
2084         getNextToken();
2085       } else {
2086         throwSyntaxError("')' expected after 'define' statement.");
2087       }
2088       if (token == TT_SEMICOLON) {
2089         getNextToken();
2090       } else {
2091         if (!phpEnd) {
2092           throwSyntaxError("';' expected after 'define' statement.");
2093         }
2094         getNextToken();
2095       }
2096       return;
2097     } else if (token == TT_function) {
2098       getNextToken();
2099       functionDefinition();
2100       return;
2101     } else if (token == TT_class) {
2102       getNextToken();
2103       classDeclarator();
2104       classBody();
2105       return;
2106       //      } else {
2107       //        throwSyntaxError("Unexpected keyword '" + keyword + "'");
2108     } else if (token == TT_LISTOPEN) {
2109       // compoundStatement
2110       getNextToken();
2111       if (token != TT_LISTCLOSE) {
2112         statementList();
2113       }
2114       if (token == TT_LISTCLOSE) {
2115         getNextToken();
2116         return;
2117       } else {
2118         throwSyntaxError("'}' expected.");
2119       }
2120     } else {
2121       if (token != TT_SEMICOLON) {
2122         expression();
2123       }
2124       if (token == TT_SEMICOLON) {
2125         getNextToken();
2126         return;
2127       } else {
2128         if (!phpEnd) {
2129           throwSyntaxError("';' expected after expression.");
2130         }
2131         getNextToken();
2132       }
2133     }
2134   }
2135
2136   private void classDeclarator() throws CoreException {
2137     //identifier
2138     //identifier 'extends' identifier
2139     if (token == TT_IDENTIFIER) {
2140       getNextToken();
2141       if (token == TT_extends) {
2142         getNextToken();
2143         if (token == TT_IDENTIFIER) {
2144           getNextToken();
2145         } else {
2146           throwSyntaxError("Class name expected after keyword 'extends'.");
2147         }
2148       }
2149     } else {
2150       throwSyntaxError("Class name expected after keyword 'class'.");
2151     }
2152   }
2153
2154   private void classBody() throws CoreException {
2155     //'{' [class-element-list] '}'
2156     if (token == TT_LISTOPEN) {
2157       getNextToken();
2158       if (token != TT_LISTCLOSE) {
2159         classElementList();
2160       }
2161       if (token == TT_LISTCLOSE) {
2162         getNextToken();
2163       } else {
2164         throwSyntaxError("'}' expected at end of class body.");
2165       }
2166     } else {
2167       throwSyntaxError("'{' expected at start of class body.");
2168     }
2169   }
2170
2171   private void classElementList() throws CoreException {
2172     do {
2173       classElement();
2174     } while (token == TT_function || token == TT_var);
2175   }
2176
2177   private void classElement() throws CoreException {
2178     //class-property
2179     //function-definition
2180     if (token == TT_function) {
2181       getNextToken();
2182       functionDefinition();
2183     } else if (token == TT_var) {
2184       getNextToken();
2185       classProperty();
2186     } else {
2187       throwSyntaxError("'function' or 'var' expected.");
2188     }
2189   }
2190
2191   private void classProperty() throws CoreException {
2192     //'var' variable ';'
2193     //'var' variable '=' constant ';'
2194     do {
2195       if (token == TT_VARIABLE) {
2196         getNextToken();
2197         if (token == TT_ASSIGN) {
2198           getNextToken();
2199           constant();
2200         }
2201       } else {
2202         throwSyntaxError("Variable expected after keyword 'var'.");
2203       }
2204       if (token != TT_COMMA) {
2205         break;
2206       }
2207       getNextToken();
2208     } while (true);
2209     if (token == TT_SEMICOLON) {
2210       getNextToken();
2211     } else {
2212       throwSyntaxError("';' expected after variable declaration.");
2213     }
2214   }
2215
2216   private void functionDefinition() throws CoreException {
2217     functionDeclarator();
2218     compoundStatement();
2219   }
2220
2221   private void functionDeclarator() throws CoreException {
2222     //identifier '(' [parameter-list] ')'
2223     if (token == TT_AMPERSAND) {
2224       getNextToken();
2225     }
2226     if (token == TT_IDENTIFIER) {
2227       getNextToken();
2228       if (token == TT_ARGOPEN) {
2229         getNextToken();
2230       } else {
2231         throwSyntaxError("'(' expected in function declaration.");
2232       }
2233       if (token != TT_ARGCLOSE) {
2234         parameterList();
2235       }
2236       if (token != TT_ARGCLOSE) {
2237         throwSyntaxError("')' expected in function declaration.");
2238       } else {
2239         getNextToken();
2240       }
2241     }
2242   }
2243   //
2244   private void parameterList() throws CoreException {
2245     //parameter-declaration
2246     //parameter-list ',' parameter-declaration
2247     do {
2248       parameterDeclaration();
2249       if (token != TT_COMMA) {
2250         break;
2251       }
2252       getNextToken();
2253     } while (true);
2254   }
2255
2256   private void parameterDeclaration() throws CoreException {
2257     //variable
2258     //variable-reference
2259     if (token == TT_AMPERSAND) {
2260       getNextToken();
2261       if (token == TT_VARIABLE) {
2262         getNextToken();
2263       } else {
2264         throwSyntaxError("Variable expected after reference operator '&'.");
2265       }
2266     }
2267     //variable '=' constant
2268     if (token == TT_VARIABLE) {
2269       getNextToken();
2270       if (token == TT_ASSIGN) {
2271         getNextToken();
2272         constant();
2273       }
2274       return;
2275     }
2276   }
2277
2278   private void labeledStatementList() throws CoreException {
2279     if (token != TT_case && token != TT_default) {
2280       throwSyntaxError("'case' or 'default' expected.");
2281     }
2282     do {
2283       if (token == TT_case) {
2284         getNextToken();
2285         constant();
2286         if (token == TT_DDOT) {
2287           getNextToken();
2288           if (token == TT_case || token == TT_default) { // empty case statement ?
2289             continue;
2290           }
2291           statementList();
2292         } else if (token == TT_SEMICOLON) {
2293           setMarker("':' expected after 'case' keyword found ';'.", rowCount, PHPParser.INFO);
2294           getNextToken();
2295           if (token == TT_case) { // empty case statement ?
2296             continue;
2297           }
2298           statementList();
2299         } else {
2300           throwSyntaxError("':' character after 'case' constant expected.");
2301         }
2302       } else { // TT_default 
2303         getNextToken();
2304         if (token == TT_DDOT) {
2305           getNextToken();
2306           statementList();
2307         } else {
2308           throwSyntaxError("':' character after 'default' expected.");
2309         }
2310       }
2311     } while (token == TT_case || token == TT_default);
2312   }
2313
2314   //  public void labeledStatement() {
2315   //    if (token == TT_case) {
2316   //      getNextToken();
2317   //      constant();
2318   //      if (token == TT_DDOT) {
2319   //        getNextToken();
2320   //        statement();
2321   //      } else {
2322   //        throwSyntaxError("':' character after 'case' constant expected.");
2323   //      }
2324   //      return;
2325   //    } else if (token == TT_default) {
2326   //      getNextToken();
2327   //      if (token == TT_DDOT) {
2328   //        getNextToken();
2329   //        statement();
2330   //      } else {
2331   //        throwSyntaxError("':' character after 'default' expected.");
2332   //      }
2333   //      return;
2334   //    }
2335   //  }
2336
2337   //  public void expressionStatement() {
2338   //  }
2339
2340   //  private void inclusionStatement() {
2341   //  }
2342
2343   //  public void compoundStatement() {
2344   //  }
2345
2346   //  public void selectionStatement() {
2347   //  }
2348   //
2349   //  public void iterationStatement() {
2350   //  }
2351   //
2352   //  public void jumpStatement() {
2353   //  }
2354   //
2355   //  public void outputStatement() {
2356   //  }
2357   //
2358   //  public void scopeStatement() {
2359   //  }
2360   //
2361   //  public void flowStatement() {
2362   //  }
2363   //
2364   //  public void definitionStatement() {
2365   //  }
2366
2367   private void ifStatement() throws CoreException {
2368     // ':' statement-list [elseif-list] [else-colon-statement] 'endif' ';'
2369     if (token == TT_DDOT) {
2370       getNextToken();
2371       statementList();
2372       switch (token) {
2373         case TT_else :
2374           getNextToken();
2375           if (token == TT_DDOT) {
2376             getNextToken();
2377             statementList();
2378           } else {
2379             if (token == TT_if) { //'else if'
2380               getNextToken();
2381               elseifStatementList();
2382             } else {
2383               throwSyntaxError("':' expected after 'else'.");
2384             }
2385           }
2386           break;
2387         case TT_elseif :
2388           getNextToken();
2389           elseifStatementList();
2390           break;
2391       }
2392
2393       if (token != TT_endif) {
2394         throwSyntaxError("'endif' expected.");
2395       }
2396       getNextToken();
2397       if (token != TT_SEMICOLON) {
2398         throwSyntaxError("';' expected after if-statement.");
2399       }
2400       getNextToken();
2401     } else {
2402       // statement [else-statement]
2403       statement();
2404       if (token == TT_elseif) {
2405         getNextToken();
2406         if (token == TT_ARGOPEN) {
2407           getNextToken();
2408         } else {
2409           throwSyntaxError("'(' expected after 'elseif' keyword.");
2410         }
2411         expression();
2412         if (token == TT_ARGCLOSE) {
2413           getNextToken();
2414         } else {
2415           throwSyntaxError("')' expected after 'elseif' condition.");
2416         }
2417         ifStatement();
2418       } else if (token == TT_else) {
2419         getNextToken();
2420         statement();
2421       }
2422     }
2423   }
2424
2425   private void elseifStatementList() throws CoreException {
2426     do {
2427       elseifStatement();
2428       switch (token) {
2429         case TT_else :
2430           getNextToken();
2431           if (token == TT_DDOT) {
2432             getNextToken();
2433             statementList();
2434             return;
2435           } else {
2436             if (token == TT_if) { //'else if'
2437               getNextToken();
2438             } else {
2439               throwSyntaxError("':' expected after 'else'.");
2440             }
2441           }
2442           break;
2443         case TT_elseif :
2444           getNextToken();
2445           break;
2446         default :
2447           return;
2448       }
2449     } while (true);
2450   }
2451
2452   private void elseifStatement() throws CoreException {
2453     if (token == TT_ARGOPEN) {
2454       getNextToken();
2455       expression();
2456       if (token != TT_ARGOPEN) {
2457         throwSyntaxError("')' expected in else-if-statement.");
2458       }
2459       getNextToken();
2460       if (token != TT_DDOT) {
2461         throwSyntaxError("':' expected in else-if-statement.");
2462       }
2463       getNextToken();
2464       statementList();
2465     }
2466   }
2467
2468   private void switchStatement() throws CoreException {
2469     if (token == TT_DDOT) {
2470       // ':' [labeled-statement-list] 'endswitch' ';'
2471       getNextToken();
2472       labeledStatementList();
2473       if (token != TT_endswitch) {
2474         throwSyntaxError("'endswitch' expected.");
2475       }
2476       getNextToken();
2477       if (token != TT_SEMICOLON) {
2478         throwSyntaxError("';' expected after switch-statement.");
2479       }
2480       getNextToken();
2481     } else {
2482       // '{' [labeled-statement-list] '}'
2483       if (token != TT_LISTOPEN) {
2484         throwSyntaxError("'{' expected in switch statement.");
2485       }
2486       getNextToken();
2487       if (token != TT_LISTCLOSE) {
2488         labeledStatementList();
2489       }
2490       if (token != TT_LISTCLOSE) {
2491         throwSyntaxError("'}' expected in switch statement.");
2492       }
2493       getNextToken();
2494
2495     }
2496   }
2497
2498   private void forStatement() throws CoreException {
2499     if (token == TT_DDOT) {
2500       getNextToken();
2501       statementList();
2502       if (token != TT_endfor) {
2503         throwSyntaxError("'endfor' expected.");
2504       }
2505       getNextToken();
2506       if (token != TT_SEMICOLON) {
2507         throwSyntaxError("';' expected after for-statement.");
2508       }
2509       getNextToken();
2510     } else {
2511       statement();
2512     }
2513   }
2514
2515   private void whileStatement() throws CoreException {
2516     // ':' statement-list 'endwhile' ';'
2517     if (token == TT_DDOT) {
2518       getNextToken();
2519       statementList();
2520       if (token != TT_endwhile) {
2521         throwSyntaxError("'endwhile' expected.");
2522       }
2523       getNextToken();
2524       if (token != TT_SEMICOLON) {
2525         throwSyntaxError("';' expected after while-statement.");
2526       }
2527       getNextToken();
2528     } else {
2529       statement();
2530     }
2531   }
2532
2533   private void foreachStatement() throws CoreException {
2534     if (token == TT_DDOT) {
2535       getNextToken();
2536       statementList();
2537       if (token != TT_endforeach) {
2538         throwSyntaxError("'endforeach' expected.");
2539       }
2540       getNextToken();
2541       if (token != TT_SEMICOLON) {
2542         throwSyntaxError("';' expected after foreach-statement.");
2543       }
2544       getNextToken();
2545     } else {
2546       statement();
2547     }
2548   }
2549
2550   private void exitStatus() throws CoreException {
2551     if (token == TT_ARGOPEN) {
2552       getNextToken();
2553     } else {
2554       throwSyntaxError("'(' expected in 'exit-status'.");
2555     }
2556     if (token != TT_ARGCLOSE) {
2557       expression();
2558     }
2559     if (token == TT_ARGCLOSE) {
2560       getNextToken();
2561     } else {
2562       throwSyntaxError("')' expected after 'exit-status'.");
2563     }
2564   }
2565
2566   private void expressionList() throws CoreException {
2567     do {
2568       expression();
2569       if (token == TT_COMMA) {
2570         getNextToken();
2571       } else {
2572         break;
2573       }
2574     } while (true);
2575   }
2576
2577   private void expression() throws CoreException {
2578     //    if (token == TT_STRING_CONSTANT || token == TT_INTERPOLATED_STRING) {
2579     //      getNextToken();
2580     //    } else {
2581     logicalinclusiveorExpression();
2582     //      while (token != TT_SEMICOLON) {
2583     //        getNextToken();
2584     //      //      }
2585     //    }
2586   }
2587
2588   private void postfixExpression() throws CoreException {
2589     String ident;
2590     boolean castFlag = false;
2591     switch (token) {
2592       case TT_new :
2593         getNextToken();
2594         expression();
2595         break;
2596       case TT_null :
2597         getNextToken();
2598         break;
2599       case TT_false :
2600         getNextToken();
2601         break;
2602       case TT_true :
2603         getNextToken();
2604         break;
2605       case TT_STRING_CONSTANT :
2606         getNextToken();
2607         break;
2608       case TT_INTERPOLATED_STRING :
2609         getNextToken();
2610         break;
2611       case TT_ARGOPEN :
2612         getNextToken();
2613         if (token == TT_IDENTIFIER) {
2614           // check if identifier is a type:
2615           ident = identifier;
2616           String str = identifier.toLowerCase();
2617           for (int i = 0; i < PHP_TYPES.length; i++) {
2618             if (PHP_TYPES[i].equals(str)) {
2619               castFlag = true;
2620               break;
2621             }
2622           }
2623           if (castFlag) {
2624             getNextToken();
2625             if (token != TT_ARGCLOSE) {
2626               throwSyntaxError(") expected after cast-type '" + ident + "'.");
2627             }
2628             getNextToken();
2629             expression();
2630             break;
2631           }
2632         }
2633         if (!castFlag) {
2634           expression();
2635         }
2636         if (token != TT_ARGCLOSE) {
2637           throwSyntaxError(") expected in postfix-expression.");
2638         }
2639         getNextToken();
2640         break;
2641       case TT_DOUBLE_NUMBER :
2642         getNextToken();
2643         break;
2644       case TT_INT_NUMBER :
2645         getNextToken();
2646         break;
2647       case TT_DOLLAROPEN :
2648         getNextToken();
2649         expression();
2650         if (token != TT_LISTCLOSE) {
2651           throwSyntaxError("'}' expected after indirect variable token '${'.");
2652         }
2653         getNextToken();
2654         break;
2655       case TT_VARIABLE :
2656         ident = identifier;
2657         getNextToken();
2658         if (token == TT_LISTOPEN) {
2659           getNextToken();
2660           expression();
2661           if (token != TT_LISTCLOSE) {
2662             throwSyntaxError("'}' expected after variable '" + ident + "' in variable-expression.");
2663           }
2664           getNextToken();
2665         } else if (token == TT_ARGOPEN) {
2666           getNextToken();
2667           if (token != TT_ARGCLOSE) {
2668             expressionList();
2669             if (token != TT_ARGCLOSE) {
2670               throwSyntaxError("')' expected after variable '" + ident + "' in postfix-expression.");
2671             }
2672           }
2673           getNextToken();
2674         }
2675         break;
2676       case TT_IDENTIFIER :
2677         ident = identifier;
2678         getNextToken();
2679         if (token == TT_ARGOPEN) {
2680           getNextToken();
2681           if (token != TT_ARGCLOSE) {
2682             expressionList();
2683             if (token != TT_ARGCLOSE) {
2684               throwSyntaxError("')' expected after identifier '" + ident + "' in postfix-expression.");
2685             }
2686           }
2687           getNextToken();
2688         }
2689         break;
2690       case TT_print :
2691         getNextToken();
2692         expression();
2693         //        if (token == TT_SEMICOLON) {
2694         //          getNextToken();
2695         //        } else {
2696         //          if (!phpEnd) {
2697         //            throwSyntaxError("';' expected after 'print' statement.");
2698         //          }
2699         //          getNextToken();
2700         //        }
2701         break;
2702       case TT_list :
2703         getNextToken();
2704         if (token == TT_ARGOPEN) {
2705           getNextToken();
2706           if (token == TT_COMMA) {
2707             getNextToken();
2708           }
2709           expressionList();
2710           if (token != TT_ARGCLOSE) {
2711             throwSyntaxError("')' expected after 'list' keyword.");
2712           }
2713           getNextToken();
2714           //          if (token == TT_SET) {
2715           //            getNextToken();
2716           //            logicalinclusiveorExpression();
2717           //          }
2718         } else {
2719           throwSyntaxError("'(' expected after 'list' keyword.");
2720         }
2721         break;
2722         //      case TT_exit :
2723         //        getNextToken();
2724         //        if (token != TT_SEMICOLON) {
2725         //          exitStatus();
2726         //        }
2727         //        if (token == TT_SEMICOLON) {
2728         //          getNextToken();
2729         //        } else {
2730         //          if (!phpEnd) {
2731         //            throwSyntaxError("';' expected after 'exit' expression.");
2732         //          }
2733         //          getNextToken();
2734         //        }
2735         //        break;
2736         //      case TT_die :
2737         //        getNextToken();
2738         //        if (token != TT_SEMICOLON) {
2739         //          exitStatus();
2740         //        }
2741         //        if (token == TT_SEMICOLON) {
2742         //          getNextToken();
2743         //        } else {
2744         //          if (!phpEnd) {
2745         //            throwSyntaxError("';' expected after 'die' expression.");
2746         //          }
2747         //        }
2748         //        break;
2749
2750         //      case TT_array :
2751         //        getNextToken();
2752         //        if (token == TT_ARGOPEN) {
2753         //          getNextToken();
2754         //          if (token == TT_COMMA) {
2755         //            getNextToken();
2756         //          }
2757         //          expressionList();
2758         //          if (token != TT_ARGCLOSE) {
2759         //            throwSyntaxError("')' expected after 'list' keyword.");
2760         //          }
2761         //          getNextToken();
2762         //          if (token == TT_SET) {
2763         //            getNextToken();
2764         //            logicalinclusiveorExpression();
2765         //          }
2766         //        } else {
2767         //          throwSyntaxError("'(' expected after 'list' keyword.");
2768         //        }
2769         //        break;
2770     }
2771     boolean while_flag = true;
2772     do {
2773       switch (token) {
2774         case TT_PARTOPEN :
2775           getNextToken();
2776           expression();
2777           if (token != TT_PARTCLOSE) {
2778             throwSyntaxError("] expected in postfix-expression.");
2779           }
2780           getNextToken();
2781           break;
2782         case TT_DDOT2 : // ::
2783         case TT_REF : // ->
2784           getNextToken();
2785           if (token > TT_KEYWORD) {
2786             ident = identifier;
2787             setMarker("Avoid using keyword '" + ident + "' as variable name.", rowCount, PHPParser.INFO);
2788           }
2789           switch (token) {
2790             case TT_VARIABLE :
2791               ident = identifier;
2792               getNextToken();
2793               //              if (token == TT_ARGOPEN) {
2794               //                getNextToken();
2795               //                expressionList();
2796               //                if (token != TT_ARGCLOSE) {
2797               //                  throwSyntaxError(") expected after variable '" + ident + "'.");
2798               //                }
2799               //                getNextToken();
2800               //              }
2801               break;
2802             case TT_IDENTIFIER :
2803               ident = identifier;
2804               getNextToken();
2805               break;
2806             case TT_LISTOPEN :
2807               getNextToken();
2808               expression();
2809               if (token != TT_LISTCLOSE) {
2810                 throwSyntaxError("} expected in postfix-expression.");
2811               }
2812               getNextToken();
2813               break;
2814             default :
2815               throwSyntaxError("Syntax error after '->' token.");
2816           } while (token == TT_PARTOPEN || token == TT_ARGOPEN || token == TT_LISTOPEN) {
2817               if (token == TT_PARTOPEN) {
2818                 getNextToken();
2819                 expressionList();
2820                 if (token != TT_PARTCLOSE) {
2821                   throwSyntaxError("] expected after '->'.");
2822                 }
2823                 getNextToken();
2824               }
2825               if (token == TT_ARGOPEN) {
2826                 getNextToken();
2827                 expressionList();
2828                 if (token != TT_ARGCLOSE) {
2829                   throwSyntaxError(") expected after '->'.");
2830                 }
2831                 getNextToken();
2832               }
2833               if (token == TT_LISTOPEN) {
2834                 getNextToken();
2835                 expression();
2836                 if (token != TT_LISTCLOSE) {
2837                   throwSyntaxError("} expected after '->'.");
2838                 }
2839                 getNextToken();
2840               }
2841             }
2842           break;
2843         case TT_INCREMENT :
2844           getNextToken();
2845           break;
2846         case TT_DECREMENT :
2847           getNextToken();
2848           break;
2849         default :
2850           while_flag = false;
2851       }
2852
2853     }
2854     while (while_flag);
2855   }
2856
2857   private void unaryExpression() throws CoreException {
2858     switch (token) {
2859       case TT_INCREMENT :
2860         getNextToken();
2861         unaryExpression();
2862         break;
2863       case TT_DECREMENT :
2864         getNextToken();
2865         unaryExpression();
2866         break;
2867         // '@' '&' '*' '+' '-' '~' '!' 
2868       case TT_AT :
2869         getNextToken();
2870         castExpression();
2871         break;
2872       case TT_AMPERSAND :
2873         getNextToken();
2874         castExpression();
2875         break;
2876       case TT_MULTIPLY :
2877         getNextToken();
2878         castExpression();
2879         break;
2880       case TT_ADD :
2881         getNextToken();
2882         castExpression();
2883         break;
2884       case TT_SUBTRACT :
2885         getNextToken();
2886         castExpression();
2887         break;
2888       case TT_TILDE :
2889         getNextToken();
2890         castExpression();
2891         break;
2892       case TT_NOT :
2893         getNextToken();
2894         castExpression();
2895         break;
2896       default :
2897         postfixExpression();
2898     }
2899   }
2900
2901   private void castExpression() throws CoreException {
2902     //    if (token == TT_ARGOPEN) {
2903     //      getNextToken();
2904     //      typeName();
2905     //      if (token != TT_ARGCLOSE) {
2906     //        throwSyntaxError(") expected after cast-expression.");
2907     //      }
2908     //      getNextToken();
2909     //    }
2910     unaryExpression();
2911   }
2912
2913   private void typeName() throws CoreException {
2914     //'string' 'unset' 'array' 'object'
2915     //'bool' 'boolean'
2916     //'real' 'double' 'float'
2917     //'int' 'integer'
2918     String ident = "";
2919     if (token == TT_IDENTIFIER) {
2920       ident = identifier;
2921       String str = identifier.toLowerCase();
2922       getNextToken();
2923       for (int i = 0; i < PHP_TYPES.length; i++) {
2924         if (PHP_TYPES[i].equals(str)) {
2925           return;
2926         }
2927       }
2928     }
2929     throwSyntaxError("Expected type cast '( <type-name> )'; Got '" + ident + "'.");
2930   }
2931
2932   private void assignExpression() throws CoreException {
2933     castExpression();
2934     if (token == TT_ASSIGN) { // =
2935       getNextToken();
2936       logicalinclusiveorExpression();
2937     } else if (token == TT_DOTASSIGN) { // .=
2938       getNextToken();
2939       logicalinclusiveorExpression();
2940     } else if (token == TT_FOREACH) { // =>
2941       getNextToken();
2942       logicalinclusiveorExpression();
2943     } else if (token == TT_ADDTO) { // +=
2944       getNextToken();
2945       logicalinclusiveorExpression();
2946     } else if (token == TT_SUBTRACTFROM) { // -=
2947       getNextToken();
2948       logicalinclusiveorExpression();
2949     } else if (token == TT_TIMESBY) { // *=
2950       getNextToken();
2951       logicalinclusiveorExpression();
2952     } else if (token == TT_DIVIDEBY) { // *=
2953       getNextToken();
2954       logicalinclusiveorExpression();
2955     } else if (token == TT_MODASSIGN) { // %=
2956       getNextToken();
2957       logicalinclusiveorExpression();
2958     } else if (token == TT_ANDASSIGN) { // &=
2959       getNextToken();
2960       logicalinclusiveorExpression();
2961     } else if (token == TT_POWASSIGN) { // ^=
2962       getNextToken();
2963       logicalinclusiveorExpression();
2964     } else if (token == TT_LSHIFTASSIGN) { // <<=
2965       getNextToken();
2966       logicalinclusiveorExpression();
2967     } else if (token == TT_RSHIFTASSIGN) { // >>=
2968       getNextToken();
2969       logicalinclusiveorExpression();
2970     } else if (token == TT_TILDEASSIGN) { // ~=
2971       getNextToken();
2972       logicalinclusiveorExpression();
2973     }
2974   }
2975
2976   private void multiplicativeExpression() throws CoreException {
2977     do {
2978       assignExpression();
2979       if (token != TT_MULTIPLY && token != TT_DIV && token != TT_MOD) {
2980         return;
2981       }
2982       getNextToken();
2983     } while (true);
2984   }
2985
2986   private void concatenationExpression() throws CoreException {
2987     do {
2988       multiplicativeExpression();
2989       if (token != TT_DOT) {
2990         return;
2991       }
2992       getNextToken();
2993     } while (true);
2994   }
2995
2996   private void additiveExpression() throws CoreException {
2997     do {
2998       concatenationExpression();
2999       if (token != TT_ADD && token != TT_SUBTRACT) {
3000         return;
3001       }
3002       getNextToken();
3003     } while (true);
3004   }
3005
3006   private void shiftExpression() throws CoreException {
3007     do {
3008       additiveExpression();
3009       if (token != TT_LSHIFT && token != TT_RSHIFT) {
3010         return;
3011       }
3012       getNextToken();
3013     } while (true);
3014   }
3015
3016   private void relationalExpression() throws CoreException {
3017     do {
3018       shiftExpression();
3019       if (token != TT_LESS && token != TT_GREATER && token != TT_LESSEQUAL && token != TT_GREATEREQUAL) {
3020         return;
3021       }
3022       getNextToken();
3023     } while (true);
3024   }
3025
3026   private void identicalExpression() throws CoreException {
3027     do {
3028       relationalExpression();
3029       if (token != TT_EX_EQUAL && token != TT_EX_UNEQUAL) {
3030         return;
3031       }
3032       getNextToken();
3033     } while (true);
3034   }
3035
3036   private void equalityExpression() throws CoreException {
3037     do {
3038       identicalExpression();
3039       if (token != TT_EQUAL && token != TT_UNEQUAL) {
3040         return;
3041       }
3042       getNextToken();
3043     } while (true);
3044   }
3045
3046   private void ternaryExpression() throws CoreException {
3047     equalityExpression();
3048     if (token == TT_QUESTIONMARK) {
3049       getNextToken();
3050       expression();
3051       if (token == TT_DDOT) {
3052         getNextToken();
3053         expression();
3054       } else {
3055         throwSyntaxError("':' expected in ternary operator '? :'.");
3056       }
3057     }
3058   }
3059
3060   private void andExpression() throws CoreException {
3061     do {
3062       ternaryExpression();
3063       if (token != TT_AMPERSAND) {
3064         return;
3065       }
3066       getNextToken();
3067     } while (true);
3068   }
3069
3070   private void exclusiveorExpression() throws CoreException {
3071     do {
3072       andExpression();
3073       if (token != TT_POW) {
3074         return;
3075       }
3076       getNextToken();
3077     } while (true);
3078   }
3079
3080   private void inclusiveorExpression() throws CoreException {
3081     do {
3082       exclusiveorExpression();
3083       if (token != TT_LINE) {
3084         return;
3085       }
3086       getNextToken();
3087     } while (true);
3088   }
3089
3090   private void booleanandExpression() throws CoreException {
3091     do {
3092       inclusiveorExpression();
3093       if (token != TT_AND) {
3094         return;
3095       }
3096       getNextToken();
3097     } while (true);
3098   }
3099
3100   private void booleanorExpression() throws CoreException {
3101     do {
3102       booleanandExpression();
3103       if (token != TT_OR) {
3104         return;
3105       }
3106       getNextToken();
3107     } while (true);
3108   }
3109
3110   private void logicalandExpression() throws CoreException {
3111     do {
3112       booleanorExpression();
3113       if (token != TT_and) {
3114         return;
3115       }
3116       getNextToken();
3117     } while (true);
3118   }
3119
3120   private void logicalexclusiveorExpression() throws CoreException {
3121     do {
3122       logicalandExpression();
3123       if (token != TT_xor) {
3124         return;
3125       }
3126       getNextToken();
3127     } while (true);
3128   }
3129
3130   private void logicalinclusiveorExpression() throws CoreException {
3131     do {
3132       logicalexclusiveorExpression();
3133       if (token != TT_or) {
3134         return;
3135       }
3136       getNextToken();
3137     } while (true);
3138   }
3139
3140   //  public void assignmentExpression() {
3141   //    if (token == TT_VARIABLE) {
3142   //      getNextToken();
3143   //      if (token == TT_SET) {
3144   //        getNextToken();
3145   //        logicalinclusiveorExpression();
3146   //      }
3147   //    } else {
3148   //      logicalinclusiveorExpression();
3149   //    }
3150   //  }
3151
3152   private void variableList() throws CoreException {
3153     do {
3154       variable();
3155       if (token == TT_COMMA) {
3156         getNextToken();
3157       } else {
3158         break;
3159       }
3160     } while (true);
3161   }
3162
3163   private void variable() throws CoreException {
3164     if (token == TT_DOLLAROPEN) {
3165       getNextToken();
3166       expression();
3167       ;
3168       if (token != TT_LISTCLOSE) {
3169         throwSyntaxError("'}' expected after indirect variable token '${'.");
3170       }
3171       getNextToken();
3172     } else {
3173       if (token == TT_VARIABLE) {
3174         getNextToken();
3175         if (token == TT_PARTOPEN) {
3176           getNextToken();
3177           expression();
3178           if (token != TT_PARTCLOSE) {
3179             throwSyntaxError("']' expected in variable-list.");
3180           }
3181           getNextToken();
3182         } else if (token == TT_ASSIGN) {
3183           getNextToken();
3184           constant();
3185         }
3186       } else {
3187         throwSyntaxError("$-variable expected in variable-list.");
3188       }
3189     }
3190   }
3191
3192   private void constant() throws CoreException {
3193     String ident;
3194     switch (token) {
3195       case TT_ADD :
3196         getNextToken();
3197         switch (token) {
3198           case TT_DOUBLE_NUMBER :
3199             getNextToken();
3200             break;
3201           case TT_INT_NUMBER :
3202             getNextToken();
3203             break;
3204           default :
3205             throwSyntaxError("Constant expected after '+' presign.");
3206         }
3207         break;
3208       case TT_SUBTRACT :
3209         getNextToken();
3210         switch (token) {
3211           case TT_DOUBLE_NUMBER :
3212             getNextToken();
3213             break;
3214           case TT_INT_NUMBER :
3215             getNextToken();
3216             break;
3217           default :
3218             throwSyntaxError("Constant expected after '-' presign.");
3219         }
3220         break;
3221       case TT_null :
3222         getNextToken();
3223         break;
3224       case TT_false :
3225         getNextToken();
3226         break;
3227       case TT_true :
3228         getNextToken();
3229         break;
3230       case TT_IDENTIFIER :
3231         ident = identifier;
3232         getNextToken();
3233         if (token == TT_ARGOPEN) {
3234           getNextToken();
3235           if (token != TT_ARGCLOSE) {
3236             expressionList();
3237             if (token != TT_ARGCLOSE) {
3238               throwSyntaxError("')' expected after identifier '" + ident + "' in postfix-expression.");
3239             }
3240           }
3241           getNextToken();
3242         }
3243         break;
3244       case TT_STRING_CONSTANT :
3245         getNextToken();
3246         break;
3247       case TT_INTERPOLATED_STRING :
3248         getNextToken();
3249         break;
3250       case TT_DOUBLE_NUMBER :
3251         getNextToken();
3252         break;
3253       case TT_INT_NUMBER :
3254         getNextToken();
3255         break;
3256       default :
3257         throwSyntaxError("Constant expected.");
3258     }
3259   }
3260
3261   /**
3262    * Call the php parse command ( php -l -f &lt;filename&gt; )
3263    * and create markers according to the external parser output
3264    */
3265   public static void phpExternalParse(IFile file) {
3266     //IFile file = (IFile) resource;
3267     IPath path = file.getFullPath();
3268     IPreferenceStore store = PHPeclipsePlugin.getDefault().getPreferenceStore();
3269     String filename = file.getLocation().toString();
3270
3271     String[] arguments = { filename };
3272     MessageFormat form = new MessageFormat(store.getString(PHPeclipsePlugin.EXTERNAL_PARSER_PREF));
3273     String command = form.format(arguments);
3274
3275     String parserResult = PHPStartApacheAction.execute(command, "External parser: ");
3276
3277     try {
3278       // parse the buffer to find the errors and warnings
3279       createMarkers(parserResult, file);
3280     } catch (CoreException e) {
3281     }
3282   }
3283
3284   /**
3285    * Create markers according to the external parser output
3286    */
3287   private static void createMarkers(String output, IFile file) throws CoreException {
3288     // delete all markers
3289     file.deleteMarkers(IMarker.PROBLEM, false, 0);
3290
3291     int indx = 0;
3292     int brIndx = 0;
3293     boolean flag = true;
3294     while ((brIndx = output.indexOf("<br />", indx)) != -1) {
3295       // newer php error output (tested with 4.2.3)
3296       scanLine(output, file, indx, brIndx);
3297       indx = brIndx + 6;
3298       flag = false;
3299     }
3300     if (flag) {
3301       while ((brIndx = output.indexOf("<br>", indx)) != -1) {
3302         // older php error output (tested with 4.2.3)
3303         scanLine(output, file, indx, brIndx);
3304         indx = brIndx + 4;
3305       }
3306     }
3307   }
3308
3309   private static void scanLine(String output, IFile file, int indx, int brIndx) throws CoreException {
3310     String current;
3311     String outLineNumberString;
3312     StringBuffer lineNumberBuffer = new StringBuffer(10);
3313     char ch;
3314     current = output.substring(indx, brIndx);
3315
3316     if (current.indexOf(PARSE_WARNING_STRING) != -1 || current.indexOf(PARSE_ERROR_STRING) != -1) {
3317       int onLine = current.indexOf("on line <b>");
3318       if (onLine != -1) {
3319         lineNumberBuffer.delete(0, lineNumberBuffer.length());
3320         for (int i = onLine; i < current.length(); i++) {
3321           ch = current.charAt(i);
3322           if ('0' <= ch && '9' >= ch) {
3323             lineNumberBuffer.append(ch);
3324           }
3325         }
3326
3327         int lineNumber = Integer.parseInt(lineNumberBuffer.toString());
3328
3329         Hashtable attributes = new Hashtable();
3330
3331         current = current.replaceAll("\n", "");
3332         current = current.replaceAll("<b>", "");
3333         current = current.replaceAll("</b>", "");
3334         MarkerUtilities.setMessage(attributes, current);
3335
3336         if (current.indexOf(PARSE_ERROR_STRING) != -1)
3337           attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_ERROR));
3338         else if (current.indexOf(PARSE_WARNING_STRING) != -1)
3339           attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_WARNING));
3340         else
3341           attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_INFO));
3342         MarkerUtilities.setLineNumber(attributes, lineNumber);
3343         MarkerUtilities.createMarker(file, attributes, IMarker.PROBLEM);
3344       }
3345     }
3346   }
3347 }