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