//
// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
//
// The contents of this file are covered under the licence agreement in the
// file "LICENCE" distributed with Cforall.
//
// Indexer.cc -- 
//
// Author           : Richard C. Bilson
// Created On       : Sun May 17 21:37:33 2015
// Last Modified By : Rob Schluntz
// Last Modified On : Wed Aug 05 13:52:42 2015
// Update Count     : 10
//

#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 {
	template< typename Container, typename VisitorType >
	inline void acceptAllNewScope( Container &container, VisitorType &visitor ) {
		visitor.enterScope();
		acceptAll( container, visitor );
		visitor.leaveScope();
	}

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

	Indexer::~Indexer() {}

	void Indexer::visit( ObjectDecl *objectDecl ) {
		enterScope();
		maybeAccept( objectDecl->get_type(), *this );
		leaveScope();
		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 );
		} // if
	}

	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 is 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 are not 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( ApplicationExpr *applicationExpr ) {
		acceptAllNewScope( applicationExpr->get_results(), *this );
		maybeAccept( applicationExpr->get_function(), *this );
		acceptAll( applicationExpr->get_args(), *this );
	}

	void Indexer::visit( UntypedExpr *untypedExpr ) {
		acceptAllNewScope( untypedExpr->get_results(), *this );
		acceptAll( untypedExpr->get_args(), *this );
	}

	void Indexer::visit( NameExpr *nameExpr ) {
		acceptAllNewScope( nameExpr->get_results(), *this );
	}

	void Indexer::visit( AddressExpr *addressExpr ) {
		acceptAllNewScope( addressExpr->get_results(), *this );
		maybeAccept( addressExpr->get_arg(), *this );
	}

	void Indexer::visit( LabelAddressExpr *labAddressExpr ) {
		acceptAllNewScope( labAddressExpr->get_results(), *this );
		maybeAccept( labAddressExpr->get_arg(), *this );
	}

	void Indexer::visit( CastExpr *castExpr ) {
		acceptAllNewScope( castExpr->get_results(), *this );
		maybeAccept( castExpr->get_arg(), *this );
	}

	void Indexer::visit( UntypedMemberExpr *memberExpr ) {
		acceptAllNewScope( memberExpr->get_results(), *this );
		maybeAccept( memberExpr->get_aggregate(), *this );
	}

	void Indexer::visit( MemberExpr *memberExpr ) {
		acceptAllNewScope( memberExpr->get_results(), *this );
		maybeAccept( memberExpr->get_aggregate(), *this );
	}

	void Indexer::visit( VariableExpr *variableExpr ) {
		acceptAllNewScope( variableExpr->get_results(), *this );
	}

	void Indexer::visit( ConstantExpr *constantExpr ) {
		acceptAllNewScope( constantExpr->get_results(), *this );
		maybeAccept( constantExpr->get_constant(), *this );
	}

	void Indexer::visit( SizeofExpr *sizeofExpr ) {
		acceptAllNewScope( sizeofExpr->get_results(), *this );
		if ( sizeofExpr->get_isType() ) {
			maybeAccept( sizeofExpr->get_type(), *this );
		} else {
			maybeAccept( sizeofExpr->get_expr(), *this );
		}
	}

	void Indexer::visit( AttrExpr *attrExpr ) {
		acceptAllNewScope( attrExpr->get_results(), *this );
		if ( attrExpr->get_isType() ) {
			maybeAccept( attrExpr->get_type(), *this );
		} else {
			maybeAccept( attrExpr->get_expr(), *this );
		}
	}

	void Indexer::visit( LogicalExpr *logicalExpr ) {
		acceptAllNewScope( logicalExpr->get_results(), *this );
		maybeAccept( logicalExpr->get_arg1(), *this );
		maybeAccept( logicalExpr->get_arg2(), *this );
	}

	void Indexer::visit( ConditionalExpr *conditionalExpr ) {
		acceptAllNewScope( conditionalExpr->get_results(), *this );
		maybeAccept( conditionalExpr->get_arg1(), *this );
		maybeAccept( conditionalExpr->get_arg2(), *this );
		maybeAccept( conditionalExpr->get_arg3(), *this );
	}

	void Indexer::visit( CommaExpr *commaExpr ) {
		acceptAllNewScope( commaExpr->get_results(), *this );
		maybeAccept( commaExpr->get_arg1(), *this );
		maybeAccept( commaExpr->get_arg2(), *this );
	}

	void Indexer::visit( TupleExpr *tupleExpr ) {
		acceptAllNewScope( tupleExpr->get_results(), *this );
		acceptAll( tupleExpr->get_exprs(), *this );
	}

	void Indexer::visit( SolvedTupleExpr *tupleExpr ) {
		acceptAllNewScope( tupleExpr->get_results(), *this );
		acceptAll( tupleExpr->get_exprs(), *this );
	}

	void Indexer::visit( TypeExpr *typeExpr ) {
		acceptAllNewScope( typeExpr->get_results(), *this );
		maybeAccept( typeExpr->get_type(), *this );
	}

	void Indexer::visit( AsmExpr *asmExpr ) {
		maybeAccept( asmExpr->get_inout(), *this );
		maybeAccept( asmExpr->get_constraint(), *this );
		maybeAccept( asmExpr->get_operand(), *this );
	}

	void Indexer::visit( UntypedValofExpr *valofExpr ) {
		acceptAllNewScope( valofExpr->get_results(), *this );
		maybeAccept( valofExpr->get_body(), *this );
	}


	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::visit( ForStmt *forStmt ) {
	    // for statements introduce a level of scope
	    enterScope();
	    Visitor::visit( forStmt );
	    leaveScope();
	}


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

	DeclarationWithType* Indexer::lookupId( const std::string &id) const {
		return idTable.lookupId(id);
	}

	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 {
	    using std::cerr;
	    using std::endl;

	    cerr << "===idTable===" << endl;
	    idTable.dump( os );
	    cerr << "===typeTable===" << endl;
	    typeTable.dump( os );
	    cerr << "===structTable===" << endl;
	    structTable.dump( os );
	    cerr << "===enumTable===" << endl;
	    enumTable.dump( os );
	    cerr << "===unionTable===" << endl;
	    unionTable.dump( os );
	    cerr << "===contextTable===" << endl;
	    contextTable.dump( os );
#if 0
		idTable.dump( os );
		typeTable.dump( os );
		structTable.dump( os );
		enumTable.dump( os );
		unionTable.dump( os );
		contextTable.dump( os );
#endif
	}
} // namespace SymTab

// Local Variables: //
// tab-width: 4 //
// mode: c++ //
// compile-command: "make install" //
// End: //
