class EVOLICITY_PARSE_ACTIONS

(source code)

description

Evolicity parse actions

note
	description: "Evolicity parse actions"

	author: "Finnian Reilly"
	copyright: "Copyright (c) 2001-2022 Finnian Reilly"
	contact: "finnian at eiffel hyphen loop dot com"

	license: "MIT license (See: en.wikipedia.org/wiki/MIT_License)"
	date: "2023-03-10 10:10:56 GMT (Friday 10th March 2023)"
	revision: "10"

deferred class
	EVOLICITY_PARSE_ACTIONS

inherit
	EVOLICITY_SHARED_TOKEN_ENUM

feature {NONE} -- Initialization

	make
		do
			create compound_directive.make
			create compound_directive_stack.make
			create loop_directive_stack.make
			create if_else_directive_stack.make
			create numeric_comparison_stack.make
			create boolean_expression_stack.make
			create number_stack.make
			create last_evaluate_directive.make
			create last_include_directive.make
		end

feature {NONE} -- Actions

	on_boolean_conjunction_expression (start_index, end_index: INTEGER)
			--
		require
			boolean_expression_stack_has_two_expressions: boolean_expression_stack.count >= 2
		local
			boolean_conjunction_expression: EVOLICITY_BOOLEAN_CONJUNCTION_EXPRESSION
		do
			if tokens_text.code (start_index) = Token.keyword_and then
				create {EVOLICITY_BOOLEAN_AND_EXPRESSION} boolean_conjunction_expression.make (boolean_expression_stack.item)
			else
				create {EVOLICITY_BOOLEAN_OR_EXPRESSION} boolean_conjunction_expression.make (boolean_expression_stack.item)
			end
			boolean_expression_stack.remove
			boolean_conjunction_expression.set_left_hand_expression (boolean_expression_stack.item)
			boolean_expression_stack.remove
			boolean_expression_stack.put (boolean_conjunction_expression)
		end

	on_boolean_not_expression (start_index, end_index: INTEGER)
			--
		require
			boolean_expression_stack_not_empty: not boolean_expression_stack.is_empty
		local
			boolean_not: EVOLICITY_BOOLEAN_NOT_EXPRESSION
		do
			create boolean_not.make (boolean_expression_stack.item)
			boolean_expression_stack.remove
			boolean_expression_stack.put (boolean_not)
		end

	on_boolean_variable (start_index, end_index: INTEGER)
			--
		local
			boolean_variable: EVOLICITY_BOOLEAN_REFERENCE_EXPRESSION
		do
			create boolean_variable.make (tokens_to_variable_ref (start_index, end_index))
			boolean_expression_stack.put (boolean_variable)
		end

	on_comparable (type: NATURAL; start_index, end_index: INTEGER)
			--
		require
			valid_comparison_text: valid_comparison_text (type, start_index, end_index)
		local
			comparable: EVOLICITY_COMPARABLE; str: ZSTRING
		do
				if type = Token.integer_64_constant then
				str := source_text_for_token (start_index)
	  			create {EVOLICITY_INTEGER_64_COMPARABLE} comparable.make_from_string (str)

				elseif type = Token.double_constant then
				str := source_text_for_token (start_index)
				create {EVOLICITY_DOUBLE_COMPARABLE} comparable.make_from_string (str)
			else
				create {EVOLICITY_COMPARABLE_VARIABLE} comparable.make (tokens_to_variable_ref (start_index, end_index))
				end
				number_stack.put (comparable)
		end

	on_comparison_expression (start_index, end_index: INTEGER)
			--
		do
				numeric_comparison_stack.item.set_right_hand_expression (number_stack.item)
				number_stack.remove
				numeric_comparison_stack.item.set_left_hand_expression (number_stack.item)
				number_stack.remove
				boolean_expression_stack.put (numeric_comparison_stack.item)
				numeric_comparison_stack.remove
		end

	on_dollor_sign_escape (start_index, end_index: INTEGER)
			--
		do
			if not compound_directive.is_empty
				and then attached {EVOLICITY_FREE_TEXT_DIRECTIVE} compound_directive.last as free_text_directive
			then
				free_text_directive.text.append_character ('$')
			else
				compound_directive.extend (create {EVOLICITY_FREE_TEXT_DIRECTIVE}.make ("$"))
			end
		end

	on_evaluate (id: NATURAL; start_index, end_index: INTEGER)
			--
		do
			if id = Token.Keyword_evaluate then
				create last_evaluate_directive.make
				compound_directive.extend (last_evaluate_directive)

			elseif id = Token.White_text then
				--	leading space
				set_nested_directive_indent (last_evaluate_directive, start_index, end_index)

			elseif id = Token.Template_name_identifier then
				last_evaluate_directive.set_template_name (source_text_for_token (start_index))

			elseif id = Token.Quoted_string then
				if attached source_text_for_token (start_index) as str then
					str.remove_quotes
					last_evaluate_directive.set_template_name (str)
				end
			elseif id = Token.operator_dot then
				last_evaluate_directive.set_template_name_variable_ref (tokens_to_variable_ref (start_index, end_index))
			else
				last_evaluate_directive.set_variable_ref (tokens_to_variable_ref (start_index, end_index))
			end
		end

	on_free_text (start_index, end_index: INTEGER)
			--
		local
			free_text_string: ZSTRING
		do
			free_text_string := source_text_for_token (start_index)
			compound_directive.extend (create {EVOLICITY_FREE_TEXT_DIRECTIVE}.make (free_text_string))
		end

	on_if (id: NATURAL; start_index, end_index: INTEGER)
			--
		do
			if id = Token.Keyword_if then
				compound_directive_stack.put (compound_directive)
				if_else_directive_stack.put (create {EVOLICITY_IF_ELSE_DIRECTIVE}.make)
				compound_directive := if_else_directive_stack.item

			elseif id = Token.Keyword_then then
	  			if_else_directive_stack.item.set_boolean_expression (boolean_expression_stack.item)
	  			boolean_expression_stack.remove

	  		elseif id = Token.Keyword_else then
				if_else_directive_stack.item.set_if_true_interval

			elseif id = Token.Keyword_end then
				compound_directive := compound_directive_stack.item
				compound_directive_stack.remove

				if_else_directive_stack.item.set_if_false_interval
				compound_directive.extend (if_else_directive_stack.item)
				if_else_directive_stack.remove
			end
		end

	on_include (id: NATURAL; start_index, end_index: INTEGER)
			--
		do
			if id = Token.Keyword_include then
				create last_include_directive.make
				compound_directive.extend (last_include_directive)

			elseif id = Token.White_text then
				-- leading space
				set_nested_directive_indent (last_include_directive, start_index, end_index)

			elseif id = Token.Quoted_string then
				if attached source_text_for_token (start_index) as str then
					str.remove_quotes
					last_include_directive.set_template_name (str)
				end

			elseif id = Token.operator_dot then
				last_include_directive.set_variable_ref (tokens_to_variable_ref (start_index, end_index))
			end
		end

	on_loop (id: NATURAL; start_index, end_index: INTEGER)
			--
		do
			if Token.loop_keywords.has (id) then
				compound_directive_stack.put (compound_directive)
				loop_directive_stack.put (new_loop_directive (id))
				compound_directive := loop_directive_stack.item

			elseif id = Token.keyword_in then
				loop_directive_stack.item.set_iterable_variable_ref (tokens_to_variable_ref (start_index, end_index))

			elseif id = Token.keyword_as then
				loop_directive_stack.item.put_item_name (tokens_to_variable_ref (start_index, end_index) @ 1)

			elseif id  = Token.keyword_end then
				compound_directive := compound_directive_stack.item
				compound_directive_stack.remove

				compound_directive.extend (loop_directive_stack.item)
				loop_directive_stack.remove
			end
		end

	on_numeric_comparison (symbol: CHARACTER; start_index, end_index: INTEGER)
			--
		local
			comparison: EVOLICITY_COMPARISON
		do
			inspect symbol
				when '<' then
		  			create {EVOLICITY_LESS_THAN_COMPARISON} comparison
		  		when '>' then
		  			create {EVOLICITY_GREATER_THAN_COMPARISON} comparison
		  		when '=' then
					create {EVOLICITY_EQUAL_TO_COMPARISON} comparison
			else
				create {EVOLICITY_EQUAL_TO_COMPARISON} comparison
			end
			numeric_comparison_stack.put (comparison)
		end

	on_simple_comparison_expression (start_index, end_index: INTEGER)
			--
		do
		end

	on_variable_reference (start_index, end_index: INTEGER)
			--
		local
			variable_subst_directive: EVOLICITY_VARIABLE_SUBST_DIRECTIVE
		do
--			log.enter ("on_variable_reference")
			create variable_subst_directive.make (tokens_to_variable_ref (start_index, end_index))
			compound_directive.extend (variable_subst_directive)

--			variable_ref := tokens_to_variable_ref (tokens_matched)
--			if log.current_routine_is_active then
--				log.put_string ("$")
--				from i := 1 until i > variable_ref.count loop
--					if i > 1 then
--						log.put_string (".")
--					end
--  					log.put_string (variable_ref @ i)
--					i := i + 1
--				end
--			end
--			log.put_new_line
--			log.exit
		end

feature {NONE} -- Parse actions

	comparable_action (id: NATURAL): PROCEDURE [INTEGER, INTEGER]
		do
			Result := agent on_comparable (id, ?, ?)
		end

	evaluate_action (id: NATURAL): PROCEDURE [INTEGER, INTEGER]
		do
			Result := agent on_evaluate (id, ?, ?)
		end

	if_action (id: NATURAL): PROCEDURE [INTEGER, INTEGER]
		do
			Result := agent on_if (id, ?, ?)
		end

	include_action (id: NATURAL): PROCEDURE [INTEGER, INTEGER]
		do
			Result := agent on_include (id, ?, ?)
		end

	loop_action (id: NATURAL): PROCEDURE [INTEGER, INTEGER]
		do
			Result := agent on_loop (id, ?, ?)
		end

	numeric_comparison_action (symbol: CHARACTER): PROCEDURE [INTEGER, INTEGER]
		do
			Result := agent on_numeric_comparison (symbol, ?, ?)
		end

feature {NONE} -- Implementation

	function_arguments (position: INTEGER; end_index: INTEGER): TUPLE
		require
			start_position_is_left_bracket: tokens_text.code (position) = Token.Left_bracket
		local
			i: INTEGER; i_th_token: NATURAL_32; string_arg: ZSTRING
			buffer: like Argument_buffer
		do
			buffer := Argument_buffer
			buffer.wipe_out
			from i := position + 1 until i > end_index loop
				i_th_token := tokens_text.code (i)
				if i_th_token = Token.Quoted_string then
					string_arg := source_text_for_token (i)
					string_arg.remove_quotes
					buffer.extend (string_arg)

				elseif i_th_token = Token.double_constant then
					buffer.extend (source_text_for_token (i).to_double)

				elseif i_th_token = Token.integer_64_constant then
					buffer.extend (source_text_for_token (i).to_integer_64)

				end
				i := i + 1
			end
			Result := buffer.to_tuple
		end

	new_loop_directive (id: NATURAL): EVOLICITY_FOREACH_DIRECTIVE
		do
			if id = Token.Keyword_across then
				create {EVOLICITY_ACROSS_DIRECTIVE} Result.make
			else
				create Result.make
			end
		end

	set_nested_directive_indent (nested_directive: EVOLICITY_NESTED_TEMPLATE_DIRECTIVE; start_index, end_index: INTEGER)
			--
		local
			leading_space: ZSTRING; space_count: INTEGER
		do
			leading_space := source_text_for_token (start_index)
			if leading_space.occurrences ('%T') = leading_space.count then
				nested_directive.set_tab_indent (leading_space.count)
			else
				space_count := leading_space.occurrences (' ') + leading_space.occurrences ('%T') * Spaces_per_tab
				nested_directive.set_tab_indent (space_count // Spaces_per_tab)
			end
		end

	tokens_to_variable_ref (start_index, end_index: INTEGER): EVOLICITY_VARIABLE_REFERENCE
			--
		local
			i: INTEGER
		do
			create Result.make (token_occurrences (Token.Unqualified_name, start_index, end_index))
			from i := start_index until i > end_index or else tokens_text.code (i) = Token.Left_bracket loop
				if tokens_text.code (i) = Token.Unqualified_name then
					Result.extend (source_text_for_token (i))
				end
				i := i + 1
			end
			if i < end_index and then tokens_text.code (i) = Token.Left_bracket
				and then attached function_arguments (i, end_index) as args
			then
				create {EVOLICITY_FUNCTION_REFERENCE} Result.make (Result.to_array, args)
			end
		ensure
			reference_contain_all_steps: Result.full
		end

	valid_comparison_text (type: NATURAL; start_index, end_index: INTEGER): BOOLEAN
		do
				if type = Token.integer_64_constant then
	  			Result := source_text_for_token (start_index).is_integer_64

				elseif type = Token.double_constant then
	  			Result := source_text_for_token (start_index).is_double
	  		else
	  			Result := True
				end
		end

feature {NONE} -- Deferred

	tokens_text: STRING_32
		deferred
		end

	source_text_for_token (i: INTEGER): ZSTRING
		deferred
		end

	token_occurrences (a_token: NATURAL; start_index, end_index: INTEGER): INTEGER
		deferred
		end

feature {NONE} -- Internal attributes

	boolean_expression_stack: LINKED_STACK [EVOLICITY_BOOLEAN_EXPRESSION]

	compound_directive: EVOLICITY_COMPOUND_DIRECTIVE

	compound_directive_stack: LINKED_STACK [EVOLICITY_COMPOUND_DIRECTIVE]

	if_else_directive_stack: LINKED_STACK [EVOLICITY_IF_ELSE_DIRECTIVE]

	last_evaluate_directive: EVOLICITY_EVALUATE_DIRECTIVE

	last_include_directive: EVOLICITY_INCLUDE_DIRECTIVE

	loop_directive_stack: LINKED_STACK [EVOLICITY_FOREACH_DIRECTIVE]

	number_stack: LINKED_STACK [EVOLICITY_COMPARABLE]

	numeric_comparison_stack: LINKED_STACK [EVOLICITY_COMPARISON]

feature {NONE} -- Constants

	Argument_buffer: EL_ARRAYED_LIST [ANY]
		once
			create Result.make (3)
		end

	Spaces_per_tab: INTEGER = 4

end