/******************************************************************************* * Copyright (c) 2006, 2008 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * 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<SymbolScope>(); /** * 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<Integer>(); } /** * 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(); } }