001    /* Copyright (c) 2015, Jesper Öqvist <jesper.oqvist@cs.lth.se>
002     * All rights reserved.
003     *
004     * Redistribution and use in source and binary forms, with or without
005     * modification, are permitted provided that the following conditions are met:
006     *
007     * 1. Redistributions of source code must retain the above copyright notice,
008     * this list of conditions and the following disclaimer.
009     *
010     * 2. Redistributions in binary form must reproduce the above copyright notice,
011     * this list of conditions and the following disclaimer in the documentation
012     * and/or other materials provided with the distribution.
013     *
014     * 3. Neither the name of the copyright holder nor the names of its
015     * contributors may be used to endorse or promote products derived from this
016     * software without specific prior written permission.
017     *
018     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
022     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
028     * POSSIBILITY OF SUCH DAMAGE.
029     */
030    
031    import org.jastadd.util.PrettyPrintable;
032    import org.jastadd.util.PrettyPrinter;
033    
034    aspect PrettyPrintUtil {
035      ASTNode implements PrettyPrintable;
036    
037      /**
038       * Pretty-print this ASTNode.
039       * @return pretty-printed representation of this AST node
040       */
041      public String ASTNode.prettyPrint() {
042        ByteArrayOutputStream buf = new ByteArrayOutputStream();
043        prettyPrint(new PrettyPrinter("  ", new PrintStream(buf)));
044        return buf.toString();
045      }
046    
047      /**
048       * Pretty print this AST node to the target PrintStream.
049       * @param out target for pretty printing
050       */
051      public void ASTNode.prettyPrint(PrintStream out) {
052        prettyPrint(new PrettyPrinter("  ", out));
053      }
054    
055      /**
056       * @return the name of the class implementing this AST node
057       */
058      public String ASTNode.toString() {
059        return getClass().getName();
060      }
061    
062      public void ASTNode.prettyPrint(PrettyPrinter out) {
063      }
064    
065      /** Pretty-print Opt nodes only if they are not empty. */
066      public void Opt.prettyPrint(PrettyPrinter out) {
067        if (getNumChild() > 0) {
068          getChild(0).prettyPrint(out);
069        }
070      }
071    
072      /** Default list pretty printing prints all list elements. */
073      public void List.prettyPrint(PrettyPrinter out) {
074        for (int i = 0; i < getNumChild(); ++i) {
075          getChild(i).prettyPrint(out);
076        }
077      }
078    
079      public void Program.prettyPrint(PrettyPrinter out) {
080        for (Iterator iter = compilationUnitIterator(); iter.hasNext(); ) {
081          CompilationUnit cu = (CompilationUnit) iter.next();
082          if (cu.fromSource()) {
083            out.print(cu);
084          }
085        }
086      }
087    
088      /**
089       * Manually implemented because it is too complex for a template.
090       */
091      public void ForStmt.prettyPrint(PrettyPrinter out) {
092        out.print("for (");
093        if (getNumInitStmt() > 0) {
094          if (getInitStmt(0) instanceof VarDeclStmt) {
095            VarDeclStmt var = (VarDeclStmt) getInitStmt(0);
096            int minDimension = Integer.MAX_VALUE;
097            for (VariableDeclaration decl : var.getSingleDeclList()) {
098              minDimension = Math.min(minDimension, decl.type().dimension());
099            }
100    
101            // Print type.
102            out.print(var.getModifiers());
103            out.print(var.type().elementType().typeName());
104            for (int i = minDimension; i > 0; i--) {
105              out.print("[]");
106            }
107    
108            // Print individual declarations.
109            for (int i = 0; i < var.getNumSingleDecl(); ++i) {
110              if (i != 0) {
111                out.print(",");
112              }
113              VariableDeclaration decl = var.getSingleDecl(i);
114              out.print(" " + decl.name());
115              for (int j = decl.type().dimension() - minDimension; j > 0; j -= 1) {
116                out.print("[]");
117              }
118              if (decl.hasInit()) {
119                out.print(" = ");
120                out.print(decl.getInit());
121              }
122            }
123          } else if (getInitStmt(0) instanceof ExprStmt) {
124            ExprStmt stmt = (ExprStmt) getInitStmt(0);
125            out.print(stmt.getExpr());
126            for (int i = 1; i < getNumInitStmt(); i++) {
127              out.print(", ");
128              stmt = (ExprStmt)getInitStmt(i);
129              out.print(stmt.getExpr());
130            }
131          } else {
132            throw new Error("Unexpected initializer in for loop: " + getInitStmt(0));
133          }
134        }
135    
136        out.print("; ");
137        if (hasCondition()) {
138          out.print(getCondition());
139        }
140        out.print("; ");
141    
142        // Print update statements.
143        for (int i = 0; i < getNumUpdateStmt(); i++) {
144          if (i > 0) {
145            out.print(", ");
146          }
147          ExprStmt update = (ExprStmt) getUpdateStmt(i);
148          out.print(update.getExpr());
149        }
150    
151        out.print(") ");
152        if (getStmt() instanceof Block) {
153          out.print(getStmt());
154        } else {
155          out.print("{");
156          out.println();
157          out.indent(1);
158          out.print(getStmt());
159          out.println();
160          out.print("}");
161        }
162      }
163    
164      public void IfStmt.prettyPrint(PrettyPrinter out) {
165        out.print("if (");
166        out.print(getCondition());
167        out.print(") ");
168        if (getThen() instanceof Block) {
169          out.print(getThen());
170        } else {
171          out.print("{");
172          out.println();
173          out.indent(1);
174          out.print(getThen());
175          out.indent(0);
176          out.println();
177          out.print("}");
178        }
179        if (hasElse()) {
180          out.print(" else ");
181          if (getElse() instanceof Block) {
182            out.print(getElse());
183          } else {
184            out.print("{");
185            out.println();
186            out.indent(1);
187            out.print(getElse());
188            out.println();
189            out.print("}");
190          }
191        }
192      }
193    
194      public void WhileStmt.prettyPrint(PrettyPrinter out) {
195        out.print("while (");
196        out.print(getCondition());
197        out.print(") ");
198        if (getStmt() instanceof Block) {
199          out.print(getStmt());
200        } else {
201          out.print("{");
202          out.println();
203          out.indent(1);
204          out.print(getStmt());
205          out.println();
206          out.print("}");
207        }
208      }
209    
210      /**
211       * Has package name (not @primitive)
212       */
213      syn boolean TypeAccess.hasPackage() = decl().isReferenceType() && !getPackage().isEmpty();
214    
215      syn boolean InstanceInitializer.blockIsEmpty() = getBlock().getNumStmt() == 0;
216    
217      syn boolean StaticInitializer.blockIsEmpty() = getBlock().getNumStmt() == 0;
218    
219      syn boolean Block.hasStmts() = getNumStmt() > 0;
220    
221      /**
222       * @return <code>true</code> if there is any printable body decl
223       */
224      syn boolean ClassInstanceExpr.hasPrintableBodyDecl() {
225        TypeDecl decl = getTypeDecl();
226        for (int i = 0; i < decl.getNumBodyDecl(); ++i) {
227          if (decl.getBodyDecl(i) instanceof ConstructorDecl) {
228            ConstructorDecl cd = (ConstructorDecl) decl.getBodyDecl(i);
229            if (!cd.isImplicitConstructor()) {
230              return true;
231            }
232          } else {
233            return true;
234          }
235        }
236        return false;
237      }
238    
239      syn List<BodyDecl> ClassInstanceExpr.bodyDecls() = getTypeDecl().getBodyDeclList();
240    
241      syn boolean ClassDecl.hasModifiers() = getModifiers().getNumModifier() > 0;
242    
243      syn boolean InterfaceDecl.hasModifiers() = getModifiers().getNumModifier() > 0;
244    
245      syn boolean ConstructorDecl.hasModifiers() = getModifiers().getNumModifier() > 0;
246    
247      syn boolean MethodDecl.hasModifiers() = getModifiers().getNumModifier() > 0;
248    
249      syn boolean FieldDeclaration.hasModifiers() = getModifiers().getNumModifier() > 0;
250    
251      syn boolean VariableDeclaration.hasModifiers() = getModifiers().getNumModifier() > 0;
252    
253      syn boolean ParameterDeclaration.hasModifiers() = getModifiers().getNumModifier() > 0;
254    
255      syn boolean ConstructorDecl.hasExceptions() = getNumException() > 0;
256    
257      syn List<Stmt> ConstructorDecl.blockStmts() = getBlock().getStmtList();
258    
259      syn boolean MethodDecl.hasExceptions() = getNumException() > 0;
260    
261      syn boolean CompilationUnit.hasPackageDecl() = !getPackageDecl().isEmpty();
262    
263      syn String StringLiteral.escapedLiteral() = escape(getLITERAL());
264    
265      syn String CharacterLiteral.escapedLiteral() = escape(getLITERAL());
266    
267      syn boolean AbstractDot.needsDot() = !(getRight() instanceof ArrayAccess);
268    
269      /** The operator string used for pretty printing this expression. */
270      syn String Binary.printOp();
271      eq MulExpr.printOp() = "*";
272      eq DivExpr.printOp() = "/";
273      eq ModExpr.printOp() = "%";
274      eq AddExpr.printOp() = "+";
275      eq SubExpr.printOp() = "-";
276      eq LShiftExpr.printOp() = "<<";
277      eq RShiftExpr.printOp() = ">>";
278      eq URShiftExpr.printOp() = ">>>";
279      eq AndBitwiseExpr.printOp() = "&";
280      eq OrBitwiseExpr.printOp() = "|";
281      eq XorBitwiseExpr.printOp() = "^";
282      eq AndLogicalExpr.printOp() = "&&";
283      eq OrLogicalExpr.printOp() = "||";
284      eq LTExpr.printOp() = "<";
285      eq GTExpr.printOp() = ">";
286      eq LEExpr.printOp() = "<=";
287      eq GEExpr.printOp() = ">=";
288      eq EQExpr.printOp() = "==";
289      eq NEExpr.printOp() = "!=";
290    
291      /** The operator string used for pretty printing this expression. */
292      syn String AssignExpr.printOp();
293      eq AssignSimpleExpr.printOp() = "=";
294      eq AssignMulExpr.printOp() = "*=";
295      eq AssignDivExpr.printOp() = "/=";
296      eq AssignModExpr.printOp() = "%=";
297      eq AssignPlusExpr.printOp() = "+=";
298      eq AssignMinusExpr.printOp() = "-=";
299      eq AssignLShiftExpr.printOp() = "<<=";
300      eq AssignRShiftExpr.printOp() = ">>=";
301      eq AssignURShiftExpr.printOp() = ">>>=";
302      eq AssignAndExpr.printOp() = "&=";
303      eq AssignXorExpr.printOp() = "^=";
304      eq AssignOrExpr.printOp()  = "|=";
305    
306      syn String Unary.printPostOp() = "";
307      eq PostIncExpr.printPostOp() = "++";
308      eq PostDecExpr.printPostOp() = "--";
309    
310      syn String Unary.printPreOp() = "";
311      eq PreIncExpr.printPreOp() = "++";
312      eq PreDecExpr.printPreOp() = "--";
313      eq MinusExpr.printPreOp() = "-";
314      eq PlusExpr.printPreOp() = "+";
315      eq BitNotExpr.printPreOp() = "~";
316      eq LogNotExpr.printPreOp() = "!";
317    
318      /**
319       * Escapes a string literal.
320       * @param s string to escape
321       * @return escaped string literal
322       */
323      protected static String Literal.escape(String s) {
324        StringBuffer result = new StringBuffer();
325        for (int i=0; i < s.length(); i++) {
326          switch(s.charAt(i)) {
327            case '\b':
328              result.append("\\b");
329              break;
330            case '\t':
331              result.append("\\t");
332              break;
333            case '\n':
334              result.append("\\n");
335              break;
336            case '\f':
337              result.append("\\f");
338              break;
339            case '\r':
340              result.append("\\r");
341              break;
342            case '\"':
343              result.append("\\\"");
344              break;
345            case '\'':
346              result.append("\\\'");
347              break;
348            case '\\':
349              result.append("\\\\");
350              break;
351            default:
352              int value = (int)s.charAt(i);
353              if (value < 0x20 || (value > 0x7e)) {
354                result.append(asEscape(value));
355              } else {
356                result.append(s.charAt(i));
357              }
358          }
359        }
360        return result.toString();
361      }
362    
363      protected static String Literal.asEscape(int value) {
364        StringBuffer s = new StringBuffer("\\u");
365        String hex = Integer.toHexString(value);
366        for (int i = 0; i < 4-hex.length(); i++) {
367          s.append("0");
368        }
369        s.append(hex);
370        return s.toString();
371      }
372    
373    }