001/*
002 * Copyright (c) 2012-2018 Institut National des Sciences Appliquées de Lyon (INSA Lyon) and others
003 *
004 * This program and the accompanying materials are made available under the
005 * terms of the Eclipse Public License 2.0 which is available at
006 * http://www.eclipse.org/legal/epl-2.0.
007 *
008 * SPDX-License-Identifier: EPL-2.0
009 */
010
011package gololang.ir;
012
013import java.io.PrintStream;
014
015public class IrTreeDumper implements GoloIrVisitor {
016
017  private final PrintStream out;
018  private int spacing = 0;
019  private GoloModule currentModule;
020
021  public IrTreeDumper() {
022    this(System.out);
023  }
024
025  public IrTreeDumper(PrintStream out) {
026    this.out = out;
027  }
028
029  private void space() {
030    this.out.print("# ");
031    for (int i = 0; i < spacing; i++) {
032      this.out.print(" ");
033    }
034  }
035
036  private void incr() {
037    spacing += 2;
038  }
039
040  private void decr() {
041    spacing -= 2;
042  }
043
044  @Override
045  public void visitModule(GoloModule module) {
046    if (currentModule == module) {
047      incr();
048      space();
049      this.out.println("current module");
050      decr();
051      return;
052    }
053    currentModule = module;
054    space();
055    this.out.print(module.getPackageAndClass());
056    this.out.print(" [Local References: ");
057    this.out.print(System.identityHashCode(module.getReferenceTable()));
058    this.out.println("]");
059    module.walk(this);
060  }
061
062  @Override
063  public void visitModuleImport(ModuleImport moduleImport) {
064    incr();
065    space();
066    this.out.append(" - ").println(moduleImport);
067    moduleImport.walk(this);
068    decr();
069  }
070
071  @Override
072  public void visitNamedAugmentation(NamedAugmentation namedAugmentation) {
073    incr();
074    space();
075    this.out.append("Named Augmentation ").println(namedAugmentation.getName());
076    namedAugmentation.walk(this);
077    decr();
078  }
079
080  @Override
081  public void visitAugmentation(Augmentation augmentation) {
082    incr();
083    space();
084    this.out.append("Augmentation on ").println(augmentation.getTarget());
085    if (augmentation.hasNames()) {
086      incr();
087      for (String name : augmentation.getNames()) {
088        space();
089        this.out.append("Named Augmentation ").println(name);
090      }
091      decr();
092    }
093    augmentation.walk(this);
094    decr();
095  }
096
097  @Override
098  public void visitStruct(Struct struct) {
099    incr();
100    space();
101    this.out.append("Struct ").println(struct.getPackageAndClass().className());
102    space();
103    this.out.append(" - target class = ").println(struct.getPackageAndClass());
104    incr();
105    space();
106    this.out.println("Members: ");
107    struct.walk(this);
108    decr();
109    decr();
110  }
111
112  @Override
113  public void visitUnion(Union union) {
114    incr();
115    space();
116    this.out.append("Union ").println(union.getPackageAndClass().className());
117    space();
118    this.out.append(" - target class = ").println(union.getPackageAndClass());
119    union.walk(this);
120    decr();
121  }
122
123  @Override
124  public void visitUnionValue(UnionValue value) {
125    incr();
126    space();
127    this.out.append("Value ").println(value.getPackageAndClass().className());
128    space();
129    this.out.append(" - target class = ").println(value.getPackageAndClass());
130    if (value.hasMembers()) {
131      incr();
132      space();
133      this.out.println("Members: ");
134      value.walk(this);
135      decr();
136    }
137    decr();
138  }
139
140  @Override
141  public void visitFunction(GoloFunction function) {
142    incr();
143    space();
144    if (function.isLocal()) {
145      this.out.print("Local function ");
146    } else {
147      this.out.print("Function ");
148    }
149    this.out.append(function.getName()).append(" = ");
150    visitFunctionDefinition(function);
151    decr();
152  }
153
154  private void visitFunctionDefinition(GoloFunction function) {
155    this.out.print("|");
156    boolean first = true;
157    for (String param : function.getParameterNames()) {
158      if (first) {
159        first = false;
160      } else {
161        this.out.print(", ");
162      }
163      this.out.print(param);
164    }
165    this.out.print("|");
166    if (function.isVarargs()) {
167      this.out.print(" (varargs)");
168    }
169    if (function.isSynthetic()) {
170      this.out.format(" (synthetic, %s synthetic parameters)",
171          function.getSyntheticParameterCount());
172      if (function.getSyntheticSelfName() != null) {
173        this.out.append(" (selfname: ")
174          .append(function.getSyntheticSelfName()).append(")");
175      }
176    }
177    this.out.println();
178    function.walk(this);
179  }
180
181  @Override
182  public void visitDecorator(Decorator decorator) {
183    incr();
184    space();
185    this.out.println("@Decorator");
186    decorator.walk(this);
187    decr();
188  }
189
190  @Override
191  public void visitBlock(Block block) {
192    if (block.isEmpty()) { return; }
193    incr();
194    space();
195    this.out.print("Block");
196    this.out.print(" [Local References: ");
197    this.out.print(System.identityHashCode(block.getReferenceTable()));
198    this.out.print(" -> ");
199    this.out.print(System.identityHashCode(block.getReferenceTable().parent()));
200    this.out.println("]");
201    block.walk(this);
202    decr();
203  }
204
205  @Override
206  public void visitLocalReference(LocalReference ref) {
207    incr();
208    space();
209    this.out.append(" - ").println(ref);
210    decr();
211  }
212
213  @Override
214  public void visitConstantStatement(ConstantStatement constantStatement) {
215    incr();
216    space();
217    Object v = constantStatement.value();
218    this.out.append("Constant = ").print(v);
219    if (v != null) {
220      this.out.append(" (").append(v.getClass().getName()).append(")");
221    }
222    this.out.println();
223    decr();
224  }
225
226  @Override
227  public void visitReturnStatement(ReturnStatement returnStatement) {
228    incr();
229    space();
230    this.out.println("Return");
231    returnStatement.walk(this);
232    decr();
233  }
234
235  @Override
236  public void visitFunctionInvocation(FunctionInvocation functionInvocation) {
237    incr();
238    space();
239    this.out.append("Function call: ").print(functionInvocation.getName());
240    this.out.append(", on reference? -> ").print(functionInvocation.isOnReference());
241    this.out.append(", on module state? -> ").print(functionInvocation.isOnModuleState());
242    this.out.append(", anonymous? -> ").print(functionInvocation.isAnonymous());
243    this.out.append(", constant? -> ").print(functionInvocation.isConstant());
244    this.out.append(", named arguments? -> ").println(functionInvocation.usesNamedArguments());
245    functionInvocation.walk(this);
246    printLocalDeclarations(functionInvocation);
247    decr();
248  }
249
250  @Override
251  public void visitAssignmentStatement(AssignmentStatement assignmentStatement) {
252    incr();
253    space();
254    this.out.append("Assignment: ")
255      .append(assignmentStatement.getLocalReference().toString())
256      .println(assignmentStatement.isDeclaring() ? " (declaring)" : "");
257    assignmentStatement.walk(this);
258    decr();
259  }
260
261  @Override
262  public void visitDestructuringAssignment(DestructuringAssignment assignment) {
263    incr();
264    space();
265    this.out.format(
266        "Destructuring assignement: {declaring=%s, varargs=%s}%n",
267        assignment.isDeclaring(),
268        assignment.isVarargs());
269    assignment.walk(this);
270    decr();
271  }
272
273  @Override
274  public void visitReferenceLookup(ReferenceLookup referenceLookup) {
275    incr();
276    space();
277    this.out.append("Reference lookup: ").println(referenceLookup.getName());
278    printLocalDeclarations(referenceLookup);
279    decr();
280  }
281
282  @Override
283  public void visitConditionalBranching(ConditionalBranching conditionalBranching) {
284    incr();
285    space();
286    this.out.println("Conditional");
287    conditionalBranching.walk(this);
288    decr();
289  }
290
291  @Override
292  public void visitCaseStatement(CaseStatement caseStatement) {
293    incr();
294    space();
295    this.out.println("Case");
296    incr();
297    for (WhenClause<Block> c : caseStatement.getClauses()) {
298      c.accept(this);
299    }
300    space();
301    this.out.println("Otherwise");
302    caseStatement.getOtherwise().accept(this);
303    decr();
304  }
305
306  @Override
307  public void visitMatchExpression(MatchExpression matchExpression) {
308    incr();
309    space();
310    this.out.println("Match");
311    incr();
312    for (WhenClause<?> c : matchExpression.getClauses()) {
313      c.accept(this);
314    }
315    space();
316    this.out.println("Otherwise");
317    matchExpression.getOtherwise().accept(this);
318    printLocalDeclarations(matchExpression);
319    decr();
320  }
321
322  @Override
323  public void visitWhenClause(WhenClause<?> whenClause) {
324    space();
325    this.out.println("When");
326    incr();
327    whenClause.walk(this);
328    decr();
329  }
330
331  @Override
332  public void visitBinaryOperation(BinaryOperation binaryOperation) {
333    incr();
334    space();
335    this.out.append("Binary operator: ").println(binaryOperation.getType());
336    binaryOperation.walk(this);
337    printLocalDeclarations(binaryOperation);
338    decr();
339  }
340
341  @Override
342  public void visitUnaryOperation(UnaryOperation unaryOperation) {
343    incr();
344    space();
345    this.out.append("Unary operator: ").println(unaryOperation.getType());
346    unaryOperation.walk(this);
347    printLocalDeclarations(unaryOperation);
348    decr();
349  }
350
351  @Override
352  public void visitLoopStatement(LoopStatement loopStatement) {
353    incr();
354    space();
355    this.out.println("Loop");
356    loopStatement.walk(this);
357    decr();
358  }
359
360  @Override
361  public void visitForEachLoopStatement(ForEachLoopStatement foreachStatement) {
362    incr();
363    space();
364    this.out.println("Foreach");
365    incr();
366    for (LocalReference ref : foreachStatement.getReferences()) {
367      ref.accept(this);
368    }
369    foreachStatement.getIterable().accept(this);
370    if (foreachStatement.hasWhenClause()) {
371      space();
372      this.out.println("When:");
373      foreachStatement.getWhenClause().accept(this);
374    }
375    foreachStatement.getBlock().accept(this);
376    decr();
377    decr();
378  }
379
380  @Override
381  public void visitMethodInvocation(MethodInvocation methodInvocation) {
382    incr();
383    space();
384    this.out.format("Method invocation: %s, null safe? -> %s%n",
385        methodInvocation.getName(),
386        methodInvocation.isNullSafeGuarded());
387    methodInvocation.walk(this);
388    printLocalDeclarations(methodInvocation);
389    decr();
390  }
391
392  @Override
393  public void visitThrowStatement(ThrowStatement throwStatement) {
394    incr();
395    space();
396    this.out.println("Throw");
397    throwStatement.walk(this);
398    decr();
399  }
400
401  @Override
402  public void visitTryCatchFinally(TryCatchFinally tryCatchFinally) {
403    incr();
404    space();
405    this.out.println("Try");
406    tryCatchFinally.getTryBlock().accept(this);
407    if (tryCatchFinally.hasCatchBlock()) {
408      space();
409      this.out.append("Catch: ").println(tryCatchFinally.getExceptionId());
410      tryCatchFinally.getCatchBlock().accept(this);
411    }
412    if (tryCatchFinally.hasFinallyBlock()) {
413      space();
414      this.out.println("Finally");
415      tryCatchFinally.getFinallyBlock().accept(this);
416    }
417    decr();
418  }
419
420  @Override
421  public void visitClosureReference(ClosureReference closureReference) {
422    GoloFunction target = closureReference.getTarget();
423    incr();
424    space();
425    if (target.isAnonymous()) {
426      this.out.print("Closure: ");
427      incr();
428      visitFunctionDefinition(target);
429      decr();
430    } else {
431      this.out.printf(
432          "Closure reference: %s, regular arguments at index %d%n",
433          target.getName(),
434          target.getSyntheticParameterCount());
435      incr();
436      for (String refName : closureReference.getCapturedReferenceNames()) {
437        space();
438        this.out.append("- capture: ").println(refName);
439      }
440      decr();
441    }
442    decr();
443  }
444
445  @Override
446  public void visitLoopBreakFlowStatement(LoopBreakFlowStatement loopBreakFlowStatement) {
447    incr();
448    space();
449    this.out.append("Loop break flow: ").println(loopBreakFlowStatement.getType().name());
450    decr();
451  }
452
453  @Override
454  public void visitCollectionLiteral(CollectionLiteral collectionLiteral) {
455    incr();
456    space();
457    this.out.append("Collection literal of type: ").println(collectionLiteral.getType());
458    collectionLiteral.walk(this);
459    printLocalDeclarations(collectionLiteral);
460    decr();
461  }
462
463  @Override
464  public void visitCollectionComprehension(CollectionComprehension collectionComprehension) {
465    incr();
466    space();
467    this.out.append("Collection comprehension of type: ").println(collectionComprehension.getType());
468    incr();
469    space();
470    this.out.println("Expression: ");
471    collectionComprehension.expression().accept(this);
472    space();
473    this.out.println("Comprehension: ");
474    for (GoloStatement<?> b : collectionComprehension.loops()) {
475      b.accept(this);
476    }
477    printLocalDeclarations(collectionComprehension);
478    decr();
479    decr();
480  }
481
482  @Override
483  public void visitNamedArgument(NamedArgument namedArgument) {
484    incr();
485    space();
486    this.out.append("Named argument: ").println(namedArgument.getName());
487    namedArgument.expression().accept(this);
488    decr();
489  }
490
491  @Override
492  public void visitMember(Member member) {
493    space();
494    this.out.print(" - ");
495    this.out.print(member.getName());
496    this.out.println();
497  }
498
499  private void printLocalDeclarations(ExpressionStatement<?> expr) {
500    if (expr.hasLocalDeclarations()) {
501      incr();
502      space();
503      System.out.println("Local declaration:");
504      for (GoloAssignment<?> a : expr.declarations()) {
505        a.accept(this);
506      }
507      decr();
508    }
509  }
510
511  @Override
512  public void visitNoop(Noop noop) {
513    incr();
514    space();
515    this.out.append("Noop: ").println(noop.comment());
516    decr();
517  }
518
519  @Override
520  public void visitToplevelElements(ToplevelElements toplevel) {
521    toplevel.walk(this);
522  }
523}