/******************************************************************************* * Copyright (c) 2009 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Zend Technologies *******************************************************************************/ package org.eclipse.php.internal.core.compiler.ast.parser; import java.util.LinkedList; import java.util.List; import java.util.Stack; import org.eclipse.dltk.ast.ASTNode; import org.eclipse.dltk.ast.declarations.MethodDeclaration; import org.eclipse.dltk.ast.declarations.TypeDeclaration; import org.eclipse.dltk.ast.statements.Block; import org.eclipse.dltk.ast.statements.Statement; import org.eclipse.dltk.compiler.problem.DefaultProblem; import org.eclipse.dltk.compiler.problem.IProblemIdentifier; import org.eclipse.dltk.compiler.problem.IProblemReporter; import org.eclipse.dltk.compiler.problem.ProblemSeverities; import org.eclipse.php.core.compiler.IPHPModifiers; import org.eclipse.php.core.compiler.ast.nodes.*; import org.eclipse.php.internal.core.ast.scanner.AstLexer; import java_cup.runtime.Scanner; import java_cup.runtime.Symbol; import java_cup.runtime.SymbolFactory; import java_cup.runtime.lr_parser; abstract public class AbstractASTParser extends lr_parser { private PHPModuleDeclaration program = new PHPModuleDeclaration(0, 0, new LinkedList<ASTNode>(), new LinkedList<ASTError>(), new LinkedList<VarComment>()); private IProblemReporter problemReporter; private String fileName; /** * This is a place holder for statements that were found after unclosed * classes */ public Statement pendingStatement = null; /** This is a latest non-bracketed namespace declaration */ public NamespaceDeclaration currentNamespace = null; /** Whether we've met the unbracketed namespace declaration in this file */ public boolean metUnbracketedNSDecl; /** Whether we've met the bracketed namespace declaration in this file */ public boolean metBracketedNSDecl; /** Top declarations stack */ public Stack<Statement> declarations = new Stack<Statement>(); public AbstractASTParser() { super(); } public AbstractASTParser(Scanner s) { super(s); } public AbstractASTParser(Scanner s, SymbolFactory sf) { super(s, sf); } public void setProblemReporter(IProblemReporter problemReporter) { this.problemReporter = problemReporter; } public IProblemReporter getProblemReporter() { return problemReporter; } public void setFileName(String fileName) { this.fileName = fileName; } public String getFileName() { return fileName; } protected List<ASTError> getErrors() { return program.getErrors(); } protected void reportError(IProblemReporter problemReporter, String fileName, int start, int end, int lineNumber, IProblemIdentifier problemIdentifier, String message) { DefaultProblem problem = new DefaultProblem(fileName, message, problemIdentifier, new String[0], ProblemSeverities.Error, start, end, lineNumber, -1); problemReporter.reportProblem(problem); } /** * Report on errors that will be added to the AST as statements */ public void reportError() { program.setHasErrors(true); } public void reportError(ASTError error) { reportError(error, null); } /** * Reporting an error that cannot be added as a statement and has to be in a * separated list. * * @param error */ public void reportError(ASTError error, String message) { getErrors().add(error); reportError(); if (message != null && problemReporter != null && fileName != null) { int lineNumber = ((AstLexer) getScanner()).getCurrentLine(); reportError(problemReporter, fileName, error.sourceStart(), error.sourceEnd(), lineNumber, error.getProblem(), message); } } public void addStatement(Statement s) { int kind = s.getKind(); if (kind != ASTNodeKinds.EMPTY_STATEMENT && kind != ASTNodeKinds.DECLARE_STATEMENT && kind != ASTNodeKinds.NAMESPACE_DECLARATION && metBracketedNSDecl) { reportError(new ASTError(s.sourceStart(), s.sourceEnd()), Messages.AbstractASTParser_0); } if (currentNamespace != null && currentNamespace != s) { currentNamespace.addStatement(s); } else { program.addStatement(s); } } public PHPModuleDeclaration getModuleDeclaration() { return program; } public void report_error(String message, Object info) { if (info instanceof Symbol) { if (((Symbol) info).left != -1) { ASTError error = new ASTError(((Symbol) info).left, ((Symbol) info).right); reportError(error); } else { reportError(new ASTError(0, 1)); } } } @SuppressWarnings("unchecked") public void unrecovered_syntax_error(Symbol cur_token) throws java.lang.Exception { // in case int start = 0; int end = 0; Object value = cur_token.value; PHPModuleDeclaration program = getModuleDeclaration(); List<ASTNode> statements = program.getStatements(); if (value instanceof List) { statements.addAll((List) value); } if (!declarations.isEmpty()) { statements.add(declarations.remove(0)); } if (!statements.isEmpty()) { Statement s1 = (Statement) statements.get(0); Statement s2 = (Statement) statements.get(statements.size() - 1); start = s1.sourceStart(); end = s2.sourceEnd(); } List<ASTError> errors = getErrors(); if (!errors.isEmpty()) { ASTError lastError = errors.get(errors.size() - 1); end = (end > lastError.sourceEnd()) ? end : lastError.sourceEnd(); } program.setStart(start); program.setEnd(end); // Set end offset of recovered class/interface node to the end of file if (statements.size() > 0) { ASTNode lastStatement = statements.get(statements.size() - 1); if (lastStatement instanceof IRecoverable) { IRecoverable recoverable = (IRecoverable) lastStatement; if (recoverable.isRecovered()) { lastStatement.setEnd(end); } } } super.unrecovered_syntax_error(cur_token); } public void syntax_error(Symbol cur_token) { super.syntax_error(cur_token); if (fileName == null || problemReporter == null) { return; } int state = ((Symbol) stack.peek()).parse_state; short[] rowOfProbe = action_tab[state]; int startPosition = cur_token.left; int endPosition = cur_token.right; int lineNumber = ((AstLexer) getScanner()).getCurrentLine(); StringBuilder errorMessage = new StringBuilder(Messages.AbstractASTParser_1); // current token can be either null, string or phpdoc - according to // this resolve: String currentText = cur_token.value instanceof String ? (String) cur_token.value : null; if (currentText == null || currentText.length() == 0) { currentText = getTokenName(cur_token.sym); } if (currentText != null && currentText.length() > 0) { if (currentText.equals(";")) { // This means EOF, //$NON-NLS-1$ // since it's // substituted by the lexer // explicitly. currentText = "EOF"; //$NON-NLS-1$ } endPosition = startPosition + currentText.length(); errorMessage.append(Messages.AbstractASTParser_4).append(currentText).append('\''); } if (rowOfProbe.length <= 6) { errorMessage.append(Messages.AbstractASTParser_5); boolean first = true; for (int probe = 0; probe < rowOfProbe.length; probe += 2) { String tokenName = getTokenName(rowOfProbe[probe]); if (tokenName != null) { if (!first) { errorMessage.append(Messages.AbstractASTParser_6); } errorMessage.append('\'').append(tokenName).append('\''); first = false; } } } reportError(problemReporter, fileName, startPosition, endPosition, lineNumber, PHPProblemIdentifier.SYNTAX, errorMessage.toString()); } protected abstract String getTokenName(int token); public void report_fatal_error(String message, Object info) throws java.lang.Exception { /* * stop parsing (not really necessary since we throw an exception, but) */ done_parsing(); /* use the normal error message reporting to put out the message */ // report_error(message, info); // throw new Exception("Can't recover from previous error(s)"); } public void addDeclarationStatement(Statement s) { if (declarations.isEmpty()) { if (s.getKind() == ASTNodeKinds.NAMESPACE_DECLARATION) { if (program.getStatements().size() > 0 && !metBracketedNSDecl && !metUnbracketedNSDecl) { boolean justDeclarationNodes = true; for (Object statement : program.getStatements()) { if (((Statement) statement).getKind() != ASTNodeKinds.DECLARE_STATEMENT) { justDeclarationNodes = false; break; } } if (!justDeclarationNodes) { reportError(new ASTError(s.sourceStart(), s.sourceEnd()), Messages.AbstractASTParser_7); } } } // we don't add top level statements to the program node this way return; } Statement node = declarations.peek(); Block block = null; if (node instanceof TypeDeclaration) { block = ((TypeDeclaration) node).getBody(); } else if (node instanceof MethodDeclaration) { block = ((MethodDeclaration) node).getBody(); } else if (node instanceof Block) { block = (Block) node; } else if (node instanceof AnonymousClassDeclaration) { block = ((AnonymousClassDeclaration) node).getBody(); } if (block == null) { return; } block.addStatement(s); block.setEnd(s.sourceEnd()); } protected int appendModifier(int start, int end, int flags, int newFlag) { if ((flags & IPHPModifiers.AccessMask) != 0 && (newFlag & IPHPModifiers.AccessMask) != 0) { reportError(new ASTError(start, end), Messages.AbstractASTParser_MultipleAccessModifiersError); } if ((flags & IPHPModifiers.AccStatic) != 0 && (newFlag & IPHPModifiers.AccStatic) != 0) { reportError(new ASTError(start, end), Messages.AbstractASTParser_MultipleStaticModifiersError); } return flags | newFlag; } protected int appendMethodModifier(int start, int end, int flags, int newFlag) { if ((flags & IPHPModifiers.AccAbstract) != 0 && (newFlag & IPHPModifiers.AccAbstract) != 0) { reportError(new ASTError(start, end), Messages.AbstractASTParser_MultipleAbstractModifierError); } if ((flags & IPHPModifiers.AccFinal) != 0 && (newFlag & IPHPModifiers.AccFinal) != 0) { reportError(new ASTError(start, end), Messages.AbstractASTParser_MultipleFinalModifierError); } flags = appendModifier(start, end, flags, newFlag); if ((flags & IPHPModifiers.AccAbstract) != 0 && (flags & IPHPModifiers.AccFinal) != 0) { reportError(new ASTError(start, end), Messages.AbstractASTParser_AbstractAsFinalError); } return flags; } protected int appendPropertyModifier(int start, int end, int flags, int newFlag) { flags = appendModifier(start, end, flags, newFlag); if ((newFlag & IPHPModifiers.AccAbstract) != 0) { reportError(new ASTError(start, end), Messages.AbstractASTParser_AbstractPropertyError); } if ((newFlag & IPHPModifiers.AccFinal) != 0) { reportError(new ASTError(start, end), Messages.AbstractASTParser_FinalPropertyError); } return flags; } }