/*
* xtc - The eXTensible Compiler
* Copyright (C) 2005-2007 Robert Grimm
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
package xtc.type;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Map;
import xtc.tree.Attribute;
import xtc.tree.Printer;
import xtc.tree.Visitor;
/**
* A visitor to print types.
*
* @author Robert Grimm
* @version $Revision: 1.63 $
*/
public class TypePrinter extends Visitor {
/** The printer utility. */
protected final Printer printer;
/** The set of visited, complex types. */
protected final Map<Object,Object> visited;
/** The flag for instantiated types. */
protected boolean isInstantiated;
/**
* Create a new type printer. Note that this constructor {@link
* xtc.tree.Utility#register registers} the new type printer with
* the specified printer.
*
* @param printer The printer utility.
*/
public TypePrinter(Printer printer) {
this.printer = printer;
this.visited = new IdentityHashMap<Object,Object>();
isInstantiated = false;
printer.register(this);
}
/** Reset this type printer. */
public void reset() {
visited.clear();
}
// =========================================================================
/**
* Print the specified type's annotations.
*
* @param t The type.
* @return <code>true</code> if anything was printed.
*/
public boolean printAnnotations(Type t) {
boolean printed = false;
if (t.hasLocation(false)) {
printer.p("line(").p(t.getLocation(false).line).p(") ");
printed = true;
}
if (t.hasLanguage(false)) {
printer.p("language(").p(t.getLanguage(false).toString()).p(") ");
printed = true;
}
if (t.hasScope(false)) {
printer.p("scope(").p(t.getScope(false)).p(") ");
printed = true;
}
if (t.hasConstant(false)) {
printer.p("value(").p(t.getConstant(false).getValue().toString()).p(") ");
printed = true;
}
if (t.hasShape(false)) {
printer.p("shape(").p(t.getShape().toString()).p(") ");
printed = true;
}
if (t.hasAttributes()) {
for (Attribute att : t.attributes()) {
printer.p(att).p(' ');
printed = true;
}
}
return printed;
}
// =========================================================================
/** Print the specified boolean type. */
public void visit(BooleanT t) {
printAnnotations(t);
printer.p("boolean");
}
/** Print the specified error type. */
public void visit(ErrorT t) {
printAnnotations(t);
printer.p("** error **");
}
/** Print the specified internal type. */
public void visit(InternalT t) {
printAnnotations(t);
printer.p(t.getName());
}
/** Print the specified label type. */
public void visit(LabelT t) {
printAnnotations(t);
printer.p("label(").p(t.getName()).p(')');
}
/** Print the specified number type. */
public void visit(NumberT t) {
printAnnotations(t);
printer.p(t.toString());
}
/** Print the specified package type. */
public void visit(PackageT t) {
printAnnotations(t);
printer.p("package(").p(t.getName()).p(')');
}
/** Print the specified type parameter. */
public void visit(Parameter t) {
printAnnotations(t);
printer.p('<').p(t.getName()).p('>');
}
/** Print the specified unit type. */
public void visit(UnitT t) {
printAnnotations(t);
printer.p(t.getName());
}
/** Print the specified void type. */
public void visit(VoidT t) {
printAnnotations(t);
printer.p("void");
}
// =========================================================================
/** Print the specified array type. */
public void visit(ArrayT t) {
printAnnotations(t);
printer.p("array(").p(t.getType());
if (t.isVarLength()) {
printer.p(", *");
} else if (t.hasLength()) {
printer.p(", ").p(t.getLength());
}
printer.p(')');
}
/**
* Print the interfaces, fields, and methods of the specified class
* or interface type.
*
* @param t The class or interface type.
*/
public void printBody(ClassOrInterfaceT t) {
if (! t.getInterfaces().isEmpty()) {
for (Iterator<Type> iter=t.getInterfaces().iterator(); iter.hasNext();) {
Type type = iter.next();
if (type.isAlias() && (null == type.toAlias().getType())) {
printer.p(type);
} else {
printer.p(((InterfaceT)type.resolve()).getQName());
}
if (iter.hasNext()) {
printer.p(", ");
}
}
}
if (visited.containsKey(t)) return;
visited.put(t, Boolean.TRUE);
if (t.getFields().isEmpty() && t.getMethods().isEmpty()) {
printer.p(" {}");
} else {
printer.pln(" {").incr();
for (Type field : t.getFields()) {
printer.indent().p(field).pln(';');
}
for (Type method : t.getMethods()) {
printer.indent().p(method).pln(';');
}
printer.decr().indent().p('}');
}
}
/** Print the specified class type. */
public void visit(ClassT t) {
printer.p("class ").p(t.getQName());
if (null != t.getParent()) {
Type type = t.getParent();
printer.p(" extends ");
if (type.isAlias() && (null == type.toAlias().getType())) {
printer.p(type);
} else {
printer.p(((ClassT)type.resolve()).getQName());
}
}
if (! t.getInterfaces().isEmpty()) {
printer.p(" implements ");
}
printBody(t);
}
/** Print the specified interface type. */
public void visit(InterfaceT t) {
printer.p("interface ").p(t.getQName());
if (! t.getInterfaces().isEmpty()) {
printer.p(" extends ");
}
printBody(t);
}
/**
* Print the specified function or method type's signature.
*
* @param t The function or method type.
*/
public void printSignature(FunctionOrMethodT t) {
printer.p('(');
for (Iterator<Type> iter = t.getParameters().iterator(); iter.hasNext(); ) {
printer.p(iter.next());
if (iter.hasNext() || t.isVarArgs()) {
printer.p(", ");
}
}
if (t.isVarArgs()) {
printer.p("...");
}
printer.p(") -> ");
if (t.getResult().resolve().isFunction()) {
printer.p('(').p(t.getResult()).p(')');
} else {
printer.p(t.getResult());
}
if ((null != t.getExceptions()) && (! t.getExceptions().isEmpty())) {
printer.p(" throws ");
for (Iterator<Type> iter = t.getExceptions().iterator(); iter.hasNext();) {
printer.p(iter.next());
if (iter.hasNext()) printer.p(", ");
}
}
}
/** Print the specified function type. */
public void visit(FunctionT t) {
printAnnotations(t);
printSignature(t);
}
/** Print the specified method type. */
public void visit(MethodT t) {
printAnnotations(t);
printer.p(t.getName()).p(' ');
printSignature(t);
}
/** Print the specified pointer type. */
public void visit(PointerT t) {
printAnnotations(t);
printer.p("pointer(").p(t.getType()).p(')');
}
/**
* Print the specified tagged type.
*
* @param kind The kind.
* @param tag The tagged type.
*/
public void printTagged(String kind, Tagged tag) {
printer.p(kind).p(' ').p(tag.getName());
if ((null != tag.getMembers()) && (! visited.containsKey(tag))) {
visited.put(tag, Boolean.TRUE);
if (tag.getMembers().isEmpty()) {
printer.p(" {}");
} else {
printer.pln(" {").incr();
for (Iterator<?> iter = tag.getMembers().iterator(); iter.hasNext(); ) {
printer.indent().p((Type)iter.next());
if ("enum".equals(kind)) {
if (iter.hasNext()) {
printer.pln(',');
} else {
printer.pln();
}
} else {
printer.pln(';');
}
}
printer.decr().indent().p('}');
}
}
}
/** Print the specified struct type. */
public void visit(StructT t) {
printAnnotations(t);
printTagged("struct", t);
}
/** Print the specified union type. */
public void visit(UnionT t) {
printAnnotations(t);
printTagged("union", t);
}
/** Print the specified tuple type. */
public void visit(TupleT t) {
printAnnotations(t);
if (null == t.getName()) {
printer.p("<anon>");
} else {
printer.p(t.getName());
}
printer.p('(');
if (null == t.getTypes()) {
printer.p("...");
} else {
for (Iterator<Type> iter = t.getTypes().iterator(); iter.hasNext(); ) {
printer.p(iter.next());
if (iter.hasNext()) printer.p(", ");
}
}
printer.p(')');
}
/** Print the specified variant typee. */
public void visit(VariantT t) {
printAnnotations(t);
if (t.isPolymorphic()) printer.p("polymorphic-");
printer.p("variant ");
if (null == t.getName()) {
printer.p("<anonymous>");
} else {
printer.p(t.getName());
}
if (visited.containsKey(t)) return;
visited.put(t, Boolean.TRUE);
if (null == t.getTuples()) {
printer.p(" { ... }");
} else {
printer.pln(" {").incr();
for (Type var : t.getTuples()) {
printer.indent().p(var).pln(';');
}
printer.decr().indent().p('}');
}
}
// =========================================================================
/** Print the specified type alias. */
public void visit(AliasT t) {
printAnnotations(t);
printer.p("alias(").p(t.getName());
if (null != t.getType()) {
printer.p(", ").p(t.getType());
}
printer.p(')');
}
/** Print the specified annotated type. */
public void visit(AnnotatedT t) {
printAnnotations(t);
printer.p(t.getType());
}
/** Print the specified enumerator. */
public void visit(EnumeratorT t) {
printAnnotations(t); // Prints value.
printer.p("enumerator(").p(t.getType()).p(' ').p(t.getName()).p(')');
}
/** Print the specified enum type. */
public void visit(EnumT t) {
printAnnotations(t);
printTagged("enum", t);
}
/** Print the specified instantiated type. */
public void visit(InstantiatedT t) {
printAnnotations(t);
// Get the wrapped parameterized type.
Iterator<Parameter> params = t.toParameterized().getParameters().iterator();
Iterator<Type> args = t.getArguments().iterator();
printer.p('<');
while (params.hasNext()) {
printer.p(params.next()).p(" = ").p(args.next());
if (params.hasNext()) printer.p(", ");
}
printer.p('>');
isInstantiated = true;
printer.p(t.getType());
}
/** Print the specified parameterized type. */
public void visit(ParameterizedT t) {
printAnnotations(t);
if (isInstantiated) {
isInstantiated = false;
} else {
printer.p('<');
for (Iterator<Parameter> iter = t.getParameters().iterator();
iter.hasNext(); ) {
printer.p(iter.next());
if (iter.hasNext()) printer.p(", ");
}
printer.p("> ");
}
printer.p(t.getType());
}
/** Print the specified variable type. */
public void visit(VariableT t) {
printAnnotations(t);
switch (t.getKind()) {
case GLOBAL:
printer.p("global"); break;
case LOCAL:
printer.p("local"); break;
case PARAMETER:
printer.p("param"); break;
case FIELD:
printer.p("field"); break;
case BITFIELD:
printer.p("bitfield"); break;
}
printer.p('(').p(t.getType()).p(", ");
if (t.hasName()) {
printer.p(t.getName());
} else {
printer.p("<anon>");
}
if (t.hasWidth()) {
printer.p(", ").p(t.getWidth());
}
printer.p(')');
}
}