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.Objects;
014import java.util.LinkedList;
015import java.util.List;
016
017/**
018 * Generic looping structure.
019 *
020 * <p>This is used to represent both {@code for} and {@code while} loops, as well as the desugared {@code foreach} loop.
021 */
022public final class LoopStatement extends GoloStatement<LoopStatement> implements BlockContainer<LoopStatement>, ReferencesHolder {
023
024  private AssignmentStatement initStatement = null;
025  private ExpressionStatement<?> conditionStatement = null;
026  private GoloStatement<?> postStatement = null;
027  private Block block = null;
028
029  private LoopStatement() {
030    super();
031  }
032
033  /**
034   * Creates a generic loop.
035   */
036  public static LoopStatement loop() {
037    return new LoopStatement().condition(null).block(null);
038  }
039
040  /**
041   * Complete loop creation.
042   *
043   * For meta-generation.
044   */
045  public static LoopStatement create(Object init, Object condition, Object post, Object block) {
046    return loop().init(init).condition(condition).post(post).block(Block.of(block));
047  }
048
049
050  protected LoopStatement self() { return this; }
051
052  /**
053   * Defines the initialization of the loop variable.
054   *
055   * <p>For instance, the <code class="lang-golo">var i = 0</code> statement.
056   * This can be {@code null} in the case of {@code while} loops.
057   *
058   * <p>This is a builder method.
059   *
060   * @param assignment a {@link AssignmentStatement} defining the loop variable.
061   */
062  public LoopStatement init(Object assignment) {
063    if (assignment instanceof AssignmentStatement) {
064      this.initStatement = makeParentOf((AssignmentStatement) assignment);
065      return this;
066    }
067    if (assignment == null) {
068      this.initStatement = null;
069      return this;
070    }
071    throw cantConvert("assignment", assignment);
072  }
073
074  /**
075   * Defines the loop condition.
076   *
077   * <p>For instance, the <code class="lang-golo">i < 10</code> expression.
078   *
079   * <p>This is a builder method.
080   *
081   * @param expression a {@link ExpressionStatement} evaluating to a boolean.
082   */
083  public LoopStatement condition(Object expression) {
084    if (expression == null) {
085      this.conditionStatement = ConstantStatement.of(false);
086    } else if (expression instanceof ExpressionStatement) {
087      this.conditionStatement = ExpressionStatement.of(expression);
088    } else {
089      throw cantConvert("expression", expression);
090    }
091    makeParentOf(this.conditionStatement);
092    return this;
093  }
094
095  /**
096   * Define the post-loop statement.
097   *
098   * <p>For instance, the <code class="lang-golo">i = i + 1</code> statement.
099   * This can be {@code null} in the case of {@code while} loops.
100   *
101   * <p>This is a builder method.
102   *
103   * @param statement a {@link GoloStatement} changing the loop variable.
104   */
105  public LoopStatement post(Object statement) {
106    if (statement instanceof GoloStatement) {
107      this.postStatement = makeParentOf((GoloStatement) statement);
108      return this;
109    }
110    if (statement == null) {
111      this.postStatement = null;
112      return this;
113    }
114    throw cantConvert("statement", statement);
115  }
116
117  /**
118   * {@inheritDoc}
119   */
120  @Override
121  public LoopStatement block(Object innerBlock) {
122    this.block = makeParentOf(Block.of(innerBlock));
123    return this;
124  }
125
126  public boolean hasInitStatement() {
127    return initStatement != null;
128  }
129
130  public AssignmentStatement init() {
131    return initStatement;
132  }
133
134  public ExpressionStatement<?> condition() {
135    return conditionStatement;
136  }
137
138  /**
139   * {@inheritDoc}
140   */
141  @Override
142  public Block getBlock() {
143    return block;
144  }
145
146  public GoloStatement<?> post() {
147    return postStatement;
148  }
149
150  public boolean hasPostStatement() {
151    return postStatement != null;
152  }
153
154  /**
155   * {@inheritDoc}
156   */
157  @Override
158  public LocalReference[] getReferences() {
159    if (hasInitStatement()) {
160      return new LocalReference[]{init().getLocalReference()};
161    }
162    return new LocalReference[0];
163  }
164
165  /**
166   * {@inheritDoc}
167   */
168  @Override
169  public int getReferencesCount() {
170    return hasInitStatement() ? 1 : 0;
171  }
172
173  /**
174   * {@inheritDoc}
175   */
176  @Override
177  public void accept(GoloIrVisitor visitor) {
178    visitor.visitLoopStatement(this);
179  }
180
181  /**
182   * {@inheritDoc}
183   */
184  @Override
185  public List<GoloElement<?>> children() {
186    LinkedList<GoloElement<?>> children = new LinkedList<>();
187    if (initStatement != null) {
188      children.add(initStatement);
189    }
190    if (conditionStatement != null) {
191      children.add(conditionStatement);
192    }
193    if (postStatement != null) {
194      children.add(postStatement);
195    }
196    if (block != null) {
197      children.add(block);
198    }
199    return children;
200  }
201
202  /**
203   * {@inheritDoc}
204   */
205  @Override
206  protected void replaceElement(GoloElement<?> original, GoloElement<?> newElement) {
207    if (Objects.equals(initStatement, original)) {
208      init(newElement);
209    } else if (Objects.equals(conditionStatement, original)) {
210      condition(newElement);
211    } else if (Objects.equals(postStatement, original)) {
212      post(newElement);
213    } else if (Objects.equals(block, original)) {
214      block(Block.of(newElement));
215    } else {
216      throw cantReplace(original, newElement);
217    }
218  }
219}