/* * xtc - The eXTensible Compiler * Copyright (C) 2007-2008 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.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import xtc.Constants; import xtc.tree.Attribute; import xtc.tree.Printer; import xtc.tree.VisitingException; import xtc.tree.Visitor; /** * A visitor to print types in C-like source form. This visitor's * functionality is available through the {@link #print(Type)} and * {@link #print(Type,String)} methods. * * @author Robert Grimm * @version $Revision: 1.6 $ */ public class SourcePrinter extends Visitor { /** The set of attributes to print literally. */ private static Set<Attribute> LITERALS = new HashSet<Attribute>(); static { LITERALS.add(Constants.ATT_ABSTRACT); LITERALS.add(Constants.ATT_CONSTANT); // Must be printed separately. LITERALS.add(Constants.ATT_INLINE); LITERALS.add(Constants.ATT_NATIVE); LITERALS.add(Constants.ATT_RESTRICT); LITERALS.add(Constants.ATT_STRICT_FP); LITERALS.add(Constants.ATT_SYNCHRONIZED); LITERALS.add(Constants.ATT_THREAD_LOCAL); // Must be printed separately. LITERALS.add(Constants.ATT_TRANSIENT); LITERALS.add(Constants.ATT_VOLATILE); } /** The printer utility. */ protected final Printer printer; /** The base type. */ protected Type base; /** The list of derived types, with the outer-most type first. */ protected List<Type> derived; /** The optional variable name. */ protected String name; /** * The flag for whether the next token needs to be preceded by a * space. */ protected boolean needsSpace; /** * Create a new source printer. Note that this visitor is * <em>not</em> registered with the printer utility. * * @param printer The printer utility. */ public SourcePrinter(Printer printer) { this.printer = printer; } /** * Print the specified type. If the specified type contains a * {@link VariableT}, this method prints a declaration for that * type's name. Otherwise, it prints an abstract declaration. Note * that the printed declaration is <em>not</em> followed by any * delimiter such as a semicolon. * * @param type The type. * @throws IllegalArgumentException Signals that the specified type * contains an error type or more than one field, member, * parameter, or variable type. */ public void print(Type type) { print(type, null); } /** * Print the specified type and variable as a declaration. Note * that the printed declaration is <em>not</em> followed by any * delimiter such as a semicolon. * * @param type The type. * @param variable The variable name. * @throws IllegalArgumentException Signals that the specified type * contains a field, member, parameter, variable, or error type. */ public void print(Type type, String variable) { // Save internal state. Type savedBase = base; List<Type> savedDerived = derived; String savedName = name; boolean savedNeedsSpace = needsSpace; base = type; derived = null; name = variable; needsSpace = false; try { dispatch(type); } catch (VisitingException x) { // Unwrap runtime exceptions. if (x.getCause() instanceof RuntimeException) { throw (RuntimeException)x.getCause(); } else { throw x; } } finally { // Restore internal state. base = savedBase; derived = savedDerived; name = savedName; needsSpace = savedNeedsSpace; } } /** * Set the variable name. * * @param variable The variable name. * @throws IllegalArgumentException Signals a duplicate variable * name. */ protected void setVariable(String variable) { if (null != name) { throw new IllegalArgumentException("duplicate variable name"); } else { name = variable; } } /** * Add the specified type to the list of derived types. * * @param type The type. */ protected void addDerived(Type type) { if (null == derived) derived = new ArrayList<Type>(); derived.add(type); } /** * Emit a space. If the {@link #needsSpace} flag is set, this * method emits the space and clears the flag. */ protected void space() { if (needsSpace) { printer.p(' '); needsSpace = false; } } /** * Emit any derived types. This method also prints the variable * name, if it is available. It must be invoked after printing a * non-derived type. */ protected void printDerived() { /* if (null != derived) { for (int i=0; i<derived.size(); i++) { System.out.print(i); System.out.print(": "); System.out.println(derived.get(i).toString()); } } */ // Print any pointer types. if (null != derived) { boolean isPointer = true; // Track nested array or function types. for (int i=derived.size()-1; i>=0; i--) { Type t = derived.get(i); Type r = t.resolve(); if (! r.isPointer()) { // Remember a nested array or function type. isPointer = false; } else { // Print a parenthesis for a nested array or function type. if (! isPointer) { space(); printer.p('('); needsSpace = false; isPointer = true; } // Print the pointer. printPointer(t); } } } // Print the variable name if available. if (null != name) { space(); printer.p(name); } // Print any array and function types. if (null != derived) { final int size = derived.size(); boolean isPointer = false; for (int i=0; i<size; i++) { Type t = derived.get(i); Type r = t.resolve(); if (r.isPointer()) { // Print a parenthesis for a nested array or function type. if (! isPointer) { for (int j=i+1; j<size; j++) { if (! derived.get(j).resolve().isPointer()) { space(); printer.p(')'); break; } } isPointer = true; } } else if (r.isArray()) { isPointer = false; printArray(t); } else if (r.isFunction()) { isPointer = false; printFunction(t); } } } } /** * Print the specified pointer type. * * @param type The pointer type, which may be wrapped */ protected void printPointer(Type type) { // Verify the pointer type. if (! type.resolve().isPointer()) { throw new IllegalStateException("printing non-pointer as pointer"); } // Print the pointer. space(); printer.p('*'); // Print any qualifiers. if (hasAttributes(type)) { printer.p(' '); printAttributes(type); } } /** * Print the specified array type. * * @param type The array type, which may be wrapped. */ protected void printArray(Type type) { // Resolve and verify the array type. Type r = type.resolve(); if (! r.isArray()) { throw new IllegalStateException("printing non-array as array"); } // Print the array type. ArrayT a = r.toArray(); space(); printer.p('['); // Print any qualifiers. if (hasAttributes(type)) { printAttributes(type); } // Print any length. if (a.isVarLength()) { space(); printer.p('*'); } else if (a.hasLength()) { space(); printer.p(a.getLength()); } printer.p(']'); } /** * Print the specified function type. * * @param type The function type, which may be wrapped. */ protected void printFunction(Type type) { // Resolve and verify the function type. Type r = type.resolve(); if (! r.isFunction()) { throw new IllegalStateException("printing non-function as function"); } // Print the function type. FunctionT f = (FunctionT)r; space(); printer.p('('); if (f.hasAttribute(Constants.ATT_STYLE_NEW)) { List<Type> params = f.getParameters(); if (0 == params.size()) { printer.p("void"); } else { for (Iterator<Type> iter=params.iterator(); iter.hasNext(); ) { print(iter.next()); if (iter.hasNext()) printer.p(", "); } } } printer.p(')'); } /** * Determine whether the specified attribute is printable. * * @param att The attribute. * @return <code>true</code> if the attribute is printable. */ public static boolean isPrintable(Attribute att) { if (LITERALS.contains(att)) return true; String name = att.getName(); return (Constants.NAME_STORAGE.equals(name) || (Constants.NAME_VISIBILITY.equals(name) && (! Constants.ATT_PACKAGE_PRIVATE.equals(att)))); } /** * Determine whether the specified type or any wrapped types have * any printable attributes. * * @param type The type. * @return <code>true</code> if the type or any wrapped types have * any printable attributes. */ public static boolean hasAttributes(Type type) { do { if (type.hasAttributes()) { for (Attribute att : type.attributes()) { if (isPrintable(att)) return true; } } if (! type.isWrapped()) return false; type = type.toWrapped().getType(); } while (true); } /** * Print the attributes for the specified type and any wrapped * types. * * @param type The type. */ protected void printAttributes(Type type) { do { if (type.hasAttributes()) { for (Attribute att : type.attributes()) dispatch(att); } if (! type.isWrapped()) return; type = type.toWrapped().getType(); } while (true); } /** Print the specified attribute. */ public void visit(Attribute att) { String name = att.getName(); if (Constants.NAME_STORAGE.equals(name)) { space(); printer.p(att.getValue().toString()); needsSpace = true; } else if (Constants.NAME_VISIBILITY.equals(name)) { if (! Constants.ATT_PACKAGE_PRIVATE.equals(att)) { space(); printer.p(att.getValue().toString()); needsSpace = true; } } else if (Constants.ATT_CONSTANT.equals(att)) { space(); printer.p("const"); needsSpace = true; } else if (Constants.ATT_THREAD_LOCAL.equals(att)) { space(); printer.p("__thread"); needsSpace = true; } else if (LITERALS.contains(att)) { space(); printer.p(name); needsSpace = true; } } /** Print the specified void type. */ public void visit(VoidT t) { printAttributes(base); space(); printer.p("void"); needsSpace = true; printDerived(); } /** Print the specified number type. */ public void visit(NumberT t) { printAttributes(base); space(); printer.p(t.toString()); needsSpace = true; printDerived(); } /** Print the specified struct type. */ public void visit(StructT t) { if (t.isUnnamed()) throw new IllegalArgumentException("anonymous struct"); printAttributes(base); space(); printer.p("struct ").p(t.getName()); needsSpace = true; printDerived(); } /** Print the specified union type. */ public void visit(UnionT t) { if (t.isUnnamed()) throw new IllegalArgumentException("anonymous union"); printAttributes(base); space(); printer.p("union ").p(t.getName()); needsSpace = true; printDerived(); } /** Print the specified enum type. */ public void visit(EnumT t) { if (t.isUnnamed()) throw new IllegalArgumentException("anonymous enum"); printAttributes(base); space(); printer.p("enum " ).p(t.getName()); needsSpace = true; printDerived(); } /** Print the specified class or interface type. */ public void visit(ClassOrInterfaceT t) { space(); printer.p(t.getName()); needsSpace = true; printDerived(); } /** Print the specified alias type. */ public void visit(AliasT t) { printAttributes(base); space(); printer.p(t.getName()); needsSpace = true; printDerived(); } /** Print the specified internal type. */ public void visit(InternalT t) { printAttributes(base); space(); printer.p(t.getName()); needsSpace = true; printDerived(); } /** Print the specified pointer type. */ public void visit(PointerT t) { addDerived(base); base = t.getType(); dispatch(base); } /** Print the specified array type. */ public void visit(ArrayT t) { addDerived(base); base = t.getType(); dispatch(base); } /** Print the specified function type. */ public void visit(FunctionT t) { addDerived(base); base = t.getResult(); dispatch(base); } /** Print the specified variable type. */ public void visit(VariableT t) { setVariable(t.getName()); dispatch(t.getType()); } /** Print the specified wrapped type. */ public void visit(WrappedT t) { dispatch(t.getType()); } /** Print the specified error type. */ public void visit(ErrorT t) { throw new IllegalArgumentException("error type"); } }