package de.skuzzle.polly.core.parser.ast.visitor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import de.skuzzle.polly.core.parser.Position;
import de.skuzzle.polly.core.parser.ast.ResolvableIdentifier;
import de.skuzzle.polly.core.parser.ast.Root;
import de.skuzzle.polly.core.parser.ast.declarations.Declaration;
import de.skuzzle.polly.core.parser.ast.declarations.Namespace;
import de.skuzzle.polly.core.parser.ast.directives.DelayDirective;
import de.skuzzle.polly.core.parser.ast.directives.Directive;
import de.skuzzle.polly.core.parser.ast.expressions.Assignment;
import de.skuzzle.polly.core.parser.ast.expressions.Call;
import de.skuzzle.polly.core.parser.ast.expressions.Delete;
import de.skuzzle.polly.core.parser.ast.expressions.Empty;
import de.skuzzle.polly.core.parser.ast.expressions.Expression;
import de.skuzzle.polly.core.parser.ast.expressions.Inspect;
import de.skuzzle.polly.core.parser.ast.expressions.NamespaceAccess;
import de.skuzzle.polly.core.parser.ast.expressions.Native;
import de.skuzzle.polly.core.parser.ast.expressions.OperatorCall;
import de.skuzzle.polly.core.parser.ast.expressions.VarAccess;
import de.skuzzle.polly.core.parser.ast.expressions.Delete.DeleteableIdentifier;
import de.skuzzle.polly.core.parser.ast.expressions.literals.DateLiteral;
import de.skuzzle.polly.core.parser.ast.expressions.literals.FunctionLiteral;
import de.skuzzle.polly.core.parser.ast.expressions.literals.ListLiteral;
import de.skuzzle.polly.core.parser.ast.expressions.literals.Literal;
import de.skuzzle.polly.core.parser.ast.expressions.literals.NumberLiteral;
import de.skuzzle.polly.core.parser.ast.expressions.literals.StringLiteral;
import de.skuzzle.polly.core.parser.problems.ProblemReporter;
import de.skuzzle.polly.tools.collections.LinkedStack;
import de.skuzzle.polly.tools.collections.Stack;
public class ExecutionVisitor extends DepthFirstVisitor {
protected final Stack<Literal> stack;
private Namespace nspace;
private final Namespace rootNs;
private final ProblemReporter reporter;
public ExecutionVisitor(Namespace rootNs, Namespace workingNs,
ProblemReporter reporter) {
this.stack = new LinkedStack<Literal>();
this.nspace = workingNs;
this.rootNs = rootNs;
this.reporter = reporter;
}
public ProblemReporter getReporter() {
return this.reporter;
}
/**
* Gets the top most element on the stack. Will throw an exception if there is no
* unique result available.
* @return The result of the execution.
*/
public Literal getSingleResult() {
return this.stack.peek();
}
/**
* Determines whether we have a unique result (e.g. stack has only one element).
*
* @return Whether we have a unique result.
*/
public boolean hasResult() {
return this.stack.size() == 1;
}
/**
* Creates a new sub namespace of the current namespace and sets that new namespace
* as the current one.
*
* @return The created namespace.
*/
public Namespace enter() {
return this.nspace = this.nspace.enter();
}
/**
* Sets the current namespace as the parent of the current namespace.
*
* @return The parent of the former current namespace.
*/
public Namespace leave() {
return this.nspace = this.nspace.getParent();
}
@Override
public boolean visit(Root node) throws ASTTraversalException {
switch (this.before(node)) {
case SKIP: return true;
case ABORT: return false;
}
final List<Literal> results =
new ArrayList<Literal>(node.getExpressions().size());
for (final Expression exp : node.getExpressions()) {
if (!exp.visit(this)) {
return false;
}
results.add(this.stack.pop());
}
for (final Directive dir : node.getDirectives().values()) {
if (!dir.visit(this)) {
return false;
}
}
node.setResults(results);
return this.after(node) == CONTINUE;
}
@Override
public int after(DelayDirective node) throws ASTTraversalException {
// there must be a timespan or date literal on the stack
final DateLiteral date = (DateLiteral) this.stack.pop();
node.setResult(date);
return CONTINUE;
}
@Override
public boolean visit(Literal node) throws ASTTraversalException {
switch (this.before(node)) {
case SKIP: return true;
case ABORT: return false;
}
this.stack.push(node);
return this.after(node) == CONTINUE;
}
@Override
public boolean visit(FunctionLiteral node) throws ASTTraversalException {
switch (this.before(node)) {
case SKIP: return true;
case ABORT: return false;
}
this.stack.push(node);
return this.after(node) == CONTINUE;
}
@Override
public boolean visit(ListLiteral node) throws ASTTraversalException {
switch (this.before(node)) {
case SKIP: return true;
case ABORT: return false;
}
// create collection of executed list content
final List<Expression> executed = new ArrayList<Expression>(
node.getContent().size());
for (final Expression exp : node.getContent()) {
// places executed expression on the stack
if (!exp.visit(this)) {
return false;
}
executed.add(this.stack.pop());
}
final ListLiteral result = new ListLiteral(node.getPosition(), executed);
result.setUnique(node.getUnique());
this.stack.push(result);
return this.after(node) == CONTINUE;
}
@Override
public boolean visit(NamespaceAccess node) throws ASTTraversalException {
switch (this.before(node)) {
case SKIP: return true;
case ABORT: return false;
}
// store current ns and switch to new one
final Namespace backup = this.nspace;
// get namespace which is accessed here and has the current namespace as
// parent.
// lhs of access is guaranteed to be a VarAccess
final VarAccess va = (VarAccess) node.getLhs();
this.nspace = Namespace.forName(va.getIdentifier()).derive(this.nspace);
// execute expression and restore old namespace
if (!node.getRhs().visit(this)) {
return false;
}
this.nspace = backup;
return this.after(node) == CONTINUE;
}
@Override
public boolean visit(Native node) throws ASTTraversalException {
switch (this.before(node)) {
case SKIP: return true;
case ABORT: return false;
}
node.execute(this.stack, this.nspace, this);
return this.after(node) == CONTINUE;
}
@Override
public boolean visit(Assignment node) throws ASTTraversalException {
switch (this.before(node)) {
case SKIP: return true;
case ABORT: return false;
}
// result of assignment is the result of the assigned expression
if (!node.getExpression().visit(this)) {
return false;
}
final Declaration vd = new Declaration(node.getName().getPosition(),
node.getName(), this.stack.peek());
vd.setPublic(node.isPublic());
vd.setTemp(node.isTemp());
if (vd.isPublic()) {
Namespace.declarePublic(vd);
} else {
this.rootNs.declare(vd);
}
return this.after(node) == CONTINUE;
}
@Override
public boolean visit(OperatorCall node) throws ASTTraversalException {
switch (this.before(node)) {
case SKIP: return true;
case ABORT: return false;
}
if (!this.visit((Call) node)) {
return false;
}
return this.after(node) == CONTINUE;
}
@Override
public boolean visit(Call node) throws ASTTraversalException {
switch (this.before(node)) {
case SKIP: return true;
case ABORT: return false;
}
// this will push the function call onto the stack
if (!node.getLhs().visit(this)) {
return false;
}
final FunctionLiteral func = (FunctionLiteral) this.stack.pop();
this.enter();
final Iterator<Expression> actualIt = node.getRhs().getContent().iterator();
final Iterator<Declaration> formalIt = func.getFormal().iterator();
while (formalIt.hasNext()) {
final Declaration formal = formalIt.next();
final Expression actual = actualIt.next();
// execute actual parameter
if (!actual.visit(this)) {
return false;
}
// declare result as local variable for this call
final Expression result = this.stack.pop();
final Declaration local =
new Declaration(actual.getPosition(), formal.getName(), result, true);
this.nspace.declare(local);
}
if (!func.getBody().visit(this)) {
return false;
}
this.leave();
return this.after(node) == CONTINUE;
}
@Override
public boolean visit(VarAccess node) throws ASTTraversalException {
switch (this.before(node)) {
case SKIP: return true;
case ABORT: return false;
}
Declaration vd = node.getIdentifier().getDeclaration();
if (vd == null || vd.isLocal() || vd.getExpression() instanceof Empty) {
vd = this.nspace.tryResolve(
node.getIdentifier(),
node.getUnique());
}
if (!vd.getExpression().visit(this)) {
return false;
}
return this.after(node) == CONTINUE;
}
@Override
public boolean visit(Delete node) throws ASTTraversalException {
switch (this.before(node)) {
case SKIP: return true;
case ABORT: return false;
}
int i = 0;
for (final DeleteableIdentifier id: node.getIdentifiers()) {
if (id.isGlobal()) {
i += Namespace.deletePublic(id);
} else {
i += this.rootNs.delete(id);
}
}
this.stack.push(new NumberLiteral(Position.NONE, i));
return this.after(node) == CONTINUE;
}
@Override
public boolean visit(Inspect node) throws ASTTraversalException {
switch (this.before(node)) {
case SKIP: return true;
case ABORT: return false;
}
Namespace target = null;
ResolvableIdentifier var = null;
if (node.getAccess() instanceof VarAccess) {
final VarAccess va = (VarAccess) node.getAccess();
target = this.nspace;
var = va.getIdentifier();
} else if (node.getAccess() instanceof NamespaceAccess) {
final NamespaceAccess nsa = (NamespaceAccess) node.getAccess();
final VarAccess nsName = (VarAccess) nsa.getLhs();
var = ((VarAccess) nsa.getRhs()).getIdentifier();
target = Namespace.forName(nsName.getIdentifier());
} else {
throw new IllegalStateException("this should not be reachable");
}
final Collection<Declaration> decls = target.lookupAll(var);
final StringBuilder b = new StringBuilder();
for (final Declaration decl : decls) {
if (decl.isPublic()) {
b.append("(public) ");
}
if (decl.isNative()) {
b.append("Native");
} else if (decl.getExpression() instanceof FunctionLiteral) {
b.append("Funktion: " + Unparser.toString(decl.getExpression()));
} else {
b.append("Wert: " + Unparser.toString(decl.getExpression()));
}
b.append(". Type: " + decl.getType().getName());
b.append("\n");
}
this.stack.push(new StringLiteral(node.getPosition(), b.toString()));
return this.after(node) == CONTINUE;
}
}