001/*
002 * Copyright (c) 2012-2017 Institut National des Sciences Appliquées de Lyon (INSA-Lyon)
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 */
009
010package org.eclipse.golo.compiler;
011
012import org.eclipse.golo.runtime.OperatorType;
013import org.eclipse.golo.compiler.ir.*;
014import org.eclipse.golo.compiler.parser.*;
015
016import java.util.Collections;
017import java.util.Deque;
018import java.util.LinkedList;
019import java.util.List;
020import java.util.stream.Collectors;
021
022import static org.eclipse.golo.compiler.ir.Builders.*;
023import static java.util.Collections.nCopies;
024import static org.eclipse.golo.compiler.GoloCompilationException.Problem.Type.*;
025import static gololang.Messages.message;
026
027public class ParseTreeToGoloIrVisitor implements GoloParserVisitor {
028
029  public ParseTreeToGoloIrVisitor() { }
030
031  @Override
032  public Object visit(ASTerror node, Object data) {
033    return null;
034  }
035
036  private static final class Context {
037    public GoloModule module;
038    private Deque<FunctionContainer> functionContainersStack = new LinkedList<>();
039    private Deque<Object> objectStack = new LinkedList<>();
040    private Deque<ReferenceTable> referenceTableStack = new LinkedList<>();
041    private GoloCompilationException.Builder exceptionBuilder;
042
043    public void push(Object object) {
044      objectStack.push(object);
045    }
046
047    public Object peek() {
048      return objectStack.peek();
049    }
050
051    public Object pop() {
052      return objectStack.pop();
053    }
054
055    public Block enterScope() {
056      ReferenceTable blockReferenceTable = referenceTableStack.peek().fork();
057      referenceTableStack.push(blockReferenceTable);
058      return block().ref(blockReferenceTable);
059    }
060
061    public void leaveScope() {
062      referenceTableStack.pop();
063    }
064
065    public GoloModule createModule(String name) {
066      ReferenceTable global = new ReferenceTable();
067      referenceTableStack.push(global);
068      module = new GoloModule(PackageAndClass.fromString(name), global);
069      functionContainersStack.push(module);
070      return module;
071    }
072
073    public void enterAugmentation(ASTAugmentDeclaration node) {
074      functionContainersStack.push(this.module.addAugmentation(
075            augment(node.getTarget()).with(node.getAugmentationNames()).ofAST(node)));
076    }
077
078    public void leaveAugmentation() {
079      functionContainersStack.pop();
080    }
081
082    public void enterNamedAugmentation(ASTNamedAugmentationDeclaration node) {
083      NamedAugmentation namedAugmentation = augmentation(node.getName()).ofAST(node);
084      functionContainersStack.push(namedAugmentation);
085      this.module.addNamedAugmentation(namedAugmentation);
086    }
087
088    public void leaveNamedAugmentation() {
089      functionContainersStack.pop();
090    }
091
092    public void addFunction(GoloFunction function) {
093      FunctionContainer container = this.functionContainersStack.peek();
094      if (container.getFunctions().contains(function)) {
095        GoloFunction firstDeclaration = null;
096        for (GoloFunction f : container.getFunctions()) {
097          if (function.equals(f)) {
098            firstDeclaration = f;
099          }
100        }
101        errorMessage(AMBIGUOUS_DECLARATION, function.getASTNode(),
102            message("ambiguous_function_declaration",
103                function.getName(),
104                firstDeclaration == null ? "unknown" : firstDeclaration.getASTNode().getPositionInSourceCode()));
105      }
106      container.addFunction(function);
107    }
108
109    public boolean checkExistingSubtype(GoloASTNode node, String name) {
110      GoloElement existing = module.getSubtypeByName(name);
111      if (existing != null) {
112        errorMessage(AMBIGUOUS_DECLARATION, node,
113            message("ambiguous_type_declaration",
114                name, existing.getASTNode().getPositionInSourceCode()));
115        return true;
116      }
117      return false;
118    }
119
120    public GoloFunction getOrCreateFunction() {
121      if (!(objectStack.peek() instanceof GoloFunction)) {
122        objectStack.push(functionDeclaration().synthetic().local().asClosure());
123      }
124      return (GoloFunction) objectStack.peek();
125    }
126
127    private LocalReference.Kind referenceKindOf(ASTLetOrVar.Type type, boolean moduleState) {
128      if (moduleState) {
129        return type == ASTLetOrVar.Type.LET
130          ? LocalReference.Kind.MODULE_CONSTANT
131          : LocalReference.Kind.MODULE_VARIABLE;
132      } else {
133        return type == ASTLetOrVar.Type.LET
134          ? LocalReference.Kind.CONSTANT
135          : LocalReference.Kind.VARIABLE;
136      }
137    }
138
139    public LocalReference getOrCreateReference(ASTLetOrVar node) {
140      return getOrCreateReference(node.getType(), node.getName(), node.isModuleState(), node);
141    }
142
143    public LocalReference getOrCreateReference(ASTDestructuringAssignment node, String name) {
144      return getOrCreateReference(node.getType(), name, false, node);
145    }
146
147    public LocalReference getReference(String name, GoloASTNode node) {
148      return referenceTableStack.peek().get(name);
149    }
150
151    private LocalReference getOrCreateReference(ASTLetOrVar.Type type, String name, boolean module, GoloASTNode node) {
152      if (type != null) {
153        LocalReference val = localRef(name).kind(referenceKindOf(type, module)).ofAST(node);
154        referenceTableStack.peek().add(val);
155        return val;
156      }
157      return getReference(name, node);
158    }
159
160    public void setExceptionBuilder(GoloCompilationException.Builder builder) {
161      exceptionBuilder = builder;
162    }
163
164    public GoloCompilationException.Builder getExceptionBuilder() {
165      return exceptionBuilder;
166    }
167
168    private GoloCompilationException.Builder getOrCreateExceptionBuilder() {
169      if (exceptionBuilder == null) {
170        exceptionBuilder = new GoloCompilationException.Builder(module.getPackageAndClass().toString());
171      }
172      return exceptionBuilder;
173    }
174
175    public void errorMessage(GoloCompilationException.Problem.Type type,
176                              GoloASTNode node,
177                              String message) {
178      String errorMessage = message + ' ' + message("source_position",
179          node.getPositionInSourceCode().getLine(),
180          node.getPositionInSourceCode().getColumn());
181      getOrCreateExceptionBuilder().report(type, node, errorMessage);
182    }
183
184  }
185
186  public GoloModule transform(ASTCompilationUnit compilationUnit, GoloCompilationException.Builder builder) {
187    Context context = new Context();
188    context.setExceptionBuilder(builder);
189    visit(compilationUnit, context);
190    return context.module;
191  }
192
193  @Override
194  public Object visit(SimpleNode node, Object data) {
195    throw new IllegalStateException("visit(SimpleNode) shall never be invoked: " + node.getClass());
196  }
197
198  @Override
199  public Object visit(ASTCompilationUnit node, Object data) {
200    return node.childrenAccept(this, data);
201  }
202
203  @Override
204  public Object visit(ASTModuleDeclaration node, Object data) {
205    Context context = (Context) data;
206    context.createModule(node.getName()).ofAST(node);
207    return node.childrenAccept(this, data);
208  }
209
210  @Override
211  public Object visit(ASTImportDeclaration node, Object data) {
212    Context context = (Context) data;
213    PackageAndClass name;
214    if (node.isRelative()) {
215      name = context.module.getPackageAndClass().createSiblingClass(node.getName());
216    } else {
217      name = PackageAndClass.fromString(node.getName());
218    }
219    if (node.getMultiple().isEmpty()) {
220      context.module.addImport(moduleImport(name).ofAST(node));
221    } else {
222      for (String sub : node.getMultiple()) {
223        context.module.addImport(moduleImport(name.createSubPackage(sub)).ofAST(node));
224      }
225    }
226    return node.childrenAccept(this, data);
227  }
228
229  @Override
230  public Object visit(ASTToplevelDeclaration node, Object data) {
231    return node.childrenAccept(this, data);
232  }
233
234  @Override
235  public Object visit(ASTMemberDeclaration node, Object data) {
236    Context context = (Context) data;
237    context.push(member(node.getName()).ofAST(node));
238    return context;
239  }
240
241  @Override
242  public Object visit(ASTStructDeclaration node, Object data) {
243    Context context = (Context) data;
244    if (!context.checkExistingSubtype(node, node.getName())) {
245      Struct theStruct = structure(node.getName()).ofAST(node);
246      for (int i = 0; i < node.jjtGetNumChildren(); i++) {
247        node.jjtGetChild(i).jjtAccept(this, context);
248        theStruct.withMember(context.pop());
249      }
250      context.module.addStruct(theStruct);
251    }
252    return context;
253  }
254
255  @Override
256  public Object visit(ASTUnionDeclaration node, Object data) {
257    Context context = (Context) data;
258    if (!context.checkExistingSubtype(node, node.getName())) {
259      context.push(union(node.getName()).ofAST(node));
260      node.childrenAccept(this, data);
261      context.module.addUnion((Union) context.pop());
262    }
263    return data;
264  }
265
266  @Override
267  public Object visit(ASTUnionValue node, Object data) {
268    Context context = (Context) data;
269    Union currentUnion = (Union) context.peek();
270    UnionValue value = currentUnion.createValue(node.getName()).ofAST(node);
271    for (int i = 0; i < node.jjtGetNumChildren(); i++) {
272      node.jjtGetChild(i).jjtAccept(this, context);
273      value.withMember(context.pop());
274    }
275
276    if (!currentUnion.addValue(value)) {
277      context.errorMessage(AMBIGUOUS_DECLARATION, node,
278          message("ambiguous_unionvalue_declaration", node.getName()));
279    }
280    return data;
281  }
282
283  @Override
284  public Object visit(ASTAugmentDeclaration node, Object data) {
285    Context context = (Context) data;
286    context.enterAugmentation(node);
287    node.childrenAccept(this, data);
288    context.leaveAugmentation();
289    return data;
290  }
291
292  @Override
293  public Object visit(ASTDecoratorDeclaration node, Object data) {
294    Context context = (Context) data;
295    node.childrenAccept(this, data);
296    context.push(
297        decorator(context.pop())
298        .constant(node.isConstant())
299        .ofAST(node));
300    return data;
301  }
302
303  @Override
304  public Object visit(ASTNamedAugmentationDeclaration node, Object data) {
305    Context context = (Context) data;
306    context.enterNamedAugmentation(node);
307    node.childrenAccept(this, data);
308    context.leaveNamedAugmentation();
309    return data;
310  }
311
312  @Override
313  public Object visit(ASTFunctionDeclaration node, Object data) {
314    Context context = (Context) data;
315    GoloFunction function = functionDeclaration().ofAST(node)
316        .name(node.getName())
317        .local(node.isLocal())
318        .inAugment(node.isAugmentation())
319        .decorator(node.isDecorator());
320    while (context.peek() instanceof Decorator) {
321      function.decoratedWith(context.pop());
322    }
323    context.push(function);
324    node.childrenAccept(this, data);
325    context.pop();
326    return data;
327  }
328
329  @Override
330  public Object visit(ASTContinue node, Object data) {
331    Context context = (Context) data;
332    LoopBreakFlowStatement statement = LoopBreakFlowStatement.newContinue();
333    node.setIrElement(statement);
334    context.push(statement);
335    return data;
336  }
337
338  @Override
339  public Object visit(ASTBreak node, Object data) {
340    Context context = (Context) data;
341    LoopBreakFlowStatement statement = LoopBreakFlowStatement.newBreak();
342    node.setIrElement(statement);
343    context.push(statement);
344    return data;
345  }
346
347  @Override
348  public Object visit(ASTFunction node, Object data) {
349    Context context = (Context) data;
350    GoloFunction function = context.getOrCreateFunction()
351      .ofAST(node)
352      .varargs(node.isVarargs())
353      .withParameters(node.getParameters());
354
355    if (node.isCompactForm()) {
356      // TODO: refactor
357      Node astChild = node.jjtGetChild(0);
358      ASTReturn astReturn = new ASTReturn(0);
359      astReturn.jjtAddChild(astChild, 0);
360      ASTBlock astBlock = new ASTBlock(0);
361      astBlock.jjtAddChild(astReturn, 0);
362      astBlock.jjtAccept(this, data);
363      // XXX
364      // if (function.isSynthetic()) {
365      //   context.pop();
366      // }
367      // node.jjtGetChild(0).jjtAccept(this, data);
368      // function.block(returns(context.pop()));
369      // context.push(function.getBlock());
370    } else {
371      node.childrenAccept(this, data);
372    }
373    if (function.isSynthetic()) {
374      context.pop();
375      context.push(function.asClosureReference());
376    } else {
377      context.addFunction(function);
378      context.pop();
379    }
380    return data;
381  }
382
383  @Override
384  public Object visit(ASTUnaryExpression node, Object data) {
385    Context context = (Context) data;
386    node.childrenAccept(this, data);
387    context.push(
388      not((ExpressionStatement) context.pop()).ofAST(node));
389    return data;
390  }
391
392  @Override
393  public Object visit(ASTLiteral node, Object data) {
394    Context context = (Context) data;
395    ConstantStatement constantStatement = constant(node.getLiteralValue());
396    context.push(constantStatement);
397    node.setIrElement(constantStatement);
398    return data;
399  }
400
401  @Override
402  public Object visit(ASTCollectionLiteral node, Object data) {
403    if (node.isComprehension()) {
404      return createCollectionComprehension(node, (Context) data);
405    }
406    return createCollectionLiteral(node, (Context) data);
407  }
408
409  private Object createCollectionLiteral(ASTCollectionLiteral node, Context context) {
410    CollectionLiteral collection = collection(node.getType()).ofAST(node);
411    for (int i = 0; i < node.jjtGetNumChildren(); i++) {
412      node.jjtGetChild(i).jjtAccept(this, context);
413      collection.add(context.pop());
414    }
415    context.push(collection);
416    return context;
417  }
418
419  private Object createCollectionComprehension(ASTCollectionLiteral node, Context context) {
420    CollectionComprehension col = collectionComprehension(node.getType()).ofAST(node);
421    node.jjtGetChild(0).jjtAccept(this, context);
422    col.expression(context.pop());
423    for (int i = 1; i < node.jjtGetNumChildren(); i++) {
424      node.jjtGetChild(i).jjtAccept(this, context);
425      col.loop(context.pop());
426    }
427    context.push(col);
428    return context;
429  }
430
431  @Override
432  public Object visit(ASTReference node, Object data) {
433    ((Context) data).push(refLookup(node.getName()).ofAST(node));
434    return data;
435  }
436
437  @Override
438  public Object visit(ASTLetOrVar node, Object data) {
439    Context context = (Context) data;
440    node.childrenAccept(this, data);
441    AssignmentStatement assignmentStatement = define(context.getOrCreateReference(node))
442      .as(context.pop())
443      .ofAST(node);
444    if (node.isModuleState()) {
445      context.module.addModuleStateInitializer(assignmentStatement);
446    } else {
447      context.push(assignmentStatement);
448    }
449    return data;
450  }
451
452  @Override
453  public Object visit(ASTAssignment node, Object data) {
454    Context context = (Context) data;
455    LocalReference reference = context.getReference(node.getName(), node);
456    node.childrenAccept(this, data);
457    if (reference == null) {
458      context.errorMessage(UNDECLARED_REFERENCE, node,
459          message("undeclared_reference", node.getName()));
460    } else {
461      AssignmentStatement assignmentStatement = assign(context.pop()).to(reference).ofAST(node);
462      context.push(assignmentStatement);
463    }
464    return data;
465  }
466
467  @Override
468  public Object visit(ASTDestructuringAssignment node, Object data) {
469    Context context = (Context) data;
470    node.jjtGetChild(0).jjtAccept(this, data);
471
472    DestructuringAssignment builder = destruct().ofAST(node)
473      .declaring(node.getType() != null)
474      .varargs(node.isVarargs())
475      .expression(context.pop());
476
477    for (String name : node.getNames()) {
478      LocalReference val = context.getOrCreateReference(node, name);
479      if (val != null) {
480        builder.to(val);
481      }
482    }
483    context.push(builder);
484    return data;
485  }
486
487  @Override
488  public Object visit(ASTReturn node, Object data) {
489    Context context = (Context) data;
490    if (node.jjtGetNumChildren() > 0) {
491      node.childrenAccept(this, data);
492    } else {
493      context.push(constant(null));
494    }
495    context.push(returns(context.pop()).ofAST(node));
496    return data;
497  }
498
499  @Override
500  public Object visit(ASTArgument node, Object data) {
501    Context context = (Context) data;
502    node.childrenAccept(this, data);
503    context.push(
504      node.isNamed()
505      ? namedArgument(node.getName()).value(context.pop())
506      : (ExpressionStatement) context.pop());
507    return data;
508  }
509
510  @Override
511  public Object visit(ASTThrow node, Object data) {
512    Context context = (Context) data;
513    node.childrenAccept(this, data);
514    context.push(raise(context.pop()).ofAST(node));
515    return data;
516  }
517
518  @Override
519  public Object visit(ASTBlock node, Object data) {
520    Context context = (Context) data;
521    Block block = context.enterScope();
522    node.setIrElement(block);
523    if (context.peek() instanceof GoloFunction) {
524      GoloFunction function = (GoloFunction) context.peek();
525      function.block(block);
526      if (function.isSynthetic()) {
527        context.pop();
528      }
529    }
530    context.push(block);
531    for (int i = 0; i < node.jjtGetNumChildren(); i++) {
532      GoloASTNode child = (GoloASTNode) node.jjtGetChild(i);
533      child.jjtAccept(this, data);
534      GoloStatement statement = (GoloStatement) context.pop();
535      block.addStatement(statement);
536    }
537    context.leaveScope();
538    return data;
539  }
540
541  @Override
542  public Object visit(ASTFunctionInvocation node, Object data) {
543    Context context = (Context) data;
544    context.push(visitAbstractInvocation(data, node, call(node.getName()).constant(node.isConstant())));
545    return data;
546  }
547
548  @Override
549  public Object visit(ASTMethodInvocation node, Object data) {
550    Context context = (Context) data;
551    context.push(visitAbstractInvocation(data, node, invoke(node.getName())));
552    return data;
553  }
554
555  @Override
556  public Object visit(ASTAnonymousFunctionInvocation node, Object data) {
557    Context context = (Context) data;
558    ExpressionStatement result = visitAbstractInvocation(data, node, functionInvocation().constant(node.isConstant()));
559    if (node.isOnExpression()) {
560      context.push(anonCall(context.pop(), result));
561    } else {
562      context.push(result);
563    }
564    return data;
565  }
566
567  private void checkNamedArgument(Context context, GoloASTNode node, AbstractInvocation invocation, ExpressionStatement statement) {
568    if (statement instanceof NamedArgument) {
569      if (!invocation.namedArgumentsComplete()) {
570        context.errorMessage(INCOMPLETE_NAMED_ARGUMENTS_USAGE, node,
571            message("incomplete_named_arguments_usage",
572            invocation.getClass(), invocation.getName()));
573      }
574      invocation.withNamedArguments();
575    }
576  }
577
578  private ExpressionStatement visitAbstractInvocation(Object data, GoloASTNode node, AbstractInvocation invocation) {
579    Context context = (Context) data;
580    invocation.ofAST(node);
581    int i = 0;
582    final int numChildren = node.jjtGetNumChildren();
583    for (i = 0; i < numChildren; i++) {
584      GoloASTNode argumentNode = (GoloASTNode) node.jjtGetChild(i);
585      if (argumentNode instanceof ASTAnonymousFunctionInvocation) {
586        break;
587      }
588      argumentNode.jjtAccept(this, context);
589      ExpressionStatement statement = (ExpressionStatement) context.pop();
590      checkNamedArgument(context, node, invocation, statement);
591      invocation.withArgs(statement);
592    }
593    ExpressionStatement result = invocation;
594    if (i < numChildren) {
595      for (; i < numChildren; i++) {
596        node.jjtGetChild(i).jjtAccept(this, context);
597        result = anonCall(result, context.pop());
598      }
599    }
600    return result;
601  }
602
603  @Override
604  public Object visit(ASTConditionalBranching node, Object data) {
605    Context context = (Context) data;
606    node.jjtGetChild(1).jjtAccept(this, data);
607    node.jjtGetChild(0).jjtAccept(this, data);
608
609    ConditionalBranching conditionalBranching = branch().ofAST(node)
610      .condition(context.pop())
611      .whenTrue(context.pop());
612
613    if (node.jjtGetNumChildren() > 2) {
614      node.jjtGetChild(2).jjtAccept(this, data);
615      conditionalBranching.otherwise(context.pop());
616    }
617    context.push(conditionalBranching);
618    return data;
619  }
620
621  private Object visitAlternatives(Object data, GoloASTNode node, Alternatives<?> alternatives) {
622    Context context = (Context) data;
623    final int lastWhen = node.jjtGetNumChildren() - 1;
624    for (int i = 0; i < lastWhen; i = i + 2) {
625      node.jjtGetChild(i).jjtAccept(this, data);
626      alternatives.when(context.pop());
627      node.jjtGetChild(i + 1).jjtAccept(this, data);
628      alternatives.then(context.pop());
629    }
630    node.jjtGetChild(lastWhen).jjtAccept(this, data);
631    alternatives.otherwise(context.pop());
632    context.push(alternatives);
633    return data;
634  }
635
636  @Override
637  public Object visit(ASTCase node, Object data) {
638    return visitAlternatives(data, node, cases().ofAST(node));
639  }
640
641  @Override
642  public Object visit(ASTMatch node, Object data) {
643    return visitAlternatives(data, node, match().ofAST(node));
644  }
645
646  @Override
647  public Object visit(ASTWhileLoop node, Object data) {
648    Context context = (Context) data;
649    node.jjtGetChild(1).jjtAccept(this, data);
650    node.jjtGetChild(0).jjtAccept(this, data);
651    context.push(
652      whileLoop(context.pop()).ofAST(node)
653        .block((Block) context.pop()));
654    return data;
655  }
656
657  @Override
658  public Object visit(ASTForLoop node, Object data) {
659    Context context = (Context) data;
660    Block block = context.enterScope();
661
662    node.jjtGetChild(0).jjtAccept(this, data);
663    node.jjtGetChild(1).jjtAccept(this, data);
664    node.jjtGetChild(2).jjtAccept(this, data);
665
666    LoopStatement loopStatement = loop().ofAST(node)
667      .post(context.pop())
668      .condition(context.pop())
669      .init(context.pop());
670
671    if (node.jjtGetNumChildren() == 4) {
672      node.jjtGetChild(3).jjtAccept(this, data);
673      loopStatement.block((Block) context.pop());
674    }
675    context.push(block.add(loopStatement));
676    context.leaveScope();
677    return data;
678  }
679
680  @Override
681  public Object visit(ASTForEachLoop node, Object data) {
682    Context context = (Context) data;
683    Block block = context.enterScope();
684
685    node.jjtGetChild(0).jjtAccept(this, data);
686
687    ForEachLoopStatement foreach = foreach().ofAST(node)
688      .varargs(node.isVarargs())
689      .on(context.pop());
690
691    if (node.getElementIdentifier() != null) {
692      foreach.var(node.getElementIdentifier());
693    } else {
694      for (String name : node.getNames()) {
695        foreach.var(name);
696      }
697    }
698
699    // there may be no block if we are in a collection comprehension, checking what we have...
700    int numChildren = node.jjtGetNumChildren();
701    if (numChildren > 2) {
702      // when and block: it's a regular loop with a when clause
703      node.jjtGetChild(2).jjtAccept(this, data);
704      node.jjtGetChild(1).jjtAccept(this, data);
705      foreach.when(context.pop()).block(context.pop());
706    } else if (numChildren == 2) {
707      // either a when and no block in collection comprehension or no when an block in regular loop
708      node.jjtGetChild(1).jjtAccept(this, data);
709      Object child = context.pop();
710      if (child instanceof Block) {
711        foreach.block(child);
712      } else if (child instanceof ExpressionStatement) {
713        foreach.when(child);
714      } else {
715        context.errorMessage(PARSING, node, message("syntax_foreach"));
716      }
717    }
718    context.push(block.add(foreach));
719    context.leaveScope();
720    return data;
721  }
722
723  @Override
724  public Object visit(ASTTryCatchFinally node, Object data) {
725    Context context = (Context) data;
726    boolean hasCatchBlock = (node.getExceptionId() != null);
727    TryCatchFinally tryCatchFinally = tryCatch(node.getExceptionId()).ofAST(node);
728
729    context.enterScope();
730    node.jjtGetChild(0).jjtAccept(this, data);
731    tryCatchFinally.trying(context.pop());
732    context.leaveScope();
733
734    context.enterScope();
735    node.jjtGetChild(1).jjtAccept(this, data);
736    if (hasCatchBlock) {
737      tryCatchFinally.catching(context.pop());
738    } else {
739      tryCatchFinally.finalizing(context.pop());
740    }
741    context.leaveScope();
742
743    if (hasCatchBlock && node.jjtGetNumChildren() > 2) {
744      context.enterScope();
745      node.jjtGetChild(2).jjtAccept(this, data);
746      tryCatchFinally.finalizing(context.pop());
747      context.leaveScope();
748    }
749
750    context.push(tryCatchFinally);
751    return data;
752  }
753
754  @Override
755  public Object visit(ASTExpressionStatement node, Object data) {
756    node.childrenAccept(this, data);
757    return data;
758  }
759
760  @Override
761  public Object visit(ASTInvocationExpression node, Object data) {
762    Context context = (Context) data;
763    node.childrenAccept(this, context);
764    BinaryOperation current = null;
765    ExpressionStatement left;
766    ExpressionStatement right = null;
767    List<String> operators = node.getOperators();
768    Collections.reverse(operators);
769    for (String symbol : operators) {
770      OperatorType operator = OperatorType.fromString(symbol);
771      if (right == null) {
772        right = (ExpressionStatement) context.pop();
773        if (operator == OperatorType.ELVIS_METHOD_CALL) {
774          ((MethodInvocation) right).setNullSafeGuarded(true);
775        }
776      } else if (operator == OperatorType.ELVIS_METHOD_CALL) {
777        BinaryOperation rOp = (BinaryOperation) right;
778        ((MethodInvocation) rOp.getLeftExpression()).setNullSafeGuarded(true);
779      }
780      left = (ExpressionStatement) context.pop();
781      right = current = binaryOperation(operator, left, right);
782    }
783    context.push(current);
784    node.setIrElement(current);
785    return data;
786  }
787
788  private BinaryOperation assembleBinaryOperation(List<ExpressionStatement> statements, List<OperatorType> operators) {
789    BinaryOperation current = null;
790    int i = 2;
791    for (OperatorType operator : operators) {
792      if (current == null) {
793        current = binaryOperation(operator, statements.get(0), statements.get(1));
794      } else {
795        current = binaryOperation(operator, current, statements.get(i));
796        i = i + 1;
797      }
798    }
799    return current;
800  }
801
802  private List<ExpressionStatement> operatorStatements(Context context, int operatorsCount) {
803    LinkedList<ExpressionStatement> statements = new LinkedList<>();
804    for (int i = 0; i < operatorsCount + 1; i++) {
805      statements.addFirst((ExpressionStatement) context.pop());
806    }
807    return statements;
808  }
809
810  @Override
811  public Object visit(ASTMultiplicativeExpression node, Object data) {
812    Context context = (Context) data;
813    node.childrenAccept(this, context);
814    List<OperatorType> operators = node.getOperators()
815        .stream()
816        .map(OperatorType::fromString)
817        .collect(Collectors.toList());
818    List<ExpressionStatement> statements = operatorStatements(context, operators.size());
819    ExpressionStatement operation = assembleBinaryOperation(statements, operators);
820    context.push(operation);
821    node.setIrElement(operation);
822    return data;
823  }
824
825  @Override
826  public Object visit(ASTAdditiveExpression node, Object data) {
827    Context context = (Context) data;
828    node.childrenAccept(this, context);
829    List<OperatorType> operators = node.getOperators()
830        .stream()
831        .map(OperatorType::fromString)
832        .collect(Collectors.toList());
833    List<ExpressionStatement> statements = operatorStatements(context, operators.size());
834    ExpressionStatement operation = assembleBinaryOperation(statements, operators);
835    context.push(operation);
836    node.setIrElement(operation);
837    return data;
838  }
839
840  @Override
841  public Object visit(ASTRelationalExpression node, Object data) {
842    Context context = (Context) data;
843    node.childrenAccept(this, data);
844    BinaryOperation operation = binaryOperation(node.getOperator())
845      .right(context.pop())
846      .left(context.pop());
847    context.push(operation);
848    node.setIrElement(operation);
849    return data;
850  }
851
852  @Override
853  public Object visit(ASTEqualityExpression node, Object data) {
854    Context context = (Context) data;
855    node.childrenAccept(this, data);
856    BinaryOperation operation = binaryOperation(node.getOperator())
857      .right(context.pop())
858      .left(context.pop());
859    context.push(operation);
860    node.setIrElement(operation);
861    return data;
862  }
863
864  @Override
865  public Object visit(ASTAndExpression node, Object data) {
866    Context context = (Context) data;
867    node.childrenAccept(this, context);
868    List<ExpressionStatement> statements = operatorStatements(context, node.count());
869    BinaryOperation operation = assembleBinaryOperation(statements, nCopies(node.count(), OperatorType.AND));
870    context.push(operation);
871    node.setIrElement(operation);
872    return data;
873  }
874
875  @Override
876  public Object visit(ASTOrExpression node, Object data) {
877    Context context = (Context) data;
878    node.childrenAccept(this, context);
879    List<ExpressionStatement> statements = operatorStatements(context, node.count());
880    BinaryOperation operation = assembleBinaryOperation(statements, nCopies(node.count(), OperatorType.OR));
881    context.push(operation);
882    node.setIrElement(operation);
883    return data;
884  }
885
886  @Override
887  public Object visit(ASTOrIfNullExpression node, Object data) {
888    Context context = (Context) data;
889    node.childrenAccept(this, context);
890    List<ExpressionStatement> statements = operatorStatements(context, node.count());
891    BinaryOperation operation = assembleBinaryOperation(statements, nCopies(node.count(), OperatorType.ORIFNULL));
892    context.push(operation);
893    node.setIrElement(operation);
894    return data;
895  }
896}