001/*
002 * Copyright (c) 2012-2018 Institut National des Sciences Appliquées de Lyon (INSA Lyon) and others
003 *
004 * This program and the accompanying materials are made available under the
005 * terms of the Eclipse Public License 2.0 which is available at
006 * http://www.eclipse.org/legal/epl-2.0.
007 *
008 * SPDX-License-Identifier: EPL-2.0
009 */
010
011package gololang.ir;
012
013import java.util.List;
014import java.util.LinkedList;
015
016import static java.util.Collections.unmodifiableList;
017
018/**
019 * Represents a collection comprehension expression.
020 *
021 * <p>Such as:
022 * <pre class="listing"><code class="lang-golo" data-lang="golo">
023 * list[2*x + y foreach x in someIterable for (var y=0, y &lt; 5, y = y + 1)]
024 * </code></pre>
025 *
026 * @see CollectionLiteral.Type
027 */
028public final class CollectionComprehension extends ExpressionStatement<CollectionComprehension> {
029
030  private final CollectionLiteral.Type type;
031  private ExpressionStatement<?> expression;
032  private final List<GoloStatement<?>> loopBlocks = new LinkedList<>();
033
034  private CollectionComprehension(CollectionLiteral.Type type) {
035    super();
036    this.type = type;
037  }
038
039  /**
040   * Creates a collection comprehension of the given type.
041   */
042  public static CollectionComprehension of(Object type) {
043    return new CollectionComprehension(
044        (type instanceof CollectionLiteral.Type)
045        ? (CollectionLiteral.Type) type
046        : CollectionLiteral.Type.valueOf(type.toString()));
047  }
048
049  /**
050   * Complete collection comprehension creation.
051   *
052   * For meta-generation.
053   */
054  public static CollectionComprehension create(Object type, Object expression, Object... loops) {
055    CollectionComprehension c = of(type).expression(expression);
056    for (Object l : loops) {
057      c.loop(l);
058    }
059    return c;
060  }
061
062  protected CollectionComprehension self() { return this; }
063
064  /**
065   * Defines the expression of the comprehension.
066   *
067   * <code>2*x + y</code> in the previous example.
068   */
069  public CollectionComprehension expression(Object expression) {
070    this.expression = makeParentOf(ExpressionStatement.of(expression));
071    return this;
072  }
073
074  /**
075   * Adds a loop instruction to this comprehension.
076   *
077   * <p>The object to add is a loop-like expression, <code>foreach x in someIterable</code> and
078   * <code>for (var y=0, y &lt; 5, y = y + 1)</code> in the previous example.
079   * @param loop a {@link ForEachLoopStatement} or a {@link LoopStatement}.
080   */
081  public CollectionComprehension loop(Object loop) {
082    if (!(loop instanceof ForEachLoopStatement || loop instanceof LoopStatement)) {
083      throw new IllegalArgumentException("Loop expected, got a " + loop.getClass().getName());
084    }
085    this.loopBlocks.add(makeParentOf(GoloStatement.of(loop)));
086    return this;
087  }
088
089  public ExpressionStatement<?> expression() {
090    return this.expression;
091  }
092
093  public List<GoloStatement<?>> loops() {
094    return unmodifiableList(loopBlocks);
095  }
096
097  public CollectionLiteral.Type getType() {
098    return this.type;
099  }
100
101  public CollectionLiteral.Type getMutableType() {
102    return (CollectionLiteral.Type.tuple.equals(type)
103       || CollectionLiteral.Type.array.equals(type))
104      ? CollectionLiteral.Type.list
105      : type;
106  }
107
108  /**
109   * {@inheritDoc}
110   */
111  @Override
112  public void accept(GoloIrVisitor visitor) {
113    visitor.visitCollectionComprehension(this);
114  }
115
116  /**
117   * {@inheritDoc}
118   */
119  @Override
120  public List<GoloElement<?>> children() {
121    LinkedList<GoloElement<?>> children = new LinkedList<>();
122    children.add(expression);
123    children.addAll(loopBlocks);
124    return children;
125  }
126
127  /**
128   * {@inheritDoc}
129   */
130  @Override
131  protected void replaceElement(GoloElement<?> original, GoloElement<?> newElement) {
132    if (expression == original && newElement instanceof ExpressionStatement) {
133      expression(newElement);
134    } else if (newElement instanceof Block && loopBlocks.contains(original)) {
135      loopBlocks.set(loopBlocks.indexOf(original), Block.of(newElement));
136    } else {
137      throw cantReplace(original, newElement);
138    }
139  }
140}