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