class EVOLICITY_FOREACH_DIRECTIVE
Implemention of iteration of a container conforming to ITERABLE [G]
The loop syntax is as follows:
#foreach $<item-variable-name> in $<iterable-container> loop #end
But if the container additionally conforms to TABLE_ITERABLE [G], an extra loop variable can be optionally used to reference the table key as follows:
#foreach $<item-variable-name>, $<key-variable-name> in $<iterable-container> loop #end
The iteration variable name references the current iteration item.
note
description: "Implemention of iteration of a container conforming to ${ITERABLE [G]}"
notes: "[
The loop syntax is as follows:
#foreach $<item-variable-name> in $<iterable-container> loop
#end
But if the container additionally conforms to ${TABLE_ITERABLE [G]}, an extra loop
variable can be optionally used to reference the table key as follows:
#foreach $<item-variable-name>, $<key-variable-name> in $<iterable-container> loop
#end
The iteration variable name references the current iteration item.
]"
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: "2024-01-20 19:18:26 GMT (Saturday 20th January 2024)"
revision: "13"
class
EVOLICITY_FOREACH_DIRECTIVE
inherit
EVOLICITY_COMPOUND_DIRECTIVE
redefine
execute, make
end
EL_MODULE_TUPLE
create
make
feature -- Initialization
make
--
do
Precursor
create outer_loop_variables.make_equal (3)
create local_scope_variable_names.make_from_array (<< var_loop_index >>)
end
feature -- Element change
put_item_name (a_item_name: STRING)
--
do
local_scope_variable_names.extend (a_item_name)
ensure
maximum_3: (1 |..| 3).has (local_scope_variable_names.count)
end
set_iterable_variable_ref (a_iterable_variable_ref: EVOLICITY_VARIABLE_REFERENCE)
--
do
iterable_variable_ref := a_iterable_variable_ref
end
has_key_item: BOOLEAN
do
Result := local_scope_variable_names.count = 3
end
feature {NONE} -- Implementation
execute (a_context: EVOLICITY_CONTEXT; output: EL_OUTPUT_MEDIUM)
require else
valid_iterable: attached iterable_container (a_context) as iterable and then a_context.is_valid_iterable (iterable)
local
loop_index: INTEGER_REF; name_space: like outer_loop_variables; cursor_index: INTEGER
is_valid_type, is_valid_key_type: BOOLEAN; table_cursor: detachable HASH_TABLE_ITERATION_CURSOR [ANY, HASHABLE]
do
name_space := a_context.object_table
if attached iterable_container (a_context) as iterable then
save_outer_loop_variables (name_space)
create loop_index
put_loop_index (a_context, loop_index)
across iterable as list loop
cursor_index := cursor_index + 1
if cursor_index = 1 then
is_valid_type := a_context.is_valid_type (list.item)
if attached {HASH_TABLE_ITERATION_CURSOR [ANY, HASHABLE]} list as l_cursor then
table_cursor := l_cursor
is_valid_key_type := a_context.is_valid_type (l_cursor.key)
end
end
loop_index.set_item (cursor_index)
if attached list.item as cursor_item then
if is_valid_type then
put_iteration_object (a_context, cursor_item)
else
put_iteration_object (a_context, Invalid_item #$ [cursor_item.generator, cursor_index])
end
if attached table_cursor as table and then has_key_item then
if is_valid_key_type then
put_table_key (a_context, table.key)
else
put_table_key (a_context, Invalid_item #$ [table.key.generator, cursor_index] )
end
end
else
name_space.remove (item_name)
end
Precursor (a_context, output)
end
name_space.remove (item_name)
restore_outer_loop_variables (name_space)
end
end
item_name: STRING
do
Result := local_scope_variable_names [2]
end
iterable_container (a_context: EVOLICITY_CONTEXT): detachable ITERABLE [ANY]
do
if attached {ITERABLE [ANY]} a_context.referenced_item (iterable_variable_ref) as iterable then
Result := iterable
end
end
key_item_name: STRING
require
has_key_item: has_key_item
do
Result := local_scope_variable_names [3]
end
put_iteration_object (a_context: EVOLICITY_CONTEXT; cursor_item: ANY)
do
a_context.put_any (item_name, cursor_item)
end
put_table_key (a_context: EVOLICITY_CONTEXT; key_item: ANY)
do
a_context.put_any (key_item_name, key_item)
end
put_loop_index (a_context: EVOLICITY_CONTEXT; a_loop_index: INTEGER_REF)
do
a_context.put_any (var_loop_index, a_loop_index)
end
restore_outer_loop_variables (name_space: like outer_loop_variables)
-- Restore any previous objects that had the same name as objects used in this loop
do
if attached outer_loop_variables as list then
from list.start until list.after loop
name_space [list.key_for_iteration] := list.item_for_iteration
list.forth
end
list.wipe_out
end
end
save_outer_loop_variables (name_space: like outer_loop_variables)
-- Save value of context objects with same names as objects used in this loop
require
empty_saved_objects_context: outer_loop_variables.is_empty
local
i: INTEGER; name: STRING
do
if attached local_scope_variable_names as list then
from i := 1 until i > list.count loop
name := list [i]
if name_space.has_key (name) then
outer_loop_variables.extend (name_space.found_item, name)
end
i := i + 1
end
end
end
var_loop_index: IMMUTABLE_STRING_8
do
Result := Var.loop_index
end
feature {NONE} -- Internal attributes
local_scope_variable_names: EL_STRING_8_LIST
outer_loop_variables: EL_STRING_8_TABLE [ANY]
-- Variables in outer loop that may have names clashing with this loop
iterable_variable_ref: EVOLICITY_VARIABLE_REFERENCE
feature -- Constants
Invalid_item: ZSTRING
once
Result := "{%S} [%S]"
end
Var: TUPLE [cursor_index, item_, key, loop_index: IMMUTABLE_STRING_8]
once
create Result
Tuple.fill_immutable (Result, "cursor_index, item, key, loop_index")
end
end