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;
015import java.util.Objects;
016
017/**
018 * Represents a {@code foreach} loop on an iterator.
019 * <p>For instance:
020 * <pre class="listing"><code class="lang-golo" data-lang="golo">
021 * foreach x in range(5) {
022 *   println(x)
023 * }
024 * </code></pre>
025 */
026public final class ForEachLoopStatement extends GoloStatement<ForEachLoopStatement> implements BlockContainer<ForEachLoopStatement>, ReferencesHolder {
027  private Block block = Block.empty();
028  private ExpressionStatement<?> iterable;
029  private final List<LocalReference> valueRefs = new LinkedList<>();
030  private ExpressionStatement<?> whenClause;
031  private boolean isVarargs = false;
032
033  private ForEachLoopStatement() {
034    super();
035  }
036
037  /**
038   * Complete foreach loop.
039   *
040   * For meta-generation.
041   *
042   * @param varargs the parameters of the loop are variable length destructuring
043   * @param iterable the iterable we loop on
044   * @param when the loop filter
045   * @param block the loop block
046   * @param vars the loop variables
047   */
048  public static ForEachLoopStatement create(boolean varargs, Object iterable, Object when, Object block, Object... vars) {
049    ForEachLoopStatement loop = new ForEachLoopStatement();
050    for (Object v : vars) {
051      loop.var(v);
052    }
053    return loop.varargs(varargs).when(when).in(iterable).block(block);
054  }
055
056  public static ForEachLoopStatement create() {
057    return new ForEachLoopStatement();
058  }
059
060  /**
061   * {@inheritDoc}
062   */
063  @Override
064  public ForEachLoopStatement block(Object block) {
065    this.block = makeParentOf(Block.of(block));
066    return this;
067  }
068
069  protected ForEachLoopStatement self() { return this; }
070
071  /**
072   * Defines the iterable on which to loop.
073   *
074   * <p>This is a builder method.
075   *
076   * @param iterable an {@link ExpressionStatement} representing an {@code Iterable} expression.
077   */
078  public ForEachLoopStatement in(Object iterable) {
079    this.iterable = ExpressionStatement.of(iterable);
080    return this;
081  }
082
083  public ForEachLoopStatement varargs(boolean b) {
084    this.isVarargs = b;
085    return this;
086  }
087
088  /**
089   * Adds a loop variable.
090   *
091   * <p>This is a builder method.
092   *
093   * @param varRef the variable (a {@link LocalReference} create)
094   * @see LocalReference#of(Object)
095   */
096  public ForEachLoopStatement var(Object varRef) {
097    this.valueRefs.add(LocalReference.of(varRef).variable());
098    return this;
099  }
100
101  /**
102   * Adds a when clause to the loop.
103   *
104   * <p>This is a builder method.
105   *
106   * @param clause an {@link ExpressionStatement} used as a condition.
107   */
108  public ForEachLoopStatement when(Object clause) {
109    if (clause != null) {
110      this.whenClause = ExpressionStatement.of(clause);
111    } else {
112      this.whenClause = null;
113    }
114    return this;
115  }
116
117  public ExpressionStatement<?> getIterable() {
118    return iterable;
119  }
120
121  public Block getBlock() {
122    return block;
123  }
124
125  public boolean isDestructuring() {
126    return valueRefs.size() > 1;
127  }
128
129  public boolean isVarargs() {
130    return this.isVarargs;
131  }
132
133  public LocalReference getLocalReference() {
134    return valueRefs.get(0);
135  }
136
137  /**
138   * {@inheritDoc}
139   */
140  @Override
141  public LocalReference[] getReferences() {
142    return valueRefs.toArray(new LocalReference[valueRefs.size()]);
143  }
144
145  /**
146   * {@inheritDoc}
147   */
148  @Override
149  public int getReferencesCount() {
150    return valueRefs.size();
151  }
152
153  public boolean hasWhenClause() {
154    return whenClause != null;
155  }
156
157  public ExpressionStatement<?> getWhenClause() {
158    return whenClause;
159  }
160
161  /**
162   * {@inheritDoc}
163   */
164  @Override
165  public void accept(GoloIrVisitor visitor) {
166    visitor.visitForEachLoopStatement(this);
167  }
168
169  /**
170   * {@inheritDoc}
171   */
172  @Override
173  public List<GoloElement<?>> children() {
174    LinkedList<GoloElement<?>> children = new LinkedList<>(valueRefs);
175    children.add(iterable);
176    if (whenClause != null) {
177      children.add(whenClause);
178    }
179    children.add(block);
180    return children;
181  }
182
183  /**
184   * {@inheritDoc}
185   */
186  @Override
187  protected void replaceElement(GoloElement<?> original, GoloElement<?> newElement) {
188    if (Objects.equals(iterable, original)) {
189      in(newElement);
190    } else if (Objects.equals(whenClause, original)) {
191      when(newElement);
192    } else if (Objects.equals(block, original)) {
193      block(newElement);
194    } else {
195      throw cantReplace(original, newElement);
196    }
197  }
198}