Home | History | Annotate | Download | only in tree
      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