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 org.eclipse.golo.compiler.SymbolGenerator;
014
015/**
016 * Represents a reference.
017 *
018 * <p>A reference is ether a variable (as defined by <code class="lang-golo">var answer = 42</code>) or a constant
019 * (as defined by <code class="lang-golo">let answer = 42</code>).
020 */
021public final class LocalReference extends GoloElement<LocalReference> {
022
023  private static final SymbolGenerator REF_SYMBOLS = new SymbolGenerator("golo.ir.builders.ref");
024
025  public enum Kind {
026    CONSTANT, VARIABLE, MODULE_CONSTANT, MODULE_VARIABLE
027  }
028
029  private Kind kind = Kind.CONSTANT;
030  private final String name;
031  private boolean synthetic = false;
032  private int index = -1;
033
034  private LocalReference(String name) {
035    super();
036    this.name = name;
037  }
038
039  /**
040   * Creates a local reference.
041   *
042   * <p>If the argument is already a local reference, it is returned unchanged, otherwise, its
043   * string representation is used to name the reference.
044   *
045   * @param name the name of the reference
046   * @return a local reference to the variable named after the argument
047   */
048  public static LocalReference of(Object name) {
049    if (name instanceof LocalReference) {
050      return (LocalReference) name;
051    }
052    if (name instanceof ReferenceLookup) {
053      return new LocalReference(((ReferenceLookup) name).getName());
054    }
055    return new LocalReference(name.toString());
056  }
057
058  /**
059   * Creates a new local reference with a generated unique name.
060   */
061  public static LocalReference generate() {
062    return new LocalReference(REF_SYMBOLS.next());
063  }
064
065  public static LocalReference create(Object name, Kind kind) {
066    return of(name).kind(kind);
067  }
068
069  protected LocalReference self() { return this; }
070
071  public Kind getKind() {
072    return kind;
073  }
074
075  public LocalReference variable() {
076    if (kind == Kind.MODULE_VARIABLE || kind == Kind.MODULE_CONSTANT) {
077      kind = Kind.MODULE_VARIABLE;
078    } else {
079      kind = Kind.VARIABLE;
080    }
081    return this;
082  }
083
084  public LocalReference moduleLevel() {
085    if (kind == Kind.CONSTANT || kind == Kind.MODULE_CONSTANT) {
086      kind = Kind.MODULE_CONSTANT;
087    } else {
088      kind = Kind.MODULE_VARIABLE;
089    }
090    return this;
091  }
092
093  public LocalReference kind(Kind k) {
094    kind = k;
095    return this;
096  }
097
098  public String getName() {
099    return name;
100  }
101
102  public LocalReference synthetic(boolean isSynthetic) {
103    this.synthetic = isSynthetic;
104    return this;
105  }
106
107  public LocalReference synthetic() {
108    return synthetic(true);
109  }
110
111  public boolean isSynthetic() {
112    return synthetic;
113  }
114
115  public boolean isModuleState() {
116    return kind == Kind.MODULE_CONSTANT || kind == Kind.MODULE_VARIABLE;
117  }
118
119  public boolean isConstant() {
120    return kind == Kind.CONSTANT || kind == Kind.MODULE_CONSTANT;
121  }
122
123  /**
124   * Internal API
125   */
126  public int getIndex() {
127    return index;
128  }
129
130  /**
131   * Internal API
132   */
133  public void setIndex(int index) {
134    this.index = index;
135  }
136
137  public LocalReference index(int index) {
138    setIndex(index);
139    return this;
140  }
141
142  /**
143   * Returns a {@link ReferenceLookup} referencing this variable.
144   */
145  public ReferenceLookup lookup() {
146    return ReferenceLookup.of(name);
147  }
148
149  /**
150   * {@inheritDoc}
151   */
152  @Override
153  public String toString() {
154    return String.format("LocalReference{kind=%s, name='%s', index=%d}", kind, name, index);
155  }
156
157  /**
158   * {@inheritDoc}
159   */
160  @Override
161  public boolean equals(Object o) {
162    if (this == o) { return true; }
163    if (o == null || getClass() != o.getClass()) { return false; }
164    LocalReference that = (LocalReference) o;
165    return kind == that.kind && name.equals(that.name);
166  }
167
168  /**
169   * {@inheritDoc}
170   */
171  @Override
172  public int hashCode() {
173    int result = kind.hashCode();
174    result = 31 * result + name.hashCode();
175    return result;
176  }
177
178  /**
179   * {@inheritDoc}
180   */
181  @Override
182  public void accept(GoloIrVisitor visitor) {
183    visitor.visitLocalReference(this);
184  }
185
186  /**
187   * {@inheritDoc}
188   */
189  @Override
190  protected void replaceElement(GoloElement<?> original, GoloElement<?> newElement) {
191    throw cantReplace();
192  }
193}