package freeboogie.ast.utils;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.math.BigInteger;
import java.util.HashMap;
import freeboogie.ast.*;
import freeboogie.util.Err;
/**
* Prints AST nodes in a readable (and parseable) way.
*
* @author rgrig
* @author reviewed by TODO
*/
@SuppressWarnings("unused") // lots of unused parameters
public class PrettyPrinter extends Transformer {
private static final int indent = 2; // indentation spaces
private Writer writer; // where the output is sent
/**
* The nesting level:
* Should be zero when I start and when I finish printing.
*/
private int indentLevel;
private int skipVar; // if >0 then skip "var "
// ready made strings to be printed for enums
private static final HashMap<AssertAssumeCmd.CmdType,String> cmdRep
= new HashMap<AssertAssumeCmd.CmdType,String>(5);
private static final HashMap<AtomLit.AtomType,String> atomRep
= new HashMap<AtomLit.AtomType,String>(5);
private static final HashMap<AtomQuant.QuantType,String> quantRep
= new HashMap<AtomQuant.QuantType,String>(5);
private static final HashMap<BinaryOp.Op,String> binRep
= new HashMap<BinaryOp.Op,String>(29);
private static final HashMap<PrimitiveType.Ptype,String> typeRep
= new HashMap<PrimitiveType.Ptype,String>(11);
private static final HashMap<Specification.SpecType,String> specRep
= new HashMap<Specification.SpecType,String>(5);
private static final HashMap<UnaryOp.Op,String> unRep
= new HashMap<UnaryOp.Op,String>(5);
static {
cmdRep.put(AssertAssumeCmd.CmdType.ASSERT, "assert ");
cmdRep.put(AssertAssumeCmd.CmdType.ASSUME, "assume ");
atomRep.put(AtomLit.AtomType.FALSE, "false");
atomRep.put(AtomLit.AtomType.TRUE, "true");
atomRep.put(AtomLit.AtomType.NULL, "null");
quantRep.put(AtomQuant.QuantType.EXISTS, "exists ");
quantRep.put(AtomQuant.QuantType.FORALL, "forall ");
binRep.put(BinaryOp.Op.AND, " && ");
binRep.put(BinaryOp.Op.DIV, " / ");
binRep.put(BinaryOp.Op.EQ, " == ");
binRep.put(BinaryOp.Op.EQUIV, " <==> ");
binRep.put(BinaryOp.Op.GE, " >= ");
binRep.put(BinaryOp.Op.GT, " > ");
binRep.put(BinaryOp.Op.IMPLIES, " ==> ");
binRep.put(BinaryOp.Op.LE, " <= ");
binRep.put(BinaryOp.Op.LT, " < ");
binRep.put(BinaryOp.Op.MINUS, " - ");
binRep.put(BinaryOp.Op.MOD, " % ");
binRep.put(BinaryOp.Op.MUL, " * ");
binRep.put(BinaryOp.Op.NEQ, " != ");
binRep.put(BinaryOp.Op.OR, " || ");
binRep.put(BinaryOp.Op.PLUS, " + ");
binRep.put(BinaryOp.Op.SUBTYPE, " <: ");
typeRep.put(PrimitiveType.Ptype.ANY, "any");
typeRep.put(PrimitiveType.Ptype.BOOL, "bool");
typeRep.put(PrimitiveType.Ptype.INT, "int");
typeRep.put(PrimitiveType.Ptype.NAME, "name");
typeRep.put(PrimitiveType.Ptype.REF, "ref");
specRep.put(Specification.SpecType.ENSURES, "ensures ");
specRep.put(Specification.SpecType.MODIFIES, "modifies ");
specRep.put(Specification.SpecType.REQUIRES, "requires ");
unRep.put(UnaryOp.Op.MINUS, "-");
unRep.put(UnaryOp.Op.NOT, "!");
}
/**
* Initialize the pretty printer with a writer.
* @param w the writer
*/
public PrettyPrinter(Writer w) {
assert w != null;
writer = w;
indentLevel = 0;
skipVar = 0;
}
/** Swallow exceptions. */
private void say(String s) {
try {
writer.write(s);
} catch (IOException e) {
Err.help("Can't pretty print. Nevermind.");
}
}
/** Send a newline to the writer. */
private void nl() {
say("\n"); // TODO: handle Windows?
for (int i = indent * indentLevel; i > 0; --i) say(" ");
}
/** End command. */
private void semi() {
say(";"); nl();
}
// === the visiting methods ===
@Override
public void see(ArrayType arrayType, Type rowType, Type colType, Type elemType) {
say("[");
rowType.eval(this);
if (colType != null) {
say(", ");
colType.eval(this);
}
say("]");
elemType.eval(this);
}
@Override
public void see(AssertAssumeCmd assertAssumeCmd, AssertAssumeCmd.CmdType type, Expr expr) {
say(cmdRep.get(type));
expr.eval(this);
semi();
}
@Override
public void see(AssignmentCmd assignmentCmd, Expr lhs, Expr rhs) {
lhs.eval(this);
say(" := ");
rhs.eval(this);
semi();
}
@Override
public void see(AtomCast atomCast, Expr e, Type type) {
say("cast(");
e.eval(this);
say(", ");
type.eval(this);
say(")");
}
@Override
public void see(AtomFun atomFun, String function, Exprs args) {
say(function);
say("(");
if (args != null) args.eval(this);
say(")");
}
@Override
public void see(AtomId atomId, String id) {
say(id);
}
@Override
public void see(AtomIdx atomIdx, Atom atom, Index idx) {
atom.eval(this);
idx.eval(this);
}
@Override
public void see(AtomLit atomLit, AtomLit.AtomType val) {
say(atomRep.get(val));
}
@Override
public void see(AtomNum atomNum, BigInteger val) {
say(val.toString());
}
@Override
public void see(AtomOld atomOld, Expr e) {
say("old(");
e.eval(this);
say(")");
}
@Override
public void see(AtomQuant atomQuant, AtomQuant.QuantType quant, Declaration vars, Trigger trig, Expr e) {
++skipVar;
say("(");
say(quantRep.get(quant));
vars.eval(this);
say(" :: ");
if (trig != null) trig.eval(this);
e.eval(this);
say(")");
--skipVar;
}
@Override
public void see(Axiom axiom, Expr expr, Declaration tail) {
say("axiom ");
expr.eval(this);
semi();
if (tail != null) tail.eval(this);
}
@Override
public void see(BinaryOp binaryOp, BinaryOp.Op op, Expr left, Expr right) {
say("(");
left.eval(this);
say(binRep.get(op));
right.eval(this);
say(")");
}
@Override
public void see(Block block, String name, Commands cmds, Identifiers succ, Block tail) {
say(name);
say(":");
++indentLevel; nl();
if (cmds != null) cmds.eval(this);
if (succ == null) {
say("return");
} else {
say("goto ");
succ.eval(this);
}
semi();
--indentLevel; nl();
if (tail != null) tail.eval(this);
}
@Override
public void see(Body body, Declaration vars, Block blocks) {
say(" {");
++indentLevel; nl();
if (vars != null) vars.eval(this);
if (blocks != null) blocks.eval(this);
--indentLevel; nl();
say("}");
nl();
}
@Override
public void see(CallCmd callCmd, String function, Identifiers results, Exprs args) {
say("call ");
if (results != null) {
results.eval(this);
say(" := ");
}
say(function);
say("(");
if (args != null) args.eval(this);
say(");");
nl();
}
@Override
public void see(Commands commands, Command cmd, Commands tail) {
cmd.eval(this);
if (tail != null) tail.eval(this);
}
@Override
public void see(ConstDecl constDecl, String id, Type type, Declaration tail) {
say("const ");
say(id);
say(" : ");
type.eval(this);
semi();
if (tail != null) tail.eval(this);
}
@Override
public void see(DepType depType, Type type, Expr pred) {
type.eval(this);
say(" where ");
pred.eval(this);
}
@Override
public void see(Exprs exprs, Expr expr, Exprs tail) {
expr.eval(this);
if (tail != null) {
say(", ");
tail.eval(this);
}
}
@Override
public void see(Function function, Signature sig, Declaration tail) {
say("function ");
sig.eval(this);
semi();
if (tail != null) tail.eval(this);
}
@Override
public void see(GenericType genericType, Type param, Type type) {
say("<");
param.eval(this);
say(">");
type.eval(this);
}
@Override
public void see(HavocCmd havocCmd, AtomId id) {
say("havoc ");
id.eval(this);
semi();
}
@Override
public void see(Identifiers identifiers, AtomId id, Identifiers tail) {
id.eval(this);
if (tail != null) {
say(", ");
tail.eval(this);
}
}
@Override
public void see(Implementation implementation, Signature sig, Body body, Declaration tail) {
say("implementation ");
sig.eval(this);
body.eval(this);
nl();
if (tail != null) tail.eval(this);
}
@Override
public void see(Index index, Expr a, Expr b) {
say("[");
a.eval(this);
if (b != null) {
say(", ");
b.eval(this);
}
say("]");
}
@Override
public void see(PrimitiveType primitiveType, PrimitiveType.Ptype ptype) {
say(typeRep.get(ptype));
}
@Override
public void see(Procedure procedure, Signature sig, Specification spec, Declaration tail) {
say("procedure ");
sig.eval(this);
say(";");
if (spec != null) {
++indentLevel; nl();
spec.eval(this);
--indentLevel; nl();
}
nl();
if (tail != null) tail.eval(this);
}
@Override
public void see(Signature signature, String name, Declaration args, Declaration results) {
++skipVar;
say(name);
say("(");
if (args != null) args.eval(this);
say(")");
if (results != null) {
say(" returns (");
results.eval(this);
say(")");
}
--skipVar;
}
@Override
public void see(Specification specification, Specification.SpecType type, Expr expr, boolean free, Specification tail) {
if (free) say("free ");
say(specRep.get(type));
expr.eval(this);
semi();
if (tail != null) tail.eval(this);
}
@Override
public void see(TupleType tupleType, Type type, TupleType tail) {
type.eval(this);
if (tail != null) {
say(", ");
tail.eval(this);
}
}
@Override
public void see(TypeDecl typeDecl, String name, Declaration tail) {
say("type ");
say(name);
semi();
tail.eval(this);
}
@Override
public void see(UnaryOp unaryOp, UnaryOp.Op op, Expr e) {
say(unRep.get(op));
e.eval(this);
}
@Override
public void see(UserType userType, String name) {
say(name);
}
@Override
public void see(VariableDecl variableDecl, String name, Type type, Declaration tail) {
if (skipVar==0) say("var ");
if (name != null) {
say(name);
say(" : ");
}
type.eval(this);
if (skipVar>0) {
if (tail != null) say(", ");
} else semi();
if (tail != null) tail.eval(this);
}
@Override
public void see(Trigger trigger, String label, Exprs exprs, Trigger tail) {
say("{");
if (label != null) {
say(":");
say(label);
say(" ");
}
if (exprs != null) exprs.eval(this);
say("} ");
if (tail != null) tail.eval(this);
}
}