1 /* 2 * Copyright 2016 Google Inc. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.turbine.tree; 18 19 import com.google.common.base.Joiner; 20 import com.google.common.base.Strings; 21 import com.google.common.collect.ImmutableList; 22 import com.google.common.collect.ImmutableSet; 23 import com.google.turbine.tree.Tree.Anno; 24 import com.google.turbine.tree.Tree.ClassLiteral; 25 import java.util.ArrayList; 26 import java.util.Collections; 27 import java.util.List; 28 29 /** A pretty-printer for {@link Tree}s. */ 30 public class Pretty implements Tree.Visitor<Void, Void> { 31 32 static String pretty(Tree tree) { 33 Pretty pretty = new Pretty(); 34 tree.accept(pretty, null); 35 return pretty.sb.toString(); 36 } 37 38 private final StringBuilder sb = new StringBuilder(); 39 int indent = 0; 40 boolean newLine = false; 41 42 void printLine() { 43 append('\n'); 44 newLine = true; 45 } 46 47 void printLine(String line) { 48 if (!newLine) { 49 append('\n'); 50 } 51 append(line).append('\n'); 52 newLine = true; 53 } 54 55 Pretty append(char c) { 56 if (c == '\n') { 57 newLine = true; 58 } else if (newLine) { 59 sb.append(Strings.repeat(" ", indent * 2)); 60 newLine = false; 61 } 62 sb.append(c); 63 return this; 64 } 65 66 Pretty append(String s) { 67 if (newLine) { 68 sb.append(Strings.repeat(" ", indent * 2)); 69 newLine = false; 70 } 71 sb.append(s); 72 return this; 73 } 74 75 @Override 76 public Void visitWildTy(Tree.WildTy wildTy, Void input) { 77 printAnnos(wildTy.annos()); 78 append('?'); 79 if (wildTy.lower().isPresent()) { 80 append(" super "); 81 wildTy.lower().get().accept(this, null); 82 } 83 if (wildTy.upper().isPresent()) { 84 append(" extends "); 85 wildTy.upper().get().accept(this, null); 86 } 87 return null; 88 } 89 90 @Override 91 public Void visitArrTy(Tree.ArrTy arrTy, Void input) { 92 arrTy.elem().accept(this, null); 93 if (!arrTy.annos().isEmpty()) { 94 append(' '); 95 printAnnos(arrTy.annos()); 96 } 97 append("[]"); 98 return null; 99 } 100 101 @Override 102 public Void visitPrimTy(Tree.PrimTy primTy, Void input) { 103 append(primTy.tykind().toString()); 104 return null; 105 } 106 107 @Override 108 public Void visitVoidTy(Tree.VoidTy primTy, Void input) { 109 append("void"); 110 return null; 111 } 112 113 @Override 114 public Void visitClassTy(Tree.ClassTy classTy, Void input) { 115 if (classTy.base().isPresent()) { 116 classTy.base().get().accept(this, null); 117 append('.'); 118 } 119 printAnnos(classTy.annos()); 120 append(classTy.name()); 121 if (!classTy.tyargs().isEmpty()) { 122 append('<'); 123 boolean first = true; 124 for (Tree t : classTy.tyargs()) { 125 if (!first) { 126 append(", "); 127 } 128 t.accept(this, null); 129 first = false; 130 } 131 append('>'); 132 } 133 return null; 134 } 135 136 @Override 137 public Void visitLiteral(Tree.Literal literal, Void input) { 138 append(literal.value().toString()); 139 return null; 140 } 141 142 @Override 143 public Void visitTypeCast(Tree.TypeCast typeCast, Void input) { 144 append('('); 145 typeCast.ty().accept(this, null); 146 append(") "); 147 typeCast.expr().accept(this, null); 148 return null; 149 } 150 151 @Override 152 public Void visitUnary(Tree.Unary unary, Void input) { 153 switch (unary.op()) { 154 case POST_INCR: 155 case POST_DECR: 156 unary.expr().accept(this, null); 157 append(unary.op().toString()); 158 break; 159 case PRE_INCR: 160 case PRE_DECR: 161 case UNARY_PLUS: 162 case NEG: 163 case NOT: 164 case BITWISE_COMP: 165 append(unary.op().toString()); 166 unary.expr().accept(this, null); 167 break; 168 default: 169 throw new AssertionError(unary.op().name()); 170 } 171 return null; 172 } 173 174 @Override 175 public Void visitBinary(Tree.Binary binary, Void input) { 176 append('('); 177 binary.lhs().accept(this, null); 178 append(" " + binary.op() + " "); 179 binary.rhs().accept(this, null); 180 append(')'); 181 return null; 182 } 183 184 @Override 185 public Void visitConstVarName(Tree.ConstVarName constVarName, Void input) { 186 append(Joiner.on('.').join(constVarName.name())); 187 return null; 188 } 189 190 @Override 191 public Void visitClassLiteral(ClassLiteral classLiteral, Void input) { 192 classLiteral.accept(this, input); 193 append(".class"); 194 return null; 195 } 196 197 @Override 198 public Void visitAssign(Tree.Assign assign, Void input) { 199 append(assign.name()).append(" = "); 200 assign.expr().accept(this, null); 201 return null; 202 } 203 204 @Override 205 public Void visitConditional(Tree.Conditional conditional, Void input) { 206 append("("); 207 conditional.cond().accept(this, null); 208 append(" ? "); 209 conditional.iftrue().accept(this, null); 210 append(" : "); 211 conditional.iffalse().accept(this, null); 212 append(")"); 213 return null; 214 } 215 216 @Override 217 public Void visitArrayInit(Tree.ArrayInit arrayInit, Void input) { 218 append('{'); 219 boolean first = true; 220 for (Tree.Expression e : arrayInit.exprs()) { 221 if (!first) { 222 append(", "); 223 } 224 e.accept(this, null); 225 first = false; 226 } 227 append('}'); 228 return null; 229 } 230 231 @Override 232 public Void visitCompUnit(Tree.CompUnit compUnit, Void input) { 233 if (compUnit.pkg().isPresent()) { 234 compUnit.pkg().get().accept(this, null); 235 printLine(); 236 } 237 for (Tree.ImportDecl i : compUnit.imports()) { 238 i.accept(this, null); 239 } 240 for (Tree.TyDecl decl : compUnit.decls()) { 241 printLine(); 242 decl.accept(this, null); 243 } 244 return null; 245 } 246 247 @Override 248 public Void visitImportDecl(Tree.ImportDecl importDecl, Void input) { 249 append("import "); 250 if (importDecl.stat()) { 251 append("static "); 252 } 253 append(Joiner.on('.').join(importDecl.type())); 254 if (importDecl.wild()) { 255 append(".*"); 256 } 257 append(";").append('\n'); 258 return null; 259 } 260 261 @Override 262 public Void visitVarDecl(Tree.VarDecl varDecl, Void input) { 263 printVarDecl(varDecl); 264 append(';'); 265 return null; 266 } 267 268 private void printVarDecl(Tree.VarDecl varDecl) { 269 printAnnos(varDecl.annos()); 270 printModifiers(varDecl.mods()); 271 varDecl.ty().accept(this, null); 272 append(' ').append(varDecl.name()); 273 if (varDecl.init().isPresent()) { 274 append(" = "); 275 varDecl.init().get().accept(this, null); 276 } 277 } 278 279 private void printAnnos(ImmutableList<Anno> annos) { 280 for (Tree.Anno anno : annos) { 281 anno.accept(this, null); 282 append(' '); 283 } 284 } 285 286 @Override 287 public Void visitMethDecl(Tree.MethDecl methDecl, Void input) { 288 for (Tree.Anno anno : methDecl.annos()) { 289 anno.accept(this, null); 290 printLine(); 291 } 292 printModifiers(methDecl.mods()); 293 if (!methDecl.typarams().isEmpty()) { 294 append('<'); 295 boolean first = true; 296 for (Tree.TyParam t : methDecl.typarams()) { 297 if (!first) { 298 append(", "); 299 } 300 t.accept(this, null); 301 first = false; 302 } 303 append('>'); 304 append(' '); 305 } 306 if (methDecl.ret().isPresent()) { 307 methDecl.ret().get().accept(this, null); 308 append(' '); 309 } 310 append(methDecl.name()); 311 append('('); 312 boolean first = true; 313 for (Tree.VarDecl param : methDecl.params()) { 314 if (!first) { 315 append(", "); 316 } 317 printVarDecl(param); 318 first = false; 319 } 320 append(')'); 321 if (!methDecl.exntys().isEmpty()) { 322 append(" throws "); 323 first = true; 324 for (Tree.Type e : methDecl.exntys()) { 325 if (!first) { 326 append(", "); 327 } 328 e.accept(this, null); 329 first = false; 330 } 331 } 332 if (methDecl.defaultValue().isPresent()) { 333 append(" default "); 334 methDecl.defaultValue().get().accept(this, null); 335 append(";"); 336 } else if (methDecl.mods().contains(TurbineModifier.ABSTRACT) 337 || methDecl.mods().contains(TurbineModifier.NATIVE)) { 338 append(";"); 339 } else { 340 append(" {}"); 341 } 342 return null; 343 } 344 345 @Override 346 public Void visitAnno(Tree.Anno anno, Void input) { 347 append('@'); 348 append(Joiner.on('.').join(anno.name())); 349 if (!anno.args().isEmpty()) { 350 append('('); 351 boolean first = true; 352 for (Tree.Expression e : anno.args()) { 353 if (!first) { 354 append(", "); 355 } 356 e.accept(this, null); 357 first = false; 358 } 359 append(')'); 360 } 361 return null; 362 } 363 364 @Override 365 public Void visitTyDecl(Tree.TyDecl tyDecl, Void input) { 366 for (Tree.Anno anno : tyDecl.annos()) { 367 anno.accept(this, null); 368 printLine(); 369 } 370 printModifiers(tyDecl.mods()); 371 switch (tyDecl.tykind()) { 372 case CLASS: 373 append("class"); 374 break; 375 case INTERFACE: 376 append("interface"); 377 break; 378 case ENUM: 379 append("enum"); 380 break; 381 case ANNOTATION: 382 append("@interface"); 383 break; 384 } 385 append(' ').append(tyDecl.name()); 386 if (!tyDecl.typarams().isEmpty()) { 387 append('<'); 388 boolean first = true; 389 for (Tree.TyParam t : tyDecl.typarams()) { 390 if (!first) { 391 append(", "); 392 } 393 t.accept(this, null); 394 first = false; 395 } 396 append('>'); 397 } 398 if (tyDecl.xtnds().isPresent()) { 399 append(" extends "); 400 tyDecl.xtnds().get().accept(this, null); 401 } 402 if (!tyDecl.impls().isEmpty()) { 403 append(" implements "); 404 boolean first = true; 405 for (Tree.ClassTy t : tyDecl.impls()) { 406 if (!first) { 407 append(", "); 408 } 409 t.accept(this, null); 410 first = false; 411 } 412 } 413 append(" {").append('\n'); 414 indent++; 415 switch (tyDecl.tykind()) { 416 case ENUM: 417 { 418 List<Tree> nonConsts = new ArrayList<>(); 419 for (Tree t : tyDecl.members()) { 420 if (t instanceof Tree.VarDecl) { 421 Tree.VarDecl decl = (Tree.VarDecl) t; 422 if (decl.mods().contains(TurbineModifier.ACC_ENUM)) { 423 append(decl.name()).append(',').append('\n'); 424 continue; 425 } 426 } 427 nonConsts.add(t); 428 } 429 printLine(";"); 430 boolean first = true; 431 for (Tree t : nonConsts) { 432 if (!first) { 433 printLine(); 434 } 435 t.accept(this, null); 436 first = false; 437 } 438 break; 439 } 440 default: 441 { 442 boolean first = true; 443 for (Tree t : tyDecl.members()) { 444 if (!first) { 445 printLine(); 446 } 447 t.accept(this, null); 448 first = false; 449 } 450 break; 451 } 452 } 453 indent--; 454 printLine("}"); 455 return null; 456 } 457 458 private void printModifiers(ImmutableSet<TurbineModifier> mods) { 459 List<TurbineModifier> modifiers = new ArrayList<>(mods); 460 Collections.sort(modifiers); 461 for (TurbineModifier mod : modifiers) { 462 switch (mod) { 463 case PRIVATE: 464 case PROTECTED: 465 case PUBLIC: 466 case ABSTRACT: 467 case FINAL: 468 case STATIC: 469 case VOLATILE: 470 case SYNCHRONIZED: 471 case STRICTFP: 472 case NATIVE: 473 case TRANSIENT: 474 case DEFAULT: 475 append(mod.toString()).append(' '); 476 break; 477 case ACC_SUPER: 478 case VARARGS: 479 case INTERFACE: 480 case ACC_ENUM: 481 case ACC_ANNOTATION: 482 case ACC_SYNTHETIC: 483 case ACC_BRIDGE: 484 break; 485 default: 486 throw new AssertionError(mod); 487 } 488 } 489 } 490 491 @Override 492 public Void visitTyParam(Tree.TyParam tyParam, Void input) { 493 printAnnos(tyParam.annos()); 494 append(tyParam.name()); 495 if (!tyParam.bounds().isEmpty()) { 496 append(" extends "); 497 boolean first = true; 498 for (Tree bound : tyParam.bounds()) { 499 if (!first) { 500 append(" & "); 501 } 502 bound.accept(this, null); 503 first = false; 504 } 505 } 506 return null; 507 } 508 509 @Override 510 public Void visitPkgDecl(Tree.PkgDecl pkgDecl, Void input) { 511 for (Tree.Anno anno : pkgDecl.annos()) { 512 anno.accept(this, null); 513 printLine(); 514 } 515 append("package ").append(Joiner.on('.').join(pkgDecl.name())).append(';'); 516 return null; 517 } 518 } 519