* Added browser like links (Ctrl+Mouseclick on identifier; same as F3 shortcut)
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / parser / Parser.java
index 91197e9..cc9ce7a 100644 (file)
@@ -1,9 +1,9 @@
 /***********************************************************************************************************************************
- * Copyright (c) 2002 Klaus Hartlage - www.eclipseproject.de All rights reserved. This program and the accompanying material are
- * made available under the terms of the Common Public License v1.0 which accompanies this distribution, and is available at
+ * Copyright (c) 2002 www.phpeclipse.de All rights reserved. This program and the accompanying material 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
+ * Contributors: www.phpeclipse.de
  **********************************************************************************************************************************/
 package net.sourceforge.phpdt.internal.compiler.parser;
 
@@ -19,20 +19,32 @@ import net.sourceforge.phpdt.internal.compiler.lookup.TypeConstants;
 import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
 import net.sourceforge.phpdt.internal.compiler.problem.ProblemSeverities;
 import net.sourceforge.phpdt.internal.compiler.util.Util;
+import net.sourceforge.phpdt.internal.ui.util.PHPFileUtil;
 import net.sourceforge.phpeclipse.builder.IdentifierIndexManager;
+import net.sourceforge.phpeclipse.internal.compiler.ast.AND_AND_Expression;
 import net.sourceforge.phpeclipse.internal.compiler.ast.ASTNode;
 import net.sourceforge.phpeclipse.internal.compiler.ast.AbstractMethodDeclaration;
+import net.sourceforge.phpeclipse.internal.compiler.ast.BinaryExpression;
 import net.sourceforge.phpeclipse.internal.compiler.ast.CompilationUnitDeclaration;
+import net.sourceforge.phpeclipse.internal.compiler.ast.ConditionalExpression;
+import net.sourceforge.phpeclipse.internal.compiler.ast.EqualExpression;
 import net.sourceforge.phpeclipse.internal.compiler.ast.Expression;
 import net.sourceforge.phpeclipse.internal.compiler.ast.FieldDeclaration;
 import net.sourceforge.phpeclipse.internal.compiler.ast.IfStatement;
 import net.sourceforge.phpeclipse.internal.compiler.ast.ImportReference;
 import net.sourceforge.phpeclipse.internal.compiler.ast.MethodDeclaration;
+import net.sourceforge.phpeclipse.internal.compiler.ast.OR_OR_Expression;
 import net.sourceforge.phpeclipse.internal.compiler.ast.SingleTypeReference;
 import net.sourceforge.phpeclipse.internal.compiler.ast.Statement;
+import net.sourceforge.phpeclipse.internal.compiler.ast.StringLiteral;
+import net.sourceforge.phpeclipse.internal.compiler.ast.StringLiteralDQ;
+import net.sourceforge.phpeclipse.internal.compiler.ast.StringLiteralSQ;
 import net.sourceforge.phpeclipse.internal.compiler.ast.TypeDeclaration;
 
 import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IPath;
 
 public class Parser //extends PHPParserSuperclass
     implements ITerminalSymbols, CompilerModifiers, ParserBasicInformation {
@@ -155,7 +167,9 @@ public class Parser //extends PHPParserSuperclass
   public void initializeScanner() {
     this.scanner = new Scanner(false /* comment */, false /* whitespace */, this.options
         .getSeverity(CompilerOptions.NonExternalizedString) != ProblemSeverities.Ignore /* nls */, false, false,
-        this.options.taskTags/* taskTags */, this.options.taskPriorites/* taskPriorities */);
+        this.options.taskTags/* taskTags */, 
+        this.options.taskPriorites/* taskPriorities */,
+        true/*isTaskCaseSensitive*/);
   }
 
   /**
@@ -196,6 +210,12 @@ public class Parser //extends PHPParserSuperclass
     throw new SyntaxError(1, 0, " ", error);
   }
 
+  private void reportSyntaxError(String error) {
+    int problemStartPosition = scanner.getCurrentTokenStartPosition();
+    int problemEndPosition = scanner.getCurrentTokenEndPosition();
+    reportSyntaxError(error, problemStartPosition, problemEndPosition + 1);
+  }
+  
   private void reportSyntaxError(String error, int problemStartPosition, int problemEndPosition) {
     problemReporter.phpParsingError(new String[] { error }, problemStartPosition, problemEndPosition, referenceContext,
         compilationUnit.compilationResult);
@@ -207,23 +227,6 @@ public class Parser //extends PHPParserSuperclass
   }
 
   /**
-   * Method Declaration.
-   * 
-   * @see
-   */
-  //  private void getChar() {
-  //    if (str.length() > chIndx) {
-  //      ch = str.charAt(chIndx++);
-  //
-  //      return;
-  //    }
-  //
-  //    chIndx = str.length() + 1;
-  //    ch = ' ';
-  //    // token = TokenNameEOF;
-  //    phpEnd = true;
-  //  }
-  /**
    * gets the next token from input
    */
   private void getNextToken() {
@@ -588,7 +591,7 @@ public class Parser //extends PHPParserSuperclass
       if ((token == TokenNameRBRACE) || (token == TokenNamecase) || (token == TokenNamedefault) || (token == TokenNameelse)
           || (token == TokenNameelseif) || (token == TokenNameendif) || (token == TokenNameendfor)
           || (token == TokenNameendforeach) || (token == TokenNameendwhile) || (token == TokenNameendswitch)
-          || (token == TokenNameEOF) || (token == TokenNameERROR)) {
+          || (token == TokenNameenddeclare) || (token == TokenNameEOF) || (token == TokenNameERROR)) {
         return;
       }
     } while (true);
@@ -861,6 +864,20 @@ public class Parser //extends PHPParserSuperclass
       getNextToken();
       functionDefinition(methodDecl);
       return statement;
+    } else if (token == TokenNamedeclare) {
+      //T_DECLARE '(' declare_list ')' declare_statement
+      getNextToken();
+      if (token != TokenNameLPAREN) {
+        throwSyntaxError("'(' expected in 'declare' statement.");
+      }
+      getNextToken();
+      declare_list();
+      if (token != TokenNameRPAREN) {
+        throwSyntaxError("')' expected in 'declare' statement.");
+      }
+      getNextToken();
+      declare_statement();
+      return statement;
     } else if (token == TokenNametry) {
       getNextToken();
       if (token != TokenNameLBRACE) {
@@ -958,6 +975,47 @@ public class Parser //extends PHPParserSuperclass
     return statement;
   }
 
+  private void declare_statement() {
+    // statement
+    //| ':' inner_statement_list T_ENDDECLARE ';'
+    //;
+    if (token == TokenNameCOLON) {
+      getNextToken();
+      // TODO: implement inner_statement_list();
+      statementList();
+      if (token != TokenNameenddeclare) {
+        throwSyntaxError("'enddeclare' expected in 'declare' statement.");
+      }
+      getNextToken();
+      if (token != TokenNameSEMICOLON) {
+        throwSyntaxError("';' expected after 'enddeclare' keyword.");
+      }
+      getNextToken();
+    } else {
+      statement(TokenNameRPAREN);
+    }
+  }
+
+  private void declare_list() {
+    // T_STRING '=' static_scalar
+    //| declare_list ',' T_STRING '=' static_scalar
+    while (true) {
+      if (token != TokenNameIdentifier) {
+        throwSyntaxError("Identifier expected in 'declare' list.");
+      }
+      getNextToken();
+      if (token != TokenNameEQUAL) {
+        throwSyntaxError("'=' expected in 'declare' list.");
+      }
+      getNextToken();
+      static_scalar();
+      if (token != TokenNameCOMMA) {
+        break;
+      }
+      getNextToken();
+    }
+  }
+
   private void additional_catches() {
     while (token == TokenNamecatch) {
       getNextToken();
@@ -978,7 +1036,9 @@ public class Parser //extends PHPParserSuperclass
         throwSyntaxError("'{' expected in 'catch' statement.");
       }
       getNextToken();
-      statementList();
+      if (token != TokenNameRBRACE) {
+        statementList();
+      }
       if (token != TokenNameRBRACE) {
         throwSyntaxError("'}' expected in 'catch' statement.");
       }
@@ -1630,7 +1690,7 @@ public class Parser //extends PHPParserSuperclass
         }
       } else { // TokenNamedefault
         getNextToken();
-        if (token == TokenNameCOLON) {
+        if (token == TokenNameCOLON || token == TokenNameSEMICOLON) {
           getNextToken();
           if (token == TokenNameRBRACE) {
             // empty default case
@@ -1921,10 +1981,12 @@ public class Parser //extends PHPParserSuperclass
   }
 
   private Expression expr_without_variable(boolean only_variable) {
+    int exprSourceStart = scanner.getCurrentTokenStartPosition();
+    int exprSourceEnd = scanner.getCurrentTokenEndPosition();
     Expression expression = new Expression();
-    expression.sourceStart = scanner.getCurrentTokenStartPosition();
+    expression.sourceStart = exprSourceStart;
     // default, may be overwritten
-    expression.sourceEnd = scanner.getCurrentTokenEndPosition();
+    expression.sourceEnd = exprSourceEnd;
     //         internal_functions_in_yacc
     // | T_CLONE expr
     // | T_PRINT expr
@@ -2078,11 +2140,17 @@ public class Parser //extends PHPParserSuperclass
       scanner.encapsedStringStack.push(new Character('\''));
       getNextToken();
       try {
+        exprSourceStart = scanner.getCurrentTokenStartPosition();
         if (token == TokenNameEncapsedString1) {
+          expression = new StringLiteralSQ(scanner.getCurrentStringLiteralSource(exprSourceStart), exprSourceStart, scanner
+              .getCurrentTokenEndPosition());
         } else {
           encaps_list();
           if (token != TokenNameEncapsedString1) {
             throwSyntaxError("\'\'\' expected at end of string" + "(Found token: " + scanner.toStringAction(token) + " )");
+          } else {
+            expression = new StringLiteralSQ(scanner.getCurrentStringLiteralSource(exprSourceStart), exprSourceStart, scanner
+                .getCurrentTokenEndPosition());
           }
         }
       } finally {
@@ -2095,11 +2163,17 @@ public class Parser //extends PHPParserSuperclass
       scanner.encapsedStringStack.push(new Character('"'));
       getNextToken();
       try {
+        exprSourceStart = scanner.getCurrentTokenStartPosition();
         if (token == TokenNameEncapsedString2) {
+          expression = new StringLiteralDQ(scanner.getCurrentStringLiteralSource(exprSourceStart), exprSourceStart, scanner
+              .getCurrentTokenEndPosition());
         } else {
           encaps_list();
           if (token != TokenNameEncapsedString2) {
             throwSyntaxError("'\"' expected at end of string" + "(Found token: " + scanner.toStringAction(token) + " )");
+          } else {
+            expression = new StringLiteralDQ(scanner.getCurrentStringLiteralSource(exprSourceStart), exprSourceStart, scanner
+                .getCurrentTokenEndPosition());
           }
         }
       } finally {
@@ -2107,10 +2181,18 @@ public class Parser //extends PHPParserSuperclass
         getNextToken();
       }
       break;
-    case TokenNameIntegerLiteral:
-    case TokenNameDoubleLiteral:
     case TokenNameStringDoubleQuote:
+      expression = new StringLiteralDQ(scanner.getCurrentStringLiteralSource(), scanner.getCurrentTokenStartPosition(), scanner
+          .getCurrentTokenEndPosition());
+      common_scalar();
+      break;
     case TokenNameStringSingleQuote:
+      expression = new StringLiteralSQ(scanner.getCurrentStringLiteralSource(), scanner.getCurrentTokenStartPosition(), scanner
+          .getCurrentTokenEndPosition());
+      common_scalar();
+      break;
+    case TokenNameIntegerLiteral:
+    case TokenNameDoubleLiteral:
     case TokenNameStringInterpolated:
     case TokenNameFILE:
     case TokenNameLINE:
@@ -2153,8 +2235,8 @@ public class Parser //extends PHPParserSuperclass
         if (token != TokenNameEQUAL) {
           throwSyntaxError("'=' expected after 'list' keyword.");
         }
-          getNextToken();
-          expr();
+        getNextToken();
+        expr();
       } else {
         throwSyntaxError("'(' expected after 'list' keyword.");
       }
@@ -2221,8 +2303,8 @@ public class Parser //extends PHPParserSuperclass
       case TokenNameXOR_EQUAL:
       case TokenNameRIGHT_SHIFT_EQUAL:
       case TokenNameLEFT_SHIFT_EQUAL:
-          getNextToken();
-          expr();
+        getNextToken();
+        expr();
         break;
       case TokenNamePLUS_PLUS:
       case TokenNameMINUS_MINUS:
@@ -2275,7 +2357,17 @@ public class Parser //extends PHPParserSuperclass
     while (true) {
       switch (token) {
       case TokenNameOR_OR:
+        getNextToken();
+        expression = new OR_OR_Expression(expression, expr(), token);
+        break;
       case TokenNameAND_AND:
+        getNextToken();
+        expression = new AND_AND_Expression(expression, expr(), token);
+        break;
+      case TokenNameEQUAL_EQUAL:
+        getNextToken();
+        expression = new EqualExpression(expression, expr(), token);
+        break;
       case TokenNameand:
       case TokenNameor:
       case TokenNamexor:
@@ -2292,28 +2384,34 @@ public class Parser //extends PHPParserSuperclass
       case TokenNameRIGHT_SHIFT:
       case TokenNameEQUAL_EQUAL_EQUAL:
       case TokenNameNOT_EQUAL_EQUAL:
-      case TokenNameEQUAL_EQUAL:
       case TokenNameNOT_EQUAL:
       case TokenNameLESS:
       case TokenNameLESS_EQUAL:
       case TokenNameGREATER:
       case TokenNameGREATER_EQUAL:
-          getNextToken();
-          expr();
+        getNextToken();
+        expression = new BinaryExpression(expression, expr(), token);
         break;
       //  | expr T_INSTANCEOF class_name_reference
       //       | expr '?' expr ':' expr
       case TokenNameinstanceof:
         getNextToken();
         class_name_reference();
+        // TODO use InstanceofExpression
+        expression = new Expression();
+        expression.sourceStart = exprSourceStart;
+        expression.sourceEnd = scanner.getCurrentTokenEndPosition();
         break;
       case TokenNameQUESTION:
-          getNextToken();
-          expr();
-        if (token == TokenNameCOLON) {
-          getNextToken();
-          expr();
+        getNextToken();
+        Expression valueIfTrue = expr();
+        if (token != TokenNameCOLON) {
+          throwSyntaxError("':' expected in conditional expression.");
         }
+        getNextToken();
+        Expression valueIfFalse = expr();
+
+        expression = new ConditionalExpression(expression, valueIfTrue, valueIfFalse);
         break;
       default:
         return expression;
@@ -3194,7 +3292,7 @@ public class Parser //extends PHPParserSuperclass
   }
 
   private void internal_functions_in_yacc() {
-    int start = 0;
+    //    int start = 0;
     ImportReference impt = null;
     switch (token) {
     case TokenNameisset:
@@ -3225,28 +3323,11 @@ public class Parser //extends PHPParserSuperclass
       break;
     case TokenNameinclude:
       //T_INCLUDE expr
-      start = scanner.getCurrentTokenStartPosition();
-      getNextToken();
-      expr();
-
-      impt = new ImportReference(scanner.getCurrentTokenSource(start), start, scanner.getCurrentTokenEndPosition(), false);
-      impt.declarationSourceEnd = impt.sourceEnd;
-      impt.declarationEnd = impt.declarationSourceEnd;
-      //endPosition is just before the ;
-      impt.declarationSourceStart = start;
-      includesList.add(impt);
+      checkFileName(token, impt);
       break;
     case TokenNameinclude_once:
       //       T_INCLUDE_ONCE expr
-      start = scanner.getCurrentTokenStartPosition();
-      getNextToken();
-      expr();
-      impt = new ImportReference(scanner.getCurrentTokenSource(start), start, scanner.getCurrentTokenEndPosition(), false);
-      impt.declarationSourceEnd = impt.sourceEnd;
-      impt.declarationEnd = impt.declarationSourceEnd;
-      //endPosition is just before the ;
-      impt.declarationSourceStart = start;
-      includesList.add(impt);
+      checkFileName(token, impt);
       break;
     case TokenNameeval:
       //       T_EVAL '(' expr ')'
@@ -3263,31 +3344,84 @@ public class Parser //extends PHPParserSuperclass
       break;
     case TokenNamerequire:
       //T_REQUIRE expr
-      start = scanner.getCurrentTokenStartPosition();
-      getNextToken();
-      expr();
-      impt = new ImportReference(scanner.getCurrentTokenSource(start), start, scanner.getCurrentTokenEndPosition(), false);
-      impt.declarationSourceEnd = impt.sourceEnd;
-      impt.declarationEnd = impt.declarationSourceEnd;
-      //endPosition is just before the ;
-      impt.declarationSourceStart = start;
-      includesList.add(impt);
+      checkFileName(token, impt);
       break;
     case TokenNamerequire_once:
       //       T_REQUIRE_ONCE expr
-      start = scanner.getCurrentTokenStartPosition();
-      getNextToken();
-      expr();
-      impt = new ImportReference(scanner.getCurrentTokenSource(start), start, scanner.getCurrentTokenEndPosition(), false);
-      impt.declarationSourceEnd = impt.sourceEnd;
-      impt.declarationEnd = impt.declarationSourceEnd;
-      //endPosition is just before the ;
-      impt.declarationSourceStart = start;
-      includesList.add(impt);
+      checkFileName(token, impt);
       break;
     }
   }
 
+  private void checkFileName(int includeToken, ImportReference impt) {
+    //<include-token> expr
+    int start = scanner.getCurrentTokenStartPosition();
+    boolean hasLPAREN = false;
+    getNextToken();
+    if (token == TokenNameLPAREN) {
+      hasLPAREN = true;
+      getNextToken();
+    }
+    Expression expression = expr();
+    if (hasLPAREN) {
+      if (token == TokenNameRPAREN) {
+        getNextToken();
+      } else {
+        throwSyntaxError("')' expected for keyword '" + scanner.toStringAction(includeToken) + "'");
+      }
+    }
+    impt = new ImportReference(scanner.getCurrentTokenSource(start), start, scanner.getCurrentTokenEndPosition(), false);
+    impt.declarationSourceEnd = impt.sourceEnd;
+    impt.declarationEnd = impt.declarationSourceEnd;
+    //endPosition is just before the ;
+    impt.declarationSourceStart = start;
+    includesList.add(impt);
+
+    if (expression instanceof StringLiteral) {
+      StringLiteral literal = (StringLiteral) expression;
+      char[] includeName = literal.source();
+      if (includeName.length == 0) {
+        reportSyntaxError("Empty filename after keyword '" + scanner.toStringAction(includeToken) + "'", literal.sourceStart,
+            literal.sourceStart + 1);
+      }
+      String includeNameString = new String(includeName);
+      if (literal instanceof StringLiteralDQ) {
+        if (includeNameString.indexOf('$') >= 0) {
+          // assuming that the filename contains a variable => no filename check
+          return;
+        }
+      }
+      if (includeNameString.startsWith("http://")) {
+        // assuming external include location
+        return;
+      }
+      if (scanner.compilationUnit != null) {
+        IResource resource = scanner.compilationUnit.getResource();
+        //        java.io.File f = new java.io.File(new String(compilationUnit.getFileName()));
+        //        System.out.println(expression.toStringExpression());
+        //      }
+        if (resource != null && resource instanceof IFile) {
+          // check the filename:
+          //      System.out.println(new String(compilationUnit.getFileName())+" - "+ expression.toStringExpression());
+          IProject project = resource.getProject();
+          if (project != null) {
+            IPath path = PHPFileUtil.determineFilePath(includeNameString, resource, project);
+            
+            if (path == null) {
+              //              reportSyntaxError("File: " + expression.toStringExpression() + " doesn't exist in project: "
+              //                  + project.getLocation().toString(), literal.sourceStart, literal.sourceEnd);
+              String[] args = { expression.toStringExpression(), project.getLocation().toString() };
+              problemReporter.phpIncludeNotExistWarning(args, literal.sourceStart, literal.sourceEnd, referenceContext,
+                  compilationUnit.compilationResult);
+            } else {
+              impt.setFile( PHPFileUtil.createFile(path, project) );
+            }
+          }
+        }
+      }
+    }
+  }
+
   private void isset_variables() {
     // variable
     // | isset_variables ','