/*****
* table.h
* Andy Hammerlindl 2002/06/18
*
* Table used to bind symbols to vars and types in a namespace.
*****/

#ifndef TABLE_H
#define TABLE_H

#include <cassert>
#include <utility>

#include "symbol.h"
#include "common.h"

namespace sym {

template <class B>
class table;

template <class B>
std::ostream& operator<< (std::ostream& out, const table<B>& t);

template <class B>
class table {
protected:
 typedef mem::multimap<symbol const,B> scope_t;
 typedef typename scope_t::iterator scope_iterator;
 typedef mem::list<scope_t> scopes_t;
 typedef mem::list<B> name_t;
 typedef typename name_t::iterator name_iterator;
 typedef mem::map<symbol const,name_t> names_t;
 typedef typename names_t::iterator names_iterator;

 scopes_t scopes;
 names_t names;

 void remove(symbol key);
public :
 table();

 void enter(symbol key, B value);
 B look(symbol key);

 // Allows scoping and overloading of symbols of the same name
 void beginScope();
 void endScope();

 // Copies all bindings in the top scope to the scope underneath it, and
 // removes the the top scope.
 void collapseScope();

 // Adds to l, all names prefixed by start.
 void completions(mem::list<symbol >& l, string start);

 friend std::ostream& operator<< <B> (std::ostream& out, const table& t);
};

template <class B>
inline table<B>::table()
{
 beginScope();
}

template <class B>
inline void table<B>::enter(symbol key, B value)
{
 scopes.front().insert(std::make_pair(key,value));
 names[key].push_front(value);
}

template <class B>
inline B table<B>::look(symbol key)
{
 if (!names[key].empty())
   return names[key].front();
 return 0;
}

template <class B>
inline void table<B>::beginScope()
{
 scopes.push_front(scope_t());
}

template <class B>
inline void table<B>::remove(symbol key)
{
 if (!names[key].empty())
   names[key].pop_front();
}

template <class B>
inline void table<B>::endScope()
{
 scope_t &scope = scopes.front();
 for (scope_iterator p = scope.begin(); p != scope.end(); ++p)
   remove(p->first);
 scopes.pop_front();
}

template <class B>
inline void table<B>::collapseScope()
{
 scope_t scope = scopes.front();
 scopes.pop_front();

 scopes.front().insert(scope.begin(), scope.end());
}

// Returns true if start is a prefix for name; eg, mac is a prefix of machine.
inline bool prefix(string start, string name) {
 return equal(start.begin(), start.end(), name.begin());
}

template <class B>
inline void table<B>::completions(mem::list<symbol>& l, string start)
{
 for (names_iterator p = names.begin(); p != names.end(); ++p)
   if (prefix(start, p->first) && !p->second.empty())
     l.push_back(p->first);
}


} // namespace sym

#endif