note
	description: "Factory for compiler which generates descendants of certain AST classes."
	legal: "See notice at end of class."
	status: "See notice at end of class."
	date: "$Date$"
	revision: "$Revision$"

class
	AST_COMPILER_FACTORY

inherit
	AST_FACTORY
		redefine
			new_array_as,
			set_expanded_class_type,
			new_debug_as,
			new_expr_address_as,
			new_feature_as,
			new_feature_name_alias_as,
			new_formal_dec_as,
			new_integer_as,
			new_integer_hexa_as,
			new_integer_octal_as,
			new_integer_binary_as,
			new_external_lang_as,
			new_vtgc1_error,
			new_vvok1_error, new_vvok2_error,
			validate_integer_real_type,
			validate_non_conforming_inheritance_type
		end

	SHARED_WORKBENCH
		export
			{NONE} all
		end

	SHARED_ERROR_HANDLER
		export
			{NONE} all
		end

	COMPILER_EXPORTER
		export
			{NONE} all
		end

	SHARED_STATELESS_VISITOR
		export
			{NONE} all
		end

	SHARED_STATEFUL_VISITOR
		export
			{NONE} all
		end

		-- FIXME: we are only using it to generate the errors in `new_expr_address_as' and `new_class_as'
		-- for reporting unsupported constructs. Ideally we need a different way of generating errors,
		-- for example, by making the parser aware of such limitations.
	SHARED_EIFFEL_PARSER
		export
			{NONE} all
		end

feature -- Access

	new_array_as (exp: detachable EIFFEL_LIST [EXPR_AS]; l_as, r_as: detachable SYMBOL_AS): detachable COMPILER_ARRAY_AS
			-- New COMPILER_ARRAY_AS
		do
			if exp /= Void then
				create Result.initialize (exp, l_as, r_as)
			end
		end

	set_expanded_class_type (a_type: detachable TYPE_AS; is_expanded: BOOLEAN; s_as: detachable KEYWORD_AS)
		do
			Precursor {AST_FACTORY} (a_type, is_expanded, s_as)
			if is_expanded then
				system.set_has_expanded
				if attached {CLASS_C} parser.current_class as l_class_c then
					l_class_c.set_has_expanded
				end
			end
		end

	new_debug_as (k: detachable KEY_LIST_AS; c: detachable EIFFEL_LIST [INSTRUCTION_AS]; d_as, e: detachable KEYWORD_AS): detachable DEBUG_AS
		local
			l_str: STRING
		do
			if e /= Void then
				create Result.initialize (k, c, d_as, e)
				if k /= Void and then k.keys /= Void then
					from
							-- Debug keys are not case sensitive
						k.keys.start
					until
						k.keys.after
					loop
						k.keys.item.value_to_lower
						l_str := k.keys.item.value
						system.add_new_debug_clause (l_str)
						k.keys.forth
					end
				end
			end
		end

	new_expr_address_as (e: detachable EXPR_AS; a_as, l_as, r_as: detachable SYMBOL_AS): detachable EXPR_ADDRESS_AS
		do
			if not system.address_expression_allowed then
				error_handler.insert_error (create {SYNTAX_ERROR}.init (eiffel_parser))
			elseif e /= Void then
				create Result.initialize (e, a_as, l_as, r_as)
			end
		end

	new_external_lang_as (l: detachable STRING_AS): detachable COMPILER_EXTERNAL_LANG_AS
			-- New EXTERNAL_LANGUAGE AST node
		do
			if l /= Void then
				create Result.initialize (l)
			end
		end

	new_feature_as (f: detachable EIFFEL_LIST [FEATURE_NAME]; b: detachable BODY_AS; i: detachable INDEXING_CLAUSE_AS; next_pos: INTEGER): detachable FEATURE_AS
			-- New FEATURE AST node
		local
			feature_name: FEATURE_NAME
			is_query: BOOLEAN
			argument_count: INTEGER
			arguments: EIFFEL_LIST [TYPE_DEC_AS]
			vfav: VFAV_SYNTAX
			l_built_in_processor: like built_in_processor
			l_built_in_class_as: CLASS_AS
			l_built_in_feature_node: FEATURE_AS
			l_routine_as: ROUTINE_AS
			l_built_in_as: BUILT_IN_AS
		do
			if
				(f /= Void and then not f.is_empty) and b /= Void
			then
					-- Check if there are any operator names that violate VFAV rules
				is_query := b.type /= Void
				from
					f.start
				until
					f.after
				loop
					feature_name := f.item
					if attached {FEATURE_NAME_ALIAS_AS} feature_name as l_feat_name_alias_as then
							-- TODO This code occurs in almost the same fashion in `{RENAMING_A}.adapt_alias_feature_name_properties'
						vfav := Void
						arguments := b.arguments
						if arguments /= Void then
								-- It's possible to calculate the value of `argument_count' once
								-- before loop, but from the other hand it is required only for
								-- features with aliases, so it makes sense to put it here.
							from
								argument_count := 0
								arguments.start
							until
								arguments.after
							loop
								argument_count := argument_count + arguments.item.id_list.count
								arguments.forth
							end
						end
						if l_feat_name_alias_as.has_bracket_alias then
							if not is_query or else argument_count < 1 then
									-- Invalid bracket alias.
								create {VFAV2_SYNTAX} vfav.make (l_feat_name_alias_as, l_feat_name_alias_as.bracket_alias_as)
							elseif l_feat_name_alias_as.has_convert_mark then
									-- Invalid convert mark.
								create {VFAV5_SYNTAX} vfav.make (l_feat_name_alias_as, l_feat_name_alias_as.bracket_alias_as)
							end
						elseif l_feat_name_alias_as.has_parentheses_alias then
							if argument_count < 1 then
									-- Invalid parenthesis alias.
								create {VFAV3_SYNTAX} vfav.make (l_feat_name_alias_as, l_feat_name_alias_as.parenthesis_alias_as)
							elseif l_feat_name_alias_as.has_convert_mark then
									-- Invalid convert mark.
								create {VFAV5_SYNTAX} vfav.make (l_feat_name_alias_as, l_feat_name_alias_as.parenthesis_alias_as)
							end
						elseif is_query then
							across
								l_feat_name_alias_as.aliases as ic
							until
								vfav /= Void
							loop
								if
									(argument_count = 0 and then ic.item.is_valid_unary) or else
									(argument_count = 1 and then ic.item.is_valid_binary)
								then
										-- FIXME: maybe move this check outside the loop [2019-09-25].
									if argument_count = 0 and then l_feat_name_alias_as.has_convert_mark then
											-- Invalid convert mark
										create {VFAV5_SYNTAX} vfav.make (l_feat_name_alias_as, ic.item.alias_name)
									end
								else
										-- Invalid operator alias
									create {VFAV1_SYNTAX} vfav.make (l_feat_name_alias_as, ic.item.alias_name)
								end
							end
							if vfav /= Void then
									-- Error detected
							elseif argument_count = 1 then
								l_feat_name_alias_as.set_is_binary
							elseif l_feat_name_alias_as.has_convert_mark then
								create {VFAV5_SYNTAX} vfav.make (l_feat_name_alias_as, Void)
							else
								l_feat_name_alias_as.set_is_unary
							end
						else
								-- This is an alias, but not a bracket or parenthesis alias, and this is not a query ...
							create {VFAV1_SYNTAX} vfav.make (l_feat_name_alias_as, Void)
						end
						if vfav /= Void then
							error_handler.insert_error (vfav)
						end
					end
					f.forth
				end
				create Result.initialize (f, b, i, system.feature_as_counter.next_id, next_pos)

				if b.is_built_in and then system.workbench.is_compiling then
						-- The system.workbench.is_compiling needs to be checked here because refactoring uses this factory.
						-- When working with uncompiled classes system.current_class will be Void. See fix_me below

						-- We have a built in so we set the replacement feature inside if available.
					l_built_in_processor := built_in_processor
					l_built_in_processor.parse_current_class (parser.current_class, system.il_generation)
					l_built_in_class_as := l_built_in_processor.class_as
					if l_built_in_class_as /= Void then
							-- We have an associating built in class.
							-- If we find a feature with the name matching the built in then we set this
							-- replacement feature as an attribute of the dummy built in.
						l_built_in_feature_node := l_built_in_class_as.feature_with_name (f.first.feature_name.name_id)
						if l_built_in_feature_node /= Void then
							l_routine_as ?= b.content
							l_built_in_as ?= l_routine_as.routine_body
							l_built_in_as.set_body (l_built_in_feature_node)
								-- Add any newly introduced suppliers if any.
							if l_built_in_class_as.suppliers /= Void then
									-- Make sure any uncompiled classes referenced by the built in get compiled.
									--| FIXME IEK Optimize if possible as this only needs to be performed once per built in class.
								eiffel_parser.suppliers.merge (l_built_in_class_as.suppliers)
							end
						end
					end
					l_built_in_processor.reset
				end

				if b.is_unique and then attached {CLASS_C} parser.current_class as l_class_c then
					l_class_c.set_has_unique
				end
			end
		end

	new_feature_name_alias_as (feature_name: detachable ID_AS; aliases: detachable LIST [FEATURE_ALIAS_NAME]; convert_keyword: detachable KEYWORD_AS): detachable FEATURE_NAME_ALIAS_AS
			-- <Precursor>
		local
			first_operator, second_operator:  STRING_AS
			has_error: BOOLEAN
		do
			if attached feature_name and then attached aliases then
				across
					aliases as a
				loop
					first_operator := a.item.alias_name
					across
						aliases as b
					loop
						second_operator := b.item.alias_name
						if
								-- Avoid processing already processed operators.
							a.target_index < b.target_index and then
								-- Check whether aliases have the same name.
							first_operator.value.same_string (second_operator.value)
						then
							error_handler.insert_error (create {VFAV4_SYNTAX}.make (feature_name, first_operator, second_operator))
							has_error := True
						end
					end
				end
				if not has_error then
					Result := Precursor (feature_name, aliases, convert_keyword)
				end
			end
		end

	new_formal_dec_as (f: detachable FORMAL_AS; c: detachable CONSTRAINT_LIST_AS; cf: detachable EIFFEL_LIST [FEATURE_NAME]; c_as: detachable SYMBOL_AS; ck_as, ek_as: detachable KEYWORD_AS): detachable FORMAL_CONSTRAINT_AS
			-- New FORMAL_DECLARATION AST node
		do
			if f /= Void then
				create Result.initialize (f, c, cf, c_as, ck_as, ek_as)
			end
		end

	new_integer_as (t: detachable TYPE_AS; s: BOOLEAN; v: detachable STRING; buf: detachable READABLE_STRING_8; s_as: detachable SYMBOL_AS; l, c, p, n, cc, cp, cn: INTEGER): detachable INTEGER_CONSTANT
			-- New INTEGER_AS node
		do
			if v /= Void then
				create Result.make_from_string (t, s, v)
				Result.set_position (l, c, p, n, cc, cp, cn)
			end
		end

	new_integer_hexa_as (t: detachable TYPE_AS; s: CHARACTER; v: detachable STRING; buf: READABLE_STRING_8; s_as: detachable SYMBOL_AS; l, c, p, n, cc, cp, cn: INTEGER): detachable INTEGER_CONSTANT
			-- New INTEGER_AS node
		do
			if v /= Void then
				create Result.make_from_hexa_string (t, s, v)
				Result.set_position (l, c, p, n, cc, cp, cn)
			end
		end

	new_integer_octal_as (t: detachable TYPE_AS; s: CHARACTER; v: detachable STRING; buf: READABLE_STRING_8; s_as: detachable SYMBOL_AS; l, c, p, n, cc, cp, cn: INTEGER): detachable INTEGER_CONSTANT
			-- New INTEGER_AS node
		do
			if v /= Void then
				create Result.make_from_octal_string (t, s, v)
				Result.set_position (l, c, p, n, cc, cp, cn)
			end
		end

	new_integer_binary_as (t: detachable TYPE_AS; s: CHARACTER; v: detachable STRING; buf: READABLE_STRING_8; s_as: detachable SYMBOL_AS; l, c, p, n, cc, cp, cn: INTEGER): detachable INTEGER_CONSTANT
			-- New INTEGER_AS node
		do
			if v /= Void then
				create Result.make_from_binary_string (t, s, v)
				Result.set_position (l, c, p, n, cc, cp, cn)
			end
		end

feature -- Access for Errors

	new_vtgc1_error (a_line: INTEGER; a_column: INTEGER; a_filename: like {ERROR}.file_name; a_type: TYPE_AS): VTGC1
			-- <Precursor>
		do
			create Result
			if attached {CLASS_C} parser.current_class as l_class_c then
				Result.set_class (l_class_c)
			end
			Result.set_location (a_type.start_location)
		end

	new_vvok1_error (a_line: INTEGER; a_column: INTEGER; a_filename: like {ERROR}.file_name; a_once_as: FEATURE_AS): VVOK1
			-- Create new VVOK1 error.
		do
			create Result.make (a_line, a_column, a_filename, Void)
		end

	new_vvok2_error (a_line: INTEGER; a_column: INTEGER; a_filename: like {ERROR}.file_name; a_once_as: FEATURE_AS): VVOK2
			-- Create new VVOK2 error.
		do
			create Result.make (a_line, a_column, a_filename, Void)
		end

feature {NONE} -- Validation

	validate_integer_real_type (a_psr: EIFFEL_SCANNER_SKELETON; a_type: detachable TYPE_AS; buffer: READABLE_STRING_8; for_integer: BOOLEAN)
			-- New integer value.
		local
			l_type: TYPE_A
			l_class_c: CLASS_C
		do
			is_valid_integer_real := True
			l_class_c ?= a_psr.current_class
			if for_integer then
				if a_type /= Void and l_class_c /= Void then
					l_type := type_a_generator.evaluate_type (a_type, l_class_c)
				end
				if l_type /= Void then
					if not l_type.is_valid or (not l_type.is_integer and not l_type.is_natural) then
						is_valid_integer_real := False
						a_psr.report_invalid_type_for_integer_error (a_type, buffer)
					end
				elseif a_type /= Void then
						-- A type was specified but did not result in a valid type
					is_valid_integer_real := False
					a_psr.report_invalid_type_for_integer_error (a_type, buffer)
				end
			else
				if a_type /= Void and l_class_c /= Void then
					l_type := type_a_generator.evaluate_type (a_type, l_class_c)
				end
				if l_type /= Void then
					if not l_type.is_valid or (not l_type.is_real_32 and not l_type.is_real_64) then
						is_valid_integer_real := False
						a_psr.report_invalid_type_for_real_error (a_type, buffer)
					end
				elseif a_type /= Void then
						-- A type was specified but did not result in a valid type
					is_valid_integer_real := False
					a_psr.report_invalid_type_for_real_error (a_type, buffer)
				end
			end
		end

	validate_non_conforming_inheritance_type (a_psr: EIFFEL_PARSER_SKELETON; a_type: detachable TYPE_AS)
			-- Validate `a_type' for non-conforming inheritance.
		local
			l_none_type_as: NONE_TYPE_AS
			l_syntax_error: SYNTAX_ERROR
		do
				-- Make sure that `a_type' is of type NONE_TYPE_AS.
			l_none_type_as ?= a_type
			if l_none_type_as = Void then
					-- Raise error.
				create l_syntax_error.make (a_psr.line, a_psr.column, a_psr.filename, "Use 'inherit {NONE}' to specify non-conforming inheritance")
				a_psr.report_one_error (l_syntax_error)
			end
		end

note
	ca_ignore:
		"CA011", "CA011: too many arguments",
		"CA033", "CA033: very long class"
	copyright:	"Copyright (c) 1984-2021, 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