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 gololang.ir;
012
013import java.util.Collections;
014import java.util.List;
015
016public abstract class GoloAssignment<T extends GoloAssignment<T>> extends GoloStatement<T> implements ReferencesHolder {
017
018  private boolean declaring = false;
019  private ExpressionStatement<?> expressionStatement;
020
021  GoloAssignment() { super(); }
022
023  /**
024   * Checks if this assignment is a declaring one.
025   *
026   * @see #declaring()
027   */
028  public final boolean isDeclaring() {
029    return declaring;
030  }
031
032  /**
033   * Defines if this assignment is a declaring one.
034   *
035   * A declaring assignment is one that first defines the value of a reference,
036   * that is a {@code let} or {@code var} assignment. An assignment to a previously defined
037   * mutable variable (one declared with {@code var}) is not a declaring assignment.
038   *
039   * <p>This is a builder method.
040   *
041   * @param isDeclaring whether the assignment is declaring.
042   * @return this assignment
043   */
044  public final T declaring(boolean isDeclaring) {
045    this.declaring = isDeclaring;
046    return self();
047  }
048
049  /**
050   * Makes this assignment a declaring one.
051   * <p>
052   * Same as {@code declaring(true)}.
053   *
054   * @return this assignment
055   */
056  public final T declaring() {
057    return this.declaring(true);
058  }
059
060  /**
061   * Makes this assignment variable.
062   *
063   * @see LocalReference#variable()
064   */
065  public abstract T variable();
066
067  /**
068   * Checks if this assignment is constant.
069   *
070   * @see LocalReference#isConstant()
071   */
072  public abstract boolean isConstant();
073
074  public final ExpressionStatement<?> expression() {
075    return this.expressionStatement;
076  }
077
078  /**
079   * Defines the value to be assigned.
080   *
081   * <p>This is a builder method.
082   *
083   * @param expr the {@link ExpressionStatement} to assign or an object that can be converted into an expression
084   * @return this assignment
085   * @see ExpressionStatement#of(Object)
086   */
087  public final T as(Object expr) {
088    this.expressionStatement = makeParentOf(ExpressionStatement.of(expr));
089    return self();
090  }
091
092  /**
093   * Defines the references to which assign the expression.
094   *
095   * <p>This is a builder method.
096   * <p>For instance, code like:
097   * <pre class="listing"><code class="lang-golo" data-lang="golo">
098   *  foo = 42
099   * </code></pre>
100   * can be generated by:
101   * <pre class"listing"><code class="lang-java" data-lang="java">
102   * assign(constant(42)).to(localRef("foo"))
103   * </code></pre>
104   * or using implicit conversions:
105   * <pre class"listing"><code class="lang-java" data-lang="java">
106   * assign(42).to("foo")
107   * </code></pre>
108   *
109   * @param refs the {@link gololang.ir.LocalReference}s to assign to
110   * @return this assignment
111   */
112  public abstract T to(Object... refs);
113
114
115  /**
116   * {@inheritDoc}
117   */
118  @Override
119  public LocalReference[] getDeclaringReferences() {
120    if (declaring) {
121      return getReferences();
122    }
123    return new LocalReference[0];
124  }
125
126  /**
127   * {@inheritDoc}
128   */
129  @Override
130  protected void replaceElement(GoloElement<?> original, GoloElement<?> newElement) {
131    if (original.equals(expression()) && newElement instanceof ExpressionStatement) {
132      as(newElement);
133    } else {
134      throw cantReplace(original, newElement);
135    }
136  }
137
138  /**
139   * {@inheritDoc}
140   */
141  @Override
142  public List<GoloElement<?>> children() {
143    return Collections.singletonList(expressionStatement);
144  }
145}