blob: ce2db41b52b7726816c9e8317943985390a85c90 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2008 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.lrparser.symboltable;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.cdt.core.dom.ast.IBinding;
/**
* Used to compute binding resolution during the parse.
*
* Imperative style symbol table with destructive update.
*
* Consists of two data structures, a hash table for fast lookup
* of bindings given their names, and a stack used to keep track
* of scopes.
*
*
* @author Mike Kucera
*/
public class CImperativeSymbolTable {
private static final int TABLE_SIZE = 256;
private Bucket[] table = new Bucket[TABLE_SIZE];
private LinkedList<SymbolScope> scopeStack = new LinkedList<>();
/**
* Represents a scope in the C language.
*/
private static class SymbolScope {
/**
* List of buckets that have been modified in the current scope.
* When the scope is closed these buckets are popped, returning the
* symbol table to the state it was in before the scope was opened.
*/
List<Integer> modifiedBuckets = new ArrayList<>();
}
/**
* A bucket object used to hold elements in the hash table.
*/
private static class Bucket {
String key;
CNamespace namespace;
IBinding binding;
Bucket next;
Bucket(Bucket next, CNamespace namespace, String key, IBinding binding) {
this.key = key;
this.namespace = namespace;
this.binding = binding;
this.next = next;
}
}
public CImperativeSymbolTable() {
openScope(); // open the global scope
// TODO populate the global scope with built-ins
}
/**
* Hashes a key into an index in the hash table.
*/
private int index(String key) {
return Math.abs(key.hashCode() % TABLE_SIZE);
}
/**
* Adds a binding to the symbol table in the current scope.
*
* @param mask A bit mask used to identify the namespace of the identifier.
*/
public void put(CNamespace namespace, String ident, IBinding b) {
int index = index(ident);
table[index] = new Bucket(table[index], namespace, ident, b);
SymbolScope scope = scopeStack.getLast();
scope.modifiedBuckets.add(index);
}
/**
* Returns the binding associated with the given identifier, or
* null if there is none.
*
* @param mask A bit mask used to identify the namespace of the identifier.
*/
public IBinding get(CNamespace namespace, String ident) {
Bucket b = table[index(ident)];
while (b != null) {
if (namespace == b.namespace && ident.equals(b.key))
return b.binding;
b = b.next;
}
return null;
}
/**
* Opens a new inner scope for identifiers.
*
* If an identifier is added that already exists in an outer scope
* then it will be shadowed.
*/
public void openScope() {
scopeStack.add(new SymbolScope());
}
/**
* Remove all the symbols defined in the scope that is being closed.
*
* @param scope An IScope object that will be used to represent this scope.
* @throws SymbolTableException If the global scope has already been closed or if bindingScope is null.
*/
public void closeScope() {
SymbolScope poppedScope = scopeStack.removeLast(); // pop the scopeStack
// pop each bucket that was modified in the scope
for (int index : poppedScope.modifiedBuckets)
table[index] = table[index].next;
}
@SuppressWarnings("nls")
@Override
public String toString() {
StringBuilder buff = new StringBuilder('[');
for (Bucket b : table) {
while (b != null) {
buff.append('<').append(b.key).append(": ").append(b.binding).append(">, ");
b = b.next;
}
}
return buff.append(']').toString();
}
}