1 /**********************************************************************
2 Copyright (c) 2002 Klaus Hartlage - www.eclipseproject.de
3 All rights reserved. This program and the accompanying materials
4 are made available under the terms of the Common Public License v1.0
5 which accompanies this distribution, and is available at
6 http://www.eclipse.org/legal/cpl-v10.html
9 Klaus Hartlage - www.eclipseproject.de
10 **********************************************************************/
11 package net.sourceforge.phpeclipse.phpeditor;
13 import java.text.MessageFormat;
14 import java.util.ArrayList;
15 import java.util.HashMap;
16 import java.util.Hashtable;
18 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
19 import net.sourceforge.phpeclipse.actions.PHPStartApacheAction;
20 import net.sourceforge.phpeclipse.phpeditor.php.PHPKeywords;
21 import org.eclipse.core.resources.IFile;
22 import org.eclipse.core.resources.IMarker;
23 import org.eclipse.core.runtime.CoreException;
24 import org.eclipse.core.runtime.IPath;
25 import org.eclipse.jface.preference.IPreferenceStore;
26 import org.eclipse.ui.texteditor.MarkerUtilities;
28 public class PHPParser extends PHPKeywords {
29 // strings for external parser call
30 private static final String PARSE_ERROR_STRING = "Parse error"; //$NON-NLS-1$
31 private static final String PARSE_WARNING_STRING = "Warning"; //$NON-NLS-1$
33 public static final int ERROR = 2;
34 public static final int WARNING = 1;
35 public static final int INFO = 0;
36 private IFile fileToParse;
37 private ArrayList phpList;
39 private int currentPHPString;
40 private boolean phpEnd;
42 private static HashMap keywordMap = null;
50 // row counter for syntax errors:
52 // column counter for syntax errors:
63 final static int TT_EOF = 0;
64 final static int TT_UNDEFINED = 1;
66 final static int TT_MOD = 30;
67 final static int TT_NOT = 31;
68 final static int TT_DOT = 32;
69 final static int TT_POW = 33;
70 final static int TT_DIV = 34;
71 final static int TT_MULTIPLY = 35;
72 final static int TT_SUBTRACT = 36;
73 final static int TT_ADD = 37;
74 final static int TT_EQUAL = 38;
75 final static int TT_UNEQUAL = 39;
76 final static int TT_GREATER = 40;
77 final static int TT_GREATEREQUAL = 41;
78 final static int TT_LESS = 42;
79 final static int TT_LESSEQUAL = 43;
80 final static int TT_AND = 44;
81 final static int TT_OR = 45;
82 final static int TT_HASH = 46;
83 final static int TT_DDOT = 47;
84 final static int TT_DOTASSIGN = 48;
86 final static int TT_ASSIGN = 49;
87 final static int TT_REF = 50;
88 final static int TT_FOREACH = 51;
89 final static int TT_AMPERSAND = 52;
90 final static int TT_DOLLARLISTOPEN = 53;
91 final static int TT_TILDE = 54;
92 final static int TT_TILDEASSIGN = 55;
93 final static int TT_MODASSIGN = 56;
94 final static int TT_POWASSIGN = 57;
95 final static int TT_RSHIFTASSIGN = 58;
96 final static int TT_LSHIFTASSIGN = 59;
97 final static int TT_ANDASSIGN = 60;
98 final static int TT_QUESTIONMARK = 61;
99 final static int TT_DDOT2 = 62;
100 final static int TT_AT = 63;
101 // final static int TT_HEREDOC = 64;
103 final static int TT_DOLLAROPEN = 127;
104 final static int TT_ARGOPEN = 128;
105 final static int TT_ARGCLOSE = 129;
106 final static int TT_LISTOPEN = 130;
107 final static int TT_LISTCLOSE = 131;
108 final static int TT_PARTOPEN = 132;
109 final static int TT_PARTCLOSE = 133;
110 final static int TT_COMMA = 134;
112 final static int TT_STRING = 136;
113 final static int TT_IDENTIFIER = 138;
114 final static int TT_DIGIT = 139;
115 final static int TT_SEMICOLON = 140;
116 final static int TT_SLOT = 141;
117 final static int TT_SLOTSEQUENCE = 142;
118 final static int TT_DECREMENT = 144;
119 final static int TT_INCREMENT = 145;
120 final static int TT_ADDTO = 146;
121 final static int TT_DIVIDEBY = 147;
122 final static int TT_SUBTRACTFROM = 148;
123 final static int TT_TIMESBY = 149;
124 final static int TT_VARIABLE = 150;
125 final static int TT_INT_NUMBER = 151;
126 final static int TT_DOUBLE_NUMBER = 152;
127 final static int TT_INTERPOLATED_STRING = 153;
128 final static int TT_STRING_CONSTANT = 154;
130 final static int TT_LSHIFT = 155;
131 final static int TT_RSHIFT = 156;
132 final static int TT_EX_EQUAL = 157;
133 final static int TT_EX_UNEQUAL = 158;
134 final static int TT_LINE = 159;
135 // final static int TT_AT = 153; // @
140 *@param sess Description of Parameter
143 public PHPParser(IFile fileToParse) {
144 if (keywordMap == null) {
145 keywordMap = new HashMap();
146 for (int i = 0; i < PHP_KEYWORS.length; i++) {
147 keywordMap.put(PHP_KEYWORS[i], new Integer(PHP_KEYWORD_TOKEN[i]));
150 this.currentPHPString = 0;
151 this.fileToParse = fileToParse;
157 this.columnCount = 0;
164 * Create marker for the parse error
166 private void setMarker(String message, int lineNumber, int errorLevel) throws CoreException {
167 setMarker(fileToParse, message, lineNumber, errorLevel);
170 public static void setMarker(IFile file, String message, int lineNumber, int errorLevel) throws CoreException {
172 Hashtable attributes = new Hashtable();
173 MarkerUtilities.setMessage(attributes, message);
174 switch (errorLevel) {
176 attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_ERROR));
179 attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_WARNING));
182 attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_INFO));
185 MarkerUtilities.setLineNumber(attributes, lineNumber);
186 MarkerUtilities.createMarker(file, attributes, IMarker.PROBLEM);
189 private void throwSyntaxError(String error) {
191 if (str.length() < chIndx) {
194 // read until end-of-line
196 while (str.length() > eol) {
197 ch = str.charAt(eol++);
203 throw new SyntaxError(rowCount, chIndx - columnCount + 1, str.substring(columnCount, eol), error);
206 private void throwSyntaxError(String error, int startRow) {
208 throw new SyntaxError(startRow, 0, " ", error);
212 * Method Declaration.
216 private void getChar() {
217 if (str.length() > chIndx) {
218 ch = str.charAt(chIndx++);
223 chIndx = str.length() + 1;
230 * gets the next token from input
232 private void getNextToken() throws CoreException {
235 while (str.length() > chIndx) {
236 ch = str.charAt(chIndx++);
237 token = TT_UNDEFINED;
240 columnCount = chIndx;
241 continue; // while loop
243 if (str.length() == chIndx) {
246 if (!Character.isWhitespace(ch)) {
248 if (str.length() > chIndx) {
249 if (str.charAt(chIndx) == '{') {
251 token = TT_DOLLAROPEN;
258 if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch == '_') || (ch == '$')) {
262 if (ch >= '0' && ch <= '9') {
267 if (str.length() > chIndx) {
268 if (str.charAt(chIndx) == '/') {
270 // read comment until end of line:
271 while ((str.length() > chIndx) && (str.charAt(chIndx) != '\n')) {
275 } else if (str.charAt(chIndx) == '*') {
277 // multi line comment:
278 while (str.length() > chIndx) {
279 if (str.charAt(chIndx) == '*' && (str.length() > (chIndx + 1)) && str.charAt(chIndx + 1) == '/') {
283 ch = str.charAt(chIndx++);
286 columnCount = chIndx;
292 } else if (ch == '#') {
293 // read comment until end of line:
294 while ((str.length() > chIndx) && (str.charAt(chIndx) != '\n')) {
298 } else if (ch == '"') {
299 // read string until end
300 boolean openString = true;
301 while (str.length() > chIndx) {
302 ch = str.charAt(chIndx++);
304 if (str.length() > chIndx) {
305 ch = str.charAt(chIndx++);
307 } else if (ch == '"') {
310 } else if (ch == '\n') {
312 columnCount = chIndx;
316 throwSyntaxError("Open string character '\"' at end of file.");
318 token = TT_INTERPOLATED_STRING;
320 } else if (ch == '\'') {
321 // read string until end
322 boolean openString = true;
323 int startRow = rowCount;
324 while (str.length() > chIndx) {
325 ch = str.charAt(chIndx++);
327 if (str.length() > chIndx) {
328 ch = str.charAt(chIndx++);
330 } else if (ch == '\'') {
333 } else if (ch == '\n') {
335 columnCount = chIndx;
339 throwSyntaxError("Open string character \"'\" at end of file.", startRow);
341 token = TT_STRING_CONSTANT;
343 } else if (ch == '`') {
344 // read string until end
345 boolean openString = true;
346 int startRow = rowCount;
347 while (str.length() > chIndx) {
348 ch = str.charAt(chIndx++);
350 if (str.length() > chIndx) {
351 ch = str.charAt(chIndx++);
353 } else if (ch == '`') {
356 } else if (ch == '\n') {
358 columnCount = chIndx;
362 throwSyntaxError("Open string character \"`\" at end of file.", startRow);
364 setMarker("Other string delimiters prefered (found \"`\").", rowCount, PHPParser.INFO);
365 token = TT_STRING_CONSTANT;
384 token = TT_LISTCLOSE;
392 token = TT_PARTCLOSE;
400 token = TT_QUESTIONMARK;
407 if (str.length() > chIndx) {
408 if (str.charAt(chIndx) == '=') {
410 token = TT_TILDEASSIGN;
418 if (str.length() > chIndx) {
419 if (str.charAt(chIndx) == '=') {
421 token = TT_DOTASSIGN;
434 if (str.length() > chIndx) {
435 if (str.charAt(chIndx) == '=') {
437 token = TT_MODASSIGN;
444 token = TT_SEMICOLON;
449 if (str.length() > chIndx) {
450 if (str.charAt(chIndx) == '=') {
452 token = TT_POWASSIGN;
461 if (str.length() > chIndx) {
462 if (str.charAt(chIndx) == '=') {
473 if (str.length() > chIndx) {
474 if (str.charAt(chIndx) == '*') {
480 if (str.charAt(chIndx) == '=') {
491 if (str.length() > chIndx) {
492 if (str.charAt(chIndx) == '+') {
494 token = TT_INCREMENT;
498 if (str.charAt(chIndx) == '=') {
508 if (str.length() > chIndx) {
509 if (str.charAt(chIndx) == '-') {
511 token = TT_DECREMENT;
515 if (str.charAt(chIndx) == '=') {
517 token = TT_SUBTRACTFROM;
521 if (str.charAt(chIndx) == '>') {
533 if (str.length() > chIndx) {
534 ch = str.charAt(chIndx);
539 if (str.length() > chIndx) {
540 ch = str.charAt(chIndx);
561 if (str.length() > chIndx) {
562 if (str.charAt(chIndx) == '=') {
565 if (str.length() > chIndx) {
566 ch = str.charAt(chIndx);
570 token = TT_EX_UNEQUAL;
581 if (str.length() > chIndx) {
582 if (str.charAt(chIndx) == '=') {
584 token = TT_GREATEREQUAL;
587 if (str.charAt(chIndx) == '>') {
590 if (str.length() > chIndx) {
591 if (str.charAt(chIndx) == '=') {
593 token = TT_RSHIFTASSIGN;
605 if (str.length() > chIndx) {
606 if (str.charAt(chIndx) == '=') {
608 token = TT_LESSEQUAL;
612 if (str.charAt(chIndx) == '<') {
615 if (str.charAt(chIndx) == '<') {
617 int startRow = rowCount;
618 if (str.length() > chIndx) {
620 ch = str.charAt(++chIndx);
621 if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch == '_')) {
624 token = TT_STRING_CONSTANT;
625 while (str.length() > chIndx) {
626 ch = str.charAt(chIndx++);
628 if (str.length() >= chIndx + identifier.length()) {
629 if (str.substring(chIndx, chIndx + identifier.length()).equals(identifier)) {
630 chIndx += identifier.length();
638 throwSyntaxError("Open heredoc syntax after operator '<<<'.", startRow);
639 } else if (str.charAt(chIndx) == '=') {
641 token = TT_LSHIFTASSIGN;
653 if (str.length() > chIndx) {
654 if (str.charAt(chIndx) == '|') {
664 token = TT_AMPERSAND;
665 if (str.length() > chIndx) {
666 if (str.charAt(chIndx) == '&') {
671 if (str.charAt(chIndx) == '=') {
673 token = TT_ANDASSIGN;
682 if (str.length() > chIndx) {
683 if (str.charAt(chIndx) == ':') {
698 throwSyntaxError("unexpected character: '" + ch + "'");
701 if (token == TT_UNDEFINED) {
702 throwSyntaxError("token not found");
709 chIndx = str.length() + 1;
714 if (phpList != null) {
715 if (currentPHPString < phpList.size()) {
716 token = TT_UNDEFINED;
717 temp = (PHPString) phpList.get(currentPHPString++);
718 this.str = temp.getPHPString();
721 this.rowCount = temp.getLineNumber();
722 this.columnCount = 0;
726 token = TT_UNDEFINED;
732 private void getIdentifier() {
733 StringBuffer ident = new StringBuffer();
739 token = TT_IDENTIFIER;
742 while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || (ch == '_')) {
746 identifier = ident.toString();
749 Integer i = (Integer) keywordMap.get(identifier.toLowerCase());
751 token = i.intValue();
755 private void getNumber() {
756 StringBuffer inum = new StringBuffer();
765 // determine number conversions:
766 if (firstCh == '0') {
795 if (numFormat == 16) {
796 while ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) {
801 while ((ch >= '0' && ch <= '9') || (ch == '.') || (ch == 'E') || (ch == 'e')) {
802 if ((ch == '.') || (ch == 'E') || (ch == 'e')) {
803 if (ch == '.' && dFlag != ' ') {
806 if ((dFlag == 'E') || (dFlag == 'e')) {
812 if ((ch == '-') || (ch == '+')) {
826 doubleNumber = new Double(inum.toString());
827 token = TT_DOUBLE_NUMBER;
830 longNumber = Long.valueOf(inum.toString(), numFormat);
831 token = TT_INT_NUMBER;
835 } catch (Throwable e) {
836 throwSyntaxError("Number format error: " + inum.toString());
840 public void htmlParse(String input) {
842 int startLineNumber = 1;
846 boolean phpMode = false;
847 boolean phpFound = false;
849 phpList = new ArrayList();
850 currentPHPString = 0;
854 while (i < input.length()) {
855 ch = input.charAt(i++);
859 if ((!phpMode) && ch == '<') {
860 ch2 = input.charAt(i++);
862 ch2 = input.charAt(i++);
863 if (Character.isWhitespace(ch2)) {
868 startLineNumber = lineNumber;
870 } else if (ch2 == 'p') {
871 ch2 = input.charAt(i++);
873 ch2 = input.charAt(i++);
878 startLineNumber = lineNumber;
884 } else if (ch2 == 'P') {
885 ch2 = input.charAt(i++);
887 ch2 = input.charAt(i++);
892 startLineNumber = lineNumber;
905 if (ch == '/' && i < input.length()) {
906 ch2 = input.charAt(i++);
908 while (i < input.length()) {
909 ch = input.charAt(i++);
910 if (ch == '?' && i < input.length()) {
911 ch2 = input.charAt(i++);
915 phpList.add(new PHPString(input.substring(startIndex, i - 2), startLineNumber));
919 } else if (ch == '\n') {
925 } else if (ch2 == '*') {
926 // multi-line comment
927 while (i < input.length()) {
928 ch = input.charAt(i++);
931 } else if (ch == '*' && i < input.length()) {
932 ch2 = input.charAt(i++);
943 } else if (ch == '#') {
944 while (i < input.length()) {
945 ch = input.charAt(i++);
946 if (ch == '?' && i < input.length()) {
947 ch2 = input.charAt(i++);
951 phpList.add(new PHPString(input.substring(startIndex, i - 2), startLineNumber));
955 } else if (ch == '\n') {
961 } else if (ch == '"') {
963 while (i < input.length()) {
964 ch = input.charAt(i++);
967 } else if (ch == '\\' && i < input.length()) { // escape
969 } else if (ch == '"') {
974 } else if (ch == '\'') {
976 while (i < input.length()) {
977 ch = input.charAt(i++);
980 } else if (ch == '\\' && i < input.length()) { // escape
982 } else if (ch == '\'') {
989 if (ch == '?' && i < input.length()) {
990 ch2 = input.charAt(i++);
994 phpList.add(new PHPString(input.substring(startIndex, i - 2), startLineNumber));
1003 setMarker("No PHP source code found.", lineNumber, PHPParser.INFO);
1006 setMarker("Open PHP tag at end of file.", lineNumber, PHPParser.INFO);
1007 phpList.add(new PHPString(input.substring(startIndex, i - 2), startLineNumber));
1009 // for (int j=0;j<phpList.size();j++) {
1010 // String temp = ((PHPString)phpList.get(j)).getPHPString();
1011 // int startIndx = temp.length()-10;
1012 // if (startIndx<0) {
1015 // System.out.println(temp.substring(startIndx)+"?>");
1019 // for(int j=0;j<phpList.size();j++) {
1020 // temp = (PHPString) phpList.get(j);
1021 // parser.start(temp.getPHPString(), temp.getLineNumber());
1024 } catch (CoreException e) {
1028 public void phpParse(String s, int rowCount) throws CoreException {
1031 if (phpList.size() != 0) {
1032 this.str = ((PHPString) phpList.get(currentPHPString++)).getPHPString();
1035 this.token = TT_EOF;
1037 this.rowCount = rowCount;
1038 this.columnCount = 0;
1039 this.phpEnd = false;
1043 if (token != TT_EOF && token != TT_UNDEFINED) {
1046 if (token != TT_EOF && token != TT_UNDEFINED) {
1047 if (token == TT_ARGCLOSE) {
1048 throwSyntaxError("Too many closing ')'; end-of-file not reached.");
1050 if (token == TT_LISTCLOSE) {
1051 throwSyntaxError("Too many closing '}'; end-of-file not reached.");
1053 if (token == TT_PARTCLOSE) {
1054 throwSyntaxError("Too many closing ']'; end-of-file not reached.");
1057 if (token == TT_ARGOPEN) {
1058 throwSyntaxError("Read character '('; end-of-file not reached.");
1060 if (token == TT_LISTOPEN) {
1061 throwSyntaxError("Read character '{'; end-of-file not reached.");
1063 if (token == TT_PARTOPEN) {
1064 throwSyntaxError("Read character '['; end-of-file not reached.");
1067 throwSyntaxError("End-of-file not reached.");
1070 } catch (SyntaxError err) {
1074 setMarker(err.getMessage(), err.getLine(), ERROR);
1076 // if an error occured,
1077 // try to find keywords 'class' or 'function'
1078 // to parse the rest of the string
1079 while (token != TT_EOF && token != TT_UNDEFINED) {
1080 if (token == TT_class || token == TT_function) {
1085 if (token == TT_EOF || token == TT_UNDEFINED) {
1093 private void statementList() throws CoreException {
1096 if ((token == TT_LISTCLOSE)
1097 || (token == TT_case)
1098 || (token == TT_default)
1099 || (token == TT_elseif)
1100 || (token == TT_endif)
1101 || (token == TT_endfor)
1102 || (token == TT_endforeach)
1103 || (token == TT_endwhile)
1104 || (token == TT_endswitch)
1105 || (token == TT_EOF)
1106 || (token == TT_UNDEFINED)) {
1112 private void compoundStatement() throws CoreException {
1113 // '{' [statement-list] '}'
1114 if (token == TT_LISTOPEN) {
1117 throwSyntaxError("'{' expected in compound-statement.");
1119 if (token != TT_LISTCLOSE) {
1122 if (token == TT_LISTCLOSE) {
1125 throwSyntaxError("'}' expected in compound-statement.");
1129 private void statement() throws CoreException {
1130 // if (token > TT_KEYWORD && token != TT_list && token != TT_new) {
1131 String keyword = identifier;
1132 if (token == TT_include || token == TT_include_once) {
1135 if (token == TT_SEMICOLON) {
1139 throwSyntaxError("';' character after 'include' or 'include_once' expected.");
1143 } else if (token == TT_require || token == TT_require_once) {
1147 if (token == TT_SEMICOLON) {
1151 throwSyntaxError("';' character after 'require' or 'require_once' expected.");
1155 } else if (token == TT_if) {
1157 if (token == TT_ARGOPEN) {
1160 throwSyntaxError("'(' expected after 'if' keyword.");
1163 if (token == TT_ARGCLOSE) {
1166 throwSyntaxError("')' expected after 'if' condition.");
1171 } else if (token == TT_switch) {
1173 if (token == TT_ARGOPEN) {
1176 throwSyntaxError("'(' expected after 'switch' keyword.");
1179 if (token == TT_ARGCLOSE) {
1182 throwSyntaxError("')' expected after 'switch' condition.");
1186 } else if (token == TT_for) {
1188 if (token == TT_ARGOPEN) {
1191 throwSyntaxError("'(' expected after 'for' keyword.");
1193 if (token == TT_SEMICOLON) {
1197 if (token == TT_SEMICOLON) {
1200 throwSyntaxError("';' expected after 'for'.");
1203 if (token == TT_SEMICOLON) {
1207 if (token == TT_SEMICOLON) {
1210 throwSyntaxError("';' expected after 'for'.");
1213 if (token == TT_ARGCLOSE) {
1217 if (token == TT_ARGCLOSE) {
1220 throwSyntaxError("')' expected after 'for'.");
1225 } else if (token == TT_while) {
1227 if (token == TT_ARGOPEN) {
1230 throwSyntaxError("'(' expected after 'while' keyword.");
1233 if (token == TT_ARGCLOSE) {
1236 throwSyntaxError("')' expected after 'while' condition.");
1240 } else if (token == TT_do) {
1242 if (token == TT_LISTOPEN) {
1245 throwSyntaxError("'{' expected after 'do' keyword.");
1247 if (token != TT_LISTCLOSE) {
1250 if (token == TT_LISTCLOSE) {
1253 throwSyntaxError("'}' expected after 'do' keyword.");
1255 if (token == TT_while) {
1257 if (token == TT_ARGOPEN) {
1260 throwSyntaxError("'(' expected after 'while' keyword.");
1263 if (token == TT_ARGCLOSE) {
1266 throwSyntaxError("')' expected after 'while' condition.");
1269 throwSyntaxError("'while' expected after 'do' keyword.");
1271 if (token == TT_SEMICOLON) {
1275 throwSyntaxError("';' expected after do-while statement.");
1279 } else if (token == TT_foreach) {
1281 if (token == TT_ARGOPEN) {
1284 throwSyntaxError("'(' expected after 'foreach' keyword.");
1287 if (token == TT_as) {
1290 throwSyntaxError("'as' expected after 'foreach' exxpression.");
1293 if (token == TT_FOREACH) {
1297 if (token == TT_ARGCLOSE) {
1300 throwSyntaxError("')' expected after 'foreach' expression.");
1305 } else if (token == TT_continue || token == TT_break || token == TT_return) {
1307 if (token != TT_SEMICOLON) {
1310 if (token == TT_SEMICOLON) {
1314 throwSyntaxError("';' expected after 'continue', 'break' or 'return'.");
1319 } else if (token == TT_echo) {
1322 if (token == TT_SEMICOLON) {
1326 throwSyntaxError("';' expected after 'echo' statement.");
1330 // } else if (token == TT_print) {
1333 // if (token == TT_SEMICOLON) {
1337 // throwSyntaxError("';' expected after 'print' statement.");
1342 } else if (token == TT_global || token == TT_static) {
1345 if (token == TT_SEMICOLON) {
1349 throwSyntaxError("';' expected after 'global' or 'static' statement.");
1354 // } else if (token == TT_unset) {
1356 // if (token == TT_ARGOPEN) {
1359 // throwSyntaxError("'(' expected after 'unset' keyword.");
1362 // if (token == TT_ARGCLOSE) {
1365 // throwSyntaxError("')' expected after 'unset' statement.");
1367 // if (token == TT_SEMICOLON) {
1371 // throwSyntaxError("';' expected after 'unset' statement.");
1376 // } else if (token == TT_exit || token == TT_die) {
1378 // if (token != TT_SEMICOLON) {
1381 // if (token == TT_SEMICOLON) {
1385 // throwSyntaxError("';' expected after 'exit' or 'die' statement.");
1390 } else if (token == TT_define) {
1392 if (token == TT_ARGOPEN) {
1395 throwSyntaxError("'(' expected after 'define' keyword.");
1398 if (token == TT_COMMA) {
1401 throwSyntaxError("',' expected after first 'define' constant.");
1404 if (token == TT_COMMA) {
1408 if (token == TT_ARGCLOSE) {
1411 throwSyntaxError("')' expected after 'define' statement.");
1413 if (token == TT_SEMICOLON) {
1417 throwSyntaxError("';' expected after 'define' statement.");
1421 } else if (token == TT_function) {
1423 functionDefinition();
1425 } else if (token == TT_class) {
1431 // throwSyntaxError("Unexpected keyword '" + keyword + "'");
1432 } else if (token == TT_LISTOPEN) {
1433 // compoundStatement
1435 if (token != TT_LISTCLOSE) {
1438 if (token == TT_LISTCLOSE) {
1442 throwSyntaxError("'}' expected.");
1445 if (token != TT_SEMICOLON) {
1448 if (token == TT_SEMICOLON) {
1453 throwSyntaxError("';' expected after expression.");
1459 private void classDeclarator() throws CoreException {
1461 //identifier 'extends' identifier
1462 if (token == TT_IDENTIFIER) {
1464 if (token == TT_extends) {
1466 if (token == TT_IDENTIFIER) {
1469 throwSyntaxError("Class name expected after keyword 'extends'.");
1473 throwSyntaxError("Class name expected after keyword 'class'.");
1477 private void classBody() throws CoreException {
1478 //'{' [class-element-list] '}'
1479 if (token == TT_LISTOPEN) {
1481 if (token != TT_LISTCLOSE) {
1484 if (token == TT_LISTCLOSE) {
1487 throwSyntaxError("'}' expected at end of class body.");
1490 throwSyntaxError("'{' expected at start of class body.");
1494 private void classElementList() throws CoreException {
1497 } while (token == TT_function || token == TT_var);
1500 private void classElement() throws CoreException {
1502 //function-definition
1503 if (token == TT_function) {
1505 functionDefinition();
1506 } else if (token == TT_var) {
1510 throwSyntaxError("'function' or 'var' expected.");
1514 private void classProperty() throws CoreException {
1515 //'var' variable ';'
1516 //'var' variable '=' constant ';'
1518 if (token == TT_VARIABLE) {
1520 if (token == TT_ASSIGN) {
1525 throwSyntaxError("Variable expected after keyword 'var'.");
1527 if (token != TT_COMMA) {
1532 if (token == TT_SEMICOLON) {
1535 throwSyntaxError("';' expected after variable declaration.");
1539 private void functionDefinition() throws CoreException {
1540 functionDeclarator();
1541 compoundStatement();
1544 private void functionDeclarator() throws CoreException {
1545 //identifier '(' [parameter-list] ')'
1546 if (token == TT_AMPERSAND) {
1549 if (token == TT_IDENTIFIER) {
1551 if (token == TT_ARGOPEN) {
1554 throwSyntaxError("'(' expected in function declaration.");
1556 if (token != TT_ARGCLOSE) {
1559 if (token != TT_ARGCLOSE) {
1560 throwSyntaxError("')' expected in function declaration.");
1567 private void parameterList() throws CoreException {
1568 //parameter-declaration
1569 //parameter-list ',' parameter-declaration
1571 parameterDeclaration();
1572 if (token != TT_COMMA) {
1579 private void parameterDeclaration() throws CoreException {
1581 //variable-reference
1582 if (token == TT_AMPERSAND) {
1584 if (token == TT_VARIABLE) {
1587 throwSyntaxError("Variable expected after reference operator '&'.");
1590 //variable '=' constant
1591 if (token == TT_VARIABLE) {
1593 if (token == TT_ASSIGN) {
1601 private void labeledStatementList() throws CoreException {
1602 if (token != TT_case && token != TT_default) {
1603 throwSyntaxError("'case' or 'default' expected.");
1606 if (token == TT_case) {
1609 if (token == TT_DDOT) {
1611 if (token == TT_case || token == TT_default) { // empty case statement ?
1615 } else if (token == TT_SEMICOLON) {
1616 setMarker("':' expected after 'case' keyword found ';'.", rowCount, PHPParser.INFO);
1618 if (token == TT_case) { // empty case statement ?
1623 throwSyntaxError("':' character after 'case' constant expected.");
1625 } else { // TT_default
1627 if (token == TT_DDOT) {
1631 throwSyntaxError("':' character after 'default' expected.");
1634 } while (token == TT_case || token == TT_default);
1637 // public void labeledStatement() {
1638 // if (token == TT_case) {
1641 // if (token == TT_DDOT) {
1645 // throwSyntaxError("':' character after 'case' constant expected.");
1648 // } else if (token == TT_default) {
1650 // if (token == TT_DDOT) {
1654 // throwSyntaxError("':' character after 'default' expected.");
1660 // public void expressionStatement() {
1663 // private void inclusionStatement() {
1666 // public void compoundStatement() {
1669 // public void selectionStatement() {
1672 // public void iterationStatement() {
1675 // public void jumpStatement() {
1678 // public void outputStatement() {
1681 // public void scopeStatement() {
1684 // public void flowStatement() {
1687 // public void definitionStatement() {
1690 private void ifStatement() throws CoreException {
1691 // ':' statement-list [elseif-list] [else-colon-statement] 'endif' ';'
1692 if (token == TT_DDOT) {
1698 if (token == TT_DDOT) {
1702 if (token == TT_if) { //'else if'
1704 elseifStatementList();
1706 throwSyntaxError("':' expected after 'else'.");
1712 elseifStatementList();
1716 if (token != TT_endif) {
1717 throwSyntaxError("'endif' expected.");
1720 if (token != TT_SEMICOLON) {
1721 throwSyntaxError("';' expected after if-statement.");
1725 // statement [else-statement]
1727 if (token == TT_elseif) {
1729 if (token == TT_ARGOPEN) {
1732 throwSyntaxError("'(' expected after 'elseif' keyword.");
1735 if (token == TT_ARGCLOSE) {
1738 throwSyntaxError("')' expected after 'elseif' condition.");
1741 } else if (token == TT_else) {
1748 private void elseifStatementList() throws CoreException {
1754 if (token == TT_DDOT) {
1759 if (token == TT_if) { //'else if'
1762 throwSyntaxError("':' expected after 'else'.");
1775 private void elseifStatement() throws CoreException {
1776 if (token == TT_ARGOPEN) {
1779 if (token != TT_ARGOPEN) {
1780 throwSyntaxError("')' expected in else-if-statement.");
1783 if (token != TT_DDOT) {
1784 throwSyntaxError("':' expected in else-if-statement.");
1791 private void switchStatement() throws CoreException {
1792 if (token == TT_DDOT) {
1793 // ':' [labeled-statement-list] 'endswitch' ';'
1795 labeledStatementList();
1796 if (token != TT_endswitch) {
1797 throwSyntaxError("'endswitch' expected.");
1800 if (token != TT_SEMICOLON) {
1801 throwSyntaxError("';' expected after switch-statement.");
1805 // '{' [labeled-statement-list] '}'
1806 if (token != TT_LISTOPEN) {
1807 throwSyntaxError("'{' expected in switch statement.");
1810 if (token != TT_LISTCLOSE) {
1811 labeledStatementList();
1813 if (token != TT_LISTCLOSE) {
1814 throwSyntaxError("'}' expected in switch statement.");
1821 private void forStatement() throws CoreException {
1822 if (token == TT_DDOT) {
1825 if (token != TT_endfor) {
1826 throwSyntaxError("'endfor' expected.");
1829 if (token != TT_SEMICOLON) {
1830 throwSyntaxError("';' expected after for-statement.");
1838 private void whileStatement() throws CoreException {
1839 // ':' statement-list 'endwhile' ';'
1840 if (token == TT_DDOT) {
1843 if (token != TT_endwhile) {
1844 throwSyntaxError("'endwhile' expected.");
1847 if (token != TT_SEMICOLON) {
1848 throwSyntaxError("';' expected after while-statement.");
1856 private void foreachStatement() throws CoreException {
1857 if (token == TT_DDOT) {
1860 if (token != TT_endforeach) {
1861 throwSyntaxError("'endforeach' expected.");
1864 if (token != TT_SEMICOLON) {
1865 throwSyntaxError("';' expected after foreach-statement.");
1873 private void exitStatus() throws CoreException {
1874 if (token == TT_ARGOPEN) {
1877 throwSyntaxError("'(' expected in 'exit-status'.");
1879 if (token != TT_ARGCLOSE) {
1882 if (token == TT_ARGCLOSE) {
1885 throwSyntaxError("')' expected after 'exit-status'.");
1889 private void expressionList() throws CoreException {
1892 if (token == TT_COMMA) {
1900 private void expression() throws CoreException {
1901 // if (token == TT_STRING_CONSTANT || token == TT_INTERPOLATED_STRING) {
1904 logicalinclusiveorExpression();
1905 // while (token != TT_SEMICOLON) {
1911 private void postfixExpression() throws CoreException {
1913 boolean castFlag = false;
1928 case TT_STRING_CONSTANT :
1931 case TT_INTERPOLATED_STRING :
1936 if (token == TT_IDENTIFIER) {
1937 // check if identifier is a type:
1939 String str = identifier.toLowerCase();
1940 for (int i = 0; i < PHP_TYPES.length; i++) {
1941 if (PHP_TYPES[i].equals(str)) {
1948 if (token != TT_ARGCLOSE) {
1949 throwSyntaxError(") expected after cast-type '" + ident + "'.");
1959 if (token != TT_ARGCLOSE) {
1960 throwSyntaxError(") expected in postfix-expression.");
1964 case TT_DOUBLE_NUMBER :
1967 case TT_INT_NUMBER :
1970 case TT_DOLLAROPEN :
1973 if (token != TT_LISTCLOSE) {
1974 throwSyntaxError("'}' expected after indirect variable token '${'.");
1981 if (token == TT_LISTOPEN) {
1984 if (token != TT_LISTCLOSE) {
1985 throwSyntaxError("'}' expected after variable '" + ident + "' in variable-expression.");
1988 } else if (token == TT_ARGOPEN) {
1990 if (token != TT_ARGCLOSE) {
1992 if (token != TT_ARGCLOSE) {
1993 throwSyntaxError("')' expected after variable '" + ident + "' in postfix-expression.");
1999 case TT_IDENTIFIER :
2002 if (token == TT_ARGOPEN) {
2004 if (token != TT_ARGCLOSE) {
2006 if (token != TT_ARGCLOSE) {
2007 throwSyntaxError("')' expected after identifier '" + ident + "' in postfix-expression.");
2016 // if (token == TT_SEMICOLON) {
2020 // throwSyntaxError("';' expected after 'print' statement.");
2026 if (token == TT_ARGOPEN) {
2028 if (token == TT_COMMA) {
2032 if (token != TT_ARGCLOSE) {
2033 throwSyntaxError("')' expected after 'list' keyword.");
2036 // if (token == TT_SET) {
2038 // logicalinclusiveorExpression();
2041 throwSyntaxError("'(' expected after 'list' keyword.");
2046 // if (token != TT_SEMICOLON) {
2049 // if (token == TT_SEMICOLON) {
2053 // throwSyntaxError("';' expected after 'exit' expression.");
2059 // if (token != TT_SEMICOLON) {
2062 // if (token == TT_SEMICOLON) {
2066 // throwSyntaxError("';' expected after 'die' expression.");
2073 // if (token == TT_ARGOPEN) {
2075 // if (token == TT_COMMA) {
2078 // expressionList();
2079 // if (token != TT_ARGCLOSE) {
2080 // throwSyntaxError("')' expected after 'list' keyword.");
2083 // if (token == TT_SET) {
2085 // logicalinclusiveorExpression();
2088 // throwSyntaxError("'(' expected after 'list' keyword.");
2092 boolean while_flag = true;
2098 if (token != TT_PARTCLOSE) {
2099 throwSyntaxError("] expected in postfix-expression.");
2103 case TT_DDOT2 : // ::
2106 if (token > TT_KEYWORD) {
2108 setMarker("Avoid using keyword '" + ident + "' as variable name.", rowCount, PHPParser.INFO);
2110 if (token == TT_ARGOPEN) {
2113 if (token != TT_ARGCLOSE) {
2114 throwSyntaxError(") expected after identifier '" + ident + "'.");
2124 // if (token == TT_ARGOPEN) {
2126 // expressionList();
2127 // if (token != TT_ARGCLOSE) {
2128 // throwSyntaxError(") expected after variable '" + ident + "'.");
2133 case TT_IDENTIFIER :
2136 if (token == TT_ARGOPEN) {
2139 if (token != TT_ARGCLOSE) {
2140 throwSyntaxError(") expected after identifier '" + ident + "'.");
2148 if (token != TT_LISTCLOSE) {
2149 throwSyntaxError("} expected in postfix-expression.");
2154 throwSyntaxError("Syntax error after '->' token.");
2168 } while (while_flag);
2171 private void unaryExpression() throws CoreException {
2181 // '@' '&' '*' '+' '-' '~' '!'
2211 postfixExpression();
2215 private void castExpression() throws CoreException {
2216 // if (token == TT_ARGOPEN) {
2219 // if (token != TT_ARGCLOSE) {
2220 // throwSyntaxError(") expected after cast-expression.");
2227 private void typeName() throws CoreException {
2228 //'string' 'unset' 'array' 'object'
2230 //'real' 'double' 'float'
2233 if (token == TT_IDENTIFIER) {
2235 String str = identifier.toLowerCase();
2237 for (int i = 0; i < PHP_TYPES.length; i++) {
2238 if (PHP_TYPES[i].equals(str)) {
2243 throwSyntaxError("Expected type cast '( <type-name> )'; Got '" + ident + "'.");
2246 private void assignExpression() throws CoreException {
2248 if (token == TT_ASSIGN) { // =
2250 logicalinclusiveorExpression();
2251 } else if (token == TT_DOTASSIGN) { // .=
2253 logicalinclusiveorExpression();
2254 } else if (token == TT_FOREACH) { // =>
2256 logicalinclusiveorExpression();
2257 } else if (token == TT_ADDTO) { // +=
2259 logicalinclusiveorExpression();
2260 } else if (token == TT_SUBTRACTFROM) { // -=
2262 logicalinclusiveorExpression();
2263 } else if (token == TT_TIMESBY) { // *=
2265 logicalinclusiveorExpression();
2266 } else if (token == TT_DIVIDEBY) { // *=
2268 logicalinclusiveorExpression();
2269 } else if (token == TT_MODASSIGN) { // %=
2271 logicalinclusiveorExpression();
2272 } else if (token == TT_ANDASSIGN) { // &=
2274 logicalinclusiveorExpression();
2275 } else if (token == TT_POWASSIGN) { // ^=
2277 logicalinclusiveorExpression();
2278 } else if (token == TT_LSHIFTASSIGN) { // <<=
2280 logicalinclusiveorExpression();
2281 } else if (token == TT_RSHIFTASSIGN) { // >>=
2283 logicalinclusiveorExpression();
2284 } else if (token == TT_TILDEASSIGN) { // ~=
2286 logicalinclusiveorExpression();
2290 private void multiplicativeExpression() throws CoreException {
2293 if (token != TT_MULTIPLY && token != TT_DIV && token != TT_MOD) {
2300 private void concatenationExpression() throws CoreException {
2302 multiplicativeExpression();
2303 if (token != TT_DOT) {
2310 private void additiveExpression() throws CoreException {
2312 concatenationExpression();
2313 if (token != TT_ADD && token != TT_SUBTRACT) {
2320 private void shiftExpression() throws CoreException {
2322 additiveExpression();
2323 if (token != TT_LSHIFT && token != TT_RSHIFT) {
2330 private void relationalExpression() throws CoreException {
2333 if (token != TT_LESS && token != TT_GREATER && token != TT_LESSEQUAL && token != TT_GREATEREQUAL) {
2340 private void identicalExpression() throws CoreException {
2342 relationalExpression();
2343 if (token != TT_EX_EQUAL && token != TT_EX_UNEQUAL) {
2350 private void equalityExpression() throws CoreException {
2352 identicalExpression();
2353 if (token != TT_EQUAL && token != TT_UNEQUAL) {
2360 private void ternaryExpression() throws CoreException {
2361 equalityExpression();
2362 if (token == TT_QUESTIONMARK) {
2365 if (token == TT_DDOT) {
2369 throwSyntaxError("':' expected in ternary operator '? :'.");
2374 private void andExpression() throws CoreException {
2376 ternaryExpression();
2377 if (token != TT_AMPERSAND) {
2384 private void exclusiveorExpression() throws CoreException {
2387 if (token != TT_POW) {
2394 private void inclusiveorExpression() throws CoreException {
2396 exclusiveorExpression();
2397 if (token != TT_LINE) {
2404 private void booleanandExpression() throws CoreException {
2406 inclusiveorExpression();
2407 if (token != TT_AND) {
2414 private void booleanorExpression() throws CoreException {
2416 booleanandExpression();
2417 if (token != TT_OR) {
2424 private void logicalandExpression() throws CoreException {
2426 booleanorExpression();
2427 if (token != TT_and) {
2434 private void logicalexclusiveorExpression() throws CoreException {
2436 logicalandExpression();
2437 if (token != TT_xor) {
2444 private void logicalinclusiveorExpression() throws CoreException {
2446 logicalexclusiveorExpression();
2447 if (token != TT_or) {
2454 // public void assignmentExpression() {
2455 // if (token == TT_VARIABLE) {
2457 // if (token == TT_SET) {
2459 // logicalinclusiveorExpression();
2462 // logicalinclusiveorExpression();
2466 private void variableList() throws CoreException {
2469 if (token == TT_COMMA) {
2477 private void variable() throws CoreException {
2478 if (token == TT_DOLLAROPEN) {
2482 if (token != TT_LISTCLOSE) {
2483 throwSyntaxError("'}' expected after indirect variable token '${'.");
2487 if (token == TT_VARIABLE) {
2489 if (token == TT_PARTOPEN) {
2492 if (token != TT_PARTCLOSE) {
2493 throwSyntaxError("']' expected in variable-list.");
2496 } else if (token == TT_ASSIGN) {
2501 throwSyntaxError("$-variable expected in variable-list.");
2506 private void constant() throws CoreException {
2512 case TT_DOUBLE_NUMBER :
2515 case TT_INT_NUMBER :
2519 throwSyntaxError("Constant expected after '+' presign.");
2525 case TT_DOUBLE_NUMBER :
2528 case TT_INT_NUMBER :
2532 throwSyntaxError("Constant expected after '-' presign.");
2544 case TT_IDENTIFIER :
2547 if (token == TT_ARGOPEN) {
2549 if (token != TT_ARGCLOSE) {
2551 if (token != TT_ARGCLOSE) {
2552 throwSyntaxError("')' expected after identifier '" + ident + "' in postfix-expression.");
2558 case TT_STRING_CONSTANT :
2561 case TT_INTERPOLATED_STRING :
2564 case TT_DOUBLE_NUMBER :
2567 case TT_INT_NUMBER :
2571 throwSyntaxError("Constant expected.");
2576 * Call the php parse command ( php -l -f <filename> )
2577 * and create markers according to the external parser output
2579 public static void phpExternalParse(IFile file) {
2580 //IFile file = (IFile) resource;
2581 IPath path = file.getFullPath();
2582 IPreferenceStore store = PHPeclipsePlugin.getDefault().getPreferenceStore();
2583 String filename = file.getLocation().toString();
2585 String[] arguments = { filename };
2586 MessageFormat form = new MessageFormat(store.getString(PHPeclipsePlugin.EXTERNAL_PARSER_PREF));
2587 String command = form.format(arguments);
2589 String parserResult = PHPStartApacheAction.execute(command, "External parser: ");
2592 // parse the buffer to find the errors and warnings
2593 createMarkers(parserResult, file);
2594 } catch (CoreException e) {
2599 * Create markers according to the external parser output
2601 private static void createMarkers(String output, IFile file) throws CoreException {
2602 // delete all markers
2603 file.deleteMarkers(IMarker.PROBLEM, false, 0);
2607 boolean flag = true;
2608 while ((brIndx = output.indexOf("<br />", indx)) != -1) {
2609 // newer php error output (tested with 4.2.3)
2610 scanLine(output, file, indx, brIndx);
2615 while ((brIndx = output.indexOf("<br>", indx)) != -1) {
2616 // older php error output (tested with 4.2.3)
2617 scanLine(output, file, indx, brIndx);
2623 private static void scanLine(String output, IFile file, int indx, int brIndx) throws CoreException {
2625 String outLineNumberString;
2626 StringBuffer lineNumberBuffer = new StringBuffer(10);
2628 current = output.substring(indx, brIndx);
2630 if (current.indexOf(PARSE_WARNING_STRING) != -1 || current.indexOf(PARSE_ERROR_STRING) != -1) {
2631 int onLine = current.indexOf("on line <b>");
2633 lineNumberBuffer.delete(0, lineNumberBuffer.length());
2634 for (int i = onLine; i < current.length(); i++) {
2635 ch = current.charAt(i);
2636 if ('0' <= ch && '9' >= ch) {
2637 lineNumberBuffer.append(ch);
2641 int lineNumber = Integer.parseInt(lineNumberBuffer.toString());
2643 Hashtable attributes = new Hashtable();
2645 current = current.replaceAll("\n", "");
2646 current = current.replaceAll("<b>", "");
2647 current = current.replaceAll("</b>", "");
2648 MarkerUtilities.setMessage(attributes, current);
2650 if (current.indexOf(PARSE_ERROR_STRING) != -1)
2651 attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_ERROR));
2652 else if (current.indexOf(PARSE_WARNING_STRING) != -1)
2653 attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_WARNING));
2655 attributes.put(IMarker.SEVERITY, new Integer(IMarker.SEVERITY_INFO));
2656 MarkerUtilities.setLineNumber(attributes, lineNumber);
2657 MarkerUtilities.createMarker(file, attributes, IMarker.PROBLEM);