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 }