package org.eclipse.dltk.tcl.internal.parser;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.dltk.ast.ASTNode;
import org.eclipse.dltk.ast.ASTVisitor;
import org.eclipse.dltk.ast.declarations.MethodDeclaration;
import org.eclipse.dltk.ast.declarations.ModuleDeclaration;
import org.eclipse.dltk.ast.declarations.TypeDeclaration;
import org.eclipse.dltk.ast.expressions.Expression;
import org.eclipse.dltk.ast.expressions.StringLiteral;
import org.eclipse.dltk.ast.parser.AbstractSourceParser;
import org.eclipse.dltk.ast.parser.ISourceParser;
import org.eclipse.dltk.ast.parser.ISourceParserExtension;
import org.eclipse.dltk.ast.references.SimpleReference;
import org.eclipse.dltk.ast.statements.Block;
import org.eclipse.dltk.compiler.env.IModuleSource;
import org.eclipse.dltk.compiler.problem.IProblemReporter;
import org.eclipse.dltk.core.RuntimePerformanceMonitor;
import org.eclipse.dltk.core.RuntimePerformanceMonitor.PerformanceNode;
import org.eclipse.dltk.core.builder.ISourceLineTracker;
import org.eclipse.dltk.tcl.ast.ComplexString;
import org.eclipse.dltk.tcl.ast.Script;
import org.eclipse.dltk.tcl.ast.StringArgument;
import org.eclipse.dltk.tcl.ast.Substitution;
import org.eclipse.dltk.tcl.ast.TclArgument;
import org.eclipse.dltk.tcl.ast.TclArgumentList;
import org.eclipse.dltk.tcl.ast.TclCodeModel;
import org.eclipse.dltk.tcl.ast.TclCommand;
import org.eclipse.dltk.tcl.ast.TclModule;
import org.eclipse.dltk.tcl.ast.TclModuleDeclaration;
import org.eclipse.dltk.tcl.ast.TclStatement;
import org.eclipse.dltk.tcl.ast.VariableReference;
import org.eclipse.dltk.tcl.ast.expressions.TclBlockExpression;
import org.eclipse.dltk.tcl.ast.expressions.TclExecuteExpression;
import org.eclipse.dltk.tcl.core.ITclCommandDetector;
import org.eclipse.dltk.tcl.core.ITclCommandDetectorExtension;
import org.eclipse.dltk.tcl.core.ITclCommandProcessor;
import org.eclipse.dltk.tcl.core.ITclParser;
import org.eclipse.dltk.tcl.core.ITclSourceParser;
import org.eclipse.dltk.tcl.core.TclNature;
import org.eclipse.dltk.tcl.core.TclParseUtil;
import org.eclipse.dltk.tcl.core.TclPlugin;
import org.eclipse.dltk.tcl.core.ITclCommandDetector.CommandInfo;
import org.eclipse.dltk.tcl.core.ast.TclAdvancedExecuteExpression;
import org.eclipse.dltk.tcl.internal.parser.ext.CommandManager;
import org.eclipse.dltk.tcl.parser.TclErrorCollector;
import org.eclipse.dltk.tcl.parser.TclParser;
import org.eclipse.dltk.tcl.parser.definitions.DefinitionManager;
import org.eclipse.dltk.tcl.parser.definitions.NamespaceScopeProcessor;
import org.eclipse.dltk.tcl.parser.printer.SimpleCodePrinter;
import org.eclipse.dltk.utils.TextUtils;
import org.eclipse.emf.common.util.EList;
public class NewTclSourceParser extends AbstractSourceParser implements
ITclParser, ISourceParser, ISourceParserExtension, ITclSourceParser {
private IProblemReporter problemReporter;
private String fileName;
boolean useProcessors = true;
private boolean useDetectors = true;
private TclModuleDeclaration moduleDeclaration;
// private TclModule tclModule;
private ISourceLineTracker tracker;
private Set<ASTNode> processedForContentNodes = new HashSet<ASTNode>();
private NamespaceScopeProcessor coreProcessor = DefinitionManager
.getInstance().createProcessor();;
public ModuleDeclaration parse(String fileName, TclModule tclModule,
IProblemReporter reporter) {
processedForContentNodes.clear();
initDetectors();
// this.tclModule = tclModule;
// TclCodeModel model = this.tclModule.getCodeModel();
this.tracker = createLineTracker(tclModule);
this.problemReporter = reporter;
this.fileName = fileName;
this.moduleDeclaration = new TclModuleDeclaration(tclModule.getSize());
this.moduleDeclaration.setTclModule(tclModule);
this.parse(tclModule, moduleDeclaration);
return moduleDeclaration;
}
public static ISourceLineTracker createLineTracker(TclModule tclModule) {
TclCodeModel model = tclModule.getCodeModel();
EList<Integer> list = model.getLineOffsets();
int[] offsets = new int[list.size()];
for (int i = 0; i < list.size(); i++) {
offsets[i] = list.get(i);
}
EList<String> delimeters = model.getDelimeters();
String[] delimetersAsArray = delimeters.toArray(new String[delimeters
.size()]);
return new TextUtils.DefaultSourceLineTracker(tclModule.getSize(),
offsets, delimetersAsArray);
}
private void initDetectors() {
if (detectors == null) {
detectors = CommandManager.getInstance().getDetectors();
}
}
private ITclCommandProcessor localProcessor = new ITclCommandProcessor() {
public ASTNode process(TclStatement st, ITclParser parser,
ASTNode parent) {
if (parent != null) {
TclParseUtil.addToDeclaration(parent, st);
// Re process internal blocks.
}
return st;
}
public void setCurrentASTTree(ModuleDeclaration module) {
}
public void setDetectedParameter(Object parameter) {
}
};
private ITclCommandDetector[] detectors;
private int globalOffset;
protected void parse(TclModule module, ASTNode decl) {
processedForContentNodes.clear();
initDetectors();
List<TclCommand> commands = module.getStatements();
processStatements(decl, commands);
}
private void processStatements(ASTNode decl, List<TclCommand> commands) {
for (Iterator<TclCommand> iter = commands.iterator(); iter.hasNext();) {
TclCommand command = iter.next();
// Command handling
TclStatement st = convertToAST(command);
if (st == null) {
continue; // could be null on errors in source code
}
runStatementProcessor(decl, st);
}
}
private void runStatementProcessor(ASTNode decl, TclStatement st) {
ITclCommandProcessor processor = this.locateProcessor(st, decl);
if (processor != null) {
try {
ASTNode nde = processor.process(st, this, decl);
if (nde == null) {
nde = localProcessor.process(st, this, decl);
}
if (nde != null && this.useDetectors) {
for (int i = 0; i < this.detectors.length; i++) {
if (detectors[i] != null) {
detectors[i].processASTNode(nde);
}
}
}
if (nde != null) {
// Lets store some position information
int globalOffset = this.globalOffset;
boolean userProcessor = this.useProcessors;
boolean useDetectors = this.useDetectors;
nde.traverse(new ASTVisitor() {
public boolean visit(TypeDeclaration s)
throws Exception {
if (processedForContentNodes.add(s)) {
List stats = s.getStatements();
processStatements(s, stats);
}
return true;
}
private void processStatements(ASTNode s, List stats) {
List<ASTNode> statements = new ArrayList<ASTNode>(
stats);
stats.clear();
for (ASTNode node : statements) {
if (node instanceof TclStatement) {
runStatementProcessor(s,
(TclStatement) node);
} else {
stats.add(node);
}
}
}
public boolean visit(MethodDeclaration s)
throws Exception {
if (processedForContentNodes.add(s)) {
List stats = s.getStatements();
processStatements(s, stats);
}
return true;
}
public boolean visit(Expression s) throws Exception {
if (s instanceof Block) {
if (processedForContentNodes.add(s)) {
Block bl = (Block) s;
List stats = bl.getStatements();
processStatements(bl, stats);
}
return true;
} else if (s instanceof TclAdvancedExecuteExpression) {
if (processedForContentNodes.add(s)) {
TclAdvancedExecuteExpression ex = (TclAdvancedExecuteExpression) s;
List stats = ex.getStatements();
processStatements(ex, stats);
}
} else if (s instanceof TclExecuteExpression) {
// This should not happen at all.
}
return true;
}
});
// Restore values
this.useDetectors = useDetectors;
this.useProcessors = userProcessor;
this.globalOffset = globalOffset;
}
} catch (Exception e) {
TclPlugin.error(e);
}
}
}
private TclStatement convertToAST(TclCommand command) {
List<ASTNode> expressions = new ArrayList<ASTNode>();
expressions.add(convertToAST(command.getName()));
for (TclArgument arg : command.getArguments()) {
expressions.add(convertToAST(arg));
}
return new TclStatement(expressions);
}
private ASTNode convertToAST(TclArgument arg) {
if (arg instanceof StringArgument) {
// Simple absolute or relative source'ing.
StringArgument argument = (StringArgument) arg;
String value = argument.getValue();
return stringToAST(argument, value);
} else if (arg instanceof ComplexString) {
ComplexString carg = (ComplexString) arg;
return stringToAST(carg, SimpleCodePrinter.getArgumentString(carg,
carg.getStart()));
} else if (arg instanceof Script) {
Script st = (Script) arg;
EList<TclCommand> eList = st.getCommands();
Block block = new Block(st.getStart(), st.getEnd());
for (TclCommand tclArgument : eList) {
TclStatement stat = convertToAST(tclArgument);
block.addStatement(stat);
}
return block;
} else if (arg instanceof VariableReference) {
VariableReference variableReference = (VariableReference) arg;
String content = SimpleCodePrinter.getArgumentString(
variableReference, variableReference.getStart());
return new SimpleReference(arg.getStart(), arg.getEnd(), content);
} else if (arg instanceof Substitution) {
Substitution st = (Substitution) arg;
EList<TclCommand> eList = st.getCommands();
TclAdvancedExecuteExpression block = new TclAdvancedExecuteExpression(
st.getStart(), st.getEnd());
for (TclCommand cmd : eList) {
TclStatement stat = convertToAST(cmd);
block.addStatement(stat);
}
// block.acceptStatements(exprs);
return block;
} else if (arg instanceof TclArgumentList) {
TclArgumentList st = (TclArgumentList) arg;
String str = SimpleCodePrinter.getArgumentString(st, st.getStart());
return stringToAST(st, str);
}
throw new RuntimeException(
"TODO: Not all cases are matched in TCL Parser AST converter");
}
private ASTNode stringToAST(TclArgument argument, String value) {
int slen = value.length();
int end = argument.getEnd();
int start = argument.getStart();
if (slen >= 2
&& value.charAt(0) == '{'
&& (value.charAt(slen - 1) == '}' || (moduleDeclaration != null && end == moduleDeclaration
.sourceEnd()))) {
// This is block argument
TclBlockExpression bl = new TclBlockExpression(start, end, value);
bl.setProcessedArgument(argument);
return bl;
} else if (slen >= 2
&& value.charAt(0) == '"'
&& (value.charAt(slen - 1) == '"' || end == moduleDeclaration
.sourceEnd())) {
// This is string literal
return new StringLiteral(start, end, value);
} else {
int len = end - start;
if (value.length() > len) {
value = value.substring(0, len);
}
// Simple reference
return new SimpleReference(start, end, value);
}
}
private ITclCommandProcessor locateProcessor(TclStatement command,
ASTNode decl) {
if (this.useProcessors == false) {
return localProcessor;
}
if (command != null && command.getCount() > 0) {
Expression expr = command.getAt(0);
if (!(expr instanceof SimpleReference)) {
return localProcessor;
}
String name = ((SimpleReference) expr).getName();
if (name.startsWith("::")) {
name = name.substring(2);
}
ITclCommandProcessor processor = CommandManager.getInstance()
.getProcessor(name);
if (processor == null) {
// advanced command detection.
if (this.useDetectors) {
for (int i = 0; i < detectors.length; i++) {
if (detectors[i] == null) {
continue;
}
if (detectors[i] instanceof ITclCommandDetectorExtension) {
((ITclCommandDetectorExtension) detectors[i])
.setBuildRuntimeModelFlag(false);
}
CommandInfo commandName = detectors[i].detectCommand(
command, this.moduleDeclaration, decl);
if (commandName != null) {
processor = CommandManager.getInstance()
.getProcessor(commandName.commandName);
if (processor != null) {
processor
.setDetectedParameter(commandName.parameter);
}
break;
}
}
}
}
if (processor != null) {
processor.setCurrentASTTree(this.moduleDeclaration);
return processor;
}
}
return this.localProcessor;
}
public IProblemReporter getProblemReporter() {
return this.problemReporter;
}
public String getFileName() {
return this.fileName;
}
public int getStartPos() {
return 0;
}
public void setProcessorsState(boolean state) {
this.useProcessors = state;
}
public void setUseDetectors(boolean b) {
this.useDetectors = false;
}
public ISourceLineTracker getCodeModel() {
return tracker;
}
/**
* Assume parsing are in same module.
*/
public void parse(String content, int offset, ASTNode parent) {
PerformanceNode p = RuntimePerformanceMonitor.begin();
initDetectors();
processedForContentNodes.clear();
TclParser newParser = new TclParser();
TclErrorCollector collector = null;
if (problemReporter != null) {
collector = new TclErrorCollector();
}
newParser.setGlobalOffset(offset);
List<TclCommand> module = newParser.parse(content, collector,
coreProcessor);
if (problemReporter != null) {
collector.reportAll(problemReporter, tracker);
}
processStatements(parent, module);
p.done(TclNature.NATURE_ID, "New tcl parser: Parse of code", content
.length());
}
public ModuleDeclaration parse(IModuleSource input,
final IProblemReporter reporter) {
PerformanceNode node = RuntimePerformanceMonitor.begin();
processedForContentNodes.clear();
this.problemReporter = reporter;
TclParser newParser = new TclParser();
TclErrorCollector collector = null;
if (reporter != null) {
collector = new TclErrorCollector();
}
newParser.setGlobalOffset(globalOffset);
String content = input.getSourceContents();
TclModule module = newParser.parseModule(content, collector,
coreProcessor);
// TODO: Add error passing to reporter here.
ModuleDeclaration result = parse(input.getFileName(), module, reporter);
if (collector != null) {
collector.reportAll(reporter, tracker);
}
node.done(TclNature.NATURE_ID, "new tcl source parser:time", content
.length());
return result;
}
public void setFlags(int flags) {
}
public void setOffset(int offset) {
this.globalOffset = offset;
}
public void parse(Script script, Block bll) {
PerformanceNode p = RuntimePerformanceMonitor.begin();
processedForContentNodes.clear();
processStatements(bll, script.getCommands());
p.done(TclNature.NATURE_ID, "New tcl parser: Parse of block", 0);
}
}