// Copyright (c) 2011, David J. Pearce (djp@ecs.vuw.ac.nz)
// All rights reserved.
//
// This software may be modified and distributed under the terms
// of the BSD license. See the LICENSE file for details.
package wyc.io;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import wyc.lang.Expr;
import wyc.lang.Stmt;
import wyc.lang.SyntacticType;
import wyc.lang.WhileyFile;
import wycc.util.Pair;
import wyil.lang.*;
/**
* Responsible for "pretty printing" a Whiley File. This is useful for
* formatting Whiley Files. Also, it can be used to programatically generate
* Whiley Files.
*
* @author David J. Pearce
*
*/
public class WhileyFilePrinter {
private PrintStream out;
public WhileyFilePrinter(OutputStream stream) {
try {
this.out = new PrintStream(stream, true, "UTF-8");
} catch(Exception e) {
this.out = new PrintStream(stream);
}
}
public void print(WhileyFile wf) {
for(WhileyFile.Declaration d : wf.declarations) {
print(d);
}
out.flush();
}
public void print(WhileyFile.Declaration decl) {
if(decl instanceof WhileyFile.Import) {
print((WhileyFile.Import)decl);
} else if(decl instanceof WhileyFile.Constant) {
print((WhileyFile.Constant)decl);
} else if(decl instanceof WhileyFile.Type) {
print((WhileyFile.Type)decl);
} else if(decl instanceof WhileyFile.FunctionOrMethodOrProperty) {
print((WhileyFile.FunctionOrMethodOrProperty)decl);
} else {
throw new RuntimeException("Unknown construct encountered: "
+ decl.getClass().getName());
}
}
public void print(WhileyFile.FunctionOrMethodOrProperty fm) {
out.println();
print(fm.modifiers());
if(fm instanceof WhileyFile.Method) {
out.print("method ");
} else {
out.print("function ");
}
out.print(fm.name());
printParameters(fm.parameters);
out.print(" -> ");
printParameters(fm.returns);
for(Expr r : fm.requires) {
out.println();
out.print("requires ");
print(r);
}
for(Expr r : fm.ensures) {
out.println();
out.print("ensures ");
print(r);
}
out.println(":");
print(fm.statements,1);
}
public void print(WhileyFile.Import decl) {
out.print("import ");
if(decl.name != null) {
out.print(decl.name);
out.print(" from ");
}
for(int i=0;i!=decl.filter.size();++i) {
if(i != 0) {
out.print(".");
}
String item = decl.filter.get(i);
if(!item.equals("**")) {
out.print(decl.filter.get(i));
}
}
out.println();
}
public void print(WhileyFile.Constant decl) {
out.println();
out.print("constant ");
out.print(decl.name());
out.print(" is ");
print(decl.constant);
out.println();
}
public void print(WhileyFile.Type decl) {
out.println();
out.print("type ");
out.print(decl.name());
out.print(" is ");
printParameter(decl.parameter,true);
for(Expr invariant : decl.invariant) {
out.print(" where ");
print(invariant);
}
out.println();
}
public void print(List<Stmt> statements, int indent) {
for(Stmt s : statements) {
print(s,indent);
}
}
public void print(Stmt stmt, int indent) {
indent(indent);
if(stmt instanceof Stmt.Assert) {
print((Stmt.Assert) stmt);
} else if(stmt instanceof Stmt.Assign) {
print((Stmt.Assign) stmt);
} else if(stmt instanceof Stmt.Assume) {
print((Stmt.Assume) stmt);
} else if(stmt instanceof Stmt.Break) {
print((Stmt.Break) stmt);
} else if(stmt instanceof Stmt.Continue) {
print((Stmt.Continue) stmt);
} else if(stmt instanceof Stmt.Debug) {
print((Stmt.Debug) stmt);
} else if(stmt instanceof Stmt.DoWhile) {
print((Stmt.DoWhile) stmt, indent);
} else if(stmt instanceof Stmt.IfElse) {
print((Stmt.IfElse) stmt, indent);
} else if(stmt instanceof Stmt.Return) {
print((Stmt.Return) stmt);
} else if(stmt instanceof Stmt.Skip) {
print((Stmt.Skip) stmt);
} else if(stmt instanceof Stmt.Switch) {
print((Stmt.Switch) stmt, indent);
} else if(stmt instanceof Stmt.NamedBlock) {
print((Stmt.NamedBlock) stmt, indent);
} else if(stmt instanceof Stmt.While) {
print((Stmt.While) stmt, indent);
} else if(stmt instanceof Stmt.VariableDeclaration) {
print((Stmt.VariableDeclaration) stmt, indent);
} else if(stmt instanceof Expr.AbstractInvoke) {
print((Expr.AbstractInvoke) stmt);
out.println();
} else {
// should be dead-code
throw new RuntimeException("Unknown statement kind encountered: "
+ stmt.getClass().getName());
}
}
public void print(Stmt.Assert s) {
out.print("assert ");
print(s.expr);
out.println();
}
public void print(Stmt.Assume s) {
out.print("assert ");
print(s.expr);
out.println();
}
public void print(Stmt.Debug s) {
out.print("debug ");
print(s.expr);
out.println();
}
public void print(Stmt.Break s) {
out.println("break");
}
public void print(Stmt.Continue s) {
out.println("break");
}
public void print(Stmt.Skip s) {
out.println("skip");
}
public void print(Stmt.Return s) {
out.print("return");
for(int i=0;i!=s.returns.size();++i) {
if(i != 0) {
out.print(",");
}
out.print(" ");
print(s.returns.get(i));
}
out.println();
}
public void print(Stmt.Assign s) {
for(int i=0;i!=s.lvals.size();++i) {
if(i!=0) {
out.print(", ");
}
print(s.lvals.get(i));
}
out.print(" = ");
for(int i=0;i!=s.rvals.size();++i) {
if(i!=0) {
out.print(", ");
}
print(s.rvals.get(i));
}
out.println();
}
public void print(Stmt.IfElse s, int indent) {
out.print("if ");
print(s.condition);
out.println(":");
print(s.trueBranch,indent+1);
if(!s.falseBranch.isEmpty()) {
indent(indent);
out.println("else:");
print(s.falseBranch, indent+1);
}
}
public void print(Stmt.DoWhile s, int indent) {
out.println("do:");
print(s.body,indent+1);
indent(indent);
// TODO: loop invariant
out.print("while ");
print(s.condition);
out.println();
}
public void print(Stmt.NamedBlock s, int indent) {
out.println(s.name + ":");
print(s.body,indent+1);
}
public void print(Stmt.While s, int indent) {
out.print("while ");
print(s.condition);
boolean firstTime = true;
for(Expr i : s.invariants) {
if(!firstTime) {
out.print(",");
} else {
firstTime = false;
}
out.print(" where ");
print(i);
}
// TODO: loop invariant
out.println(":");
print(s.body,indent+1);
}
public void print(Stmt.Switch s, int indent) {
out.print("switch ");
print(s.expr);
out.println(":");
for(Stmt.Case cas : s.cases) {
indent(indent+1);
boolean firstTime = true;
if(cas.expr.isEmpty()) {
out.print("default");
} else {
out.print("case ");
for(Expr e : cas.expr) {
if(!firstTime) {
out.print(", ");
}
firstTime=false;
print(e);
}
}
out.println(":");
print(cas.stmts,indent+2);
}
}
public void print(Stmt.VariableDeclaration s, int indent) {
printParameter(s.parameter,false);
if(s.expr != null) {
out.print(" = ");
print(s.expr);
}
out.println();
}
public void printWithBrackets(Expr expression, Class<? extends Expr>... matches) {
boolean withBrackets = false;
// First, decide whether brackets are needed or not
for(Class<? extends Expr> match : matches) {
if(match.isInstance(expression)) {
withBrackets = true;
break;
}
}
// Second, print with brackets if needed
if(withBrackets) {
out.print("(");
print(expression);
out.print(")");
} else {
print(expression);
}
}
public void print(Expr expression) {
if (expression instanceof Expr.Constant) {
print ((Expr.Constant) expression);
} else if (expression instanceof Expr.AbstractVariable) {
print ((Expr.AbstractVariable) expression);
} else if (expression instanceof Expr.ConstantAccess) {
print ((Expr.ConstantAccess) expression);
} else if (expression instanceof Expr.ArrayInitialiser) {
print ((Expr.ArrayInitialiser) expression);
} else if (expression instanceof Expr.BinOp) {
print ((Expr.BinOp) expression);
} else if (expression instanceof Expr.Dereference) {
print ((Expr.Dereference) expression);
} else if (expression instanceof Expr.Cast) {
print ((Expr.Cast) expression);
} else if (expression instanceof Expr.IndexOf) {
print ((Expr.IndexOf) expression);
} else if (expression instanceof Expr.UnOp) {
print ((Expr.UnOp) expression);
} else if (expression instanceof Expr.AbstractInvoke) {
print ((Expr.AbstractInvoke) expression);
} else if (expression instanceof Expr.IndirectFunctionCall) {
print ((Expr.IndirectFunctionCall) expression);
} else if (expression instanceof Expr.IndirectMethodCall) {
print ((Expr.IndirectMethodCall) expression);
} else if (expression instanceof Expr.Quantifier) {
print ((Expr.Quantifier) expression);
} else if (expression instanceof Expr.FieldAccess) {
print ((Expr.FieldAccess) expression);
} else if (expression instanceof Expr.Record) {
print ((Expr.Record) expression);
} else if (expression instanceof Expr.AbstractFunctionOrMethod) {
print ((Expr.AbstractFunctionOrMethod) expression);
} else if (expression instanceof Expr.Lambda) {
print ((Expr.Lambda) expression);
} else if (expression instanceof Expr.New) {
print ((Expr.New) expression);
} else if (expression instanceof Expr.TypeVal) {
print ((Expr.TypeVal) expression);
} else {
// should be dead-code
throw new RuntimeException("Unknown expression kind encountered: " + expression.getClass().getName());
}
}
public void print(Expr.Constant c) {
out.print(c.value);
}
public void print(Expr.AbstractVariable v) {
out.print(v);
}
public void print(Expr.ConstantAccess v) {
if(v.qualification != null) {
out.print(v.qualification + "." + v.name);
} else {
out.print(v.name);
}
}
public void print(Expr.ArrayInitialiser e) {
out.print("[");
boolean firstTime = true;
for(Expr i : e.arguments) {
if(!firstTime) {
out.print(", ");
}
firstTime=false;
print(i);
}
out.print("]");
}
public void print(Expr.BinOp e) {
printWithBrackets(e.lhs, Expr.BinOp.class, Expr.Cast.class);
out.print(" ");
out.print(e.op);
out.print(" ");
printWithBrackets(e.rhs, Expr.BinOp.class, Expr.Cast.class);
}
public void print(Expr.Dereference e) {
out.print("*");
print(e.src);
}
public void print(Expr.Cast e) {
out.print("(");
print(e.unresolvedType);
out.print(") ");
printWithBrackets(e.expr,Expr.BinOp.class,Expr.Cast.class);
}
public void print(Expr.IndexOf e) {
print(e.src);
out.print("[");
print(e.index);
out.print("]");
}
public void print(Expr.UnOp e) {
switch(e.op) {
case NEG:
out.print("-");
break;
case NOT:
out.print("!");
break;
case INVERT:
out.print("~");
break;
case ARRAYLENGTH:
out.print("|");
print(e.mhs);
out.print("|");
return;
}
printWithBrackets(e.mhs,Expr.BinOp.class,Expr.Cast.class);
}
public void print(Expr.AbstractInvoke e) {
if(e.qualification != null) {
out.print(e.qualification.toString());
out.print(".");
}
out.print(e.name);
if (!e.lifetimeArguments.isEmpty()) {
out.print("<");
boolean firstTime = true;
for (String lifetime : e.lifetimeArguments) {
if (!firstTime) {
out.print(", ");
}
firstTime = false;
out.print(lifetime);
}
out.print(">");
}
out.print("(");
boolean firstTime = true;
for(Expr i : e.arguments) {
if(!firstTime) {
out.print(", ");
}
firstTime=false;
print(i);
}
out.print(")");
}
public void print(Expr.IndirectFunctionCall e) {
print(e.src);
if (!e.lifetimeArguments.isEmpty()) {
out.print("<");
boolean firstTime = true;
for (String lifetime : e.lifetimeArguments) {
if (!firstTime) {
out.print(", ");
}
firstTime = false;
out.print(lifetime);
}
out.print(">");
}
out.print("(");
boolean firstTime = true;
for(Expr i : e.arguments) {
if(!firstTime) {
out.print(", ");
}
firstTime=false;
print(i);
}
out.print(")");
}
public void print(Expr.IndirectMethodCall e) {
print(e.src);
if (!e.lifetimeArguments.isEmpty()) {
out.print("<");
boolean firstTime = true;
for (String lifetime : e.lifetimeArguments) {
if (!firstTime) {
out.print(", ");
}
firstTime = false;
out.print(lifetime);
}
out.print(">");
}
out.print("(");
boolean firstTime = true;
for(Expr i : e.arguments) {
if(!firstTime) {
out.print(", ");
}
firstTime=false;
print(i);
}
out.print(")");
}
public void print(Expr.Quantifier e) {
switch(e.cop) {
case SOME:
out.print("some ");
break;
case ALL:
out.print("all ");
break;
}
out.print("{ ");
boolean firstTime=true;
for(Pair<String,Expr> src : e.sources) {
if(!firstTime) {
out.print(", ");
}
firstTime=false;
out.print(src.first());
out.print(" in ");
print(src.second());
}
out.print(" | ");
print(e.condition);
out.print(" }");
}
public void print(Expr.FieldAccess e) {
if(e.src instanceof Expr.Dereference) {
printWithBrackets(((Expr.Dereference)e.src).src,Expr.New.class);
out.print("->");
out.print(e.name);
} else {
printWithBrackets(e.src,Expr.New.class);
out.print(".");
out.print(e.name);
}
}
public void print(Expr.Record e) {
out.print("{");
boolean firstTime = true;
for(Map.Entry<String,Expr> i : e.fields.entrySet()) {
if(!firstTime) {
out.print(", ");
}
firstTime=false;
out.print(i.getKey());
out.print(": ");
print(i.getValue());
}
out.print("}");
}
public void print(Expr.AbstractFunctionOrMethod e) {
out.print("&");
out.print(e.name);
if(e.paramTypes != null && e.paramTypes.size() > 0) {
out.print("(");
boolean firstTime = true;
for(SyntacticType t : e.paramTypes) {
if(!firstTime) {
out.print(", ");
}
firstTime=false;
print(t);
}
out.print(")");
}
}
public void print(Expr.Lambda e) {
out.print("&");
if (!e.contextLifetimes.isEmpty()) {
out.print("[");
boolean firstTime = true;
for (String lifetime : e.contextLifetimes) {
if (!firstTime) {
out.print(", ");
}
firstTime = false;
out.print(lifetime);
}
out.print("]");
}
if (!e.lifetimeParameters.isEmpty()) {
out.print("<");
boolean firstTime = true;
for (String lifetime : e.lifetimeParameters) {
if (!firstTime) {
out.print(", ");
}
firstTime = false;
out.print(lifetime);
}
out.print(">");
}
out.print("(");
boolean firstTime = true;
for(WhileyFile.Parameter p : e.parameters) {
if(!firstTime) {
out.print(", ");
}
firstTime=false;
print(p.type);
out.print(" ");
out.print(p.name);
}
out.print(" -> ");
print(e.body);
out.print(")");
}
public void print(Expr.New e) {
out.print("new ");
print(e.expr);
}
public void print(Expr.TypeVal e) {
print(e.unresolvedType);
}
private void printParameters(List<WhileyFile.Parameter> parameters) {
out.print("(");
boolean firstTime = true;
for(int i = 0; i < parameters.size();++i) {
WhileyFile.Parameter p = parameters.get(i);
if(!firstTime) {
out.print(", ");
}
firstTime=false;
printParameter(p,false);
}
out.print(")");
}
private void printParameter(WhileyFile.Parameter parameter, boolean braces) {
braces &= parameter.name == null;
if(braces) {
out.print("(");
}
print(parameter.type);
out.print(" ");
out.print(parameter.name);
if(braces) {
out.print(")");
}
}
public void print(List<Modifier> modifiers) {
for(Modifier m : modifiers) {
out.print(m);
out.print(" ");
}
}
public void print(SyntacticType t) {
if(t instanceof SyntacticType.Any) {
out.print("any");
} else if(t instanceof SyntacticType.Bool) {
out.print("bool");
} else if(t instanceof SyntacticType.Byte) {
out.print("byte");
} else if(t instanceof SyntacticType.Int) {
out.print("int");
} else if(t instanceof SyntacticType.Null) {
out.print("null");
} else if(t instanceof SyntacticType.Void) {
out.print("void");
} else if(t instanceof SyntacticType.Nominal) {
SyntacticType.Nominal nt = (SyntacticType.Nominal) t;
boolean firstTime = true;
for(String name : nt.names) {
if(!firstTime) {
out.print(".");
}
firstTime=false;
out.print(name);
}
} else if(t instanceof SyntacticType.Array) {
out.print("[");
print(((SyntacticType.Array)t).element);
out.print("]");
} else if(t instanceof SyntacticType.FunctionOrMethod) {
SyntacticType.FunctionOrMethod tt = (SyntacticType.FunctionOrMethod) t;
if(t instanceof SyntacticType.Method) {
out.print("method ");
} else {
out.print("function ");
}
if (!tt.contextLifetimes.isEmpty()) {
out.print("[");
boolean firstTime = true;
for (String lifetime : tt.contextLifetimes) {
if (!firstTime) {
out.print(", ");
}
firstTime = false;
out.print(lifetime);
}
out.print("]");
}
if (!tt.lifetimeParameters.isEmpty()) {
out.print("<");
boolean firstTime = true;
for (String lifetime : tt.lifetimeParameters) {
if (!firstTime) {
out.print(", ");
}
firstTime = false;
out.print(lifetime);
}
out.print(">");
}
printParameterTypes(tt.paramTypes);
out.print("->");
printParameterTypes(tt.returnTypes);
} else if(t instanceof SyntacticType.Record) {
SyntacticType.Record tt = (SyntacticType.Record) t;
out.print("{");
boolean firstTime = true;
for(Map.Entry<String, SyntacticType> et : tt.types.entrySet()) {
if(!firstTime) {
out.print(", ");
}
firstTime=false;
print(et.getValue());
out.print(" ");
out.print(et.getKey());
}
if(tt.isOpen) {
out.print(", ...");
}
out.print("}");
} else if(t instanceof SyntacticType.Reference) {
out.print("ref ");
print(((SyntacticType.Reference) t).element);
} else if(t instanceof SyntacticType.Negation) {
out.print("!");
print(((SyntacticType.Negation) t).element);
} else if(t instanceof SyntacticType.Union) {
SyntacticType.Union ut = (SyntacticType.Union) t;
boolean firstTime = true;
for(SyntacticType et : ut.bounds) {
if(!firstTime) {
out.print(" | ");
}
firstTime=false;
print(et);
}
} else if(t instanceof SyntacticType.Intersection) {
SyntacticType.Intersection ut = (SyntacticType.Intersection) t;
boolean firstTime = true;
for(SyntacticType et : ut.bounds) {
if(!firstTime) {
out.print(" & ");
}
firstTime=false;
print(et);
}
} else {
// should be dead-code
throw new RuntimeException("Unknown type kind encountered: " + t.getClass().getName());
}
}
private void printParameterTypes(List<SyntacticType> parameters) {
out.print("(");
boolean firstTime = true;
for(int i = 0; i < parameters.size();++i) {
SyntacticType p = parameters.get(i);
if(!firstTime) {
out.print(", ");
}
firstTime=false;
print(p);
}
out.print(")");
}
public void indent(int level) {
for(int i=0;i!=level;++i) {
out.print(" ");
}
}
}