| 1 | | % (c) 2009-2025 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, |
| 2 | | % Heinrich Heine Universitaet Duesseldorf |
| 3 | | % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) |
| 4 | | |
| 5 | | :- module(bmachine_construction,[reset_bmachine_construction/0, |
| 6 | | check_machine/4, |
| 7 | | type_in_machine_l/6, |
| 8 | | type_open_predicate_with_quantifier/6, |
| 9 | | get_constructed_machine_name/2, get_constructed_machine_name_and_filenumber/3, |
| 10 | | type_open_formula/8, |
| 11 | | create_scope/6, % create type-checking scope |
| 12 | | filter_linking_invariant/3, |
| 13 | | machine_promotes_operations/2, |
| 14 | | machine_hides_unpromoted_operation/4, |
| 15 | | external_procedure_used/1, |
| 16 | | abstract_variable_removed_in/3, |
| 17 | | dummy_machine_name/2]). |
| 18 | | |
| 19 | | :- use_module(module_information,[module_info/2]). |
| 20 | | :- module_info(group,typechecker). |
| 21 | | :- module_info(description,'This module contains the rules for loading, including, seeing, etc. B machines, scope of constants, variables, parameters, etc.'). |
| 22 | | |
| 23 | | :- use_module(library(lists)). |
| 24 | | :- use_module(library(avl)). |
| 25 | | |
| 26 | | :- use_module(self_check). |
| 27 | | :- use_module(tools). |
| 28 | | :- use_module(error_manager). |
| 29 | | :- use_module(debug). |
| 30 | | :- use_module(preferences). |
| 31 | | |
| 32 | | :- use_module(btypechecker). |
| 33 | | :- use_module(b_ast_cleanup). |
| 34 | | :- use_module(bsyntaxtree). |
| 35 | | :- use_module(bmachine_structure). |
| 36 | | :- use_module(pragmas). |
| 37 | | :- use_module(b_global_sets,[register_enumerated_sets/2]). |
| 38 | | |
| 39 | | :- use_module(bmachine_static_checks). |
| 40 | | :- use_module(tools_lists,[ord_member_nonvar_chk/2, remove_dups_keep_order/2]). |
| 41 | | |
| 42 | | :- use_module(translate,[print_machine/1]). |
| 43 | | |
| 44 | | |
| 45 | | :- set_prolog_flag(double_quotes, codes). |
| 46 | | |
| 47 | | %maximum_type_errors(100). |
| 48 | | |
| 49 | | :- dynamic debug_machine/0. |
| 50 | | :- dynamic abstract_variable_removed_in/3. |
| 51 | | |
| 52 | | :- use_module(pref_definitions,[b_get_important_preferences_from_raw_machine/2]). |
| 53 | | set_important_prefs_from_machine(Main,Machines) :- |
| 54 | | find_machine(Main,Machines,_MType,_Header,RawMachine), |
| 55 | ? | get_raw_model_type(Main,Machines,RawModelType),!, |
| 56 | | b_get_important_preferences_from_raw_machine(RawMachine,RawModelType), |
| 57 | | check_important_annotions_from_machine(RawMachine). |
| 58 | | |
| 59 | | check_important_annotions_from_machine(RawMachine) :- |
| 60 | ? | member(definitions(_Pos,Defs),RawMachine), |
| 61 | ? | member(expression_definition(DPOS,'PROB_REQUIRED_VERSION',[],RawValue),Defs),!, |
| 62 | | (RawValue = string(VPos,Version) |
| 63 | | -> add_message(bmachine_construction,'Checking PROB_REQUIRED_VERSION: ',Version,VPos), |
| 64 | | (check_version(Version,VPos) -> true ; true) |
| 65 | | ; add_warning(bmachine_construction,'PROB_REQUIRED_VERSION must provide a version number string.','',DPOS) |
| 66 | | ). |
| 67 | | check_important_annotions_from_machine(_). |
| 68 | | |
| 69 | | :- use_module(version,[compare_against_current_version/2,full_version_str/1]). |
| 70 | | check_version(VersAtom,DPOS) :- atom_codes(VersAtom,Codes), |
| 71 | | split_chars(Codes,".",VCNrs), |
| 72 | | maplist(codes_to_number(DPOS),VCNrs,VNrs), |
| 73 | | compare_against_current_version(VNrs,Result), |
| 74 | | (Result = current_older |
| 75 | | -> full_version_str(Cur), |
| 76 | | ajoin(['This model requires at newer version of ProB than ',Cur,'. Download at least version: '],Msg), |
| 77 | | add_warning(prob_too_old,Msg,VersAtom,DPOS) |
| 78 | | ; true). |
| 79 | | |
| 80 | | codes_to_number(DPOS,C,A) :- |
| 81 | | catch(number_codes(A,C), error(syntax_error(_N),_), |
| 82 | | (atom_codes(AA,C), |
| 83 | | add_warning(bmachine_construction,'Illegal part of version number (use only numbers separated by dots): ',AA,DPOS),fail)). |
| 84 | | |
| 85 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 86 | | |
| 87 | | :- use_module(preferences,[temporary_set_preference/3,reset_temporary_preference/2]). |
| 88 | | |
| 89 | | % typecheck complete machines (incl. includes) |
| 90 | | check_machine(MainName,MachinesMayContainGenerated,Result,FinalErrors) :- |
| 91 | | clear_warnings, |
| 92 | | debug_println(9,checking_machine(MainName)), |
| 93 | | temporary_set_preference(perform_stricter_static_checks,true,CHNG), |
| 94 | | strip_global_pragmas(MachinesMayContainGenerated,Machines,_WasGenerated), % TODO: is generated still used? |
| 95 | | set_important_prefs_from_machine(MainName,Machines), % set important prefs before type-checking,... |
| 96 | | check_main_machine_file_origin(MainName,Machines), |
| 97 | | % we need to find all SEES/USES declarations, because all references |
| 98 | | % refer to the same machine and that has to be included exactly once. |
| 99 | | find_uses(Machines,GlobalUses,NotIncluded,Errors,E1), |
| 100 | | % if a seen/used machine was not included, we include it by constructing |
| 101 | | % a dummy machine that extends the seen/used machine and the origional main machine |
| 102 | | extend_not_included_uses(NotIncluded,MainName,NewMainName,Machines,IMachines), |
| 103 | | % figure out the right order of machines, so that each machine is |
| 104 | | % type-checked before a dependend machine is loaded |
| 105 | | machine_order(IMachines,Order), |
| 106 | | assert_machine_order(Order), |
| 107 | | % expand (= type-check + includes) machines in the given order |
| 108 | | expand_machines(Order,IMachines,GlobalUses,[],Results1,E1,E2), |
| 109 | | % until now, the initialisation section consists of a list of init_stmt(MachineName,Desc,Substitution) |
| 110 | | % fold_initialisation merges those to one substitution, respecting the dependencies |
| 111 | | % between the machines |
| 112 | | maplist(fold_initialisation(Order),Results1,Results), |
| 113 | | % find main machine |
| 114 | | memberchk(machine(NewMainName,MainMachine),Results), %nl,print('MAIN:'),nl,print_machine(MainMachine), |
| 115 | | % if the main machine has parameters, we convert them to global sets resp. constants |
| 116 | | convert_parameters_to_global_sets(E2,[],MainMachine,Result1), |
| 117 | | % add some additional informations about the machine |
| 118 | | add_machine_infos(MainName,Machines,Results,Result1,Result), |
| 119 | | ( debug_machine -> print_machine(Result); true), |
| 120 | | %% comment in to pretty print all machines: |
| 121 | | %( member(machine(Name,Mchx),Results), format('~n*** Machine ~w~n',[Name]), print_machine(Mchx),nl,fail ; true), |
| 122 | | % finalize the list of errors, remove duplicates |
| 123 | | sort(Errors,FinalErrors), |
| 124 | | % output warnings |
| 125 | | show_warnings, |
| 126 | | % run static checks on the resulting machine |
| 127 | | static_check_main_machine(Result), |
| 128 | | reset_temporary_preference(perform_stricter_static_checks,CHNG). |
| 129 | | check_machine(Main,_,Result,FinalErrors) :- |
| 130 | | add_internal_error('Internal error: Checking the machine failed: ', |
| 131 | | check_machine(Main,_,Result,FinalErrors)), |
| 132 | | fail. |
| 133 | | |
| 134 | | % remove global pragrams at top-level and assert them |
| 135 | | strip_global_pragmas([],[],false). |
| 136 | | strip_global_pragmas([generated(POS,M)|Ms],MachinesOut,true) :- !, % @generated pragma used at top of file |
| 137 | | assertz(pragmas:global_pragma(generated,POS)), |
| 138 | | strip_global_pragmas([M|Ms],MachinesOut,_). |
| 139 | | strip_global_pragmas([unit_alias(_,Alias,Content,M)|Ms],MachinesOut,true) :- !, |
| 140 | | assertz(pragmas:global_pragma(unit_alias,[Alias|Content])), |
| 141 | | strip_global_pragmas([M|Ms],MachinesOut,_). |
| 142 | | strip_global_pragmas([M|Ms],[M|SMs],WasGenerated) :- |
| 143 | | strip_global_pragmas(Ms,SMs,WasGenerated). |
| 144 | | |
| 145 | | |
| 146 | | fold_initialisation(Order,machine(Name,Sections),machine(Name,NewSections)) :- |
| 147 | | select_section(initialisation,List,Subst,Sections,NewSections), |
| 148 | | maplist(extract_init_substitutions(List),Order,LSubstitutions), |
| 149 | | append(LSubstitutions,Substitutions), |
| 150 | | create_init_sequence(Substitutions,Subst). |
| 151 | | extract_init_substitutions(Unsorted,Name,Substitutions) :- |
| 152 | | convlist(unzip_init(Name),Unsorted,Substitutions). % keep inits for Name |
| 153 | | unzip_init(Name,init_stmt(Name,Subst),Subst). |
| 154 | | create_init_sequence([],Subst) :- !, create_texpr(skip,subst,[generated],Subst). |
| 155 | | create_init_sequence([I],I) :- !. |
| 156 | | create_init_sequence(L,Sequence) :- |
| 157 | | (L = [First|_], get_texpr_info(First,AllInfos), extract_pos_infos(AllInfos,Pos) -> true ; Pos=[]), |
| 158 | | % TODO: try and merge first and last position info |
| 159 | | (get_preference(allow_overriding_initialisation,true), |
| 160 | ? | override_initialisation_sequence(L,NL) |
| 161 | | -> NewList=NL ; NewList=L), |
| 162 | | create_texpr(sequence(NewList),subst,[initial_sequence|Pos],Sequence). |
| 163 | | |
| 164 | | :- use_module(b_read_write_info,[get_accessed_vars/4]). |
| 165 | | % override initialisation sequence, remove unnecessary earlier assignments (which may time-out, ...) |
| 166 | | % in future we may inline equalities into becomes_such that, e.g., x :: S ; x := 1 --> x :: X /\ {1} |
| 167 | | % currently x :: S ; x := 1 gets simplified to skip ; x := 1 |
| 168 | | override_initialisation_sequence(List,NewList) :- |
| 169 | | append(Prefix,[Last],List), |
| 170 | | Prefix = [_|_], % there are some earlier initialisation statements to simplify |
| 171 | | get_accessed_vars(Last,[],LIds,Read), |
| 172 | | ord_intersection(LIds,Read,RWIds), |
| 173 | | (RWIds = [] |
| 174 | | -> true |
| 175 | | ; add_warning(b_machine_construction,'Initialisation (override) statement reads written variables:',RWIds,Last) |
| 176 | | ), |
| 177 | | process_override(Last,Prefix, NewLast, SPrefix), |
| 178 | | append(SPrefix,[NewLast],NewList). |
| 179 | | |
| 180 | | |
| 181 | | process_override(b(parallel(List),subst,Info), Prefix, |
| 182 | | b(parallel(NewList),subst,Info), NewPrefix) :- !, |
| 183 | | l_process_override(List,Prefix,NewList,NewPrefix). |
| 184 | | process_override(Subst, Prefix, NewSubst, NewPrefix) :- |
| 185 | | create_after_pred(Subst,AfterPred), |
| 186 | | get_accessed_vars(Subst,[],Ids,Read), |
| 187 | | % write(after_pred(Ids, read(Read))),nl, translate:print_bexpr(AfterPred),nl, |
| 188 | | ord_intersection(Read,Ids,RWIds), |
| 189 | | (RWIds=[] |
| 190 | | -> maplist(try_simplify_init_stmt(Ids,AfterPred,Keep),Prefix,NewPrefix), |
| 191 | | !, |
| 192 | | (Keep==merged_override_stmt |
| 193 | | -> NewSubst = b(skip,subst,[was(Subst)]) |
| 194 | | ; NewSubst = Subst |
| 195 | | ) |
| 196 | | ; add_warning(b_machine_construction,'Initialisation statement reads written variables:',RWIds,Subst), |
| 197 | | fail % we actually have a before after predicate which may modify RWIds |
| 198 | | ). |
| 199 | | process_override(Subst, Prefix , Subst, Prefix) :- add_message(b_machine_construction,'Keeping: ',Subst,Subst). |
| 200 | | |
| 201 | | l_process_override([],Prefix,[],Prefix). |
| 202 | | l_process_override([H|List],Prefix,[H1|NewList],NewPrefix) :- |
| 203 | | process_override(H,Prefix,H1,Prefix1), |
| 204 | | l_process_override(List,Prefix1,NewList,NewPrefix). |
| 205 | | |
| 206 | | :- use_module(library(ordsets)). |
| 207 | | simplify_init_stmt(b(parallel(List),subst,I),OverrideIds,AfterPred,Keep,b(parallel(SList),subst,I)) :- !, |
| 208 | | maplist(try_simplify_init_stmt(OverrideIds,AfterPred,Keep),List,SList). |
| 209 | | simplify_init_stmt(Assign,OverrideIds,AfterPred,Keep,NewSubst) :- |
| 210 | | get_accessed_vars(Assign,[],AssignIds,_Read), |
| 211 | | (ord_subset(OverrideIds,AssignIds), |
| 212 | | merge_statement(Assign,AssignIds,AfterPred,NewSubst), |
| 213 | | Keep = merged_override_stmt |
| 214 | | -> % we need to remove the override assign, if it is non-det. |
| 215 | | add_message(b_machine_construction,'Adapting initialisation due to override: ',NewSubst,Assign) |
| 216 | | ; ord_subset(AssignIds,OverrideIds) |
| 217 | | % The assignment is useless, will be completely overriden |
| 218 | | -> NewSubst = b(skip,subst,[was(Assign)]), |
| 219 | | add_message(b_machine_construction,'Removing initialisation due to override: ',Assign,Assign), |
| 220 | | Keep = keep_override_stmt |
| 221 | | ). |
| 222 | | % TODO: we could simplify IF-THEN-ELSE, ... and other constructs |
| 223 | | |
| 224 | | % Note: the merging assumes the initialisation before the override assigns each overriden variable only once |
| 225 | | try_simplify_init_stmt(OverrideIds,AfterPred,Keep,Stmt,NewStmt) :- |
| 226 | | (simplify_init_stmt(Stmt,OverrideIds,AfterPred,Keep,N) |
| 227 | | -> NewStmt=N ; NewStmt=Stmt). |
| 228 | | |
| 229 | | merge_statement(b(Subst,subst,Info),AssignIds,AfterPred,b(NewSubst,subst,Info)) :- |
| 230 | | merge_stmt_aux(Subst,AssignIds,AfterPred,NewSubst). |
| 231 | | |
| 232 | | merge_stmt_aux(becomes_such(Ids,Pred),_AssignIds,AfterPred,becomes_such(Ids,NewPred)) :- |
| 233 | | conjunct_predicates([AfterPred,Pred],NewPred). |
| 234 | | merge_stmt_aux(becomes_element_of(Ids,Set),_,AfterPred2,becomes_such(Ids,NewPred)) :- |
| 235 | | create_couple(Ids,Couple), |
| 236 | | safe_create_texpr(member(Couple,Set),pred,AfterPred1), |
| 237 | | conjunct_predicates([AfterPred1,AfterPred2],NewPred). |
| 238 | | |
| 239 | | create_after_pred(b(Subst,subst,Info),Pred) :- create_after_pred_aux(Subst,Info,Pred). |
| 240 | | |
| 241 | | create_after_pred_aux(assign_single_id(Id,RHS),Info,b(equal(Id,RHS),pred,Info)). |
| 242 | | create_after_pred_aux(assign(LHS,RHS),_Info,Conj) :- % TODO: split assignments so that we can individually apply preds |
| 243 | | maplist(create_equality,LHS,RHS,Eqs), |
| 244 | | conjunct_predicates(Eqs,Conj). |
| 245 | | % the following two are non-deterministic; hence we need to remove the substitutions |
| 246 | | % in case we have managed to merge them into an earlier becomes_such,... (otherwise we may do a second non-det incompatible choice) |
| 247 | | create_after_pred_aux(becomes_element_of(Id,Set),Info,b(member(Couple,Set),pred,Info)) :- |
| 248 | | create_couple(Id,Couple). |
| 249 | | create_after_pred_aux(becomes_such(_,Pred),_Info,Pred). |
| 250 | | |
| 251 | | |
| 252 | | % ------------------------- |
| 253 | | |
| 254 | | add_machine_infos(MainName,Machines,CheckedMachines,Old,New) :- |
| 255 | ? | get_raw_model_type(MainName,Machines,RawModelType), functor(RawModelType,ModelType,_), % argument is position |
| 256 | | % model type could be machine or system (or model ?) |
| 257 | | !, |
| 258 | | append_to_section(meta,[model_type/ModelType,hierarchy/Hierarchy,header_pos/HeaderPosList],Old,NewT), |
| 259 | | get_refinement_hierarchy(MainName,Machines,Hierarchy), |
| 260 | | find_machine_header_positions(Machines,HeaderPosList), |
| 261 | | add_refined_machine(Hierarchy,CheckedMachines,NewT,New). |
| 262 | | get_refinement_hierarchy(Main,Machines,[Main|Abstractions]) :- |
| 263 | | ( find_refinement(Machines,Main,Abstract) -> |
| 264 | | get_refinement_hierarchy(Abstract,Machines,Abstractions) |
| 265 | | ; |
| 266 | | Abstractions = []). |
| 267 | | find_refinement([M|Rest],Name,Abstract) :- |
| 268 | | ( get_constructed_machine_name(M,Name) -> |
| 269 | | refines(M,Abstract) |
| 270 | | ; |
| 271 | | find_refinement(Rest,Name,Abstract)). |
| 272 | | |
| 273 | | find_machine_header_positions(Machines,SRes) :- |
| 274 | | Header = machine_header(Pos,_Nm,_Paras), |
| 275 | | findall(Name/Pos,find_machine(Name,Machines,_Type,Header,_Sections),Res), |
| 276 | | sort(Res,SRes). |
| 277 | | |
| 278 | | :- use_module(specfile,[animation_minor_mode/1]). |
| 279 | | % check whether the main machine has filenumber 1; if not something strange is going on. |
| 280 | | % an example can be found in prob_examples/public_examples/B/ErrorMachines/IllegalSeesIncludes/WrongNameM1.mch |
| 281 | | check_main_machine_file_origin(MainName,Machines) :- |
| 282 | ? | member(M,Machines), get_machine_parameters(M,MainName,_,Position), |
| 283 | | !, |
| 284 | | (Position = none -> true |
| 285 | | ; get_nr_name(Position,Number,Name) |
| 286 | | -> (Number=1 -> true |
| 287 | | ; ajoin(['Main machine name ',MainName,' overriden by machine in file ',Number,' :'],Msg), |
| 288 | | add_error(bmachine_construction,Msg,Name,Position)) |
| 289 | | ; add_error(bmachine_construction,'Could not extract file number and name:',Position) |
| 290 | | ). |
| 291 | | check_main_machine_file_origin(MainName,_) :- |
| 292 | | add_error(bmachine_construction,'Could not extract file number and name for main machine:',MainName). |
| 293 | | get_nr_name(none,Nr,Name) :- !, Nr=1,Name=none. |
| 294 | | get_nr_name(1,Nr,Name) :- !, Nr=1,Name=none. % TLA mode, animation_minor_mode(tla) not yet set and positions are numbers |
| 295 | | get_nr_name(Position,Number,Name) :- extract_file_number_and_name(Position,Number,Name). |
| 296 | | |
| 297 | | add_refined_machine([_Main,Refined|_],Machines,Old,New) :- |
| 298 | ? | member(machine(Refined,Body),Machines),!, |
| 299 | | append_to_section(meta,[refined_machine/Body],Old,New). |
| 300 | | add_refined_machine(_,_,M,M). % not refining |
| 301 | | |
| 302 | | convert_parameters_to_global_sets(Ein,Eout) --> |
| 303 | | % extract and remove parameters and constraints |
| 304 | | select_section(parameters,PP,[]), |
| 305 | | select_section(internal_parameters,IP,[]), |
| 306 | | {create_texpr(truth,pred,[],Truth)}, |
| 307 | | select_section(constraints,C,Truth), |
| 308 | | % split parameters into sets and scalars |
| 309 | | { split_list(is_set_parameter,PP,Sets,Scalars), |
| 310 | | foldl(type_set_parameter,Sets,Ein,Eout) }, |
| 311 | | % put the sets to the deferred sets |
| 312 | | append_to_section(deferred_sets,Sets), |
| 313 | | % and the scalars to the constants |
| 314 | | append_to_section(concrete_constants,Scalars), |
| 315 | | append_to_section(concrete_constants,IP), |
| 316 | | % the scalars should be typed by constraints, |
| 317 | | % so move the constraints to the properties |
| 318 | | select_section(properties,OldProps,NewProps), |
| 319 | | {conjunct_predicates([C,OldProps],NewProps)}. |
| 320 | | is_set_parameter(TExpr) :- |
| 321 | | % upper case identifiers denote set parameters, otherwise scalars |
| 322 | | get_texpr_id(TExpr,Name),is_upper_case(Name). |
| 323 | | type_set_parameter(TExpr,Ein,Eout) :- |
| 324 | | get_texpr_id(TExpr,Name), |
| 325 | | get_texpr_type(TExpr,Type), |
| 326 | | get_texpr_pos(TExpr,Pos), |
| 327 | | % we directly type the deferred set |
| 328 | | unify_types_werrors(set(global(Name)),Type,Pos,'PARAMETER',Ein,Eout). |
| 329 | | |
| 330 | | |
| 331 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 332 | | % included machines |
| 333 | | |
| 334 | | expand_machines([],_,_,M,M,Errors,Errors). |
| 335 | | expand_machines([M|Rest],Machines,GlobalUses,Typed,Result,Ein,Eout) :- |
| 336 | ? | ( expand_machine(M,Machines,GlobalUses,Typed,New,Ein,E1) -> true |
| 337 | | ; add_error(bmachine_construction,'Expansion of machine failed:',M),fail), |
| 338 | | expand_machines(Rest,Machines,GlobalUses,[machine(M,New)|Typed],Result,E1,Eout). |
| 339 | | |
| 340 | | % resolve all includes and typecheck the machine |
| 341 | | % expand_machine(Name,Machines,TypeChecked,Expanded) : |
| 342 | | % Name of the Machine to expand |
| 343 | | % Machines contains the list of all loaded machines |
| 344 | | % TypeChecked contains the list of all expanded machines so far |
| 345 | | % Expanded is the resulting machine |
| 346 | | expand_machine(Name,Machines,GlobalUses,TypeChecked,Expanded,Ein,Eout) :- |
| 347 | | debug_format(9,'~nExpanding machine ~w~n',[Name]), |
| 348 | | % find the machine in the list |
| 349 | | find_machine(Name,Machines,MType,Header,RawMachineWithPragmas), |
| 350 | | % remove pragmas for later reattachment |
| 351 | | strip_machine_section_pragmas(RawMachineWithPragmas,RawMachine,_Pragmas), |
| 352 | | % look for abstract machine |
| 353 | | get_abstractions(Name,Machines,TypeChecked,Abstractions), |
| 354 | | % merge all used and seen machines |
| 355 | ? | use_and_see_machines(RawMachine,TypeChecked,SeenRefs), |
| 356 | | % merge all included machines into one machine |
| 357 | ? | include_machines(RawMachine, TypeChecked, GlobalUses, Includes, Parameters, Included, Promotes), |
| 358 | | % Parameters contains now a list of parameters for each included machine |
| 359 | | % Included contains now a machine that represents all included machines |
| 360 | | % their parameters are renamed to internal_parameters |
| 361 | | append([Abstractions,Included,SeenRefs],RefMachines), |
| 362 | | % typecheck this machine |
| 363 | | debug_stats(type_machine(Name)), |
| 364 | ? | type_machine(Header,Name,MType,RawMachine,RefMachines,TypedLocal,RefMachines2,Ein,Err1), |
| 365 | | % merge included and including machine |
| 366 | | debug_stats(merge_included_machines(Name)), |
| 367 | | merge_included_machines(Name,TypedLocal,Included,Promotes,Expanded2,Err1,Err2), |
| 368 | | % add some predicates that state the equivalence between arguments |
| 369 | | % in the include statement and the parameters of the included machine |
| 370 | | add_link_constraints(Includes,MType,Parameters,RefMachines2,Expanded2,Expanded3,Err2,Err3), |
| 371 | | % put together refinement and abstraction. |
| 372 | ? | merge_refinement_and_abstraction(Name,Expanded3,[ref(local,TypedLocal)|RefMachines2],Expanded4,Err3,Eout), |
| 373 | | % merge used machines, will also prefix |
| 374 | | debug_stats(merge_used_machines(Name)), |
| 375 | | merge_used_machines(Included,Expanded4,Expanded5), |
| 376 | | % clean up the syntax tree |
| 377 | | debug_stats(clean_up_machine(Name)), |
| 378 | | clean_up_machine(Expanded5,RefMachines2,Expanded), |
| 379 | | debug_stats(finished_clean_up_and_expand_machine(Name)). |
| 380 | | |
| 381 | | strip_machine_section_pragmas(RawMachineWithPragmas,RawMachine,Pragmas) :- |
| 382 | | selectchk(units(_Pos,RealVariables,Pragmas),RawMachineWithPragmas,TRawMachine), !, |
| 383 | | RawMachine = [RealVariables|TRawMachine]. |
| 384 | | strip_machine_section_pragmas(Machine,Machine,[]). |
| 385 | | |
| 386 | | merge_included_machines(Name, TypedLocal, RefMachines, Promotes, Merged, Ein, Eout) :- |
| 387 | | (Promotes=[] -> true ; assertz(machine_promotes_operations(Name,Promotes))), |
| 388 | | include(is_included_ref,RefMachines,Included), |
| 389 | | create_machine(Name,Empty), |
| 390 | | % move the included operations into the promoted or unpromoted section |
| 391 | | move_operations(Included,Promotes,Included2,Ein,Eout), |
| 392 | | LocalAndIncluded = [ref(local,TypedLocal)|Included2], |
| 393 | | % TODO: make sure that the constants of two instances of the same machine do not repeat (check what should be done for distinct machines with same identifiers) |
| 394 | | concat_sections_of_refs([identifiers,initialisation,operation_bodies, |
| 395 | | assertions,used,values],LocalAndIncluded,Empty,Merged1), |
| 396 | | conjunct_sections_of_refs([constraints,properties,invariant],LocalAndIncluded,Merged1,Merged2), |
| 397 | | concat_section_of_simple_lists(freetypes,LocalAndIncluded,Merged2,Merged3), |
| 398 | | get_section(definitions,TypedLocal,Defs), |
| 399 | | write_section(definitions,Defs,Merged3,Merged). |
| 400 | | is_included_ref(ref(included,_)). |
| 401 | | |
| 402 | | concat_section_of_simple_lists(Sec,References,In,Out) :- |
| 403 | | maplist(extract_machine_from_ref,References,Machines), |
| 404 | | sort_machines_by_global_order(Machines,OMachines), |
| 405 | | foldl(concat_section_of_simple_lists2(Sec),OMachines,In,Out). |
| 406 | | concat_section_of_simple_lists2(Sec,Machine,In,Out) :- |
| 407 | | get_section(Sec,In,Orig), |
| 408 | | get_section(Sec,Machine,List), |
| 409 | | append(Orig,List,NewList), |
| 410 | | write_section(Sec,NewList,In,Out). |
| 411 | | |
| 412 | | merge_used_machines(RefMachines,Old,New) :- |
| 413 | | % get all included machines from the references |
| 414 | | convlist(ref_to_included_machine,RefMachines,Included), |
| 415 | | foldl(merge_used_machines2,Included,Old,New). |
| 416 | | merge_used_machines2(Included,Old,New) :- |
| 417 | | get_section(used,Included,IncludedUse), |
| 418 | | (IncludedUse = [] -> New = Old ; rename_used(IncludedUse,Old,New)). |
| 419 | | ref_to_included_machine(ref(included,Inc),Inc). |
| 420 | | |
| 421 | | % will add prefixes to identifiers |
| 422 | | rename_used(IncludedUse,Old,New) :- |
| 423 | | %print(renaming(IncludedUse)),nl, |
| 424 | | expand_shortcuts([properties,invariant, assertions, |
| 425 | | initialisation,operation_bodies],Sections), % TO DO: also traverse GOAL ? should we later apply this at the REPL level as well ? should we apply it to other DEFINITIONS ? |
| 426 | | foldl(rename_used_sec(IncludedUse),Sections,Old,New). |
| 427 | | rename_used_sec(IncludedUse,Sec,Old,New) :- |
| 428 | | select_section_texprs(Sec,TExprs,NewTExprs,Old,New), |
| 429 | | rename_used2_l(TExprs,IncludedUse,NewTExprs). |
| 430 | | |
| 431 | | rename_used2(TExpr,IncludedUse,NewTExpr) :- |
| 432 | | get_texpr_expr(TExpr,operation(Id,Params,Results,Subst)),!, |
| 433 | | rename_used2_l(Params,IncludedUse,NParams), |
| 434 | | rename_used2_l(Results,IncludedUse,NResults), |
| 435 | | rename_used2(Subst,IncludedUse,NSubst), |
| 436 | | get_texpr_type(TExpr,Type), |
| 437 | | get_texpr_info(TExpr,Info), |
| 438 | | selectchk(modifies(MIn),Info,Info1),selectchk(reads(RIn),Info1,RestInfo), |
| 439 | | rename_used_ids(MIn,IncludedUse,MOut), |
| 440 | | rename_used_ids(RIn,IncludedUse,ROut), |
| 441 | | create_texpr(operation(Id,NParams,NResults,NSubst),Type,[modifies(MOut),reads(ROut)|RestInfo], |
| 442 | | NewTExpr). |
| 443 | | rename_used2(TExpr,IncludedUse,NewTExpr) :- |
| 444 | | % rename identifier by adding machine prefix M.id |
| 445 | | get_texpr_id(TExpr,_), % result is also Id (always ??) |
| 446 | | get_texpr_info(TExpr,Info), |
| 447 | ? | member(usesee(Name,Id,_Mode),Info), |
| 448 | ? | member(includeduse(Name,Id,NewTExpr),IncludedUse), % This seems to reuse position information from includeduse list for all identifiers !! TO DO: check this |
| 449 | | % we now rename Id to Name.Id |
| 450 | | !. %, print(ren_id(Name,Id)),nl. |
| 451 | | rename_used2(TExpr,IncludedUse,NTExpr1) :- |
| 452 | | remove_bt(TExpr,Expr,NExpr,NTExpr), |
| 453 | ? | syntaxtransformation_for_renaming(Expr,Subs,_,NSubs,NExpr),!, |
| 454 | | rename_used2_l(Subs,IncludedUse,NSubs), |
| 455 | | rename_infos(NTExpr,IncludedUse,NTExpr1). |
| 456 | | rename_used2(TExpr,IncludedUse,NTExpr) :- |
| 457 | | add_internal_error('Rename failed: ',rename_used2(TExpr,IncludedUse,NTExpr)), |
| 458 | | NTExpr = TExpr. |
| 459 | | |
| 460 | | % update infos, e.g., read/modifies for while loops |
| 461 | | rename_infos(b(E,T,I),List,b(E,T,NI)) :- maplist(rename_info(List),I,NI). |
| 462 | | |
| 463 | | rename_info(IncludeUseList,Info,NewInfo) :- info_field_contains_ids(Info,I,NewInfo,SNI),!, |
| 464 | | maplist(rename_usage_info(IncludeUseList),I,NI), sort(NI,SNI). |
| 465 | | rename_info(_,I,I). |
| 466 | | |
| 467 | | info_field_contains_ids(reads(I),I,reads(SNI),SNI). |
| 468 | | info_field_contains_ids(modifies(I),I,modifies(SNI),SNI). |
| 469 | | info_field_contains_ids(non_det_modifies(I),I,non_det_modifies(SNI),SNI). |
| 470 | | info_field_contains_ids(modifies_locals(I),I,modifies_locals(SNI),SNI). |
| 471 | | info_field_contains_ids(reads_locals(I),I,reads_locals(SNI),SNI). |
| 472 | | info_field_contains_ids(used_ids(I),I,used_ids(SNI),SNI). |
| 473 | | |
| 474 | | rename_usage_info(IncludeUseList,ID,NewID) :- |
| 475 | ? | (member(includeduse(_,ID,NewTExpr),IncludeUseList) -> get_texpr_id(NewTExpr,NewID) ; NewID = ID). |
| 476 | | |
| 477 | | |
| 478 | | rename_used2_l([],_,R) :- !, R=[]. |
| 479 | | rename_used2_l([T|Rest],IncludedUse,[NT|NRest]) :- |
| 480 | | rename_used2(T,IncludedUse,NT), !, |
| 481 | | rename_used2_l(Rest,IncludedUse,NRest). |
| 482 | | rename_used2_l(X,Y,Z) :- add_internal_error('Rename failed: ', rename_used2_l(X,Y,Z)),Z=X. |
| 483 | | |
| 484 | | rename_used_ids(InIds,IncludedUse,OutIds) :- |
| 485 | | maplist(rename_used_ids2(IncludedUse),InIds,OutIds). |
| 486 | | rename_used_ids2(IncludedUse,InId,OutId) :- |
| 487 | | memberchk(includeduse(_,InId,TOutId),IncludedUse),!, |
| 488 | | get_texpr_id(TOutId,OutId). |
| 489 | | rename_used_ids2(_IncludedUse,Id,Id). |
| 490 | | |
| 491 | | get_abstractions(CName,Machines,TypedMachines,[ref(abstraction,Abstraction)]) :- |
| 492 | ? | refines(M,AName), |
| 493 | | get_constructed_machine_name(M,CName), |
| 494 | | memberchk(M,Machines),!, |
| 495 | | memberchk(machine(AName,Abstraction),TypedMachines). |
| 496 | | get_abstractions(_,_,_,[]). |
| 497 | | |
| 498 | | get_includes_and_promotes(Sections,M,Includes,Promotes) :- |
| 499 | | optional_rawmachine_section(includes,Sections,[],Includes1), |
| 500 | | optional_rawmachine_section(imports,Sections,[],Imports), |
| 501 | | optional_rawmachine_section(extends,Sections,[],Extends), |
| 502 | | optional_rawmachine_section(promotes,Sections,[],Promotes1), |
| 503 | | append([Includes1,Extends,Imports],Includes), |
| 504 | ? | maplist(expand_extends(M),Extends,Promotes2), |
| 505 | | append([Promotes1|Promotes2],Promotes). |
| 506 | | expand_extends(Machines,machine_reference(_,Ref,_),Promotes) :- |
| 507 | | split_prefix(Ref,Prefix,Name), |
| 508 | | memberchk(machine(Name,Body),Machines), |
| 509 | | get_section(promoted,Body,Promoted), |
| 510 | | prefix_identifiers(Promoted,Prefix,Renamings), |
| 511 | ? | rename_bt_l(Promoted,Renamings,TPromotes), |
| 512 | | maplist(add_nonpos,TPromotes,Promotes). |
| 513 | | add_nonpos(TId,identifier(none,Id)) :- |
| 514 | | get_texpr_id(TId,op(Id)). |
| 515 | | |
| 516 | | move_operations([],Promotes,[],Ein,Eout) :- |
| 517 | | foldl(add_promotes_not_found_error,Promotes,Ein,Eout). |
| 518 | | move_operations([ref(_,IncMachine)|IncRest],Promotes,[ref(included,NewIncMachine)|RefRest],Ein,Eout) :- |
| 519 | | move_operations2(IncMachine,Promotes,NewIncMachine,RestPromotes), |
| 520 | | move_operations(IncRest,RestPromotes,RefRest,Ein,Eout). |
| 521 | | move_operations2(Included,Promotes,Result,RestPromotes) :- |
| 522 | | select_section(promoted,IncOperations,Promoted,Included,Included1), |
| 523 | | select_section(unpromoted,OldUnpromoted,Unpromoted,Included1,Result), |
| 524 | | filter_promoted(IncOperations,Promotes,Promoted,NewUnpromoted,RestPromotes), |
| 525 | | append(OldUnpromoted,NewUnpromoted,Unpromoted). |
| 526 | | filter_promoted([],Promotes,[],[],Promotes). |
| 527 | | filter_promoted([TExpr|OpsRest],Promotes,Promoted,Unpromoted,RestPromotes) :- |
| 528 | | get_texpr_id(TExpr,op(P)), |
| 529 | ? | ( select(identifier(_,P),Promotes,RestPromotes1) -> |
| 530 | | !,Promoted = [TExpr|RestPromoted], |
| 531 | | Unpromoted = RestUnpromoted |
| 532 | | ; |
| 533 | | RestPromotes1 = Promotes, |
| 534 | | Promoted = RestPromoted, |
| 535 | | Unpromoted = [TExpr|RestUnpromoted]), |
| 536 | | filter_promoted(OpsRest,RestPromotes1,RestPromoted,RestUnpromoted,RestPromotes). |
| 537 | | add_promotes_not_found_error(identifier(Pos,Id),[error(Msg,Pos)|E],E) :- |
| 538 | | ajoin(['Promoted operation ',Id,' not found'],Msg). |
| 539 | | |
| 540 | | find_machine(Name,Machines,Type,Header,Sections) :- |
| 541 | | Header = machine_header(_,Name,_), |
| 542 | ? | ( (member(abstract_machine(_,_ModelType,Header,Sections),Machines), |
| 543 | | Type=machine) |
| 544 | ? | ; (member(refinement_machine(_,Header,_Abstract,Sections),Machines), |
| 545 | | Type=refinement) |
| 546 | ? | ; (member(implementation_machine(_,Header,_Abstract,Sections),Machines), |
| 547 | | Type=implementation)), |
| 548 | | !. |
| 549 | | |
| 550 | | include_machines(RawMachine, TypeChecked, GlobalUses, Includes, Parameters, Included, Promotes) :- |
| 551 | ? | get_includes_and_promotes(RawMachine,TypeChecked,Includes,Promotes), |
| 552 | ? | maplist(include_machine(TypeChecked, GlobalUses), Includes, Parameters, Singles), |
| 553 | | remove_duplicate_set_inclusions(Singles,Singles1), |
| 554 | | % duplicate constants inclusion is handled currently in concat_section_contents via section_can_have_duplicates |
| 555 | | % create refs |
| 556 | | maplist(create_ref,Singles1,Included). |
| 557 | | create_ref(IncMachine,ref(included,IncMachine)). |
| 558 | | |
| 559 | | % it is possible that a deferred or enumerated set is declared in an included machine |
| 560 | | % that is included more than once. We remove all but one occurrences. |
| 561 | | remove_duplicate_set_inclusions([],[]). |
| 562 | | remove_duplicate_set_inclusions([M],[M]) :- !. |
| 563 | | remove_duplicate_set_inclusions([M|Rest],[M|CleanedRest]) :- |
| 564 | | get_section(deferred_sets,M,DSets), |
| 565 | | get_section(enumerated_sets,M,ESets), |
| 566 | | get_section(enumerated_elements,M,EElements), |
| 567 | | append([DSets,ESets,EElements],Identifiers), |
| 568 | | sort(Identifiers,SortedIds), |
| 569 | | maplist(remove_duplicate_sets2(SortedIds),Rest,Rest2), |
| 570 | | remove_duplicate_set_inclusions(Rest2,CleanedRest). |
| 571 | | remove_duplicate_sets2(Identifiers,M,Cleaned) :- |
| 572 | | remove_duplicate_sets_section(deferred_sets,Identifiers,M,M1), |
| 573 | | remove_duplicate_sets_section(enumerated_sets,Identifiers,M1,M2), |
| 574 | | remove_duplicate_sets_section(enumerated_elements,Identifiers,M2,Cleaned). |
| 575 | | remove_duplicate_sets_section(Section,Identifiers,In,Out) :- |
| 576 | | select_section(Section,Old,New,In,Out), |
| 577 | | %get_texpr_ids(Identifiers,II),format('Removing duplicates ~w = ~w~n',[Section,II]), |
| 578 | | exclude(element_is_duplicate(Identifiers),Old,New). |
| 579 | | |
| 580 | | element_is_duplicate(Identifiers,TId) :- |
| 581 | | get_texpr_id(TId,Name), |
| 582 | | get_texpr_type(TId,Type), |
| 583 | | get_texpr_id(ToRemove,Name), |
| 584 | | get_texpr_type(ToRemove,Type), |
| 585 | | ord_member_nonvar_chk(ToRemove,Identifiers), |
| 586 | | get_texpr_info(TId,InfosA), |
| 587 | | get_texpr_info(ToRemove,InfosB), |
| 588 | | member(def(Sec,File),InfosA), |
| 589 | | member(def(Sec,File),InfosB), |
| 590 | | debug_format(5,'Removed duplicate included identifier: ~w~n',[Name]), |
| 591 | | !. |
| 592 | | |
| 593 | | include_machine(TypeChecked,GlobalUses,machine_reference(_Pos,FullRef,_Args), |
| 594 | | parameters(FullRef,Parameters), TM) :- |
| 595 | | split_prefix(FullRef,Prefix,Name), |
| 596 | | % TM1 is the already typechecked included machine: |
| 597 | ? | member(machine(Name,TM1),TypeChecked),!, |
| 598 | | debug_println(9,including_machine(Name)), |
| 599 | | % TM2 is a fresh copy, so we prevent unification of the parameter types: |
| 600 | | (get_section(parameters,TM1,[]) -> TM2=TM1 |
| 601 | | ; copy_term(TM1,TM2) % this can be expensive; only copy if there are parameters |
| 602 | | ), |
| 603 | | % If the included machine is used somewhere, we store the identifiers |
| 604 | | % to enable joining the different references later: |
| 605 | | include_usings(Name,GlobalUses,TM2,TM3), |
| 606 | | % TM3 is typechecked and all internal variables are renamed with a prefix |
| 607 | ? | hide_private_information(Name,FullRef,TM3,TM4), |
| 608 | | % TM4 is typechecked, and if it was referenced with a prefix (e.g. INCLUDES p.M2) |
| 609 | | % all variables are prefixed |
| 610 | | %print(prefixing(Prefix)),nl, |
| 611 | | prefix_machine(Prefix,TM4,TM5), |
| 612 | | % We need the parameters later to state their equivalence to the arguments |
| 613 | | get_section(parameters,TM5,Parameters), |
| 614 | | % We move the parameters to the internal parameters, because the resulting |
| 615 | | % machine has only the parameters of the including machine. |
| 616 | | parameters_to_internal(TM5,TM). |
| 617 | | |
| 618 | | include_usings(Name,GlobalUses,Old,New) :- |
| 619 | ? | ( member(usemch(Name,Prefix,_Kind,_FromMch,_Pos),GlobalUses) -> |
| 620 | | add_machine_prefix(Name,Prefix,FullName), |
| 621 | | store_usage_info(Old,FullName,UsedInfo), |
| 622 | | append_to_section(used,UsedInfo,Old,New) |
| 623 | | ; |
| 624 | | Old = New). |
| 625 | | % returns a list of which identifiers are used in the machine |
| 626 | | % for each identifier, we have a trible includeuse(Name,Id,TExpr) |
| 627 | | % where Name is the name of the used machine, Id is the |
| 628 | | % original ID and TExpr is the currently used reference to this |
| 629 | | % identifier |
| 630 | | store_usage_info(Machine,Name,UsedInfo) :- |
| 631 | | expand_shortcuts([identifiers],IdSections), |
| 632 | | foldl(store_usage_info2(Machine,Name),IdSections,UsedInfo,[]). |
| 633 | | store_usage_info2(Machine,Name,Sec,I,O) :- |
| 634 | | get_section(Sec,Machine,Content), |
| 635 | | foldl(store_usage_info3(Name),Content,I,O). |
| 636 | | store_usage_info3(Name,TId,[includeduse(Name,Id,TId)|L],L) :- |
| 637 | | get_texpr_id(TId,Id). |
| 638 | | |
| 639 | | % conjunct sections that contain predicates (CONSTRAINTS, PROPERTIES, INVARIANT) |
| 640 | | conjunct_sections_of_refs(Sections1,References,Old,New) :- |
| 641 | | expand_shortcuts(Sections1,Sections), |
| 642 | | maplist(extract_machine_from_ref,References,Machines), |
| 643 | | sort_machines_by_global_order(Machines,OMachines), |
| 644 | | foldl(conjunct_sections2(OMachines),Sections,Old,New). |
| 645 | | conjunct_sections2(Machines,Section,Old,New) :- |
| 646 | | % Section is constraints,properties,invariant, ... |
| 647 | | write_section(Section,NewContent,Old,New), % prepare new section |
| 648 | | get_section_of_machines(Machines,Section,Preds), |
| 649 | | %maplist(get_machine_name,Machines,Ns),print(got_sections(Section,Ns)),nl, |
| 650 | | %translate:l_print_bexpr_or_subst(Preds),nl, |
| 651 | | conjunct_predicates(Preds,NewContent). |
| 652 | | |
| 653 | | |
| 654 | | % merge sections that contain a list of expressions/formulas like identifiers, assertions, ... |
| 655 | | concat_sections_of_refs(Sections1,References,Old,New) :- |
| 656 | | maplist(extract_machine_from_ref,References,Machines), |
| 657 | | maplist(create_tag_by_reference,References,Tags), |
| 658 | | sort_machines_by_global_order(Machines,Tags,OMachines,STags), |
| 659 | | % should we only sort for some sections, e.g., assertions |
| 660 | | % for each machine, create a tag where the expression comes from |
| 661 | | concat_sections(Sections1,OMachines,STags,Old,New). |
| 662 | | |
| 663 | | extract_machine_from_ref(ref(_,M),M). |
| 664 | | |
| 665 | | create_tag_by_reference(ref(local,_Machine),[]) :- !. |
| 666 | | create_tag_by_reference(ref(RefType,Machine),[RefType/Name]) :- |
| 667 | | get_machine_name(Machine,Name). |
| 668 | | |
| 669 | | concat_sections(Sections1,Machines,Tags,Old,New) :- |
| 670 | | expand_shortcuts(Sections1,Sections), |
| 671 | | foldl(concat_section2(Machines,Tags),Sections,Old,New). |
| 672 | | concat_section2(Machines,Tags,Section,Old,New) :- |
| 673 | | write_section(Section,NewContent,Old,New), |
| 674 | | maplist(get_tagged_lsection_of_machine(Section),Machines,Tags,Contents), |
| 675 | | concat_section_contents(Section,Contents,NewContent). |
| 676 | | |
| 677 | | concat_section_contents(_,[SingleContent],Res) :- !, Res=SingleContent. % no need to remove_dups |
| 678 | | concat_section_contents(Section,Contents,NewContent) :- |
| 679 | | append(Contents,ConcContents), |
| 680 | | (section_can_have_duplicates(Section) |
| 681 | | -> remove_dup_tids_keep_order(ConcContents,NewContent) |
| 682 | | ; NewContent=ConcContents). |
| 683 | | |
| 684 | | % remove_dups version which keeps order of original elements |
| 685 | | % and works with typed identifiers |
| 686 | | % used if a machine is seen multiple times with different prefixes for e.g. constants |
| 687 | | % see Section 7.26 of Atelier-B handbook: |
| 688 | | % nom des éléments énumérés et des constantes de MSees, sans le préfixe de |
| 689 | | % renommage, si plusieurs instances de machines sont vues, les noms des |
| 690 | | % données ne doivent pas être répétés, |
| 691 | | remove_dup_tids_keep_order([],[]). |
| 692 | | remove_dup_tids_keep_order([H|T],[H|Res]) :- empty_avl(E), |
| 693 | | get_texpr_id(H,ID),avl_store(ID,E,true,A1), |
| 694 | | rem_dups3(T,A1,Res). |
| 695 | | |
| 696 | | rem_dups3([],_,[]). |
| 697 | | rem_dups3([H|T],AVL,Res) :- |
| 698 | | get_texpr_id(H,ID), |
| 699 | | (avl_fetch(ID,AVL) -> rem_dups3(T,AVL,Res) |
| 700 | | ; Res=[H|TRes], |
| 701 | | avl_store(ID,AVL,true,AVL1), |
| 702 | | rem_dups3(T,AVL1,TRes)). |
| 703 | | |
| 704 | | % see issue PROB-403 |
| 705 | | section_can_have_duplicates(X) :- section_can_be_included_multiple_times_nonprefixed(X). |
| 706 | | % should we also remove duplicates in PROPERTIES section ? cf. machines used in test 1857 |
| 707 | | |
| 708 | | |
| 709 | | get_tagged_lsection_of_machine(Section,Machine,Tags,TaggedContent) :- |
| 710 | | get_section(Section,Machine,Content), |
| 711 | | (Tags=[] -> TaggedContent=Content ; maplist(tag_with_origin(Tags),Content,TaggedContent)). |
| 712 | | |
| 713 | | tag_with_origin(Origins,TExpr,TaggedExpr) :- |
| 714 | | change_info_of_expression_or_init(TExpr,Info,TaggedInfo,TaggedExpr), |
| 715 | | % add a new origin to the old tag or if not existent, add a new info field |
| 716 | | ( Origins = [] -> TaggedInfo = Info |
| 717 | | ; selectchk(origin(Orest),Info,origin(Onew),TaggedInfo) -> append(Origins,Orest,Onew) |
| 718 | | ; TaggedInfo = [origin(Origins)|Info]). |
| 719 | | % the substitutions in the initialisation are additionally wrapped by an init_stmt/2 term |
| 720 | | % a small hack to work with those too. |
| 721 | | % TODO: this became a very ugly hack -- redo! |
| 722 | | change_info_of_expression_or_init(init_stmt(A,OExpr),Old,New,init_stmt(A,NExpr)) :- % ... |
| 723 | | !,change_info_of_expression_or_init(OExpr,Old,New,NExpr). |
| 724 | | % ignore the info for includeduse/3 completely |
| 725 | | change_info_of_expression_or_init(includeduse(A,B,C),[],_,includeduse(A,B,C)) :- !. |
| 726 | | change_info_of_expression_or_init(freetype(FTypeId,Cases),[],_,freetype(FTypeId,Cases)) :- !. |
| 727 | | change_info_of_expression_or_init(OExpr,Old,New,NExpr) :- |
| 728 | | create_texpr(Expr,Type,Old,OExpr),!, |
| 729 | | create_texpr(Expr,Type,New,NExpr). |
| 730 | | change_info_of_expression_or_init(OExpr,Old,New,NExpr) :- |
| 731 | | add_internal_error('Illegal typed expression:',change_info_of_expression_or_init(OExpr,Old,New,NExpr)), |
| 732 | | Old=[], NExpr=OExpr. |
| 733 | | |
| 734 | | % adds a prefix to all variables and promoted operations |
| 735 | | prefix_machine('',TM,TM) :- !. |
| 736 | | prefix_machine(Prefix,Old,New) :- |
| 737 | | debug_println(5,prefixing_machine(Prefix)), |
| 738 | | expand_shortcuts([variables,promoted], RenamedIdentiferSections), |
| 739 | | get_all_identifiers(RenamedIdentiferSections,Old,Identifiers), |
| 740 | | prefix_identifiers(Identifiers,Prefix,Renamings), |
| 741 | | find_relevant_sections(RenamedIdentiferSections,[machine],Sections1), |
| 742 | | append(RenamedIdentiferSections,Sections1,Sections), |
| 743 | | rename_in_sections(Sections,Renamings,Old,M), |
| 744 | | rename_includeduse(M,Renamings,New). |
| 745 | | rename_includeduse(Old,Renamings,New) :- |
| 746 | | select_section(used,OldContent,NewContent,Old,New), |
| 747 | | maplist(rename_includeduse2(Renamings),OldContent,NewContent). |
| 748 | | rename_includeduse2(Renamings,includeduse(M,N,TExpr),includeduse(M,N,NExpr)) :- |
| 749 | | rename_bt(TExpr,Renamings,NExpr). |
| 750 | | |
| 751 | | get_all_identifiers(Sections1,M,Identifiers) :- |
| 752 | | expand_shortcuts(Sections1,Sections), |
| 753 | | maplist(get_all_identifiers2(M),Sections,LIdentifiers), |
| 754 | | append(LIdentifiers,Identifiers). |
| 755 | | get_all_identifiers2(M,Sec,Identifiers) :- |
| 756 | | get_section(Sec,M,Identifiers). |
| 757 | | |
| 758 | | % hide all parameters and unpromoted operations |
| 759 | | hide_private_information(MachName,Prefix,Machine,NewMachine) :- |
| 760 | | get_section(parameters,Machine,Params), |
| 761 | | get_section(unpromoted,Machine,UnPromoted), |
| 762 | | append(Params,UnPromoted,ToHide), |
| 763 | | %debug_println(9,hide_private(Prefix,ToHide)), |
| 764 | | ( ToHide = [] -> NewMachine=Machine |
| 765 | | ; |
| 766 | | debug_format(9,'Hiding private parameters ~w and unpromoted operations ~w of machine ~w~n', |
| 767 | | [Params,UnPromoted,MachName]), |
| 768 | | maplist(store_hides_operation_info(MachName,Prefix),UnPromoted), |
| 769 | | prefix_identifiers(ToHide,Prefix,Renamings), |
| 770 | | % we have to do the renaming in promoted operations, too, because |
| 771 | | % those operations might use the renamed parameters and their reads/modifies |
| 772 | | % info must be updated |
| 773 | | rename_in_sections([parameters,promoted,unpromoted],Renamings,Machine,Machine1), |
| 774 | | rename_includeduse(Machine1,Renamings,Machine2), |
| 775 | | % now find sections that can see parameters,operations: |
| 776 | ? | rename_relevant_sections([parameters,operations],Renamings,Machine2,NewMachine) |
| 777 | | ). |
| 778 | | |
| 779 | | % store information about which operations got renamed: |
| 780 | | store_hides_operation_info(MachName,Prefix,TExpr) :- Prefix \= '', |
| 781 | | prefix_identifier(Prefix,TExpr,rename(op(Old),op(New))), |
| 782 | | get_texpr_pos(TExpr,Pos), |
| 783 | | !, |
| 784 | | debug_format(19,'Hiding unpromoted operation ~w in machine ~w, new name: ~w~n',[Old,MachName,New]), |
| 785 | | assertz(machine_hides_unpromoted_operation(Old,MachName,New,Pos)). |
| 786 | | store_hides_operation_info(_,_,_). |
| 787 | | |
| 788 | | prefix_identifiers(Identifiers,'',Identifiers) :- !. |
| 789 | | prefix_identifiers(Old,Prefix,New) :- |
| 790 | | maplist(prefix_identifier(Prefix),Old,New). |
| 791 | | prefix_identifier(Prefix,TExpr,rename(Old,New)) :- |
| 792 | | get_texpr_expr(TExpr,identifier(Old)), |
| 793 | | (Old=op(OI) -> New=op(NI) ; OI=Old,NI=New), |
| 794 | | ajoin([Prefix,'.',OI],NI). % , print(rename(Old,New)),nl. |
| 795 | | |
| 796 | | parameters_to_internal(M1,M2) :- |
| 797 | | select_section(internal_parameters,OldParams,Params,M1,M3), |
| 798 | | select_section(parameters,NewParams,[],M3,M2), |
| 799 | | append(OldParams,NewParams,Params). |
| 800 | | |
| 801 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 802 | | % uses and sees relations |
| 803 | | find_uses(Machines,GlobalUses,NotIncludedWoDups,Ein,Eout) :- |
| 804 | | findall(usemch(UsedMch,UPrefix,Kind,FromMchName,Pos), |
| 805 | | use_usage(Machines,Pos,Kind,UsedMch,UPrefix,FromMchName), UnsortedUses), |
| 806 | | sort(UnsortedUses,Uses), |
| 807 | | check_include_use(Uses,Machines,NotIncluded,Ein,Eout), |
| 808 | | remove_duplicate_uses(NotIncluded,NotIncludedWoDups), |
| 809 | | GlobalUses = Uses. %maplist(remove_prefix,Uses,GlobalUses). |
| 810 | | |
| 811 | | %remove_prefix(usemch(U,_,_,_,_),U). |
| 812 | | same_usemch(usemch(U,Prefix,_,_,_),usemch(U,Prefix,_,_,_)). % use same machine with same prefix |
| 813 | | same_usemch(usemch_not_inc(U,Prefix,_,_,_,_),usemch_not_inc(U,Prefix,_,_,_,_)). |
| 814 | | |
| 815 | | % remove duplicate uses: a non-included/imported machine can be seen in many places; |
| 816 | | % we only need to add it later once to the dummy machine |
| 817 | | remove_duplicate_uses([],R) :- !, R=[]. |
| 818 | | remove_duplicate_uses([USE|T],Res) :- remove_dup_aux(T,USE,Res). |
| 819 | | remove_dup_aux([],USE,[USE]). |
| 820 | | remove_dup_aux([USE|T],PREVUSE,Res) :- |
| 821 | | (same_usemch(USE,PREVUSE) -> remove_dup_aux(T,PREVUSE,Res) |
| 822 | | ; Res = [PREVUSE|TR], remove_dup_aux(T,USE,TR)). |
| 823 | | |
| 824 | | |
| 825 | | % check_include_use/5 checks if the used machines are included in any machine |
| 826 | | % check_include_use(+Uses,+Machines,-NotIncluded,Ein,Eout) |
| 827 | | % Uses: The list of machine names that are used |
| 828 | | % Machines: The list of machines |
| 829 | | % NotIncluded: The list of used machines (their names) that are not included |
| 830 | | % Ein/Eout: The errors (as difference-list) |
| 831 | | check_include_use([],_,[],Errors,Errors). |
| 832 | | check_include_use([USEMCH|Rest],Machines,NotIncluded,Ein,Eout) :- |
| 833 | | USEMCH = usemch(UsedMach,UsePrefix,Kind,FromMchName,Pos), |
| 834 | | findall(i(MachName,UsePrefix,IPos),include_usage(Machines,UsedMach,UsePrefix,MachName,IPos),Inc), |
| 835 | | ( Inc=[] -> Ein=E1, |
| 836 | | % find all places where the machine is included with another prefix |
| 837 | | findall(OtherPrefix,include_usage(Machines,UsedMach,OtherPrefix,_,_IPos),OtherInclusions), |
| 838 | | NotIncluded = [usemch_not_inc(UsedMach,UsePrefix,Kind,FromMchName,Pos,OtherInclusions)|RestNotIncluded], |
| 839 | | (debug_mode(off),OtherInclusions=[],UsePrefix='' -> true |
| 840 | | ; add_machine_prefix(UsedMach,UsePrefix,FullName), |
| 841 | | (OtherInclusions=[] |
| 842 | | -> ajoin(['machine ',Kind, ' in ', FromMchName, |
| 843 | | ' not included/imported anywhere else (will create dummy top-level machine to include it): '],Msg) |
| 844 | | ; ajoin_with_sep(OtherInclusions,',',I2), |
| 845 | | (UsePrefix='' |
| 846 | | -> ajoin(['machine ',Kind, ' in ', FromMchName, |
| 847 | | ' without prefix only included/imported with prefixes [',I2, |
| 848 | | '] (will create dummy top-level machine to include it): '],Msg) |
| 849 | | ; I2='' |
| 850 | | -> ajoin(['machine ',Kind, ' in ', FromMchName, |
| 851 | | ' only included/imported without prefix (will create dummy top-level machine to include it): '],Msg) |
| 852 | | ; ajoin(['machine ',Kind, ' in ', FromMchName, ' only included/imported with other prefixes [',I2, |
| 853 | | '] (will create dummy top-level machine to include it): '],Msg) |
| 854 | | ) |
| 855 | | ), |
| 856 | | add_message(bmachine_construction,Msg,FullName,Pos) |
| 857 | | ) |
| 858 | | ; Inc=[_] -> NotIncluded = RestNotIncluded, Ein=E1 |
| 859 | | ; Inc=[i(_M1,_,Pos1),i(_M2,_,Pos2)|_], |
| 860 | | NotIncluded = RestNotIncluded, |
| 861 | | translate_span(Pos1,PS1), |
| 862 | | translate_span(Pos2,PS2), |
| 863 | | (UsePrefix = '' |
| 864 | | -> ajoin([Kind,' machine ',UsedMach,' is included more than once: ',PS1,' and ',PS2],Msg) |
| 865 | | ; ajoin([Kind,' machine ',UsedMach,' is included more than once with prefix ',UsePrefix, |
| 866 | | ': ',PS1,' and ',PS2],Msg) |
| 867 | | ), |
| 868 | | Ein = [error(Msg,Pos)|E1] |
| 869 | | ), |
| 870 | | check_include_use(Rest,Machines,RestNotIncluded,E1,Eout). |
| 871 | | % extend_not_included_uses(+Uses,+Main,-Name,+Machines,-AllMachines): |
| 872 | | % Create a dummy machine that extends the main machine and extends or includes |
| 873 | | % all seen/used machines that are not included if such machines exist. |
| 874 | | % In case of refinement this is done for the whole refinement chain. |
| 875 | | % Uses: List of machine names and prefix usemch/2 of the machines that are used/seen but not included |
| 876 | | % Main: The name of the main machine |
| 877 | | % Name: The name of the generated dummy machine (or Main if no dummy machine is generated) |
| 878 | | % Machines: List of all specified Machines |
| 879 | | % AllMachines: List of all Machines + the new dummy machine(s) |
| 880 | | extend_not_included_uses([],Main,Main,Machines,Machines) :- !. |
| 881 | | extend_not_included_uses(Uses,Main,NewMainName,Machines,AllMachines) :- |
| 882 | | get_refinement_hierarchy(Main,Machines,RefChain), |
| 883 | | maplist(extend_not_included_uses2(Uses,Machines),RefChain,NewMachines), |
| 884 | | append(NewMachines,Machines,AllMachines), |
| 885 | | dummy_machine_name(Main,NewMainName), |
| 886 | | debug_format(19,'Creating dummy machine called ~w for main machine~n',[NewMainName]). |
| 887 | | extend_not_included_uses2(Uses,Machines,Name,DummyMachine) :- |
| 888 | | debug_format(19,'Creating dummy subsidiary machine for refined machine ~w~n',[Name]), |
| 889 | | create_dummy_machine(Name,Machines,Parameters,DummyParameters,Sections,DummyMachine), |
| 890 | | IncludeMain = machine_reference(none,Name,Parameters), |
| 891 | | ( get_preference(seen_machines_included,true) -> |
| 892 | | % extend just the main machine, include the used/seen machines |
| 893 | | References = [extends(none,[IncludeMain]),includes(none,UReferences)] |
| 894 | | ; |
| 895 | | % extends the main machine and all used/seen machines |
| 896 | | References = [extends(none,[IncludeMain|UReferences])]), |
| 897 | | maplist(find_using(Machines),Uses,UReferences,LUParameters), |
| 898 | | append([Parameters|LUParameters],DummyParameters), |
| 899 | | copy_raw_definitions(Name,Machines,OptDefs), |
| 900 | | % we store a flag "is_dummy" in the machine because we have a special case |
| 901 | | % later, see type_constraints/7. |
| 902 | | append([References,[is_dummy],OptDefs],Sections). |
| 903 | | create_dummy_machine(Name,Machines,Parameters,DummyParameters,Sections,DummyMachine) :- |
| 904 | | dummy_machine_name(Name,DummyName), |
| 905 | ? | get_raw_model_type(Name,Machines,ModelType), |
| 906 | | !, |
| 907 | | Header = machine_header(_,Name,Parameters), |
| 908 | | DummyHeader = machine_header(none,DummyName,DummyParameters), |
| 909 | ? | member(Machine,Machines), |
| 910 | ? | generate_raw_machine(Header,DummyHeader,ModelType,Sections,Machine,DummyMachine),!. |
| 911 | | |
| 912 | | generate_raw_machine(OldHeader,NewHeader,_,NewSections, |
| 913 | | abstract_machine(_, ModelType,OldHeader,_), |
| 914 | | abstract_machine(none,ModelType,NewHeader,NewSections)). |
| 915 | | generate_raw_machine(OldHeader,NewHeader,ModelType,NewSections, |
| 916 | | refinement_machine(_, OldHeader,_Abstract, _), |
| 917 | | abstract_machine(none,ModelType,NewHeader,NewSections)). |
| 918 | | generate_raw_machine(OldHeader,NewHeader,ModelType,NewSections, |
| 919 | | implementation_machine(_, OldHeader,_Abstract, _), |
| 920 | | abstract_machine(none,ModelType,NewHeader,NewSections)). |
| 921 | | |
| 922 | | dummy_machine_name(Name,DummyName) :- |
| 923 | | atom_concat('MAIN_MACHINE_FOR_',Name,DummyName). |
| 924 | | |
| 925 | | add_machine_prefix(UsedMch,'',Name) :- !, UsedMch=Name. |
| 926 | | add_machine_prefix(UsedMch,UsePrefix,Name) :- ajoin([UsePrefix,'.',UsedMch],Name). |
| 927 | | |
| 928 | | find_using(Machines,usemch_not_inc(UsedMch,UsePrefix,_,_,_,OtherPrefixes), |
| 929 | | machine_reference(none,Name,Arguments),Arguments) :- |
| 930 | | % invent a dummy prefix for the SEEN/USED machine which is not included |
| 931 | | % to avoid name-clashes with main machine |
| 932 | | % was using(U).U, but awkward to type in REPL |
| 933 | | % try machine name itself; unless somewhere else it is included this way |
| 934 | | (member(UsedMch,OtherPrefixes) % somewhere we already have an INCLUDES UsedMach.UsedMach |
| 935 | | -> DummyPrefix = using(UsedMch) |
| 936 | | ; DummyPrefix = UsedMch), |
| 937 | | ajoin([DummyPrefix,'.',UsedMch],Name0), |
| 938 | | add_machine_prefix(Name0,UsePrefix,Name), |
| 939 | ? | member(M,Machines), get_machine_parameters(M,UsedMch,Params,_),!, |
| 940 | | maplist(add_use_param,Params,Arguments). |
| 941 | | add_use_param(identifier(_,Param),identifier(none,Name)) :- |
| 942 | | ( is_upper_case(Param) -> |
| 943 | | ajoin(['Useparam(',Param,')'],Name) |
| 944 | | ; |
| 945 | | ajoin(['useparam(',Param,')'],Name)). |
| 946 | | |
| 947 | | % copy_raw_definitions(+Name,+Machines,-OptDefs): |
| 948 | | % Get the definitions section from a raw (untyped) machine |
| 949 | | % Name: Name of the machine |
| 950 | | % Machines: List of Machines |
| 951 | | % OptDefs: [] if no definitions are present or [Def] if a definition section Def is present |
| 952 | | copy_raw_definitions(Name,Machines,OptDefs) :- |
| 953 | ? | get_constructed_machine_name_and_body(M,Name,_,Sections), |
| 954 | | memberchk(M,Machines),!, |
| 955 | | Def = definitions(_,_), |
| 956 | | ( memberchk(Def,Sections) -> |
| 957 | | OptDefs = [Def] |
| 958 | | ; |
| 959 | | OptDefs = []). |
| 960 | | |
| 961 | | add_def_dependency_information(DefsIn,DefsOut,Ein,Eout) :- |
| 962 | | extract_def_name_set(DefsIn,DefNames,DN), |
| 963 | | maplist(add_def_dep(DN),DefsIn,Defs1), |
| 964 | | check_for_cyclic_def_dependency(Defs1,DefNames,DefsOut,Ein,Eout). |
| 965 | | |
| 966 | | extract_def_name_set(Defs,DefNames,DN) :- |
| 967 | | maplist(get_def_name,Defs,DefNames), |
| 968 | | maplist(to_mapset_entry,DefNames,DN1), |
| 969 | | list_to_avl(DN1,DN). |
| 970 | | |
| 971 | | get_def_name(Def,Name) :- arg(1,Def,Name). |
| 972 | | get_def_pos(Def,Pos) :- arg(3,Def,Pos). |
| 973 | | get_def_dependencies(Def,Dependencies) :- arg(6,Def,Dependencies). |
| 974 | | to_mapset_entry(Name,Name-true). |
| 975 | | |
| 976 | | add_def_dep(DN,In,Out) :- |
| 977 | | analyse_definition_dependencies(In,DN,Deps), |
| 978 | | In = definition_decl(Name,DefType,Pos,Args,RawExpr), |
| 979 | | Out = definition_decl(Name,DefType,Pos,Args,RawExpr,Deps). |
| 980 | | |
| 981 | | check_for_cyclic_def_dependency(Defs,DefNames,DefsOut,Ein,Eout) :- |
| 982 | | % check if we have a cyclic definition: |
| 983 | | create_definitions_avl(Defs,DefsAvl), |
| 984 | | search_for_cyclic_definition(DefNames,DefsAvl,[],Pos,RCycle),!, |
| 985 | | % if we have found a cyclic definition, generate an error message, ... |
| 986 | | reverse(RCycle,Cycle),add_arrows(Cycle,Msg0), |
| 987 | | ajoin(['Found cyclic definitions: '|Msg0],Msg), |
| 988 | | Ein = [error(Msg,Pos)|E1], |
| 989 | | % ... remove the definitions in the cycle (to prevent later infinite loops) ... |
| 990 | | exclude(definition_in_list(Cycle),Defs,Defs1), |
| 991 | | % ... and check the remaining definitions. |
| 992 | | check_for_cyclic_def_dependency(Defs1,DefNames,DefsOut,E1,Eout). |
| 993 | | check_for_cyclic_def_dependency(Defs,_DefNames,Defs,E,E). |
| 994 | | add_arrows([E],[E]) :- !. |
| 995 | | add_arrows([E|Erest],[E,'->'|Arest]) :- add_arrows(Erest,Arest). |
| 996 | | definition_in_list(List,Def) :- |
| 997 | | get_def_name(Def,Name),memberchk(Name,List). |
| 998 | | |
| 999 | | create_definitions_avl(Defs,DefsAvl) :- |
| 1000 | | maplist(split_def_name,Defs,Entries), |
| 1001 | | list_to_avl(Entries,DefsAvl). |
| 1002 | | split_def_name(Def,Name-Def) :- get_def_name(Def,Name). |
| 1003 | | |
| 1004 | | |
| 1005 | | % just a depth-first search to find a cycle |
| 1006 | | search_for_cyclic_definition(DefNames,Definitions,Visited,Pos,Cycle) :- |
| 1007 | ? | member(Name,DefNames),avl_fetch(Name,Definitions,Definition), |
| 1008 | | get_def_pos(Definition,Pos), |
| 1009 | | ( memberchk(Name,Visited) -> |
| 1010 | | Cycle = [Name|Visited] |
| 1011 | | ; |
| 1012 | | get_def_dependencies(Definition,Dependencies), |
| 1013 | | search_for_cyclic_definition(Dependencies,Definitions,[Name|Visited],_,Cycle) |
| 1014 | | ). |
| 1015 | | |
| 1016 | | :- assert_must_succeed(( |
| 1017 | | list_to_avl([def1-true,def2-true,def3-true,def4-true],DefNames), |
| 1018 | | analyse_definition_dependencies( |
| 1019 | | conjunct(none, |
| 1020 | | equals(none, |
| 1021 | | identifier(none,x), |
| 1022 | | identifier(none,def1)), |
| 1023 | | equals(none, |
| 1024 | | definition(none,def4, |
| 1025 | | [function(none, |
| 1026 | | identifier(none,def3), |
| 1027 | | integer(none,5))]), |
| 1028 | | identifier(y))),DefNames,Defs), |
| 1029 | | Defs==[def1,def3,def4] |
| 1030 | | )). |
| 1031 | | % analyse_definition_dependencies(+Expr,+DefinitionNames,Deps): |
| 1032 | | % Expr: raw (i.e. untyped) expression to analyse |
| 1033 | | % DN: AVL set (i.e. mapping from elements to 'true') of the names of the definitions |
| 1034 | | % This is needed to decide if an identifier is a reference to a definition |
| 1035 | | % Deps: A list of used definitions (a list of their names) |
| 1036 | | analyse_definition_dependencies(Expr,DN,Deps) :- |
| 1037 | | analyse_definition_dependencies2(Expr,DN,Unsorted,[]), |
| 1038 | | sort(Unsorted,Deps). |
| 1039 | | analyse_definition_dependencies2(VAR,_DN) --> {var(VAR)},!, |
| 1040 | | {add_internal_error('Variable DEFINITION expression in',analyse_definition_dependencies)}. |
| 1041 | | analyse_definition_dependencies2([Expr|Rest],DN) --> |
| 1042 | | !, analyse_definition_dependencies2(Expr,DN), |
| 1043 | | analyse_definition_dependencies2(Rest,DN). |
| 1044 | | analyse_definition_dependencies2(definition(_Pos,Name,Args),DN) --> |
| 1045 | | !,[Name],analyse_definition_dependencies2(Args,DN). |
| 1046 | | analyse_definition_dependencies2(identifier(_Pos,Name),DN) --> |
| 1047 | | {avl_fetch(Name,DN),!},[Name]. |
| 1048 | | analyse_definition_dependencies2(Expr,DN) --> |
| 1049 | | { compound(Expr),functor(Expr,_Functor,Arity),!}, |
| 1050 | | analyse_definition_dependencies_arg(2,Arity,Expr,DN). |
| 1051 | | analyse_definition_dependencies2(_Expr,_DN) --> []. |
| 1052 | | |
| 1053 | | analyse_definition_dependencies_arg(I,Arity,Expr,DN) --> |
| 1054 | | { I =< Arity,!,arg(I,Expr,Arg),I2 is I+1 }, |
| 1055 | | analyse_definition_dependencies2(Arg,DN), |
| 1056 | | analyse_definition_dependencies_arg(I2,Arity,Expr,DN). |
| 1057 | | analyse_definition_dependencies_arg(_I,_Arity,_Expr,_DN) --> []. |
| 1058 | | |
| 1059 | | |
| 1060 | | :- use_module(tools_positions, [get_position_filenumber/2]). |
| 1061 | | |
| 1062 | | get_constructed_machine_name(MachineTerm,Name) :- get_constructed_machine_name_and_body(MachineTerm,Name,_Pos,_). |
| 1063 | | % name and pos; pos can be used for filenumber |
| 1064 | | get_constructed_machine_name_and_filenumber(MachineTerm,Name,Filenumber) :- |
| 1065 | | get_constructed_machine_name_and_body(MachineTerm,Name,Pos,_), |
| 1066 | | (get_position_filenumber(Pos,FN) -> Filenumber=FN ; Filenumber=unknown). |
| 1067 | | get_constructed_machine_name_and_body(abstract_machine(_,_ModelType,machine_header(Pos,Name,_),Body),Name,Pos,Body). |
| 1068 | | get_constructed_machine_name_and_body(refinement_machine(_,machine_header(Pos,Name,_),_Abstract,Body),Name,Pos,Body). |
| 1069 | | get_constructed_machine_name_and_body(implementation_machine(_,machine_header(Pos,Name,_),_Abstract,Body),Name,Pos,Body). |
| 1070 | | |
| 1071 | | refines(refinement_machine(_,machine_header(_,_Name,_),Abstract,_Body),Abstract). |
| 1072 | | refines(implementation_machine(_,machine_header(_,_Name,_),Abstract,_Body),Abstract). |
| 1073 | | |
| 1074 | | get_machine_parameters(abstract_machine(Pos,_ModelType,machine_header(_,Name,Parameters),_),Name,Parameters,Pos). |
| 1075 | | get_machine_parameters(refinement_machine(Pos,machine_header(_,Name,Parameters),_,_),Name,Parameters,Pos). |
| 1076 | | get_machine_parameters(implementation_machine(Pos,machine_header(_,Name,Parameters),_,_),Name,Parameters,Pos). |
| 1077 | | |
| 1078 | | get_raw_model_type(Main,Machines,ModelType) :- |
| 1079 | ? | get_constructed_machine_name_and_body(M,Main,_,_), |
| 1080 | | memberchk(M,Machines), |
| 1081 | | ( refines(M,RefName) -> |
| 1082 | ? | get_raw_model_type(RefName,Machines,ModelType) |
| 1083 | | ; |
| 1084 | | M = abstract_machine(_,ModelType,_,_)). |
| 1085 | | |
| 1086 | | some_machine_name_body(Machines,M,Name,Body) :- |
| 1087 | ? | member(M,Machines), |
| 1088 | | get_constructed_machine_name_and_body(M,Name,_,Body). |
| 1089 | | |
| 1090 | | use_usage(Machines,Pos,Kind,UsedMch,UsePrefix,FromMchName) :- |
| 1091 | ? | some_machine_name_body(Machines,_,FromMchName,Body), |
| 1092 | ? | ( member(sees(Pos,R),Body),Kind=seen |
| 1093 | ? | ; member(uses(Pos,R),Body),Kind=used), |
| 1094 | ? | member(identifier(_,PrefixUse),R), |
| 1095 | | split_prefix(PrefixUse,UsePrefix,UsedMch). |
| 1096 | | % include_usage/4 checks if there is any machine in Machines that |
| 1097 | | % includes/extends/imports the used machine |
| 1098 | | % include_usage(+Machines,+Use,-Name): |
| 1099 | | % Machines: The list of machines |
| 1100 | | % UsedMch: The name of the used machine |
| 1101 | | % UsePrefix: the prefix with which the machine is used |
| 1102 | | % Name: The name of the machine that imports the used machine |
| 1103 | | % include_usage/4 fails if there is not such an import |
| 1104 | | include_usage(Machines,UsedMch,UsePrefix,MachName,Pos) :- |
| 1105 | ? | some_machine_name_body(Machines,_,MachName,Body), |
| 1106 | ? | ( member(includes(_,R),Body) |
| 1107 | ? | ; member(extends(_,R), Body) |
| 1108 | ? | ; member(imports(_,R), Body)), |
| 1109 | ? | member(machine_reference(Pos,PrefixRef,_),R), |
| 1110 | | % The name could be prefixed, we need it without prefix: |
| 1111 | | split_prefix(PrefixRef,UsePrefix,UsedMch). |
| 1112 | | |
| 1113 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 1114 | | % uses and sees clauses |
| 1115 | | |
| 1116 | | % returns a list of references to used and seen machines |
| 1117 | | % the machines contain only identifier sections and |
| 1118 | | % the identifier are prefixed accordingly |
| 1119 | | % all identifier are marked as coming from a seen/used machine |
| 1120 | | use_and_see_machines(Sections,Machines,References) :- |
| 1121 | | get_uses_and_sees(Sections,Uses,Sees), |
| 1122 | ? | maplist(use_or_see_machine(used,Machines),Uses,Used), |
| 1123 | ? | maplist(use_or_see_machine(seen,Machines),Sees,Seen), |
| 1124 | | append(Used,Seen,References). |
| 1125 | | |
| 1126 | | % get uses and sees sections from the untyped machines |
| 1127 | | get_uses_and_sees(Sections,Uses,Sees) :- |
| 1128 | | get_uses_or_sees2(sees,Sections,Sees), |
| 1129 | | get_uses_or_sees2(uses,Sections,Uses). |
| 1130 | | get_uses_or_sees2(Mode,Sections,US) :- |
| 1131 | | optional_rawmachine_section(Mode,Sections,[],US1), |
| 1132 | | findall(I,member(identifier(_,I),US1),US). |
| 1133 | | |
| 1134 | | use_or_see_machine(Mode,TypedMachines,Ref,ref(Mode,Result)) :- |
| 1135 | | split_prefix(Ref,Prefix,Name), |
| 1136 | | memberchk(machine(Name,Typed),TypedMachines), |
| 1137 | | create_machine(Name,Empty), |
| 1138 | ? | use_sections([sets],Mode,'',Name,Typed,Empty,M1), |
| 1139 | ? | use_sections([constants,variables,promoted],Mode,Prefix,Name,Typed,M1,Result). |
| 1140 | | use_sections(Sections,Mode,Prefix,MName,Typed,Old,New) :- |
| 1141 | | expand_shortcuts(Sections,AllSections), |
| 1142 | ? | foldl(use_section(Mode,Prefix,MName,Typed),AllSections,Old,New). |
| 1143 | | use_section(Mode,Prefix,MName,Machine,Section,OldM,NewM) :- |
| 1144 | ? | get_section_texprs(Section,Machine,Identifiers), |
| 1145 | | write_section(Section,NewIds,OldM,NewM), |
| 1146 | | ( Prefix='' -> |
| 1147 | | Ids1=Identifiers |
| 1148 | | ; |
| 1149 | | prefix_identifiers(Identifiers,Prefix,Renamings), |
| 1150 | | rename_bt_l(Identifiers,Renamings,Ids1)), |
| 1151 | | maplist(add_use_info_to_identifier(Mode,MName),Ids1,NewIds). |
| 1152 | | add_use_info_to_identifier(Mode,Name,TExpr,New) :- |
| 1153 | | get_texpr_id(TExpr,PId), get_texpr_type(TExpr,Type), |
| 1154 | | get_texpr_id(New,PId), get_texpr_type(New,Type), |
| 1155 | | split_prefix(PId,_Prefix,Id), |
| 1156 | | get_texpr_info(TExpr,Info), |
| 1157 | | get_texpr_info(New,[usesee(Name,Id,Mode)|Info]). |
| 1158 | | |
| 1159 | | |
| 1160 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 1161 | | % add a section to the machine that describes the linking of |
| 1162 | | % parameters and arguments |
| 1163 | | add_link_constraints(Includes,MType,Parameters,RefMachines,Old,New,Ein,Eout) :- |
| 1164 | | AllRefMachines = [ref(local,Old)|RefMachines], |
| 1165 | | extract_parameter_types(RefMachines,NonGroundExceptions), |
| 1166 | | foldl(add_link_section(MType,Parameters,AllRefMachines,NonGroundExceptions), |
| 1167 | | Includes,Links/Ein,[]/Eout), % TO DO: Daniel check if [] is correct here |
| 1168 | | %print(conjunct_predicates(Links,Link)),nl, |
| 1169 | | conjunct_predicates(Links,Link), |
| 1170 | | select_section(constraints,OConstraints,NConstraints,Old,New), |
| 1171 | | conjunct_predicates([Link,OConstraints],NConstraints). |
| 1172 | | add_link_section(MType,Parameters,RefMachines,NonGroundExceptions, |
| 1173 | | machine_reference(Pos,Ref,Args),Links/Ein,RLinks/Eout) :- |
| 1174 | | visible_env(MType,includes,RefMachines,Env,Ein,E1), |
| 1175 | | memberchk(parameters(Ref,TParameters),Parameters), |
| 1176 | | ( same_length(TParameters, Args) -> |
| 1177 | | get_texpr_types(TParameters,Types), |
| 1178 | | btype_ground_dl(Args,Env,NonGroundExceptions,Types,TArgs,E1,Eout), |
| 1179 | | maplist(create_plink_equality,TParameters,TArgs,LLinks) |
| 1180 | | ; |
| 1181 | | E1 = [error('wrong number of machine arguments',Pos)|Eout], |
| 1182 | | LLinks = []), |
| 1183 | | append(LLinks,RLinks,Links). |
| 1184 | | create_plink_equality(P,A,E) :- |
| 1185 | | create_texpr(equal(P,A),pred,[parameterlink],E). |
| 1186 | | |
| 1187 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 1188 | | % type machines |
| 1189 | | |
| 1190 | | type_machine(Header,Name,MType,RawMachine,RefMachines,TypedMachine,NewRefMachines,Ein,Eout) :- |
| 1191 | | Header = machine_header(_,Name,_), |
| 1192 | | create_machine(Name,Empty), |
| 1193 | | % (optional) definitions |
| 1194 | | extract_definitions(RawMachine,Empty,DefsOnly,Ein,E0), |
| 1195 | | debug_stats(extracted_definitions(Name)), |
| 1196 | | % create the identifiers that will be typed, |
| 1197 | | % Local will contain the identifier sections |
| 1198 | | create_id_sections(Header,RawMachine,Name,DefsOnly,Local), |
| 1199 | | debug_stats(created_identifier_sections(Name)), |
| 1200 | ? | create_freetypes(RawMachine,MType,RefMachines,Local,Local1,E0,E1), |
| 1201 | | % in case of a refinement, check if all newly defined operations are refinements |
| 1202 | | link_to_refinement(MType,Name,RawMachine,RefMachines,Local1,Local2,E1,E2), |
| 1203 | | % extract types that can be variables |
| 1204 | | debug_stats(created_link_to_refinement(Name)), |
| 1205 | | extract_parameter_types([ref(local,Local2)|RefMachines],NonGroundExceptions), |
| 1206 | | % check for a VALUES clause. They are a little bit tricky because they can replace |
| 1207 | | % already defined deferred sets by integers or other deferred sets |
| 1208 | | process_values_section(MType,RawMachine,NonGroundExceptions, |
| 1209 | | Local2/E2/RefMachines,Local3/E3/NewRefMachines), |
| 1210 | | % type-check the other sections (properties, invariant, operation_bodies, etc) |
| 1211 | ? | type_sections(RawMachine,MType,[ref(local,Local3)|NewRefMachines],NonGroundExceptions, |
| 1212 | | Name,E3,Eout,Local3,TypedMachine). |
| 1213 | | |
| 1214 | | % extract definitions from a definition file |
| 1215 | | extract_only_definitions(MainName,RawMachine,DefsOnlyMachine,FinalErrors) :- |
| 1216 | | create_machine(MainName,Empty), |
| 1217 | | extract_definitions(RawMachine,Empty,DefsOnlyMachine,Errors,[]), |
| 1218 | | sort(Errors,FinalErrors), |
| 1219 | | add_all_perrors(FinalErrors). |
| 1220 | | |
| 1221 | | extract_definitions(RawMachine,In,Out,Ein,Eout) :- |
| 1222 | | optional_rawmachine_section(definitions,RawMachine,[],AllDefinitions), |
| 1223 | | write_section(definitions,Definitions,In,Out), |
| 1224 | | % remove all references to definition files from definitions |
| 1225 | | exclude(is_file_definition,AllDefinitions,Definitions1), |
| 1226 | | % replace expression_definition(...) by defintion(expression,...), etc. |
| 1227 | | change_definition_style(Definitions1,Definitions2), |
| 1228 | | % analyse dependencies |
| 1229 | | add_def_dependency_information(Definitions2,Definitions3,Ein,Eout), |
| 1230 | | % replace external function definitions by calls to the external function |
| 1231 | | replace_external_declarations(Definitions3,Definitions). |
| 1232 | | |
| 1233 | | is_file_definition(file_definition(_Pos,_Filename)). |
| 1234 | | |
| 1235 | | change_definition_style(DefsIn,DefsOut) :- |
| 1236 | | maplist(change_definition_style2,DefsIn,DefsOut). |
| 1237 | | change_definition_style2(conversion(Pos,InnerDef),definition_decl(Name,DefType,InnerPos,Args,conversion(Pos,RawExpr))) :- |
| 1238 | | change_definition_style2(InnerDef,definition_decl(Name,DefType,InnerPos,Args,RawExpr)). |
| 1239 | | change_definition_style2(Def,definition_decl(Name,DefType,Pos,Args,RawExpr)) :- |
| 1240 | | Def =.. [Functor,Pos,Name,Args,RawExpr], |
| 1241 | | maplist(check_def_argument(Name,Pos),Args), |
| 1242 | | map_def_functor(Functor,DefType). |
| 1243 | | map_def_functor(expression_definition,expression). |
| 1244 | | map_def_functor(substitution_definition,substitution). |
| 1245 | | map_def_functor(predicate_definition,predicate). |
| 1246 | | |
| 1247 | | % check formal arguments of definitions , e.g., here xx in square2 is not an identifier: xx == 20; square2(xx) == xx*xx |
| 1248 | | check_def_argument(_,_,identifier(_,_)) :- !. |
| 1249 | | check_def_argument(DefName,DefPos,definition(_,ID,_)) :- !, |
| 1250 | | tools:ajoin(['Formal parameter ', ID, ' is a definition call in Definition: '],Msg), |
| 1251 | | add_error(bmachine_construction,Msg,DefName,DefPos). |
| 1252 | | check_def_argument(DefName,DefPos,FP) :- !, |
| 1253 | | tools:ajoin(['Formal parameter ', FP, ' is not an identifier in Definition: '],Msg), |
| 1254 | | add_error(bmachine_construction,Msg,DefName,DefPos). |
| 1255 | | |
| 1256 | | replace_external_declarations(Definitions,NewDefs) :- |
| 1257 | | split_list(is_external_declaration,Definitions,ExtFunctionDecls,RestDefs), |
| 1258 | | foldl(replace_external_declaration,ExtFunctionDecls,RestDefs,NewDefs). |
| 1259 | | is_external_declaration(definition_decl(DefName,expression,_Pos,_Params,_Def,_Dependencies)) :- |
| 1260 | ? | external_name(DefName,ExtType,_ExpectedDefType,_ExtCall,FunName), |
| 1261 | | debug_format(4,'external ~w declared: ~w~n', [ExtType,FunName]). |
| 1262 | | external_name(DefName,_,_,_,_) :- |
| 1263 | | \+ atom(DefName),!, |
| 1264 | | add_internal_error('Non-atomic DEFINITION id:',DefName),fail. |
| 1265 | | external_name(DefName,function,expression,external_function_call,FunName) :- |
| 1266 | | atom_concat('EXTERNAL_FUNCTION_',FunName,DefName). |
| 1267 | | external_name(DefName,predicate,predicate,external_pred_call,FunName) :- |
| 1268 | | atom_concat('EXTERNAL_PREDICATE_',FunName,DefName). |
| 1269 | | external_name(DefName,substitution,substitution,external_subst_call,FunName) :- |
| 1270 | | atom_concat('EXTERNAL_SUBSTITUTION_',FunName,DefName). |
| 1271 | | |
| 1272 | | replace_external_declaration(definition_decl(DefName,expression,DefPos,TypeParams,Decl,_Deps),In,Out) :- |
| 1273 | | OldDefinition = definition_decl(FunName,ExpectedDefType,Pos,FunParams,FunDef,Deps), |
| 1274 | | NewDefinition = definition_decl(FunName,ExpectedDefType,Pos,FunParams,ExtCall,Deps), |
| 1275 | ? | ( external_name(DefName,_ExtType,ExpectedDefType,ExtCallFunctor,FunName), |
| 1276 | | ExtCall =.. [ExtCallFunctor,Pos,FunName,FunParams,FunDef, |
| 1277 | | rewrite_protected(TypeParams),rewrite_protected(Decl)], |
| 1278 | | selectchk(OldDefinition,In,NewDefinition,Out) -> |
| 1279 | | assert_external_procedure_used(FunName) |
| 1280 | | ; external_name(DefName,ExpectedKind,_,_,FunName), |
| 1281 | | selectchk(definition_decl(FunName,OtherDefType,_OtherPos,_,_,_),In,_,_),!, |
| 1282 | | % definition found for different type of external function |
| 1283 | | ajoin(['No DEFINITION associated with external ',ExpectedKind, |
| 1284 | | ' (but definition as ',OtherDefType,' exists):'],Msg), |
| 1285 | | add_error(replace_external_declaration,Msg,DefName,DefPos), Out=In |
| 1286 | | ; external_name(DefName,ExpectedKind,_,_,FunName) -> |
| 1287 | | % no definition found for external function |
| 1288 | | ajoin(['No DEFINITION associated with external ',ExpectedKind,':'],Msg), |
| 1289 | | add_error(replace_external_declaration,Msg,DefName,DefPos), Out=In |
| 1290 | | ; % no definition found for external function |
| 1291 | | ajoin(['No DEFINITION associated with:'],Msg), |
| 1292 | | add_error(replace_external_declaration,Msg,DefName,DefPos), Out=In |
| 1293 | | ). |
| 1294 | | |
| 1295 | | :- use_module(external_function_declarations, |
| 1296 | | [external_function_library/2, safe_external_function_library/2, |
| 1297 | | get_external_function_definition/3]). |
| 1298 | | % store external definitions of a given library in the type environment, e.g. for debugging in Repl or VisB |
| 1299 | | % Library could be "LibraryStrings.def" |
| 1300 | | |
| 1301 | | store_ext_defs(Library,In,Out) :- |
| 1302 | | (Library = all_available_libraries |
| 1303 | | -> findall(extfun(Id,Lib),external_function_library(Id,Lib),Ids) |
| 1304 | | ; Library = safe_available_libraries |
| 1305 | | -> findall(extfun(Id,Lib),safe_external_function_library(Id,Lib),Ids) |
| 1306 | | ; findall(extfun(Id,Library),external_function_library(Id,Library),Ids) |
| 1307 | | ), |
| 1308 | | (Ids=[] -> add_warning(store_ext_defs,'No external functions found for library: ',Library) ; true), |
| 1309 | | foldl(store_ext_def,Ids,In,Out). |
| 1310 | | |
| 1311 | | store_ext_def(extfun(Id,Library),In,Out) :- env_lookup_type(Id,In,_),!, |
| 1312 | | debug_println(4,not_storing_ext_def(Id,Library)), % already imported from stdlib or user has other definition |
| 1313 | | Out=In. |
| 1314 | | store_ext_def(extfun(FunName,Library),In,Out) :- |
| 1315 | ? | get_external_function_definition(FunName,Library,DEFINITION),!, |
| 1316 | | debug_println(4,storing_ext_def(FunName,Library)), |
| 1317 | | env_store(FunName,DEFINITION,[loc('automatically included',Library,definitions)],In,Out). |
| 1318 | | store_ext_def(extfun(Id,_),In,Out) :- |
| 1319 | | add_internal_error('Cannot add external definition: ',Id), |
| 1320 | | Out=In. |
| 1321 | | |
| 1322 | | |
| 1323 | | % store which machine promotes which operations |
| 1324 | | :- dynamic machine_promotes_operations/2, machine_hides_unpromoted_operation/4, machine_global_order/1. |
| 1325 | | |
| 1326 | | reset_bmachine_construction :- |
| 1327 | | retractall(machine_promotes_operations(_,_)), |
| 1328 | | retractall(machine_hides_unpromoted_operation(_,_,_,_)), |
| 1329 | | retractall(abstract_variable_removed_in(_,_,_)), |
| 1330 | | retractall(machine_global_order(_)), |
| 1331 | | reset_external_procedure_used. |
| 1332 | | |
| 1333 | | |
| 1334 | | % maybe this information should be stored somewhere else ?? |
| 1335 | | :- dynamic external_procedure_used/1. |
| 1336 | | reset_external_procedure_used :- retractall(external_procedure_used(_)). |
| 1337 | | assert_external_procedure_used(FunName) :- |
| 1338 | | (external_procedure_used(FunName) -> true ; assertz(external_procedure_used(FunName))). |
| 1339 | | |
| 1340 | | link_to_refinement(machine,_,_RawMachine,_RefMachines,Local,Local,Errors,Errors). |
| 1341 | | link_to_refinement(refinement,Name,RawMachine,RefMachines,Local,NewLocal,Ein,Eout) :- |
| 1342 | | link_to_refinement(implementation,Name,RawMachine,RefMachines,Local,NewLocal,Ein,Eout). |
| 1343 | | link_to_refinement(implementation,Name,RawMachine,RefMachines,Local,NewLocal,Ein,Eout) :- |
| 1344 | | link_to_refinement2(implementation,Name,RawMachine,RefMachines,Local,NewLocal,Ein,Eout). |
| 1345 | | link_to_refinement2(_MType,Name,RawMachine,RefMachines,Local,NewLocal,Ein,Eout) :- |
| 1346 | | memberchk(ref(abstraction,AbstractMachine),RefMachines), |
| 1347 | | copy_constraints(Local,AbstractMachine,NewLocal,Ein,E1), |
| 1348 | | type_vars_in_refinement(AbstractMachine,NewLocal), |
| 1349 | | type_refinement_operations(Name,RawMachine,AbstractMachine,NewLocal,E1,Eout). |
| 1350 | | |
| 1351 | | copy_constraints(Local,AbstractMachine,NewLocal,Ein,Eout) :- |
| 1352 | | get_section(parameters,Local,LocalParameters), |
| 1353 | | get_section(parameters,AbstractMachine,AbstractParameters), |
| 1354 | | check_if_equal_identifiers(LocalParameters,AbstractParameters,Ein,Eout,Local), |
| 1355 | | get_section(constraints,AbstractMachine,Constraints), |
| 1356 | | write_section(constraints,Constraints,Local,NewLocal). |
| 1357 | | check_if_equal_identifiers(Local,Abstract,Ein,Eout,LocalMachine) :- |
| 1358 | | ( same_length(Local,Abstract) -> |
| 1359 | | foldl(check_if_equal_identifiers2,Local,Abstract,Ein,Eout) |
| 1360 | | ; |
| 1361 | | get_texpr_ids(Abstract,AIDs), |
| 1362 | | get_machine_name(LocalMachine,MachName), |
| 1363 | | ajoin(['Refinement ',MachName,' must have same Parameters ', AIDs,' as abstract Machine'],Msg), |
| 1364 | | Ein = [error(Msg,none)|Eout] |
| 1365 | | ). |
| 1366 | | check_if_equal_identifiers2(LParam,AParam,Ein,Eout) :- |
| 1367 | | get_texpr_id(LParam,LName), |
| 1368 | | get_texpr_id(AParam,AName), |
| 1369 | | ( LName = AName -> |
| 1370 | | % type in refinement is the same as in the abstraction |
| 1371 | | get_texpr_type(LParam,Type), |
| 1372 | | get_texpr_type(AParam,Type), |
| 1373 | | Ein=Eout |
| 1374 | | ; |
| 1375 | | get_texpr_pos(LParam,Pos), |
| 1376 | | ajoin(['Parameter ',LName,' of refinement machine must be ', |
| 1377 | | AName,' like in the abstract machine'],Msg), |
| 1378 | | Ein = [error(Msg,Pos)|Eout] |
| 1379 | | ). |
| 1380 | | |
| 1381 | | % in case of a refinement, give variables the same type as in the abstract machine |
| 1382 | | % the same for constants |
| 1383 | | type_vars_in_refinement(AbstractMachine,ConcreteMachine) :- |
| 1384 | | pass_type(AbstractMachine,[abstract_variables,concrete_variables], |
| 1385 | | ConcreteMachine,[abstract_variables,concrete_variables]), |
| 1386 | | pass_type(AbstractMachine,[abstract_constants,concrete_constants], |
| 1387 | | ConcreteMachine,[abstract_constants,concrete_constants]). |
| 1388 | | |
| 1389 | | % pass the type from variables in Sections1 of Machine1 to |
| 1390 | | % the variables of the same name in Sections2 of Machine2 |
| 1391 | | % Machine1,Machine2: a machine |
| 1392 | | % Sections1, Sections2: a list of section names |
| 1393 | | pass_type(Machine1,Sections1,Machine2,Sections2) :- |
| 1394 | | get_sections_and_append(Sections1,Machine1,Vars1), |
| 1395 | | get_sections_and_append(Sections2,Machine2,Vars2), |
| 1396 | | maplist(pass_type2(Vars2),Vars1). |
| 1397 | | get_sections_and_append([],_M,[]). |
| 1398 | | get_sections_and_append([Sec|RestSections],M,R) :- |
| 1399 | | get_section(Sec,M,L), append(L,Rest,R), |
| 1400 | | get_sections_and_append(RestSections,M,Rest). |
| 1401 | | pass_type2(DstVariables,SrcVariable) :- |
| 1402 | | get_texpr_id(SrcVariable,Id), |
| 1403 | | get_texpr_id(DstVariable,Id), |
| 1404 | | ( memberchk(DstVariable,DstVariables) -> |
| 1405 | | get_texpr_type(DstVariable,Type), |
| 1406 | | get_texpr_type(SrcVariable,Type) |
| 1407 | | ; |
| 1408 | | true). |
| 1409 | | |
| 1410 | | |
| 1411 | | % in case of a refinement, check if the defined operations are already defined |
| 1412 | | % in the abstract machine and copy that type. |
| 1413 | | type_refinement_operations(MName,RawMachine,AbstractMachine,Local,Ein,Eout) :- |
| 1414 | | get_operation_identifiers(RawMachine,Operations), |
| 1415 | | type_refinement_operations2(Operations,MName,Local,AbstractMachine,Ein,Eout). |
| 1416 | | type_refinement_operations2([],_,_,_AbstractMachine,Errors,Errors). |
| 1417 | | type_refinement_operations2([Op|Rest],MName,M,AbstractMachine,Ein,Eout) :- |
| 1418 | | get_abstract_operation_name_wo_infos(Op,AOp), |
| 1419 | | %print(treating_refines_operation(Op,AOp)),nl, |
| 1420 | | get_texpr_pos(Op,Pos), |
| 1421 | | % look up the abstract definition |
| 1422 | | get_abstract_op(AOp,MName,AbstractMachine,Pos,Ein,E1), |
| 1423 | | % store the type in the identifier section |
| 1424 | | copy_texpr_wo_info(Op,SOp), |
| 1425 | | ( get_section(promoted,M,Operations), |
| 1426 | | memberchk(SOp, Operations) -> true |
| 1427 | | ; get_section(unpromoted,M,Operations), % this is probably a LOCAL_OPERATION |
| 1428 | | memberchk(SOp, Operations) -> true |
| 1429 | | ; add_error(btypechecker,'Could not find operation for type checking:',Op),fail |
| 1430 | | ), |
| 1431 | | % do the rest |
| 1432 | | type_refinement_operations2(Rest,MName,M,AbstractMachine,E1,Eout). |
| 1433 | | % looks up the type of the operator in an abstract machine |
| 1434 | | get_abstract_op(Op,_,Abstraction,_,Errors,Errors) :- |
| 1435 | | % look for the operation in promoted and unpromoted |
| 1436 | ? | ( get_section(promoted,Abstraction,AbstractOps), member(Op,AbstractOps) |
| 1437 | | ; get_section(unpromoted,Abstraction,AbstractOps), member(Op,AbstractOps) ), |
| 1438 | | % forget alternatives |
| 1439 | | !. |
| 1440 | | get_abstract_op(Op,_,_Abstraction,_Pos,Errors,Errors) :- |
| 1441 | | % we might allow new operations |
| 1442 | | get_preference(allow_new_ops_in_refinement,true),!, % ALLOW_NEW_OPERATIONS_IN_REFINEMENT |
| 1443 | | get_texpr_type(Op,op(_,_)). |
| 1444 | | get_abstract_op(Op,MName,AbstractMachine,Pos,[warning(Msg,Pos)|Eout],Eout) :- |
| 1445 | | % in case we do not find the operation, store an error |
| 1446 | | get_texpr_id(Op,op(Id)), |
| 1447 | | get_machine_name(AbstractMachine,Name), |
| 1448 | | ajoin(['operation ', Id, ' from ', MName, ' is not defined in the abstract machine ',Name, |
| 1449 | | ' (set ALLOW_NEW_OPERATIONS_IN_REFINEMENT to TRUE to allow this)'], Msg). |
| 1450 | | % copy a typed expression without the additional information (just expression and type) |
| 1451 | | copy_texpr_wo_info(A,B) :- |
| 1452 | | % copy the expression and type, the additional information may be different |
| 1453 | | get_texpr_expr(A,Expr),get_texpr_expr(B,Expr), |
| 1454 | | get_texpr_type(A,Type),get_texpr_type(B,Type). |
| 1455 | | |
| 1456 | | get_abstract_operation_name_wo_infos(b(_,Type,Infos),Res) :- |
| 1457 | | memberchk(refines_operation(RefID),Infos),!, % renaming occurs: |
| 1458 | | Res = b(identifier(op(RefID)),Type,_). |
| 1459 | | get_abstract_operation_name_wo_infos(ID,Copy) :- copy_texpr_wo_info(ID,Copy). |
| 1460 | | |
| 1461 | | create_id_sections(Header,RawMachine,Name) --> |
| 1462 | | create_id_sections_header(Header), |
| 1463 | | %{print(created_header(Name)),nl}, |
| 1464 | | create_set_sections(RawMachine,Name), |
| 1465 | | %{print(created_set(Name)),nl}, |
| 1466 | | create_constants_sections(RawMachine), |
| 1467 | | %{print(created_constants(Name)),nl}, |
| 1468 | | create_variables_sections(RawMachine), |
| 1469 | | %{print(created_variables(Name)),nl}, |
| 1470 | | create_operations_sections(RawMachine,Name). |
| 1471 | | |
| 1472 | | extract_parameter_types(MachineRefs,ParameterTypes) :- |
| 1473 | | maplist(extract_parameter_types2,MachineRefs,ParameterTypesL), |
| 1474 | | append(ParameterTypesL,ParameterTypes). |
| 1475 | | extract_parameter_types2(ref(_,Machine),ParameterTypes) :- |
| 1476 | | get_section(parameters,Machine,VisibleParams), |
| 1477 | | get_section(internal_parameters,Machine,InternalParams), |
| 1478 | | append(VisibleParams,InternalParams,Params), |
| 1479 | | include(is_a_parameter_set,Params,ParameterSets), |
| 1480 | | maplist(get_texpr_set_type,ParameterSets,ParameterTypes). |
| 1481 | | is_a_parameter_set(TExpr) :- |
| 1482 | | get_texpr_info(TExpr,Info), |
| 1483 | | memberchk(parameter_set,Info). |
| 1484 | | |
| 1485 | | type_sections(RawMachine,MachineType,RefMachines,NonGroundExceptions,Name,Ein,Eout) --> |
| 1486 | | {debug_stats('TYPING CONSTRAINTS'(Name))}, |
| 1487 | | type_constraints(MachineType,Name,RawMachine,RefMachines,NonGroundExceptions,Ein,E1), |
| 1488 | | % Maybe the VALUES section must be moved up later because it may be used to |
| 1489 | | % substitute types (e.g. deferred sets to integers) for later use |
| 1490 | | {debug_stats('TYPING PROPERTIES'(Name))}, |
| 1491 | | type_section_with_single_predicate(properties,Name,[constants],MachineType,RawMachine,RefMachines,NonGroundExceptions,E1,E2), |
| 1492 | | {debug_stats('TYPING INVARIANT'(Name))}, |
| 1493 | | type_section_with_single_predicate(invariant,Name,[variables],MachineType,RawMachine,RefMachines,NonGroundExceptions,E2,E3), |
| 1494 | | {debug_stats('TYPING ASSERTIONS'(Name))}, |
| 1495 | | type_section_with_predicate_list(assertions,[],MachineType,RawMachine,RefMachines,NonGroundExceptions,E3,E4), |
| 1496 | | {debug_stats('TYPING INITIALISATION'(Name))}, |
| 1497 | ? | type_initialisation_section(RawMachine,Name,MachineType,RefMachines,NonGroundExceptions,E4,E5), |
| 1498 | | {debug_stats('TYPING OPERATIONS'(Name))}, |
| 1499 | | type_operations_section(RawMachine,MachineType,RefMachines,NonGroundExceptions,E5,Eout), |
| 1500 | | {debug_stats('FINISHED TYPING SECTIONS'(Name))}. |
| 1501 | | |
| 1502 | | % skip this section, it is copied from the abstract machine and |
| 1503 | | % does not need to be typed again |
| 1504 | | type_constraints(refinement,_,_RawMachine,_RefMachines,_NonGroundExceptions,Errors,Errors) --> !. |
| 1505 | | type_constraints(implementation,_,_RawMachine,_RefMachines,_NonGroundExceptions,Errors,Errors) --> !. |
| 1506 | | type_constraints(machine,Name,RawMachine,RefMachines,NonGroundExceptions,Ein,Eout) --> |
| 1507 | | % if the machine is a dummy machine (in presence of a not included seen or used |
| 1508 | | % machine), we must omit the check that the (lower case) parameters are all typed. |
| 1509 | | % We can assume that they are properly typed by the included machine. |
| 1510 | | {(is_dummy_machine(RawMachine) -> IdsToType = [] ; IdsToType = [parameters])}, |
| 1511 | | type_section_with_single_predicate(constraints,Name,IdsToType,machine, |
| 1512 | | RawMachine,RefMachines,NonGroundExceptions,Ein,Eout). |
| 1513 | | |
| 1514 | ? | is_dummy_machine(RawMachine) :- member(is_dummy,RawMachine),!. |
| 1515 | | |
| 1516 | | % Header: Parameters |
| 1517 | | create_id_sections_header(machine_header(_,_,Parameters),Old,New) :- |
| 1518 | | write_section(parameters,TParams,Old,New), |
| 1519 | | maplist(create_id_section_parameter,Parameters,TParams). |
| 1520 | | create_id_section_parameter(Param,TParam) :- |
| 1521 | | Expr=identifier(Name), |
| 1522 | | ext2int_with_pragma(Param,Expr,_,Type,Expr,[ParameterType],TParam), |
| 1523 | | ( is_upper_case(Name) -> |
| 1524 | | ParameterType = parameter_set, |
| 1525 | | Type = set(_) |
| 1526 | | ; |
| 1527 | | ParameterType = parameter_scalar). |
| 1528 | | is_upper_case(Name) :- \+ atom(Name),!, add_internal_error('Illegal call:', is_upper_case(Name)),fail. |
| 1529 | | is_upper_case(Name) :- atom_codes(Name,[C|_]), |
| 1530 | | memberchk(C,"ABCDEFGHIJKLMNOPQRSTUVWXYZ"). |
| 1531 | | |
| 1532 | | % Body: Sets |
| 1533 | | create_set_sections(Sections,MachineName) --> |
| 1534 | | write_section(deferred_sets,DeferredSets), |
| 1535 | | write_section(enumerated_sets,EnumeratedSets), |
| 1536 | | write_section(enumerated_elements,EnumeratedElements), |
| 1537 | | {optional_rawmachine_section(sets,Sections,[],Sets), |
| 1538 | | split_list(is_deferred_set_element,Sets,RawDeferredSets,RawEnumeratedSets), |
| 1539 | | maplist(create_deferred_set_section(MachineName),RawDeferredSets,DeferredSets), |
| 1540 | | maplist(create_enumerated_set_section(Sections,MachineName), |
| 1541 | | RawEnumeratedSets,EnumeratedSets,LEnumeratedElements), |
| 1542 | | append(LEnumeratedElements,EnumeratedElements)}. |
| 1543 | | is_deferred_set_element(AstElem) :- has_functor(AstElem,deferred_set,_). |
| 1544 | | create_deferred_set_section(MachineName,DS,TExpr) :- |
| 1545 | | unwrap_opt_description(DS,deferred_set(Pos,I),TInfos), |
| 1546 | | Infos = [given_set,def(deferred_set,MachineName)|TInfos], |
| 1547 | | create_global_id(I,Pos,Infos,TExpr). |
| 1548 | | create_enumerated_set_section(Sections,MachineName,ES,TExpr,Elements) :- |
| 1549 | | unwrap_opt_description(ES,EnumSetList,TInfos), |
| 1550 | | create_enum_set_aux(EnumSetList,Sections,MachineName,TInfos,TExpr,Elements). |
| 1551 | | |
| 1552 | | create_global_id(identifier(_,Id),Pos,Infos,TExpr) :- !, |
| 1553 | | add_warning(bmachine_construction,'Identifier unexpectedly not atomic: ',Id,Pos), % happened e.g. in ANTLR parser |
| 1554 | | create_identifier(Id,Pos,set(global(Id)),Infos,TExpr). |
| 1555 | | create_global_id(Id,Pos,Infos,TExpr) :- !, |
| 1556 | | create_identifier(Id,Pos,set(global(Id)),Infos,TExpr). |
| 1557 | | |
| 1558 | | create_enum_set_aux(enumerated_set(Pos,I,Elems),_,MachineName,TInfos,TExpr,Elements) :- !, |
| 1559 | | % regular case I = {Elems1, ...} |
| 1560 | | Infos = [given_set,def(enumerated_set,MachineName)|TInfos], |
| 1561 | | create_global_id(I,Pos,Infos,TExpr), |
| 1562 | | maplist(create_enum_set_element(I,MachineName),Elems,Elements). |
| 1563 | | create_enum_set_aux(enumerated_set_via_def(Pos,I,ElemsDEF),Sections,MachineName,TInfos,TExpr,Elements) :- !, |
| 1564 | | % we have the case I = ElemsDEF and DEFINITIONS ElemsDEF == {Elems1, ...} |
| 1565 | | Infos = [given_set,def(enumerated_set,MachineName)|TInfos], |
| 1566 | | create_global_id(I,Pos,Infos,TExpr), |
| 1567 | | (optional_rawmachine_section(definitions,Sections,[],Defs), |
| 1568 | | member(expression_definition(DPos,ElemsDEF,Paras,DefBody),Defs) |
| 1569 | | -> (Paras \= [] |
| 1570 | | -> add_error(bmachine_construction, |
| 1571 | | 'DEFINITION for enumerated set elements must not have parameters:',ElemsDEF,DPos), |
| 1572 | | Elements=[] |
| 1573 | | ; DefBody = set_extension(_,Elems), |
| 1574 | | maplist(create_enum_set_element(I,MachineName),Elems,Elements) |
| 1575 | | -> true |
| 1576 | | ; add_error(bmachine_construction, |
| 1577 | | 'DEFINITION for enumerated set elements must be of the form {El1,El2,...}:',ElemsDEF,DPos), |
| 1578 | | Elements=[] |
| 1579 | | ) |
| 1580 | | ; add_error(bmachine_construction,'No DEFINITION for enumerated set elements found:',ElemsDEF,Pos), |
| 1581 | | Elements=[] |
| 1582 | | ). |
| 1583 | | create_enum_set_aux(E,_,_,_,_,_) :- add_internal_error('Illegal enumerated set:',E),fail. |
| 1584 | | |
| 1585 | | |
| 1586 | | % deal with optional description(Pos,Desc,A) wrapper |
| 1587 | | has_functor(description(_,_Desc,A),Functor,Arity) :- !, functor(A,Functor,Arity). |
| 1588 | | has_functor(A,Functor,Arity) :- functor(A,Functor,Arity). |
| 1589 | | |
| 1590 | | % remove description wrapper and generate info field |
| 1591 | | unwrap_opt_description(Pragma,Res,Infos) :- |
| 1592 | | unwrap_pragma(Pragma,Expr,I),!, Res=Expr,Infos=I. |
| 1593 | | unwrap_opt_description(A,A,[]). |
| 1594 | | |
| 1595 | | |
| 1596 | | create_enum_set_element(Id,MachineName,Ext,Elem) :- |
| 1597 | | (Id=identifier(none,RID) |
| 1598 | | -> Type=global(RID), add_warning(bmachine_construction,'Unexpected non-atomic global set id: ',RID) |
| 1599 | | ; Type=global(Id)), |
| 1600 | | ext2int_with_pragma(Ext,Expr,_Pos,Type,Expr,[enumerated_set_element,def(enumerated_element,MachineName)],Elem). |
| 1601 | | |
| 1602 | | create_identifier(Id,Pos,Type,Infos,TExpr) :- |
| 1603 | | create_texpr(identifier(Id),Type,[nodeid(Pos)|Infos],TExpr). |
| 1604 | | |
| 1605 | | % Constants |
| 1606 | | create_constants_sections(RawMachine) --> |
| 1607 | | create_section_identifiers(constants,concrete_constants,RawMachine), |
| 1608 | | create_section_identifiers(abstract_constants,abstract_constants,RawMachine). |
| 1609 | | % Variables |
| 1610 | | create_variables_sections(RawMachine) --> |
| 1611 | | create_section_identifiers(concrete_variables,concrete_variables,RawMachine), |
| 1612 | | create_section_identifiers(variables,abstract_variables,RawMachine). |
| 1613 | | |
| 1614 | | % Freetypes: Treat them as additional constants, plus add entries in the "freetypes" |
| 1615 | | % section of the resulting machine |
| 1616 | | create_freetypes(RawMachine,MType,RefMachines,Old,New,Ein,Eout) :- |
| 1617 | | optional_rawmachine_section(freetypes,RawMachine,[],RawFreetypes), |
| 1618 | ? | create_freetypes2(RawFreetypes,MType,[ref(local,Old)|RefMachines],Old,New,Ein,Eout). |
| 1619 | | create_freetypes2([],_MType,_RefMachines,M,M,E,E) :- !. |
| 1620 | | create_freetypes2(RawFreetypes,MType,RefMachines,Old,New,Ein,Eout) :- |
| 1621 | | % we need the NonGroundExceptions for type checking |
| 1622 | | extract_parameter_types(RefMachines,NonGroundExceptions), |
| 1623 | | % create identifiers in the constants section |
| 1624 | ? | maplist(create_ids_for_freetype,RawFreetypes,IdsFreetypes), |
| 1625 | | % we just combined the results to keep the numbers of arguments low (too much for maplist) |
| 1626 | | maplist(split_ft,IdsFreetypes,IdentifiersL,Freetypes), |
| 1627 | | append(IdentifiersL,Identifiers), |
| 1628 | | % create properties for each freetype |
| 1629 | | foldl(create_properties_for_freetype(MType,RefMachines,NonGroundExceptions,Identifiers), |
| 1630 | | RawFreetypes,IdsFreetypes,PropertiesL,Ein,Eout), |
| 1631 | | conjunct_predicates(PropertiesL,Properties), |
| 1632 | | (debug_mode(off) -> true |
| 1633 | | ; format('Created PROPERTIES for FREETYPES:~n',[]), translate:nested_print_bexpr(Properties),nl), |
| 1634 | | append_to_section(abstract_constants,Identifiers,Old,Inter), |
| 1635 | | conjunct_to_section(properties,Properties,Inter,Inter2), |
| 1636 | | write_section(freetypes,Freetypes,Inter2,New). |
| 1637 | | |
| 1638 | | split_ft(ft(Ids,Freetype),Ids,Freetype). |
| 1639 | | |
| 1640 | | |
| 1641 | | create_ids_for_freetype(FT,ft([TId|TCons],freetype(Id,Cases))) :- |
| 1642 | | is_freetype_declaration(FT,_Pos,Id,_TypeParams,Constructors), |
| 1643 | | !, |
| 1644 | | create_typed_id_with_given_set_info(Id,set(freetype(Id)),TId), |
| 1645 | ? | maplist(create_ids_for_constructor(Id),Constructors,TCons,Cases). |
| 1646 | | create_ids_for_freetype(FT,_) :- |
| 1647 | | add_internal_error('Illegal freetype term:',create_ids_for_freetype(FT,_)),fail. |
| 1648 | | |
| 1649 | | % add given_set info so that is_just_type3 can detect this as a type |
| 1650 | | create_typed_id_with_given_set_info(IDName,Type,b(identifier(IDName),Type,[given_set])). |
| 1651 | | |
| 1652 | | % deconstruct a .prob Prolog encoding of a freetype declaration |
| 1653 | | % new versions of parser generate freetype parameters |
| 1654 | | is_freetype_declaration(freetype(Pos,Id,Constructors),Pos,Id,[],Constructors). |
| 1655 | | is_freetype_declaration(freetype(Pos,Id,TypeParams,Constructors),Pos,Id,TypeParams,Constructors) :- |
| 1656 | | (TypeParams=[] -> true ; add_warning(bmachine_construction,'Not yet supporting freetype parameters:',Id,Pos)). |
| 1657 | | |
| 1658 | | create_ids_for_constructor(Id,constructor(_Pos,Name,_Arg),TCons,case(Name,Type)) :- |
| 1659 | | create_typed_id(Name,set(couple(Type,freetype(Id))),TCons). |
| 1660 | | create_ids_for_constructor(Id,element(_Pos,Name),TCons,case(Name,constant([Name]))) :- |
| 1661 | | create_typed_id(Name,freetype(Id),TCons). |
| 1662 | | |
| 1663 | | create_properties_for_freetype(MType,RefMachines,NonGroundExceptions, AllFTIdentifiers, |
| 1664 | | FREETYPE,ft(Ids,_Freetypes),Properties,Ein,Eout) :- |
| 1665 | | is_freetype_declaration(FREETYPE,_Pos,Id,_TypeParams,Constructors), |
| 1666 | | debug_format(9,'Processing freetype ~w (one of ~w)~n',[Id,AllFTIdentifiers]), |
| 1667 | | % The freetype type |
| 1668 | | FType = freetype(Id), |
| 1669 | | % We use the standard type environment of properties... |
| 1670 | | visible_env(MType,properties,RefMachines,CEnv,Ein,E1), |
| 1671 | | % ...plus the identifiers of the free type (type name and constructor names) |
| 1672 | | add_identifiers_to_environment(Ids,CEnv,FEnv0), |
| 1673 | | % ...plus the identifiers of all free type names (will overwrite Id, but not a problem) |
| 1674 | | % Note: instead of adding all freetype ids, we could just add the ones preceding Id |
| 1675 | | add_identifiers_to_environment(AllFTIdentifiers,FEnv0,FEnv), |
| 1676 | | % We generate a comprehension set for all elements |
| 1677 | | create_typed_id(Id,set(FType),TId), |
| 1678 | | create_texpr(equal(TId,TComp),pred,[],FDef), |
| 1679 | | unique_typed_id("_freetype_arg",FType,Element), |
| 1680 | | create_recursive_compset([Element],ECond,set(FType),[],RecId,TComp), |
| 1681 | | create_typed_id(RecId,set(FType),TRecId), |
| 1682 | | % For each constructor, we generate a definition and a condition for the |
| 1683 | | % comprehension set above |
| 1684 | | foldl(create_properties_for_constructor(Id,FEnv,Element,TRecId,NonGroundExceptions), |
| 1685 | | Constructors,Defs,Conds,E1,Eout), |
| 1686 | | conjunct_predicates(Conds,ECond), |
| 1687 | | conjunct_predicates([FDef|Defs],Properties). |
| 1688 | | |
| 1689 | | /* create_properties_for_constructor(+Env,+Element,+RecId,+NGE,+Constructor,-Def,-Cond,Ein,Eout) |
| 1690 | | Env: Type environment |
| 1691 | | Element: A typed identifier "e" that is used in the definition of the freetype set: |
| 1692 | | ft = {e | e has_freetype_constructor x => e:...} |
| 1693 | | RecId: The typed identifier that can be used to refer to the freetype set (ft in the |
| 1694 | | example above |
| 1695 | | NGE: "Non ground exceptions", needed for type checking when having a parametrized machine |
| 1696 | | Constructor: The constructor expression as it comes from the parser |
| 1697 | | (constructor(Pos,Name,ArgSet) or element(Pos,Name)) |
| 1698 | | Def: The predicate that defines the constant for the constructor, |
| 1699 | | e.g. "cons = {i,o | i:NAT & o = freetype(cons,i)}" |
| 1700 | | Cond: The predicate that checks the argument of a freetype in the freetype set |
| 1701 | | (That would be the part "e:..." in the example above. |
| 1702 | | Ein,Eout: Used for type checker errors |
| 1703 | | */ |
| 1704 | | create_properties_for_constructor(FID,Env,Element,RecId,NonGroundExceptions, |
| 1705 | | Constructor,Def,Cond,Ein,Eout) :- |
| 1706 | | constructor_name(Constructor,Name), |
| 1707 | | env_lookup_type(Name,Env,CType), |
| 1708 | | create_properties_for_constructor2(Constructor,Env,NonGroundExceptions,FID, |
| 1709 | | Element,RecId,CDef,Cond,Ein,Eout), |
| 1710 | | get_texpr_type(CDef,CType), |
| 1711 | | create_typed_id(Name,CType,CId), |
| 1712 | | create_texpr(equal(CId,CDef),pred,[],Def). |
| 1713 | | constructor_name(element(_Pos,Name),Name). |
| 1714 | | constructor_name(constructor(_Pos,Name,_Domain),Name). |
| 1715 | | create_properties_for_constructor2(element(_Pos,Name),_Env,_NonGroundExceptions,FID, |
| 1716 | | _Element,_RecId,CDef,Cond,Ein,Ein) :- |
| 1717 | | create_texpr(value(freeval(FID,Name,term(Name))),freetype(FID),[],CDef), |
| 1718 | | create_texpr(truth,pred,[],Cond). |
| 1719 | | create_properties_for_constructor2(constructor(_Pos,Name,RArg),Env,NonGroundExceptions, |
| 1720 | | FID,Element,RecId,CDef,Cond,Ein,Eout) :- |
| 1721 | | % First, type check the given set of the domain: |
| 1722 | | btype_ground_dl([RArg],Env,NonGroundExceptions,[set(DType)],[TDomain],Ein,Eout), |
| 1723 | | % then create the RHS of "c = {i,o | i:Domain & o=freetype_constructor(Name,i)}" |
| 1724 | | create_definition_for_constructor(Name,TDomain,FID,CDef), |
| 1725 | | % The check in the freetype comprehension set is of the form |
| 1726 | | % e "of_freetype_case" Name => "content_of"(e) : Domain |
| 1727 | | create_texpr(implication(IsCase,DomainTest),pred,[],Cond), |
| 1728 | | create_texpr(freetype_case(FID,Name,Element),pred,[],IsCase), |
| 1729 | | create_texpr(freetype_destructor(FID,Name,Element),DType,[],Content), |
| 1730 | | % all references to the freetype itself are replaced by the recursive reference |
| 1731 | | replace_id_by_expr(TDomain,FID,RecId,TDomain2), |
| 1732 | | create_texpr(member(Content,TDomain2),pred,[],DomainTest). |
| 1733 | | |
| 1734 | | :- use_module(bsyntaxtree,[get_texpr_set_type/2]). |
| 1735 | | |
| 1736 | | /* The constructor is a function to the freetype, defined with a comprehension set: |
| 1737 | | The general form is "c = {i,o | i:Domain & o=freetype_constructor(Name,i)}" |
| 1738 | | create_definition_for_constructor(+Name,+DType,+FID,-CType,-CDef) :- |
| 1739 | | Name: Name of the constructor |
| 1740 | | TDomain: The user-specified domain |
| 1741 | | FID: The name of the free type |
| 1742 | | CDef: The RHS of the definition "Name = CDef" |
| 1743 | | */ |
| 1744 | | create_definition_for_constructor(Name,TDomain,FID,CDef) :- |
| 1745 | | % get the type of the domain: |
| 1746 | | get_texpr_set_type(TDomain,DType), |
| 1747 | | % create argument and result identifiers: |
| 1748 | | unique_typed_id("_constr_arg",DType,TArgId), % was constructor_arg and constructor_res |
| 1749 | | unique_typed_id("_constr_res",freetype(FID),TResId), |
| 1750 | | % The comprehension set as a whole |
| 1751 | | CType = set(couple(DType,freetype(FID))), |
| 1752 | | create_texpr(comprehension_set([TArgId,TResId],Pred),CType, |
| 1753 | | [prob_annotation('SYMBOLIC')],CDef), |
| 1754 | | create_texpr(conjunct(DomainCheck,Construction),pred,[],Pred), |
| 1755 | | % "i:Domain" |
| 1756 | | create_texpr(member(TArgId,TDomain),pred,[],DomainCheck), |
| 1757 | | % "o=freetype_constructor(i) |
| 1758 | | create_texpr(freetype_constructor(FID,Name,TArgId),freetype(FID),[],FreetypeCons), |
| 1759 | | create_texpr(equal(TResId,FreetypeCons),pred,[],Construction). |
| 1760 | | |
| 1761 | | % Operations |
| 1762 | | create_operations_sections(RawMachine,Name,Old,New) :- |
| 1763 | | write_section(promoted,PromotedOperationIds,Old,New0), |
| 1764 | | get_operation_identifiers(RawMachine,operations,OperationIdentifiers), |
| 1765 | | (get_operation_identifiers(RawMachine,local_operations,LocOpIDs), |
| 1766 | | LocOpIDs \= [] % if there is a LOCAL_OPERATIONS section we assume user wants to call them |
| 1767 | | -> maplist(add_infos_to_identifier([is_local_operation]),LocOpIDs,LocOpIds2), |
| 1768 | | append_to_section(unpromoted,LocOpIds2,New0,New), |
| 1769 | | % Now remove all LOCAL_OPERATIONS from the promoted ones |
| 1770 | | % see for example prob_examples/examples/B/ClearSy/RoboSim/SRanger_case_Jul25/logic_i.imp |
| 1771 | | exclude(duplicate_local_operation_id(Name,LocOpIDs),OperationIdentifiers,PromotedOperationIds) |
| 1772 | | ; New = New0, PromotedOperationIds = OperationIdentifiers |
| 1773 | | ). |
| 1774 | | |
| 1775 | | |
| 1776 | | |
| 1777 | | % check if a LOCAL_OPERATION is already declared in the OPERATIONS section: |
| 1778 | | % in this case: we use the operation body defined in the OPERATIONS section, but remove it from the promoted list |
| 1779 | | duplicate_local_operation_id(Name,OpIds,TID) :- |
| 1780 | | get_texpr_id(TID,op(OpID)), |
| 1781 | ? | member(TID2,OpIds), |
| 1782 | | get_texpr_id(TID2,op(OpID)),!, |
| 1783 | | debug_println(9,duplicate_local_operation_id(OpID,Name)). |
| 1784 | | |
| 1785 | | get_operation_identifiers(RawMachine,OperationIdentifiers) :- |
| 1786 | | get_operation_identifiers(RawMachine,operations,OperationIdentifiers). |
| 1787 | | get_operation_identifiers(RawMachine,SECTION,OperationIdentifiers) :- |
| 1788 | | optional_rawmachine_section(SECTION,RawMachine,[],Ops), |
| 1789 | | maplist(extract_operation_identifier,Ops,OperationIdentifiers). |
| 1790 | | extract_operation_identifier(Ext,TId) :- |
| 1791 | | remove_pos(Ext, operation(ExtId,_,_,_)),!, |
| 1792 | | ext2int_with_pragma(ExtId,identifier(I),_,op(_,_),identifier(op(I)),Infos,TId), |
| 1793 | | operation_infos(Infos). |
| 1794 | | extract_operation_identifier(Ext,TId) :- |
| 1795 | | remove_pos(Ext, refined_operation(ExtId,_,_,RefinesOp,_)),!, |
| 1796 | | ext2int_with_pragma(ExtId,identifier(I),_,op(_,_),identifier(op(I)),[refines_operation(RefinesOp)|Infos],TId), |
| 1797 | | operation_infos(Infos). |
| 1798 | | extract_operation_identifier(Ext,TId) :- |
| 1799 | | remove_pos(Ext, description_operation(_Desc,RealOp)),!, |
| 1800 | | extract_operation_identifier(RealOp,TId). |
| 1801 | | extract_operation_identifier(Ext,_) :- add_internal_error('Unknown operation node:',Ext),fail. |
| 1802 | | |
| 1803 | | % VALUES section: |
| 1804 | | % process_values_section(MachineType,RawMachine,NonGroundExceptions,Min/Ein/RefMIn,Mout/Eout/RefMOut): |
| 1805 | | % Type-check the VALUES section and change the type of valuated deferred sets, if necessary |
| 1806 | | % MachineType, RawMachine, NonGroundExceptions: as usual, see other comments |
| 1807 | | % Min/Mout: The currently constructed machine |
| 1808 | | % Ein/Eout: The difference list of errors |
| 1809 | | % RefMin/RefMout: The list of already typechecked machines. These typechecked machines can be |
| 1810 | | % altered by this predicate because if a deferred set is valuated by an integer set or |
| 1811 | | % other deferred set, all occurrences of the original type are replaced by the new type, |
| 1812 | | % even for the already typed machines. |
| 1813 | | process_values_section(MachineType,RawMachine,NonGroundExceptions,Min/Ein/RefMIn,Mout/Eout/RefMOut) :- |
| 1814 | | optional_rawmachine_section(values,RawMachine,[],RawValues), |
| 1815 | | process_values_section_aux(RawValues,MachineType,NonGroundExceptions, |
| 1816 | | Min/Ein/RefMIn,Mout/Eout/RefMOut). |
| 1817 | | process_values_section_aux([],_MachineType,_NonGroundExceptions,In,Out) :- !,In=Out. |
| 1818 | | process_values_section_aux(RawValues,MachineType,NonGroundExceptions, |
| 1819 | | Min/Ein/RefMin,Mout/Eout/RefMout) :- |
| 1820 | | debug_stats('TYPING VALUES'), |
| 1821 | | type_values_section(MachineType,RawValues,RefMin,NonGroundExceptions,Min/Ein,Mout/Eout), |
| 1822 | | % will be added as additional_property in bmachine |
| 1823 | | RefMin=RefMout. |
| 1824 | | |
| 1825 | | type_values_section(MachineType,RawValues,RefMachines,NonGroundExceptions,Min/Ein,Mout/Eout) :- |
| 1826 | | write_section(values,Values,Min,Mout), |
| 1827 | | visible_env(MachineType,values_expression,RefMachines,Env,Ein,E1), |
| 1828 | | % We have to pass an environment that can be modified because in each |
| 1829 | | % valuation the previously valuated constants can be used. |
| 1830 | | foldl(extract_values_entry(NonGroundExceptions),RawValues,Values,Env/E1,_ResultingEnv/Eout). |
| 1831 | | |
| 1832 | | extract_values_entry(NonGroundExceptions, values_entry(POS,ValueID,ValueExpr), Entry, |
| 1833 | | EnvIn/Ein,EnvOut/Eout) :- |
| 1834 | | % TODO: There seem to be a lot of additional constraints for valuations in VALUES that are not |
| 1835 | | % yet handled here |
| 1836 | | btype_ground_dl([ValueExpr],EnvIn,NonGroundExceptions,[Type],[TExpr],Ein,Eout), |
| 1837 | | clean_up(TExpr,[],CTExpr), % ensure we remove things like mult_or_cart/2 |
| 1838 | | create_identifier(ValueID,POS,Type,[valuated_constant],TypedID), |
| 1839 | | create_texpr(values_entry(TypedID,CTExpr),values_entry,[nodeid(POS)],Entry), |
| 1840 | | debug_println(9,valued_constant(ValueID)), |
| 1841 | | EnvOut=EnvIn. |
| 1842 | | %add_identifiers_to_environment([TypedID],EnvIn,EnvOut). % ideally we should register the new type of TypedID |
| 1843 | | % However: ProB can currently only process VALUES clauses where the type does not change |
| 1844 | | |
| 1845 | | % type_section_with_single_predicate(+Sec,+Name,+SectionsToType,+MachineType,+Sections, |
| 1846 | | % +RefMachines,+NonGroundExceptions,+Ein,-Eout,+Old,-New): |
| 1847 | | % Type a section such as INVARIANT, PROPERTIES, CONSTRAINTS with a single predicate |
| 1848 | | % Sec: section name in the raw (untyped) machine (e.g. invariant) |
| 1849 | | % Name: name of machine from which this section comes |
| 1850 | | % SectionsToType: list of section names that contain identifiers that must be typed |
| 1851 | | % by this predicate (e.g. all variables must be typed by the invariant) |
| 1852 | | % MachineType: machine type (machine, refinement, ...) |
| 1853 | | % Sections: list of sections representing the raw (untyped) machine |
| 1854 | | % RefMachines: list of already typed machines |
| 1855 | | % NonGroundExceptions: list of types that may be not ground because the concrete type |
| 1856 | | % is determinded by machine parameter |
| 1857 | | % Ein/Eout: difference list of errors |
| 1858 | | % Old/New: the new typed section is added (by conjunction) to the machine |
| 1859 | | type_section_with_single_predicate(Sec,Name,SectionsToType,MachineType,Sections, |
| 1860 | | RefMachines,NonGroundExceptions,Ein,Eout,Old,New) :- |
| 1861 | | optional_rawmachine_section(Sec,Sections,truth(none),Predicate), |
| 1862 | | ( Predicate = truth(_) -> |
| 1863 | | % even if there is no content, we must check if all identifiers are typed |
| 1864 | | check_if_all_ids_are_typed(SectionsToType,RefMachines,NonGroundExceptions,Ein,Eout), |
| 1865 | | Old=New |
| 1866 | | ; |
| 1867 | | get_machine_infos(Sections,Infos), |
| 1868 | | toplevel_raw_predicate_sanity_check(Sec,Name,Predicate,Infos), |
| 1869 | | type_predicates(Sec,SectionsToType,MachineType,[Predicate],RefMachines,NonGroundExceptions, |
| 1870 | | [Typed],Ein,Eout), |
| 1871 | | conjunct_to_section(Sec,Typed,Old,New) |
| 1872 | | ), |
| 1873 | | !. |
| 1874 | | type_section_with_single_predicate(Sec,Name,SectsToType,MchType,_,_,_,_,_,_,_) :- |
| 1875 | | add_internal_error('type_section_with_single_predicate failed', |
| 1876 | | type_section_with_single_predicate(Sec,Name,SectsToType,MchType,_,_,_,_,_,_,_)), |
| 1877 | | fail. |
| 1878 | | |
| 1879 | | % get some infos relevant for sanity check: |
| 1880 | | get_machine_infos(Sections,Infos) :- |
| 1881 | | ((rawmachine_section_exists(concrete_variables,Sections) ; rawmachine_section_exists(abstract_variables,Sections)) |
| 1882 | | -> Infos = [has_variables|I1] ; Infos = I1), |
| 1883 | | ((rawmachine_section_exists(concrete_constants,Sections) ; rawmachine_section_exists(abstract_constants,Sections)) |
| 1884 | | -> I1 = [has_constants] ; I1 = []). |
| 1885 | | |
| 1886 | | % Type a section with multiple predicates, such as ASSERTIONS |
| 1887 | | type_section_with_predicate_list(Sec,SectionsToType,MachineType,Sections, |
| 1888 | | RefMachines,NonGroundExceptions,Ein,Eout,Old,New) :- |
| 1889 | | write_section(Sec,Typed,Old,New), |
| 1890 | | optional_rawmachine_section(Sec,Sections,[],Predicates), |
| 1891 | | type_predicates(Sec,SectionsToType,MachineType,Predicates,RefMachines,NonGroundExceptions,Typed,Ein,Eout), |
| 1892 | | !. |
| 1893 | | type_section_with_predicate_list(Sec,SectsToType,MchType,Sects,RefMchs,NonGrndExc,Ein,Eout,Old,New) :- |
| 1894 | | add_internal_error('type_section_with_predicate_list failed', |
| 1895 | | type_section_with_predicate_list(Sec,SectsToType,MchType,Sects,RefMchs,NonGrndExc,Ein,Eout,Old,New)), |
| 1896 | | fail. |
| 1897 | | |
| 1898 | | |
| 1899 | | type_predicates(_Sec,SectionsToType,_MachineType,[],RefMachines,NonGroundExceptions,Typed,Ein,Eout) :- |
| 1900 | | !,Typed=[], |
| 1901 | | check_if_all_ids_are_typed(SectionsToType,RefMachines,NonGroundExceptions,Ein,Eout). |
| 1902 | | type_predicates(Sec,SectionsToType,MachineType,Predicates,RefMachines,NonGroundExceptions,Typed,Ein,Eout) :- |
| 1903 | | visible_env(MachineType, Sec, RefMachines, Env, Ein, E1), |
| 1904 | | same_length(Predicates,Types),maplist(is_pred_type,Types), |
| 1905 | | btype_ground_dl_in_section(Sec,Predicates,Env,NonGroundExceptions,Types,Typed1,E1,E2), |
| 1906 | | mark_with_section(Sec,Typed1,Typed), |
| 1907 | | check_if_all_ids_are_typed(SectionsToType,RefMachines,NonGroundExceptions,E2,Eout), |
| 1908 | | (no_perrors_occured(Ein,Eout) |
| 1909 | | -> perform_post_static_check(Typed) % only run if no type errors, it makes use of find_typed_identifier_uses |
| 1910 | | ; true |
| 1911 | | ). |
| 1912 | | |
| 1913 | | no_perrors_occured(Ein,Eout) :- Ein==Eout,!. |
| 1914 | | no_perrors_occured([H|T],Eout) :- nonvar(H),not_a_perror(H),no_perrors_occured(T,Eout). |
| 1915 | | |
| 1916 | | not_a_perror(warning(_,_)). |
| 1917 | | % other possible values: error(Msg,Pos), internal_error(Msg,Pos); see get_perror/2 |
| 1918 | | |
| 1919 | | % check if the identifiers that should be typed by this section are completly typed |
| 1920 | | check_if_all_ids_are_typed([],_RefMachines,_NonGroundExceptions,Ein,Eout) :- !,Ein=Eout. |
| 1921 | | check_if_all_ids_are_typed(SectionsToType,RefMachines,NonGroundExceptions,Ein,Eout) :- |
| 1922 | | memberchk(ref(local,Local),RefMachines), |
| 1923 | | get_all_identifiers(SectionsToType,Local,IdentifiersToType), |
| 1924 | | check_ground_types_dl(IdentifiersToType, NonGroundExceptions, Ein, Eout). |
| 1925 | | |
| 1926 | | |
| 1927 | | mark_with_section(Sec,In,Out) :- |
| 1928 | | maplist(mark_with_section2(Sec),In,Out). |
| 1929 | | mark_with_section2(Sec,In,Out) :- |
| 1930 | | remove_bt(In,conjunct(A,B),conjunct(NA,NB),Out),!, |
| 1931 | | mark_with_section2(Sec,A,NA), mark_with_section2(Sec,B,NB). |
| 1932 | | mark_with_section2(Sec,In,Out) :- |
| 1933 | | add_texpr_infos(In,[section(Sec)],Out). |
| 1934 | | |
| 1935 | | type_initialisation_section(Sections,Name,MType,RefMachines,NonGroundExceptions,Ein,Eout,Old,New) :- |
| 1936 | | write_section(initialisation,Initialisation,Old,New), |
| 1937 | | ( rawmachine_section_with_opt_desc(initialisation,Sections,Init,Desc,DescPos) -> |
| 1938 | | visible_env(MType, initialisation, RefMachines, InitEnv, Ein, E1), |
| 1939 | ? | btype_ground_dl([Init],InitEnv,NonGroundExceptions,[subst],[TypedInit],E1,Eout), |
| 1940 | | (Desc='' -> TypedInit2 = TypedInit |
| 1941 | | ; add_texpr_infos(TypedInit,[description(Desc),description_position(DescPos)],TypedInit2) |
| 1942 | | ), |
| 1943 | | Initialisation=[init_stmt(Name,TypedInit2)] |
| 1944 | | ; |
| 1945 | | Ein=Eout, |
| 1946 | | Initialisation=[]). |
| 1947 | | |
| 1948 | | :- use_module(library(ugraphs)). |
| 1949 | | |
| 1950 | | type_operations_section(Sections,MType,RefMachines,NonGroundExceptions,Ein,Eout,Old,New) :- |
| 1951 | | write_section(operation_bodies,Operations,Old,New), |
| 1952 | | visible_env(MType, operation_bodies, RefMachines, Env, Ein, E1), |
| 1953 | | optional_rawmachine_section(operations,Sections,[],Ops1), |
| 1954 | | optional_rawmachine_section(local_operations,Sections,[], LocalOps2), |
| 1955 | | exclude(duplicate_local_operation(Ops1),LocalOps2,FOps2), |
| 1956 | | append(FOps2,Ops1,Ops), |
| 1957 | | topological_sort_operations(Ops,LocalOps2,Env,SOps), |
| 1958 | | (debug_mode(off) -> true ; length(SOps,NrOps),debug_stats(finished_topological_sorting(NrOps))), |
| 1959 | | same_length(SOps,Types), maplist(is_op_type,Types), |
| 1960 | ? | btype_ground_dl(SOps,Env,NonGroundExceptions,Types,Operations,E1,Eout),!. |
| 1961 | | |
| 1962 | | % check if a LOCAL_OPERATION also exists as a regular operation; if so we can ignore it and |
| 1963 | | % just use the full operation; see also duplicate_local_operation_id above |
| 1964 | | duplicate_local_operation(NonLocalOps,LocalOperation) :- |
| 1965 | | raw_op_id(LocalOperation,OpName), |
| 1966 | ? | member(NonLocalOp,NonLocalOps), |
| 1967 | | raw_op_id(NonLocalOp,OpName), % we found a non-local operation with the same name |
| 1968 | | !, |
| 1969 | | raw_op_pos(LocalOperation,LPos), |
| 1970 | | raw_op_pos(NonLocalOp,NPos), |
| 1971 | | translate_span(NPos,PS2), |
| 1972 | | ajoin(['Ignoring LOCAL_OPERATION already defined in OPERATIONS section ',PS2,': '],Msg), |
| 1973 | | add_message(bmachine_construction,Msg,OpName,LPos). |
| 1974 | | |
| 1975 | | |
| 1976 | | allow_local_or_expr_op_calls :- |
| 1977 | | (get_preference(allow_local_operation_calls,true) -> true |
| 1978 | | ; get_preference(allow_operation_calls_in_expr,true)). |
| 1979 | | % at the moment ProB allows local calls inside expressions, independently of the allow_local_operation_calls preference |
| 1980 | | |
| 1981 | | % perform a topological sort of the operations: treat called operation before calling operation |
| 1982 | | % only relevant when allow_local_operation_calls is set to true |
| 1983 | | topological_sort_operations(Ops,LocalOps,Env,SortedOps) :- |
| 1984 | | (LocalOps = [] % there are no LOCAL_OPERATIONS |
| 1985 | | -> allow_local_or_expr_op_calls % unless call operations in machine itself, no need to perform a topological sort |
| 1986 | | ; true % we have local_operations, we need topological sort to ensure they are processed before other operations |
| 1987 | | ), |
| 1988 | | findall(OtherID-ID, (member(Op,Ops),op_calls_op(Op,Env,ID,OtherID)),Edges), |
| 1989 | | % print(edges(Edges)),nl, |
| 1990 | | % TO DO: maybe only store edges where OtherID also appears in Ops (i.e., call within same machine) |
| 1991 | | Edges \= [], |
| 1992 | | !, |
| 1993 | | findall(ID,(member(operation(_,RawID,_,_,_),Ops),raw_id(RawID,ID)),Vertices), %print(vertices(Vertices)),nl, |
| 1994 | | vertices_edges_to_ugraph(Vertices,Edges,Graph), |
| 1995 | | (top_sort(Graph,Sorted) |
| 1996 | | -> sort_ops(Sorted,Ops,SortedOps) |
| 1997 | | ; get_preference(allow_operation_calls_in_expr,true) -> |
| 1998 | | add_warning(topological_sort,'Mutual recursion or cycle in the (local) operation call graph, this may cause problems computing reads information: ',Edges), |
| 1999 | | SortedOps=Ops |
| 2000 | | % not necessarily a problem, because operations called in expressions are not allowed to modify the state |
| 2001 | | % TODO: however, we may have an issue with computing reads information correctly for mutual recursion !? |
| 2002 | | % direct recursion should be ok |
| 2003 | | ; add_error(topological_sort,'Cycle in the (local) operation call graph: ',Edges), |
| 2004 | | SortedOps=Ops). |
| 2005 | | topological_sort_operations(Ops,_,_,Ops). |
| 2006 | | |
| 2007 | | sort_ops([],Ops,Ops). % Ops should be [] |
| 2008 | | sort_ops([OpID|T],Ops,Res) :- |
| 2009 | | raw_op_id(Op1,OpID), |
| 2010 | ? | (select(Op1,Ops,TOps) |
| 2011 | | -> Res = [Op1|TSOps], sort_ops(T,TOps,TSOps) |
| 2012 | | ; % operation from another machine |
| 2013 | | % print(could_not_find(OpID,Ops)),nl, |
| 2014 | | sort_ops(T,Ops,Res) |
| 2015 | | ). |
| 2016 | | |
| 2017 | | is_op_type(op(_,_)). |
| 2018 | | is_pred_type(pred). |
| 2019 | | |
| 2020 | | % compute which other operations are directly called |
| 2021 | | op_calls_op(operation(_,RawID,_,_,RawBody),Env,ID,OtherID) :- raw_id(RawID,ID), |
| 2022 | ? | raw_body_calls_operation(RawBody,ID,Env,OtherID). |
| 2023 | | |
| 2024 | | raw_body_calls_operation(RawBody,ID,Env,OtherID) :- |
| 2025 | ? | raw_member(OpCall,RawBody), |
| 2026 | | raw_op_call(OpCall,ID,Env,RawOtherID), raw_id(RawOtherID,OtherID). |
| 2027 | | |
| 2028 | | raw_op_call(operation_call(_,RawOtherID,_,_),_,_, RawOtherID). |
| 2029 | | raw_op_call(operation_call_in_expr(_,RawOtherID,_),ID,_, RawOtherID) :- |
| 2030 | | \+ raw_id(RawOtherID,ID). % we do not look at direct recursion: it poses no problem for computing reads/writes info |
| 2031 | | raw_op_call(function(_,RawOtherID,_), ID, Env, RawOtherID) :- % function calls possibly not yet translated to operation_call_in_expr |
| 2032 | | get_preference(allow_operation_calls_in_expr,true), |
| 2033 | | \+ raw_id(RawOtherID,ID), % direct recursion ok |
| 2034 | | btypechecker:is_operation_call(RawOtherID,Env). |
| 2035 | | raw_op_call(identifier(Pos,OtherID), ID, Env, RawOtherID) :- % possible operation call in expr without arguments |
| 2036 | | OtherID \= ID, % direct recursion ok |
| 2037 | | get_preference(allow_operation_calls_in_expr,true), |
| 2038 | | RawOtherID = identifier(Pos,OtherID), |
| 2039 | | btypechecker:is_operation_call(RawOtherID,Env). |
| 2040 | | |
| 2041 | | |
| 2042 | | raw_op_id(operation(_,RawID,_,_,_RawBody),ID) :- raw_id(RawID,ID). |
| 2043 | | raw_id(identifier(_,ID),ID). |
| 2044 | | raw_op_pos(operation(Pos,_RawID,_,_,_RawBody),Pos). |
| 2045 | | |
| 2046 | | % a utility function to work on the raw AST Functor(POS,Arg1,...,Argn) |
| 2047 | | % this will not be able to look inside DEFINITIONS ! |
| 2048 | | % TO DO: deal with more raw substitutions which have list arguments |
| 2049 | | raw_member(X,X). |
| 2050 | ? | raw_member(X,parallel(_,List)) :- !, member(Term,List), raw_member(X,Term). |
| 2051 | ? | raw_member(X,sequence(_,List)) :- !, member(Term,List), raw_member(X,Term). |
| 2052 | ? | raw_member(X,[H|T]) :- !, (raw_member(X,H) ; raw_member(X,T)). |
| 2053 | | raw_member(X,Term) :- compound(Term), Term =.. [_Functor,_Pos|Args], |
| 2054 | ? | member(A,Args), raw_member(X,A). |
| 2055 | | |
| 2056 | | |
| 2057 | | create_section_identifiers(Section,DestSection,RawMachine,Old,New) :- |
| 2058 | | write_section(DestSection,Vars,Old,New), |
| 2059 | | optional_rawmachine_section(Section,RawMachine,[],Identifiers), |
| 2060 | | create_section_ids2(Identifiers,[],Vars,DestSection,New). |
| 2061 | | |
| 2062 | | create_section_ids2([],_,[],_,_). |
| 2063 | | create_section_ids2([Ext|Rest],Infos,Res,DestSection,MachSections) :- |
| 2064 | | expand_definition_to_variable_list(Ext,MachSections,List),!, |
| 2065 | | append(List,Rest,NewList), |
| 2066 | | create_section_ids2(NewList,Infos,Res,DestSection,MachSections). |
| 2067 | | create_section_ids2([Ext|Rest],Infos,Res,DestSection,MachSections) :- |
| 2068 | | create_section_id(Ext,Infos,DestSection,TId), |
| 2069 | | ( TId = error(Msg,Term,Pos) -> |
| 2070 | | Res = TRest, add_error(bmachine_construction,Msg,Term,Pos) |
| 2071 | | ; |
| 2072 | | Res = [TId|TRest]), |
| 2073 | | create_section_ids2(Rest,Infos,TRest,DestSection,MachSections). |
| 2074 | | create_section_id(Ext,Infos,DestSection,TId) :- |
| 2075 | | (unwrap_pragma(Ext,Ext2,PragmaInfos) -> append(PragmaInfos,Infos,FullInfos) |
| 2076 | | ; Ext2=Ext, FullInfos=Infos), |
| 2077 | | I=identifier(_), |
| 2078 | | ( ext2int(Ext2,I,_Pos,_Type,I,FullInfos,TId) -> |
| 2079 | | true |
| 2080 | | ; Ext2 = definition(POSINFO,ID,_) -> |
| 2081 | | TId = error('Trying to use DEFINITION name as identifier: ', |
| 2082 | | (ID,within(DestSection)), POSINFO) |
| 2083 | | ; |
| 2084 | | TId = error('Illegal identifier: ', |
| 2085 | | (Ext2,within(DestSection)), Ext2) |
| 2086 | | ). |
| 2087 | | |
| 2088 | | % support using DEFINITIONS which are variable lists; |
| 2089 | | % currently for ProB parser you need to write VARS == (x,y,..) for Atelier-B: VARS == x,y,.. |
| 2090 | | expand_definition_to_variable_list(definition(_POSINFO,ID,_),MachSections,List) :- |
| 2091 | | get_section(definitions,MachSections,Defs), |
| 2092 | | member(definition_decl(ID,expression,_InnerPos,[],RawExpr,_Deps),Defs), |
| 2093 | | extract_identifier_list(RawExpr,List,[]). |
| 2094 | | |
| 2095 | | % convert a raw tuple into a raw identifier list: |
| 2096 | | extract_identifier_list(identifier(Pos,ID)) --> [identifier(Pos,ID)]. |
| 2097 | | extract_identifier_list(couple(_,List)) --> |
| 2098 | | extract_identifier_list(List). |
| 2099 | | extract_identifier_list([]) --> []. |
| 2100 | | extract_identifier_list([H|T]) --> extract_identifier_list(H), extract_identifier_list(T). |
| 2101 | | |
| 2102 | | |
| 2103 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 2104 | | % sort the machines topologically |
| 2105 | | |
| 2106 | | % can be used to store the result: |
| 2107 | | assert_machine_order(Order) :- |
| 2108 | | retractall(machine_global_order(_)), |
| 2109 | | debug_print(9,machine_global_order(Order)), |
| 2110 | | assertz(machine_global_order(Order)). |
| 2111 | | |
| 2112 | | get_mach_position(Order,M,Ref,Pos-mch(M,Ref)) :- |
| 2113 | | get_machine_name(M,Name), |
| 2114 | ? | (nth1(Pos,Order,Name) -> true |
| 2115 | | ; add_internal_error('Unknown machine:',Name:Order), |
| 2116 | | Pos=0). |
| 2117 | | get_machine(_-mch(M,_),M). % re-extract info after sorting |
| 2118 | | get_reference(_-mch(_,R),R). |
| 2119 | | |
| 2120 | | :- use_module(library(samsort),[samkeysort/2]). |
| 2121 | | sort_machines_by_global_order(Machines,SortedMachines) :- |
| 2122 | | sort_machines_by_global_order(Machines,_,SortedMachines,_). |
| 2123 | | sort_machines_by_global_order(Machines,Refs,SortedMachines,SortedRefs) :- |
| 2124 | | machine_global_order(Order), |
| 2125 | | maplist(get_mach_position(Order),Machines,Refs,KM), % add position so that sorting works |
| 2126 | | samkeysort(KM,SKM), % for test 925 it is important to keep duplicates and not use sort/2 |
| 2127 | | maplist(get_machine,SKM,SortedMachines), |
| 2128 | | maplist(get_reference,SKM,SortedRefs),!. |
| 2129 | | %maplist(get_machine_name,Sorted,SNs),print(sorted(SNs)),nl. |
| 2130 | | sort_machines_by_global_order(M,R,M,R) :- |
| 2131 | | add_internal_error('Sorting machines failed:',M). |
| 2132 | | |
| 2133 | | % perform the actual computation: |
| 2134 | | machine_order(Machines,Order) :- |
| 2135 | | machine_dependencies(Machines,Dependencies), |
| 2136 | | topsort(Dependencies,Order). |
| 2137 | | |
| 2138 | | % sort the machines topologically |
| 2139 | | topsort(Deps,Sorted) :- |
| 2140 | | topsort2(Deps,[],Sorted). |
| 2141 | | topsort2([],_,[]) :- !. |
| 2142 | | topsort2(Deps,Known,Sorted) :- |
| 2143 | | split_list(all_deps_known(Known),Deps,DAvailable,DNotAvailable), |
| 2144 | | DAvailable = [_|_], % we have new machines available whose dependencies are all known |
| 2145 | | !, |
| 2146 | | maplist(dep_name,DAvailable,Available), |
| 2147 | | append(Available,Known,NewKnown), |
| 2148 | | append(Available,Rest,Sorted), |
| 2149 | | topsort2(DNotAvailable,NewKnown,Rest). |
| 2150 | | topsort2(Deps,_Known,_) :- |
| 2151 | | member(dep(Name,NameDeps),Deps), |
| 2152 | | add_error(bmachine_construction,'Could not resolve machine dependencies for: ',Name:depends_on(NameDeps)), |
| 2153 | | fail. |
| 2154 | | |
| 2155 | ? | all_deps_known(K,dep(_Name,Deps)) :- sort(Deps,DS),sort(K,KS),subseq0(KS,DS),!. |
| 2156 | | dep_name(dep(Name,_Deps),Name). |
| 2157 | | |
| 2158 | | % find dependencies between machines |
| 2159 | | machine_dependencies(Machines,Dependencies) :- |
| 2160 | | maplist(machine_dependencies2,Machines,Deps), |
| 2161 | | sort(Deps,Dependencies). |
| 2162 | | machine_dependencies2(M,dep(Name,Deps)) :- |
| 2163 | | get_constructed_machine_name_and_body(M,Name,_,Body), |
| 2164 | | findall(Ref, |
| 2165 | | (refines(M,Ref);machine_reference(Body,Ref)), |
| 2166 | | Deps). |
| 2167 | | |
| 2168 | | machine_reference(MachineBody,Ref) :- |
| 2169 | ? | ( member(sees(_,R),MachineBody) |
| 2170 | ? | ; member(uses(_,R),MachineBody) ), |
| 2171 | ? | member(identifier(_,PrefixRef),R), |
| 2172 | | split_prefix(PrefixRef,_,Ref). |
| 2173 | | machine_reference(MachineBody,Ref) :- |
| 2174 | ? | ( member(includes(_,R),MachineBody) |
| 2175 | ? | ; member(extends(_,R),MachineBody) |
| 2176 | ? | ; member(imports(_,R),MachineBody) ), |
| 2177 | ? | member(machine_reference(_,PrefixRef,_),R), |
| 2178 | | split_prefix(PrefixRef,_,Ref). |
| 2179 | | |
| 2180 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 2181 | | % refinements |
| 2182 | | merge_refinement_and_abstraction(Name,Concrete,RefMachines,Result,Ein,Eout) :- |
| 2183 | | memberchk(ref(abstraction,Abstraction),RefMachines), |
| 2184 | | append_sections([sets,concrete_constants,concrete_variables],Abstraction,Concrete,M1), |
| 2185 | | append_if_new(abstract_constants,Abstraction,M1,M2), |
| 2186 | | get_section(properties,Abstraction,AbstractProperties), |
| 2187 | | conjunct_to_section(properties,AbstractProperties,M2,M3), |
| 2188 | | % now get current invariant Invariant and filter out gluing/linking invariant |
| 2189 | | % (the linking invariant talks about variables which no longer exist; hence we cannot check it anymore) |
| 2190 | | select_section(invariant,Invariant,FullConcreteInvariant,M3,M4), |
| 2191 | | write_section(linking_invariant,LinkingInvariant,M4,Merged), |
| 2192 | | % we now also copy from the abstraction those invariants which are still valid |
| 2193 | | get_machine_sorted_variables(Abstraction,SortedAbsVars), |
| 2194 | | get_machine_sorted_variables(Concrete,SortedConcrVars), |
| 2195 | | assert_removed_abs_vars(SortedAbsVars,SortedConcrVars,Name), |
| 2196 | | get_section(invariant,Abstraction,AbstractInvariant), |
| 2197 | | filter_abstract_invariant(AbstractInvariant,SortedConcrVars,AbsInvariantStillValid), |
| 2198 | | filter_linking_invariant(Invariant,LinkingInvariant,ConcreteInvariant), |
| 2199 | | conjunct_predicates([AbsInvariantStillValid,ConcreteInvariant],FullConcreteInvariant), |
| 2200 | | propagate_abstract_operations(Abstraction,Merged,RefMachines,Result,Ein,Eout). |
| 2201 | | merge_refinement_and_abstraction(_,Machine,_,Machine,Errors,Errors). |
| 2202 | | |
| 2203 | | :- use_module(probsrc(bsyntaxtree), [same_id/3]). |
| 2204 | | assert_removed_abs_vars([],_,_). |
| 2205 | | assert_removed_abs_vars([TID1|H],[],Name) :- !, |
| 2206 | | assert_abstract_variable_removed_in(Name,TID1), |
| 2207 | | assert_removed_abs_vars(H,[],Name). |
| 2208 | | assert_removed_abs_vars([TID1|T1],[TID2|T2],Name) :- same_id(TID1,TID2,_),!, |
| 2209 | | assert_removed_abs_vars(T1,T2,Name). |
| 2210 | | assert_removed_abs_vars([TID1|T1],[TID2|T2],Name) :- TID1 @< TID2,!, |
| 2211 | | assert_abstract_variable_removed_in(Name,TID1), |
| 2212 | | assert_removed_abs_vars(T1,[TID2|T2],Name). |
| 2213 | | assert_removed_abs_vars([TID1|T1],[_|T2],Name) :- % TID2 added in Name |
| 2214 | | assert_removed_abs_vars([TID1|T1],T2,Name). |
| 2215 | | |
| 2216 | | |
| 2217 | | assert_abstract_variable_removed_in(Name,TID) :- |
| 2218 | | get_texpr_id(TID,ID), |
| 2219 | | ajoin(['Variable ',ID,' removed in refinement machine: '],Msg), |
| 2220 | | add_debug_message(bmachine_construction,Msg,Name,TID), |
| 2221 | | assertz(abstract_variable_removed_in(ID,Name,TID)). |
| 2222 | | |
| 2223 | | |
| 2224 | | % append sections from abstract machine to concrete machine: |
| 2225 | | append_sections(Sections,AbsMachine,Old,New) :- |
| 2226 | | expand_shortcuts(Sections,AllSections), |
| 2227 | | append_sections2(AllSections,AbsMachine,Old,New). |
| 2228 | | append_sections2([],_,M,M). |
| 2229 | | append_sections2([Section|Rest],AbsMachine,Old,New) :- |
| 2230 | | get_section(Section,AbsMachine,Content), |
| 2231 | | append_to_section3(Section,Content,Old,Inter), |
| 2232 | | append_sections2(Rest,AbsMachine,Inter,New). |
| 2233 | | |
| 2234 | | append_to_section3(Section,Content,Old,Inter) :- section_can_have_duplicates(Section),!, |
| 2235 | | append_to_section_and_remove_dups(Section,Content,Old,Inter). |
| 2236 | | append_to_section3(Section,Content,Old,Inter) :- append_to_section(Section,Content,Old,Inter). |
| 2237 | | |
| 2238 | | :- assert_must_succeed((create_machine(abs,EA), create_machine(conc,EB), |
| 2239 | | write_section(abstract_constants,[b(identifier(x),integer,[some_info])],EA,A), |
| 2240 | | write_section(abstract_constants,[b(identifier(x),integer,[other_info]), |
| 2241 | | b(identifier(y),integer,[info])],EB,B), |
| 2242 | | append_if_new(abstract_constants,A,B,ResultM), |
| 2243 | | get_section(abstract_constants,ResultM,ResultConst), |
| 2244 | | ResultConst==[b(identifier(x),integer,[other_info]), |
| 2245 | | b(identifier(y),integer,[info])] |
| 2246 | | )). |
| 2247 | | |
| 2248 | | append_if_new(Section,Machine,In,Out) :- |
| 2249 | | get_section(Section,Machine,Content), |
| 2250 | | select_section(Section,Old,New,In,Out), |
| 2251 | | get_texpr_ids(Old,ExistingIds), |
| 2252 | | exclude(is_in_existing_ids(ExistingIds),Content,NewElements), |
| 2253 | | append(Old,NewElements,New). |
| 2254 | | is_in_existing_ids(ExistingIds,TId) :- |
| 2255 | | get_texpr_id(TId,Id), |
| 2256 | | memberchk(Id,ExistingIds). |
| 2257 | | |
| 2258 | | % filter linking and concrete invariant |
| 2259 | | filter_linking_invariant(Invariant,Linking,Concrete) :- |
| 2260 | | split_conjuncts(Invariant,Predicates), |
| 2261 | | split_list(contains_abstraction_refs,Predicates,Linkings,Concretes), |
| 2262 | | conjunct_predicates(Linkings,Linking), |
| 2263 | | conjunct_predicates(Concretes,Concrete). |
| 2264 | | |
| 2265 | | |
| 2266 | | % contains_abstraction_refs can be used on predicates of the current machine: the abstraction info field has been computed for this machine |
| 2267 | | contains_abstraction_refs(TExpr) :- |
| 2268 | | syntaxtraversion(TExpr,_,_,Infos,Subs,_), |
| 2269 | | ( memberchk(abstraction,Infos) % This info field comes from the last argument of visibility/6 |
| 2270 | | -> true |
| 2271 | ? | ; member(Sub,Subs), |
| 2272 | ? | contains_abstraction_refs(Sub)). |
| 2273 | | |
| 2274 | | % Determine which part of the abstract invariant can be imported into the refinement machine |
| 2275 | | % TODO: should this be applied to WHILE loop INVARIANTS? |
| 2276 | | filter_abstract_invariant(AbsInvariant,SortedConcrVars,ConcreteInv) :- |
| 2277 | | split_conjuncts(AbsInvariant,Predicates), |
| 2278 | | filter_abs_invs(Predicates,SortedConcrVars,Concretes), |
| 2279 | | conjunct_predicates(Concretes,ConcreteInv). %, print('COPY: '), translate:print_bexpr(Concrete),nl. |
| 2280 | | :- use_module(translate,[translate_bexpression/2]). |
| 2281 | | |
| 2282 | | filter_abs_invs([],_,[]). |
| 2283 | | filter_abs_invs([TExpr|TInv],SortedConcrVars,Res) :- |
| 2284 | ? | contains_abstract_variables2(SortedConcrVars,TExpr,Cause),!, |
| 2285 | | (silent_mode(on) -> true |
| 2286 | | ; translate_bexpression(Cause,ID), |
| 2287 | | ajoin(['Discarding abstract INVARIANT (requires variable `',ID,'`): '],Msg), |
| 2288 | | add_message(bmachine_construction,Msg,TExpr,Cause) |
| 2289 | | ), |
| 2290 | | (adapt_invariant(SortedConcrVars,TExpr,NewTExpr) |
| 2291 | | -> Res = [NewTExpr|TRes], |
| 2292 | | add_message(bmachine_construction,'Replaced abstract INVARIANT by: ',NewTExpr,Cause) |
| 2293 | | ; Res=TRes |
| 2294 | | ), |
| 2295 | | filter_abs_invs(TInv,SortedConcrVars,TRes). |
| 2296 | | filter_abs_invs([TExpr|TInv],SortedConcrVars,[TExpr|TRes]) :- |
| 2297 | | filter_abs_invs(TInv,SortedConcrVars,TRes). |
| 2298 | | |
| 2299 | | contains_abstract_variables2(SortedConcrVars,TExpr,Cause) :- |
| 2300 | | syntaxtraversion(TExpr,Expr,Type,Infos,Subs,_), |
| 2301 | | ( memberchk(loc(_,_Mch,abstract_variables),Infos) % are there other things that pose problems: abstract_constants ? |
| 2302 | | -> %print('Abs: '),translate:print_bexpr(TExpr),nl, print(SortedConcrVars),nl, |
| 2303 | | Cause=TExpr, |
| 2304 | | \+ ord_member_nonvar_chk(b(Expr,Type,_),SortedConcrVars) % otherwise variable is re-introduced with same type |
| 2305 | | % in some Event-B models: VARIABLES keyword is used and in refinement VARIABLES are re-listed |
| 2306 | | % TO DO: check what happens when variable not immediately re-introduced |
| 2307 | ? | ; member(Sub,Subs), |
| 2308 | ? | contains_abstract_variables2(SortedConcrVars,Sub,Cause) |
| 2309 | | ). |
| 2310 | | |
| 2311 | | % try and keep part of invariant, e.g., if f:ABS-->Ran translate to f: TYPE +-> Ran |
| 2312 | | adapt_invariant(SortedConcrVars,b(member(LHS,RHS),pred,I),b(member(LHS,NewRHS),pred,I)) :- |
| 2313 | | \+ contains_abstract_variables2(SortedConcrVars,LHS,_), |
| 2314 | | adapt_to_concrete_superset(RHS,SortedConcrVars,NewRHS), |
| 2315 | | \+ get_texpr_expr(NewRHS,typeset). % not really useful |
| 2316 | | |
| 2317 | | adapt_to_concrete_superset(b(E,Type,Info),SortedConcrVars,b(NewE,Type,Info)) :- |
| 2318 | | adapt_super2(E,SortedConcrVars,NewE). % TODO: adapt info fields? |
| 2319 | | adapt_to_concrete_superset(b(_E,Type,_Info),_SortedConcrVars,b(typeset,Type,[])). |
| 2320 | | % Range of function remains concrete, Domain is abstract and no longer available: |
| 2321 | | adapt_super2(PFUN,SortedConcrVars,partial_function(NewDom,RAN)) :- |
| 2322 | | is_fun(PFUN,partial,DOM,RAN), |
| 2323 | | \+ contains_abstract_variables2(SortedConcrVars,RAN,_), |
| 2324 | | adapt_to_concrete_superset(DOM,SortedConcrVars,NewDom). |
| 2325 | | adapt_super2(TFUN,SortedConcrVars,partial_function(NewDom,RAN)) :- |
| 2326 | | is_fun(TFUN,total,DOM,RAN), |
| 2327 | | \+ contains_abstract_variables2(SortedConcrVars,RAN,_), |
| 2328 | | adapt_to_concrete_superset(DOM,SortedConcrVars,NewDom). |
| 2329 | | % Domain of function remains concrete, Range is abstract and no longer available: |
| 2330 | | adapt_super2(PFUN,SortedConcrVars,partial_function(DOM,NewRan)) :- |
| 2331 | | is_fun(PFUN,partial,DOM,RAN), |
| 2332 | | \+ contains_abstract_variables2(SortedConcrVars,DOM,_), |
| 2333 | | adapt_to_concrete_superset(RAN,SortedConcrVars,NewRan). |
| 2334 | | adapt_super2(TFUN,SortedConcrVars,total_function(DOM,NewRan)) :- |
| 2335 | | is_fun(TFUN,total,DOM,RAN), |
| 2336 | | \+ contains_abstract_variables2(SortedConcrVars,DOM,_), |
| 2337 | | adapt_to_concrete_superset(RAN,SortedConcrVars,NewRan). |
| 2338 | | adapt_super2(FUN,SortedConcrVars,partial_function(NewDom,NewRan)) :- |
| 2339 | | is_fun(FUN,_,DOM,RAN), |
| 2340 | | adapt_to_concrete_superset(DOM,SortedConcrVars,NewDom), |
| 2341 | | adapt_to_concrete_superset(RAN,SortedConcrVars,NewRan). |
| 2342 | | % TODO: more cases, intersection(ABS,CONCR) -> CONCR, cartesian_product |
| 2343 | | |
| 2344 | | is_fun(partial_function(DOM,RAN),partial,DOM,RAN). |
| 2345 | | is_fun(partial_injection(DOM,RAN),partial,DOM,RAN). |
| 2346 | | is_fun(partial_surjection(DOM,RAN),partial,DOM,RAN). |
| 2347 | | is_fun(partial_bijection(DOM,RAN),partial,DOM,RAN). |
| 2348 | | is_fun(total_function(DOM,RAN),total,DOM,RAN). |
| 2349 | | is_fun(total_injection(DOM,RAN),total,DOM,RAN). |
| 2350 | | is_fun(total_surjection(DOM,RAN),total,DOM,RAN). |
| 2351 | | is_fun(total_bijection(DOM,RAN),total,DOM,RAN). |
| 2352 | | |
| 2353 | | % --------------------- |
| 2354 | | |
| 2355 | | get_machine_sorted_variables(Machine,SortedAllVars) :- |
| 2356 | | get_section(abstract_variables,Machine,AbsVars), |
| 2357 | | get_section(concrete_variables,Machine,ConcVars), |
| 2358 | | append(ConcVars,AbsVars,AllVars), |
| 2359 | | sort(AllVars,SortedAllVars). |
| 2360 | | |
| 2361 | | split_conjuncts(Expr,List) :- |
| 2362 | | split_conjuncts2(Expr,List,[]). |
| 2363 | | split_conjuncts2(Expr) --> |
| 2364 | | {get_texpr_expr(Expr,conjunct(A,B)),!}, |
| 2365 | | split_conjuncts2(A), |
| 2366 | | split_conjuncts2(B). |
| 2367 | | split_conjuncts2(Expr) --> [Expr]. |
| 2368 | | |
| 2369 | | % copy the abstract operations or re-use their preconditions |
| 2370 | | % TODO: think about copying Initialisation? |
| 2371 | | propagate_abstract_operations(Abstract,Concrete,RefMachines,Result,Ein,Eout) :- |
| 2372 | | get_section(promoted,Abstract,APromoted), |
| 2373 | | get_section(operation_bodies,Abstract,ABodies), |
| 2374 | | % signature: select_section(SecName,OldContent,NewContent,OldMachine,NewMachine) |
| 2375 | | select_section(promoted,CPromotedIn,CPromotedOut,Concrete,Concrete2), |
| 2376 | | select_section(operation_bodies,CBodiesIn,CBodiesOut,Concrete2,Result), |
| 2377 | | propagate_aops(APromoted,ABodies,RefMachines,CPromotedIn,CBodiesIn,CPromotedOut,CBodiesOut,Ein,Eout). |
| 2378 | | propagate_aops([],_ABodies,_RefMachines,CPromoted,CBodies,CPromoted,CBodies,Errors,Errors). |
| 2379 | | propagate_aops([APromoted|ApRest],ABodies,RefMachines,CPromotedIn,CBodiesIn,CPromotedOut,CBodiesOut,Ein,Eout) :- |
| 2380 | | get_operation(APromoted,ABodies,AbstractOp), |
| 2381 | | def_get_texpr_id(APromoted,op(APromotedOpName)), |
| 2382 | | copy_texpr_wo_info(APromoted,CProm), |
| 2383 | ? | ( member(CProm,CPromotedIn) -> |
| 2384 | | debug_format(19,'Refining promoted abstract operation ~w to refinement machine.~n',[APromotedOpName]), |
| 2385 | | extract_concrete_preconditions(AbstractOp,RefMachines,Pre), |
| 2386 | | change_operation(APromoted,ConcreteOpOld,ConcreteOpNew,CBodiesIn,CBodiesRest), |
| 2387 | | add_precondition(Pre,ConcreteOpOld,ConcreteOpNew), % propagate PRE down to concrete operation |
| 2388 | | CPromotedIn = CPromotedRest, |
| 2389 | | Ein = E1 |
| 2390 | | % TO DO: do not copy if event is refined at least once with renaming ! |
| 2391 | ? | ; is_refined_by_some_event(APromotedOpName,CPromotedIn,ConcreteOpName) -> |
| 2392 | | debug_format(19,'Not copying abstract operation ~w to refinement machine, as it is refined by ~w.~n',[APromotedOpName,ConcreteOpName]), |
| 2393 | | CPromotedRest=CPromotedIn, CBodiesRest=CBodiesIn, |
| 2394 | | E1=Ein |
| 2395 | | ; |
| 2396 | | debug_format(19,'Copying abstract operation ~w to refinement machine, as it is not refined.~n',[APromotedOpName]), |
| 2397 | | % TO DO: check that this is also the right thing to do for Atelier-B Event-B |
| 2398 | | % TO DO: check that the variables are also still there |
| 2399 | | check_copied_operation(APromoted,AbstractOp,RefMachines,Ein,E1), |
| 2400 | | append(CPromotedIn,[APromoted],CPromotedRest), |
| 2401 | | append(CBodiesIn,[AbstractOp],CBodiesRest) |
| 2402 | | ), |
| 2403 | | propagate_aops(ApRest,ABodies,RefMachines,CPromotedRest,CBodiesRest,CPromotedOut,CBodiesOut,E1,Eout). |
| 2404 | | |
| 2405 | | is_refined_by_some_event(AbstractName,CPromotedList,ConcreteName) :- |
| 2406 | ? | member(TID,CPromotedList), |
| 2407 | | get_texpr_info(TID,Infos), |
| 2408 | | memberchk(refines_operation(AbstractName),Infos), |
| 2409 | | def_get_texpr_id(TID,ConcreteName). |
| 2410 | | |
| 2411 | | get_operation(TId,Bodies,Operation) :- |
| 2412 | | select_operation(TId,Bodies,Operation,_BodiesRest). |
| 2413 | | change_operation(TId,OldOp,NewOp,OldBodies,[NewOp|NewBodies]) :- |
| 2414 | | select_operation(TId,OldBodies,OldOp,NewBodies). |
| 2415 | | select_operation(TId,Bodies,Operation,BodiesRest) :- |
| 2416 | | copy_texpr_wo_info(TId,OpId), |
| 2417 | | get_texpr_expr(Operation,operation(OpId,_,_,_)), |
| 2418 | ? | select(Operation,Bodies,BodiesRest),!. |
| 2419 | | |
| 2420 | | extract_concrete_preconditions(Op,RefMachines,FPre) :- |
| 2421 | | extract_preconditions_op(Op,Pre), |
| 2422 | | extract_op_arguments(Op,Args), |
| 2423 | | conjunction_to_list(Pre,Pres), |
| 2424 | | % todo: check the "machine" parameter |
| 2425 | | visible_env(machine,operation_bodies,RefMachines,Env1,_Errors,[]), |
| 2426 | | store_variables(Args,Env1,Env), |
| 2427 | | filter_predicates_with_unknown_identifiers(Pres,Env,FPres), |
| 2428 | | conjunct_predicates(FPres,FPre). |
| 2429 | | |
| 2430 | | extract_op_arguments(Op,Params) :- |
| 2431 | | get_texpr_expr(Op,operation(_,_,Params,_)). |
| 2432 | | |
| 2433 | | extract_preconditions_op(OpExpr,Pre) :- |
| 2434 | | get_texpr_expr(OpExpr,operation(_,_,_,Subst)), |
| 2435 | | extract_preconditions(Subst,Pres,_), |
| 2436 | | conjunct_predicates(Pres,Pre). |
| 2437 | | extract_preconditions(TExpr,Pres,Inner) :- |
| 2438 | | get_texpr_expr(TExpr,Expr), |
| 2439 | | extract_preconditions2(Expr,TExpr,Pres,Inner). |
| 2440 | | extract_preconditions2(precondition(Pre,Subst),_,[Pre|Pres],Inner) :- !, |
| 2441 | | extract_preconditions(Subst,Pres,Inner). |
| 2442 | | extract_preconditions2(block(Subst),_,Pres,Inner) :- !, |
| 2443 | | extract_preconditions(Subst,Pres,Inner). |
| 2444 | | extract_preconditions2(_,Inner,[],Inner). |
| 2445 | | |
| 2446 | | :- use_module(btypechecker,[prime_atom0/2]). |
| 2447 | | filter_predicates_with_unknown_identifiers([],_Env,[]). |
| 2448 | | filter_predicates_with_unknown_identifiers([Pred|Prest],Env,Result) :- |
| 2449 | | ( find_unknown_identifier(Pred,Env,_Id) -> |
| 2450 | | !,Result = Rrest |
| 2451 | | ; |
| 2452 | | Result = [Pred|Rrest]), |
| 2453 | | filter_predicates_with_unknown_identifiers(Prest,Env,Rrest). |
| 2454 | | find_unknown_identifier(Pred,Env,Id) :- |
| 2455 | | get_texpr_id(Pred,Id),!, |
| 2456 | | \+ env_lookup_type(Id,Env,_), |
| 2457 | | (atom(Id),prime_atom0(UnprimedId,Id) |
| 2458 | | -> % we have an identifier with $0 at end |
| 2459 | | \+ env_lookup_type(UnprimedId,Env,_) % check unprimed identifier also unknown |
| 2460 | | ; true). |
| 2461 | | find_unknown_identifier(Pred,Env,Id) :- |
| 2462 | | syntaxtraversion(Pred,_,_,_,Subs,Names), |
| 2463 | | store_variables(Names,Env,Subenv), |
| 2464 | | find_unknown_identifier_l(Subs,Subenv,Id). |
| 2465 | | find_unknown_identifier_l([S|_],Env,Id) :- |
| 2466 | | find_unknown_identifier(S,Env,Id),!. |
| 2467 | | find_unknown_identifier_l([_|Rest],Env,Id) :- |
| 2468 | | find_unknown_identifier_l(Rest,Env,Id). |
| 2469 | | |
| 2470 | | :- use_module(library(ordsets),[ord_union/3]). |
| 2471 | | % we add a precondition to an existing operation |
| 2472 | | % Note: we need to update the reads info computed by the type checker (compute_accessed_vars_infos_for_operation) |
| 2473 | | add_precondition(b(truth,_,_),Old,New) :- !, Old=New. |
| 2474 | | add_precondition(Pre,b(Old,T,I),b(New,T,I2)) :- |
| 2475 | | Old=operation(Id,Res,Params,Subst), |
| 2476 | | New=operation(Id,Res,Params,NewSubst), |
| 2477 | | extract_preconditions(Subst,OldPres,Inner), |
| 2478 | | conjunct_predicates([Pre|OldPres],NewPre), |
| 2479 | | create_texpr(precondition(NewPre,Inner),subst,[],NewSubst), |
| 2480 | ? | (select(reads(OldReads),I,I1) % we do not have to update modifies(.), non_det_modifies(.), reads_locals(.),... |
| 2481 | | -> I2=[reads(NewReads)|I1], |
| 2482 | | get_texpr_ids(Params,Ignore), |
| 2483 | | find_identifier_uses(Pre,Ignore,PreUsedIds), |
| 2484 | | ord_union(PreUsedIds,OldReads,NewReads) |
| 2485 | | ; add_internal_error('No reads info for operation: ',add_precondition(Pre,b(Old,T,I),b(New,T,I2))), |
| 2486 | | I2=I). |
| 2487 | | |
| 2488 | | |
| 2489 | | |
| 2490 | | check_copied_operation(OpRef,Op,RefMachines,Ein,Eout) :- |
| 2491 | | % todo: check the "refinement" parameter |
| 2492 | | visible_env(refinement,operation_bodies,RefMachines,Env1,_Errors,[]), |
| 2493 | | get_texpr_id(OpRef,OpId),get_texpr_type(OpRef,OpType), |
| 2494 | | env_store(OpId,OpType,[],Env1,Env), |
| 2495 | | findall(U, find_unknown_identifier(Op,Env,U), Unknown1), |
| 2496 | | ( Unknown1=[] -> Ein=Eout |
| 2497 | | ; |
| 2498 | | sort(Unknown1,Unknown), |
| 2499 | | op(OpName) = OpId, |
| 2500 | | join_ids(Unknown,IdList), |
| 2501 | | (Unknown = [_] -> Plural=[]; Plural=['s']), |
| 2502 | | append([['Operation ',OpName, |
| 2503 | | ' was copied from abstract machine but the identifier'], |
| 2504 | | Plural, |
| 2505 | | [' '], |
| 2506 | | IdList, |
| 2507 | | [' cannot be seen anymore']],Msgs), |
| 2508 | | ajoin(Msgs,Msg), Ein = [error(Msg,none)|Eout] |
| 2509 | | ). |
| 2510 | | join_ids([I],[Msg]) :- !,opname(I,Msg). |
| 2511 | | join_ids([A|Rest],[Msg,','|Mrest]) :- opname(A,Msg),join_ids(Rest,Mrest). |
| 2512 | | opname(op(Id),Id) :- !. |
| 2513 | | opname(Id,Id). |
| 2514 | | |
| 2515 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 2516 | | % split an identifier into a (possible empty) prefix and the name itself |
| 2517 | | % e.g. split_prefix('path.to.machine', 'path.to', 'machine'). |
| 2518 | | split_prefix(Term,Prefix,Main) :- |
| 2519 | | one_arg_term(Functor,Arg,Term),!, |
| 2520 | | one_arg_term(Functor,MArg,Main), |
| 2521 | | split_prefix(Arg,Prefix,MArg). |
| 2522 | | split_prefix(PR,Prefix,Main) :- |
| 2523 | | safe_atom_chars(PR,Chars,split_prefix1), |
| 2524 | | split_prefix2(Chars,Chars,[],CPrefix,CMain), |
| 2525 | | safe_atom_chars(Main,CMain,split_prefix2), |
| 2526 | | safe_atom_chars(Prefix,CPrefix,split_prefix3). |
| 2527 | | split_prefix2([],Main,_,[],Main). |
| 2528 | | split_prefix2([C|Rest],Previous,PrefBefore,Prefix,Main) :- |
| 2529 | | ( C='.' -> |
| 2530 | | append(PrefBefore,RestPrefix,Prefix), |
| 2531 | | split_prefix2(Rest,Rest,[C],RestPrefix,Main) |
| 2532 | | ; |
| 2533 | | append(PrefBefore,[C],NextPref), |
| 2534 | | split_prefix2(Rest,Previous,NextPref,Prefix,Main)). |
| 2535 | | |
| 2536 | | rawmachine_section(Elem,List,Result) :- |
| 2537 | | rawmachine_section_with_opt_desc(Elem,List,Result,_,_). |
| 2538 | | |
| 2539 | | |
| 2540 | | rawmachine_section_with_opt_desc(Elem,List,Result,Desc,Pos) :- section_can_have_description_pragma(Elem), |
| 2541 | | functor(Pattern,Elem,2), |
| 2542 | | arg(2,Pattern,Result), |
| 2543 | | select(description_machine_clause(Pos,DescText,Pattern),List,_),!, |
| 2544 | | (DescText = description_text(_Pos,Desc) -> true ; Desc='?illegal_desc?'). |
| 2545 | | rawmachine_section_with_opt_desc(Elem,List,Result,'',unknown) :- |
| 2546 | | functor(Pattern,Elem,2), |
| 2547 | | arg(2,Pattern,Result), |
| 2548 | ? | select(Pattern,List,Rest),!, |
| 2549 | | (functor(Pattern2,Elem,2),member(Pattern2,Rest) |
| 2550 | | -> arg(1,Pattern2,Pos), |
| 2551 | | add_error(bmachine_construction,'Multiple sections for: ',Elem,Pos) |
| 2552 | | ; true). |
| 2553 | | |
| 2554 | | |
| 2555 | | section_can_have_description_pragma(initialisation). |
| 2556 | | |
| 2557 | | optional_rawmachine_section(Elem,List,Default,Result) :- |
| 2558 | | ( rawmachine_section(Elem,List,Result1) -> Result=Result1 |
| 2559 | | ; Result=Default). |
| 2560 | | |
| 2561 | | |
| 2562 | | one_arg_term(Functor,Arg,Term) :- %print(one_arg_term(Functor,Arg,Term)),nl, |
| 2563 | | functor(Term,Functor,1),arg(1,Term,Arg). |
| 2564 | | |
| 2565 | | % check if a rawmachine section list has a given section |
| 2566 | | rawmachine_section_exists(Elem,List) :- % |
| 2567 | | functor(Pattern,Elem,2), |
| 2568 | ? | (member(Pattern,List) -> true). |
| 2569 | | |
| 2570 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 2571 | | % visibility rules |
| 2572 | | |
| 2573 | | % the visibility/5 predicate declares what a part of a machine can see |
| 2574 | | % visibility(MType, Scope, Section, Access, Info) means: |
| 2575 | | % In a machine of type MType (machine, refinement, implementation), |
| 2576 | | % an expression in a Section (like invariant) can see |
| 2577 | | % the identifiers in the Access sections. Access is a list of |
| 2578 | | % sections, where shortcuts are allowed (see shortcut/2, e.g., shortcut(operations,[unpromoted,promoted]).). |
| 2579 | | % Scope defines where (in relation to the Section) |
| 2580 | | % the section in Access are (local: same machine, included: in an |
| 2581 | | % included machine, etc.) |
| 2582 | | % Info is a list of additional information that is added to each |
| 2583 | | % identifier in the environment to build up. E.g. in an operation |
| 2584 | | % definition, constants are marked readonly. |
| 2585 | | visibility(machine, local, constraints, [parameters],[]). |
| 2586 | | visibility(machine, local, includes, [parameters,sets,constants],[]). |
| 2587 | | visibility(machine, local, properties, [sets,constants],[]). |
| 2588 | | visibility(machine, local, invariant, [parameters,sets,constants,variables],[]). |
| 2589 | | visibility(machine, local, operation_bodies, [parameters,sets,constants],[readonly]). |
| 2590 | | visibility(machine, local, operation_bodies, [operations],Info) :- Info =[readonly,dontcall]. |
| 2591 | | % an operation to be readonly: means we cannot assign to it; but the operation itself can change variables |
| 2592 | | % for allow_operation_calls_in_expr=true we check that operation is inquiry in type checker |
| 2593 | | % (get_preference(allow_operation_calls_in_expr,true) -> Info = [inquiry] |
| 2594 | | % ; Info =[readonly,dontcall] ). % we check in btype(operation_call_in_expr) |
| 2595 | | visibility(machine, local, operation_bodies, [variables],[]). |
| 2596 | ? | visibility(machine, local, initialisation, Access,[not_initialised|Info]) :- visibility(machine,local,operation_bodies,Access,Info). |
| 2597 | | |
| 2598 | | visibility(machine, Scope, assertions, [Allow],[inquiry|DC]) :- |
| 2599 | | (Scope = local -> Allow=operations, DC=[dontcall] ; Allow=promoted, DC=[]), |
| 2600 | | get_preference(allow_operation_calls_in_expr,true). % if we allow calling operations in expressions |
| 2601 | | visibility(machine, Scope, invariant, [promoted],[inquiry]) :- |
| 2602 | | Scope \= local, % do not allow calling local operations in invariant; their correctness relies on the invariant |
| 2603 | | get_preference(allow_operation_calls_in_expr,true). |
| 2604 | | |
| 2605 | | visibility(machine, included, properties, [sets,constants],[]). |
| 2606 | | visibility(machine, included, invariant, [sets,constants,variables],[]). |
| 2607 | | visibility(machine, included, operation_bodies, [sets,constants,variables],[readonly]). |
| 2608 | | visibility(machine, included, operation_bodies, [promoted],Info) :- Info = [readonly]. |
| 2609 | | % for allow_operation_calls_in_expr=true we check that operation is inquiry in type checker |
| 2610 | | % (get_preference(allow_operation_calls_in_expr,true) -> Info = [inquiry] ; Info=[readonly]). |
| 2611 | | %visibility(machine, included, initialisation, [sets,constants,variables,promoted],[readonly]). |
| 2612 | | visibility(machine, included, initialisation, [sets,constants,promoted],[readonly]). |
| 2613 | | visibility(machine, included, initialisation, [variables],Info) :- |
| 2614 | | (get_preference(allow_overriding_initialisation,true) |
| 2615 | | -> Info = [] % allow overriding INITIALISATION / making it more concrete |
| 2616 | | ; Info = [readonly]). % default Atelier-B semantics |
| 2617 | | |
| 2618 | | visibility(machine, used, properties, [sets,constants],[]). |
| 2619 | | visibility(machine, used, invariant, [parameters,sets,constants,variables],[]). |
| 2620 | | visibility(machine, used, operation_bodies, [parameters,sets,constants,variables],[readonly]). |
| 2621 | | visibility(machine, used, initialisation, [parameters,sets,constants,variables],[readonly]). |
| 2622 | | visibility(machine, used, operation_bodies, [operations],[inquiry]) :- |
| 2623 | | get_preference(allow_operation_calls_for_uses,true). %% added by leuschel, allowed in Schneider Book |
| 2624 | | % but not allowed by Atelier-B; see test 2135 |
| 2625 | | |
| 2626 | | visibility(machine, seen, includes, [sets,constants],[]). |
| 2627 | | visibility(machine, seen, properties, [sets,constants],[]). |
| 2628 | | visibility(machine, seen, invariant, [sets,constants],[]). |
| 2629 | | visibility(machine, seen, operation_bodies, [sets,constants,variables],[readonly]). |
| 2630 | | visibility(machine, seen, initialisation, [sets,constants,variables],[readonly]). |
| 2631 | | visibility(machine, seen, operation_bodies, [operations],[inquiry]). %% added by leuschel, allow query operation |
| 2632 | | |
| 2633 | | visibility(refinement, local, Section, Access, Info) :- |
| 2634 | | Section \= assertions, % assertions are handled below |
| 2635 | ? | visibility(machine, local, Section, Access, Info). |
| 2636 | | |
| 2637 | | visibility(refinement, abstraction, includes, [sets,concrete_constants],[]). |
| 2638 | | visibility(refinement, abstraction, properties, [sets,constants],[]). |
| 2639 | | visibility(refinement, abstraction, invariant, [sets,constants,concrete_variables],[]). |
| 2640 | | visibility(refinement, abstraction, invariant, [abstract_variables],[abstraction]). |
| 2641 | | visibility(refinement, abstraction, operation_bodies, [sets,concrete_constants],[readonly]). |
| 2642 | | visibility(refinement, abstraction, operation_bodies, [concrete_variables],[]). |
| 2643 | | |
| 2644 | | visibility(refinement, included, properties, [sets,constants],[]). |
| 2645 | | visibility(refinement, included, invariant, [sets,constants,variables],[]). |
| 2646 | | visibility(refinement, included, operation_bodies, [sets,constants,variables,promoted],[re]). % What is re ??? TO DO: investigate |
| 2647 | | |
| 2648 | | visibility(refinement, seen, includes, [sets,constants],[]). |
| 2649 | | visibility(refinement, seen, properties, [sets,constants],[]). |
| 2650 | | visibility(refinement, seen, invariant, [sets,constants],[]). |
| 2651 | | visibility(refinement, seen, operation_bodies, [sets,constants,variables],[readonly]). |
| 2652 | | visibility(refinement, seen, operation_bodies, [operations],[inquiry]). |
| 2653 | | |
| 2654 | | visibility(refinement, Ref, initialisation, Access, [not_initialised|Info]) :- |
| 2655 | ? | visibility(refinement,Ref,operation_bodies,Access,Info). |
| 2656 | | |
| 2657 | | % assertions have same visibility as invariant |
| 2658 | | visibility(MType, Ref, assertions, Part, Access) :- |
| 2659 | ? | visibility(MType,Ref,invariant,Part,Access). |
| 2660 | | |
| 2661 | | visibility(implementation, Ref, Section, Part, Access) :- |
| 2662 | ? | visibility(refinement, Ref, Section, Part, Access). |
| 2663 | | visibility(implementation, local, values_expression, [concrete_constants,sets],[]). % seems to have no effect (yet); see ArrayValuationAImp |
| 2664 | | visibility(implementation, included, values_expression, [concrete_constants,sets],[]). |
| 2665 | | visibility(implementation, seen, values_expression, [concrete_constants,sets],[]). |
| 2666 | | visibility(implementation, abstraction,values_expression, [concrete_constants,sets],[]). |
| 2667 | | |
| 2668 | | % For predicates over pre- and post-states |
| 2669 | | visibility(MType, Rev, prepost, Access, Info) :- |
| 2670 | ? | visibility(MType, Rev, invariant, Access, Info). |
| 2671 | | visibility(MType, Rev, prepost, Access, [primed,poststate|Info]) :- |
| 2672 | ? | visibility(MType, Rev, invariant, Access, Info). |
| 2673 | | |
| 2674 | | % add error messages for some common mistakes (access to parameters in the properties) |
| 2675 | | visibility(machine, local, properties, [parameters], [error('a parameter cannot be accessed in the PROPERTIES section')]). |
| 2676 | | % the following rule should be helpful for the user, but additionally it also |
| 2677 | | % provides a mechanism to re-introduce abstract variables in the INVARIANT |
| 2678 | | % section of a while loop (to enable this, an identifier is also marked with "abstraction") |
| 2679 | | % in the predicate allow_access_to_abstract_var |
| 2680 | | visibility(refinement, abstraction, operation_bodies, [abstract_variables], |
| 2681 | | [error('illegal access to an abstract variable in an operation'),abstraction]). |
| 2682 | | visibility(refinement, abstraction, operation_bodies, [abstract_constants], |
| 2683 | | [error('illegal access to an abstract constant in an operation (only allowed in WHILE INVARIANT or ASSERT)'),abstraction]). |
| 2684 | | |
| 2685 | | |
| 2686 | | % lookups up all identifier sections that are accessible from |
| 2687 | | % the given section, removes all shortcuts and removes |
| 2688 | | % duplicate entries |
| 2689 | | % |
| 2690 | | % Returns a list of vis(Section,Info) where Section is the |
| 2691 | | % identifier section that can be seen with Info as a list |
| 2692 | | % of additional information |
| 2693 | | expanded_visibility(MType, Ref, Part, Access) :- |
| 2694 | | findall(vis(Section,Infos), |
| 2695 | | ( visibility(MType,Ref,Part,Sections1,Infos), |
| 2696 | | expand_shortcuts(Sections1,Sections), |
| 2697 | | member(Section,Sections)), |
| 2698 | | Access1), |
| 2699 | | sort(Access1,Access). |
| 2700 | | %format('MType=~w, Ref=~w, Part=~w~n Vis=~w~n',[MType,Ref,Part,Access]). |
| 2701 | | |
| 2702 | | % visible_env/6 creates a type environment for a certain |
| 2703 | | % part of a machine by looking up which identifier should |
| 2704 | | % be visible from there and what additional information should |
| 2705 | | % be added (e.g. to restrict access to read-only) |
| 2706 | | % |
| 2707 | | % The visibility/6 facts are used to define which identifier are visible |
| 2708 | | % |
| 2709 | | % MType: machine type (machine, refinement, ...) |
| 2710 | | % Part: part of the machine for which the environment should be created |
| 2711 | | % RefMachines: referred machines that are already typed |
| 2712 | | % Env: The created environment |
| 2713 | | % Errors: errors might be added if multiple variables with the same identifier |
| 2714 | | % are declared |
| 2715 | | visible_env(MType, Part, RefMachines, Env, Err_in, Err_out) :- |
| 2716 | | env_empty(Init), |
| 2717 | | visible_env(MType, Part, RefMachines, Init, Env, Err_in, Err_out). |
| 2718 | | % visible_env/7 is like visible_env/6, but an initial environment |
| 2719 | | % can be given in "In" |
| 2720 | | visible_env(MType, Part, RefMachines, In, Out, Err_in, Err_out) :- |
| 2721 | | foldl(visible_env2(MType,Part),RefMachines,In/Err_in,Out/Err_out). |
| 2722 | | visible_env2(MType,Part,extended_local_ref(Machine),InEnvErr,OutEnvErr) :- !, |
| 2723 | | Scope=local, |
| 2724 | | get_machine_name(Machine,MName), |
| 2725 | | %format('adding identifiers from machine ~w for scope ~w~n',[MName,Scope]), |
| 2726 | | get_section(definitions,Machine,Defs), |
| 2727 | | foldl(env_add_def(MName),Defs,InEnvErr,InEnvErr2), |
| 2728 | | (bmachine:additional_configuration_machine(_MchName,AddMachine), |
| 2729 | | get_section(definitions,AddMachine,AdditionalDefs) |
| 2730 | | -> foldl(env_add_def(MName),AdditionalDefs,InEnvErr2,InterEnvErr) |
| 2731 | | ; InterEnvErr = InEnvErr2 |
| 2732 | | ), |
| 2733 | | expanded_visibility(MType, Scope, Part, Access), |
| 2734 | | foldl(create_vis_env(Scope,MName,Machine),Access,InterEnvErr,OutEnvErr). |
| 2735 | | visible_env2(MType,Part,ref(Scope,Machine),InEnvErr,OutEnvErr) :- |
| 2736 | | get_machine_name(Machine,MName), |
| 2737 | | %format('adding identifiers from machine ~w for scope ~w~n',[MName,Scope]), |
| 2738 | | ( Scope == local -> |
| 2739 | | get_section(definitions,Machine,Defs), |
| 2740 | | %nl,print(adding_defs(Defs)),nl,nl, |
| 2741 | | foldl(env_add_def(MName),Defs,InEnvErr,InterEnvErr) |
| 2742 | | ; |
| 2743 | | InEnvErr=InterEnvErr), |
| 2744 | | expanded_visibility(MType, Scope, Part, Access), |
| 2745 | | foldl(create_vis_env(Scope,MName,Machine),Access,InterEnvErr,OutEnvErr). |
| 2746 | | env_add_def(MName,definition_decl(Name,PType,Pos,Params,Body,_Deps),InEnvErr,OutEnvErr) :- |
| 2747 | | Type = definition(PType,Params,Body), |
| 2748 | | Info = [nodeid(Pos),loc(local,MName,definitions)], |
| 2749 | | create_texpr(identifier(Name),Type,Info,TExpr), |
| 2750 | | add_unique_variable(TExpr,InEnvErr,OutEnvErr),!. |
| 2751 | | env_add_def(MName,Def,EnvErr,EnvErr) :- |
| 2752 | | add_internal_error('Cannot deal with DEFINITION: ',env_add_def(MName,Def)). |
| 2753 | | |
| 2754 | | create_vis_env(Scope,MName,IDS,vis(Section,Infos),InEnvErr,OutEnvErr) :- |
| 2755 | | get_section(Section,IDS,Identifiers1), |
| 2756 | | % get_texpr_ids(Identifiers1,II),format('Got identifiers for ~w:~w = ~w~n',[MName,Section,II]), |
| 2757 | | (Section=freetypes |
| 2758 | | -> generate_free_type_ids(Identifiers1,Identifiers2) |
| 2759 | | ; Identifiers2=Identifiers1), |
| 2760 | | maplist(add_infos_to_identifier([loc(Scope,MName,Section)|Infos]),Identifiers2,Identifiers), |
| 2761 | | l_add_unique_variables(Identifiers,InEnvErr,OutEnvErr). |
| 2762 | | |
| 2763 | | % optimized version of foldl(add_unique_variable,Identifiers,InEnvErr,OutEnvErr). |
| 2764 | | l_add_unique_variables([],Env,Env). |
| 2765 | | l_add_unique_variables([ID|T],InEnvErr,OutEnvErr) :- |
| 2766 | | add_unique_variable(ID,InEnvErr,IntEnv), |
| 2767 | | l_add_unique_variables(T,IntEnv,OutEnvErr). |
| 2768 | | |
| 2769 | | % freetypes: generate IDs for Z freetype section |
| 2770 | | % The classical B FREETYPE section is dealt with in another place (TODO: unify this) |
| 2771 | | generate_free_type_ids([freetype(FTypeId,Cases)|T],[b(identifier(FTypeId),set(freetype(FTypeId)),[])|FT]) :- !, |
| 2772 | | findall(b(identifier(CaseID),CaseType,[]), |
| 2773 | | (member(case(CaseID,ArgType),Cases), gen_freecase_type(ArgType,FTypeId,CaseType)), |
| 2774 | | FT, FT2), |
| 2775 | | %write(generated_freetype_cases(FT)),nl, |
| 2776 | | generate_free_type_ids(T,FT2). |
| 2777 | | generate_free_type_ids(T,T). |
| 2778 | | |
| 2779 | | gen_freecase_type(ArgType,FTypeId,Type) :- nonvar(ArgType), ArgType=constant(_),!, Type = freetype(FTypeId). |
| 2780 | | gen_freecase_type(ArgType,FTypeId,set(couple(ArgType,freetype(FTypeId)))). |
| 2781 | | |
| 2782 | | add_unique_variable(Var1,Old/Err_in,New/Err_out) :- |
| 2783 | | optionally_rewrite_id(Var1,Var), |
| 2784 | | get_texpr_id(Var,Id), |
| 2785 | | get_texpr_type(Var,Type), |
| 2786 | | get_texpr_info(Var,InfosOfNew),!, |
| 2787 | | ( env_lookup_type(Id,Old,_) -> |
| 2788 | | % we have a collision of two identifiers |
| 2789 | | handle_collision(Var,Id,Type,Old,InfosOfNew,New,Err_in,Err_out) |
| 2790 | | ; |
| 2791 | | % no collision, just introduce the new identifier |
| 2792 | | env_store(Id,Type,InfosOfNew,Old,New), |
| 2793 | | %btypechecker:portray_env(New),nl, |
| 2794 | | Err_in=Err_out |
| 2795 | | ). |
| 2796 | | add_unique_variable(Var,Env/Err,Env/Err) :- print(Var),nl, |
| 2797 | | ( Var = b(definition(DEFNAME,_),_,INFO) -> |
| 2798 | | add_error(add_unique_variable,'DEFINITION used in place of Identifier: ',DEFNAME,INFO) |
| 2799 | | ; Var = b(description(Txt,_),_,INFO) -> |
| 2800 | | add_error(add_unique_variable,'Unsupported @desc pragma: ',Txt,INFO) |
| 2801 | | ; |
| 2802 | | add_error(add_unique_variable,'Expected Identifier, but got: ',Var,Var) |
| 2803 | | ). |
| 2804 | | |
| 2805 | | add_infos_to_identifier(NewInfos,In,Out) :- |
| 2806 | | add_texpr_infos(In,NewInfos,Out). |
| 2807 | | |
| 2808 | | % get the ID of the variable, prime it if the infos contain "primed" |
| 2809 | | optionally_rewrite_id(Var,NewVar) :- |
| 2810 | | get_texpr_info(Var,InfosIn), |
| 2811 | | ( selectchk(primed,InfosIn,InfosOut) -> |
| 2812 | | get_texpr_id(Var,Id1), |
| 2813 | | get_texpr_type(Var,Type), |
| 2814 | | atom_concat(Id1,'\'',Id), |
| 2815 | | create_texpr(identifier(Id),Type,InfosOut,NewVar) |
| 2816 | | ; |
| 2817 | | Var = NewVar). |
| 2818 | | |
| 2819 | | :- use_module(translate,[translate_span/2]). |
| 2820 | | % in case of a collision, we have three options: |
| 2821 | | % - overwrite the old identifier, |
| 2822 | | % - ignore the new identifier or |
| 2823 | | % - generate an error message |
| 2824 | | handle_collision(Var,Name,Type,OldEnv,InfosOfNew,NewEnv,Ein,Eout) :- |
| 2825 | | env_lookup_infos(Name,OldEnv,InfosOfExisting), |
| 2826 | | %btypechecker:portray_env(OldEnv),nl, |
| 2827 | ? | ( collision_precedence(Name,InfosOfExisting,InfosOfNew) -> |
| 2828 | | % this identifier should be ignored |
| 2829 | | OldEnv = NewEnv, |
| 2830 | | Ein = Eout |
| 2831 | | ; collision_precedence(Name,InfosOfNew,InfosOfExisting) -> |
| 2832 | | % the existing should be overwritten |
| 2833 | | env_store(Name,Type,InfosOfNew,OldEnv,NewEnv), |
| 2834 | | Ein = Eout |
| 2835 | | ; |
| 2836 | | % generate error and let the environment unchanged |
| 2837 | | (Name = op(IName) -> Kind='Operation identifier'; Name=IName, Kind='Identifier'), |
| 2838 | | get_texpr_pos(Var,Pos1), |
| 2839 | | safe_get_info_pos(InfosOfExisting,Pos2), |
| 2840 | ? | ( double_inclusion_allowed(Name,Pos1,Pos2,InfosOfExisting) |
| 2841 | | -> %print(double_inclusion_of_id_allowed(Name,Type,Pos1,OldEnv,InfosOfExisting)),nl, |
| 2842 | | OldEnv=NewEnv, |
| 2843 | | Ein=Eout |
| 2844 | ? | ; (better_pos(Pos2,Pos1), \+ better_pos(Pos1,Pos2) |
| 2845 | | -> Pos = Pos2, translate_span(Pos1,PS1) |
| 2846 | | ; Pos = Pos1, |
| 2847 | | PS1='' % Pos1 is already part of error span |
| 2848 | | ), |
| 2849 | | translate_inclusion_path(InfosOfExisting,Path2), |
| 2850 | | get_texpr_info(Var,Info1), translate_inclusion_path(Info1,Path1), |
| 2851 | | (Pos1=Pos2, Path1=Path2 |
| 2852 | | -> ajoin([Kind,' \'', IName, '\'', PS1, Path1, ' included twice '],Msg) |
| 2853 | | % This should probably not happen; internal error in machine construction? |
| 2854 | | ; translate_span(Pos2,PS2), |
| 2855 | | %ajoin(['Identifier \'', IName, '\' declared twice at (Line:Col[:File]) ', PS1, Path1, ' and ', PS2, Path2],Msg), |
| 2856 | | ajoin([Kind,' \'', IName, '\'', PS1, Path1, ' already declared at ', PS2, Path2],Msg) |
| 2857 | | ), |
| 2858 | | %format(user_error,'*** ~w~n',[Msg]),trace, |
| 2859 | | Ein = [error(Msg,Pos)|Eout], |
| 2860 | | OldEnv = NewEnv |
| 2861 | | ) |
| 2862 | | ). |
| 2863 | | |
| 2864 | | |
| 2865 | | % example identifier: %b(identifier(aa),integer,[loc(seen,M2,concrete_constants),readonly,usesee(M2,aa,seen),origin([included/M1]),nodeid(pos(18,2,2,11,2,13))]) |
| 2866 | | |
| 2867 | | % try and infer inclusion path of identifier from Infos |
| 2868 | ? | translate_inclusion_path(Infos,Str) :- member(loc(How,Machine,Section),Infos),!, |
| 2869 | | (member(origin(Path),Infos) -> get_origin_path(Path,T) ; T=[')']), |
| 2870 | | ajoin([' (', How,' from ', Section, ' section of ', Machine |T],Str). |
| 2871 | | translate_inclusion_path(_,''). |
| 2872 | | |
| 2873 | | get_origin_path([How/Machine|T],[', ', How,' from ',Machine|TRes]) :- !, |
| 2874 | | get_origin_path(T,TRes). |
| 2875 | | get_origin_path(_,[')']). |
| 2876 | | |
| 2877 | | |
| 2878 | | % SEE ISSUE PROB-403, test 1857 |
| 2879 | | % Correct behaviour related to multiple instantiation is specified in |
| 2880 | | % B Reference Manual (AtelierB 4.2.1), 8.3 B Project/Instantiating and renaming. |
| 2881 | | % => Constants and Sets defined in machines instantiated multiple times CAN be used in the including machine with their original (non-prefixed names). |
| 2882 | | % Note: this code did lead to the constants being added multiple times; this has been fixed in concat_section_contents |
| 2883 | | double_inclusion_allowed(Name,Pos1,Pos2,InfosOfExisting) :- |
| 2884 | | %print(check_double_inclusion(Name,Pos1,Pos2,InfosOfExisting)),nl, |
| 2885 | | Pos1==Pos2, |
| 2886 | | %print(chk(InfosOfExisting)),nl, |
| 2887 | ? | member(loc(LOC,_,Section),InfosOfExisting), |
| 2888 | | %print(try(LOC,Section)),nl, |
| 2889 | | section_can_be_included_multiple_times_nonprefixed(Section), |
| 2890 | | % check that we are in a context of an included machine identifier: |
| 2891 | | (inclusion_directive(LOC,Name,Pos2) |
| 2892 | | -> true |
| 2893 | | ; %LOC is probably local |
| 2894 | ? | member(origin([INCL/_MACHINE|_]),InfosOfExisting), |
| 2895 | | inclusion_directive(INCL,Name,Pos2) |
| 2896 | | ). |
| 2897 | | |
| 2898 | | inclusion_directive(included,_,_). |
| 2899 | | inclusion_directive(used,_,_). |
| 2900 | | inclusion_directive(seen,_,_). % imports ?? |
| 2901 | | inclusion_directive(abstraction,Name,Pos) :- % probably not allowed by Atelier-B |
| 2902 | | (debug_mode(off) -> true |
| 2903 | | ; add_message(bmachine_construction,'Allowing double inclusion from abstraction of: ',Name,Pos)). |
| 2904 | | |
| 2905 | | section_can_be_included_multiple_times_nonprefixed(abstract_constants). |
| 2906 | | section_can_be_included_multiple_times_nonprefixed(concrete_constants). |
| 2907 | | section_can_be_included_multiple_times_nonprefixed(sets). |
| 2908 | | section_can_be_included_multiple_times_nonprefixed(enumerated_sets). |
| 2909 | | section_can_be_included_multiple_times_nonprefixed(enumerated_elements). |
| 2910 | | section_can_be_included_multiple_times_nonprefixed(deferred_sets). % added 2.12.2022 |
| 2911 | | section_can_be_included_multiple_times_nonprefixed(constants). % shortcut |
| 2912 | | |
| 2913 | | |
| 2914 | | % decide which position info is better: prefer info in main file (highlighting) |
| 2915 | | :- use_module(bmachine,[b_get_main_filenumber/1]). |
| 2916 | | better_pos(Pos,_) :- get_position_filenumber(Pos,Filenumber), |
| 2917 | | b_get_main_filenumber(Filenumber). |
| 2918 | | better_pos(_,none). |
| 2919 | | |
| 2920 | | safe_get_info_pos(Info,Pos) :- (get_info_pos(Info,Pos) -> true ; Pos=none). |
| 2921 | | |
| 2922 | | % collision_precedence/3 decides if the first variable takes |
| 2923 | | % precedence over the second in case of a collision |
| 2924 | | % the decision is made by the additional information of both |
| 2925 | | % variables |
| 2926 | | collision_precedence(_Name,PreferredVarInfos,DroppedVarInfos) :- |
| 2927 | | % in case of a re-introduced variable from the abstraction, |
| 2928 | | % we prefer the concrete variable to the abstract one. |
| 2929 | ? | is_abstraction(DroppedVarInfos,PreferredVarInfos),!. |
| 2930 | | collision_precedence(_Name,PreferredVarInfos,DroppedVarInfos) :- |
| 2931 | | % We are checking an Event-B model with multi-level support |
| 2932 | | % and have the same variable in two different refinement levels. |
| 2933 | | % Then the one in the more refined module takes precedence |
| 2934 | | member(level(L1),PreferredVarInfos), |
| 2935 | | member(level(L2),DroppedVarInfos), |
| 2936 | | % Level 0 is the abstract level, level 1 the first refinement, etc. |
| 2937 | | L1 > L2,!. |
| 2938 | | collision_precedence(Name,PreferredVarInfos,DroppedVarInfos) :- |
| 2939 | | % A local definition takes precedence over a non-local identifier |
| 2940 | | % TODO: |
| 2941 | | member(loc(local,_DefMachine,definitions),PreferredVarInfos), |
| 2942 | ? | member(loc(_,_VarMachine,Section),DroppedVarInfos), |
| 2943 | | Section \= definitions,!, |
| 2944 | | (preferences:get_preference(warn_if_definition_hides_variable,true) |
| 2945 | | % default is true; we could also check clash_strict_checks |
| 2946 | ? | -> get_id_kind(Section,HiddenIdKind), |
| 2947 | | (get_info_pos(PreferredVarInfos,Pos1), Pos1 \= none, |
| 2948 | | get_info_pos(DroppedVarInfos,Pos2), Pos2 \= none |
| 2949 | | -> translate:translate_span(Pos1,Pos1Str), translate:translate_span(Pos2,Pos2Str), |
| 2950 | | ajoin(['Warning: DEFINITION of ', Name, ' at ', Pos1Str, |
| 2951 | | ' hides ',HiddenIdKind,' with same name at ', Pos2Str, '.'], Msg) |
| 2952 | | ; ajoin(['Warning: DEFINITION of ', Name, ' hides ',HiddenIdKind,' with same name.'], Msg), |
| 2953 | | Pos1 = unknown |
| 2954 | | ), |
| 2955 | | store_warning(Msg,Pos1) % TO DO: add position info |
| 2956 | | ; true |
| 2957 | | ). |
| 2958 | | % TO DO: allow identical DEFINITIONS ? |
| 2959 | | |
| 2960 | | % translate section name into a name for the identifier |
| 2961 | | get_id_kind(abstract_constants,'constant'). |
| 2962 | | get_id_kind(concrete_constants,'constant'). |
| 2963 | | get_id_kind(parameters,'parameter'). |
| 2964 | | get_id_kind(deferred_sets,'deferred set'). |
| 2965 | | get_id_kind(enumerated_sets,'enumerated set'). |
| 2966 | | get_id_kind(enumerated_elements,'enumerated set element'). |
| 2967 | | get_id_kind(freetypes,'freetype'). |
| 2968 | | get_id_kind(promoted,'operation'). |
| 2969 | | % TODO: complete and use in other places |
| 2970 | | get_id_kind(_SectionName,'variable'). |
| 2971 | | |
| 2972 | | |
| 2973 | | % is_abstraction/2 tries to find out if (in case of a name clash of |
| 2974 | | % two variables) the second variable is just the re-introduced abstract |
| 2975 | | % variable in a refinement. |
| 2976 | | % InfosAbs is the list of information about the abstract variable |
| 2977 | | % InfosConc is the list of information about the concrete variable |
| 2978 | | is_abstraction(InfosAbs,InfosConc) :- |
| 2979 | | % one variable is an abstract variable, introduced in the abstraction |
| 2980 | ? | member(loc(abstraction,_,abstract_variables),InfosAbs), |
| 2981 | | % the other is either an abstract or concrete variable, |
| 2982 | ? | member(VarType,[abstract_variables,concrete_variables]), |
| 2983 | | % introduced either locally or in an included machine |
| 2984 | ? | member(Scope,[local,included]), |
| 2985 | ? | member(loc(Scope,_,VarType),InfosConc). |
| 2986 | | % and the same for constants: |
| 2987 | | is_abstraction(InfosAbs,InfosConc) :- |
| 2988 | | % one variable is an abstract variable, introduced in the abstraction |
| 2989 | ? | member(loc(abstraction,_,abstract_constants),InfosAbs), |
| 2990 | | % the other is either an abstract or concrete variable, |
| 2991 | ? | member(VarType,[abstract_constants,concrete_constants]), |
| 2992 | | % introduced either locally or in an included machine |
| 2993 | ? | member(Scope,[local,included]), |
| 2994 | ? | member(loc(Scope,_,VarType),InfosConc). |
| 2995 | | |
| 2996 | | % shortcuts for sections, to ease the use of typical combinations of |
| 2997 | | % sections |
| 2998 | | shortcut(all_parameters,[parameters,internal_parameters]). |
| 2999 | | shortcut(sets,[deferred_sets,enumerated_sets,enumerated_elements|T]) :- |
| 3000 | | (animation_minor_mode(M), (M=z ; M=eventb) |
| 3001 | | -> T = [freetypes] |
| 3002 | | ; T = []). % The FREETYPES section in classical B is unfortunately dealt with differently and |
| 3003 | | % we currently have errors if add the freetypes section here |
| 3004 | | shortcut(constants,[abstract_constants,concrete_constants]). |
| 3005 | | shortcut(variables,[abstract_variables,concrete_variables]). |
| 3006 | | shortcut(operations,[unpromoted,promoted]). |
| 3007 | | shortcut(identifiers,[all_parameters,sets,constants,variables,operations]). |
| 3008 | | |
| 3009 | | expand_shortcuts(Sections,Expanded) :- |
| 3010 | | foldl(expand_shortcut,Sections,Expanded,[]). |
| 3011 | | expand_shortcut(Section,Sections,RSections) :- |
| 3012 | | ( shortcut(Section,Expanded) -> |
| 3013 | | foldl(expand_shortcut,Expanded,Sections,RSections) |
| 3014 | | ; valid_section(Section) -> |
| 3015 | | Sections = [Section|RSections] |
| 3016 | | ; |
| 3017 | | add_internal_error('invalid section',expand_shortcut(Section,Sections,RSections)),fail). |
| 3018 | | |
| 3019 | | % find sections that can see given sections |
| 3020 | | find_relevant_sections(RSecs,MTypes,Result) :- |
| 3021 | | expand_shortcuts(RSecs,Sections), |
| 3022 | | findall(R, |
| 3023 | | ( member(MType,MTypes), |
| 3024 | | visibility(MType,local,R,SAccess,_), |
| 3025 | | expand_shortcuts(SAccess,Access), |
| 3026 | | member(S,Sections), |
| 3027 | | member(S,Access), |
| 3028 | | valid_section(R)), |
| 3029 | | Result1), |
| 3030 | | sort(Result1,Result). |
| 3031 | | |
| 3032 | | |
| 3033 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 3034 | | % renaming |
| 3035 | | |
| 3036 | | rename_relevant_sections(DefSecs,Renamings,Machine,New) :- |
| 3037 | | find_relevant_sections(DefSecs,[machine],Relevant), |
| 3038 | ? | rename_in_sections(Relevant,Renamings,Machine,New). |
| 3039 | | rename_in_sections([],_,M,M). |
| 3040 | | rename_in_sections([Section|Rest],Renamings,Old,New) :- |
| 3041 | | select_section_texprs(Section,OTExprs,NTExprs,Old,Inter), |
| 3042 | ? | rename_bt_l(OTExprs,Renamings,NTExprs), |
| 3043 | ? | rename_in_sections(Rest,Renamings,Inter,New). |
| 3044 | | |
| 3045 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 3046 | | :- use_module(kernel_freetypes,[register_freetypes/1]). |
| 3047 | | % cleaning up machine |
| 3048 | | clean_up_machine(In,RefMachines,Out) :- |
| 3049 | | extract_parameter_types([ref(local,In)|RefMachines],NonGroundExceptions), |
| 3050 | | clean_up_machine2(NonGroundExceptions,In,Out). |
| 3051 | | clean_up_machine2(NonGroundExceptions) --> |
| 3052 | | get_section_content(enumerated_sets,Enum), |
| 3053 | | get_section_content(enumerated_elements,Elems), |
| 3054 | | {register_enumerated_sets(Enum,Elems)}, % register name of enumerated sets, e.g., to detect finite in ast_cleanup |
| 3055 | | get_section_content(freetypes,Freetypes), |
| 3056 | | {register_freetypes(Freetypes)}, % so that info is available in ast_cleanup for eval_set_extension |
| 3057 | | clean_up_section(constraints,NonGroundExceptions), |
| 3058 | | clean_up_section(properties,NonGroundExceptions), |
| 3059 | | clean_up_section(invariant,NonGroundExceptions), |
| 3060 | | clean_up_section(initialisation,NonGroundExceptions), |
| 3061 | | clean_up_section(assertions,NonGroundExceptions), |
| 3062 | | clean_up_section(operation_bodies,NonGroundExceptions). |
| 3063 | | :- load_files(library(system), [when(compile_time), imports([environ/2])]). |
| 3064 | | :- if(environ(prob_safe_mode,true)). |
| 3065 | | clean_up_section(Section,NonGroundExceptions,In,Out) :- |
| 3066 | | select_section_texprs(Section,Old,New,In,Out), |
| 3067 | | %format('Cleaning up and optimizing section ~w~n',[Section]), %maplist(check_ast,Old), % this will raise errors |
| 3068 | | clean_up_l_with_optimizations(Old,NonGroundExceptions,New,Section), |
| 3069 | | %format('Checking result of clean_up section ~w~n',[Section]), |
| 3070 | | maplist(check_ast(true),New), |
| 3071 | | formatsilent('Finished checking section ~w~n',[Section]). |
| 3072 | | :- else. |
| 3073 | | clean_up_section(Section,NonGroundExceptions,In,Out) :- |
| 3074 | | % debug_stats(cleaning_up(Section)), |
| 3075 | | select_section_texprs(Section,Old,New,In,Out), |
| 3076 | | clean_up_l_with_optimizations(Old,NonGroundExceptions,New,Section). |
| 3077 | | :- endif. |
| 3078 | | |
| 3079 | | get_section_content(SecName,SectionContent,Mch,Mch) :- get_section(SecName,Mch,SectionContent). |
| 3080 | | |
| 3081 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 3082 | | % type expressions in context of an already type-checked machine |
| 3083 | | % TO DO: maybe do some performance optimisations for identifiers, values, ... to avoid creating scope, see formula_typecheck2_for_eval optimisation |
| 3084 | | % b_type_literal does it in bmachine before calling type_in_machine_l |
| 3085 | | type_in_machine_l(Exprs,Scope,Machine,Types,TExprs,Errors) :- |
| 3086 | | MType = machine, |
| 3087 | | create_scope_if_necessary(Exprs,Scope,MType,Machine,Env,Errors,E1), % this can be expensive for big B machines |
| 3088 | | %runtime_profiler:profile_single_call(create_scope,unknown, |
| 3089 | | % bmachine_construction:create_scope(Scope,MType,Machine,Env,Errors,E1)), |
| 3090 | | btype_ground_dl(Exprs,Env,[],Types,TExprUncleans,E1,[]), |
| 3091 | | perform_post_static_check(TExprUncleans), |
| 3092 | ? | maplist(clean_up_pred_or_expr([]),TExprUncleans,TExprs). |
| 3093 | | |
| 3094 | | % detect a few common expressions which do not require creating a scope of all the identifiers |
| 3095 | | create_scope_if_necessary([E],_Scope,MType,Machine,Env,Ein,Eout) :- raw_expr_wo_ids(E),!, |
| 3096 | | create_scope([],MType,Machine,Env,Ein,Eout). |
| 3097 | | create_scope_if_necessary(_Exprs,Scope,MType,Machine,Env,Ein,Eout) :- |
| 3098 | | create_scope(Scope,MType,Machine,Env,Ein,Eout). |
| 3099 | | |
| 3100 | | % a few common raw ASTs which do not refer to identifiers |
| 3101 | | raw_expr_wo_ids(falsity(_)). |
| 3102 | | raw_expr_wo_ids(truth(_)). |
| 3103 | | raw_expr_wo_ids(empty_set(_)). |
| 3104 | | raw_expr_wo_ids(equal(_,A,B)) :- raw_expr_wo_ids(A), raw_expr_wo_ids(B). |
| 3105 | | raw_expr_wo_ids(not_equal(_,A,B)) :- raw_expr_wo_ids(A), raw_expr_wo_ids(B). |
| 3106 | | raw_expr_wo_ids(interval(_,A,B)) :- raw_expr_wo_ids(A), raw_expr_wo_ids(B). |
| 3107 | | raw_expr_wo_ids(add(_,A,B)) :- raw_expr_wo_ids(A), raw_expr_wo_ids(B). |
| 3108 | | raw_expr_wo_ids(minus_or_set_subtract(_,A,B)) :- raw_expr_wo_ids(A), raw_expr_wo_ids(B). |
| 3109 | | raw_expr_wo_ids(mult_or_cart(_,A,B)) :- raw_expr_wo_ids(A), raw_expr_wo_ids(B). |
| 3110 | | raw_expr_wo_ids(unary_minus(_,A)) :- raw_expr_wo_ids(A). |
| 3111 | | raw_expr_wo_ids(boolean_true(_)). |
| 3112 | | raw_expr_wo_ids(boolean_false(_)). |
| 3113 | | raw_expr_wo_ids(bool_set(_)). |
| 3114 | | raw_expr_wo_ids(integer(_,_)). |
| 3115 | | raw_expr_wo_ids(real(_,_)). |
| 3116 | | raw_expr_wo_ids(string(_,_)). |
| 3117 | | |
| 3118 | | |
| 3119 | | % note prob_ids(visible), external_library(all_available_libraries) scope is expanded somewhere else |
| 3120 | | create_scope(pre_expanded_scope(PEnv,Errors),_MType,_Machine,Env,Ein,Eout) :- |
| 3121 | | !, % already pre-expanded |
| 3122 | | Env=PEnv, append(Errors,Eout, Ein). |
| 3123 | | create_scope(Scope,MType,Machine,Env,Ein,Eout) :- |
| 3124 | | env_empty(Init), |
| 3125 | | add_theory_operators(Machine,Init,WithOperators), |
| 3126 | | foldl(create_scope2(MType,Machine),Scope,WithOperators/Ein,Env/Eout). |
| 3127 | | add_theory_operators(Machine,Ein,Eout) :- |
| 3128 | | get_section(operators,Machine,Operators), |
| 3129 | | keys_and_values(Operators,Ids,Ops), |
| 3130 | | foldl(env_store_operator,Ids,Ops,Ein,Eout). |
| 3131 | | create_scope2(MType,Machine,Scope,In/Ein,Out/Eout) :- |
| 3132 | | ( Scope = constants -> |
| 3133 | | visible_env(MType,properties,[ref(local,Machine)],In,Out,Ein,Eout) |
| 3134 | | ; Scope = variables -> |
| 3135 | | visible_env(MType,invariant,[ref(local,Machine)],In,Out,Ein,Eout) |
| 3136 | | ; Scope = variables_and_additional_defs -> |
| 3137 | | visible_env(MType,invariant,[extended_local_ref(Machine)],In,Out,Ein,Eout) |
| 3138 | | ; Scope = assertions_scope_and_additional_defs -> |
| 3139 | | visible_env(MType,assertions,[extended_local_ref(Machine)],In,Out,Ein,Eout) |
| 3140 | | ; Scope = prepost -> |
| 3141 | | visible_env(MType,prepost,[ref(local,Machine)],In,Out,Ein,Eout) |
| 3142 | | ; Scope = operation_bodies -> |
| 3143 | | visible_env(MType,operation_bodies,[ref(local,Machine)],In,Out,Ein,Eout) |
| 3144 | | ; Scope = operation(Op) -> |
| 3145 | | create_operation_scope(Op,Machine,In,Out), Ein=Eout |
| 3146 | | ; Scope = env(ExplicitEnv) -> |
| 3147 | | ExplicitEnv = Out, Ein=Eout |
| 3148 | | ; Scope = identifier(Ids) -> |
| 3149 | | store_variables(Ids,In,Out), Ein=Eout |
| 3150 | | ; Scope = external_library(LibName) % be sure to make these last to avoid duplication errors |
| 3151 | | -> store_ext_defs(LibName,In,Out), Ein=Eout |
| 3152 | | ; |
| 3153 | | add_error(bmachine_construction, 'invalid scope', Scope),fail). |
| 3154 | | create_operation_scope(Op,Machine,In,Out) :- |
| 3155 | | get_section(operation_bodies,Machine,OpBodies), |
| 3156 | | get_texpr_id(OpId,op(Op)), |
| 3157 | | get_texpr_expr(TOp,operation(OpId,Results,Params,_)), |
| 3158 | ? | ( member(TOp,OpBodies) -> |
| 3159 | | append(Results,Params,LocalVariables), |
| 3160 | | store_variables(LocalVariables,In,Out) |
| 3161 | | ; |
| 3162 | | ajoin(['operation \'',Op,'\' not found for building scope'], Msg), |
| 3163 | | add_error(bmachine_construction,Msg),fail). |
| 3164 | | store_variables(Ids,In,Out) :- foldl(store_variable,Ids,In,Out). |
| 3165 | | store_variable(Id,In,Out) :- |
| 3166 | | get_texpr_id(Id,Name),get_texpr_type(Id,Type), |
| 3167 | | env_store(Name,Type,[],In,Out). |
| 3168 | | |
| 3169 | | type_open_predicate_with_quantifier(OptionalOuterQuantifier,Predicate,Scope,Machine,TResult,Errors) :- |
| 3170 | ? | type_open_formula(Predicate,Scope,false,Machine,pred,Identifiers,TPred,Errors), |
| 3171 | | ( Identifiers = [] -> |
| 3172 | | TResult = TPred |
| 3173 | | ; OptionalOuterQuantifier=forall -> |
| 3174 | | create_forall(Identifiers,TPred,TResult) |
| 3175 | | ; OptionalOuterQuantifier=no_quantifier -> |
| 3176 | | TResult = TPred |
| 3177 | | ; % OptionalOuterQuantifier=exists |
| 3178 | | %perform_do_not_enumerate_analysis(Identifiers,TPred,'EXISTS',Span,TPred2), % now done by apply_kodkod_or_other_optimisations |
| 3179 | | create_exists(Identifiers,TPred,TResult) |
| 3180 | | ). |
| 3181 | | |
| 3182 | | type_open_formula(Raw,Scope,AllowOpenIdsinExpressions,Machine,Type,Identifiers,Result,Errors) :- |
| 3183 | | create_scope_if_necessary([Raw],Scope,machine,Machine,Env1,Errors,E1), |
| 3184 | | ( Identifiers==[] -> Mode=closed, Env1=Env |
| 3185 | | ; Mode=open, openenv(Env1,Env)), |
| 3186 | ? | btype_ground_dl([Raw],Env,[],[Type],[TExprUnclean],E1,E2), |
| 3187 | | ( Mode=closed -> true |
| 3188 | | ; |
| 3189 | | openenv_identifiers(Env,Identifiers), % TODO: treat theory operators ? |
| 3190 | | check_ground_types_dl(Identifiers,[],E2,E3) |
| 3191 | | ), |
| 3192 | | %print(type_open_formula(Identifiers,TExprUnclean)),nl, |
| 3193 | | mark_outer_quantifier_ids(TExprUnclean,TExprUnclean2), |
| 3194 | | perform_post_static_check([TExprUnclean2]), |
| 3195 | ? | clean_up_pred_or_expr([],TExprUnclean2,TResult), % TODO: only run if requested |
| 3196 | | ( Identifiers = [] -> % no newly introduced identifiers, no problem |
| 3197 | | E3 = [] |
| 3198 | | ; Type = pred -> % newly introduced identifiers in a predicate - ok |
| 3199 | | E3 = [] |
| 3200 | | ; AllowOpenIdsinExpressions=true -> % we explicitly allow open ids in expressions |
| 3201 | | E3 = [] |
| 3202 | | ; % newly introduced identifiers in expression make no sense |
| 3203 | | % (is that so?) |
| 3204 | | foldl(add_unknown_identifier_error,Identifiers,E3,[]) |
| 3205 | | ), |
| 3206 | | Result = TResult. |
| 3207 | | add_unknown_identifier_error(TId,[error(Msg,Pos)|E],E) :- |
| 3208 | | get_texpr_id(TId,Id), |
| 3209 | | ajoin(['Unknown identifier ',Id,'.'],Msg), |
| 3210 | | % TO DO: generate fuzzy match and possible completions message |
| 3211 | | get_texpr_pos(TId,Pos). |
| 3212 | | |
| 3213 | | % mark outermost identfiers so that they don't get optimized away |
| 3214 | | % e.g., ensure that we print the solution for something like #(y2,x2).(x2 : 0..10 & y2 : 0..10 & x2 = 10 & y2 = 10) |
| 3215 | | mark_outer_quantifier_ids(b(exists(IDS,Pred),pred,Info),Res) :- |
| 3216 | | maplist(mark_id,IDS,MIDS),!, |
| 3217 | | Res = b(exists(MIDS,Pred),pred,Info). |
| 3218 | | mark_outer_quantifier_ids(b(let_predicate(IDS,Exprs,Pred),pred,Info),Res) :- |
| 3219 | | maplist(mark_id,IDS,MIDS),!, |
| 3220 | | Res = b(let_predicate(MIDS,Exprs,Pred),pred,Info). |
| 3221 | | mark_outer_quantifier_ids(b(forall(IDS,LHS,RHS),pred,Info),Res) :- |
| 3222 | | maplist(mark_id,IDS,MIDS),!, |
| 3223 | | Res = b(forall(MIDS,LHS,RHS),pred,Info). |
| 3224 | | mark_outer_quantifier_ids(X,X). |
| 3225 | | |
| 3226 | | mark_id(b(identifier(ID),TYPE,INFO),b(identifier(ID),TYPE,[do_not_optimize_away|INFO])). |
| 3227 | | |
| 3228 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 3229 | | |
| 3230 | | % some additional static checks; they should be run only once before ast_cleanup runs |
| 3231 | | % TO DO: move maybe some of the exists/forall checks here; would be simpler and not require removed_typing inspection |
| 3232 | | |
| 3233 | | :- use_module(bsyntaxtree,[map_over_typed_bexpr/2]). |
| 3234 | | perform_post_static_check([Typed|_]) :- %print(t(Typed)),nl, |
| 3235 | | preferences:get_preference(disprover_mode,false), |
| 3236 | | map_over_typed_bexpr(post_static_check,Typed), |
| 3237 | | fail. |
| 3238 | | perform_post_static_check([_|T]) :- !, perform_post_static_check(T). |
| 3239 | | perform_post_static_check(_). |
| 3240 | | |
| 3241 | | post_static_check(b(E,T,I)) :- |
| 3242 | | %bsyntaxtree:check_infos(I,post_static_check), |
| 3243 | | post_static_check_aux(E,T,I). |
| 3244 | | |
| 3245 | | :- use_module(bsyntaxtree,[find_identifier_uses/3, is_a_disjunct_or_implication/4]). |
| 3246 | | :- use_module(library(ordsets),[ord_subtract/3]). |
| 3247 | | % should detect patterns like { zz | # r__1, q__1, zz . ( ...)} |
| 3248 | | % {x,z|z=1}[{TRUE}] = {1} |
| 3249 | | % %x.(1=1|1)(TRUE) = 1 |
| 3250 | | post_static_check_aux(lambda(Ids,Body,Expr),_T,Info) :- |
| 3251 | | get_texpr_ids(Ids,AtomicIds), sort(AtomicIds,SortedIds), |
| 3252 | | find_identifier_uses(Body,[],UsedIds1), |
| 3253 | | find_identifier_uses(Expr,[],UsedIds2), % relevant for tests 1106, 1264, 1372, 1622 to also look at Expr |
| 3254 | | ord_subtract(SortedIds,UsedIds1,S2), |
| 3255 | | ord_subtract(S2,UsedIds2,UnusedIds), UnusedIds \= [], |
| 3256 | | add_warning_with_info('Condition of lambda does not use these identifiers: ',UnusedIds,Body,Info). |
| 3257 | | post_static_check_aux(comprehension_set(Ids,Body),_T,Info) :- |
| 3258 | | get_texpr_ids(Ids,AtomicIds), sort(AtomicIds,SortedIds), |
| 3259 | | find_identifier_uses(Body,[],UsedIds), |
| 3260 | | ord_subtract(SortedIds,UsedIds,UnusedIds), UnusedIds \= [], |
| 3261 | | add_warning_with_info('Body of comprehension set does not use these identifiers: ',UnusedIds,Body,Info). |
| 3262 | | post_static_check_aux(exists(Ids,P),_T,Info) :- |
| 3263 | | is_a_disjunct_or_implication(P,Type,_Q,_R), |
| 3264 | | exists_body_warning(Ids,P,Info,Type). |
| 3265 | | |
| 3266 | | % see also check_implication_inside_exists ast cleanup rule |
| 3267 | | exists_body_warning(_,_,_,_) :- animation_minor_mode(X),(X=eventb ; X=tla),!. |
| 3268 | | exists_body_warning(Ids,P,I,Type) :- |
| 3269 | | ajoin(['Body of existential quantifier is a(n) ',Type, |
| 3270 | | ' (not allowed by Atelier-B): '],Msg), |
| 3271 | | add_warning_with_info(Msg,b(exists(Ids,P),pred,I),P,I). |
| 3272 | | |
| 3273 | | |
| 3274 | | |
| 3275 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 3276 | | % Warnings |
| 3277 | | |
| 3278 | | :- use_module(bsyntaxtree,[contains_info_pos/1]). |
| 3279 | | add_warning_with_info(Msg1,Msg2,P,Info) :- |
| 3280 | | (contains_info_pos(Info) -> Pos=Info ; Pos=P), |
| 3281 | | add_warning(bmachine_construction,Msg1,Msg2,Pos). |
| 3282 | | |
| 3283 | | :- dynamic warnings/2. |
| 3284 | | clear_warnings :- |
| 3285 | | retractall( warnings(_,_) ). |
| 3286 | | show_warnings :- |
| 3287 | | warnings(Warning,Span), |
| 3288 | | add_warning(bmachine_construction, Warning,'',Span), |
| 3289 | | fail. |
| 3290 | | show_warnings. |
| 3291 | | |
| 3292 | | |
| 3293 | | store_warning(Warning,Span) :- |
| 3294 | | (warnings(Warning,Span) -> true ; assertz(warnings(Warning,Span))). |