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