/*
 * This file is part of the Cforall project
 *
 * $Id: Indexer.cc,v 1.12 2005/08/29 20:14:17 rcbilson Exp $
 *
 */

#include "SynTree/Declaration.h"
#include "SynTree/Type.h"
#include "SynTree/Expression.h"
#include "SynTree/Initializer.h"
#include "SynTree/Statement.h"
#include "Indexer.h"
#include <typeinfo>
#include "utility.h"

#define debugPrint(x) if( doDebug ) { std::cout << x; }

namespace SymTab {

Indexer::Indexer( bool useDebug )
  : doDebug( useDebug )
{
}

Indexer::~Indexer()
{
}

void 
Indexer::visit( ObjectDecl *objectDecl )
{
  maybeAccept( objectDecl->get_type(), *this );
  maybeAccept( objectDecl->get_init(), *this );
  maybeAccept( objectDecl->get_bitfieldWidth(), *this );
  if( objectDecl->get_name() != "" ) {
    debugPrint( "Adding object " << objectDecl->get_name() << std::endl );
    idTable.addDecl( objectDecl );
  }
}

void 
Indexer::visit( FunctionDecl *functionDecl )
{
  if( functionDecl->get_name() == "" ) return;
  debugPrint( "Adding function " << functionDecl->get_name() << std::endl );
  idTable.addDecl( functionDecl );
  enterScope();
  maybeAccept( functionDecl->get_functionType(), *this );
  acceptAll( functionDecl->get_oldDecls(), *this );
  maybeAccept( functionDecl->get_statements(), *this );
  leaveScope();
}

/********
 * A NOTE ON THE ORDER OF TRAVERSAL
 *
 * Types and typedefs have their base types visited before they are added to the type table.
 * This is ok, since there is no such thing as a recursive type or typedef.
 *             typedef struct { T *x; } T; // never allowed
 *
 * for structs/unions, it is possible to have recursion, so the decl should be added as if it's
 * incomplete to begin, the members are traversed, and then the complete type should be added
 * (assuming the type is completed by this particular declaration).
 *             struct T { struct T *x; }; // allowed
 *
 * It's important to add the complete type to the symbol table *after* the members/base has been
 * traversed, since that traversal may modify the definition of the type and these modifications
 * should be visible when the symbol table is queried later in this pass.
 *
 * TODO: figure out whether recursive contexts are sensible/possible/reasonable.
 */

void 
Indexer::visit( TypeDecl *typeDecl )
{
  // see A NOTE ON THE ORDER OF TRAVERSAL, above
  // note that assertions come after the type is added to the symtab, since they aren't part
  // of the type proper and may depend on the type itself
  enterScope();
  acceptAll( typeDecl->get_parameters(), *this );
  maybeAccept( typeDecl->get_base(), *this );
  leaveScope();
  debugPrint( "Adding type " << typeDecl->get_name() << std::endl );
  typeTable.add( typeDecl );
  acceptAll( typeDecl->get_assertions(), *this );
}

void 
Indexer::visit( TypedefDecl *typeDecl )
{
  enterScope();
  acceptAll( typeDecl->get_parameters(), *this );
  maybeAccept( typeDecl->get_base(), *this );
  leaveScope();
  debugPrint( "Adding typedef " << typeDecl->get_name() << std::endl );
  typeTable.add( typeDecl );
}

void 
Indexer::visit( StructDecl *aggregateDecl )
{
  // make up a forward declaration and add it before processing the members
  StructDecl fwdDecl( aggregateDecl->get_name() );
  cloneAll( aggregateDecl->get_parameters(), fwdDecl.get_parameters() );
  debugPrint( "Adding fwd decl for struct " << fwdDecl.get_name() << std::endl );
  structTable.add( &fwdDecl );
  
  enterScope();
  acceptAll( aggregateDecl->get_parameters(), *this );
  acceptAll( aggregateDecl->get_members(), *this );
  leaveScope();
  
  debugPrint( "Adding struct " << aggregateDecl->get_name() << std::endl );
  // this addition replaces the forward declaration
  structTable.add( aggregateDecl );
}

void 
Indexer::visit( UnionDecl *aggregateDecl )
{
  // make up a forward declaration and add it before processing the members
  UnionDecl fwdDecl( aggregateDecl->get_name() );
  cloneAll( aggregateDecl->get_parameters(), fwdDecl.get_parameters() );
  debugPrint( "Adding fwd decl for union " << fwdDecl.get_name() << std::endl );
  unionTable.add( &fwdDecl );
  
  enterScope();
  acceptAll( aggregateDecl->get_parameters(), *this );
  acceptAll( aggregateDecl->get_members(), *this );
  leaveScope();
  
  debugPrint( "Adding union " << aggregateDecl->get_name() << std::endl );
  unionTable.add( aggregateDecl );
}

void 
Indexer::visit( EnumDecl *aggregateDecl )
{
  debugPrint( "Adding enum " << aggregateDecl->get_name() << std::endl );
  enumTable.add( aggregateDecl );
  // unlike structs, contexts, and unions, enums inject their members into the
  // global scope
  acceptAll( aggregateDecl->get_members(), *this );
}

void 
Indexer::visit( ContextDecl *aggregateDecl )
{
  enterScope();
  acceptAll( aggregateDecl->get_parameters(), *this );
  acceptAll( aggregateDecl->get_members(), *this );
  leaveScope();
  
  debugPrint( "Adding context " << aggregateDecl->get_name() << std::endl );
  contextTable.add( aggregateDecl );
}

void 
Indexer::visit( CompoundStmt *compoundStmt )
{
  enterScope();
  acceptAll( compoundStmt->get_kids(), *this );
  leaveScope();
}

void 
Indexer::visit( ContextInstType *contextInst )
{
  acceptAll( contextInst->get_parameters(), *this );
  acceptAll( contextInst->get_members(), *this );
}

void 
Indexer::visit( StructInstType *structInst )
{
  if( !structTable.lookup( structInst->get_name() ) ) {
    debugPrint( "Adding struct " << structInst->get_name() << " from implicit forward declaration" << std::endl );
    structTable.add( structInst->get_name() );
  }
  enterScope();
  acceptAll( structInst->get_parameters(), *this );
  leaveScope();
}

void 
Indexer::visit( UnionInstType *unionInst )
{
  if( !unionTable.lookup( unionInst->get_name() ) ) {
    debugPrint( "Adding union " << unionInst->get_name() << " from implicit forward declaration" << std::endl );
    unionTable.add( unionInst->get_name() );
  }
  enterScope();
  acceptAll( unionInst->get_parameters(), *this );
  leaveScope();
}

void
Indexer::lookupId( const std::string &id, std::list< DeclarationWithType* > &list ) const
{
  idTable.lookupId( id, list );
}

NamedTypeDecl *
Indexer::lookupType( const std::string &id ) const
{
  return typeTable.lookup( id );
}

StructDecl *
Indexer::lookupStruct( const std::string &id ) const
{
  return structTable.lookup( id );
}

EnumDecl *
Indexer::lookupEnum( const std::string &id ) const
{
  return enumTable.lookup( id );
}

UnionDecl *
Indexer::lookupUnion( const std::string &id ) const
{
  return unionTable.lookup( id );
}

ContextDecl *
Indexer::lookupContext( const std::string &id ) const
{
  return contextTable.lookup( id );
}

void 
Indexer::enterScope()
{
  if( doDebug ) {
    std::cout << "--- Entering scope" << std::endl;
  }
  idTable.enterScope();
  typeTable.enterScope();
  structTable.enterScope();
  enumTable.enterScope();
  unionTable.enterScope();
  contextTable.enterScope();
}

void 
Indexer::leaveScope()
{
  using std::cout;
  using std::endl;
  
  if( doDebug ) {
    cout << "--- Leaving scope containing" << endl;
    idTable.dump( cout );
    typeTable.dump( cout );
    structTable.dump( cout );
    enumTable.dump( cout );
    unionTable.dump( cout );
    contextTable.dump( cout );
  }
  idTable.leaveScope();
  typeTable.leaveScope();
  structTable.leaveScope();
  enumTable.leaveScope();
  unionTable.leaveScope();
  contextTable.leaveScope();
}

void 
Indexer::print( std::ostream &os, int indent ) const
{
    idTable.dump( os );
    typeTable.dump( os );
    structTable.dump( os );
    enumTable.dump( os );
    unionTable.dump( os );
    contextTable.dump( os );
}

} // namespace SymTab
