// SymbolTable.java

// Date: 3/1/98
// Author: Adam Berger
// Description: implements symbol table for SL interpreter


import java.util.*;

public class SymbolTable implements TokenTypes
{
  private Hashtable ht; 
  private Vector satelliteData; // token and binding for lexeme
  private static final int TABLE_SIZE = 8096; // collisions aren't fatal

  public SymbolTable() { 
    // default constructor: initialize symbol table with reserved lexemes 
    // (keywords and operators) of SL 
    ht = new Hashtable(TABLE_SIZE);
    satelliteData = new Vector (TABLE_SIZE);    // Vectors are growable 
    for (int i=0; i<reservedLexemes.length; i++) 
      insertLexeme(reservedLexemes[i], i); // insert lexeme with its token type 
  }

  public SymbolTable(SymbolTable st) {
    // construct a local symbol table from the global one by inheriting everything, 
    // and binding all identifiers to null
    this();  // invoke the no-argument constructor 
    Enumeration e = st.ht.keys();
    try { 
      for (int i=0; e.hasMoreElements(); i++) {
	String lexeme = (String) e.nextElement();
	int token = st.tokenOfLexeme(lexeme);
	insertLexeme(lexeme, token); 
	if (token == T_IDENT) {
	  // Viewed as a token, lexeme is an idenfiier. If it's a function, 
	  // inherit binding from parent symbol table. Else bind to null.
	  if (st.getBinding(lexeme) instanceof SL_FunctionNode) 
	    setBinding(lexeme, st.getBinding(lexeme));
	  else setBinding(lexeme, (SL_NumberNode) null);
	}
      }
    }
    catch (InterpretException iex) {}
  }    
  
  public void setBinding(String lexeme, ParseNode binding) 
    throws InterpretException {
      int idx = indexOfLexeme(lexeme);
      if (idx == -1) 
	throw new InterpretException(); // Tried to bind unknown lexeme 
      if (tokenOfLexeme(lexeme)!=T_IDENT) 
	throw new InterpretException() ; //Tried to bind non-identifier
      ((SData) satelliteData.elementAt(idx)).setBinding(binding);
  }
  
  public ParseNode getBinding(String lexeme) throws InterpretException {
    int idx = indexOfLexeme(lexeme);
    if (idx == -1) 
      throw new InterpretException(); //Tried to get binding of unknown lexeme
    if (tokenOfLexeme(lexeme)!=T_IDENT) 
      throw new InterpretException(); // Tried to get binding of non-identifier
    return ((SData) satelliteData.elementAt(idx)).getBinding();
  }

  public void insertLexeme(String lexeme) {
    // insert lexeme into symbol table. We can tell determine the token type from the lexeme.
    if (ht.containsKey(lexeme)) return; // already in the table 
    if (Character.isDigit(lexeme.charAt(0)))    // first character: is it a digit? 
      insertLexeme(lexeme, T_NUMBER); // yes, so lexeme is a number
    else insertLexeme(lexeme, T_IDENT);  // no, so lexeme is an identifier  
  }
  
  private int indexOfLexeme(String lexeme) {
    // return index of lexeme in table, or -1 if not there
    Integer value = (Integer) ht.get(lexeme);
    return (value==null)? -1: value.intValue();
  }
  
  public int tokenOfLexeme(String lexeme) {
    int idx = indexOfLexeme(lexeme);
    return ((SData) satelliteData.elementAt(idx)).getTokenType();
  }

  public void print() { 
    // diagnostic
    Enumeration e = ht.keys();
    for (int i=0; e.hasMoreElements(); i++) {
      String lexeme = (String) e.nextElement();
      Integer token = new Integer(tokenOfLexeme(lexeme));
      System.out.println("Lexeme: "+lexeme+" Token: "+token.toString());
    }
  }

  private void insertLexeme(String lexeme, int tokenType) {
    if (ht.containsKey(lexeme)) return; // don't add twice 
    SData sd = new SData(tokenType, null);  
    satelliteData.insertElementAt(sd, ht.size());
    ht.put(lexeme, new Integer(ht.size()));
  }
} 


