package de.skuzzle.polly.core.parser.ast.visitor; import java.io.PrintStream; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import de.skuzzle.polly.core.parser.Position; import de.skuzzle.polly.core.parser.ast.Identifier; import de.skuzzle.polly.core.parser.ast.Node; 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.Braced; 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.OperatorCall; import de.skuzzle.polly.core.parser.ast.expressions.Problem; import de.skuzzle.polly.core.parser.ast.expressions.VarAccess; import de.skuzzle.polly.core.parser.ast.expressions.literals.ChannelLiteral; 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.HelpLiteral; 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.LiteralFormatter; import de.skuzzle.polly.core.parser.ast.expressions.literals.NumberLiteral; import de.skuzzle.polly.core.parser.ast.expressions.literals.ProductLiteral; import de.skuzzle.polly.core.parser.ast.expressions.literals.StringLiteral; import de.skuzzle.polly.core.parser.ast.expressions.literals.TimespanLiteral; import de.skuzzle.polly.core.parser.ast.expressions.literals.UserLiteral; import de.skuzzle.polly.core.parser.util.ASTDotBuilder; public class ExpASTVisualizer extends DepthFirstVisitor { private ASTDotBuilder dotBuilder; private final LiteralFormatter formatter; public ExpASTVisualizer() { this.formatter = new LiteralFormatter() { @Override public String formatUser(UserLiteral user) { return DEFAULT.formatUser(user); } @Override public String formatTimespan(TimespanLiteral timespan) { return DEFAULT.formatTimespan(timespan); } @Override public String formatString(StringLiteral string) { return string.getValue(); } @Override public String formatNumberLiteral(NumberLiteral number) { return DEFAULT.formatNumberLiteral(number); } @Override public String formatList(ListLiteral listLiteral) { return DEFAULT.formatList(listLiteral); } @Override public String formatFunction(FunctionLiteral functionLiteral) { return "\\" + DEFAULT.formatFunction(functionLiteral); } @Override public String formatDate(DateLiteral date) { return DEFAULT.formatDate(date); } @Override public String formatChannel(ChannelLiteral channel) { return DEFAULT.formatChannel(channel); } @Override public String formatHelp(HelpLiteral helpLiteral) { return DEFAULT.formatHelp(helpLiteral); } }; } public void visualize(Node root, PrintStream out, Namespace ns) throws ASTTraversalException { this.dotBuilder = new ASTDotBuilder(out); //this.dotBuilder.printNameSpace(ns); root.visit(this); this.dotBuilder.finish(); } @Override public boolean visit(Root node) throws ASTTraversalException { switch (this.before(node)) { case SKIP: return true; case ABORT: return false; } this.dotBuilder.printNode(node, "Root", node.getPosition().toString()); this.dotBuilder.printNode(node.getCommand(), "Name: " + node.getCommand(), node.getCommand().getPosition().toString()); this.dotBuilder.printEdge(node, node.getCommand(), "command"); for (final Expression exp : node.getExpressions()) { if (!exp.visit(this)) { return false; } this.dotBuilder.printEdge(node, exp, Unparser.toString(exp, this.formatter)); } for (final Directive dir : node.getDirectives().values()) { if (!dir.visit(this)) { return false; } this.dotBuilder.printEdge(node, dir, "Directive"); } return this.after(node) == CONTINUE; } @Override public int after(DelayDirective node) throws ASTTraversalException { this.dotBuilder.printNode(node, "Delay Directive"); this.dotBuilder.printEdge(node, node.getTargetTime(), "target time"); return CONTINUE; } @Override public boolean visit(Call node) throws ASTTraversalException { switch (this.before(node)) { case SKIP: return true; case ABORT: return false; } this.dotBuilder.printExpression("Call", node); if (!node.getLhs().visit(this)) { return false; } if (!node.getRhs().visit(this)) { return false; } this.dotBuilder.printEdge(node, node.getLhs(), Unparser.toString(node.getLhs(), this.formatter)); this.dotBuilder.printEdge(node, node.getRhs(), Unparser.toString(node.getRhs(), this.formatter)); 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; } this.dotBuilder.printExpression("OpCall: " + node.getOperator().getId(), node); if (!node.getLhs().visit(this)) { return false; } if (!node.getRhs().visit(this)) { return false; } this.dotBuilder.printEdge(node, node.getLhs(), Unparser.toString(node.getLhs(), this.formatter)); this.dotBuilder.printEdge(node, node.getRhs(), Unparser.toString(node.getRhs(), this.formatter)); 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; } this.dotBuilder.printExpression("List", node); int i = 0; for (final Expression exp : node.getContent()) { if (!exp.visit(this)) { return false; } this.dotBuilder.printEdge(node, exp, "" + (i++)); } return this.after(node) == CONTINUE; } @Override public boolean visit(Literal node) throws ASTTraversalException { switch (this.before(node)) { case SKIP: return true; case ABORT: return false; } this.dotBuilder.printExpression(node.toString(), node); return this.after(node) == CONTINUE; } @Override public boolean visit(ProductLiteral node) throws ASTTraversalException { switch (this.before(node)) { case SKIP: return true; case ABORT: return false; } this.dotBuilder.printExpression("Product", node); int i = 0; for (final Expression exp : node.getContent()) { if (!exp.visit(this)) { return false; } this.dotBuilder.printEdge(node, exp, "" + (i++)); } 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.dotBuilder.printFunction(node); for (final Declaration param : node.getFormal()) { if (!param.visit(this)) { return false; } this.dotBuilder.printEdge(node, param, "param"); } if (!node.getBody().visit(this)) { return false; } this.dotBuilder.printEdge(node, node.getBody(), "body"); for (final Declaration param : node.getFormal()) { for (final VarAccess va : param.getUsage()) { this.dotBuilder.printUsage(va, param); } } 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; } this.dotBuilder.printExpression("Assignment", node); if (!node.getExpression().visit(this)) { return false; } this.dotBuilder.printNode(node.getName(), "Name: " + node.getName(), node.getName().getPosition().toString()); this.dotBuilder.printEdge(node, node.getExpression(), "expresion"); this.dotBuilder.printEdge(node, node.getName(), "to"); 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; } this.dotBuilder.printExpression("Namespace Access", node); if (!node.getLhs().visit(this)) { return false; } if (!node.getRhs().visit(this)) { return false; } this.dotBuilder.printEdge(node, node.getLhs(), "LHS"); this.dotBuilder.printEdge(node, node.getRhs(), "RHS"); return this.after(node) == CONTINUE; } @Override public int before(VarAccess node) throws ASTTraversalException { final StringBuilder b = new StringBuilder(); final Declaration vd = node.getIdentifier().getDeclaration(); b.append("VarAccess: "); b.append(node.getIdentifier()); if(vd != null) { final List<String> attributes = new ArrayList<String>(5); if (vd.isLocal()) attributes.add("local"); if (vd.isNative()) attributes.add("native"); if (vd.isPublic()) attributes.add("public"); if (!attributes.isEmpty()) { b.append(" ("); final Iterator<String> it = attributes.iterator(); while (it.hasNext()) { b.append(it.next()); if (it.hasNext()) { b.append(", "); } } b.append(")"); } } this.dotBuilder.printExpression(b.toString(), node); if (vd != null && !vd.isLocal() && !vd.isNative() && !(vd.getExpression() instanceof Empty)) { vd.getExpression().visit(this); this.dotBuilder.printEdge(node, vd.getExpression(), ""); } return CONTINUE; } @Override public int before(Problem node) throws ASTTraversalException { this.dotBuilder.printExpression("PROBLEM", node); return CONTINUE; } @Override public int before(Declaration node) throws ASTTraversalException { final StringBuilder b = new StringBuilder(); b.append("Usage (by name):\\n"); for (final VarAccess va : node.getUsage()) { b.append(va.getPosition()); b.append("\\n"); } String pos = node.getPosition() == Position.NONE ? "" : " (" + node.getPosition().toString() + ")"; this.dotBuilder.printNode(node, "Declaration" + pos , "Name: " + node.getName(), "Type: " + node.getType(), b.toString()); return CONTINUE; } @Override public boolean visit(Braced node) throws ASTTraversalException { switch (this.before(node)) { case SKIP: return true; case ABORT: return false; } this.dotBuilder.printExpression("Embraced", node); if (!node.getExpression().visit(this)) { return false; } this.dotBuilder.printEdge(node, node.getExpression(), ""); 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; } this.dotBuilder.printExpression("Delete", node); for (final Identifier id : node.getIdentifiers()) { this.dotBuilder.printNode(id, id.getId()); this.dotBuilder.printEdge(node, id, ""); } 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; } this.dotBuilder.printExpression("Inspect", node); if (!node.getAccess().visit(this)) { return false; } this.dotBuilder.printEdge(node, node.getAccess(), ""); return this.after(node) == CONTINUE; } }