/*******************************************************************************
* 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 org2.eclipse.php.internal.core.ast.nodes;
import java.io.CharArrayReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java_cup.runtime.Scanner;
import java_cup.runtime.Symbol;
import java_cup.runtime.lr_parser;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org2.eclipse.php.internal.core.CoreMessages;
import org2.eclipse.php.internal.core.PHPVersion;
import org2.eclipse.php.internal.core.ast.scanner.AstLexer;
import com.aptana.core.build.IBuildParticipant.BuildType;
import com.aptana.core.logging.IdeLog;
import com.aptana.core.util.StringUtil;
import com.aptana.editor.php.core.model.ISourceModule;
import com.aptana.editor.php.epl.PHPEplPlugin;
/**
* A PHP language parser for creating abstract syntax trees (ASTs).
* <p>
* Example: Create basic AST from source string
*
* <pre>
* String source = ...;
* Program program = ASTParser.parse(source);
* </pre>
*/
public class ASTParser {
// version tags
private static final Reader EMPTY_STRING_READER = new StringReader(StringUtil.EMPTY);
/**
* THREAD SAFE AST PARSER STARTS HERE
*/
private final AST ast;
private final ISourceModule sourceModule;
private ASTParser(Reader reader, PHPVersion phpVersion, boolean useASPTags,
boolean useShortTags) throws IOException {
this(reader, phpVersion, useASPTags, useShortTags, null);
}
private ASTParser(Reader reader, PHPVersion phpVersion, boolean useASPTags,
boolean useShortTags, ISourceModule sourceModule)
throws IOException{
this(reader, phpVersion, useASPTags, useShortTags, sourceModule, BuildType.RECONCILE);
}
private ASTParser(Reader reader, PHPVersion phpVersion, boolean useASPTags,
boolean useShortTags, ISourceModule sourceModule, BuildType type)
throws IOException {
this.sourceModule = sourceModule;
Object resource = null;
if (sourceModule != null)
{
resource = sourceModule.getResource();
}
this.ast = new AST(reader, phpVersion, useASPTags, useShortTags, resource, type);
this.ast.setDefaultNodeFlag(ASTNode.ORIGINAL);
// Aptana mod - Commented out.
// set resolve binding property and the binding resolver
// if (sourceModule != null) {
// this.ast.setFlag(AST.RESOLVED_BINDINGS);
// try {
// this.ast.setBindingResolver(new DefaultBindingResolver(
// sourceModule, sourceModule.getOwner()));
// } catch (ModelException e) {
// throw new IOException("ModelException " + e.getMessage());
// }
//}
}
/**
* Factory methods for ASTParser
*/
public static ASTParser newParser(PHPVersion version, boolean useShortTags) {
try {
return new ASTParser(new StringReader(StringUtil.EMPTY), version, false,
useShortTags);
} catch (IOException e) {
assert false;
// Since we use empty reader we cannot have an IOException here
return null;
}
}
/**
* Factory methods for ASTParser
*/
public static ASTParser newParser(PHPVersion version) {
return newParser(version, true);
}
/**
* Factory methods for ASTParser
*/
// Aptana Mod - Commented out.
// public static ASTParser newParser(ISourceModule sourceModule) {
// PHPVersion phpVersion = ProjectOptions.getPhpVersion(sourceModule
// .getScriptProject().getProject());
//
// return newParser(phpVersion, sourceModule);
// }
public static ASTParser newParser(PHPVersion version,
ISourceModule sourceModule) {
if (sourceModule == null) {
throw new IllegalStateException(
"ASTParser - Can't parser with null ISourceModule"); //$NON-NLS-1$
}
try {
// Aptana Mod - use short tags by default (TODO - add a preference for that)
final ASTParser parser = new ASTParser(new StringReader(""), //$NON-NLS-1$
version, false, true, sourceModule);
parser.setSource(sourceModule.getSourceAsCharArray());
return parser;
} catch (Exception e) {
IdeLog.logError(PHPEplPlugin.getDefault(), "Error creating a new PHP AST Parser", e); //$NON-NLS-1$
}
return null;
}
public static ASTParser newParser(Reader reader, PHPVersion version,
boolean useShortTags) throws IOException {
return new ASTParser(reader, version, false, useShortTags);
}
public static ASTParser newParser(Reader reader, PHPVersion version,
boolean useASPTags, boolean useShortTags) throws IOException {
return new ASTParser(reader, version, useASPTags, useShortTags);
}
public static ASTParser newParser(Reader reader, PHPVersion version,
boolean useASPTags, ISourceModule sourceModule) throws IOException {
// Aptana Mod - use short tags by default (TODO - add a preference for that)
return new ASTParser(reader, version, useASPTags,
true, sourceModule);
}
public static ASTParser newParser(Reader reader, PHPVersion version,
boolean useASPTags, ISourceModule sourceModule, BuildType type) throws IOException {
// Aptana Mod - use short tags by default (TODO - add a preference for that)
return new ASTParser(reader, version, useASPTags,
true, sourceModule, type);
}
/**
* Set the raw source that will be used on parsing
*
* @throws IOException
*/
public void setSource(char[] source) throws IOException {
final CharArrayReader charArrayReader = new CharArrayReader(source);
setSource(charArrayReader);
}
/**
* Set source of the parser
*
* @throws IOException
*/
public void setSource(Reader source) throws IOException {
this.ast.setSource(source);
}
/**
* Set the source from source module
*
* @throws IOException
* @throws CoreException
*/
public void setSource(ISourceModule sourceModule) throws IOException, CoreException {
this.ast.setSource(new CharArrayReader(sourceModule
.getSourceAsCharArray()));
}
/**
* This operation creates an abstract syntax tree for the given AST Factory
*
* @param progressMonitor
* @return Program that represents the equivalent AST
* @throws Exception
* - for exception occurs on the parsing step
*/
public Program createAST(IProgressMonitor progressMonitor) throws Exception {
if (progressMonitor == null) {
progressMonitor = new NullProgressMonitor();
}
progressMonitor.beginTask(
"Creating Abstract Syntax Tree for source...", 3); //$NON-NLS-1$
final Scanner lexer = this.ast.lexer();
final lr_parser phpParser = this.ast.parser();
progressMonitor.worked(1);
phpParser.setScanner(lexer);
progressMonitor.worked(2);
final Symbol symbol = phpParser.parse();
progressMonitor.done();
if (symbol == null || !(symbol.value instanceof Program)) {
return null;
}
Program p = (Program) symbol.value;
AST ast = p.getAST();
p.setSourceModule(sourceModule);
// now reset the ast default node flag back to differntate between
// original nodes
ast.setDefaultNodeFlag(0);
// Set the original modification count to the count after the creation
// of the Program.
// This is important to allow the AST rewriting.
ast.setOriginalModificationCount(ast.modificationCount());
return p;
}
/**
* Returns the AST that was originally created with this ASTParser.
* [Aptana Mod]
*/
public AST getAST() {
return this.ast;
}
/********************************************************************************
* NOT THREAD SAFE IMPLEMENTATION STARTS HERE
*********************************************************************************/
// php 5.3 analysis
private static org2.eclipse.php.internal.core.ast.scanner.php53.PhpAstLexer createEmptyLexer_53() {
return new org2.eclipse.php.internal.core.ast.scanner.php53.PhpAstLexer(
ASTParser.EMPTY_STRING_READER);
}
// php 5.4 analysis
private static org2.eclipse.php.internal.core.ast.scanner.php54.PhpAstLexer createEmptyLexer_54() {
return new org2.eclipse.php.internal.core.ast.scanner.php54.PhpAstLexer(
ASTParser.EMPTY_STRING_READER);
}
private static org2.eclipse.php.internal.core.ast.scanner.php54.PhpAstParser createEmptyParser_54() {
return new org2.eclipse.php.internal.core.ast.scanner.php54.PhpAstParser(
createEmptyLexer_54());
}
private static org2.eclipse.php.internal.core.ast.scanner.php53.PhpAstParser createEmptyParser_53() {
return new org2.eclipse.php.internal.core.ast.scanner.php53.PhpAstParser(
createEmptyLexer_53());
}
// php 5 analysis
private static org2.eclipse.php.internal.core.ast.scanner.php5.PhpAstLexer createEmptyLexer_5() {
return new org2.eclipse.php.internal.core.ast.scanner.php5.PhpAstLexer(
ASTParser.EMPTY_STRING_READER);
}
private static org2.eclipse.php.internal.core.ast.scanner.php5.PhpAstParser createEmptyParser_5() {
return new org2.eclipse.php.internal.core.ast.scanner.php5.PhpAstParser(
createEmptyLexer_5());
}
// php 4 analysis
private static org2.eclipse.php.internal.core.ast.scanner.php4.PhpAstLexer createEmptyLexer_4() {
return new org2.eclipse.php.internal.core.ast.scanner.php4.PhpAstLexer(
ASTParser.EMPTY_STRING_READER);
}
private static org2.eclipse.php.internal.core.ast.scanner.php4.PhpAstParser createEmptyParser_4() {
return new org2.eclipse.php.internal.core.ast.scanner.php4.PhpAstParser(
createEmptyLexer_4());
}
/**
* Constructs a scanner from a given reader
*
* @param ast
* @param reader
* @param phpVersion
* @param aspTagsAsPhp\
* @param createAST In case the given AST was null, create an AST and return the AstLexer that holds it.
* @return
* @throws IOException
*/
public static AstLexer getLexer(AST ast, Reader reader,
PHPVersion phpVersion, boolean aspTagsAsPhp, boolean useShortTags, boolean createAST)
throws IOException {
// Aptana mod
if (ast == null) {
ast = new AST(reader, phpVersion, aspTagsAsPhp, useShortTags);
ast.setDefaultNodeFlag(ASTNode.ORIGINAL);
return ast.lexer();
}
// end Aptana mod
if (PHPVersion.PHP4 == phpVersion) {
final org2.eclipse.php.internal.core.ast.scanner.php4.PhpAstLexer lexer4 = getLexer4(reader);
lexer4.setUseAspTagsAsPhp(aspTagsAsPhp);
lexer4.setUseShortTags(useShortTags);
lexer4.setAST(ast);
return lexer4;
} else if (PHPVersion.PHP5 == phpVersion) {
final org2.eclipse.php.internal.core.ast.scanner.php5.PhpAstLexer lexer5 = getLexer5(reader);
lexer5.setUseAspTagsAsPhp(aspTagsAsPhp);
lexer5.setUseShortTags(useShortTags);
lexer5.setAST(ast);
return lexer5;
} else if (PHPVersion.PHP5_3 == phpVersion) {
final org2.eclipse.php.internal.core.ast.scanner.php53.PhpAstLexer lexer53 = getLexer53(reader);
lexer53.setUseAspTagsAsPhp(aspTagsAsPhp);
lexer53.setUseShortTags(useShortTags);
lexer53.setAST(ast);
return lexer53;
} else if (PHPVersion.PHP5_4 == phpVersion) {
final org2.eclipse.php.internal.core.ast.scanner.php54.PhpAstLexer lexer54 = getLexer54(reader);
lexer54.setUseAspTagsAsPhp(aspTagsAsPhp);
lexer54.setUseShortTags(useShortTags);
lexer54.setAST(ast);
return lexer54;
} else {
throw new IllegalArgumentException(
CoreMessages.getString("ASTParser_1") + phpVersion); //$NON-NLS-1$
}
}
@SuppressWarnings("unused")
private static lr_parser getParser(PHPVersion phpVersion, AST ast)
throws IOException {
if (PHPVersion.PHP4 == phpVersion) {
org2.eclipse.php.internal.core.ast.scanner.php4.PhpAstParser parser = createEmptyParser_4();
parser.setAST(ast);
return parser;
} else if (PHPVersion.PHP5 == phpVersion) {
org2.eclipse.php.internal.core.ast.scanner.php5.PhpAstParser parser = createEmptyParser_5();
parser.setAST(ast);
return parser;
} else if (PHPVersion.PHP5_3 == phpVersion) {
org2.eclipse.php.internal.core.ast.scanner.php53.PhpAstParser parser = createEmptyParser_53();
parser.setAST(ast);
return parser;
} else if (PHPVersion.PHP5_4 == phpVersion) {
org2.eclipse.php.internal.core.ast.scanner.php54.PhpAstParser parser = createEmptyParser_54();
parser.setAST(ast);
return parser;
} else {
throw new IllegalArgumentException(
CoreMessages.getString("ASTParser_1") + phpVersion); //$NON-NLS-1$
}
}
/**
* @param reader
* @return the singleton
* {@link org2.eclipse.php.internal.core.ast.scanner.php53.PhpAstLexer}
*/
private static org2.eclipse.php.internal.core.ast.scanner.php53.PhpAstLexer getLexer53(
Reader reader) throws IOException {
final org2.eclipse.php.internal.core.ast.scanner.php53.PhpAstLexer phpAstLexer53 = createEmptyLexer_53();
phpAstLexer53.yyreset(reader);
phpAstLexer53.resetCommentList();
return phpAstLexer53;
}
/**
* @param reader
* @return the singleton
* {@link org2.eclipse.php.internal.core.ast.scanner.php53.PhpAstLexer}
*/
private static org2.eclipse.php.internal.core.ast.scanner.php54.PhpAstLexer getLexer54(
Reader reader) throws IOException {
final org2.eclipse.php.internal.core.ast.scanner.php54.PhpAstLexer phpAstLexer54 = createEmptyLexer_54();
phpAstLexer54.yyreset(reader);
phpAstLexer54.resetCommentList();
return phpAstLexer54;
}
/**
* @param reader
* @return the singleton
* {@link org2.eclipse.php.internal.core.ast.scanner.php5.PhpAstLexer}
*/
private static org2.eclipse.php.internal.core.ast.scanner.php5.PhpAstLexer getLexer5(
Reader reader) throws IOException {
final org2.eclipse.php.internal.core.ast.scanner.php5.PhpAstLexer phpAstLexer5 = createEmptyLexer_5();
phpAstLexer5.yyreset(reader);
phpAstLexer5.resetCommentList();
return phpAstLexer5;
}
/**
* @param reader
* @return the singleton
* {@link org2.eclipse.php.internal.core.ast.scanner.php4.PhpAstLexer}
*/
private static org2.eclipse.php.internal.core.ast.scanner.php4.PhpAstLexer getLexer4(
Reader reader) throws IOException {
final org2.eclipse.php.internal.core.ast.scanner.php4.PhpAstLexer phpAstLexer4 = createEmptyLexer_4();
phpAstLexer4.yyreset(reader);
phpAstLexer4.resetCommentList();
return phpAstLexer4;
}
}