''	FreeBASIC - 32-bit BASIC Compiler.
''	Copyright (C) 2004-2010 The FreeBASIC development team.
''
''	This program is free software; you can redistribute it and/or modify
''	it under the terms of the GNU General Public License as published by
''	the Free Software Foundation; either version 2 of the License, or
''	(at your option) any later version.
''
''	This program is distributed in the hope that it will be useful,
''	but WITHOUT ANY WARRANTY; without even the implied warranty of
''	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
''	GNU General Public License for more details.
''
''	You should have received a copy of the GNU General Public License
''	along with this program; if not, write to the Free Software
''	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.


'' symbol table module for namespaces
'' (note: all functions but Add() and Del() can be used with any UDT)
''
'' chng: may/2006 written [v1ctor]


#include once "inc\fb.bi"
#include once "inc\fbint.bi"
#include once "inc\parser.bi"

declare sub symbCompAddToImportList _
	( _
		byval imp_ as FBSYMBOL ptr _
	)

declare sub symbCompDelFromImportList _
	( _
		byval imp_ as FBSYMBOL ptr _
	)

declare sub symbCompAddToExportList _
	( _
		byval imp_ as FBSYMBOL ptr _
	)

declare sub symbCompDelFromExportList _
	( _
		byval imp_ as FBSYMBOL ptr _
	)

'':::::
function symbAddNamespace _
	( _
		byval id as zstring ptr, _
		byval id_alias as zstring ptr _
	) as FBSYMBOL ptr

    dim as FBSYMBOL ptr s = any

    '' no explict alias given?
    if( id_alias = NULL ) then
    	'' only preserve a case-sensitive version if in BASIC mangling
    	if( parser.mangling <> FB_MANGLING_BASIC ) then
    		id_alias = id
    	end if
    end if

    s = symbNewSymbol( FB_SYMBOPT_DOHASH, _
    				   NULL, _
    				   NULL, NULL,  _
    				   FB_SYMBCLASS_NAMESPACE, _
    				   id, id_alias, _
    				   FB_DATATYPE_NAMESPC, NULL )
    if( s = NULL ) then
    	return NULL
    end if

	symbSymbTbInit( s->nspc.ns.symtb, s )
    symbHashTbInit( s->nspc.ns.hashtb, s, FB_INITSYMBOLNODES \ 10 )

    s->nspc.ns.ext = NULL
    s->nspc.cnt = 0
    s->nspc.last_tail = NULL

	function = s

end function

'':::::
sub symbDelNamespace _
	( _
		byval ns as FBSYMBOL ptr, _
		byval is_tbdel as integer _
	)

    if( ns = NULL ) then
    	exit sub
    end if

    ''
    symbCompDelImportList( ns )

    '' del all symbols inside the namespace
    do
		'' starting from last because of the USING's that could be
		'' referencing a namespace in the same scope block
		dim as FBSYMBOL ptr s = symbGetCompSymbTb( ns ).tail
		if( s = NULL ) then
			exit do
		end if

		symbDelSymbol( s, TRUE )
	loop

	''
	if( ns->nspc.ns.ext <> NULL ) then
		symbCompFreeExt( ns->nspc.ns.ext )
		ns->nspc.ns.ext = NULL
	end if

	''
	hashFree( @ns->nspc.ns.hashtb.tb )

	'' del node
	symbFreeSymbol( ns )

end sub

'':::::
private function hAddImport _
	( _
		byval dst_ns as FBSYMBOL ptr _
	) as FBSYMBOL ptr

	dim as FBSYMBOLTB ptr symbtb = any
	dim as FBHASHTB ptr hashtb = any
	dim as integer is_local = any

    if( dst_ns = symbGetCurrentNamespc( ) ) then
		symbtb = NULL
		hashtb = NULL
		is_local = 0
	else
		symbtb = @symbGetCompSymbTb( dst_ns )
		hashtb = @symbGetCompHashTb( dst_ns )
		is_local = symbIsLocal( dst_ns )
	end if

    '' easier to be added as a symbol because it will be removed
    '' respecting the scope blocks (or procs)
    function = symbNewSymbol( FB_SYMBOPT_NONE, _
    				   		  NULL, _
    				   		  symbtb, hashtb, _
    				   		  FB_SYMBCLASS_NSIMPORT, _
    				   		  NULL, NULL, _
    				   		  FB_DATATYPE_INVALID, NULL, _
    				   		  is_local )
end function

'':::::
private sub hAddToHashTbList _
	( _
		byval ns as FBSYMBOL ptr _
	)

	'' add it to hash tb list
	symbGetCompExt( ns )->cnt += 1
	if( symbGetCompExt( ns )->cnt = 1 ) then
		symbHashListInsertNamespace( ns, symbGetCompSymbTb( ns ).head )
	end if

end sub

'':::::
private sub hDelFromHashTbList _
	( _
		byval ns as FBSYMBOL ptr _
	)

	'' del the ns from hash tb list
	symbGetCompExt( ns )->cnt -= 1
	if( symbGetCompExt( ns )->cnt = 0 ) then
		symbHashListRemoveNamespace( ns )
	end if

end sub

'':::::
private function hIsOnParentList _
	( _
		byval src_ns as FBSYMBOL ptr, _
		byval dst_ns as FBSYMBOL ptr _
	) as integer

	dim as FBSYMBOL ptr parent = dst_ns
	do until( parent = @symbGetGlobalNamespc( ) )
		if( src_ns = parent ) then
			return TRUE
		end if
		parent = symbGetNamespace( parent )
	loop

	function = FALSE

end function

'':::::
private function hIsOnImportList _
	( _
		byval src_ns as FBSYMBOL ptr, _
		byval dst_ns as FBSYMBOL ptr _
	) as integer

	dim as FBSYMBOL ptr imp_ = symbGetCompImportHead( dst_ns )
	do while( imp_ <> NULL )
	    if( symbGetImportNamespc( imp_ ) = src_ns ) then
	    	return TRUE
	    end if
		imp_ = symbGetImportNext( imp_ )
	loop

	function = FALSE

end function

'':::::
function symbNamespaceImportEx _
	( _
		byval ns as FBSYMBOL ptr, _
		byval to_ns as FBSYMBOL ptr _
	) as integer

	'' importing itself or a parent?
	if( hIsOnParentList( ns, to_ns ) ) then
		return FALSE
	end if

	if( symbGetCompExt( ns ) = NULL ) then
		symbGetCompExt( ns ) = symbCompAllocExt( )
	end if

	'' not already on list?
	if( hIsOnImportList( ns, to_ns ) = FALSE ) then
		dim as FBSYMBOL ptr imp_ = any

		imp_ = hAddImport( to_ns )
		if( imp_ = NULL ) then
			return FALSE
		end if

		imp_->nsimp.imp_ns = ns
		imp_->nsimp.exp_ns = to_ns

		'' add to the import list of the dst ns
		symbCompAddToImportList( imp_ )

		'' add to the export list of the src ns
		symbCompAddToExportList( imp_ )

		'' recurse into the imported ns, importing its imports too
		imp_ = symbGetCompImportHead( ns )
		do while( imp_ <> NULL )
			symbNamespaceImportEx( symbGetImportNamespc( imp_ ), to_ns )
			imp_ = symbGetImportNext( imp_ )
		loop

		'' add to hash tb list
		hAddToHashTbList( ns )
	end if

	function = TRUE

end function

'':::::
function symbNamespaceImport _
	( _
		byval ns as FBSYMBOL ptr _
	) as integer

	function = symbNamespaceImportEx( ns, symbGetCurrentNamespc( ) )

end function

'':::::
sub symbNamespaceRemove _
	( _
		byval imp_ as FBSYMBOL ptr, _
		byval hashonly as integer, _
		byval is_tbdel as integer _
	)

	if( symbGetImportNamespc( imp_ ) <> NULL ) then
		'' remove all USING's
		hDelFromHashTbList( symbGetImportNamespc( imp_ ) )
		symbCompDelFromExportList( imp_ )
		symbCompDelFromImportList( imp_ )
		symbGetImportNamespc( imp_ ) = NULL
	end if

	if( hashonly = FALSE ) then
		'' del node
		symbFreeSymbol( imp_ )
	end if

end sub

'':::::
sub symbNamespaceReImport _
	( _
		byval ns as FBSYMBOL ptr _
	)

	'' currently at the import hash tb?
	if( symbGetCompExt( ns )->impsym_head <> NULL ) then
		dim as FBSYMBOL ptr head = symbGetNamespaceLastTail( ns )
		if( head = NULL ) then
	    	head = symbGetNamespaceTbHead( ns )
		else
			'' skip the last (already added)
			head = head->next
		end if

		if( head <> NULL ) then
			symbHashListInsertNamespace( ns, head )
		end if
	end if

	'' for each namespace importing this namespace, update
	'' their import lists with our import list (that could
	'' have been updated in the re-implementation)
	dim as FBSYMBOL ptr exp_ = symbGetCompExportHead( ns )
	do while( exp_ <> NULL )
		dim as FBSYMBOL ptr imp_ = symbGetCompImportHead( ns )
		do while( imp_ <> NULL )
        	symbNamespaceImportEx( symbGetImportNamespc( imp_ ), _
        						   symbGetExportNamespc( exp_ ) )
			imp_ = symbGetImportNext( imp_ )
		loop
		exp_ = symbGetExportNext( exp_ )
	loop

end sub

