001/*
002 * Copyright (c) 2012-2017 Institut National des Sciences Appliquées de Lyon (INSA-Lyon)
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 */
009
010package org.eclipse.golo.compiler.ir;
011
012import java.util.*;
013
014import static java.util.Collections.unmodifiableCollection;
015import static java.util.Collections.unmodifiableSet;
016import static org.eclipse.golo.compiler.ir.Builders.localRef;
017
018public final class ReferenceTable implements Scope {
019
020  private ReferenceTable parent;
021  private final Map<String, LocalReference> table = new LinkedHashMap<>();
022
023  public ReferenceTable() {
024    this(null);
025  }
026
027  private ReferenceTable(ReferenceTable parent) {
028    this.parent = parent;
029  }
030
031  public ReferenceTable add(LocalReference reference) {
032    table.put(reference.getName(), reference);
033    return this;
034  }
035
036  public int size() {
037    return table.size() + (parent != null ? parent.size() : 0);
038  }
039
040  public boolean hasReferenceFor(String name) {
041    return table.containsKey(name) || parent != null && parent.hasReferenceFor(name);
042  }
043
044  public void updateFrom(GoloStatement statement) {
045    if (statement instanceof AssignmentStatement) {
046      AssignmentStatement assign = (AssignmentStatement) statement;
047      if (assign.isDeclaring()) {
048        this.add(assign.getLocalReference());
049      }
050    }
051    if (statement instanceof LoopStatement) {
052      LoopStatement loop = (LoopStatement) statement;
053      if (loop.hasInitStatement()) {
054        this.add(loop.getInitStatement().getLocalReference());
055      }
056    }
057    if (statement instanceof ForEachLoopStatement) {
058      ForEachLoopStatement foreach = (ForEachLoopStatement) statement;
059      for (LocalReference r : foreach.getReferences()) {
060        this.add(r);
061      }
062    }
063  }
064
065  public LocalReference get(String name) {
066    LocalReference reference = table.get(name);
067    if (reference != null) {
068      return reference;
069    }
070    if (parent != null) {
071      return parent.get(name);
072    }
073    return null;
074  }
075
076  public Set<String> ownedSymbols() {
077    return unmodifiableSet(table.keySet());
078  }
079
080  public Collection<LocalReference> ownedReferences() {
081    return unmodifiableCollection(table.values());
082  }
083
084  @Override
085  public void relink(ReferenceTable parent) {
086    for (LocalReference reference : parent.references()) {
087      if (this.hasReferenceFor(reference.getName())) {
088        this.remove(reference.getName());
089      }
090    }
091    this.parent = parent;
092  }
093
094  private boolean isLinkedTo(ReferenceTable other) {
095    if (this != other && this.parent == null) {
096      return false;
097    }
098    return this == other || this.parent == other || this.parent.isLinkedTo(other);
099  }
100
101  @Override
102  public void relinkTopLevel(ReferenceTable topLevel) {
103    if (this == topLevel) { return; }
104    if (this.parent == null) {
105      this.parent = topLevel;
106    } else if (!this.isLinkedTo(topLevel) && !topLevel.isLinkedTo(this)) {
107      this.parent.relinkTopLevel(topLevel);
108    }
109  }
110
111  public Set<String> symbols() {
112    LinkedHashSet<String> localSymbols = new LinkedHashSet<>(table.keySet());
113    if (parent != null) {
114      localSymbols.addAll(parent.symbols());
115    }
116    return localSymbols;
117  }
118
119  public Collection<LocalReference> references() {
120    Collection<LocalReference> localReferences = new LinkedHashSet<>(table.values());
121    if (parent != null) {
122      for (LocalReference ref : parent.references()) {
123        if (!table.containsKey(ref.getName())) {
124          localReferences.add(ref);
125        }
126      }
127    }
128    return localReferences;
129  }
130
131  public ReferenceTable fork() {
132    return new ReferenceTable(this);
133  }
134
135  public ReferenceTable flatDeepCopy(boolean turnIntoConstants) {
136    ReferenceTable referenceTable = new ReferenceTable();
137    Set<String> tableSymbols = ownedSymbols();
138    for (LocalReference reference : references()) {
139      String refName = reference.getName();
140      if (reference.isModuleState()) {
141        referenceTable.add(localRef(refName).kind(reference.getKind()));
142        continue;
143      }
144      if (turnIntoConstants && !tableSymbols.contains(refName)) {
145        referenceTable.add(localRef(refName).synthetic(reference.isSynthetic()));
146      } else {
147        referenceTable.add(localRef(refName)
148            .kind(reference.getKind())
149            .synthetic(reference.isSynthetic()));
150      }
151    }
152    return referenceTable;
153  }
154
155  public void remove(String name) {
156    table.remove(name);
157  }
158
159  @Override
160  public String toString() {
161    StringBuilder representation = new StringBuilder("ReferenceTable: {\n");
162    for (Map.Entry<String, LocalReference> elt : table.entrySet()) {
163      representation.append(elt.getKey()).append(": ").append(elt.getValue()).append('\n');
164    }
165    representation.append('}');
166    if (parent != null) {
167      representation.append(" => ").append(parent.toString());
168    }
169    return representation.toString();
170  }
171}