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