package ql.ast.visitor.prettyprinter; import ql.ast.Expression; import ql.ast.QLNode; import ql.ast.QLType; import ql.ast.Statement; import ql.ast.expression.Binary; import ql.ast.expression.Identifier; import ql.ast.expression.Unary; import ql.ast.expression.arithmetic.Add; import ql.ast.expression.arithmetic.Divide; import ql.ast.expression.arithmetic.Multiply; import ql.ast.expression.arithmetic.Negation; import ql.ast.expression.arithmetic.Positive; import ql.ast.expression.arithmetic.Subtract; import ql.ast.expression.booleanalgebra.And; import ql.ast.expression.booleanalgebra.Not; import ql.ast.expression.booleanalgebra.Or; import ql.ast.expression.literal.BooleanLiteral; import ql.ast.expression.literal.FloatLiteral; import ql.ast.expression.literal.IntegerLiteral; import ql.ast.expression.literal.MoneyLiteral; import ql.ast.expression.literal.StringLiteral; import ql.ast.expression.relational.Equal; import ql.ast.expression.relational.Greater; import ql.ast.expression.relational.GreaterOrEqual; import ql.ast.expression.relational.Lower; import ql.ast.expression.relational.LowerOrEqual; import ql.ast.expression.relational.NotEqual; import ql.ast.statement.Block; import ql.ast.statement.ComputedQuestion; import ql.ast.statement.Form; import ql.ast.statement.If; import ql.ast.statement.IfElse; import ql.ast.statement.Question; import ql.ast.type.QLBoolean; import ql.ast.type.QLError; import ql.ast.type.QLFloat; import ql.ast.type.QLForm; import ql.ast.type.QLInteger; import ql.ast.type.QLMoney; import ql.ast.type.QLNumeric; import ql.ast.type.QLString; import ql.ast.visitor.ExpressionVisitor; import ql.ast.visitor.StatementVisitor; import ql.ast.visitor.TypeVisitor; public class PrettyPrinter extends StatementVisitor<String> implements ExpressionVisitor<String>, TypeVisitor<String> { public static final String DEFAULT_PREFIX = "└── "; private String prefix = ""; private final String prefixSymbol; private PrettyPrinter(String prefixSymbol) { super.setExpressionVisitor(this); super.setTypeVisitor(this); this.prefixSymbol = prefixSymbol; } /* * Expression with custom prefix */ public static void print(Expression expression, PrintWriter printWriter, String prefixSymbol) { PrettyPrinter printer = new PrettyPrinter(prefixSymbol); printWriter.printString(expression.accept(printer)); } /* * Statement with custom prefix */ public static void print(Statement statement, PrintWriter printWriter, String prefixSymbol) { PrettyPrinter printer = new PrettyPrinter(prefixSymbol); printWriter.printString(statement.accept(printer)); } /* * Statement with custom prefix */ public static void print(QLType type, PrintWriter printWriter, String prefixSymbol) { PrettyPrinter printer = new PrettyPrinter(prefixSymbol); printWriter.printString(type.accept(printer)); } /** * Indent the prefix to indicate that a printable block * of nodes has been entered. */ private void indent() { prefix += " "; } /** * Unindent the prefix. This happens when a block of * printable nodes has been printed and the function * exits. */ private void unindent() { prefix = prefix.substring(0, this.prefix.length() - 3); } /** * Print a node in the console. The node its toString * method will be printed first, followed the the type * of the node, separated by a colon. * * @param node - The node to print. */ private String printNode(QLNode node) { String type = node.getClass().getSimpleName(); // Strip the string until only the name of the node is left. String nodeString = node.toString().split("\\(")[0]; return prefix + prefixSymbol + nodeString + " : " + type + "\n"; } /** * Binary nodes need to printed in the same way. This * function centralises the logic for printing such a * node. * * @param binaryNode - The binary node to print. */ private String printBinaryNode(Binary binaryNode) { StringBuilder binaryString = new StringBuilder(printNode(binaryNode)); indent(); binaryString.append(binaryNode.getLeft().accept(this)); binaryString.append(binaryNode.getRight().accept(this)); unindent(); return binaryString.toString(); } /** * Unary nodes need to be printed in the same way. * This function centralises the logic for printing * such a node. * * @param unaryNode - The unary node to print. */ private String printUnaryNode(Unary unaryNode) { StringBuilder unaryString = new StringBuilder(printNode(unaryNode)); indent(); unaryString.append(unaryNode.getExpression().accept(this)); unindent(); return unaryString.toString(); } /********************* == IDENTIFIER NODE == *********************/ @Override public String visit(Identifier identNode) { return printNode(identNode); } /**************** == TYPE NODES == ****************/ @Override public String visit(QLBoolean booleanNode) { return printNode(booleanNode); } @Override public String visit(QLFloat floatNode) { return printNode(floatNode); } @Override public String visit(QLMoney moneyNode) { return printNode(moneyNode); } @Override public String visit(QLForm formNode) { return printNode(formNode); } @Override public String visit(QLInteger intNode) { return printNode(intNode); } @Override public String visit(QLNumeric numericNode) { return printNode(numericNode); } @Override public String visit(QLString stringNode) { return printNode(stringNode); } @Override public String visit(QLError qlError) { return printNode(qlError); } /******************* == LITERAL NODES == *******************/ @Override public String visit(BooleanLiteral booleanNode) { return printNode(booleanNode); } @Override public String visit(FloatLiteral floatNode) { return printNode(floatNode); } @Override public String visit(MoneyLiteral moneyNode) { return printNode(moneyNode); } @Override public String visit(IntegerLiteral intNode) { return printNode(intNode); } @Override public String visit(StringLiteral stringNode) { return printNode(stringNode); } /****************** == BINARY NODES == ******************/ @Override public String visit(Add addNode) { return printBinaryNode(addNode); } @Override public String visit(Divide divNode) { return printBinaryNode(divNode); } @Override public String visit(Multiply mulNode) { return printBinaryNode(mulNode); } @Override public String visit(Subtract subNode) { return printBinaryNode(subNode); } @Override public String visit(And andNode) { return printBinaryNode(andNode); } @Override public String visit(Equal eqNode) { return printBinaryNode(eqNode); } @Override public String visit(GreaterOrEqual geqNode) { return printBinaryNode(geqNode); } @Override public String visit(Greater gtNode) { return printBinaryNode(gtNode); } @Override public String visit(LowerOrEqual leqNode) { return printBinaryNode(leqNode); } @Override public String visit(Lower ltNode) { return printBinaryNode(ltNode); } @Override public String visit(NotEqual neqNode) { return printBinaryNode(neqNode); } @Override public String visit(Or orNode) { return printBinaryNode(orNode); } /***************** == UNARY NODES == *****************/ @Override public String visit(Negation negNode) { return printUnaryNode(negNode); } @Override public String visit(Not notNode) { return printUnaryNode(notNode); } @Override public String visit(Positive posNode) { return printUnaryNode(posNode); } /********************* == STATEMENT NODES == *********************/ @Override public String visit(Block blockNode) { StringBuilder blockString = new StringBuilder(printNode(blockNode)); indent(); for(Statement statement : blockNode.getStatements()) { blockString.append(statement.accept(this)); } unindent(); return blockString.toString(); } @Override public String visit(ComputedQuestion compQuestionNode) { StringBuilder compQuestionString = new StringBuilder(printNode(compQuestionNode)); indent(); compQuestionString.append(compQuestionNode.getIdentifier().accept(this)); compQuestionString.append(compQuestionNode.getType().accept(this)); compQuestionString.append(compQuestionNode.getText().accept(this)); compQuestionString.append(compQuestionNode.getExpression().accept(this)); unindent(); return compQuestionString.toString(); } @Override public String visit(Form formNode) { StringBuilder formString = new StringBuilder(printNode(formNode)); indent(); formString.append(formNode.getIdentifier().accept(this)); formString.append(formNode.getBlock().accept(this)); unindent(); return formString.toString(); } @Override public String visit(If ifNode) { StringBuilder ifString = new StringBuilder(printNode(ifNode)); indent(); ifString.append(ifNode.getExpression().accept(this)); ifString.append(ifNode.getBlock().accept(this)); unindent(); return ifString.toString(); } @Override public String visit(IfElse ifElseNode) { StringBuilder ifElseString = new StringBuilder(printNode(ifElseNode)); indent(); ifElseString.append(ifElseNode.getExpression().accept(this)); ifElseString.append(ifElseNode.getIfBranch().accept(this)); ifElseString.append(ifElseNode.getElseBranch().accept(this)); unindent(); return ifElseString.toString(); } @Override public String visit(Question questionNode) { StringBuilder questionString = new StringBuilder(printNode(questionNode)); indent(); questionString.append(questionNode.getIdentifier().accept(this)); questionString.append(questionNode.getType().accept(this)); questionString.append(questionNode.getText().accept(this)); unindent(); return questionString.toString(); } }