//
// 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.
//
// IdTable.cc -- 
//
// Author           : Richard C. Bilson
// Created On       : Sun May 17 17:04:02 2015
// Last Modified By : Peter A. Buhr
// Last Modified On : Sun May 17 17:07:43 2015
// Update Count     : 3
//

#include <cassert>

#include "SynTree/Declaration.h"
#include "ResolvExpr/typeops.h"
#include "Indexer.h"
#include "Mangler.h"
#include "IdTable.h"
#include "SemanticError.h"

using std::string;

namespace SymTab {
	IdTable::IdTable() : scopeLevel( 0 ) {
	}

	void IdTable::enterScope() {
		scopeLevel++;
	}

	void IdTable::leaveScope() {
		for ( OuterTableType::iterator outer = table.begin(); outer != table.end(); ++outer ) {
			for ( InnerTableType::iterator inner = outer->second.begin(); inner != outer->second.end(); ++inner ) {
				std::stack< DeclEntry >& entry = inner->second;
				if ( ! entry.empty() && entry.top().second == scopeLevel ) {
					entry.pop();
				} // if
			} // for
		} // for

		scopeLevel--;
		assert( scopeLevel >= 0 );
	}

	void IdTable::addDecl( DeclarationWithType *decl ) {
		const string &name = decl->get_name();
		string manglename;
		if ( decl->get_linkage() == LinkageSpec::C ) {
			manglename = name;
		} else {
			manglename = Mangler::mangle( decl );
		} // if

		InnerTableType &declTable = table[ name ];
		InnerTableType::iterator it = declTable.find( manglename );

		if ( it == declTable.end() ) {
			declTable[ manglename ].push( DeclEntry( decl, scopeLevel ) );
		} else {
			std::stack< DeclEntry >& entry = it->second;
			if ( ! entry.empty() && entry.top().second == scopeLevel ) {
				if ( decl->get_linkage() != LinkageSpec::C || ResolvExpr::typesCompatible( decl->get_type(), entry.top().first->get_type(), Indexer() ) ) {
					FunctionDecl *newentry = dynamic_cast< FunctionDecl* >( decl );
					FunctionDecl *old = dynamic_cast< FunctionDecl* >( entry.top().first );
					if ( newentry && old && newentry->get_statements() && old->get_statements() ) {
						throw SemanticError( "duplicate function definition for ", decl );
					} else {
						ObjectDecl *newobj = dynamic_cast< ObjectDecl* >( decl );
						ObjectDecl *oldobj = dynamic_cast< ObjectDecl* >( entry.top().first );
						if ( newobj && oldobj && newobj->get_init() && oldobj->get_init() ) {
							throw SemanticError( "duplicate definition for ", decl );
						} // if
					} // if
				} else {
					throw SemanticError( "duplicate definition for ", decl );
				} // if
			} else {
				declTable[ manglename ].push( DeclEntry( decl, scopeLevel ) );
			} // if
		} // if
		// ensure the set of routines with C linkage cannot be overloaded
		for ( InnerTableType::iterator i = declTable.begin(); i != declTable.end(); ++i ) {
			if ( ! i->second.empty() && i->second.top().first->get_linkage() == LinkageSpec::C && declTable.size() > 1 ) {
				InnerTableType::iterator j = i;
				for ( j++; j != declTable.end(); ++j ) {
					if ( ! j->second.empty() && j->second.top().first->get_linkage() == LinkageSpec::C ) {
						throw SemanticError( "invalid overload of C function " );
					} // if
				} // for
			} // if
		} // for
	}

	void IdTable::lookupId( const std::string &id, std::list< DeclarationWithType* >& decls ) const {
		OuterTableType::const_iterator outer = table.find( id );
		if ( outer == table.end() ) return;
		const InnerTableType &declTable = outer->second;
		for ( InnerTableType::const_iterator it = declTable.begin(); it != declTable.end(); ++it ) {
			const std::stack< DeclEntry >& entry = it->second;
			if ( ! entry.empty() ) {
				decls.push_back( entry.top().first );
			} // if
		} // for
	}

	DeclarationWithType * IdTable::lookupId( const std::string &id) const {
		DeclarationWithType* result = 0;
		int depth = -1;

		OuterTableType::const_iterator outer = table.find( id );
		if ( outer == table.end() ) return 0;
		const InnerTableType &declTable = outer->second;
		for ( InnerTableType::const_iterator it = declTable.begin(); it != declTable.end(); ++it ) {
			const std::stack< DeclEntry >& entry = it->second;
			if ( ! entry.empty() && entry.top().second > depth ) {
				result = entry.top().first;
				depth = entry.top().second;
			} // if
		} // for
		return result;
	}

	void IdTable::dump( std::ostream &os ) const {
		for ( OuterTableType::const_iterator outer = table.begin(); outer != table.end(); ++outer ) {
			for ( InnerTableType::const_iterator inner = outer->second.begin(); inner != outer->second.end(); ++inner ) {
#if 0
				const std::stack< DeclEntry >& entry = inner->second;
				if ( ! entry.empty() ) { // && entry.top().second == scopeLevel ) {
					os << outer->first << " (" << inner->first << ") (" << entry.top().second << ")" << std::endl;
				} else {
					os << outer->first << " (" << inner->first << ") ( entry-empty)" << std::endl;
				} // if
#endif
#if 0
				std::stack<DeclEntry> stack = inner->second;
				os << "dumping a stack" << std::endl;
				while ( ! stack.empty()) {
					DeclEntry d = stack.top();
					os << outer->first << " (" << inner->first << ") (" << d.second << ") " << std::endl;
					stack.pop();
				} // while
#endif
			} // for
		} // for
#if 0
		for ( OuterTableType::const_iterator outer = table.begin(); outer != table.end(); ++outer ) {
			for ( InnerTableType::const_iterator inner = outer->second.begin(); inner != outer->second.end(); ++inner ) {
				const std::stack< DeclEntry >& entry = inner->second;
				if ( ! entry.empty() && entry.top().second == scopeLevel ) {
					os << outer->first << " (" << inner->first << ") (" << scopeLevel << ")" << std::endl;
				} // if
			} // for
		} // for
#endif
	}
} // namespace SymTab

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