note
	description:

		"Maps variables to their type"

	copyright: "Copyright (c) 2005, Andreas Leitner and others"
	license: "Eiffel Forum License v2 (see forum.txt)"
	date: "$Date$"
	revision: "$Revision$"

class AUT_VARIABLE_TABLE

inherit
	AUT_SHARED_RANDOM
		export {NONE} all end

	ITP_VARIABLE_CONSTANTS

create

	make

feature {NONE} -- Initialization

	make (a_system: like system)
			-- Create new handler.
		require
			a_system_not_void: a_system /= Void
		local
			tester: AUT_VARIABLE_EQUALITY_TESTER
		do
			create name_generator.make_with_string_stream (variable_name_prefix)
			create tester.make
			create variable_type_table.make_with_key_tester (10, tester)
			system := a_system
		ensure
			system_set: system = a_system
		end

feature -- Status report

	is_variable_defined (a_variable: ITP_VARIABLE): BOOLEAN
			-- Is variable `a_variable' defined in interpreter?
		require
			a_variable_not_void: a_variable /= Void
		do
			Result := variable_type_table.has (a_variable)
		end

	are_expressions_valid (a_list: DS_LINEAR [ITP_EXPRESSION]): BOOLEAN
			-- Are the expressions in `a_list' valid? I.e. are the variables in `a_list' defined?
		require
			a_list_not_void: a_list /= Void
			a_list_doesnt_have_void: not a_list.has (Void)
		local
			cs: DS_LINEAR_CURSOR [ITP_EXPRESSION]
			v: ITP_VARIABLE
		do
			from
				cs := a_list.new_cursor
				cs.start
				Result := True
			until
				cs.off or not Result
			loop
				v ?= cs.item
				if v /= Void and then not is_variable_defined (v) then
					Result := False
				else
					cs.forth
				end
			end
			cs.go_after
		end

feature -- Access

	system: SYSTEM_I
			-- system

	variable_type (a_variable: ITP_VARIABLE): TYPE_A
			-- Type of `a_variable'
			-- Void of the value of `a_variable' is Void so we don't know the exact type of `a_variable' from the proxy side.
		require
			a_variable_not_void: a_variable /= Void
			variable_defined: is_variable_defined (a_variable)
		do
			Result := variable_type_table.item (a_variable)
		ensure
			result_attached: Result /= Void
		end

	random_variable: ITP_VARIABLE
			-- Random variable from interpreter or `Void' if none
			-- defined
		local
			i: INTEGER
			j: INTEGER
			l_variable_type_table: like variable_type_table
		do
			l_variable_type_table := variable_type_table
			if variable_type_table.count > 0 then
				random.forth
				i := (random.item  \\ variable_type_table.count) + 1
				from
					j := 1
					l_variable_type_table.start
				until
					i = j
				loop
					l_variable_type_table.forth
					j := j + 1
				end
				Result := l_variable_type_table.key_for_iteration
			end
		ensure
			variable_defined: Result /= Void implies is_variable_defined (Result)
		end

	random_conforming_variable (a_context_class: CLASS_C; a_type: TYPE_A): ITP_VARIABLE
			-- Random variable of `conforming_variables (a_type)' or Void if list
			-- is emtpy
		require
			a_context_class_not_Void: a_context_class /= Void
			a_context_class_valid: a_context_class.is_valid
			a_type_not_void: a_type /= Void
		local
			list: like conforming_variables
			cs: DS_LINEAR_CURSOR [ITP_VARIABLE]
			i: INTEGER
			j: INTEGER
		do
			list := conforming_variables (a_context_class, a_type)
			if list.count > 0 then
				random.forth
				i := (random.item  \\ list.count) + 1
				from
					j := 1
					cs := list.new_cursor
					cs.start
				until
					i = j
				loop
					cs.forth
					j := j + 1
				end
				Result := cs.item
				cs.go_after
			end
		ensure
			variable_defined: Result /= Void implies is_variable_defined (Result)
		end

	conforming_variables (a_context_class: CLASS_C; a_type: TYPE_A): DS_LIST [ITP_VARIABLE]
			-- All defeined variables conforming to `a_type'
		require
			a_context_class_not_Void: a_context_class /= Void
			a_context_class_valid: a_context_class.is_valid
			a_type_not_void: a_type /= Void
		local
			l_type: TYPE_A
		do
			create {DS_ARRAYED_LIST [ITP_VARIABLE]} Result.make (variable_type_table.count)
			across variable_type_table as cs loop
				l_type := cs.item.actual_type

					-- On non-Void-Safe
					-- We only allow Void conforms to a non expanded type.
					--
					-- On Void-Safe: Void is discarded
					-- when the type `a_type` is directly attached.
					--
					-- To check if the current system is Void-Safe we just do the following comparison.
					-- system.void_safety_index /= {CONF_TARGET_OPTION}.void_safety_index_none

				if system.void_safety_index /= {CONF_TARGET_OPTION}.void_safety_index_none and then l_type.is_none and then a_type.is_implicitly_attached then
						-- Avoid void.
				elseif l_type.is_conformant_to (a_context_class, a_type) and then not (a_type.is_expanded and then l_type.is_none) then
					Result.force_last (cs.key)
				end
			end
		ensure
			variables_not_void: Result /= Void
			variables_doesnt_have_void: not Result.has (Void)
		end

	new_variable: ITP_VARIABLE
			-- Variable not yet defined
		do
			name_generator.generate_new_name
			create Result.make (name_generator.index)
			name_generator.output_string.wipe_out
		ensure
			variable_not_void: Result /= Void
			variable_not_defined: not is_variable_defined (Result)
		end

feature -- Element change

	define_variable (a_variable: ITP_VARIABLE; a_type: TYPE_A)
			-- Define variable `a_variable' to be of type `a_type'.
			-- `a_type' is Void means that the value of `a_variable' is Void so we don't know the exact type of it
			-- from the proxy side.
		require
			a_variable_not_void: a_variable /= Void
			a_type_attached: a_type /= Void
		do
			variable_type_table.force (a_type, a_variable)
		ensure
			variable_defined: is_variable_defined (a_variable)
			variable_has_valid_type: variable_type (a_variable) = a_type
		end

feature -- Removal

	wipe_out
			-- Remove all variable mappings.
		do
			create name_generator.make_with_string_stream (variable_name_prefix)
			variable_type_table.wipe_out
		ensure
			variable_type_table_empty: variable_type_table.is_empty
		end

	name_generator: AUT_UNIQUE_NAME_GENERATOR
			-- Name generator for variable names

	variable_type_table: HASH_TABLE_EX [TYPE_A, ITP_VARIABLE]
			-- Table mapping interprteter variables to their type

invariant

	system_not_void: system /= Void
	name_generator_not_void: name_generator /= Void
	variable_type_table_not_void: variable_type_table /= Void
	all_variables_have_type: not variable_type_table.has (Void)

note
	copyright: "Copyright (c) 1984-2020, Eiffel Software"
	license:   "GPL version 2 (see http://www.eiffel.com/licensing/gpl.txt)"
	licensing_options: "http://www.eiffel.com/licensing"
	copying: "[
			This file is part of Eiffel Software's Eiffel Development Environment.
			
			Eiffel Software's Eiffel Development Environment 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, version 2 of the License
			(available at the URL listed under "license" above).
			
			Eiffel Software's Eiffel Development Environment 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 Eiffel Software's Eiffel Development
			Environment; if not, write to the Free Software Foundation,
			Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
		]"
	source: "[
			Eiffel Software
			5949 Hollister Ave., Goleta, CA 93117 USA
			Telephone 805-685-1006, Fax 805-685-6869
			Website http://www.eiffel.com
			Customer support http://support.eiffel.com
		]"
end