PrintingVisitor.java
package com.mackenziehigh.autumn.lang.compiler.pretty;
import autumn.lang.compiler.ast.commons.ConstructList;
import autumn.lang.compiler.ast.commons.IBinaryOperation;
import autumn.lang.compiler.ast.commons.IConstruct;
import autumn.lang.compiler.ast.commons.IConversionOperation;
import autumn.lang.compiler.ast.commons.IExpression;
import autumn.lang.compiler.ast.commons.IRecord;
import autumn.lang.compiler.ast.commons.IStatement;
import autumn.lang.compiler.ast.commons.IUnaryOperation;
import autumn.lang.compiler.ast.nodes.*;
import autumn.lang.compiler.exceptions.IncompleteNodeException;
import autumn.lang.compiler.exceptions.RepeatedNodeException;
import autumn.lang.compiler.exceptions.UnprintableNodeException;
import autumn.util.F;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.mackenziehigh.autumn.resources.Finished;
import java.util.List;
import java.util.Set;
/**
* An instance of this class is a simple pretty-printer of Abstract Syntax Trees.
*
* <p>
* This visitor throws a RepeatedNodeException, if an AST node appears more than once in an AST.
* </p>
*
* <p>
* This visitor throws a IncompleteNodeException, if an AST is incomplete.
* </p>
*
* <p>
* This visitor throws an UnprintableNodeException, if an AST node is unprintable.
* </p>
*
* <p>
* This visitor records the AST nodes that it visits.
* This record can be obtained via the visited() method in order to linearize the AST.
* </p>
*
* @author Mackenzie High
*/
@Finished("2014/07/12")
public final class PrintingVisitor
implements IAstVisitor
{
private static final String ID_REGEX = "([A-Za-z_$][A-Za-z_$0-9]*)";
private final CurlyPrinter p = new CurlyPrinter();
private final Set<IConstruct> visited = Sets.newIdentityHashSet();
private final List<IConstruct> linearized = Lists.newLinkedList();
/**
* This method returns all the AST nodes that were visited thus far,
* in the order they were visited.
*
* @return the aforedescribed nodes.
*/
public List<IConstruct> visited()
{
return ImmutableList.copyOf(linearized);
}
/**
* This method retrieves the pretty printable string that was created.
*
* <p>
* Note: Calling this method causes the internal state of this object to be reset.
* </p>
*
* @return the aforedescribed string.
*/
public String buildString()
{
visited.clear();
final String result = p.buildString();
return result;
}
/**
* The method requires that a specific AST node exists.
*
* @param parent is the parent of the node that must exist.
* @param node is the node that must exist.
* @throws IncompleteNodeException if the node is null (i.e. does not exist).
*/
private void require(final IConstruct parent,
final IConstruct node)
{
assert parent != null;
if (node == null)
{
throw new IncompleteNodeException(parent);
}
}
/**
* The method requires that a list of AST nodes exist.
*
* @param parent is the parent of the nodes that must exist.
* @param list is the list that must exist and contain node that exist.
* @throws IncompleteNodeException if the the list or any of its element do not exist.
*/
private void require(final IConstruct parent,
final ConstructList<? extends IConstruct> list)
{
assert parent != null;
if (list == null || list.asMutableList().contains(null))
{
throw new IncompleteNodeException(parent);
}
}
/**
* The method reports that an AST node is missing, unless a condition is fulfilled.
*
* @param parent is the parent of the nodes that must exist.
* @param condition is the condition that must be fulfilled.
* @throws IncompleteNodeException if the condition is false.
*/
private void require(final IConstruct parent,
final boolean condition)
{
assert parent != null;
if (condition == false)
{
throw new IncompleteNodeException(parent);
}
}
/**
* This method records the visitation of an AST node.
*
* @param node is the node being visited.
* @throws RepeatedNodeException if the node was already visited once.
* @throws NullPointerException if the node is does not exist (i.e. null).
* @throws NullPointerException if the node's location information is missing.
*/
private void record(final IConstruct node)
{
Preconditions.checkNotNull(node, "An abstract-syntax-tree node is missing.");
if (node.getLocation() == null)
{
throw new IncompleteNodeException(node);
}
if (visited.contains(node))
{
throw new RepeatedNodeException(node);
}
visited.add(node);
linearized.add(node);
// The node files are auto-generated.
// Therefore, it is not necessary to test the equals(Object) of hashCode() methods.
// However, the lack of tests messes up the unit-test coverage statistics.
// Thus, these assertions will prevent that issue.
assert node.toString().equals(node.toString());
assert node.getLocation().equals(node.getLocation());
assert node.getLocation().hashCode() == node.getLocation().hashCode();
assert node.getLocation().toString().equals(node.getLocation().toString());
}
/**
* This method reports that an AST node is unprintable, unless a condition is fulfilled.
*
* @param node is the node that may be unprintable.
* @param condition is the condition that must be true for the node to be printable.
* @throws UnprintableNodeException if the node is unprintable.
*/
private void reportUnprintableNode(final IConstruct node,
final boolean condition)
{
if (condition == false)
{
throw new UnprintableNodeException(node);
}
}
@Override
public void visit(final autumn.lang.compiler.ast.nodes.Module object)
{
record(object);
require(object, object.getImportDirectives());
require(object, object.getModuleDirectives());
require(object, object.getAnnotations());
require(object, object.getExceptions());
require(object, object.getEnums());
require(object, object.getDesigns());
require(object, object.getStructs());
require(object, object.getTuples());
require(object, object.getFunctors());
require(object, object.getFunctions());
for (ModuleDirective x : object.getModuleDirectives())
{
x.accept(this);
}
p.addEmptyLine();
for (ImportDirective x : object.getImportDirectives())
{
x.accept(this);
}
p.addEmptyLine();
for (AnnotationDefinition x : object.getAnnotations())
{
x.accept(this);
}
p.addEmptyLine();
for (ExceptionDefinition x : object.getExceptions())
{
x.accept(this);
}
p.addEmptyLine();
for (EnumDefinition x : object.getEnums())
{
x.accept(this);
}
p.addEmptyLine();
for (DesignDefinition x : object.getDesigns())
{
x.accept(this);
}
p.addEmptyLine();
for (TupleDefinition x : object.getTuples())
{
x.accept(this);
}
p.addEmptyLine();
for (StructDefinition x : object.getStructs())
{
x.accept(this);
}
p.addEmptyLine();
for (FunctorDefinition x : object.getFunctors())
{
x.accept(this);
}
p.addEmptyLine();
for (FunctionDefinition x : object.getFunctions())
{
x.accept(this);
}
p.removeEmptyLines();
}
@Override
public void visit(final ModuleDirective object)
{
record(object);
require(object, object.getComment());
require(object, object.getAnnotations());
require(object, object.getNamespace());
object.getComment().accept(this);
object.getAnnotations().accept(this);
p.addLine();
p.addText("module ");
if (object.getName() == null)
{
p.addText("*");
}
else
{
object.getName().accept(this);
}
p.addText(" in ");
object.getNamespace().accept(this);
p.addText(";");
}
@Override
public void visit(final ImportDirective object)
{
record(object);
require(object, object.getType());
p.addLine();
p.addText("import ");
object.getType().accept(this);
p.addText(";");
}
@Override
public void visit(final AnnotationDefinition object)
{
record(object);
require(object, object.getComment());
require(object, object.getAnnotations());
require(object, object.getName());
object.getComment().accept(this);
object.getAnnotations().accept(this);
p.addLine();
p.addText("annotation ");
object.getName().accept(this);
p.addText(";");
p.addEmptyLine();
}
@Override
public void visit(final ExceptionDefinition object)
{
record(object);
require(object, object.getComment());
require(object, object.getAnnotations());
require(object, object.getName());
require(object, object.getSuperclass());
object.getComment().accept(this);
object.getAnnotations().accept(this);
p.addLine();
p.addText("exception ");
object.getName().accept(this);
p.addText(" extends ");
object.getSuperclass().accept(this);
p.addText(";");
p.addEmptyLine();
}
@Override
public void visit(final DesignDefinition object)
{
visitRecord("design", object);
}
@Override
public void visit(final TupleDefinition object)
{
visitRecord("tuple", object);
}
@Override
public void visit(final StructDefinition object)
{
visitRecord("struct", object);
}
@Override
public void visit(final FunctorDefinition object)
{
record(object);
require(object, object.getComment());
require(object, object.getAnnotations());
require(object, object.getName());
require(object, object.getParameters());
require(object, object.getReturnType());
object.getComment().accept(this);
object.getAnnotations().accept(this);
p.addLine();
p.addText("functor ");
object.getName().accept(this);
p.addText(" ");
object.getParameters().accept(this);
p.addText(" : ");
object.getReturnType().accept(this);
if (object.getSuperclass() != null)
{
p.addText(" extends ");
object.getSuperclass().accept(this);
}
p.addText(";");
p.addEmptyLine();
}
@Override
public void visit(final EnumDefinition object)
{
record(object);
require(object, object.getComment());
require(object, object.getAnnotations());
require(object, object.getName());
require(object, object.getConstants());
object.getComment().accept(this);
object.getAnnotations().accept(this);
p.addLine();
p.addText("enum ");
object.getName().accept(this);
p.addText(" ");
printList("(", object.getConstants(), ", ", ")");
p.addText(";");
p.addEmptyLine();
}
@Override
public void visit(final FunctionDefinition object)
{
record(object);
require(object, object.getComment());
require(object, object.getAnnotations());
require(object, object.getName());
require(object, object.getParameters());
require(object, object.getReturnType());
require(object, object.getBody());
object.getComment().accept(this);
object.getAnnotations().accept(this);
p.addLine();
p.addText("defun ");
object.getName().accept(this);
p.addText(" ");
object.getParameters().accept(this);
p.addText(" : ");
object.getReturnType().accept(this);
object.getBody().accept(this);
p.addEmptyLine();
}
@Override
public void visit(final IfStatement object)
{
record(object);
require(object, object.getMainCase());
p.addLine();
p.addText("if ");
object.getMainCase().accept(this);
for (ConditionalCase elif_case : object.getElifCases())
{
p.addLine();
p.addText("elif ");
elif_case.accept(this);
}
if (object.getElseCase() != null)
{
p.addLine();
p.addText("else");
object.getElseCase().accept(this);
}
p.addEmptyLine();
}
@Override
public void visit(final WhenStatement object)
{
record(object);
require(object, object.getCondition());
require(object, object.getBody());
p.addLine();
p.addText("when (");
object.getCondition().accept(this);
p.addText(") then");
p.addOpeningBracket();
object.getBody().accept(this);
p.removeEmptyLines();
p.addClosingBracket();
p.addEmptyLine();
}
@Override
public void visit(final GotoStatement object)
{
record(object);
require(object, object.getLabel());
p.addLine();
p.addText("goto ");
object.getLabel().accept(this);
p.addText(";");
p.addEmptyLine();
}
@Override
public void visit(final MarkerStatement object)
{
record(object);
require(object, object.getLabel());
p.addLine();
p.addText("marker ");
object.getLabel().accept(this);
p.addText(";");
p.addEmptyLine();
}
@Override
public void visit(final BranchStatement object)
{
record(object);
require(object, object.getIndex());
require(object, object.getLabels());
require(object, object.getDefaultLabel());
p.addLine();
p.addText("branch ");
p.addText("(");
object.getIndex().accept(this);
p.addText(")");
p.addText(" ");
printList("(", object.getLabels(), ", ", ")");
p.addText(" default ");
object.getDefaultLabel().accept(this);
p.addText(";");
p.addEmptyLine();
}
@Override
public void visit(final ForeverStatement object)
{
record(object);
require(object, object.getBody());
p.addLine();
p.addText("forever");
object.getBody().accept(this);
p.addEmptyLine();
}
@Override
public void visit(final WhileStatement object)
{
record(object);
require(object, object.getCondition());
require(object, object.getBody());
p.addLine();
p.addText("while (");
object.getCondition().accept(this);
p.addText(")");
object.getBody().accept(this);
p.addEmptyLine();
}
@Override
public void visit(final UntilStatement object)
{
record(object);
require(object, object.getCondition());
require(object, object.getBody());
p.addLine();
p.addText("until (");
object.getCondition().accept(this);
p.addText(")");
object.getBody().accept(this);
p.addEmptyLine();
}
@Override
public void visit(final DoWhileStatement object)
{
record(object);
require(object, object.getBody());
require(object, object.getCondition());
p.addLine();
p.addText("do");
object.getBody().accept(this);
p.addLine();
p.addText("while (");
object.getCondition().accept(this);
p.addText(")");
p.addEmptyLine();
}
@Override
public void visit(final DoUntilStatement object)
{
record(object);
require(object, object.getBody());
require(object, object.getCondition());
p.addLine();
p.addText("do");
object.getBody().accept(this);
p.addLine();
p.addText("until (");
object.getCondition().accept(this);
p.addText(")");
p.addEmptyLine();
}
@Override
public void visit(final ForStatement object)
{
record(object);
require(object, object.getVariable());
require(object, object.getInitializer());
require(object, object.getCondition());
require(object, object.getNext());
require(object, object.getBody());
p.addLine();
p.addText("for (");
object.getVariable().accept(this);
p.addText(" = ");
object.getInitializer().accept(this);
p.addText("; ");
object.getCondition().accept(this);
p.addText("; ");
object.getNext().accept(this);
p.addText(")");
object.getBody().accept(this);
p.addEmptyLine();
}
@Override
public void visit(final ForeachStatement object)
{
record(object);
require(object, object.getVariable());
require(object, object.getType());
require(object, object.getIterable());
require(object, object.getBody());
p.addLine();
p.addText("foreach (");
object.getVariable().accept(this);
p.addText(" : ");
object.getType().accept(this);
p.addText(" in ");
object.getIterable().accept(this);
p.addText(")");
object.getBody().accept(this);
p.addEmptyLine();
}
@Override
public void visit(final BreakStatement object)
{
record(object);
p.addLine();
p.addText("break;");
p.addEmptyLine();
}
@Override
public void visit(final ContinueStatement object)
{
record(object);
p.addLine();
p.addText("continue;");
p.addEmptyLine();
}
@Override
public void visit(final RedoStatement object)
{
record(object);
p.addLine();
p.addText("redo;");
p.addEmptyLine();
}
@Override
public void visit(final VarStatement object)
{
record(object);
require(object, object.getVariable());
require(object, object.getValue());
p.addLine();
p.addText("var ");
object.getVariable().accept(this);
p.addText(" = ");
object.getValue().accept(this);
p.addText(";");
p.addEmptyLine();
}
@Override
public void visit(final ValStatement object)
{
record(object);
require(object, object.getVariable());
require(object, object.getValue());
p.addLine();
p.addText("val ");
object.getVariable().accept(this);
p.addText(" = ");
object.getValue().accept(this);
p.addText(";");
p.addEmptyLine();
}
@Override
public void visit(final LetStatement object)
{
record(object);
require(object, object.getVariable());
require(object, object.getValue());
p.addLine();
p.addText("let ");
object.getVariable().accept(this);
p.addText(" = ");
object.getValue().accept(this);
p.addText(";");
p.addEmptyLine();
}
@Override
public void visit(final LambdaStatement object)
{
record(object);
require(object, object.getVariable());
require(object, object.getType());
require(object, object.getBody());
p.addLine();
p.addText("lambda ");
object.getVariable().accept(this);
p.addText(" : ");
object.getType().accept(this);
p.addText(" => ");
object.getBody().accept(this);
p.addText(";");
p.addEmptyLine();
}
@Override
public void visit(final DelegateStatement object)
{
record(object);
require(object, object.getVariable());
require(object, object.getType());
require(object, object.getOwner());
require(object, object.getMethod());
p.addLine();
p.addText("delegate ");
object.getVariable().accept(this);
p.addText(" : ");
object.getType().accept(this);
p.addText(" => ");
printStaticMemberAccess(object.getOwner(), object.getMethod());
p.addText(";");
p.addEmptyLine();
}
@Override
public void visit(final SequenceStatement object)
{
record(object);
require(object, object.getElements());
p.addOpeningBracket();
{
for (IStatement x : object.getElements())
{
x.accept(this);
}
}
p.removeEmptyLines();
p.addClosingBracket();
}
@Override
public void visit(final ExpressionStatement object)
{
record(object);
require(object, object.getExpression());
p.addLine();
object.getExpression().accept(this);
p.addText(";");
p.addEmptyLine();
}
@Override
public void visit(final NopStatement object)
{
record(object);
p.addLine();
p.addText("nop;");
p.addEmptyLine();
}
@Override
public void visit(final TryCatchStatement object)
{
record(object);
require(object, object.getBody());
require(object, object.getHandlers());
require(object, object.getHandlers().isEmpty() == false);
p.addLine();
p.addText("try");
object.getBody().accept(this);
for (ExceptionHandler x : object.getHandlers())
{
x.accept(this);
}
p.addEmptyLine();
}
@Override
public void visit(final ExceptionHandler object)
{
record(object);
require(object, object.getVariable());
require(object, object.getType());
require(object, object.getBody());
p.addLine();
p.addText("catch (");
object.getVariable().accept(this);
p.addText(" : ");
object.getType().accept(this);
p.addText(")");
object.getBody().accept(this);
}
@Override
public void visit(final ThrowStatement object)
{
record(object);
require(object, object.getValue());
p.addLine();
p.addText("throw ");
object.getValue().accept(this);
p.addText(";");
p.addEmptyLine();
}
@Override
public void visit(final AssertStatement object)
{
record(object);
require(object, object.getCondition());
p.addLine();
p.addText("assert ");
object.getCondition().accept(this);
if (object.getMessage() != null)
{
p.addText(" echo ");
object.getMessage().accept(this);
}
p.addText(";");
p.addEmptyLine();
}
@Override
public void visit(final AssumeStatement object)
{
record(object);
require(object, object.getCondition());
p.addLine();
p.addText("assume ");
object.getCondition().accept(this);
if (object.getMessage() != null)
{
p.addText(" echo ");
object.getMessage().accept(this);
}
p.addText(";");
p.addEmptyLine();
}
@Override
public void visit(final ReturnVoidStatement object)
{
record(object);
p.addLine();
p.addText("return;");
p.addEmptyLine();
}
@Override
public void visit(final ReturnValueStatement object)
{
record(object);
require(object, object.getValue());
p.addLine();
p.addText("return ");
object.getValue().accept(this);
p.addText(";");
p.addEmptyLine();
}
@Override
public void visit(final RecurStatement object)
{
record(object);
require(object, object.getArguments());
p.addLine();
p.addText("recur");
printIf(" ", !object.getArguments().isEmpty());
printList("", object.getArguments(), ", ", ";");
p.addEmptyLine();
p.addEmptyLine();
}
@Override
public void visit(final PrognExpression object)
{
record(object);
require(object, object.getElements());
require(object, object.getElements().isEmpty() == false);
printList("progn (", object.getElements(), ", ", ")");
}
@Override
public void visit(final BooleanDatum object)
{
record(object);
p.addText(object.getValue());
}
@Override
public void visit(final CharDatum object)
{
record(object);
require(object, object.getValue() != null);
reportUnprintableNode(object, object.getValue().isParsable());
final char value = object.getValue().value();
if (value >= 33 && value <= 126)
{
p.addText("'" + value + "'");
}
else
{
p.addText(Long.toString(value) + "C");
}
}
@Override
public void visit(final ByteDatum object)
{
record(object);
require(object, object.getValue() != null);
reportUnprintableNode(object, object.getValue().isParsable());
p.addText(object.getValue());
}
@Override
public void visit(final ShortDatum object)
{
record(object);
require(object, object.getValue() != null);
reportUnprintableNode(object, object.getValue().isParsable());
p.addText(object.getValue());
}
@Override
public void visit(final IntDatum object)
{
record(object);
require(object, object.getValue() != null);
reportUnprintableNode(object, object.getValue().isParsable());
p.addText(object.getValue());
}
@Override
public void visit(final LongDatum object)
{
record(object);
require(object, object.getValue() != null);
reportUnprintableNode(object, object.getValue().isParsable());
p.addText(object.getValue());
}
@Override
public void visit(final FloatDatum object)
{
record(object);
require(object, object.getValue() != null);
reportUnprintableNode(object, object.getValue().isParsable());
p.addText(object.getValue().source());
}
@Override
public void visit(final DoubleDatum object)
{
record(object);
require(object, object.getValue() != null);
reportUnprintableNode(object, object.getValue().isParsable());
p.addText(object.getValue().source());
}
@Override
public void visit(final BigDecimalDatum object)
{
record(object);
require(object, object.getValue() != null);
reportUnprintableNode(object, object.getValue().isParsable());
p.addText(object.getValue().source());
}
@Override
public void visit(final BigIntegerDatum object)
{
record(object);
require(object, object.getValue() != null);
reportUnprintableNode(object, object.getValue().isParsable());
p.addText(object.getValue().source());
}
@Override
public void visit(final StringDatum object)
{
record(object);
require(object, object.getValue() != null);
reportUnprintableNode(object, object.getValue().contains("'''") == false);
final String value = object.getValue();
p.addText(object.getVerbatim() ? "@" : "");
p.addText("'''");
p.addText(value);
p.addText("'''");
}
@Override
public void visit(final NullDatum object)
{
record(object);
p.addText("null");
}
@Override
public void visit(final VariableDatum object)
{
record(object);
require(object, object.getVariable());
object.getVariable().accept(this);
}
@Override
public void visit(final ClassDatum object)
{
record(object);
require(object, object.getType());
p.addText("(class ");
object.getType().accept(this);
p.addText(")");
}
@Override
public void visit(final ListExpression object)
{
record(object);
require(object, object.getElements());
printList("[ ", object.getElements(), ", ", " ]");
}
@Override
public void visit(final ListComprehensionExpression object)
{
record(object);
require(object, object.getModifier());
require(object, object.getVariable());
require(object, object.getType());
require(object, object.getIterable());
p.addText("[");
object.getModifier().accept(this);
p.addText(" for ");
object.getVariable().accept(this);
p.addText(" : ");
object.getType().accept(this);
p.addText(" in ");
object.getIterable().accept(this);
if (object.getCondition() != null)
{
p.addText(" if ");
object.getCondition().accept(this);
}
p.addText("]");
}
@Override
public void visit(final DispatchExpression object)
{
record(object);
require(object, object.getName());
require(object, object.getArguments());
p.addText("dispatch ");
object.getName().accept(this);
p.addText(" ");
printList("(", object.getArguments(), ", ", ")");
}
@Override
public void visit(final CallStaticMethodExpression object)
{
record(object);
require(object, object.getName());
require(object, object.getArguments());
p.addText("call ");
printStaticMemberAccess(object.getOwner(), object.getName());
p.addText(" ");
printList("(", object.getArguments(), ", ", ")");
}
@Override
public void visit(final CallMethodExpression object)
{
record(object);
require(object, object.getOwner());
require(object, object.getName());
require(object, object.getArguments());
p.addText("call ");
printInstanceMemberAccess(object.getOwner(), object.getName());
p.addText(" ");
printList("(", object.getArguments(), ", ", ")");
}
@Override
public void visit(final SetStaticFieldExpression object)
{
record(object);
require(object, object.getOwner());
require(object, object.getName());
require(object, object.getValue());
p.addText("field ");
printStaticMemberAccess(object.getOwner(), object.getName());
p.addText(" = ");
object.getValue().accept(this);
}
@Override
public void visit(final GetStaticFieldExpression object)
{
record(object);
require(object, object.getOwner());
require(object, object.getName());
p.addText("field ");
printStaticMemberAccess(object.getOwner(), object.getName());
}
@Override
public void visit(final NewExpression object)
{
record(object);
require(object, object.getType());
require(object, object.getArguments());
p.addText("new ");
object.getType().accept(this);
p.addText(" ");
printList("(", object.getArguments(), ", ", ")");
}
@Override
public void visit(final SetFieldExpression object)
{
record(object);
require(object, object.getOwner());
require(object, object.getName());
require(object, object.getValue());
p.addText("field ");
printInstanceMemberAccess(object.getOwner(), object.getName());
p.addText(" = ");
object.getValue().accept(this);
}
@Override
public void visit(final GetFieldExpression object)
{
record(object);
require(object, object.getOwner());
require(object, object.getName());
p.addText("field ");
printInstanceMemberAccess(object.getOwner(), object.getName());
}
@Override
public void visit(final InstanceOfExpression object)
{
record(object);
require(object, object.getValue());
require(object, object.getType());
p.addText("instanceof ");
object.getValue().accept(this);
p.addText(" : ");
object.getType().accept(this);
}
@Override
public void visit(final TernaryConditionalExpression object)
{
record(object);
require(object, object.getCondition());
require(object, object.getCaseTrue());
require(object, object.getCaseFalse());
p.addText("if ");
object.getCondition().accept(this);
p.addText(" then ");
object.getCaseTrue().accept(this);
p.addText(" else ");
object.getCaseFalse().accept(this);
}
@Override
public void visit(final LocalsExpression object)
{
record(object);
p.addText("(locals)");
}
@Override
public void visit(final OnceExpression object)
{
record(object);
p.addText("(once ");
object.getValue().accept(this);
p.addText(")");
}
@Override
public void visit(final AsOperation object)
{
visitConversionOperation("as", object);
}
@Override
public void visit(final IsOperation object)
{
visitConversionOperation("is", object);
}
@Override
public void visit(final NegateOperation object)
{
visitUnaryOperation("-", object);
}
@Override
public void visit(final NotOperation object)
{
visitUnaryOperation("!", object);
}
@Override
public void visit(final DivideOperation object)
{
visitBinaryOperation("/", object);
}
@Override
public void visit(final ModuloOperation object)
{
visitBinaryOperation("%", object);
}
@Override
public void visit(final MultiplyOperation object)
{
visitBinaryOperation("*", object);
}
@Override
public void visit(final AddOperation object)
{
visitBinaryOperation("+", object);
}
@Override
public void visit(final SubtractOperation object)
{
visitBinaryOperation("-", object);
}
@Override
public void visit(final LessThanOperation object)
{
visitBinaryOperation("<", object);
}
@Override
public void visit(final LessThanOrEqualsOperation object)
{
visitBinaryOperation("<=", object);
}
@Override
public void visit(final GreaterThanOperation object)
{
visitBinaryOperation(">", object);
}
@Override
public void visit(final GreaterThanOrEqualsOperation object)
{
visitBinaryOperation(">=", object);
}
@Override
public void visit(final EqualsOperation object)
{
visitBinaryOperation("==", object);
}
@Override
public void visit(final NotEqualsOperation object)
{
visitBinaryOperation("!=", object);
}
@Override
public void visit(final IdentityEqualsOperation object)
{
visitBinaryOperation("===", object);
}
@Override
public void visit(final IdentityNotEqualsOperation object)
{
visitBinaryOperation("!==", object);
}
@Override
public void visit(final AndOperation object)
{
visitBinaryOperation("&", object);
}
@Override
public void visit(final OrOperation object)
{
visitBinaryOperation("|", object);
}
@Override
public void visit(final XorOperation object)
{
visitBinaryOperation("^", object);
}
@Override
public void visit(final ImpliesOperation object)
{
visitBinaryOperation("->", object);
}
@Override
public void visit(final NullCoalescingOperation object)
{
visitBinaryOperation("??", object);
}
@Override
public void visit(final ConcatOperation object)
{
visitBinaryOperation("..", object);
}
@Override
public void visit(final Variable object)
{
record(object);
require(object, object.getName() != null);
reportUnprintableNode(object, object.getName().matches(ID_REGEX));
p.addText(object.getName());
}
@Override
public void visit(final DocComment object)
{
record(object);
require(object, object.getLines());
for (DocCommentLine line : object.getLines())
{
line.accept(this);
}
}
@Override
public void visit(final DocCommentLine object)
{
record(object);
p.addLine();
p.addText("'' ");
p.addText(object.getText() == null ? "" : object.getText());
}
@Override
public void visit(final Name object)
{
record(object);
require(object, object.getName() != null);
reportUnprintableNode(object, object.getName().matches(ID_REGEX));
p.addText(object.getName());
}
@Override
public void visit(final TypeSpecifier object)
{
record(object);
require(object, object.getName());
final Integer dimensions = object.getDimensions();
reportUnprintableNode(object, dimensions == null || dimensions >= 0);
if (object.getNamespace() != null)
{
object.getNamespace().accept(this);
printIf(".", object.getNamespace() != null);
}
object.getName().accept(this);
p.addText(Strings.repeat("[]", dimensions == null ? 0 : dimensions));
}
@Override
public void visit(final Namespace object)
{
record(object);
require(object, object.getNames());
require(object, object.getNames().size() >= 1);
int count = 0;
for (Name name : object.getNames())
{
assert name != null;
++count;
name.accept(this);
if (count < object.getNames().size())
{
p.addText(".");
}
}
}
@Override
public void visit(final AnnotationList object)
{
record(object);
require(object, object.getAnnotations());
for (Annotation x : object.getAnnotations())
{
x.accept(this);
}
}
@Override
public void visit(final Annotation object)
{
record(object);
require(object, object.getType());
p.addLine();
p.addText("@");
object.getType().accept(this);
if (object.getValues() != null)
{
p.addText(" ");
p.addText(F.str(createArrayOfStringLiterals(object.getValues()), "(", ", ", ")"));
}
}
@Override
public void visit(final FormalParameterList object)
{
record(object);
require(object, object.getParameters());
p.addText("(");
{
int count = 0;
for (FormalParameter param : object.getParameters())
{
++count;
param.accept(this);
if (count < object.getParameters().size())
{
p.addText(", ");
}
}
}
p.addText(")");
}
@Override
public void visit(final FormalParameter object)
{
record(object);
require(object, object.getVariable());
require(object, object.getType());
object.getVariable().accept(this);
p.addText(" : ");
object.getType().accept(this);
}
@Override
public void visit(final Element object)
{
record(object);
require(object, object.getName());
require(object, object.getType());
object.getName().accept(this);
p.addText(" : ");
object.getType().accept(this);
}
@Override
public void visit(final ElementList object)
{
record(object);
require(object, object.getElements());
p.addText("(");
{
int count = 0;
for (Element element : object.getElements())
{
++count;
element.accept(this);
if (count < object.getElements().size())
{
p.addText(", ");
}
}
}
p.addText(")");
}
@Override
public void visit(final ConditionalCase object)
{
record(object);
require(object, object.getCondition());
require(object, object.getBody());
p.addText("(");
object.getCondition().accept(this);
p.addText(")");
object.getBody().accept(this);
}
@Override
public void visit(final Label object)
{
record(object);
require(object, object.getName() != null);
reportUnprintableNode(object, object.getName().matches(ID_REGEX));
p.addText(object.getName());
}
@Override
public void visit(final SourceLocation object)
{
// Do Nothing
}
/**
* This method generalizes the visitation of a record definition.
*
* @param keyword specifies the type of record definition (e.g. tuple, struct, design).
* @param object is the definition itself.
*/
private void visitRecord(final String keyword,
final IRecord object)
{
record(object);
require(object, object.getComment());
require(object, object.getAnnotations());
require(object, object.getName());
require(object, object.getElements());
require(object, object.getSupers());
object.getComment().accept(this);
object.getAnnotations().accept(this);
p.addLine();
p.addText(keyword);
p.addText(" ");
object.getName().accept(this);
p.addText(" ");
object.getElements().accept(this);
if (object.getSupers().isEmpty() == false)
{
this.printList(" extends ", object.getSupers(), " & ", "");
}
p.addText(";");
p.addEmptyLine();
}
/**
* This method generalizes the visitation of a conversion operation.
*
* @param operator is the string representation of the conversion operator.
* @param operation is the conversion operation itself.
*/
private void visitConversionOperation(final String operator,
final IConversionOperation operation)
{
record(operation);
require(operation, operation.getValue());
require(operation, operation.getType());
p.addText("(");
operation.getValue().accept(this);
p.addText(" ");
p.addText(operator);
p.addText(" ");
operation.getType().accept(this);
p.addText(")");
}
/**
* This method generalizes the visitation of a unary operation.
*
* @param operator is the string representation of the unary operator.
* @param operation is the operation itself.
*/
private void visitUnaryOperation(final String operator,
final IUnaryOperation operation)
{
record(operation);
require(operation, operation.getOperand());
p.addText("(");
p.addText(operator);
p.addText(" ");
operation.getOperand().accept(this);
p.addText(")");
}
/**
* This method generalizes the visitation of a binary operation.
*
* @param operator is the string representation of the binary operator.
* @param operation is the operation itself.
*/
private void visitBinaryOperation(final String operator,
final IBinaryOperation operation)
{
record(operation);
require(operation, operation.getLeftOperand());
require(operation, operation.getRightOperand());
p.addText("(");
operation.getLeftOperand().accept(this);
p.addText(" ");
p.addText(operator);
p.addText(" ");
operation.getRightOperand().accept(this);
p.addText(")");
}
/**
* This method prints a static member accessor.
*
* @param owner is the owner of the member being accessed.
* @param member is the name of the member being accessed.
*/
private void printStaticMemberAccess(final TypeSpecifier owner,
final Name member)
{
assert owner != null;
assert member != null;
owner.accept(this);
p.addText("::");
member.accept(this);
}
/**
* This method prints an instance member accessor.
*
* @param owner is an expression that produces the owner of the member being accessed.
* @param member is the name of the member being accessed.
*/
private void printInstanceMemberAccess(final IExpression owner,
final Name member)
{
assert owner != null;
assert member != null;
owner.accept(this);
p.addText(".");
member.accept(this);
}
/**
* This method converts a sequence of strings to string literal style strings.
*
* @param values are the escaped strings.
* @return the values such that they appear to be string literals.
*/
private List<String> createArrayOfStringLiterals(final Iterable<String> values)
{
final List<String> result = Lists.newLinkedList();
for (String value : values)
{
final String unescaped = F.unescape(value);
result.add("\"" + unescaped + "\"");
}
return result;
}
/**
* This method prints a list of constructs.
*
* @param prefix is a string to print immediately before the arguments.
* @param arguments are the arguments to print.
* @param separator is used to separate the arguments.
* @param suffix is a string to print immediately after the arguments.
*/
private void printList(final String prefix,
final ConstructList<? extends IConstruct> arguments,
final String separator,
final String suffix)
{
assert prefix != null;
assert arguments != null;
assert suffix != null;
p.addText(prefix);
{
int count = 0;
for (IConstruct arg : arguments)
{
assert arg != null;
++count;
arg.accept(this);
if (count < arguments.size())
{
p.addText(separator);
}
}
}
p.addText(suffix);
}
/**
* This method conditionally prints a string of text.
*
* @param text is the text that may be printed.
* @param condition is true, iff the text is to be printed.
*/
private void printIf(final String text,
final boolean condition)
{
assert text != null;
if (condition)
{
p.addText(text);
}
}
}