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