compiler-design-eth/src/cd/frontend/semantic/SymTable.java

88 lines
2.0 KiB
Java

package cd.frontend.semantic;
import cd.frontend.semantic.SemanticFailure.Cause;
import cd.ir.Symbol;
import java.util.*;
/**
* A simple symbol table, with a pointer to the enclosing scope.
* Used by {@link TypeChecker} to store the various scopes for
* local, parameter, and field lookup.
*/
public class SymTable<S extends Symbol> {
private final Map<String, S> map = new HashMap<String, S>();
private final SymTable<S> parent;
public SymTable(SymTable<S> parent) {
this.parent = parent;
}
public void add(S sym) {
// check that the symbol is not already declared *at this level*
if (containsLocally(sym.name))
throw new SemanticFailure(Cause.DOUBLE_DECLARATION);
map.put(sym.name, sym);
}
public List<S> allSymbols() {
List<S> result = new ArrayList<S>();
SymTable<S> st = this;
while (st != null) {
for (S sym : st.map.values())
result.add(sym);
st = st.parent;
}
return result;
}
public Collection<S> localSymbols() {
return this.map.values();
}
/**
* True if there is a declaration with the given name at any
* level in the symbol table
*/
public boolean contains(String name) {
return get(name) != null;
}
/**
* True if there is a declaration at THIS level in the symbol
* table; may return {@code false} even if a declaration exists
* in some enclosing scope
*/
public boolean containsLocally(String name) {
return this.map.containsKey(name);
}
/**
* Base method: returns {@code null} if no symbol by that
* name can be found, in this table or in its parents
*/
public S get(String name) {
S res = map.get(name);
if (res != null)
return res;
if (parent == null)
return null;
return parent.get(name);
}
/**
* Finds the symbol with the given name, or fails with a
* NO_SUCH_TYPE error. Only really makes sense to use this
* if S == TypeSymbol, but we don't strictly forbid it...
*/
public S getType(String name) {
S res = get(name);
if (res == null)
throw new SemanticFailure(
Cause.NO_SUCH_TYPE,
"No type '%s' was found", name);
return res;
}
}