001    /* $Id: PrettyPrint.java,v 1.11 2006/08/05 16:58:04 chalin Exp $
002     * Copyright 2000, 2001, Compaq Computer Corporation.
003     * Copyright 2006, DSRG, Concordia University and others.
004     */
005    
006    package javafe.ast;
007    
008    import java.io.OutputStream;
009    import java.io.ByteArrayOutputStream;
010    import java.io.IOException;
011    
012    import javafe.util.Assert;
013    import javafe.util.Location;
014    
015    // FIXME - should this write Strings instead of bytes?
016    public abstract class PrettyPrint {
017      
018      /*****************************************************************************
019       * * Creation & delegation support: * *
020       ****************************************************************************/
021      
022      /**
023       * The only instance front-end code should use to pretty print information.
024       * <p>
025       * 
026       * Will be some subclass of PrettyPrint; defaults to an instance of
027       * StandardPrettyPrint. Extensions should replace with an instance that
028       * understands how to pretty print the extensions.
029       */
030      public static /*@ non_null */ PrettyPrint inst = new StandardPrettyPrint();
031      
032      /**
033       * When an instance of PrettyPrint wishes to call itself recursively, it does
034       * not do so by using this, but rather by using this explicit self instance
035       * variable.
036       * <p>
037       * 
038       * This allows instances of PrettyPrint to be extended at runtime (rather than
039       * by compile-time static subclassing) using the DelegatingPrettyPrint class.
040       * See javafe.tc.TypePrint for an example of how this may be done.
041       */
042      /*@ spec_public */ protected /*@ non_null */ PrettyPrint self;
043      
044      /**
045       * Create a normal instance of PrettyPrint that does not have a runtime
046       * extension.
047       */
048      //@ ensures this.self == this;
049      protected PrettyPrint() {
050        this.self = this;
051      }
052      
053      /**
054       * Create an instance of PrettyPrint that has a runtime extension.
055       * <p>
056       * 
057       * Self should be an instance of DelegatingPrettyPrint that eventually calls
058       * us after some amount of filtering.
059       */
060      //@ ensures this.self == self;
061      protected PrettyPrint(/*@ non_null */ PrettyPrint self) {
062        this.self = self;
063      }
064      
065      /*****************************************************************************
066       * * Variables controling printing: * *
067       ****************************************************************************/
068      
069      public static int INDENT = 3;
070      
071      /**
072       * Should we display code that is inferred?
073       * <p>
074       * 
075       * E.g., the inferred "this.", superclass constructor calls, etc.
076       */
077      public static boolean displayInferred = false;
078      
079      /*****************************************************************************
080       * * Procedures to print various things: * *
081       ****************************************************************************/
082      
083      /**
084       * Print a compilation onto to a stream. Works best when <code>o</code> is
085       * positioned at the start of a new line.
086       */
087      
088      public abstract void print(/*@ non_null */ OutputStream o, CompilationUnit cu);
089      
090      /**
091       * Print a type declaration onto to a stream.
092       * <p>
093       * 
094       * Ends with a newline.
095       * <p>
096       */
097      public void print(/*@ non_null */ OutputStream o, int ind, TypeDecl d) {
098        printnoln(o, ind, d);
099        writeln(o);
100      }
101      
102      /**
103       * Print a type declaration onto to a stream, without a final newline.
104       * <p>
105       */
106      public abstract void printnoln(/*@ non_null */ OutputStream o, int ind, TypeDecl d);
107      
108      /**
109       * Print a statement. Assumes that <code>s</code> should be printed starting
110       * at the current position of <code>o</code>. It does <em>not</em> print
111       * a new-line at the end of the statement. However, if the statement needs to
112       * span multiple lines (for example, because it has embedded statements), then
113       * these lines are indented by <code>ind</code> spaces.
114       */
115      
116      public abstract void print(/*@ non_null */ OutputStream o, int ind, Stmt s);
117      
118      /**
119       * Print a member or static initializer of a type declaration. Assumes that
120       * <code>s</code> should be printed starting at the current position of
121       * <code>o</code>. If the declaration needs to span multiple lines (for
122       * example, to print the statements in the body of a method), then these lines
123       * are indented by <code>ind</code> spaces. It should leave <code>o</code>
124       * at the start of a new-line.
125       */
126      
127      //@ requires d != null ==> d.hasParent;
128      public abstract void print(/*@ non_null */ OutputStream o, 
129                                 int ind, 
130                                 /*@ nullable */ TypeDeclElem d,
131                                 /*@ non_null */ Identifier classId, 
132                                 boolean showBody);
133      
134      public abstract void print(/*@ non_null */ OutputStream o, TypeNameVec tns);
135      
136      public abstract void print(/*@ non_null */ OutputStream o, int ind, FormalParaDeclVec fps);
137      
138      public abstract void print(/*@ non_null */ OutputStream o, int ind, ExprVec es);
139      
140      public abstract void print(/*@ non_null */ OutputStream o, GenericVarDecl d);
141      
142      public abstract void print(/*@ non_null */ OutputStream o, int ind, LocalVarDecl d,
143          boolean showBody);
144      
145      public abstract void print(/*@ non_null */ OutputStream o, int ind, FieldDecl d,
146          boolean showBody);
147      
148      public abstract void print(/*@ non_null */ OutputStream o, /*@ non_null */ Type t);
149      
150      public abstract void print(/*@ non_null */ OutputStream o, Name n);
151      
152      public abstract void print(/*@ non_null */ OutputStream o, int ind, ObjectDesignator od);
153      
154      public abstract void print(/*@ non_null */ OutputStream o, int ind, VarInit e);
155      
156      /**
157       * Print a lexical pragma. Assumes <code>o</code> is at the start of the
158       * line; should leave <code>o</code> at the start of a new line.
159       */
160      
161      public abstract void print(/*@ non_null */ OutputStream o, /*@ non_null */ LexicalPragma lp);
162      
163      public abstract void print(/*@ non_null */ OutputStream o, 
164                                 int ind, 
165                                 /*@ non_null */ TypeDeclElemPragma tp);
166      
167      /**
168       * TODO Fill in class description
169       * 
170       * @author David R. Cok
171       */
172    
173      /**
174       * Print a member or static initializer of a type declaration. Assumes that
175       * <code>s</code> should be printed starting at the current position of
176       * <code>o</code>. If the declaration needs to span multiple lines (for
177       * example, to print the statements in the body of a method), then these lines
178       * are indented by <code>ind</code> spaces. It should leave <code>o</code>
179       * at the start of a new-line.
180       */
181      
182      public abstract void print(/*@ non_null */ OutputStream o, int ind, /*@ non_null */ ModifierPragma mp);
183      public abstract void print(/*@ non_null */ OutputStream o, int ind, /*@ non_null */ StmtPragma sp);
184      public abstract void print(/*@ non_null */ OutputStream o, int ind, /*@ non_null */ TypeModifierPragma tp);
185      
186      /**
187       * Writes an Object (a type of ASTNode) to the given PrintStream, followed by
188       * an end-of-line.
189       * 
190       * @param out The PrintStream to write to
191       * @param e The expression to write
192       */
193      public void println(/*@ non_null */ java.io.PrintStream out, Object e) {
194        out.println(e.toString());
195      }
196      
197      /**
198       * Writes an Expr (a type of ASTNode) to the given PrintStream, followed by an
199       * end-of-line.
200       * 
201       * @param out The PrintStream to write to
202       * @param e The expression to write
203       */
204      public void println(/*@ non_null */ java.io.PrintStream out, Expr e) {
205        print(out, 0, e);
206        out.println("");
207      }
208      
209      /**
210       * Writes an ObjectDesignator (a type of ASTNode) to the given PrintStream,
211       * followed by an end-of-line.
212       * 
213       * @param out The PrintStream to write to
214       * @param e The expression to write
215       */
216      public void println(/*@ non_null */ java.io.PrintStream out, ObjectDesignator e) {
217        print(out, 0, e);
218        out.println("");
219      }
220      
221      //// toString methods
222      
223      /**
224       * Returns a canonical text representation for literal values. Requires
225       * <code>tag</code> is one of constants on the left of this table:
226       * 
227       * <center><code><table>
228       <tr> <td> TagConstants.BOOLEANLIT </td> <td> Boolean </td> </tr>
229       <tr> <td> TagConstants.CHARLIT </td>   <td> Integer </td> </tr>
230       <tr> <td> TagConstants.DOUBLELIT </td> <td> Double </td> </tr>
231       <tr> <td> TagConstants.FLOATLIT </td>  <td> Float </td> </tr>
232       <tr> <td> TagConstants.INTLIT </td>    <td> Integer </td> </tr>
233       <tr> <td> TagConstants.LONGLIT </td>   <td> Long </td> </tr>
234       <tr> <td> TagConstants.STRINGLIT </td> <td> String </td> </tr>
235       </center></code> </table>
236       * 
237       * and that <code>val</code> is an instance of the corresponding type on the
238       * right.
239       */
240      
241      /*
242       * @ requires ( (tag==TagConstants.BOOLEANLIT) || (tag==TagConstants.INTLIT) ||
243       * (tag==TagConstants.LONGLIT) || (tag==TagConstants.FLOATLIT) ||
244       * (tag==TagConstants.DOUBLELIT) || (tag==TagConstants.STRINGLIT) ||
245       * (tag==TagConstants.CHARLIT) );
246       */
247      /*
248       * @ requires ( ((tag==TagConstants.BOOLEANLIT) ==> (val instanceof Boolean)) &&
249       * ((tag==TagConstants.INTLIT) ==> (val instanceof Integer)) &&
250       * ((tag==TagConstants.LONGLIT) ==> (val instanceof Long)) &&
251       * ((tag==TagConstants.FLOATLIT) ==> (val instanceof Float)) &&
252       * ((tag==TagConstants.DOUBLELIT) ==> (val instanceof Double)) &&
253       * ((tag==TagConstants.STRINGLIT) ==> (val instanceof String)) &&
254       * ((tag==TagConstants.CHARLIT) ==> (val instanceof Integer)) );
255       */
256      public static /*@non_null*/ String toCanonicalString(int tag, Object val) {
257        if (tag == TagConstants.BOOLEANLIT) return val.toString();
258        if (tag == TagConstants.DOUBLELIT) return val.toString() + "D";
259        if (tag == TagConstants.FLOATLIT) return val.toString() + "F";
260        
261        if (tag == TagConstants.INTLIT) {
262          //@ assert val instanceof Integer;
263          int v = ((Integer)val).intValue();
264          if (v == Integer.MIN_VALUE) return "0x80000000";
265          else if (v < 0) return "0x" + Integer.toHexString(v);
266          else return Integer.toString(v);
267        }
268        
269        if (tag == TagConstants.LONGLIT) {
270          long v = ((Long)val).longValue();
271          if (v == Long.MIN_VALUE) return "0x8000000000000000L";
272          else if (v < 0) return "0x" + Long.toHexString(v) + "L";
273          else return Long.toString(v) + "L";
274        }
275        
276        if (tag == TagConstants.CHARLIT || tag == TagConstants.STRINGLIT) {
277          char quote;
278          if (tag == TagConstants.CHARLIT) {
279            quote = '\'';
280            val = new Character((char)((Integer)val).intValue());
281          } else quote = '\"';
282          String s = val.toString();
283          StringBuffer result = new StringBuffer(s.length() + 2);
284          result.append(quote);
285          for (int i = 0, len = s.length(); i < len; i++) {
286            char c = s.charAt(i);
287            switch (c) {
288              case '\b':
289                result.append("\\b");
290                break;
291              case '\t':
292                result.append("\\t");
293                break;
294              case '\n':
295                result.append("\\n");
296                break;
297              case '\f':
298                result.append("\\f");
299                break;
300              case '\r':
301                result.append("\\r");
302                break;
303              case '\"':
304                result.append("\\\"");
305                break;
306              case '\'':
307                result.append("\\'");
308                break;
309              case '\\':
310                result.append("\\\\");
311                break;
312              default:
313                if (32 <= c && c < 128) result.append(c);
314                else {
315                  result.append("\\u");
316                  for (int j = 12; j >= 0; j -= 4)
317                    result.append(Character.forDigit((c >> j) & 0xf, 16));
318                }
319            }
320          }
321          result.append(quote);
322          return result.toString();
323        }
324        
325        Assert.precondition(false);
326        return null; // Dummy
327      }
328      
329      public /*@non_null*/ String toString(int tag) {
330        // Best version available in the front end:
331        return javafe.tc.TagConstants.toString(tag);
332      }
333      
334      public final /*@non_null*/ String toString(TypeNameVec tns) {
335        ByteArrayOutputStream result = new ByteArrayOutputStream(20);
336        print(result, tns);
337        return result.toString();
338      }
339      
340      public final /*@non_null*/ String toString(FormalParaDeclVec fps) {
341        ByteArrayOutputStream result = new ByteArrayOutputStream(20);
342        print(result, 0, fps);
343        return result.toString();
344      }
345      
346      public final /*@non_null*/ String toString(ExprVec es) {
347        ByteArrayOutputStream result = new ByteArrayOutputStream(20);
348        print(result, 0, es);
349        return result.toString();
350      }
351      
352      public final /*@non_null*/ String toString(GenericVarDecl d) {
353        ByteArrayOutputStream result = new ByteArrayOutputStream(20);
354        print(result, d);
355        return result.toString();
356      }
357      
358      public final /*@non_null*/ String toString(LocalVarDecl d, boolean showBody) {
359        ByteArrayOutputStream result = new ByteArrayOutputStream(20);
360        print(result, 0, d, showBody);
361        return result.toString();
362      }
363      
364      public final /*@non_null*/ String toString(FieldDecl d, boolean showBody) {
365        ByteArrayOutputStream result = new ByteArrayOutputStream(20);
366        print(result, 0, d, showBody);
367        return result.toString();
368      }
369      
370      public final /*@non_null*/ String toString(Type t) {
371        ByteArrayOutputStream result = new ByteArrayOutputStream(20);
372        print(result, t);
373        return result.toString();
374      }
375      
376      public final /*@non_null*/ String toString(Name n) {
377        ByteArrayOutputStream result = new ByteArrayOutputStream(20);
378        print(result, n);
379        return result.toString();
380      }
381      
382      public final /*@non_null*/ String toString(VarInit e) {
383        ByteArrayOutputStream result = new ByteArrayOutputStream(20);
384        print(result, 0, e);
385        return result.toString();
386      }
387      
388      public final /*@non_null*/ String toString(ObjectDesignator od) {
389        ByteArrayOutputStream result = new ByteArrayOutputStream(20);
390        print(result, 0, od);
391        return result.toString();
392      }
393      
394      //// Helper methods
395      
396      public static void writeln(/*@ non_null */ OutputStream o) {
397        write(o, '\n');
398      }
399      
400      public static void writeln(/*@ non_null */ OutputStream o, /*@ non_null */ String s) {
401        write(o, s);
402        write(o, '\n');
403      }
404      
405      public static void write(/*@ non_null */ OutputStream o, char c) {
406        try {
407          o.write((byte)c);
408        } catch (IOException e) {
409          Assert.fail("IO exception");
410        }
411      }
412      
413      public static void write(/*@ non_null */ OutputStream o, /*@ non_null */ String s) {
414        byte[] outBuf = s.getBytes();
415        try {
416          o.write(outBuf);
417        } catch (IOException e) {
418          Assert.fail("IO Exception");
419        }
420      }
421      
422      public static void spaces(/*@ non_null */ OutputStream o, int number) {
423        try {
424          while (number > 0) {
425            int i = Math.min(number, _spaces.length);
426            o.write(_spaces, 0, i);
427            number -= i;
428          }
429        } catch (IOException e) {
430          Assert.fail("IO Exception");
431        }
432      }
433      
434      private static /*@non_null*/ byte[] _spaces = { (byte)' ', (byte)' ', (byte)' ', (byte)' ',
435                                        (byte)' ', /* 5 spaces */
436                                        (byte)' ', (byte)' ', (byte)' ', (byte)' ',
437                                        (byte)' ', /* 5 spaces */
438                                        (byte)' ', (byte)' ', (byte)' ', (byte)' ',
439                                        (byte)' ', /* 5 spaces */
440                                        (byte)' ', (byte)' ', (byte)' ', (byte)' ',
441                                        (byte)' ', /* 5 spaces */
442                                        (byte)' ', (byte)' ', (byte)' ', (byte)' ',
443                                        (byte)' ', /* 5 spaces */
444                                       (byte)' ', (byte)' ', (byte)' ', (byte)' ',
445                                       (byte)' ' /* 5 spaces */
446      };
447    }