new phpedit.gif
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / phpeditor / PHPParser.java
index 91620bc..a1282df 100644 (file)
@@ -10,17 +10,25 @@ Contributors:
 **********************************************************************/
 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;
 
 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,6 +98,7 @@ public class PHPParser extends PHPKeywords {
   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;
@@ -194,6 +203,11 @@ 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.
    *
@@ -306,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 == '\\') {
@@ -321,13 +336,14 @@ 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 == '\\') {
@@ -343,7 +359,7 @@ public class PHPParser extends PHPKeywords {
             }
           }
           if (openString) {
-            throwSyntaxError("Open string character \"`\" at end of file.");
+            throwSyntaxError("Open string character \"`\" at end of file.", startRow);
           }
           setMarker("Other string delimiters prefered (found \"`\").", rowCount, PHPParser.INFO);
           token = TT_STRING_CONSTANT;
@@ -596,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;
               }
@@ -988,53 +1026,68 @@ 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);
   }
 
   private void statementList() throws CoreException {
@@ -1274,17 +1327,17 @@ public class PHPParser extends PHPKeywords {
         }
       }
       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_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();
@@ -1957,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) {
@@ -2508,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