001: /*
002: * xtc - The eXTensible Compiler
003: * Copyright (C) 2007 Robert Grimm
004: *
005: * This program is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU General Public License
007: * version 2 as published by the Free Software Foundation.
008: *
009: * This program is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: * GNU General Public License for more details.
013: *
014: * You should have received a copy of the GNU General Public License
015: * along with this program; if not, write to the Free Software
016: * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
017: * USA.
018: */
019: package xtc.type;
020:
021: import java.util.ArrayList;
022: import java.util.HashSet;
023: import java.util.Iterator;
024: import java.util.List;
025: import java.util.Set;
026:
027: import xtc.Constants;
028:
029: import xtc.tree.Attribute;
030: import xtc.tree.Printer;
031: import xtc.tree.VisitingException;
032: import xtc.tree.Visitor;
033:
034: /**
035: * A visitor to print types in C-like source form. This visitor's
036: * functionality is available through the {@link #print(Type)} and
037: * {@link #print(Type,String)} methods.
038: *
039: * @author Robert Grimm
040: * @version $Revision: 1.5 $
041: */
042: public class SourcePrinter extends Visitor {
043:
044: /** The set of attributes to print literally. */
045: private static Set<Attribute> LITERALS = new HashSet<Attribute>();
046:
047: static {
048: LITERALS.add(Constants.ATT_ABSTRACT);
049: LITERALS.add(Constants.ATT_CONSTANT); // Must be printed separately.
050: LITERALS.add(Constants.ATT_INLINE);
051: LITERALS.add(Constants.ATT_NATIVE);
052: LITERALS.add(Constants.ATT_RESTRICT);
053: LITERALS.add(Constants.ATT_STRICT_FP);
054: LITERALS.add(Constants.ATT_SYNCHRONIZED);
055: LITERALS.add(Constants.ATT_TRANSIENT);
056: LITERALS.add(Constants.ATT_VOLATILE);
057: }
058:
059: /** The printer utility. */
060: protected final Printer printer;
061:
062: /** The base type. */
063: protected Type base;
064:
065: /** The list of derived types, with the outer-most type first. */
066: protected List<Type> derived;
067:
068: /** The optional variable name. */
069: protected String name;
070:
071: /**
072: * The flag for whether the next token needs to be preceded by a
073: * space.
074: */
075: protected boolean needsSpace;
076:
077: /**
078: * Create a new source printer. Note that this visitor is
079: * <em>not</em> registered with the printer utility.
080: *
081: * @param printer The printer utility.
082: */
083: public SourcePrinter(Printer printer) {
084: this .printer = printer;
085: }
086:
087: /**
088: * Print the specified type. If the specified type contains a
089: * {@link VariableT}, this method prints a declaration for that
090: * type's name. Otherwise, it prints an abstract declaration. Note
091: * that the printed declaration is <em>not</em> followed by any
092: * delimiter such as a semicolon.
093: *
094: * @param type The type.
095: * @throws IllegalArgumentException Signals that the specified type
096: * contains an error type or more than one field, member,
097: * parameter, or variable type.
098: */
099: public void print(Type type) {
100: print(type, null);
101: }
102:
103: /**
104: * Print the specified type and variable as a declaration. Note
105: * that the printed declaration is <em>not</em> followed by any
106: * delimiter such as a semicolon.
107: *
108: * @param type The type.
109: * @param variable The variable name.
110: * @throws IllegalArgumentException Signals that the specified type
111: * contains a field, member, parameter, variable, or error type.
112: */
113: public void print(Type type, String variable) {
114: // Save internal state.
115: Type savedBase = base;
116: List<Type> savedDerived = derived;
117: String savedName = name;
118: boolean savedNeedsSpace = needsSpace;
119:
120: base = type;
121: derived = null;
122: name = variable;
123: needsSpace = false;
124:
125: try {
126: dispatch(type);
127: } catch (VisitingException x) {
128: // Unwrap runtime exceptions.
129: if (x.getCause() instanceof RuntimeException) {
130: throw (RuntimeException) x.getCause();
131: } else {
132: throw x;
133: }
134: } finally {
135: // Restore internal state.
136: base = savedBase;
137: derived = savedDerived;
138: name = savedName;
139: needsSpace = savedNeedsSpace;
140: }
141: }
142:
143: /**
144: * Set the variable name.
145: *
146: * @param variable The variable name.
147: * @throws IllegalArgumentException Signals a duplicate variable
148: * name.
149: */
150: protected void setVariable(String variable) {
151: if (null != name) {
152: throw new IllegalArgumentException(
153: "duplicate variable name");
154: } else {
155: name = variable;
156: }
157: }
158:
159: /**
160: * Add the specified type to the list of derived types.
161: *
162: * @param type The type.
163: */
164: protected void addDerived(Type type) {
165: if (null == derived)
166: derived = new ArrayList<Type>();
167: derived.add(type);
168: }
169:
170: /**
171: * Emit a space. If the {@link #needsSpace} flag is set, this
172: * method emits the space and clears the flag.
173: */
174: protected void space() {
175: if (needsSpace) {
176: printer.p(' ');
177: needsSpace = false;
178: }
179: }
180:
181: /**
182: * Emit any derived types. This method also prints the variable
183: * name, if it is available. It must be invoked after printing a
184: * non-derived type.
185: */
186: protected void printDerived() {
187: /*
188: if (null != derived) {
189: for (int i=0; i<derived.size(); i++) {
190: System.out.print(i);
191: System.out.print(": ");
192: System.out.println(derived.get(i).toString());
193: }
194: }
195: */
196:
197: // Print any pointer types.
198: if (null != derived) {
199: boolean isPointer = true; // Track nested array or function types.
200: for (int i = derived.size() - 1; i >= 0; i--) {
201: Type t = derived.get(i);
202: Type r = t.resolve();
203:
204: if (!r.isPointer()) {
205: // Remember a nested array or function type.
206: isPointer = false;
207: } else {
208: // Print a parenthesis for a nested array or function type.
209: if (!isPointer) {
210: space();
211: printer.p('(');
212: needsSpace = false;
213: isPointer = true;
214: }
215:
216: // Print the pointer.
217: printPointer(t);
218: }
219: }
220: }
221:
222: // Print the variable name if available.
223: if (null != name) {
224: space();
225: printer.p(name);
226: }
227:
228: // Print any array and function types.
229: if (null != derived) {
230: final int size = derived.size();
231: boolean isPointer = false;
232: for (int i = 0; i < size; i++) {
233: Type t = derived.get(i);
234: Type r = t.resolve();
235:
236: if (r.isPointer()) {
237: // Print a parenthesis for a nested array or function type.
238: if (!isPointer) {
239: for (int j = i + 1; j < size; j++) {
240: if (!derived.get(j).resolve().isPointer()) {
241: space();
242: printer.p(')');
243: break;
244: }
245: }
246: isPointer = true;
247: }
248:
249: } else if (r.isArray()) {
250: isPointer = false;
251: printArray(t);
252:
253: } else if (r.isFunction()) {
254: isPointer = false;
255: printFunction(t);
256: }
257: }
258: }
259: }
260:
261: /**
262: * Print the specified pointer type.
263: *
264: * @param type The pointer type, which may be wrapped
265: */
266: protected void printPointer(Type type) {
267: // Verify the pointer type.
268: if (!type.resolve().isPointer()) {
269: throw new IllegalStateException(
270: "printing non-pointer as pointer");
271: }
272:
273: // Print the pointer.
274: space();
275: printer.p('*');
276:
277: // Print any qualifiers.
278: if (hasAttributes(type)) {
279: printer.p(' ');
280: printAttributes(type);
281: }
282: }
283:
284: /**
285: * Print the specified array type.
286: *
287: * @param type The array type, which may be wrapped.
288: */
289: protected void printArray(Type type) {
290: // Resolve and verify the array type.
291: Type r = type.resolve();
292: if (!r.isArray()) {
293: throw new IllegalStateException(
294: "printing non-array as array");
295: }
296:
297: // Print the array type.
298: ArrayT a = r.toArray();
299: space();
300: printer.p('[');
301:
302: // Print any qualifiers.
303: if (hasAttributes(type)) {
304: printAttributes(type);
305: }
306:
307: // Print any length.
308: if (a.isVarLength()) {
309: space();
310: printer.p('*');
311: } else if (a.hasLength()) {
312: space();
313: printer.p(a.getLength());
314: }
315:
316: printer.p(']');
317: }
318:
319: /**
320: * Print the specified function type.
321: *
322: * @param type The function type, which may be wrapped.
323: */
324: protected void printFunction(Type type) {
325: // Resolve and verify the function type.
326: Type r = type.resolve();
327: if (!r.isFunction()) {
328: throw new IllegalStateException(
329: "printing non-function as function");
330: }
331:
332: // Print the function type.
333: FunctionT f = (FunctionT) r;
334: space();
335: printer.p('(');
336:
337: if (f.hasAttribute(Constants.ATT_STYLE_NEW)) {
338: List<Type> params = f.getParameters();
339: if (0 == params.size()) {
340: printer.p("void");
341: } else {
342: for (Iterator<Type> iter = params.iterator(); iter
343: .hasNext();) {
344: print(iter.next());
345: if (iter.hasNext())
346: printer.p(", ");
347: }
348: }
349: }
350:
351: printer.p(')');
352: }
353:
354: /**
355: * Determine whether the specified attribute is printable.
356: *
357: * @param att The attribute.
358: * @return <code>true</code> if the attribute is printable.
359: */
360: public static boolean isPrintable(Attribute att) {
361: if (LITERALS.contains(att))
362: return true;
363:
364: String name = att.getName();
365: return (Constants.NAME_STORAGE.equals(name) || (Constants.NAME_VISIBILITY
366: .equals(name) && (!Constants.ATT_PACKAGE_PRIVATE
367: .equals(att))));
368: }
369:
370: /**
371: * Determine whether the specified type or any wrapped types have
372: * any printable attributes.
373: *
374: * @param type The type.
375: * @return <code>true</code> if the type or any wrapped types have
376: * any printable attributes.
377: */
378: public static boolean hasAttributes(Type type) {
379: do {
380: if (type.hasAttributes()) {
381: for (Attribute att : type.attributes()) {
382: if (isPrintable(att))
383: return true;
384: }
385: }
386:
387: if (!type.isWrapped())
388: return false;
389: type = type.toWrapped().getType();
390: } while (true);
391: }
392:
393: /**
394: * Print the attributes for the specified type and any wrapped
395: * types.
396: *
397: * @param type The type.
398: */
399: protected void printAttributes(Type type) {
400: do {
401: if (type.hasAttributes()) {
402: for (Attribute att : type.attributes())
403: dispatch(att);
404: }
405:
406: if (!type.isWrapped())
407: return;
408: type = type.toWrapped().getType();
409: } while (true);
410: }
411:
412: /** Print the specified attribute. */
413: public void visit(Attribute att) {
414: String name = att.getName();
415:
416: if (Constants.NAME_STORAGE.equals(name)) {
417: space();
418: printer.p(att.getValue().toString());
419: needsSpace = true;
420:
421: } else if (Constants.NAME_VISIBILITY.equals(name)) {
422: if (!Constants.ATT_PACKAGE_PRIVATE.equals(att)) {
423: space();
424: printer.p(att.getValue().toString());
425: needsSpace = true;
426: }
427:
428: } else if (Constants.ATT_CONSTANT.equals(att)) {
429: space();
430: printer.p("const");
431: needsSpace = true;
432:
433: } else if (LITERALS.contains(att)) {
434: space();
435: printer.p(name);
436: needsSpace = true;
437: }
438: }
439:
440: /** Print the specified void type. */
441: public void visit(VoidT t) {
442: printAttributes(base);
443: space();
444: printer.p("void");
445: needsSpace = true;
446: printDerived();
447: }
448:
449: /** Print the specified number type. */
450: public void visit(NumberT t) {
451: printAttributes(base);
452: space();
453: printer.p(t.toString());
454: needsSpace = true;
455: printDerived();
456: }
457:
458: /** Print the specified struct type. */
459: public void visit(StructT t) {
460: if (t.isUnnamed())
461: throw new IllegalArgumentException("anonymous struct");
462: printAttributes(base);
463: space();
464: printer.p("struct ").p(t.getName());
465: needsSpace = true;
466: printDerived();
467: }
468:
469: /** Print the specified union type. */
470: public void visit(UnionT t) {
471: if (t.isUnnamed())
472: throw new IllegalArgumentException("anonymous union");
473: printAttributes(base);
474: space();
475: printer.p("union ").p(t.getName());
476: needsSpace = true;
477: printDerived();
478: }
479:
480: /** Print the specified enum type. */
481: public void visit(EnumT t) {
482: if (t.isUnnamed())
483: throw new IllegalArgumentException("anonymous enum");
484: printAttributes(base);
485: space();
486: printer.p("enum ").p(t.getName());
487: needsSpace = true;
488: printDerived();
489: }
490:
491: /** Print the specified class or interface type. */
492: public void visit(ClassOrInterfaceT t) {
493: space();
494: printer.p(t.getName());
495: needsSpace = true;
496: printDerived();
497: }
498:
499: /** Print the specified alias type. */
500: public void visit(AliasT t) {
501: printAttributes(base);
502: space();
503: printer.p(t.getName());
504: needsSpace = true;
505: printDerived();
506: }
507:
508: /** Print the specified internal type. */
509: public void visit(InternalT t) {
510: printAttributes(base);
511: space();
512: printer.p(t.getName());
513: needsSpace = true;
514: printDerived();
515: }
516:
517: /** Print the specified pointer type. */
518: public void visit(PointerT t) {
519: addDerived(base);
520: base = t.getType();
521: dispatch(base);
522: }
523:
524: /** Print the specified array type. */
525: public void visit(ArrayT t) {
526: addDerived(base);
527: base = t.getType();
528: dispatch(base);
529: }
530:
531: /** Print the specified function type. */
532: public void visit(FunctionT t) {
533: addDerived(base);
534: base = t.getResult();
535: dispatch(base);
536: }
537:
538: /** Print the specified variable type. */
539: public void visit(VariableT t) {
540: setVariable(t.getName());
541: dispatch(t.getType());
542: }
543:
544: /** Print the specified wrapped type. */
545: public void visit(WrappedT t) {
546: dispatch(t.getType());
547: }
548:
549: /** Print the specified error type. */
550: public void visit(ErrorT t) {
551: throw new IllegalArgumentException("error type");
552: }
553:
554: }
|