improved/refactored php syntax parser
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / phpeditor / PHPParser.java
index a6f236e..a1282df 100644 (file)
@@ -1,28 +1,34 @@
+/**********************************************************************
+Copyright (c) 2002 Klaus Hartlage - www.eclipseproject.de
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+    Klaus Hartlage - www.eclipseproject.de
+**********************************************************************/
 package net.sourceforge.phpeclipse.phpeditor;
 
+import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Hashtable;
 
+import net.sourceforge.phpeclipse.PHPeclipsePlugin;
+import net.sourceforge.phpeclipse.actions.PHPStartApacheAction;
 import net.sourceforge.phpeclipse.phpeditor.php.PHPKeywords;
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IMarker;
 import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jface.preference.IPreferenceStore;
 import org.eclipse.ui.texteditor.MarkerUtilities;
 
-/**********************************************************************
-Copyright (c) 2000, 2002 IBM Corp. and others.
-All rights reserved. This program and the accompanying materials
-are made available under the terms of the Common Public License v1.0
-which accompanies this distribution, and is available at
-http://www.eclipse.org/legal/cpl-v10.html
-
-Contributors:
-    IBM Corporation - Initial implementation
-    Klaus Hartlage - www.eclipseproject.de
-**********************************************************************/
-
 public class PHPParser extends PHPKeywords {
+  // strings for external parser call
+  private static final String PARSE_ERROR_STRING = "Parse error"; //$NON-NLS-1$
+  private static final String PARSE_WARNING_STRING = "Warning"; //$NON-NLS-1$
 
   public static final int ERROR = 2;
   public static final int WARNING = 1;
@@ -90,7 +96,11 @@ public class PHPParser extends PHPKeywords {
   final static int TT_LSHIFTASSIGN = 59;
   final static int TT_ANDASSIGN = 60;
   final static int TT_QUESTIONMARK = 61;
+  final static int TT_DDOT2 = 62;
+  final static int TT_AT = 63;
+  // final static int TT_HEREDOC = 64;
 
+  final static int TT_DOLLAROPEN = 127;
   final static int TT_ARGOPEN = 128;
   final static int TT_ARGCLOSE = 129;
   final static int TT_LISTOPEN = 130;
@@ -153,7 +163,7 @@ public class PHPParser extends PHPKeywords {
   /**
    * Create marker for the parse error
    */
-  protected void setMarker(String message, int lineNumber, int errorLevel) throws CoreException {
+  private void setMarker(String message, int lineNumber, int errorLevel) throws CoreException {
     setMarker(fileToParse, message, lineNumber, errorLevel);
   }
 
@@ -193,12 +203,17 @@ public class PHPParser extends PHPKeywords {
     throw new SyntaxError(rowCount, chIndx - columnCount + 1, str.substring(columnCount, eol), error);
   }
 
+  private void throwSyntaxError(String error, int startRow) {
+
+    throw new SyntaxError(startRow, 0, " ", error);
+  }
+
   /**
    *  Method Declaration.
    *
    *@see
    */
-  void getChar() {
+  private void getChar() {
     if (str.length() > chIndx) {
       ch = str.charAt(chIndx++);
 
@@ -214,7 +229,7 @@ public class PHPParser extends PHPKeywords {
   /**
    * gets the next token from input
    */
-  void getNextToken() {
+  private void getNextToken() throws CoreException {
     phpEnd = false;
 
     while (str.length() > chIndx) {
@@ -229,7 +244,18 @@ public class PHPParser extends PHPKeywords {
         phpEnd = true;
       }
       if (!Character.isWhitespace(ch)) {
-        if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch == '_') || (ch == '$') || (ch == '@')) {
+        if (ch == '$') {
+          if (str.length() > chIndx) {
+            if (str.charAt(chIndx) == '{') {
+              chIndx++;
+              token = TT_DOLLAROPEN;
+              return;
+            }
+          }
+          getIdentifier();
+          return;
+        }
+        if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch == '_') || (ch == '$')) {
           getIdentifier();
           return;
         }
@@ -294,6 +320,7 @@ public class PHPParser extends PHPKeywords {
         } else if (ch == '\'') {
           // read string until end
           boolean openString = true;
+          int startRow = rowCount;
           while (str.length() > chIndx) {
             ch = str.charAt(chIndx++);
             if (ch == '\\') {
@@ -309,8 +336,32 @@ public class PHPParser extends PHPKeywords {
             }
           }
           if (openString) {
-            throwSyntaxError("Open string character \"'\" at end of file.");
+            throwSyntaxError("Open string character \"'\" at end of file.", startRow);
+          }
+          token = TT_STRING_CONSTANT;
+          return;
+        } else if (ch == '`') {
+          // read string until end
+          boolean openString = true;
+          int startRow = rowCount;
+          while (str.length() > chIndx) {
+            ch = str.charAt(chIndx++);
+            if (ch == '\\') {
+              if (str.length() > chIndx) {
+                ch = str.charAt(chIndx++);
+              }
+            } else if (ch == '`') {
+              openString = false;
+              break;
+            } else if (ch == '\n') {
+              rowCount++;
+              columnCount = chIndx;
+            }
+          }
+          if (openString) {
+            throwSyntaxError("Open string character \"`\" at end of file.", startRow);
           }
+          setMarker("Other string delimiters prefered (found \"`\").", rowCount, PHPParser.INFO);
           token = TT_STRING_CONSTANT;
           return;
         }
@@ -348,6 +399,9 @@ public class PHPParser extends PHPKeywords {
           case '?' :
             token = TT_QUESTIONMARK;
             break;
+          case '@' :
+            token = TT_AT;
+            break;
           case '~' :
             token = TT_TILDE;
             if (str.length() > chIndx) {
@@ -558,12 +612,34 @@ public class PHPParser extends PHPKeywords {
               if (str.charAt(chIndx) == '<') {
                 chIndx++;
                 token = TT_LSHIFT;
-                if (str.length() > chIndx) {
-                  if (str.charAt(chIndx) == '=') {
-                    chIndx++;
-                    token = TT_LSHIFTASSIGN;
-                    break;
+                if (str.charAt(chIndx) == '<') {
+                  // heredoc
+                  int startRow = rowCount;
+                  if (str.length() > chIndx) {
+
+                    ch = str.charAt(++chIndx);
+                    if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch == '_')) {
+                      chIndx++;
+                      getIdentifier();
+                      token = TT_STRING_CONSTANT;
+                      while (str.length() > chIndx) {
+                        ch = str.charAt(chIndx++);
+                        if (ch == '\n') {
+                          if (str.length() >= chIndx + identifier.length()) {
+                            if (str.substring(chIndx, chIndx + identifier.length()).equals(identifier)) {
+                              chIndx += identifier.length();
+                              return;
+                            }
+                          }
+                        }
+                      }
+                    }
                   }
+                  throwSyntaxError("Open heredoc syntax after operator '<<<'.", startRow);
+                } else if (str.charAt(chIndx) == '=') {
+                  chIndx++;
+                  token = TT_LSHIFTASSIGN;
+                  break;
                 }
                 break;
               }
@@ -603,7 +679,12 @@ public class PHPParser extends PHPKeywords {
             break;
           case ':' :
             token = TT_DDOT;
-
+            if (str.length() > chIndx) {
+              if (str.charAt(chIndx) == ':') {
+                chIndx++;
+                token = TT_DDOT2;
+              }
+            }
             break;
           case '#' :
             token = TT_HASH;
@@ -648,7 +729,7 @@ public class PHPParser extends PHPKeywords {
     }
   }
 
-  void getIdentifier() {
+  private void getIdentifier() {
     StringBuffer ident = new StringBuffer();
 
     ident.append(ch);
@@ -671,7 +752,7 @@ public class PHPParser extends PHPKeywords {
     }
   }
 
-  void getNumber() {
+  private void getNumber() {
     StringBuffer inum = new StringBuffer();
     char dFlag = ' ';
     int numFormat = 10;
@@ -757,11 +838,6 @@ public class PHPParser extends PHPKeywords {
   }
 
   public void htmlParse(String input) {
-    boolean lineCommentMode = false;
-    boolean multiLineCommentMode = false;
-    boolean stringMode = false;
-
-    StringBuffer buf = new StringBuffer();
     int lineNumber = 1;
     int startLineNumber = 1;
     int startIndex = 0;
@@ -771,6 +847,7 @@ public class PHPParser extends PHPKeywords {
     boolean phpFound = false;
 
     phpList = new ArrayList();
+    currentPHPString = 0;
 
     try {
       int i = 0;
@@ -825,53 +902,91 @@ public class PHPParser extends PHPKeywords {
         }
 
         if (phpMode) {
-          buf.append(ch);
-          if (lineCommentMode && (ch == '\n')) {
-            lineCommentMode = false;
-            // read until end of line
-          } else if ((!stringMode) && (ch == '#')) {
-            // read until end of line
-            lineCommentMode = true;
-            continue;
-          } else if ((!stringMode) && (!multiLineCommentMode) && (ch == '/')) {
+          if (ch == '/' && i < input.length()) {
             ch2 = input.charAt(i++);
             if (ch2 == '/') {
-              lineCommentMode = true;
+              while (i < input.length()) {
+                ch = input.charAt(i++);
+                if (ch == '?' && i < input.length()) {
+                  ch2 = input.charAt(i++);
+                  if (ch2 == '>') {
+                    // php end
+                    phpMode = false;
+                    phpList.add(new PHPString(input.substring(startIndex, i - 2), startLineNumber));
+                    continue;
+                  }
+                  i--;
+                } else if (ch == '\n') {
+                  lineNumber++;
+                  break;
+                }
+              }
               continue;
             } else if (ch2 == '*') {
-              multiLineCommentMode = true;
-              continue;
-            } else {
-              i--;
-            }
-          } else if (ch == '*' && multiLineCommentMode) {
-            ch2 = input.charAt(i++);
-            if (ch2 == '/') {
-              multiLineCommentMode = false;
+              // multi-line comment
+              while (i < input.length()) {
+                ch = input.charAt(i++);
+                if (ch == '\n') {
+                  lineNumber++;
+                } else if (ch == '*' && i < input.length()) {
+                  ch2 = input.charAt(i++);
+                  if (ch2 == '/') {
+                    break;
+                  }
+                  i--;
+                }
+              }
               continue;
             } else {
               i--;
             }
-          } else if (ch == '\\' && stringMode) {
-            ch2 = input.charAt(i++);
-            if (ch2 == '"') {
-              continue;
-            } else {
-              i--;
+          } else if (ch == '#') {
+            while (i < input.length()) {
+              ch = input.charAt(i++);
+              if (ch == '?' && i < input.length()) {
+                ch2 = input.charAt(i++);
+                if (ch2 == '>') {
+                  // php end
+                  phpMode = false;
+                  phpList.add(new PHPString(input.substring(startIndex, i - 2), startLineNumber));
+                  continue;
+                }
+                i--;
+              } else if (ch == '\n') {
+                lineNumber++;
+                break;
+              }
             }
-          } else if ((!lineCommentMode) && (!multiLineCommentMode) && (ch == '"')) {
-            if (stringMode) {
-              stringMode = false;
-            } else {
-              stringMode = true;
+            continue;
+          } else if (ch == '"') {
+            ch = ' ';
+            while (i < input.length()) {
+              ch = input.charAt(i++);
+              if (ch == '\n') {
+                lineNumber++;
+              } else if (ch == '\\' && i < input.length()) { // escape
+                i++;
+              } else if (ch == '"') {
+                break;
+              }
             }
             continue;
-          }
-          if (lineCommentMode || multiLineCommentMode || stringMode) {
+          } else if (ch == '\'') {
+            ch = ' ';
+            while (i < input.length()) {
+              ch = input.charAt(i++);
+              if (ch == '\n') {
+                lineNumber++;
+              } else if (ch == '\\' && i < input.length()) { // escape
+                i++;
+              } else if (ch == '\'') {
+                break;
+              }
+            }
             continue;
           }
 
-          if (ch == '?') {
+          if (ch == '?' && i < input.length()) {
             ch2 = input.charAt(i++);
             if (ch2 == '>') {
               // php end
@@ -881,12 +996,16 @@ public class PHPParser extends PHPKeywords {
             }
             i--;
           }
-        } else {
         }
       }
+
       if (!phpFound) {
         setMarker("No PHP source code found.", lineNumber, PHPParser.INFO);
       } else {
+        if (phpMode) {
+          setMarker("Open PHP tag at end of file.", lineNumber, PHPParser.INFO);
+          phpList.add(new PHPString(input.substring(startIndex, i - 2), startLineNumber));
+        }
         //        for (int j=0;j<phpList.size();j++) {
         //          String temp = ((PHPString)phpList.get(j)).getPHPString();
         //          int startIndx = temp.length()-10;
@@ -907,56 +1026,71 @@ public class PHPParser extends PHPKeywords {
   }
 
   public void phpParse(String s, int rowCount) throws CoreException {
-    // start up
-    try {
-      this.str = s;
-      if (s == null) {
-        if (phpList.size() != 0) {
-          this.str = ((PHPString) phpList.get(currentPHPString++)).getPHPString();
-        }
+    this.str = s;
+    if (s == null) {
+      if (phpList.size() != 0) {
+        this.str = ((PHPString) phpList.get(currentPHPString++)).getPHPString();
       }
-      this.token = TT_EOF;
-      this.chIndx = 0;
-      this.rowCount = rowCount;
-      this.columnCount = 0;
-      this.phpEnd = false;
-      getNextToken();
-      if (token != TT_EOF && token != TT_UNDEFINED) {
-        statementList();
-      }
-      if (token != TT_EOF && token != TT_UNDEFINED) {
-        if (token == TT_ARGCLOSE) {
-          throwSyntaxError("too many closing ')'; end-of-file not reached");
-        }
-        if (token == TT_LISTCLOSE) {
-          throwSyntaxError("too many closing '}'; end-of-file not reached");
-        }
-        if (token == TT_PARTCLOSE) {
-          throwSyntaxError("too many closing ']'; end-of-file not reached");
+    }
+    this.token = TT_EOF;
+    this.chIndx = 0;
+    this.rowCount = rowCount;
+    this.columnCount = 0;
+    this.phpEnd = false;
+    getNextToken();
+    do {
+      try {
+        if (token != TT_EOF && token != TT_UNDEFINED) {
+          statementList();
         }
+        if (token != TT_EOF && token != TT_UNDEFINED) {
+          if (token == TT_ARGCLOSE) { 
+            throwSyntaxError("Too many closing ')'; end-of-file not reached.");
+          }
+          if (token == TT_LISTCLOSE) {
+            throwSyntaxError("Too many closing '}'; end-of-file not reached.");
+          }
+          if (token == TT_PARTCLOSE) {
+            throwSyntaxError("Too many closing ']'; end-of-file not reached.");
+          }
 
-        if (token == TT_ARGOPEN) {
-          throwSyntaxError("read character '('; end-of-file not reached");
+          if (token == TT_ARGOPEN) {
+            throwSyntaxError("Read character '('; end-of-file not reached.");
+          }
+          if (token == TT_LISTOPEN) {
+            throwSyntaxError("Read character '{';  end-of-file not reached.");
+          }
+          if (token == TT_PARTOPEN) {
+            throwSyntaxError("Read character '[';  end-of-file not reached.");
+          }
+
+          throwSyntaxError("End-of-file not reached.");
         }
-        if (token == TT_LISTOPEN) {
-          throwSyntaxError("read character '{';  end-of-file not reached");
+        return;
+      } catch (SyntaxError err) {
+        if (s != null) {
+          throw err;
+        } else {
+          setMarker(err.getMessage(), err.getLine(), ERROR);
         }
-        if (token == TT_PARTOPEN) {
-          throwSyntaxError("read character '[';  end-of-file not reached");
+        // if an error occured, 
+        // try to find keywords 'class' or 'function'
+        // to parse the rest of the string
+        while (token != TT_EOF && token != TT_UNDEFINED) {
+          if (token == TT_class || token == TT_function) {
+            break;
+          }
+          getNextToken();
+        }
+        if (token == TT_EOF || token == TT_UNDEFINED) {
+          return;
         }
-
-        throwSyntaxError("end-of-file not reached");
-      }
-    } catch (SyntaxError err) {
-      if (s != null) {
-        throw err;
-      } else {
-        setMarker(err.getMessage(), err.getLine(), ERROR);
       }
     }
+    while (true);
   }
 
-  public void statementList() throws CoreException {
+  private void statementList() throws CoreException {
     do {
       statement();
       if ((token == TT_LISTCLOSE)
@@ -975,7 +1109,7 @@ public class PHPParser extends PHPKeywords {
     } while (true);
   }
 
-  public void compoundStatement() throws CoreException {
+  private void compoundStatement() throws CoreException {
     // '{' [statement-list] '}'
     if (token == TT_LISTOPEN) {
       getNextToken();
@@ -992,103 +1126,133 @@ public class PHPParser extends PHPKeywords {
     }
   }
 
-  public void statement() throws CoreException {
-    if (token > TT_KEYWORD && token != TT_list) {
-      String keyword = identifier;
-      if (token == TT_include || token == TT_include_once) {
+  private void statement() throws CoreException {
+    //   if (token > TT_KEYWORD && token != TT_list && token != TT_new) {
+    String keyword = identifier;
+    if (token == TT_include || token == TT_include_once) {
+      getNextToken();
+      expression();
+      if (token == TT_SEMICOLON) {
         getNextToken();
-        expression();
-        if (token == TT_SEMICOLON) {
-          getNextToken();
-        } else {
-          if (!phpEnd) {
-            throwSyntaxError("';' character after 'include' or 'include_once' expected.");
-          }
+      } else {
+        if (!phpEnd) {
+          throwSyntaxError("';' character after 'include' or 'include_once' expected.");
         }
-        return;
-      } else if (token == TT_require || token == TT_require_once) {
+      }
+      return;
+    } else if (token == TT_require || token == TT_require_once) {
+      getNextToken();
+      //constant();
+      expression();
+      if (token == TT_SEMICOLON) {
         getNextToken();
-        //constant();
-        expression();
-        if (token == TT_SEMICOLON) {
-          getNextToken();
-        } else {
-          if (!phpEnd) {
-            throwSyntaxError("';' character after 'require' or 'require_once' expected.");
-          }
+      } else {
+        if (!phpEnd) {
+          throwSyntaxError("';' character after 'require' or 'require_once' expected.");
         }
-        return;
-      } else if (token == TT_if) {
+      }
+      return;
+    } else if (token == TT_if) {
+      getNextToken();
+      if (token == TT_ARGOPEN) {
         getNextToken();
-        if (token == TT_ARGOPEN) {
-          getNextToken();
-        } else {
-          throwSyntaxError("'(' expected after 'if' keyword.");
-        }
-        expression();
-        if (token == TT_ARGCLOSE) {
-          getNextToken();
-        } else {
-          throwSyntaxError("')' expected after 'if' condition.");
-        }
-        ifStatement();
-        return;
+      } else {
+        throwSyntaxError("'(' expected after 'if' keyword.");
+      }
+      expression();
+      if (token == TT_ARGCLOSE) {
+        getNextToken();
+      } else {
+        throwSyntaxError("')' expected after 'if' condition.");
+      }
+      ifStatement();
+      return;
 
-      } else if (token == TT_switch) {
+    } else if (token == TT_switch) {
+      getNextToken();
+      if (token == TT_ARGOPEN) {
         getNextToken();
-        if (token == TT_ARGOPEN) {
-          getNextToken();
-        } else {
-          throwSyntaxError("'(' expected after 'switch' keyword.");
-        }
-        expression();
-        if (token == TT_ARGCLOSE) {
-          getNextToken();
-        } else {
-          throwSyntaxError("')' expected after 'switch' condition.");
-        }
-        switchStatement();
-        return;
-      } else if (token == TT_for) {
+      } else {
+        throwSyntaxError("'(' expected after 'switch' keyword.");
+      }
+      expression();
+      if (token == TT_ARGCLOSE) {
         getNextToken();
-        if (token == TT_ARGOPEN) {
-          getNextToken();
-        } else {
-          throwSyntaxError("'(' expected after 'for' keyword.");
-        }
+      } else {
+        throwSyntaxError("')' expected after 'switch' condition.");
+      }
+      switchStatement();
+      return;
+    } else if (token == TT_for) {
+      getNextToken();
+      if (token == TT_ARGOPEN) {
+        getNextToken();
+      } else {
+        throwSyntaxError("'(' expected after 'for' keyword.");
+      }
+      if (token == TT_SEMICOLON) {
+        getNextToken();
+      } else {
+        expressionList();
         if (token == TT_SEMICOLON) {
           getNextToken();
         } else {
-          expression();
-          if (token == TT_SEMICOLON) {
-            getNextToken();
-          } else {
-            throwSyntaxError("';' character after 'for' expected.");
-          }
+          throwSyntaxError("';' expected after 'for'.");
         }
+      }
+      if (token == TT_SEMICOLON) {
+        getNextToken();
+      } else {
+        expressionList();
         if (token == TT_SEMICOLON) {
           getNextToken();
         } else {
-          expression();
-          if (token == TT_SEMICOLON) {
-            getNextToken();
-          } else {
-            throwSyntaxError("';' character after 'for' expected.");
-          }
+          throwSyntaxError("';' expected after 'for'.");
         }
+      }
+      if (token == TT_ARGCLOSE) {
+        getNextToken();
+      } else {
+        expressionList();
         if (token == TT_ARGCLOSE) {
           getNextToken();
         } else {
-          expression();
-          if (token == TT_ARGCLOSE) {
-            getNextToken();
-          } else {
-            throwSyntaxError("')' expected after 'for' condition.");
-          }
+          throwSyntaxError("')' expected after 'for'.");
         }
-        forStatement();
-        return;
-      } else if (token == TT_while) {
+      }
+      forStatement();
+      return;
+    } else if (token == TT_while) {
+      getNextToken();
+      if (token == TT_ARGOPEN) {
+        getNextToken();
+      } else {
+        throwSyntaxError("'(' expected after 'while' keyword.");
+      }
+      expression();
+      if (token == TT_ARGCLOSE) {
+        getNextToken();
+      } else {
+        throwSyntaxError("')' expected after 'while' condition.");
+      }
+      whileStatement();
+      return;
+    } else if (token == TT_do) {
+      getNextToken();
+      if (token == TT_LISTOPEN) {
+        getNextToken();
+      } else {
+        throwSyntaxError("'{' expected after 'do' keyword.");
+      }
+      if (token != TT_LISTCLOSE) {
+        statementList();
+      }
+      if (token == TT_LISTCLOSE) {
+        getNextToken();
+      } else {
+        throwSyntaxError("'}' expected after 'do' keyword.");
+      }
+      if (token == TT_while) {
         getNextToken();
         if (token == TT_ARGOPEN) {
           getNextToken();
@@ -1101,198 +1265,170 @@ public class PHPParser extends PHPKeywords {
         } else {
           throwSyntaxError("')' expected after 'while' condition.");
         }
-        whileStatement();
-        return;
-      } else if (token == TT_do) {
+      } else {
+        throwSyntaxError("'while' expected after 'do' keyword.");
+      }
+      if (token == TT_SEMICOLON) {
         getNextToken();
-        if (token == TT_LISTOPEN) {
-          getNextToken();
-        } else {
-          throwSyntaxError("'{' expected after 'do' keyword.");
-        }
-        if (token != TT_LISTCLOSE) {
-          statementList();
-        }
-        if (token == TT_LISTCLOSE) {
-          getNextToken();
-        } else {
-          throwSyntaxError("'}' expected after 'do' keyword.");
-        }
-        if (token == TT_while) {
-          getNextToken();
-          if (token == TT_ARGOPEN) {
-            getNextToken();
-          } else {
-            throwSyntaxError("'(' expected after 'while' keyword.");
-          }
-          expression();
-          if (token == TT_ARGCLOSE) {
-            getNextToken();
-          } else {
-            throwSyntaxError("')' expected after 'while' condition.");
-          }
-        } else {
-          throwSyntaxError("'while' expected after 'do' keyword.");
-        }
-        if (token == TT_SEMICOLON) {
-          getNextToken();
-        } else {
-          if (!phpEnd) {
-            throwSyntaxError("';' expected after do-while statement.");
-          }
+      } else {
+        if (!phpEnd) {
+          throwSyntaxError("';' expected after do-while statement.");
         }
-        return;
-      } else if (token == TT_foreach) {
+      }
+      return;
+    } else if (token == TT_foreach) {
+      getNextToken();
+      if (token == TT_ARGOPEN) {
+        getNextToken();
+      } else {
+        throwSyntaxError("'(' expected after 'foreach' keyword.");
+      }
+      expression();
+      if (token == TT_as) {
+        getNextToken();
+      } else {
+        throwSyntaxError("'as' expected after 'foreach' exxpression.");
+      }
+      variable();
+      if (token == TT_FOREACH) {
         getNextToken();
-        if (token == TT_ARGOPEN) {
-          getNextToken();
-        } else {
-          throwSyntaxError("'(' expected after 'foreach' keyword.");
-        }
-        expression();
-        if (token == TT_as) {
-          getNextToken();
-        } else {
-          throwSyntaxError("'as' expected after 'foreach' exxpression.");
-        }
         variable();
-        if (token == TT_FOREACH) {
-          getNextToken();
-          variable();
-        }
-        if (token == TT_ARGCLOSE) {
-          getNextToken();
-        } else {
-          throwSyntaxError("')' expected after 'foreach' expression.");
-        }
-        foreachStatement();
-        return;
+      }
+      if (token == TT_ARGCLOSE) {
+        getNextToken();
+      } else {
+        throwSyntaxError("')' expected after 'foreach' expression.");
+      }
+      foreachStatement();
+      return;
 
-      } else if (token == TT_continue || token == TT_break || token == TT_return) {
+    } else if (token == TT_continue || token == TT_break || token == TT_return) {
+      getNextToken();
+      if (token != TT_SEMICOLON) {
+        expression();
+      }
+      if (token == TT_SEMICOLON) {
         getNextToken();
-        if (token != TT_SEMICOLON) {
-          expression();
-        }
-        if (token == TT_SEMICOLON) {
-          getNextToken();
-        } else {
-          if (!phpEnd) {
-            throwSyntaxError("';' expected after 'continue', 'break' or 'return'.");
-          }
+      } else {
+        if (!phpEnd) {
+          throwSyntaxError("';' expected after 'continue', 'break' or 'return'.");
         }
-        return;
+      }
+      return;
 
-      } else if (token == TT_echo) {
+    } else if (token == TT_echo) {
+      getNextToken();
+      expressionList();
+      if (token == TT_SEMICOLON) {
         getNextToken();
-        expressionList();
-        if (token == TT_SEMICOLON) {
-          getNextToken();
-        } else {
-          if (!phpEnd) {
-            throwSyntaxError("';' expected after 'echo' statement.");
-          }
+      } else {
+        if (!phpEnd) {
+          throwSyntaxError("';' expected after 'echo' statement.");
         }
-        return;
-      } else if (token == TT_print) {
+      }
+      return;
+      //    } else if (token == TT_print) {
+      //      getNextToken();
+      //      expression();
+      //      if (token == TT_SEMICOLON) {
+      //        getNextToken();
+      //      } else {
+      //        if (!phpEnd) {
+      //          throwSyntaxError("';' expected after 'print' statement.");
+      //        }
+      //      }
+      //      return;
+
+    } else if (token == TT_global || token == TT_static) {
+      getNextToken();
+      variableList();
+      if (token == TT_SEMICOLON) {
         getNextToken();
-        expression();
-        if (token == TT_SEMICOLON) {
-          getNextToken();
-        } else {
-          if (!phpEnd) {
-            throwSyntaxError("';' expected after 'print' statement.");
-          }
+      } else {
+        if (!phpEnd) {
+          throwSyntaxError("';' expected after 'global' or 'static' statement.");
         }
-        return;
+      }
+      return;
 
-      } else if (token == TT_global || token == TT_static) {
+      //      } else if (token == TT_unset) {
+      //        getNextToken();
+      //        if (token == TT_ARGOPEN) {
+      //          getNextToken();
+      //        } else {
+      //          throwSyntaxError("'(' expected after 'unset' keyword.");
+      //        }
+      //        variableList();
+      //        if (token == TT_ARGCLOSE) {
+      //          getNextToken();
+      //        } else {
+      //          throwSyntaxError("')' expected after 'unset' statement.");
+      //        }
+      //        if (token == TT_SEMICOLON) {
+      //          getNextToken();
+      //        } else {
+      //          if (!phpEnd) {
+      //            throwSyntaxError("';' expected after 'unset' statement.");
+      //          }
+      //        }
+      //        return;
+
+      //      } else if (token == TT_exit || token == TT_die) {
+      //        getNextToken();
+      //        if (token != TT_SEMICOLON) {
+      //          exitStatus();
+      //        }
+      //        if (token == TT_SEMICOLON) {
+      //          getNextToken();
+      //        } else {
+      //          if (!phpEnd) {
+      //            throwSyntaxError("';' expected after 'exit' or 'die' statement.");
+      //          }
+      //        }
+      //        return;
+
+    } else if (token == TT_define) {
+      getNextToken();
+      if (token == TT_ARGOPEN) {
         getNextToken();
-        variableList();
-        if (token == TT_SEMICOLON) {
-          getNextToken();
-        } else {
-          if (!phpEnd) {
-            throwSyntaxError("';' expected after 'global' or 'static' statement.");
-          }
-        }
-        return;
-
-      } else if (token == TT_unset) {
+      } else {
+        throwSyntaxError("'(' expected after 'define' keyword.");
+      }
+      expression();
+      if (token == TT_COMMA) {
         getNextToken();
-        if (token == TT_ARGOPEN) {
-          getNextToken();
-        } else {
-          throwSyntaxError("'(' expected after 'unset' keyword.");
-        }
-        variableList();
-        if (token == TT_ARGCLOSE) {
-          getNextToken();
-        } else {
-          throwSyntaxError("')' expected after 'unset' statement.");
-        }
-        if (token == TT_SEMICOLON) {
-          getNextToken();
-        } else {
-          if (!phpEnd) {
-            throwSyntaxError("';' expected after 'unset' statement.");
-          }
-        }
-        return;
-
-        //      } else if (token == TT_exit || token == TT_die) {
-        //        getNextToken();
-        //        if (token != TT_SEMICOLON) {
-        //          exitStatus();
-        //        }
-        //        if (token == TT_SEMICOLON) {
-        //          getNextToken();
-        //        } else {
-        //          if (!phpEnd) {
-        //            throwSyntaxError("';' expected after 'exit' or 'die' statement.");
-        //          }
-        //        }
-        //        return;
-
-      } else if (token == TT_define) {
+      } else {
+        throwSyntaxError("',' expected after first 'define' constant.");
+      }
+      expression();
+      if (token == TT_COMMA) {
         getNextToken();
-        if (token == TT_ARGOPEN) {
-          getNextToken();
-        } else {
-          throwSyntaxError("'(' expected after 'define' keyword.");
-        }
-        constant();
-        if (token == TT_COMMA) {
-          getNextToken();
-        } else {
-          throwSyntaxError("',' expected after first 'define' constant.");
-        }
-        constant();
-        if (token == TT_ARGCLOSE) {
-          getNextToken();
-        } else {
-          throwSyntaxError("')' expected after 'define' statement.");
-        }
-        if (token == TT_SEMICOLON) {
-          getNextToken();
-        } else {
-          if (!phpEnd) {
-            throwSyntaxError("';' expected after 'define' statement.");
-          }
-        }
-        return;
-      } else if (token == TT_function) {
+        expression();
+      }
+      if (token == TT_ARGCLOSE) {
         getNextToken();
-        functionDefinition();
-        return;
-      } else if (token == TT_class) {
+      } else {
+        throwSyntaxError("')' expected after 'define' statement.");
+      }
+      if (token == TT_SEMICOLON) {
         getNextToken();
-        classDeclarator();
-        classBody();
-        return;
       } else {
-        throwSyntaxError("Unexpected keyword '" + keyword + "'");
+        if (!phpEnd) {
+          throwSyntaxError("';' expected after 'define' statement.");
+        }
       }
-
+      return;
+    } else if (token == TT_function) {
+      getNextToken();
+      functionDefinition();
+      return;
+    } else if (token == TT_class) {
+      getNextToken();
+      classDeclarator();
+      classBody();
+      return;
+      //      } else {
+      //        throwSyntaxError("Unexpected keyword '" + keyword + "'");
     } else if (token == TT_LISTOPEN) {
       // compoundStatement
       getNextToken();
@@ -1318,10 +1454,9 @@ public class PHPParser extends PHPKeywords {
         }
       }
     }
-
   }
 
-  public void classDeclarator() {
+  private void classDeclarator() throws CoreException {
     //identifier
     //identifier 'extends' identifier
     if (token == TT_IDENTIFIER) {
@@ -1339,7 +1474,7 @@ public class PHPParser extends PHPKeywords {
     }
   }
 
-  public void classBody() throws CoreException {
+  private void classBody() throws CoreException {
     //'{' [class-element-list] '}'
     if (token == TT_LISTOPEN) {
       getNextToken();
@@ -1356,13 +1491,13 @@ public class PHPParser extends PHPKeywords {
     }
   }
 
-  public void classElementList() throws CoreException {
+  private void classElementList() throws CoreException {
     do {
       classElement();
     } while (token == TT_function || token == TT_var);
   }
 
-  public void classElement() throws CoreException {
+  private void classElement() throws CoreException {
     //class-property
     //function-definition
     if (token == TT_function) {
@@ -1376,36 +1511,41 @@ public class PHPParser extends PHPKeywords {
     }
   }
 
-  public void classProperty() {
+  private void classProperty() throws CoreException {
     //'var' variable ';'
     //'var' variable '=' constant ';'
-    if (token == TT_VARIABLE) {
-      getNextToken();
-      if (token == TT_ASSIGN) {
+    do {
+      if (token == TT_VARIABLE) {
         getNextToken();
-        constant();
-        if (token == TT_SEMICOLON) {
+        if (token == TT_ASSIGN) {
           getNextToken();
-        } else {
-          throwSyntaxError("';' expected after variable declaration.");
+          constant();
         }
-      } else if (token == TT_SEMICOLON) {
-        getNextToken();
       } else {
-        throwSyntaxError("';' or '=' expected after variable declaration.");
+        throwSyntaxError("Variable expected after keyword 'var'.");
+      }
+      if (token != TT_COMMA) {
+        break;
       }
+      getNextToken();
+    } while (true);
+    if (token == TT_SEMICOLON) {
+      getNextToken();
     } else {
-      throwSyntaxError("Variable expected after keyword 'var'.");
+      throwSyntaxError("';' expected after variable declaration.");
     }
   }
 
-  public void functionDefinition() throws CoreException {
+  private void functionDefinition() throws CoreException {
     functionDeclarator();
     compoundStatement();
   }
 
-  public void functionDeclarator() {
+  private void functionDeclarator() throws CoreException {
     //identifier '(' [parameter-list] ')'
+    if (token == TT_AMPERSAND) {
+      getNextToken();
+    }
     if (token == TT_IDENTIFIER) {
       getNextToken();
       if (token == TT_ARGOPEN) {
@@ -1424,7 +1564,7 @@ public class PHPParser extends PHPKeywords {
     }
   }
   //
-  public void parameterList() {
+  private void parameterList() throws CoreException {
     //parameter-declaration
     //parameter-list ',' parameter-declaration
     do {
@@ -1436,9 +1576,17 @@ public class PHPParser extends PHPKeywords {
     } while (true);
   }
 
-  public void parameterDeclaration() {
+  private void parameterDeclaration() throws CoreException {
     //variable
     //variable-reference
+    if (token == TT_AMPERSAND) {
+      getNextToken();
+      if (token == TT_VARIABLE) {
+        getNextToken();
+      } else {
+        throwSyntaxError("Variable expected after reference operator '&'.");
+      }
+    }
     //variable '=' constant
     if (token == TT_VARIABLE) {
       getNextToken();
@@ -1450,7 +1598,7 @@ public class PHPParser extends PHPKeywords {
     }
   }
 
-  public void labeledStatementList() throws CoreException {
+  private void labeledStatementList() throws CoreException {
     if (token != TT_case && token != TT_default) {
       throwSyntaxError("'case' or 'default' expected.");
     }
@@ -1460,10 +1608,16 @@ public class PHPParser extends PHPKeywords {
         constant();
         if (token == TT_DDOT) {
           getNextToken();
+          if (token == TT_case || token == TT_default) { // empty case statement ?
+            continue;
+          }
           statementList();
         } else if (token == TT_SEMICOLON) {
           setMarker("':' expected after 'case' keyword found ';'.", rowCount, PHPParser.INFO);
           getNextToken();
+          if (token == TT_case) { // empty case statement ?
+            continue;
+          }
           statementList();
         } else {
           throwSyntaxError("':' character after 'case' constant expected.");
@@ -1503,11 +1657,11 @@ public class PHPParser extends PHPKeywords {
   //    }
   //  }
 
-  public void expressionStatement() {
-  }
+  //  public void expressionStatement() {
+  //  }
 
-  public void inclusionStatement() {
-  }
+  //  private void inclusionStatement() {
+  //  }
 
   //  public void compoundStatement() {
   //  }
@@ -1533,7 +1687,7 @@ public class PHPParser extends PHPKeywords {
   //  public void definitionStatement() {
   //  }
 
-  public void ifStatement() throws CoreException {
+  private void ifStatement() throws CoreException {
     // ':' statement-list [elseif-list] [else-colon-statement] 'endif' ';'
     if (token == TT_DDOT) {
       getNextToken();
@@ -1590,7 +1744,8 @@ public class PHPParser extends PHPKeywords {
       }
     }
   }
-  public void elseifStatementList() throws CoreException {
+
+  private void elseifStatementList() throws CoreException {
     do {
       elseifStatement();
       switch (token) {
@@ -1617,7 +1772,7 @@ public class PHPParser extends PHPKeywords {
     } while (true);
   }
 
-  public void elseifStatement() throws CoreException {
+  private void elseifStatement() throws CoreException {
     if (token == TT_ARGOPEN) {
       getNextToken();
       expression();
@@ -1633,7 +1788,7 @@ public class PHPParser extends PHPKeywords {
     }
   }
 
-  public void switchStatement() throws CoreException {
+  private void switchStatement() throws CoreException {
     if (token == TT_DDOT) {
       // ':' [labeled-statement-list] 'endswitch' ';'
       getNextToken();
@@ -1663,7 +1818,7 @@ public class PHPParser extends PHPKeywords {
     }
   }
 
-  public void forStatement() throws CoreException {
+  private void forStatement() throws CoreException {
     if (token == TT_DDOT) {
       getNextToken();
       statementList();
@@ -1680,7 +1835,7 @@ public class PHPParser extends PHPKeywords {
     }
   }
 
-  public void whileStatement() throws CoreException {
+  private void whileStatement() throws CoreException {
     // ':' statement-list 'endwhile' ';'
     if (token == TT_DDOT) {
       getNextToken();
@@ -1698,7 +1853,7 @@ public class PHPParser extends PHPKeywords {
     }
   }
 
-  public void foreachStatement() throws CoreException {
+  private void foreachStatement() throws CoreException {
     if (token == TT_DDOT) {
       getNextToken();
       statementList();
@@ -1715,7 +1870,7 @@ public class PHPParser extends PHPKeywords {
     }
   }
 
-  public void exitStatus() {
+  private void exitStatus() throws CoreException {
     if (token == TT_ARGOPEN) {
       getNextToken();
     } else {
@@ -1731,7 +1886,7 @@ public class PHPParser extends PHPKeywords {
     }
   }
 
-  public void expressionList() {
+  private void expressionList() throws CoreException {
     do {
       expression();
       if (token == TT_COMMA) {
@@ -1742,7 +1897,7 @@ public class PHPParser extends PHPKeywords {
     } while (true);
   }
 
-  public void expression() {
+  private void expression() throws CoreException {
     //    if (token == TT_STRING_CONSTANT || token == TT_INTERPOLATED_STRING) {
     //      getNextToken();
     //    } else {
@@ -1753,7 +1908,7 @@ public class PHPParser extends PHPKeywords {
     //    }
   }
 
-  public void postfixExpression() {
+  private void postfixExpression() throws CoreException {
     String ident;
     boolean castFlag = false;
     switch (token) {
@@ -1812,6 +1967,14 @@ public class PHPParser extends PHPKeywords {
       case TT_INT_NUMBER :
         getNextToken();
         break;
+      case TT_DOLLAROPEN :
+        getNextToken();
+        expression();
+        if (token != TT_LISTCLOSE) {
+          throwSyntaxError("'}' expected after indirect variable token '${'.");
+        }
+        getNextToken();
+        break;
       case TT_VARIABLE :
         ident = identifier;
         getNextToken();
@@ -1822,6 +1985,15 @@ public class PHPParser extends PHPKeywords {
             throwSyntaxError("'}' expected after variable '" + ident + "' in variable-expression.");
           }
           getNextToken();
+        } else if (token == TT_ARGOPEN) {
+          getNextToken();
+          if (token != TT_ARGCLOSE) {
+            expressionList();
+            if (token != TT_ARGCLOSE) {
+              throwSyntaxError("')' expected after variable '" + ident + "' in postfix-expression.");
+            }
+          }
+          getNextToken();
         }
         break;
       case TT_IDENTIFIER :
@@ -1838,6 +2010,17 @@ public class PHPParser extends PHPKeywords {
           getNextToken();
         }
         break;
+      case TT_print :
+        getNextToken();
+        expression();
+        //        if (token == TT_SEMICOLON) {
+        //          getNextToken();
+        //        } else {
+        //          if (!phpEnd) {
+        //            throwSyntaxError("';' expected after 'print' statement.");
+        //          }
+        //        }
+        break;
       case TT_list :
         getNextToken();
         if (token == TT_ARGOPEN) {
@@ -1917,43 +2100,59 @@ public class PHPParser extends PHPKeywords {
           }
           getNextToken();
           break;
+        case TT_DDOT2 : // ::
         case TT_REF : // ->
           getNextToken();
-          switch (token) {
-            case TT_VARIABLE :
-              ident = identifier;
+          if (token > TT_KEYWORD) {
+            ident = identifier;
+            setMarker("Avoid using keyword '" + ident + "' as variable name.", rowCount, PHPParser.INFO);
+            getNextToken();
+            if (token == TT_ARGOPEN) {
               getNextToken();
-              //              if (token == TT_ARGOPEN) {
-              //                getNextToken();
-              //                expressionList();
-              //                if (token != TT_ARGCLOSE) {
-              //                  throwSyntaxError(") expected after variable '" + ident + "'.");
-              //                }
-              //                getNextToken();
-              //              }
-              break;
-            case TT_IDENTIFIER :
-              ident = identifier;
+              expressionList();
+              if (token != TT_ARGCLOSE) {
+                throwSyntaxError(") expected after identifier '" + ident + "'.");
+              }
               getNextToken();
-              if (token == TT_ARGOPEN) {
+            }
+            break;
+          } else {
+            switch (token) {
+              case TT_VARIABLE :
+                ident = identifier;
                 getNextToken();
-                expressionList();
-                if (token != TT_ARGCLOSE) {
-                  throwSyntaxError(") expected after identifier '" + ident + "'.");
+                //              if (token == TT_ARGOPEN) {
+                //                getNextToken();
+                //                expressionList();
+                //                if (token != TT_ARGCLOSE) {
+                //                  throwSyntaxError(") expected after variable '" + ident + "'.");
+                //                }
+                //                getNextToken();
+                //              }
+                break;
+              case TT_IDENTIFIER :
+                ident = identifier;
+                getNextToken();
+                if (token == TT_ARGOPEN) {
+                  getNextToken();
+                  expressionList();
+                  if (token != TT_ARGCLOSE) {
+                    throwSyntaxError(") expected after identifier '" + ident + "'.");
+                  }
+                  getNextToken();
                 }
+                break;
+              case TT_LISTOPEN :
                 getNextToken();
-              }
-              break;
-            case TT_LISTOPEN :
-              getNextToken();
-              expression();
-              if (token != TT_LISTCLOSE) {
-                throwSyntaxError("} expected in postfix-expression.");
-              }
-              getNextToken();
-              break;
-            default :
-              throwSyntaxError("Syntax error after '->' token.");
+                expression();
+                if (token != TT_LISTCLOSE) {
+                  throwSyntaxError("} expected in postfix-expression.");
+                }
+                getNextToken();
+                break;
+              default :
+                throwSyntaxError("Syntax error after '->' token.");
+            }
           }
           break;
         case TT_INCREMENT :
@@ -1965,10 +2164,11 @@ public class PHPParser extends PHPKeywords {
         default :
           while_flag = false;
       }
+
     } while (while_flag);
   }
 
-  public void unaryExpression() {
+  private void unaryExpression() throws CoreException {
     switch (token) {
       case TT_INCREMENT :
         getNextToken();
@@ -1978,7 +2178,11 @@ public class PHPParser extends PHPKeywords {
         getNextToken();
         unaryExpression();
         break;
-        //'&' '*' '+' '-' '~' '!' 
+        // '@' '&' '*' '+' '-' '~' '!' 
+      case TT_AT :
+        getNextToken();
+        castExpression();
+        break;
       case TT_AMPERSAND :
         getNextToken();
         castExpression();
@@ -2008,7 +2212,7 @@ public class PHPParser extends PHPKeywords {
     }
   }
 
-  public void castExpression() {
+  private void castExpression() throws CoreException {
     //    if (token == TT_ARGOPEN) {
     //      getNextToken();
     //      typeName();
@@ -2020,7 +2224,7 @@ public class PHPParser extends PHPKeywords {
     unaryExpression();
   }
 
-  public void typeName() {
+  private void typeName() throws CoreException {
     //'string' 'unset' 'array' 'object'
     //'bool' 'boolean'
     //'real' 'double' 'float'
@@ -2039,7 +2243,7 @@ public class PHPParser extends PHPKeywords {
     throwSyntaxError("Expected type cast '( <type-name> )'; Got '" + ident + "'.");
   }
 
-  public void assignExpression() {
+  private void assignExpression() throws CoreException {
     castExpression();
     if (token == TT_ASSIGN) { // =
       getNextToken();
@@ -2083,7 +2287,7 @@ public class PHPParser extends PHPKeywords {
     }
   }
 
-  public void multiplicativeExpression() {
+  private void multiplicativeExpression() throws CoreException {
     do {
       assignExpression();
       if (token != TT_MULTIPLY && token != TT_DIV && token != TT_MOD) {
@@ -2093,7 +2297,7 @@ public class PHPParser extends PHPKeywords {
     } while (true);
   }
 
-  public void concatenationExpression() {
+  private void concatenationExpression() throws CoreException {
     do {
       multiplicativeExpression();
       if (token != TT_DOT) {
@@ -2103,7 +2307,7 @@ public class PHPParser extends PHPKeywords {
     } while (true);
   }
 
-  public void additiveExpression() {
+  private void additiveExpression() throws CoreException {
     do {
       concatenationExpression();
       if (token != TT_ADD && token != TT_SUBTRACT) {
@@ -2113,7 +2317,7 @@ public class PHPParser extends PHPKeywords {
     } while (true);
   }
 
-  public void shiftExpression() {
+  private void shiftExpression() throws CoreException {
     do {
       additiveExpression();
       if (token != TT_LSHIFT && token != TT_RSHIFT) {
@@ -2123,7 +2327,7 @@ public class PHPParser extends PHPKeywords {
     } while (true);
   }
 
-  public void relationalExpression() {
+  private void relationalExpression() throws CoreException {
     do {
       shiftExpression();
       if (token != TT_LESS && token != TT_GREATER && token != TT_LESSEQUAL && token != TT_GREATEREQUAL) {
@@ -2133,7 +2337,7 @@ public class PHPParser extends PHPKeywords {
     } while (true);
   }
 
-  public void identicalExpression() {
+  private void identicalExpression() throws CoreException {
     do {
       relationalExpression();
       if (token != TT_EX_EQUAL && token != TT_EX_UNEQUAL) {
@@ -2143,7 +2347,7 @@ public class PHPParser extends PHPKeywords {
     } while (true);
   }
 
-  public void equalityExpression() {
+  private void equalityExpression() throws CoreException {
     do {
       identicalExpression();
       if (token != TT_EQUAL && token != TT_UNEQUAL) {
@@ -2153,7 +2357,7 @@ public class PHPParser extends PHPKeywords {
     } while (true);
   }
 
-  public void ternaryExpression() {
+  private void ternaryExpression() throws CoreException {
     equalityExpression();
     if (token == TT_QUESTIONMARK) {
       getNextToken();
@@ -2167,7 +2371,7 @@ public class PHPParser extends PHPKeywords {
     }
   }
 
-  public void andExpression() {
+  private void andExpression() throws CoreException {
     do {
       ternaryExpression();
       if (token != TT_AMPERSAND) {
@@ -2177,7 +2381,7 @@ public class PHPParser extends PHPKeywords {
     } while (true);
   }
 
-  public void exclusiveorExpression() {
+  private void exclusiveorExpression() throws CoreException {
     do {
       andExpression();
       if (token != TT_POW) {
@@ -2187,7 +2391,7 @@ public class PHPParser extends PHPKeywords {
     } while (true);
   }
 
-  public void inclusiveorExpression() {
+  private void inclusiveorExpression() throws CoreException {
     do {
       exclusiveorExpression();
       if (token != TT_LINE) {
@@ -2197,7 +2401,7 @@ public class PHPParser extends PHPKeywords {
     } while (true);
   }
 
-  public void booleanandExpression() {
+  private void booleanandExpression() throws CoreException {
     do {
       inclusiveorExpression();
       if (token != TT_AND) {
@@ -2207,7 +2411,7 @@ public class PHPParser extends PHPKeywords {
     } while (true);
   }
 
-  public void booleanorExpression() {
+  private void booleanorExpression() throws CoreException {
     do {
       booleanandExpression();
       if (token != TT_OR) {
@@ -2217,7 +2421,7 @@ public class PHPParser extends PHPKeywords {
     } while (true);
   }
 
-  public void logicalandExpression() {
+  private void logicalandExpression() throws CoreException {
     do {
       booleanorExpression();
       if (token != TT_and) {
@@ -2227,7 +2431,7 @@ public class PHPParser extends PHPKeywords {
     } while (true);
   }
 
-  public void logicalexclusiveorExpression() {
+  private void logicalexclusiveorExpression() throws CoreException {
     do {
       logicalandExpression();
       if (token != TT_xor) {
@@ -2237,7 +2441,7 @@ public class PHPParser extends PHPKeywords {
     } while (true);
   }
 
-  public void logicalinclusiveorExpression() {
+  private void logicalinclusiveorExpression() throws CoreException {
     do {
       logicalexclusiveorExpression();
       if (token != TT_or) {
@@ -2259,7 +2463,7 @@ public class PHPParser extends PHPKeywords {
   //    }
   //  }
 
-  public void variableList() {
+  private void variableList() throws CoreException {
     do {
       variable();
       if (token == TT_COMMA) {
@@ -2270,15 +2474,37 @@ public class PHPParser extends PHPKeywords {
     } while (true);
   }
 
-  public void variable() {
-    if (token == TT_VARIABLE) {
+  private void variable() throws CoreException {
+    if (token == TT_DOLLAROPEN) {
+      getNextToken();
+      expression();
+      ;
+      if (token != TT_LISTCLOSE) {
+        throwSyntaxError("'}' expected after indirect variable token '${'.");
+      }
       getNextToken();
     } else {
-      throwSyntaxError("$-variable expected in variable-list.");
+      if (token == TT_VARIABLE) {
+        getNextToken();
+        if (token == TT_PARTOPEN) {
+          getNextToken();
+          expression();
+          if (token != TT_PARTCLOSE) {
+            throwSyntaxError("']' expected in variable-list.");
+          }
+          getNextToken();
+        } else if (token == TT_ASSIGN) {
+          getNextToken();
+          constant();
+        }
+      } else {
+        throwSyntaxError("$-variable expected in variable-list.");
+      }
     }
   }
 
-  public void constant() {
+  private void constant() throws CoreException {
+    String ident;
     switch (token) {
       case TT_ADD :
         getNextToken();
@@ -2315,6 +2541,20 @@ public class PHPParser extends PHPKeywords {
       case TT_true :
         getNextToken();
         break;
+      case TT_IDENTIFIER :
+        ident = identifier;
+        getNextToken();
+        if (token == TT_ARGOPEN) {
+          getNextToken();
+          if (token != TT_ARGCLOSE) {
+            expressionList();
+            if (token != TT_ARGCLOSE) {
+              throwSyntaxError("')' expected after identifier '" + ident + "' in postfix-expression.");
+            }
+          }
+          getNextToken();
+        }
+        break;
       case TT_STRING_CONSTANT :
         getNextToken();
         break;
@@ -2332,4 +2572,90 @@ public class PHPParser extends PHPKeywords {
     }
   }
 
+  /**
+   * Call the php parse command ( php -l -f &lt;filename&gt; )
+   * and create markers according to the external parser output
+   */
+  public static void phpExternalParse(IFile file) {
+    //IFile file = (IFile) resource;
+    IPath path = file.getFullPath();
+    IPreferenceStore store = PHPeclipsePlugin.getDefault().getPreferenceStore();
+    String filename = file.getLocation().toString();
+
+    String[] arguments = { filename };
+    MessageFormat form = new MessageFormat(store.getString(PHPeclipsePlugin.EXTERNAL_PARSER_PREF));
+    String command = form.format(arguments);
+
+    String parserResult = PHPStartApacheAction.execute(command, "External parser: ");
+
+    try {
+      // parse the buffer to find the errors and warnings
+      createMarkers(parserResult, file);
+    } catch (CoreException e) {
+    }
+  }
+
+  /**
+   * Create markers according to the external parser output
+   */
+  private static void createMarkers(String output, IFile file) throws CoreException {
+    // delete all markers
+    file.deleteMarkers(IMarker.PROBLEM, false, 0);
+
+    int indx = 0;
+    int brIndx = 0;
+    boolean flag = true;
+    while ((brIndx = output.indexOf("<br />", indx)) != -1) {
+      // newer php error output (tested with 4.2.3)
+      scanLine(output, file, indx, brIndx);
+      indx = brIndx + 6;
+      flag = false;
+    }
+    if (flag) {
+      while ((brIndx = output.indexOf("<br>", indx)) != -1) {
+        // older php error output (tested with 4.2.3)
+        scanLine(output, file, indx, brIndx);
+        indx = brIndx + 4;
+      }
+    }
+  }
+
+  private static void scanLine(String output, IFile file, int indx, int brIndx) throws CoreException {
+    String current;
+    String outLineNumberString;
+    StringBuffer lineNumberBuffer = new StringBuffer(10);
+    char ch;
+    current = output.substring(indx, brIndx);
+
+    if (current.indexOf(PARSE_WARNING_STRING) != -1 || current.indexOf(PARSE_ERROR_STRING) != -1) {
+      int onLine = current.indexOf("on line <b>");
+      if (onLine != -1) {
+        lineNumberBuffer.delete(0, lineNumberBuffer.length());
+        for (int i = onLine; i < current.length(); i++) {
+          ch = current.charAt(i);
+          if ('0' <= ch && '9' >= ch) {
+            lineNumberBuffer.append(ch);
+          }
+        }
+
+        int lineNumber = Integer.parseInt(lineNumberBuffer.toString());
+
+        Hashtable attributes = new Hashtable();
+
+        current = current.replaceAll("\n", "");
+        current = current.replaceAll("<b>", "");
+        current = current.replaceAll("</b>", "");
+        MarkerUtilities.setMessage(attributes, current);
+
+        if (current.indexOf(PARSE_ERROR_STRING) != -1)
+          attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_ERROR));
+        else if (current.indexOf(PARSE_WARNING_STRING) != -1)
+          attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_WARNING));
+        else
+          attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_INFO));
+        MarkerUtilities.setLineNumber(attributes, lineNumber);
+        MarkerUtilities.createMarker(file, attributes, IMarker.PROBLEM);
+      }
+    }
+  }
 }
\ No newline at end of file