/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.mmtk.harness.lang;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.PrintStream;
import java.util.Iterator;
import org.mmtk.harness.Harness;
import org.mmtk.harness.lang.ast.AST;
import org.mmtk.harness.lang.ast.Alloc;
import org.mmtk.harness.lang.ast.Assert;
import org.mmtk.harness.lang.ast.Assignment;
import org.mmtk.harness.lang.ast.Call;
import org.mmtk.harness.lang.ast.Constant;
import org.mmtk.harness.lang.ast.Expression;
import org.mmtk.harness.lang.ast.IfStatement;
import org.mmtk.harness.lang.ast.LoadField;
import org.mmtk.harness.lang.ast.LoadNamedField;
import org.mmtk.harness.lang.ast.Method;
import org.mmtk.harness.lang.ast.NormalMethod;
import org.mmtk.harness.lang.ast.Operator;
import org.mmtk.harness.lang.ast.PrintStatement;
import org.mmtk.harness.lang.ast.Return;
import org.mmtk.harness.lang.ast.Sequence;
import org.mmtk.harness.lang.ast.Spawn;
import org.mmtk.harness.lang.ast.Statement;
import org.mmtk.harness.lang.ast.StoreField;
import org.mmtk.harness.lang.ast.StoreNamedField;
import org.mmtk.harness.lang.ast.TypeLiteral;
import org.mmtk.harness.lang.ast.Variable;
import org.mmtk.harness.lang.ast.WhileStatement;
import org.mmtk.harness.lang.parser.MethodTable;
import org.mmtk.harness.lang.parser.Parser;
import org.vmmagic.unboxed.harness.ArchitecturalWord;
/**
* Format an AST back into Harness script-language code
*
* Implemented as a visitor over the AST
*/
public class PrettyPrinter extends Visitor {
private final OutputFormatter fmt;
/**
* Create a pretty printer that sends output to an internal buffer
*/
public PrettyPrinter() {
this(new OutputFormatter());
}
/**
* @param stream Where to send the output
*/
public PrettyPrinter(PrintStream stream) {
this(new OutputFormatter(stream));
}
private PrettyPrinter(OutputFormatter outputFormatter) {
fmt = outputFormatter;
}
private static class OutputFormatter {
private static final int INDENT = 2;
private int indent = 0;
private boolean pendingIndent = false;
private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
private final PrintStream out;
OutputFormatter() {
out = new PrintStream(buffer,true);
}
OutputFormatter(PrintStream out) {
this.out = out;
}
void newline() {
out.printf("%n");
pendingIndent = true;
}
void out(String format, Object... args) {
if (pendingIndent) {
out.printf(margin());
pendingIndent = false;
}
out.printf(format, args);
}
private String margin() {
String indentStr = "";
for (int i=0; i < indent; i++) {
indentStr += " ";
}
return indentStr;
}
void increaseIndent() { indent += INDENT; }
void decreaseIndent() { indent -= INDENT; }
String read() {
return buffer.toString();
}
}
/**
* Visit a method definition
*
* @see org.mmtk.harness.lang.Visitor#visit(org.mmtk.harness.lang.ast.NormalMethod)
*/
@Override
public Object visit(NormalMethod method) {
fmt.out("%s %s(",method.getReturnType(),method.getName());
boolean first = true;
for (Declaration decl : method.getParams()) {
if (first) {
first = false;
} else {
fmt.out(",");
}
decl.accept(this);
}
fmt.out(") {"); fmt.newline();
fmt.increaseIndent();
method.getBody().accept(this);
fmt.decreaseIndent();
fmt.out("%s}",fmt.margin()); fmt.newline();
return null;
}
/**
* Visit a method-call
*
* @see org.mmtk.harness.lang.Visitor#visit(org.mmtk.harness.lang.ast.Call)
*/
@Override
public Object visit(Call call) {
fmt.out("%s(",call.getMethod().getName());
boolean first = true;
for (Expression param : call.getParams()) {
if (!first) {
fmt.out(",");
} else {
first = false;
}
param.accept(this);
}
fmt.out(")");
return null;
}
@Override
public Object visit(Sequence ass) {
for (Statement stmt : ass) {
stmt.accept(this); fmt.newline();
}
return null;
}
@Override
public Object visit(Assignment a) {
fmt.out("%s = ", a.getSymbol().getName());
a.getRhs().accept(this);
fmt.out(";");
return null;
}
@Override
public Object visit(IfStatement conditional) {
String keyword = "if";
Iterator<Expression> condIter = conditional.getConds().iterator();
for (Statement body : conditional.getStmts()) {
if (condIter.hasNext()) {
Expression cond = condIter.next();
fmt.out("%s (", keyword);
cond.accept(this);
fmt.out(") {"); fmt.newline();
keyword = "elif";
} else {
fmt.out("else {"); fmt.newline();
}
fmt.increaseIndent();
body.accept(this);
fmt.decreaseIndent();
fmt.out("} ");
}
return null;
}
@Override
public Object visit(WhileStatement w) {
fmt.out("while (");
w.getCond().accept(this);
fmt.out(") {"); fmt.newline();
fmt.increaseIndent();
w.getBody().accept(this);
fmt.decreaseIndent();
fmt.out("}");
return null;
}
@Override
public Object visit(Operator op) {
fmt.out(" %s ", op.toString());
return null;
}
@Override
public Object visit(LoadField load) {
fmt.out("%s.%s[", load.getObjectSymbol().getName(), load.getFieldType().toString());
load.getIndex().accept(this);
fmt.out("]");
return null;
}
@Override
public Object visit(LoadNamedField load) {
fmt.out("%s.%s", load.getObjectSymbol().getName(), load.getFieldName());
return null;
}
@Override
public Object visit(Constant c) {
fmt.out(c.value.toString());
return null;
}
@Override
public Object visit(Variable var) {
fmt.out("%s", var.getSymbol().getName());
return null;
}
@Override
public Object visit(StoreField store) {
fmt.out("%s.%s[",store.getObjectSymbol().getName(), store.getFieldType().toString());
store.getIndex().accept(this);
fmt.out("] := ");
store.getRhs().accept(this);
fmt.out(";");
return null;
}
@Override
public Object visit(StoreNamedField store) {
fmt.out("%s.%s := ",store.getObjectSymbol().getName(), store.getFieldName());
store.getRhs().accept(this);
fmt.out(";");
return null;
}
@Override
public Object visit(Return ret) {
fmt.out("return");
if (ret.hasReturnValue()) {
fmt.out(" ");
ret.getRhs().accept(this);
}
fmt.out(";");
return null;
}
@Override
public Object visit(Assert ass) {
fmt.out("assert(");
ass.getPredicate().accept(this);
for (Expression expr : ass.getOutputs()) {
fmt.out(",");
expr.accept(this);
}
fmt.out(");");
return null;
}
@Override
public Object visit(Declaration decl) {
fmt.out("%s %s", decl.getType(), decl.getName());
return null;
}
@Override
public Object visit(PrintStatement print) {
fmt.out("print");
String separator = "(";
for (Expression expr : print.getArgs()) {
fmt.out(separator);
separator = ",";
expr.accept(this);
}
fmt.out(");");
return null;
}
@Override
public Object visit(Alloc alloc) {
fmt.out("alloc(");
final int nArgs = alloc.numArgs();
for (int i=0; i < nArgs; i++) {
Expression arg = alloc.getArg(i);
arg.accept(this);
if (i < nArgs-1) {
fmt.out(",");
} else {
fmt.out(")");
}
}
return null;
}
@Override
public Object visit(Spawn spawn) {
fmt.out("spawn");
String separator = "(";
for (Expression expr : spawn.getArgs()) {
fmt.out(separator);
expr.accept(this);
}
fmt.out(");");
return null;
}
@Override
public Object visit(TypeLiteral type) {
fmt.out(type.getType().toString());
return null;
}
/*******************************************************************
* Utility methods
*/
/**
* Return the string representation of the most recently formatted AST
*/
public String read() {
return fmt.read();
}
/**
* Format an AST and return it as a string
* @param ast
* @return
*/
public static String format(AST ast) {
PrettyPrinter printer = new PrettyPrinter();
ast.accept(printer);
return printer.read();
}
/**
* Format an AST and print it on the given stream
* @param stream
* @param ast
*/
public static void print(PrintStream stream, AST ast) {
PrettyPrinter printer = new PrettyPrinter(System.out);
ast.accept(printer);
}
/**
* Format an AST and print it on the given stream
* @param stream
* @param ast
*/
public static void println(PrintStream stream, AST ast) {
print(stream,ast);
stream.println();
}
/**
* Print a script (represented by a MethodTable)
* @param methods
*/
private static void printMethodTable(MethodTable methods) {
PrettyPrinter printer = new PrettyPrinter(System.out);
for (Method m : methods.normalMethods()) {
m.accept(printer);
}
}
public static void main(String[] args) {
ArchitecturalWord.init(Harness.bits.getValue());
try {
MethodTable methods = new Parser(new BufferedInputStream(new FileInputStream(args[0]))).script();
PrettyPrinter.printMethodTable(methods);
} catch (Exception e) {
e.printStackTrace();
}
}
}