note description: "Loads a semantic document into a SEM_QUERYABLE" date: "$Date$" revision: "$Revision$" class SEM_DOCUMENT_LOADER inherit SEM_FIELD_NAMES SEM_UTILITY SHARED_WORKBENCH feature -- Loading load_from_file (a_filename: STRING) -- Load from `a_filename' local l_file: PLAIN_TEXT_FILE do create l_file.make_open_read (a_filename) l_file.read_stream (l_file.count) load (l_file.last_string) l_file.close end load (a_document: like document) -- Load `a_document' into a queryable do document := a_document create fields.make(50) from position := 1 until position >= document.count loop parse_field end -- Determine document-type if fields[document_type_field].is_equal ("transition") then -- Transition if fields.has(feature_field) then -- Feature-call transition load_feature_call_transition else -- Snippet load_snippet_transition end elseif fields[document_type_field].is_equal ("objects") then load_object end end feature -- Access last_queryable: SEM_QUERYABLE feature {NONE} -- Helpers variable_prefix: STRING = "v_" serialization_data: ARRAY[NATURAL_8] -- Reads the comma-separated numbers in the serialization-field into an ARRAY[NATURAL] local l_string_serialized: STRING l_slices: LIST[STRING] l_index: INTEGER do l_string_serialized := fields[serialization_field] if l_string_serialized /= void then l_slices := l_string_serialized.split (serialization_field_array_separator) create Result.make (1, l_slices.count) from l_slices.start l_index := 1 until l_slices.after loop Result.force (l_slices.item.to_natural_8, l_index) l_slices.forth l_index := l_index + 1 end end end variable_locations (a_var_index_list: LIST[STRING]): LIST[TUPLE[type:STRING;position:INTEGER]] -- Extracts types and positions from a list of variables ({type}@position) local l_cur_var: LIST[STRING] l_type_name: STRING do create {LINKED_LIST[TUPLE[name:STRING;position:INTEGER]]}Result.make from a_var_index_list.start until a_var_index_list.after loop l_cur_var := a_var_index_list.item.split ('@') l_type_name := l_cur_var.i_th (1) l_type_name := l_type_name.substring (2, l_type_name.count-1) Result.extend ([l_type_name, l_cur_var.i_th (2).to_integer]) a_var_index_list.forth end end concrete_variable_locations (a_var_loc_list: like variable_locations): like variable_locations -- Filters out abstract types from `a_var_loc'. Uses that the concrete type always comes last. local l_last: TUPLE[name:STRING;position:INTEGER] do create {LINKED_LIST[TUPLE[name:STRING;position:INTEGER]]}Result.make if not a_var_loc_list.is_empty then from l_last := a_var_loc_list.first a_var_loc_list.start until a_var_loc_list.after loop if a_var_loc_list.item.position /= l_last.position then Result.extend ([l_last.name, l_last.position]) end l_last := a_var_loc_list.item a_var_loc_list.forth end Result.extend ([l_last.name, l_last.position]) end end context_from_var_locations (a_var_loc_list: like variable_locations): EPA_CONTEXT -- Returns a context with the variables in `a_var_loc_list' local l_last: TUPLE[type:STRING;position:INTEGER] l_var_hash: HASH_TABLE[TYPE_A,STRING] l_cur_type: TYPE_A l_any: CLASS_C do l_any := first_class_starts_with_name("ANY") from create l_var_hash.make (a_var_loc_list.count) a_var_loc_list.start until a_var_loc_list.after loop l_cur_type := type_a_from_string (a_var_loc_list.item.type, l_any) l_var_hash.extend (l_cur_type, variable_prefix+a_var_loc_list.item.position.out) a_var_loc_list.forth end create Result.make (l_var_hash) end linear_operand_list (a_count: INTEGER): HASH_TABLE[STRING,INTEGER] -- Return a simple operand-list where vn is at index n with n=0..a_count-1 local l_index: INTEGER do create Result.make (a_count) from l_index := 0 until l_index >= a_count loop Result.extend(variable_prefix+l_index.out, l_index) l_index := l_index + 1 end end variable_form_from_anonymous (a_string: STRING): STRING -- Converts any {n} to vn local l_pos: INTEGER l_change: BOOLEAN do from create Result.make (a_string.count) l_pos := 1 until l_pos > a_string.count loop if a_string.item (l_pos) = '{' then l_change := true elseif a_string.item (l_pos) /= '}' then if l_change then Result.append (variable_prefix) l_change := false end Result.extend (a_string.item (l_pos)) end l_pos := l_pos + 1 end end is_anonymous_form (a_string: STRING): BOOLEAN -- Is `a_string' in anonymous form? ({0}.count etc) local l_anon_start: INTEGER do -- Find '{' -- Check if next character is an integer l_anon_start := a_string.index_of ('{', 1) Result := l_anon_start = 0 or else a_string.item (l_anon_start+1).is_digit end linear_variable_positions (a_count: INTEGER): HASH_TABLE [INTEGER, STRING] -- Linear list of variabels positions (0,v0) (1,v1) etc local l_index: INTEGER do create Result.make (a_count) from l_index := 0 until l_index >= a_count loop Result.extend(l_index, variable_prefix+l_index.out) l_index := l_index + 1 end end linear_variable_set (a_count: INTEGER): DS_HASH_SET [STRING] -- Linear list of variables, i.e. vn with n=[0..a_count-1] local l_index: INTEGER do create Result.make (a_count) from l_index := 0 until l_index >= a_count loop Result.put (variable_prefix+l_index.out) l_index := l_index + 1 end end transition_context: EPA_CONTEXT -- Returns the context of the transition local l_variables: LIST[STRING] l_var_locations: like variable_locations l_concrete_var_locations: like variable_locations do -- Split variable field into single variables+index l_variables := string_slices (fields[variables_field], field_value_separator) -- Extract typename and positions l_var_locations := variable_locations (l_variables) -- Filter out abstracted types l_concrete_var_locations := concrete_variable_locations (l_var_locations) -- Create a context with the variables Result := context_from_var_locations (l_concrete_var_locations) end transition_inputs: DS_HASH_SET [STRING] -- Inputs of transition local l_variables: LIST[STRING] l_var_locations: like variable_locations l_concrete_var_locations: like variable_locations do -- Split variable field into single variables+index l_variables := string_slices (fields[inputs_field], field_value_separator) -- Extract typename and positions l_var_locations := variable_locations (l_variables) from l_var_locations.start create Result.make(l_var_locations.count) Result.set_equality_tester (create {KL_STRING_EQUALITY_TESTER_A[STRING]}) until l_var_locations.after loop Result.put (variable_prefix+l_var_locations.item.position.out) l_var_locations.forth end end transition_outputs: DS_HASH_SET [STRING] -- Outputs of transition local l_variables: LIST[STRING] l_var_locations: like variable_locations l_concrete_var_locations: like variable_locations do -- Split variable field into single variables+index l_variables := string_slices (fields[outputs_field], field_value_separator) -- Extract typename and positions l_var_locations := variable_locations (l_variables) from l_var_locations.start create Result.make(l_var_locations.count) Result.set_equality_tester (create {KL_STRING_EQUALITY_TESTER_A[STRING]}) until l_var_locations.after loop Result.put (variable_prefix+l_var_locations.item.position.out) l_var_locations.forth end end set_properties (a_objects: SEM_OBJECTS) local l_field: STRING l_queries: HASH_TABLE[STRING,STRING] l_properties: EPA_STATE l_current_anon_property, l_current_var_property, l_current_property_value: STRING do from fields.start create l_queries.make (20) until fields.after loop l_field := fields.key_for_iteration if l_field.starts_with (to_field_prefix) then l_current_anon_property := l_field.substring (to_field_prefix.count+1, l_field.count) if is_anonymous_form (l_current_anon_property) then l_current_var_property := variable_form_from_anonymous (l_current_anon_property) l_current_property_value := fields.item_for_iteration l_queries.extend (l_current_property_value, l_current_var_property) end end fields.forth end -- Create and set pre- & poststates create l_properties.make_from_object_state (l_queries, system.root_type.associated_class, void) a_objects.set_properties (l_properties) end set_pre_post_states (a_transition: SEM_TRANSITION; a_context_class: CLASS_C; a_context_feature: detachable FEATURE_I) local l_field: STRING l_prestate, l_poststate: EPA_STATE l_prestate_queries, l_poststate_queries: HASH_TABLE[STRING,STRING] l_current_anon_property, l_current_var_property, l_current_property_value: STRING do from fields.start create l_prestate_queries.make (20) create l_poststate_queries.make (20) until fields.after loop l_field := fields.key_for_iteration if l_field.starts_with (precondition_field_prefix) then l_current_anon_property := l_field.substring (precondition_field_prefix.count+1, l_field.count) if is_anonymous_form (l_current_anon_property) then l_current_var_property := variable_form_from_anonymous (l_current_anon_property) l_current_property_value := fields.item_for_iteration l_prestate_queries.extend (l_current_property_value, l_current_var_property) end elseif l_field.starts_with (postcondition_field_prefix) then l_current_anon_property := l_field.substring (postcondition_field_prefix.count+1, l_field.count) if is_anonymous_form (l_current_anon_property) then l_current_var_property := variable_form_from_anonymous (l_current_anon_property) l_current_property_value := fields.item_for_iteration l_poststate_queries.extend (l_current_property_value, l_current_var_property) end end fields.forth end -- Create and set pre- & poststates create l_prestate.make_from_object_state (l_prestate_queries, a_context_class, a_context_feature) create l_poststate.make_from_object_state (l_poststate_queries, a_context_class, a_context_feature) a_transition.set_precondition (l_prestate) a_transition.set_postcondition (l_poststate) end feature {NONE} -- Load load_object -- Load SEM_OBJECTS local l_context: EPA_CONTEXT l_objects: SEM_OBJECTS l_ser_data: like serialization_data l_properties: EPA_STATE do l_context := transition_context l_ser_data := serialization_data create l_objects.make_with_transition_data (l_context, l_ser_data) set_properties (l_objects) last_queryable := l_objects end load_snippet_transition -- Loads a snippet transition local l_context: EPA_CONTEXT l_transition: SEM_SNIPPET l_variables: LIST[STRING] l_var_locations: like variable_locations l_concrete_var_locations: like variable_locations l_operands: HASH_TABLE[STRING,INTEGER] l_var_replacement_map: HASH_TABLE[STRING,STRING] l_var_positions: like linear_variable_positions l_princ_var_index: INTEGER l_princ_var_class: CLASS_C l_content: STRING do l_context := transition_context l_var_positions := linear_variable_positions (l_context.variables.count) l_content := fields[content_field] create l_transition.make (l_context, l_var_positions, transition_inputs, transition_outputs, l_content) -- Gather pre- & poststate-queries l_princ_var_index := principal_variable_from_anon_content (l_content) l_princ_var_class := l_context.variables[variable_prefix+l_princ_var_index.out].associated_class set_pre_post_states (l_transition, l_princ_var_class, void) last_queryable := l_transition end load_feature_call_transition -- Loads a feature-call transition local l_class: CLASS_C l_feature: FEATURE_I l_context: EPA_CONTEXT l_transition: SEM_FEATURE_CALL_TRANSITION l_operands: HASH_TABLE[STRING,INTEGER] l_var_replacement_map: HASH_TABLE[STRING,STRING] do l_context := transition_context -- Create simple operand-list l_operands := linear_operand_list (l_context.variables.count) -- Create the transition l_class := first_class_starts_with_name (fields[class_field]) l_feature := l_class.feature_named (fields[feature_field]) create l_transition.make (l_class, l_feature, l_operands, l_context, false) set_pre_post_states (l_transition, l_class, l_feature) last_queryable := l_transition end feature {NONE} -- Parsing document: STRING position: INTEGER fields: HASH_TABLE[STRING, STRING] -- Field name -> content parse_field local field_name: STRING field_content_string: STRING field_content_list: LIST[STRING] name_start, name_end: like position content_start, content_end: like position do name_start := position -- Get field name name_end := document.index_of ('%N', name_start) field_name := document.substring (name_start, name_end-1) -- Skip boost & type content_start := document.index_of ('%N', name_end+1) content_start := document.index_of ('%N', content_start+1) -- Read content content_end := document.index_of ('%N', content_start+1) field_content_string := document.substring (content_start+1, content_end-1) -- Add field fields.extend (field_content_string, field_name) -- Skip to next field from position := content_end+1 until document.item (position) /= '%N' or position>=document.count loop position := position + 1 end end end