1 | | % (c) 2009-2019 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 | | filter_linking_invariant/3, |
12 | | external_procedure_used/1]). |
13 | | |
14 | | :- use_module(library(lists)). |
15 | | :- use_module(library(avl)). |
16 | | |
17 | | :- use_module(self_check). |
18 | | :- use_module(tools). |
19 | | :- use_module(error_manager). |
20 | | :- use_module(debug). |
21 | | :- use_module(preferences). |
22 | | |
23 | | :- use_module(btypechecker). |
24 | | :- use_module(b_ast_cleanup). |
25 | | :- use_module(bsyntaxtree). |
26 | | :- use_module(bmachine_structure). |
27 | | :- use_module(pragmas). |
28 | | |
29 | | :- use_module(bmachine_static_checks). |
30 | | |
31 | | :- use_module(translate,[print_machine/1]). |
32 | | |
33 | | :- use_module(module_information,[module_info/2]). |
34 | | :- module_info(group,typechecker). |
35 | | :- module_info(description,'This module contains the rules for loading, including, seeing, etc. B machines, scope of constants, variables, parameters, etc.'). |
36 | | |
37 | | %maximum_type_errors(100). |
38 | | |
39 | | :- dynamic debug_machine/0. |
40 | | |
41 | | :- use_module(pref_definitions,[b_get_important_preferences_from_raw_machine/2]). |
42 | | set_important_prefs_from_machine(Main,Machines) :- |
43 | ? | find_machine(Main,Machines,_MType,_Header,RawMachine), |
44 | ? | get_raw_model_type(Main,Machines,RawModelType), |
45 | | b_get_important_preferences_from_raw_machine(RawMachine,RawModelType). |
46 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
47 | | |
48 | | :- use_module(preferences,[temporary_set_preference/3,reset_temporary_preference/2]). |
49 | | |
50 | | % typecheck complete machines (incl. includes) |
51 | | check_machine(MainName,MachinesMayContainGenerated,Result,FinalErrors) :- |
52 | ? | clear_warnings, |
53 | ? | debug_println(9,checking_machine(MainName)), |
54 | ? | temporary_set_preference(perform_stricter_static_checks,true,CHNG), |
55 | ? | strip_global_pragmas(MachinesMayContainGenerated,Machines,_WasGenerated), % TODO: is generated still used? |
56 | ? | set_important_prefs_from_machine(MainName,Machines), % set important prefs before type-checking,... |
57 | | % we need to find all SEES/USES declarations, because all references |
58 | | % refer to the same machine and that has to be included exactly once. |
59 | ? | ~~mnf( find_uses(Machines,Uses,NotIncluded,Errors,E1) ), |
60 | | % if a seen/used machine was not included, we include it by constructing |
61 | | % a dummy machine that extends the seen/used machine and the origional |
62 | | % main machine |
63 | ? | ~~mnf( extend_not_included_uses(NotIncluded,MainName,NewMainName,Machines,IMachines) ), |
64 | | % figure out the right order of machines, so that each machine is |
65 | | % type-checked before a dependend machine is loaded |
66 | ? | ~~mnf( machine_order(IMachines,Order) ), |
67 | | % expand (= type-check + includes) machines in the given order |
68 | ? | ~~mnf( expand_machines(Order,IMachines,Uses,[],Results1,E1,E2) ), |
69 | | % until now, the initialisation section consists of a list of init(MachineName,Substitution) |
70 | | % fold_initialisation merges those to one substitution, respecting the dependencies |
71 | | % between the machines |
72 | ? | maplist(fold_initialisation(Order),Results1,Results), |
73 | | % find main machine |
74 | ? | memberchk(machine(NewMainName,MainMachine),Results), %nl,print('MAIN:'),nl,print_machine(MainMachine), |
75 | | % if the main machine has parameters, we convert them to global sets resp. constants |
76 | ? | ~~mnf( convert_parameters_to_global_sets(E2,[],MainMachine,Result1) ), |
77 | | % add some additional informations about the machine |
78 | ? | add_machine_infos(MainName,Machines,Results,Result1,Result), |
79 | | ( debug_machine -> print_machine(Result); true), |
80 | | % finalize the list of errors, remove duplicates |
81 | | sort(Errors,FinalErrors), |
82 | | % output warnings |
83 | | show_warnings, |
84 | | % run static checks on the resulting machine |
85 | | static_check_main_machine(Result), |
86 | | reset_temporary_preference(perform_stricter_static_checks,CHNG). |
87 | | check_machine(Main,_,Result,FinalErrors) :- |
88 | | add_internal_error('Internal error: Checking the machine failed: ', |
89 | | check_machine(Main,_,Result,FinalErrors)), |
90 | | % trace,check_machine(Main,Machines,Result,FinalErrors), |
91 | | fail. |
92 | | |
93 | | strip_global_pragmas([],[],false). |
94 | | strip_global_pragmas([generated(_,M)|Ms],MachinesOut,true) :- !, |
95 | | strip_global_pragmas([M|Ms],MachinesOut,_). |
96 | | strip_global_pragmas([unit_alias(_,Alias,Content,M)|Ms],MachinesOut,true) :- !, |
97 | | assert(pragmas:global_pragma(unit_alias,[Alias|Content])), |
98 | | strip_global_pragmas([M|Ms],MachinesOut,_). |
99 | | strip_global_pragmas([M|Ms],[M|SMs],WasGenerated) :- |
100 | | strip_global_pragmas(Ms,SMs,WasGenerated). |
101 | | |
102 | | fold_initialisation(Order,machine(Name,Sections),machine(Name,NewSections)) :- |
103 | ? | select_section(initialisation,List,Subst,Sections,NewSections), |
104 | ? | maplist(extract_init_substitutions(List),Order,LSubstitutions), |
105 | ? | append(LSubstitutions,Substitutions), |
106 | ? | create_sequence(Substitutions,Subst). |
107 | | extract_init_substitutions(Unsorted,Name,Substitutions) :- |
108 | | convlist(unzip_init(Name),Unsorted,Substitutions). |
109 | | unzip_init(Name,init(Name,Subst),Subst). |
110 | | create_sequence([],Subst) :- create_texpr(skip,subst,[generated],Subst). |
111 | | create_sequence([I],I) :- !. |
112 | | create_sequence(L,Sequence) :- create_texpr(sequence(L),subst,[initial_sequence],Sequence). |
113 | | |
114 | | add_machine_infos(Main,Machines,CheckedMachines,Old,New) :- |
115 | ? | get_raw_model_type(Main,Machines,RawModelType), functor(RawModelType,ModelType,_), % argument is position |
116 | | % model type could be machine or system (or model ?) |
117 | | append_to_section(meta,[model_type/ModelType,hierarchy/Hierarchy],Old,NewT), |
118 | | get_refinement_hierarchy(Main,Machines,Hierarchy), |
119 | | add_refined_machine(Hierarchy,CheckedMachines,NewT,New). |
120 | | get_refinement_hierarchy(Main,Machines,[Main|Abstractions]) :- |
121 | | ( find_refinement(Machines,Main,Abstract) -> |
122 | | get_refinement_hierarchy(Abstract,Machines,Abstractions) |
123 | | ; otherwise -> |
124 | | Abstractions = []). |
125 | | find_refinement([M|Rest],Name,Abstract) :- |
126 | | ( get_constructed_machine_name(M,Name) -> |
127 | | refines(M,Abstract) |
128 | | ; otherwise -> |
129 | | find_refinement(Rest,Name,Abstract)). |
130 | | |
131 | | add_refined_machine([_Main,Refined|_],Machines,Old,New) :- |
132 | | member(machine(Refined,Body),Machines), |
133 | | append_to_section(meta,[refined_machine/Body],Old,New). |
134 | | add_refined_machine(_,_,M,M). % not refining |
135 | | |
136 | | convert_parameters_to_global_sets(Ein,Eout) --> |
137 | | % extract and remove parameters and constraints |
138 | | select_section(parameters,PP,[]), |
139 | | select_section(internal_parameters,IP,[]), |
140 | | {create_texpr(truth,pred,[],Truth)}, |
141 | | select_section(constraints,C,Truth), |
142 | | % split parameters into sets and scalars |
143 | | { split_list(is_set_parameter,PP,Sets,Scalars), |
144 | | foldl(type_set_parameter,Sets,Ein,Eout) }, |
145 | | % put the sets to the deferred sets |
146 | | append_to_section(deferred_sets,Sets), |
147 | | % and the scalars to the constants |
148 | | append_to_section(concrete_constants,Scalars), |
149 | | append_to_section(concrete_constants,IP), |
150 | | % the scalars should be typed by constraints, |
151 | | % so move the constraints to the properties |
152 | | select_section(properties,OldProps,NewProps), |
153 | | {conjunct_predicates([C,OldProps],NewProps)}. |
154 | | is_set_parameter(TExpr) :- |
155 | | % upper case identifiers denote set parameters, otherwise scalars |
156 | | get_texpr_id(TExpr,Name),is_upper_case(Name). |
157 | | type_set_parameter(TExpr,Ein,Eout) :- |
158 | | get_texpr_id(TExpr,Name), |
159 | | get_texpr_type(TExpr,Type), |
160 | | get_texpr_pos(TExpr,Pos), |
161 | | % we directly type the deferred set |
162 | | unify_types_werrors(set(global(Name)),Type,Pos,Ein,Eout). |
163 | | |
164 | | |
165 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
166 | | % included machines |
167 | | |
168 | | expand_machines([],_,_,M,M,Errors,Errors). |
169 | | expand_machines([M|Rest],Machines,Uses,Typed,Result,Ein,Eout) :- |
170 | ? | ~~mnf( expand_machine(M,Machines,Uses,Typed,New,Ein,E1) ), |
171 | ? | expand_machines(Rest,Machines,Uses,[machine(M,New)|Typed],Result,E1,Eout). |
172 | | |
173 | | % resolve all includes and typecheck the machine |
174 | | % expand_machine(Name,Machines,TypeChecked,Expanded) : |
175 | | % Name of the Machine to expand |
176 | | % Machines contains the list of all loaded machines |
177 | | % TypeChecked contains the list of all expanded machines so far |
178 | | % Expanded is the resulting machine |
179 | | expand_machine(Name,Machines,GlobalUses,TypeChecked,Expanded,Ein,Eout) :- |
180 | | % find the machine in the list |
181 | ? | ~~mnf( find_machine(Name,Machines,MType,Header,RawMachineWithPragmas) ), |
182 | | % remove pragmas for later reattachment |
183 | ? | ~~mnf( strip_machine_section_pragmas(RawMachineWithPragmas,RawMachine,_Pragmas) ), |
184 | | % look for abstract machine |
185 | ? | ~~mnf( get_abstractions(Name,Machines,TypeChecked,Abstractions) ), |
186 | | % merge all used and seen machines |
187 | ? | ~~mnf( use_and_see_machines(RawMachine,TypeChecked,SeenRefs) ), |
188 | | % merge all included machines into one machine |
189 | | ~~mnf( include_machines(RawMachine, TypeChecked, GlobalUses, Includes, Parameters, Included, Promotes) ), |
190 | | % Parameters contains now a list of parameters for each included machine |
191 | | % Included contains now a machine that represents all included machines |
192 | | % their parameters are renamed to internal_parameters |
193 | | append([Abstractions,Included,SeenRefs],RefMachines), |
194 | | % typecheck this machine |
195 | | debug_stats(type_machine(Name)), |
196 | | ~~mnf( type_machine(Header,Name,MType,RawMachine,RefMachines,TypedLocal,RefMachines2,Ein,Err1) ), |
197 | | % merge included and including machine |
198 | | debug_stats(merge_included_machines(Name)), |
199 | | ~~mnf( merge_included_machines(Name,TypedLocal,Included,Promotes,Expanded2,Err1,Err2) ), |
200 | | % add some predicates that state the equivalence between arguments |
201 | | % in the include statement and the parameters of the included machine |
202 | | ~~mnf( add_link_constraints(Includes,MType,Parameters,RefMachines2,Expanded2,Expanded3,Err2,Err3) ), |
203 | | % put together refinement and abstraction. |
204 | | ~~mnf( merge_refinement_and_abstraction(Expanded3,[ref(local,TypedLocal)|RefMachines2],Expanded4,Err3,Eout)), |
205 | | % merge used machines, will also prefix |
206 | | debug_stats(merge_used_machines(Name)), |
207 | | ~~mnf( merge_used_machines(Included,Expanded4,Expanded5) ), |
208 | | % clean up the syntax tree |
209 | | debug_stats(clean_up_machine(Name)), |
210 | | ~~mnf( clean_up_machine(Expanded5,RefMachines2,Expanded) ), |
211 | | debug_stats(finished_clean_up_and_expand_machine(Name)). |
212 | | |
213 | | strip_machine_section_pragmas(RawMachineWithPragmas,RawMachine,Pragmas) :- |
214 | | selectchk(units(_Pos,RealVariables,Pragmas),RawMachineWithPragmas,TRawMachine), !, |
215 | | RawMachine = [RealVariables|TRawMachine]. |
216 | | strip_machine_section_pragmas(Machine,Machine,[]). |
217 | | |
218 | | merge_included_machines(Name, TypedLocal, RefMachines, Promotes, Merged, Ein, Eout) :- |
219 | | include(is_included_ref,RefMachines,Included), |
220 | | create_machine(Name,Empty), |
221 | | % move the included operations into the promoted or unpromoted section |
222 | | ~~mnf( move_operations(Included,Promotes,Included2,Ein,Eout) ), |
223 | | LocalAndIncluded = [ref(local,TypedLocal)|Included2], |
224 | | % 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) |
225 | | ~~mnf( concat_sections_of_refs([identifiers,initialisation,operation_bodies, |
226 | | assertions,used,values],LocalAndIncluded,Empty,Merged1) ), |
227 | | ~~mnf( conjunct_sections_of_refs([constraints,properties,invariant],LocalAndIncluded,Merged1,Merged2) ), |
228 | | ~~mnf( concat_section_of_simple_lists(freetypes,LocalAndIncluded,Merged2,Merged3) ), |
229 | | ~~mnf( get_section(definitions,TypedLocal,Defs) ), |
230 | | ~~mnf( write_section(definitions,Defs,Merged3,Merged) ). |
231 | | is_included_ref(ref(included,_)). |
232 | | |
233 | | concat_section_of_simple_lists(Sec,References,In,Out) :- |
234 | | maplist(extract_machine_from_ref,References,Machines), |
235 | | foldl(concat_section_of_simple_lists2(Sec),Machines,In,Out). |
236 | | concat_section_of_simple_lists2(Sec,Machine,In,Out) :- |
237 | | get_section(Sec,In,Orig), |
238 | | get_section(Sec,Machine,List), |
239 | | append(Orig,List,NewList), |
240 | | write_section(Sec,NewList,In,Out). |
241 | | |
242 | | merge_used_machines(RefMachines,Old,New) :- |
243 | | % get all included machines from the references |
244 | | convlist(ref_to_included_machine,RefMachines,Included), |
245 | | foldl(merge_used_machines2,Included,Old,New). |
246 | | merge_used_machines2(Included,Old,New) :- |
247 | | get_section(used,Included,IncludedUse), |
248 | | ( IncludedUse = [] -> New = Old |
249 | | ; otherwise -> |
250 | | rename_used(IncludedUse,Old,New)). |
251 | | ref_to_included_machine(ref(included,Inc),Inc). |
252 | | |
253 | | % will add prefixes to identifiers |
254 | | rename_used(IncludedUse,Old,New) :- |
255 | | %print(renaming(IncludedUse)),nl, |
256 | | expand_shortcuts([properties,invariant, assertions, |
257 | | 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 ? |
258 | | foldl(rename_used_sec(IncludedUse),Sections,Old,New). |
259 | | rename_used_sec(IncludedUse,Sec,Old,New) :- |
260 | | select_section_texprs(Sec,TExprs,NewTExprs,Old,New), |
261 | | rename_used2_l(TExprs,IncludedUse,NewTExprs). |
262 | | |
263 | | rename_used2(TExpr,IncludedUse,NewTExpr) :- |
264 | | get_texpr_expr(TExpr,operation(Id,Params,Results,Subst)),!, |
265 | | rename_used2_l(Params,IncludedUse,NParams), |
266 | | rename_used2_l(Results,IncludedUse,NResults), |
267 | | rename_used2(Subst,IncludedUse,NSubst), |
268 | | get_texpr_type(TExpr,Type), |
269 | | get_texpr_info(TExpr,Info), |
270 | | selectchk(modifies(MIn),Info,Info1),selectchk(reads(RIn),Info1,RestInfo), |
271 | | rename_used_ids(MIn,IncludedUse,MOut), |
272 | | rename_used_ids(RIn,IncludedUse,ROut), |
273 | | create_texpr(operation(Id,NParams,NResults,NSubst),Type,[modifies(MOut),reads(ROut)|RestInfo], |
274 | | NewTExpr). |
275 | | rename_used2(TExpr,IncludedUse,NewTExpr) :- |
276 | | % rename identifier by adding machine prefix M.id |
277 | | get_texpr_id(TExpr,_), % result is also Id (always ??) |
278 | | get_texpr_info(TExpr,Info), |
279 | ? | member(usesee(Name,Id,_Mode),Info), |
280 | ? | member(includeduse(Name,Id,NewTExpr),IncludedUse), % This seems to reuse position information from includeduse list for all identifiers !! TO DO: check this |
281 | | % we now rename Id to Name.Id |
282 | | !. %, print(ren_id(Name,Id)),nl. |
283 | | rename_used2(TExpr,IncludedUse,NTExpr1) :- |
284 | | remove_bt(TExpr,Expr,NExpr,NTExpr), |
285 | | syntaxtransformation(Expr,Subs,_,NSubs,NExpr),!, |
286 | | rename_used2_l(Subs,IncludedUse,NSubs), |
287 | | rename_infos(NTExpr,IncludedUse,NTExpr1). |
288 | | rename_used2(TExpr,IncludedUse,NTExpr) :- |
289 | | add_internal_error('Rename failed: ',rename_used2(TExpr,IncludedUse,NTExpr)), |
290 | | NTExpr = TExpr. |
291 | | |
292 | | % update infos, e.g., read/modifies for while loops |
293 | | rename_infos(b(E,T,I),List,b(E,T,NI)) :- maplist(rename_info(List),I,NI). |
294 | | |
295 | | rename_info(IncludeUseList,Info,NewInfo) :- info_field_contains_ids(Info,I,NewInfo,SNI),!, |
296 | | maplist(rename_usage_info(IncludeUseList),I,NI), sort(NI,SNI). |
297 | | rename_info(_,I,I). |
298 | | |
299 | | info_field_contains_ids(reads(I),I,reads(SNI),SNI). |
300 | | info_field_contains_ids(modifies(I),I,modifies(SNI),SNI). |
301 | | info_field_contains_ids(non_det_modifies(I),I,non_det_modifies(SNI),SNI). |
302 | | info_field_contains_ids(modifies_locals(I),I,modifies_locals(SNI),SNI). |
303 | | info_field_contains_ids(reads_locals(I),I,reads_locals(SNI),SNI). |
304 | | info_field_contains_ids(used_ids(I),I,used_ids(SNI),SNI). |
305 | | |
306 | | rename_usage_info(IncludeUseList,ID,NewID) :- |
307 | ? | (member(includeduse(_,ID,NewTExpr),IncludeUseList) -> get_texpr_id(NewTExpr,NewID) ; NewID = ID). |
308 | | |
309 | | |
310 | | rename_used2_l([],_,R) :- !, R=[]. |
311 | | rename_used2_l([T|Rest],IncludedUse,[NT|NRest]) :- |
312 | | rename_used2(T,IncludedUse,NT), !, |
313 | | rename_used2_l(Rest,IncludedUse,NRest). |
314 | | rename_used2_l(X,Y,Z) :- add_internal_error('Rename failed: ', rename_used2_l(X,Y,Z)),Z=X. |
315 | | |
316 | | rename_used_ids(InIds,IncludedUse,OutIds) :- |
317 | | maplist(rename_used_ids2(IncludedUse),InIds,OutIds). |
318 | | rename_used_ids2(IncludedUse,InId,OutId) :- |
319 | | memberchk(includeduse(_,InId,TOutId),IncludedUse),!, |
320 | | get_texpr_id(TOutId,OutId). |
321 | | rename_used_ids2(_IncludedUse,Id,Id). |
322 | | |
323 | | get_abstractions(CName,Machines,TypedMachines,[ref(abstraction,Abstraction)]) :- |
324 | ? | refines(M,AName), |
325 | | get_constructed_machine_name(M,CName), |
326 | | memberchk(M,Machines),!, |
327 | | memberchk(machine(AName,Abstraction),TypedMachines). |
328 | | get_abstractions(_,_,_,[]). |
329 | | |
330 | | get_includes_and_promotes(Sections,M,Includes,Promotes) :- |
331 | | optional_rawmachine_section(includes,Sections,[],Includes1), |
332 | | optional_rawmachine_section(imports,Sections,[],Imports), |
333 | | optional_rawmachine_section(extends,Sections,[],Extends), |
334 | | optional_rawmachine_section(promotes,Sections,[],Promotes1), |
335 | | append([Includes1,Extends,Imports],Includes), |
336 | | maplist(expand_extends(M),Extends,Promotes2), |
337 | | append([Promotes1|Promotes2],Promotes). |
338 | | expand_extends(Machines,machine_reference(_,Ref,_),Promotes) :- |
339 | | split_prefix(Ref,Prefix,Name), |
340 | | memberchk(machine(Name,Body),Machines), |
341 | | get_section(promoted,Body,Promoted), |
342 | | prefix_identifiers(Promoted,Prefix,Renamings), |
343 | | rename_bt_l(Promoted,Renamings,TPromotes), |
344 | | maplist(add_nonpos,TPromotes,Promotes). |
345 | | add_nonpos(TId,identifier(none,Id)) :- |
346 | | get_texpr_id(TId,op(Id)). |
347 | | |
348 | | move_operations([],Promotes,[],Ein,Eout) :- |
349 | | foldl(add_promotes_not_found_error,Promotes,Ein,Eout). |
350 | | move_operations([ref(_,IncMachine)|IncRest],Promotes,[ref(included,NewIncMachine)|RefRest],Ein,Eout) :- |
351 | | move_operations2(IncMachine,Promotes,NewIncMachine,RestPromotes), |
352 | | move_operations(IncRest,RestPromotes,RefRest,Ein,Eout). |
353 | | move_operations2(Included,Promotes,Result,RestPromotes) :- |
354 | | select_section(promoted,IncOperations,Promoted,Included,Included1), |
355 | | select_section(unpromoted,OldUnpromoted,Unpromoted,Included1,Result), |
356 | | filter_promoted(IncOperations,Promotes,Promoted,NewUnpromoted,RestPromotes), |
357 | | append(OldUnpromoted,NewUnpromoted,Unpromoted). |
358 | | filter_promoted([],Promotes,[],[],Promotes). |
359 | | filter_promoted([TExpr|OpsRest],Promotes,Promoted,Unpromoted,RestPromotes) :- |
360 | | get_texpr_id(TExpr,op(P)), |
361 | ? | ( select(identifier(_,P),Promotes,RestPromotes1) -> |
362 | | !,Promoted = [TExpr|RestPromoted], |
363 | | Unpromoted = RestUnpromoted |
364 | | ; otherwise -> |
365 | | RestPromotes1 = Promotes, |
366 | | Promoted = RestPromoted, |
367 | | Unpromoted = [TExpr|RestUnpromoted]), |
368 | | filter_promoted(OpsRest,RestPromotes1,RestPromoted,RestUnpromoted,RestPromotes). |
369 | | add_promotes_not_found_error(identifier(Pos,Id),[error(Msg,Pos)|E],E) :- |
370 | | ajoin(['Promoted operation ',Id,' not found'],Msg). |
371 | | |
372 | | find_machine(Name,Machines,Type,Header,Sections) :- |
373 | | Header = machine_header(_,Name,_), |
374 | ? | ( (member(abstract_machine(_,_ModelType,Header,Sections),Machines), |
375 | | Type=machine) |
376 | | ; (member(refinement_machine(_,Header,_Abstract,Sections),Machines), |
377 | | Type=refinement) |
378 | | ; (member(implementation_machine(_,Header,_Abstract,Sections),Machines), |
379 | | Type=implementation)), |
380 | | !. |
381 | | |
382 | | include_machines(RawMachine, TypeChecked, GlobalUses, Includes, Parameters, Included, Promotes) :- |
383 | | get_includes_and_promotes(RawMachine,TypeChecked,Includes,Promotes), |
384 | | maplist(include_machine(TypeChecked, GlobalUses), Includes, Parameters, Singles), |
385 | | remove_duplicate_set_inclusions(Singles,Singles1), |
386 | | % create refs |
387 | | maplist(create_ref,Singles1,Included). |
388 | | create_ref(IncMachine,ref(included,IncMachine)). |
389 | | |
390 | | % it is possible that a deferred or enumerated set is declared in an included machine |
391 | | % that is included more than once. We remove all but one occurrences. |
392 | | remove_duplicate_set_inclusions([],[]). |
393 | | remove_duplicate_set_inclusions([M],[M]) :- !. |
394 | | remove_duplicate_set_inclusions([M|Rest],[M|CleanedRest]) :- |
395 | | get_section(deferred_sets,M,DSets), |
396 | | get_section(enumerated_sets,M,ESets), |
397 | | get_section(enumerated_elements,M,EElements), |
398 | | append([DSets,ESets,EElements],Identifiers), |
399 | | maplist(remove_duplicate_sets2(Identifiers),Rest,CleanedRest). |
400 | | remove_duplicate_sets2(Identifiers,M,Cleaned) :- |
401 | | remove_duplicate_sets_section(deferred_sets,Identifiers,M,M1), |
402 | | remove_duplicate_sets_section(enumerated_sets,Identifiers,M1,M2), |
403 | | remove_duplicate_sets_section(enumerated_elements,Identifiers,M2,Cleaned). |
404 | | remove_duplicate_sets_section(Section,Identifiers,In,Out) :- |
405 | | select_section(Section,Old,New,In,Out), |
406 | | exclude(element_is_duplicate(Identifiers),Old,New). |
407 | | element_is_duplicate(Identifiers,TId) :- |
408 | | get_texpr_id(TId,Name), |
409 | | get_texpr_type(TId,Type), |
410 | | get_texpr_id(ToRemove,Name), |
411 | | get_texpr_type(ToRemove,Type), |
412 | | member(ToRemove,Identifiers), |
413 | | get_texpr_info(TId,InfosA), |
414 | | get_texpr_info(ToRemove,InfosB), |
415 | | member(def(Sec,File),InfosA), |
416 | | member(def(Sec,File),InfosB), |
417 | | !. |
418 | | |
419 | | include_machine(TypeChecked,Used,machine_reference(_Pos,FullRef,_Args), |
420 | | parameters(FullRef,Parameters), TM) :- |
421 | | split_prefix(FullRef,Prefix,Name), |
422 | | % TM1 is the already typechecked included machine: |
423 | ? | member(machine(Name,TM1),TypeChecked),!, |
424 | | debug_println(9,including_machine(Name)), |
425 | | % TM2 is a fresh copy, so we prevent unification of the parameter types: |
426 | | copy_term(TM1,TM2), |
427 | | % If the included machine is used somewhere, we store the identifiers |
428 | | % to enable joining the different references later: |
429 | | include_usings(Name,Used,TM2,TM3), |
430 | | % TM3 is typechecked and all internal variables are renamed with a prefix |
431 | | hide_private_information(FullRef,TM3,TM4), |
432 | | % TM4 is typechecked, and if it was referenced with a prefix (e.g. INCLUDES p.M2) |
433 | | % all variables are prefixed |
434 | | %print(prefixing(Prefix)),nl, |
435 | | prefix_machine(Prefix,TM4,TM5), |
436 | | % We need the parameters later to state their equivilance to the arguments |
437 | | get_section(parameters,TM5,Parameters), |
438 | | % We move the parameters to the internal parameters, because the resulting |
439 | | % machine has only the parameters of the including machine. |
440 | | parameters_to_internal(TM5,TM). |
441 | | |
442 | | include_usings(Name,Uses,Old,New) :- |
443 | ? | ( member(Name,Uses) -> |
444 | | store_usage_info(Old,Name,UsedInfo), |
445 | | append_to_section(used,UsedInfo,Old,New) |
446 | | ; otherwise -> |
447 | | Old = New). |
448 | | % returns a list of which identifiers are used in the machine |
449 | | % for each identifier, we have a trible includeuse(Name,Id,TExpr) |
450 | | % where Name is the name of the used machine, Id is the |
451 | | % original ID and TExpr is the currently used reference to this |
452 | | % identifier |
453 | | store_usage_info(Machine,Name,UsedInfo) :- |
454 | | expand_shortcuts([identifiers],IdSections), |
455 | | foldl(store_usage_info2(Machine,Name),IdSections,UsedInfo,[]). |
456 | | store_usage_info2(Machine,Name,Sec,I,O) :- |
457 | | get_section(Sec,Machine,Content), |
458 | | foldl(store_usage_info3(Name),Content,I,O). |
459 | | store_usage_info3(Name,TId,[includeduse(Name,Id,TId)|L],L) :- |
460 | | get_texpr_id(TId,Id). |
461 | | |
462 | | % conjunct sections that contain predicates |
463 | | conjunct_sections_of_refs(Sections1,References,Old,New) :- |
464 | | expand_shortcuts(Sections1,Sections), |
465 | | maplist(extract_machine_from_ref,References,Machines), |
466 | | foldl(conjunct_sections2(Machines),Sections,Old,New). |
467 | | conjunct_sections2(Machines,Section,Old,New) :- |
468 | | write_section(Section,NewContent,Old,New), |
469 | | get_section_of_machines(Machines,Section,Preds), |
470 | | conjunct_predicates(Preds,NewContent). |
471 | | |
472 | | % merge sections that contain a list of expressions |
473 | | concat_sections_of_refs(Sections1,References,Old,New) :- |
474 | | maplist(extract_machine_from_ref,References,Machines), |
475 | | % for each machine, create a tag where the expression comes from |
476 | | maplist(create_tag_by_reference,References,Tags), |
477 | | concat_sections(Sections1,Machines,Tags,Old,New). |
478 | | extract_machine_from_ref(ref(_,M),M). |
479 | | create_tag_by_reference(ref(local,_Machine),[]) :- !. |
480 | | create_tag_by_reference(ref(RefType,Machine),[RefType/Name]) :- |
481 | | get_machine_name(Machine,Name). |
482 | | concat_sections(Sections1,Machines,Tags,Old,New) :- |
483 | | expand_shortcuts(Sections1,Sections), |
484 | | foldl(concat_section2(Machines,Tags),Sections,Old,New). |
485 | | concat_section2(Machines,Tags,Section,Old,New) :- |
486 | | write_section(Section,NewContent,Old,New), |
487 | | maplist(get_tagged_lsection_of_machine(Section),Machines,Tags,Contents), |
488 | | append(Contents,NewContent). |
489 | | get_tagged_lsection_of_machine(Section,Machine,Tags,TaggedContent) :- |
490 | | get_section(Section,Machine,Content), |
491 | | maplist(tag_with_origin(Tags),Content,TaggedContent). |
492 | | tag_with_origin(Origins,TExpr,TaggedExpr) :- |
493 | | change_info_of_expression_or_init(TExpr,Info,TaggedInfo,TaggedExpr), |
494 | | % add a new origin to the old tag or if not existent, add a new info field |
495 | | ( Origins = [] -> TaggedInfo = Info |
496 | | ; selectchk(origin(Orest),Info,origin(Onew),TaggedInfo) -> append(Origins,Orest,Onew) |
497 | | ; otherwise -> TaggedInfo = [origin(Origins)|Info]). |
498 | | % the substitutions in the initialisation are additionally wrapped by an init/2 term |
499 | | % a small hack to work with those to. |
500 | | % TODO: this became a very ugly hack -- redo! |
501 | | change_info_of_expression_or_init(init(A,OExpr),Old,New,init(A,NExpr)) :- |
502 | | !,change_info_of_expression_or_init(OExpr,Old,New,NExpr). |
503 | | % ignore the info for includeduse/3 completely |
504 | | change_info_of_expression_or_init(includeduse(A,B,C),[],_,includeduse(A,B,C)) :- !. |
505 | | change_info_of_expression_or_init(OExpr,Old,New,NExpr) :- |
506 | | create_texpr(Expr,Type,Old,OExpr), create_texpr(Expr,Type,New,NExpr). |
507 | | |
508 | | |
509 | | % adds a prefix to all variables and promoted operations |
510 | | prefix_machine('',TM,TM) :- !. |
511 | | prefix_machine(Prefix,Old,New) :- |
512 | | debug_println(5,prefixing_machine(Prefix)), |
513 | | expand_shortcuts([variables,promoted], RenamedIdentiferSections), |
514 | | get_all_identifiers(RenamedIdentiferSections,Old,Identifiers), |
515 | | prefix_identifiers(Identifiers,Prefix,Renamings), |
516 | | find_relevant_sections(RenamedIdentiferSections,[machine],Sections1), |
517 | | append(RenamedIdentiferSections,Sections1,Sections), |
518 | | rename_in_sections(Sections,Renamings,Old,M), |
519 | | rename_includeduse(M,Renamings,New). |
520 | | rename_includeduse(Old,Renamings,New) :- |
521 | | select_section(used,OldContent,NewContent,Old,New), |
522 | | maplist(rename_includeduse2(Renamings),OldContent,NewContent). |
523 | | rename_includeduse2(Renamings,includeduse(M,N,TExpr),includeduse(M,N,NExpr)) :- |
524 | | rename_bt(TExpr,Renamings,NExpr). |
525 | | |
526 | | get_all_identifiers(Sections1,M,Identifiers) :- |
527 | | expand_shortcuts(Sections1,Sections), |
528 | | maplist(get_all_identifiers2(M),Sections,LIdentifiers), |
529 | | append(LIdentifiers,Identifiers). |
530 | | get_all_identifiers2(M,Sec,Identifiers) :- |
531 | | get_section(Sec,M,Identifiers). |
532 | | |
533 | | % hide all parameters and unpromoted operations |
534 | | hide_private_information(Prefix,Machine,NewMachine) :- |
535 | | get_section(parameters,Machine,Params), |
536 | | get_section(unpromoted,Machine,UnPromoted), |
537 | | append(Params,UnPromoted,ToHide), |
538 | | %debug_println(9,hide_private(Prefix,ToHide)), |
539 | | ( ToHide = [] -> NewMachine=Machine |
540 | | ; otherwise -> |
541 | | prefix_identifiers(ToHide,Prefix,Renamings), |
542 | | % we have to do the renaming in promoted operations, too, because |
543 | | % those operations might use the renamed parameters and their reads/modifies |
544 | | % info must be updated |
545 | | rename_in_sections([parameters,promoted,unpromoted],Renamings,Machine,Machine1), |
546 | | rename_includeduse(Machine1,Renamings,Machine2), |
547 | | rename_relevant_sections([parameters,operations],Renamings,Machine2,NewMachine)). |
548 | | |
549 | | prefix_identifiers(Identifiers,'',Identifiers) :- !. |
550 | | prefix_identifiers(Old,Prefix,New) :- |
551 | | maplist(prefix_identifier(Prefix),Old,New). |
552 | | prefix_identifier(Prefix,TExpr,rename(Old,New)) :- |
553 | | get_texpr_expr(TExpr,identifier(Old)), |
554 | | ( Old=op(OI) -> New=op(NI) |
555 | | ; otherwise -> OI=Old,NI=New), |
556 | | ajoin([Prefix,'.',OI],NI). % , print(rename(Old,New)),nl. |
557 | | |
558 | | parameters_to_internal(M1,M2) :- |
559 | | select_section(internal_parameters,OldParams,Params,M1,M3), |
560 | | select_section(parameters,NewParams,[],M3,M2), |
561 | | append(OldParams,NewParams,Params). |
562 | | |
563 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
564 | | % uses and sees relations |
565 | | find_uses(Machines,Uses,NotIncluded,Ein,Eout) :- |
566 | | findall(U, use_usage(Machines,_,U), UnsortedUses), |
567 | | sort(UnsortedUses,Uses), |
568 | | check_include_use(Uses,Machines,NotIncluded,Ein,Eout). |
569 | | |
570 | | % check_include_use/5 checks if the used machines are included in any machine |
571 | | % check_include_use(+Uses,+Machines,-NotIncluded,Ein,Eout) |
572 | | % Uses: The list of machine names that are used |
573 | | % Machines: The list of machines |
574 | | % NotIncluded: The list of used machines (their names) that are not included |
575 | | % Ein/Eout: The errors (as difference-list) |
576 | | check_include_use([],_,[],Errors,Errors). |
577 | | check_include_use([U|Rest],Machines,NotIncluded,Ein,Eout) :- |
578 | | findall(I,include_usage(Machines,U,I),Inc), |
579 | | ( Inc=[] -> NotIncluded = [U|RestNotIncluded], Ein=E1 |
580 | | ; Inc=[_] -> NotIncluded = RestNotIncluded, Ein=E1 |
581 | | ; otherwise -> |
582 | | NotIncluded = RestNotIncluded, |
583 | | use_usage(Machines,Pos,U), |
584 | | Ein = [error('used machine is included more than once',Pos)|E1] |
585 | | ), |
586 | | check_include_use(Rest,Machines,RestNotIncluded,E1,Eout). |
587 | | % extend_not_included_uses(+Uses,+Main,-Name,+Machines,-AllMachines): |
588 | | % Create a dummy machine that extends the main machine and extends or includes |
589 | | % all seen/used machines that are not included if such machines exist. |
590 | | % In case of refinement this is done for the whole refinement chain. |
591 | | % Uses: List of machine names of the machines that are used/seen but not included |
592 | | % Main: The name of the main machine |
593 | | % Name: The name of the generated dummy machine (or Main if no dummy machine is generated) |
594 | | % Machines: List of all specified Machines |
595 | | % AllMachines: List of all Machines + the new dummy machine(s) |
596 | | extend_not_included_uses([],Main,Main,Machines,Machines) :- !. |
597 | | extend_not_included_uses(Uses,Main,Name,Machines,AllMachines) :- |
598 | | get_refinement_hierarchy(Main,Machines,RefChain), |
599 | | maplist(extend_not_included_uses2(Uses,Machines),RefChain,NewMachines), |
600 | | append(NewMachines,Machines,AllMachines), |
601 | | dummy_machine_name(Main,Name). |
602 | | extend_not_included_uses2(Uses,Machines,Name,DummyMachine) :- |
603 | | create_dummy_machine(Name,Machines,Parameters,DummyParameters,Sections,DummyMachine), |
604 | | IncludeMain = machine_reference(none,Name,Parameters), |
605 | | ( get_preference(seen_machines_included,true) -> |
606 | | % extend just the main machine, include the used/seen machines |
607 | | References = [extends(none,[IncludeMain]),includes(none,UReferences)] |
608 | | ; otherwise -> |
609 | | % extends the main machine and all used/seen machines |
610 | | References = [extends(none,[IncludeMain|UReferences])]), |
611 | | maplist(find_using(Machines),Uses,UReferences,LUParameters), |
612 | | append([Parameters|LUParameters],DummyParameters), |
613 | | copy_raw_definitions(Name,Machines,OptDefs), |
614 | | % we store a flag "is_dummy" in the machine because we have a special case |
615 | | % later, see type_constraints/7. |
616 | | append([References,[is_dummy],OptDefs],Sections). |
617 | | create_dummy_machine(Name,Machines,Parameters,DummyParameters,Sections,DummyMachine) :- |
618 | | dummy_machine_name(Name,DummyName), |
619 | ? | get_raw_model_type(Name,Machines,ModelType), |
620 | | Header = machine_header(_,Name,Parameters), |
621 | | DummyHeader = machine_header(none,DummyName,DummyParameters), |
622 | ? | member(Machine,Machines), |
623 | | generate_raw_machine(Header,DummyHeader,ModelType,Sections,Machine,DummyMachine),!. |
624 | | |
625 | | generate_raw_machine(OldHeader,NewHeader,_,NewSections, |
626 | | abstract_machine(_, ModelType,OldHeader,_), |
627 | | abstract_machine(none,ModelType,NewHeader,NewSections)). |
628 | | generate_raw_machine(OldHeader,NewHeader,ModelType,NewSections, |
629 | | refinement_machine(_, OldHeader,_Abstract, _), |
630 | | abstract_machine(none,ModelType,NewHeader,NewSections)). |
631 | | generate_raw_machine(OldHeader,NewHeader,ModelType,NewSections, |
632 | | implementation_machine(_, OldHeader,_Abstract, _), |
633 | | abstract_machine(none,ModelType,NewHeader,NewSections)). |
634 | | dummy_machine_name(Name,DummyName) :- |
635 | | atom_concat('MAIN_MACHINE_FOR_',Name,DummyName). |
636 | | |
637 | | find_using(Machines,U,machine_reference(none,Name,Arguments),Arguments) :- |
638 | | % was using(U).U |
639 | | ajoin([U,'.',U],Name), |
640 | ? | member(M,Machines), get_machine_parameters(M,U,Params),!, |
641 | | maplist(add_use_param,Params,Arguments). |
642 | | add_use_param(identifier(_,Param),identifier(none,Name)) :- |
643 | | ( is_upper_case(Param) -> |
644 | | ajoin(['Useparam(',Param,')'],Name) |
645 | | ; otherwise -> |
646 | | ajoin(['useparam(',Param,')'],Name)). |
647 | | |
648 | | % copy_raw_definitions(+Name,+Machines,-OptDefs): |
649 | | % Get the definitions section from a raw (untyped) machine |
650 | | % Name: Name of the machine |
651 | | % Machines: List of Machines |
652 | | % OptDefs: [] if no definitions are present or [Def] if a definition section Def is present |
653 | | copy_raw_definitions(Name,Machines,OptDefs) :- |
654 | ? | get_constructed_machine_name_and_body(M,Name,_,Sections), |
655 | | memberchk(M,Machines),!, |
656 | | Def = definitions(_,_), |
657 | | ( memberchk(Def,Sections) -> |
658 | | OptDefs = [Def] |
659 | | ; otherwise -> |
660 | | OptDefs = []). |
661 | | |
662 | | add_def_dependency_information(DefsIn,DefsOut,Ein,Eout) :- |
663 | | extract_def_name_set(DefsIn,DefNames,DN), |
664 | | maplist(add_def_dep(DN),DefsIn,Defs1), |
665 | | check_for_cyclic_def_dependency(Defs1,DefNames,DefsOut,Ein,Eout). |
666 | | |
667 | | extract_def_name_set(Defs,DefNames,DN) :- |
668 | | maplist(get_def_name,Defs,DefNames), |
669 | | maplist(to_mapset_entry,DefNames,DN1), |
670 | | list_to_avl(DN1,DN). |
671 | | |
672 | | get_def_name(Def,Name) :- arg(1,Def,Name). |
673 | | get_def_pos(Def,Pos) :- arg(3,Def,Pos). |
674 | | get_def_dependencies(Def,Dependencies) :- arg(6,Def,Dependencies). |
675 | | to_mapset_entry(Name,Name-true). |
676 | | |
677 | | add_def_dep(DN,In,Out) :- |
678 | | analyse_definition_dependencies(In,DN,Deps), |
679 | | In = definition_decl(Name,DefType,Pos,Args,RawExpr), |
680 | | Out = definition_decl(Name,DefType,Pos,Args,RawExpr,Deps). |
681 | | |
682 | | check_for_cyclic_def_dependency(Defs,DefNames,DefsOut,Ein,Eout) :- |
683 | | % check if we have a cyclic definition: |
684 | | create_definitions_avl(Defs,DefsAvl), |
685 | | search_for_cyclic_definition(DefNames,DefsAvl,[],Pos,RCycle),!, |
686 | | % if we have found a cyclic definition, generate an error message, ... |
687 | | reverse(RCycle,Cycle),add_arrows(Cycle,Msg0), |
688 | | ajoin(['Found cyclic definitions: '|Msg0],Msg), |
689 | | Ein = [error(Msg,Pos)|E1], |
690 | | % ... remove the definitions in the cycle (to prevent later infinite loops) ... |
691 | | exclude(definition_in_list(Cycle),Defs,Defs1), |
692 | | % ... and check the remaining definitions. |
693 | | check_for_cyclic_def_dependency(Defs1,DefNames,DefsOut,E1,Eout). |
694 | | check_for_cyclic_def_dependency(Defs,_DefNames,Defs,E,E). |
695 | | add_arrows([E],[E]) :- !. |
696 | | add_arrows([E|Erest],[E,'->'|Arest]) :- add_arrows(Erest,Arest). |
697 | | definition_in_list(List,Def) :- |
698 | | get_def_name(Def,Name),memberchk(Name,List). |
699 | | |
700 | | create_definitions_avl(Defs,DefsAvl) :- |
701 | | maplist(split_def_name,Defs,Entries), |
702 | | list_to_avl(Entries,DefsAvl). |
703 | | split_def_name(Def,Name-Def) :- get_def_name(Def,Name). |
704 | | |
705 | | |
706 | | % just a depth-first search to find a cycle |
707 | | search_for_cyclic_definition(DefNames,Definitions,Visited,Pos,Cycle) :- |
708 | ? | member(Name,DefNames),avl_fetch(Name,Definitions,Definition), |
709 | | get_def_pos(Definition,Pos), |
710 | | ( memberchk(Name,Visited) -> |
711 | | Cycle = [Name|Visited] |
712 | | ; otherwise -> |
713 | | get_def_dependencies(Definition,Dependencies), |
714 | | search_for_cyclic_definition(Dependencies,Definitions,[Name|Visited],_,Cycle) |
715 | | ). |
716 | | |
717 | | :- assert_must_succeed(( |
718 | | list_to_avl([def1-true,def2-true,def3-true,def4-true],DefNames), |
719 | | analyse_definition_dependencies( |
720 | | conjunct(none, |
721 | | equals(none, |
722 | | identifier(none,x), |
723 | | identifier(none,def1)), |
724 | | equals(none, |
725 | | definition(none,def4, |
726 | | [function(none, |
727 | | identifier(none,def3), |
728 | | integer(none,5))]), |
729 | | identifier(y))),DefNames,Defs), |
730 | | Defs==[def1,def3,def4] |
731 | | )). |
732 | | % analyse_definition_dependencies(+Expr,+DefinitionNames,Deps): |
733 | | % Expr: raw (i.e. untyped) expression to analyse |
734 | | % DN: AVL set (i.e. mapping from elements to 'true') of the names of the definitions |
735 | | % This is needed to decide if an identifier is a reference to a definition |
736 | | % Deps: A list of used definitions (a list of their names) |
737 | | analyse_definition_dependencies(Expr,DN,Deps) :- |
738 | | analyse_definition_dependencies2(Expr,DN,Unsorted,[]), |
739 | | sort(Unsorted,Deps). |
740 | | analyse_definition_dependencies2(VAR,_DN) --> {var(VAR)},!, |
741 | | {add_internal_error('Variable DEFINITION expression in',analyse_definition_dependencies)}. |
742 | | analyse_definition_dependencies2([Expr|Rest],DN) --> |
743 | | !, analyse_definition_dependencies2(Expr,DN), |
744 | | analyse_definition_dependencies2(Rest,DN). |
745 | | analyse_definition_dependencies2(definition(_Pos,Name,Args),DN) --> |
746 | | !,[Name],analyse_definition_dependencies2(Args,DN). |
747 | | analyse_definition_dependencies2(identifier(_Pos,Name),DN) --> |
748 | | {avl_fetch(Name,DN),!},[Name]. |
749 | | analyse_definition_dependencies2(Expr,DN) --> |
750 | | { compound(Expr),functor(Expr,_Functor,Arity),!}, |
751 | | analyse_definition_dependencies_arg(2,Arity,Expr,DN). |
752 | | analyse_definition_dependencies2(_Expr,_DN) --> []. |
753 | | |
754 | | analyse_definition_dependencies_arg(I,Arity,Expr,DN) --> |
755 | | { I =< Arity,!,arg(I,Expr,Arg),I2 is I+1 }, |
756 | | analyse_definition_dependencies2(Arg,DN), |
757 | | analyse_definition_dependencies_arg(I2,Arity,Expr,DN). |
758 | | analyse_definition_dependencies_arg(_I,_Arity,_Expr,_DN) --> []. |
759 | | |
760 | | |
761 | | |
762 | | get_constructed_machine_name(MachineTerm,Name) :- get_constructed_machine_name_and_body(MachineTerm,Name,_Pos,_). |
763 | | % name and pos; pos can be used for filenumber |
764 | | get_constructed_machine_name_and_filenumber(MachineTerm,Name,Filenumber) :- |
765 | | get_constructed_machine_name_and_body(MachineTerm,Name,Pos,_), |
766 | | (Pos = pos(_,FN,_Srow,_Scol,_Erow,_Ecol) -> Filenumber=FN ; Filenumber=unknown). |
767 | | get_constructed_machine_name_and_body(abstract_machine(_,_ModelType,machine_header(Pos,Name,_),Body),Name,Pos,Body). |
768 | | get_constructed_machine_name_and_body(refinement_machine(_,machine_header(Pos,Name,_),_Abstract,Body),Name,Pos,Body). |
769 | | get_constructed_machine_name_and_body(implementation_machine(_,machine_header(Pos,Name,_),_Abstract,Body),Name,Pos,Body). |
770 | | |
771 | | refines(refinement_machine(_,machine_header(_,_Name,_),Abstract,_Body),Abstract). |
772 | | refines(implementation_machine(_,machine_header(_,_Name,_),Abstract,_Body),Abstract). |
773 | | |
774 | | get_machine_parameters(abstract_machine(_,_ModelType,machine_header(_,Name,Parameters),_),Name,Parameters). |
775 | | get_machine_parameters(refinement_machine(_,machine_header(_,Name,Parameters),_,_),Name,Parameters). |
776 | | get_machine_parameters(implementation_machine(_,machine_header(_,Name,Parameters),_,_),Name,Parameters). |
777 | | |
778 | | get_raw_model_type(Main,Machines,ModelType) :- |
779 | ? | get_constructed_machine_name_and_body(M,Main,_,_), |
780 | | memberchk(M,Machines), |
781 | | ( refines(M,RefName) -> |
782 | | get_raw_model_type(RefName,Machines,ModelType) |
783 | | ; otherwise -> |
784 | | M = abstract_machine(_,ModelType,_,_)). |
785 | | |
786 | | some_machine_name_body(Machines,M,Name,Body) :- |
787 | ? | member(M,Machines), |
788 | | get_constructed_machine_name_and_body(M,Name,_,Body). |
789 | | |
790 | | use_usage(Machines,Pos,Use) :- |
791 | ? | some_machine_name_body(Machines,_,_,Body), |
792 | ? | ( member(sees(Pos,R),Body) |
793 | | ; member(uses(Pos,R),Body)), |
794 | ? | member(identifier(_,PrefixUse),R), |
795 | | split_prefix(PrefixUse,_,Use). |
796 | | % include_usage/3 checks if there is any machine in Machines that |
797 | | % includes/extends/imports the used machine |
798 | | % include_usage(+Machines,+Use,-Name): |
799 | | % Machines: The list of machines |
800 | | % Use: The name of the used machine |
801 | | % Name: The name of the machine that imports the used machine |
802 | | % include_usage/3 fails if there is not such an import |
803 | | include_usage(Machines,Use,Name) :- |
804 | ? | some_machine_name_body(Machines,_,Name,Body), |
805 | | ( member(includes(_,R),Body) |
806 | | ; member(extends(_,R), Body) |
807 | | ; member(imports(_,R), Body)), |
808 | | member(machine_reference(_Pos,PrefixRef,_),R), |
809 | | % The name could be prefixed, we need it without prefix: |
810 | | split_prefix(PrefixRef,_,Use). |
811 | | |
812 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
813 | | % uses and sees clauses |
814 | | |
815 | | % returns a list of references to used and seen machines |
816 | | % the machines contain only identifier sections and |
817 | | % the identifier are prefixed accordingly |
818 | | % all identifier are marked as coming from a seen/used machine |
819 | | use_and_see_machines(Sections,Machines,References) :- |
820 | ? | get_uses_and_sees(Sections,Uses,Sees), |
821 | ? | maplist(use_or_see_machine(used,Machines),Uses,Used), |
822 | ? | maplist(use_or_see_machine(seen,Machines),Sees,Seen), |
823 | | append(Used,Seen,References). |
824 | | |
825 | | % get uses and sees sections from the untyped machines |
826 | | get_uses_and_sees(Sections,Uses,Sees) :- |
827 | | get_uses_or_sees2(sees,Sections,Sees), |
828 | | get_uses_or_sees2(uses,Sections,Uses). |
829 | | get_uses_or_sees2(Mode,Sections,US) :- |
830 | | optional_rawmachine_section(Mode,Sections,[],US1), |
831 | | findall(I,member(identifier(_,I),US1),US). |
832 | | |
833 | | use_or_see_machine(Mode,TypedMachines,Ref,ref(Mode,Result)) :- |
834 | ? | split_prefix(Ref,Prefix,Name), |
835 | ? | memberchk(machine(Name,Typed),TypedMachines), |
836 | ? | create_machine(Name,Empty), |
837 | ? | use_sections([sets],Mode,'',Name,Typed,Empty,M1), |
838 | ? | use_sections([constants,variables,promoted],Mode,Prefix,Name,Typed,M1,Result). |
839 | | use_sections(Sections,Mode,Prefix,MName,Typed,Old,New) :- |
840 | ? | expand_shortcuts(Sections,AllSections), |
841 | ? | foldl(use_section(Mode,Prefix,MName,Typed),AllSections,Old,New). |
842 | | use_section(Mode,Prefix,MName,Machine,Section,OldM,NewM) :- |
843 | ? | get_section_texprs(Section,Machine,Identifiers), |
844 | | write_section(Section,NewIds,OldM,NewM), |
845 | | ( Prefix='' -> |
846 | | Ids1=Identifiers |
847 | | ; otherwise -> |
848 | | prefix_identifiers(Identifiers,Prefix,Renamings), |
849 | | rename_bt_l(Identifiers,Renamings,Ids1)), |
850 | | maplist(add_use_info_to_identifier(Mode,MName),Ids1,NewIds). |
851 | | add_use_info_to_identifier(Mode,Name,TExpr,New) :- |
852 | | get_texpr_id(TExpr,PId), get_texpr_type(TExpr,Type), |
853 | | get_texpr_id(New,PId), get_texpr_type(New,Type), |
854 | | split_prefix(PId,_Prefix,Id), |
855 | | get_texpr_info(TExpr,Info), |
856 | | get_texpr_info(New,[usesee(Name,Id,Mode)|Info]). |
857 | | |
858 | | |
859 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
860 | | % add a section to the machine that describes the linking of |
861 | | % parameters and arguments |
862 | | add_link_constraints(Includes,MType,Parameters,RefMachines,Old,New,Ein,Eout) :- |
863 | | AllRefMachines = [ref(local,Old)|RefMachines], |
864 | | extract_parameter_types(RefMachines,NonGroundExceptions), |
865 | | foldl(add_link_section(MType,Parameters,AllRefMachines,NonGroundExceptions), |
866 | | Includes,Links/Ein,[]/Eout), % TO DO: Daniel check if [] is correct here |
867 | | %print(conjunct_predicates(Links,Link)),nl, |
868 | | conjunct_predicates(Links,Link), |
869 | | select_section(constraints,OConstraints,NConstraints,Old,New), |
870 | | conjunct_predicates([Link,OConstraints],NConstraints). |
871 | | add_link_section(MType,Parameters,RefMachines,NonGroundExceptions, |
872 | | machine_reference(Pos,Ref,Args),Links/Ein,RLinks/Eout) :- |
873 | | visible_env(MType,includes,RefMachines,Env,Ein,E1), |
874 | | memberchk(parameters(Ref,TParameters),Parameters), |
875 | | ( same_length(TParameters, Args) -> |
876 | | get_texpr_types(TParameters,Types), |
877 | | btype_ground_dl(Args,Env,NonGroundExceptions,Types,TArgs,E1,Eout), |
878 | | maplist(create_equality,TParameters,TArgs,LLinks) |
879 | | ; otherwise -> |
880 | | E1 = [error('wrong number of machine arguments',Pos)|Eout], |
881 | | LLinks = []), |
882 | | append(LLinks,RLinks,Links). |
883 | | create_equality(P,A,E) :- |
884 | | create_texpr(equal(P,A),pred,[parameterlink],E). |
885 | | |
886 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
887 | | % type machines |
888 | | |
889 | | type_machine(Header,Name,MType,RawMachine,RefMachines,TypedMachine,NewRefMachines,Ein,Eout) :- |
890 | | Header = machine_header(_,Name,_), |
891 | | create_machine(Name,Empty), |
892 | | % (optional) definitions |
893 | | extract_definitions(RawMachine,Empty,DefsOnly,Ein,E0), |
894 | | debug_stats(extracted_definitions(Name)), |
895 | | % create the identifiers that will be typed, |
896 | | % Local will contain the identifier sections |
897 | | ~~mnf( create_id_sections(Header,RawMachine,Name,DefsOnly,Local)), |
898 | | debug_stats(created_identifier_sections(Name)), |
899 | | ~~mnf( create_freetypes(RawMachine,MType,RefMachines,Local,Local1,E0,E1)), |
900 | | % in case of a refinement, check if all newly defined operations are refinements |
901 | | ~~mnf( link_to_refinement(MType,RawMachine,RefMachines,Local1,Local2,E1,E2)), |
902 | | % extract types that can be variables |
903 | | debug_stats(created_link_to_refinement(Name)), |
904 | | ~~mnf(extract_parameter_types([ref(local,Local2)|RefMachines],NonGroundExceptions)), |
905 | | % check for a VALUES clause. They are a little bit tricky because they can replace |
906 | | % already defined deferred sets by integers or other deferred sets |
907 | | process_values_section(MType,RawMachine,NonGroundExceptions, |
908 | | Local2/E2/RefMachines,Local3/E3/NewRefMachines), |
909 | | % type-check the other sections (properties, invariant, operation_bodies, etc) |
910 | | ~~mnf( type_sections(RawMachine,MType,[ref(local,Local3)|NewRefMachines],NonGroundExceptions, |
911 | | Name,E3,Eout,Local3,TypedMachine)). |
912 | | |
913 | | extract_definitions(RawMachine,In,Out,Ein,Eout) :- |
914 | | optional_rawmachine_section(definitions,RawMachine,[],AllDefinitions), |
915 | | write_section(definitions,Definitions,In,Out), |
916 | | % remove all references to definition files from definitions |
917 | | exclude(is_file_definition,AllDefinitions,Definitions1), |
918 | | % replace expression_definition(...) by defintion(expression,...), etc. |
919 | | change_definition_style(Definitions1,Definitions2), |
920 | | % analyse dependencies |
921 | | add_def_dependency_information(Definitions2,Definitions3,Ein,Eout), |
922 | | % replace external function definitions by calls to the external function |
923 | | replace_external_declarations(Definitions3,Definitions). |
924 | | |
925 | | is_file_definition(file_definition(_Pos,_Filename)). |
926 | | |
927 | | change_definition_style(DefsIn,DefsOut) :- |
928 | | maplist(change_definition_style2,DefsIn,DefsOut). |
929 | | change_definition_style2(conversion(Pos,InnerDef),definition_decl(Name,DefType,InnerPos,Args,conversion(Pos,RawExpr))) :- |
930 | | change_definition_style2(InnerDef,definition_decl(Name,DefType,InnerPos,Args,RawExpr)). |
931 | | change_definition_style2(Def,definition_decl(Name,DefType,Pos,Args,RawExpr)) :- |
932 | | Def =.. [Functor,Pos,Name,Args,RawExpr], |
933 | | maplist(check_def_argument(Name,Pos),Args), |
934 | | map_def_functor(Functor,DefType). |
935 | | map_def_functor(expression_definition,expression). |
936 | | map_def_functor(substitution_definition,substitution). |
937 | | map_def_functor(predicate_definition,predicate). |
938 | | |
939 | | % check formal arguments of definitions , e.g., here xx in square2 is not an identifier: xx == 20; square2(xx) == xx*xx |
940 | | check_def_argument(_,_,identifier(_,_)) :- !. |
941 | | check_def_argument(DefName,DefPos,definition(_,ID,_)) :- !, |
942 | | tools:ajoin(['Formal parameter ', ID, ' is a definition call in Definition: '],Msg), |
943 | | add_error(bmachine_construction,Msg,DefName,DefPos). |
944 | | check_def_argument(DefName,DefPos,FP) :- !, |
945 | | tools:ajoin(['Formal parameter ', FP, ' is not an identifier in Definition: '],Msg), |
946 | | add_error(bmachine_construction,Msg,DefName,DefPos). |
947 | | |
948 | | replace_external_declarations(Definitions,NewDefs) :- |
949 | | split_list(is_external_declaration,Definitions,ExtFunctionDecls,RestDefs), |
950 | | foldl(replace_external_declaration,ExtFunctionDecls,RestDefs,NewDefs). |
951 | | is_external_declaration(definition_decl(DefName,expression,_Pos,_Params,_Def,_Dependencies)) :- |
952 | ? | external_name(DefName,ExtType,_ExpectedDefType,_ExtCall,FunName), |
953 | | (debug_mode(on) -> format('external ~w declared: ~w~n', [ExtType,FunName]) ; true). |
954 | | external_name(DefName,function,expression,external_function_call,FunName) :- |
955 | | atom_concat('EXTERNAL_FUNCTION_',FunName,DefName). |
956 | | external_name(DefName,predicate,predicate,external_pred_call,FunName) :- |
957 | | atom_concat('EXTERNAL_PREDICATE_',FunName,DefName). |
958 | | external_name(DefName,substitution,substitution,external_subst_call,FunName) :- |
959 | | atom_concat('EXTERNAL_SUBSTITUTION_',FunName,DefName). |
960 | | replace_external_declaration(definition_decl(DefName,expression,DefPos,TypeParams,Decl,_Deps),In,Out) :- |
961 | | OldDefinition = definition_decl(FunName,ExpectedDefType,Pos,FunParams,FunDef,Deps), |
962 | | NewDefinition = definition_decl(FunName,ExpectedDefType,Pos,FunParams,ExtCall,Deps), |
963 | ? | ( external_name(DefName,_ExtType,ExpectedDefType,ExtCallFunctor,FunName), |
964 | | ExtCall =.. [ExtCallFunctor,Pos,FunName,FunParams,FunDef, |
965 | | rewrite_protected(TypeParams),rewrite_protected(Decl)], |
966 | | selectchk(OldDefinition,In,NewDefinition,Out) -> |
967 | | assert_external_procedure_used(FunName) |
968 | | ; otherwise -> |
969 | | % no definition found for external function |
970 | | add_error(replace_external_declaration,'No DEFINITION associated with: ',DefName,DefPos), |
971 | | Out=In). |
972 | | |
973 | | reset_bmachine_construction :- reset_external_procedure_used. |
974 | | |
975 | | % maybe this information should be stored somewhere else ?? |
976 | | :- dynamic external_procedure_used/1. |
977 | | reset_external_procedure_used :- retractall(external_procedure_used(_)). |
978 | | assert_external_procedure_used(FunName) :- |
979 | | (external_procedure_used(FunName) -> true ; assert(external_procedure_used(FunName))). |
980 | | |
981 | | link_to_refinement(machine,_RawMachine,_RefMachines,Local,Local,Errors,Errors). |
982 | | link_to_refinement(refinement,RawMachine,RefMachines,Local,NewLocal,Ein,Eout) :- |
983 | | link_to_refinement(implementation,RawMachine,RefMachines,Local,NewLocal,Ein,Eout). |
984 | | link_to_refinement(implementation,RawMachine,RefMachines,Local,NewLocal,Ein,Eout) :- |
985 | | link_to_refinement2(implementation,RawMachine,RefMachines,Local,NewLocal,Ein,Eout). |
986 | | link_to_refinement2(_MType,RawMachine,RefMachines,Local,NewLocal,Ein,Eout) :- |
987 | | memberchk(ref(abstraction,AbstractMachine),RefMachines), |
988 | | copy_constraints(Local,AbstractMachine,NewLocal,Ein,E1), |
989 | | type_vars_in_refinement(AbstractMachine,NewLocal), |
990 | | type_refinement_operations(RawMachine,AbstractMachine,NewLocal,E1,Eout). |
991 | | |
992 | | copy_constraints(Local,AbstractMachine,NewLocal,Ein,Eout) :- |
993 | | get_section(parameters,Local,LocalParameters), |
994 | | get_section(parameters,AbstractMachine,AbstractParameters), |
995 | | check_if_equal_identifiers(LocalParameters,AbstractParameters,Ein,Eout,Local), |
996 | | get_section(constraints,AbstractMachine,Constraints), |
997 | | write_section(constraints,Constraints,Local,NewLocal). |
998 | | check_if_equal_identifiers(Local,Abstract,Ein,Eout,LocalMachine) :- |
999 | | ( same_length(Local,Abstract) -> |
1000 | | foldl(check_if_equal_identifiers2,Local,Abstract,Ein,Eout) |
1001 | | ; otherwise -> |
1002 | | get_texpr_ids(Abstract,AIDs), |
1003 | | get_machine_name(LocalMachine,MachName), |
1004 | | ajoin(['Refinement ',MachName,' must have same Parameters ', AIDs,' as abstract Machine'],Msg), |
1005 | | Ein = [error(Msg,none)|Eout] |
1006 | | ). |
1007 | | check_if_equal_identifiers2(LParam,AParam,Ein,Eout) :- |
1008 | | get_texpr_id(LParam,LName), |
1009 | | get_texpr_id(AParam,AName), |
1010 | | ( LName = AName -> |
1011 | | % type in refinement is the same as in the abstraction |
1012 | | get_texpr_type(LParam,Type), |
1013 | | get_texpr_type(AParam,Type), |
1014 | | Ein=Eout |
1015 | | ; otherwise -> |
1016 | | get_texpr_pos(LParam,Pos), |
1017 | | ajoin(['Parameter ',LName,' of refinement machine must be ', |
1018 | | AName,' like in the abstract machine'],Msg), |
1019 | | Ein = [error(Msg,Pos)|Eout] |
1020 | | ). |
1021 | | |
1022 | | % in case of a refinement, give variables the same type as in the abstract machine |
1023 | | % the same for constants |
1024 | | type_vars_in_refinement(AbstractMachine,ConcreteMachine) :- |
1025 | | pass_type(AbstractMachine,[abstract_variables,concrete_variables], |
1026 | | ConcreteMachine,[abstract_variables,concrete_variables]), |
1027 | | pass_type(AbstractMachine,[abstract_constants,concrete_constants], |
1028 | | ConcreteMachine,[abstract_constants,concrete_constants]). |
1029 | | |
1030 | | % pass the type from variables in Sections1 of Machine1 to |
1031 | | % the variables of the same name in Sections2 of Machine2 |
1032 | | % Machine1,Machine2: a machine |
1033 | | % Sections1, Sections2: a list of section names |
1034 | | pass_type(Machine1,Sections1,Machine2,Sections2) :- |
1035 | | get_sections_and_append(Sections1,Machine1,Vars1), |
1036 | | get_sections_and_append(Sections2,Machine2,Vars2), |
1037 | | maplist(pass_type2(Vars2),Vars1). |
1038 | | get_sections_and_append([],_M,[]). |
1039 | | get_sections_and_append([Sec|RestSections],M,R) :- |
1040 | | get_section(Sec,M,L), append(L,Rest,R), |
1041 | | get_sections_and_append(RestSections,M,Rest). |
1042 | | pass_type2(DstVariables,SrcVariable) :- |
1043 | | get_texpr_id(SrcVariable,Id), |
1044 | | get_texpr_id(DstVariable,Id), |
1045 | | ( memberchk(DstVariable,DstVariables) -> |
1046 | | get_texpr_type(DstVariable,Type), |
1047 | | get_texpr_type(SrcVariable,Type) |
1048 | | ; otherwise -> |
1049 | | true). |
1050 | | |
1051 | | |
1052 | | % in case of a refinement, check if the defined operations are already defined |
1053 | | % in the abstract machine and copy that type. |
1054 | | type_refinement_operations(RawMachine,AbstractMachine,Local,Ein,Eout) :- |
1055 | | get_operation_identifiers(RawMachine,Operations), |
1056 | | ~~mnf(type_refinement_operations2(Operations,Local,AbstractMachine,Ein,Eout)). |
1057 | | type_refinement_operations2([],_,_AbstractMachine,Errors,Errors). |
1058 | | type_refinement_operations2([Op|Rest],M,AbstractMachine,Ein,Eout) :- |
1059 | | get_abstract_operation_name_wo_infos(Op,AOp), |
1060 | | %print(treating_refines_operation(Op,AOp)),nl, |
1061 | | get_texpr_pos(Op,Pos), |
1062 | | % look up the abstract definition |
1063 | | get_abstract_op(AOp,AbstractMachine,Pos,Ein,E1), |
1064 | | % store the type in the identifier section |
1065 | | copy_texpr_wo_info(Op,SOp), |
1066 | | get_section(promoted,M,Operations), |
1067 | | memberchk(SOp, Operations), |
1068 | | % do the rest |
1069 | | type_refinement_operations2(Rest,M,AbstractMachine,E1,Eout). |
1070 | | % looks up the type of the operator in an abstract machine |
1071 | | get_abstract_op(Op,Abstraction,_,Errors,Errors) :- |
1072 | | % look for the operation in promoted and unpromoted |
1073 | | ( get_section(promoted,Abstraction,AbstractOps), member(Op,AbstractOps) |
1074 | | ; get_section(unpromoted,Abstraction,AbstractOps), member(Op,AbstractOps) ), |
1075 | | % forget alternatives |
1076 | | !. |
1077 | | get_abstract_op(Op,_Abstraction,_Pos,Errors,Errors) :- |
1078 | | % we might allow new operations |
1079 | | get_preference(allow_new_ops_in_refinement,true),!, |
1080 | | get_texpr_type(Op,op(_,_)). |
1081 | | get_abstract_op(Op,_,Pos,[error(Msg,Pos)|Eout],Eout) :- |
1082 | | % in case we do not find the operation, store an error |
1083 | | get_texpr_id(Op,op(Id)), |
1084 | | ajoin(['operation ', Id, ' is not defined in the abstract machine'], Msg). |
1085 | | % copy a typed expression without the additional information (just expression and type) |
1086 | | copy_texpr_wo_info(A,B) :- |
1087 | | % copy the expression and type, the additional information may be different |
1088 | | get_texpr_expr(A,Expr),get_texpr_expr(B,Expr), |
1089 | | get_texpr_type(A,Type),get_texpr_type(B,Type). |
1090 | | |
1091 | | get_abstract_operation_name_wo_infos(b(_,Type,Infos),Res) :- |
1092 | | memberchk(refines_operation(RefID),Infos),!, % renaming occurs: |
1093 | | Res = b(identifier(op(RefID)),Type,_). |
1094 | | get_abstract_operation_name_wo_infos(ID,Copy) :- copy_texpr_wo_info(ID,Copy). |
1095 | | |
1096 | | create_id_sections(Header,RawMachine,Name) --> |
1097 | | create_id_sections_header(Header), |
1098 | | %{print(created_header(Name)),nl}, |
1099 | | create_set_sections(RawMachine,Name), |
1100 | | %{print(created_set(Name)),nl}, |
1101 | | create_constants_sections(RawMachine), |
1102 | | %{print(created_constants(Name)),nl}, |
1103 | | create_variables_sections(RawMachine), |
1104 | | %{print(created_variables(Name)),nl}, |
1105 | | create_operations_sections(RawMachine). |
1106 | | |
1107 | | extract_parameter_types(MachineRefs,ParameterTypes) :- |
1108 | | maplist(extract_parameter_types2,MachineRefs,ParameterTypesL), |
1109 | | append(ParameterTypesL,ParameterTypes). |
1110 | | extract_parameter_types2(ref(_,Machine),ParameterTypes) :- |
1111 | | get_section(parameters,Machine,VisibleParams), |
1112 | | get_section(internal_parameters,Machine,InternalParams), |
1113 | | append(VisibleParams,InternalParams,Params), |
1114 | | include(is_a_parameter_set,Params,ParameterSets), |
1115 | | maplist(get_texpr_set_type,ParameterSets,ParameterTypes). |
1116 | | is_a_parameter_set(TExpr) :- |
1117 | | get_texpr_info(TExpr,Info), |
1118 | | memberchk(parameter_set,Info). |
1119 | | |
1120 | | type_sections(RawMachine,MachineType,RefMachines,NonGroundExceptions,Name,Ein,Eout) --> |
1121 | | {debug_stats('TYPING CONSTRAINTS'(Name))}, |
1122 | | type_constraints(MachineType,Name,RawMachine,RefMachines,NonGroundExceptions,Ein,E1), |
1123 | | % Maybe the VALUES section must be moved up later because it may be used to |
1124 | | % substitute types (e.g. deferred sets to integers) for later use |
1125 | | {debug_stats('TYPING PROPERTIES'(Name))}, |
1126 | | type_section_with_single_predicate(properties,Name,[constants],MachineType,RawMachine,RefMachines,NonGroundExceptions,E1,E2), |
1127 | | {debug_stats('TYPING INVARIANT'(Name))}, |
1128 | | type_section_with_single_predicate(invariant,Name,[variables],MachineType,RawMachine,RefMachines,NonGroundExceptions,E2,E3), |
1129 | | {debug_stats('TYPING ASSERTIONS'(Name))}, |
1130 | | type_section_with_predicate_list(assertions,[],MachineType,RawMachine,RefMachines,NonGroundExceptions,E3,E4), |
1131 | | {debug_stats('TYPING INITIALISATION'(Name))}, |
1132 | | type_initialisation_section(RawMachine,Name,MachineType,RefMachines,NonGroundExceptions,E4,E5), |
1133 | | {debug_stats('TYPING OPERATIONS'(Name))}, |
1134 | | type_operations_section(RawMachine,MachineType,RefMachines,NonGroundExceptions,E5,Eout), |
1135 | | {debug_stats('FINISHED TYPING SECTIONS'(Name))}. |
1136 | | |
1137 | | % skip this section, it is copied from the abstract machine and |
1138 | | % does not need to be typed again |
1139 | | type_constraints(refinement,_,_RawMachine,_RefMachines,_NonGroundExceptions,Errors,Errors) --> !. |
1140 | | type_constraints(implementation,_,_RawMachine,_RefMachines,_NonGroundExceptions,Errors,Errors) --> !. |
1141 | | type_constraints(machine,Name,RawMachine,RefMachines,NonGroundExceptions,Ein,Eout) --> |
1142 | | % if the machine is a dummy machine (in presence of a not included seen or used |
1143 | | % machine), we must omit the check that the (lower case) parameters are all typed. |
1144 | | % We can assume that they are properly typed by the included machine. |
1145 | | {( is_dummy_machine(RawMachine) -> IdsToType = [] |
1146 | | ; otherwise -> IdsToType = [parameters])}, |
1147 | | type_section_with_single_predicate(constraints,Name,IdsToType,machine, |
1148 | | RawMachine,RefMachines,NonGroundExceptions,Ein,Eout). |
1149 | | |
1150 | ? | is_dummy_machine(RawMachine) :- member(is_dummy,RawMachine),!. |
1151 | | |
1152 | | % Header: Parameters |
1153 | | create_id_sections_header(machine_header(_,_,Parameters),Old,New) :- |
1154 | | write_section(parameters,TParams,Old,New), |
1155 | | maplist(create_id_section_parameter,Parameters,TParams). |
1156 | | create_id_section_parameter(Param,TParam) :- |
1157 | | Expr=identifier(Name), |
1158 | | ext2int(Param,Expr,_,Type,Expr,[ParameterType],TParam), |
1159 | | ( is_upper_case(Name) -> |
1160 | | ParameterType = parameter_set, |
1161 | | Type = set(_) |
1162 | | ; otherwise -> |
1163 | | ParameterType = parameter_scalar). |
1164 | | is_upper_case(Name) :- \+ atom(Name),!, add_internal_error('Illegal call:', is_upper_case(Name)),fail. |
1165 | | is_upper_case(Name) :- atom_codes(Name,[C|_]), |
1166 | | memberchk(C,"ABCDEFGHIJKLMNOPQRSTUVWXYZ"). |
1167 | | |
1168 | | % Body: Sets |
1169 | | create_set_sections(Sections,MachineName) --> |
1170 | | write_section(deferred_sets,DeferredSets), |
1171 | | write_section(enumerated_sets,EnumeratedSets), |
1172 | | write_section(enumerated_elements,EnumeratedElements), |
1173 | | {optional_rawmachine_section(sets,Sections,[],Sets), |
1174 | | split_list(is_deferred_set_element,Sets,RawDeferredSets,RawEnumeratedSets), |
1175 | | maplist(create_deferred_set_section(MachineName),RawDeferredSets,DeferredSets), |
1176 | | maplist(create_enumerated_set_section(MachineName), |
1177 | | RawEnumeratedSets,EnumeratedSets,LEnumeratedElements), |
1178 | | append(LEnumeratedElements,EnumeratedElements)}. |
1179 | | is_deferred_set_element(AstElem) :- has_functor(AstElem,deferred_set,_). |
1180 | | create_deferred_set_section(MachineName,DS,TExpr) :- |
1181 | | unwrap_opt_description(DS,deferred_set(Pos,I),TInfos), |
1182 | | Infos = [given_set,def(deferred_set,MachineName)|TInfos], |
1183 | | create_identifier(I,Pos,set(global(I)),Infos,TExpr). |
1184 | | create_enumerated_set_section(MachineName,ES,TExpr,Elements) :- |
1185 | | unwrap_opt_description(ES,enumerated_set(Pos,I,Elems),TInfos), |
1186 | | Infos = [given_set,def(enumerated_set,MachineName)|TInfos], |
1187 | | create_identifier(I,Pos,set(global(I)),Infos,TExpr), |
1188 | | maplist(create_element(global(I),MachineName),Elems,Elements). |
1189 | | |
1190 | | % deal with optional description(Pos,Desc,A) wrapper |
1191 | | has_functor(description(_,_Desc,A),Functor,Arity) :- !, functor(A,Functor,Arity). |
1192 | | has_functor(A,Functor,Arity) :- functor(A,Functor,Arity). |
1193 | | |
1194 | | % remove description wrapper and generate info field |
1195 | | unwrap_opt_description(description(_,Desc,A),Res,Infos) :- !, Res=A, Infos=[description(Desc)]. |
1196 | | unwrap_opt_description(A,A,[]). |
1197 | | |
1198 | | create_element(Type,MachineName,Ext,Elem) :- |
1199 | | ext2int(Ext,Expr,_,Type,Expr,[enumerated_set_element,def(enumerated_element,MachineName)],Elem). |
1200 | | create_identifier(Id,Pos,Type,Infos,TExpr) :- |
1201 | | create_texpr(identifier(Id),Type,[nodeid(Pos)|Infos],TExpr). |
1202 | | |
1203 | | % Constants |
1204 | | create_constants_sections(RawMachine) --> |
1205 | | create_section_identifiers(constants,concrete_constants,RawMachine), |
1206 | | create_section_identifiers(abstract_constants,abstract_constants,RawMachine). |
1207 | | % Variables |
1208 | | create_variables_sections(RawMachine) --> |
1209 | | create_section_identifiers(concrete_variables,concrete_variables,RawMachine), |
1210 | | create_section_identifiers(variables,abstract_variables,RawMachine). |
1211 | | |
1212 | | % Freetypes: Treat them as additional constants, plus add entries in the "freetypes" |
1213 | | % section of the resulting machine |
1214 | | create_freetypes(RawMachine,MType,RefMachines,Old,New,Ein,Eout) :- |
1215 | | optional_rawmachine_section(freetypes,RawMachine,[],RawFreetypes), |
1216 | | create_freetypes2(RawFreetypes,MType,[ref(local,Old)|RefMachines],Old,New,Ein,Eout). |
1217 | | create_freetypes2([],_MType,_RefMachines,M,M,E,E) :- !. |
1218 | | create_freetypes2(RawFreetypes,MType,RefMachines,Old,New,Ein,Eout) :- |
1219 | | % we need the NonGroundExceptions for type checking |
1220 | | extract_parameter_types(RefMachines,NonGroundExceptions), |
1221 | | % create identifiers in the constants section |
1222 | | maplist(create_ids_for_freetype,RawFreetypes,IdsFreetypes), |
1223 | | % we just combined the results to keep the numbers of arguments low (too much for maplist) |
1224 | | maplist(split_ft,IdsFreetypes,IdentifiersL,Freetypes), |
1225 | | append(IdentifiersL,Identifiers), |
1226 | | % create properties for each freetype |
1227 | | foldl(create_properties_for_freetype(MType,RefMachines,NonGroundExceptions), |
1228 | | RawFreetypes,IdsFreetypes,PropertiesL,Ein,Eout), |
1229 | | conjunct_predicates(PropertiesL,Properties), |
1230 | | append_to_section(abstract_constants,Identifiers,Old,Inter), |
1231 | | conjunct_to_section(properties,Properties,Inter,Inter2), |
1232 | | write_section(freetypes,Freetypes,Inter2,New). |
1233 | | split_ft(ft(Ids,Freetype),Ids,Freetype). |
1234 | | create_ids_for_freetype(freetype(_Pos,Id,Constructors),ft([TId|TCons],freetype(Id,Cases))) :- |
1235 | | create_typed_id(Id,set(freetype(Id)),TId), |
1236 | | maplist(create_ids_for_constructor(Id),Constructors,TCons,Cases). |
1237 | | create_ids_for_constructor(Id,constructor(_Pos,Name,_Arg),TCons,case(Name,Type)) :- |
1238 | | create_typed_id(Name,set(couple(Type,freetype(Id))),TCons). |
1239 | | create_ids_for_constructor(Id,element(_Pos,Name),TCons,case(Name,constant([Name]))) :- |
1240 | | create_typed_id(Name,freetype(Id),TCons). |
1241 | | create_properties_for_freetype(MType,RefMachines,NonGroundExceptions, |
1242 | | freetype(_Pos,Id,Constructors),ft(Ids,_Freetypes),Properties,Ein,Eout) :- |
1243 | | % The freetype type |
1244 | | FType = freetype(Id), |
1245 | | % We use the standard type environment of properties... |
1246 | | visible_env(MType,properties,RefMachines,CEnv,Ein,E1), |
1247 | | % ...plus the identifiers of the free type (type name and constructor names) |
1248 | | add_identifiers_to_environment(Ids,CEnv,FEnv), |
1249 | | % We generate a comprehension set for all elements |
1250 | | create_typed_id(Id,set(FType),TId), |
1251 | | create_texpr(equal(TId,TComp),pred,[],FDef), |
1252 | | unique_typed_id("_freetype_arg",FType,Element), |
1253 | | create_recursive_compset([Element],ECond,set(FType),[],RecId,TComp), |
1254 | | create_typed_id(RecId,set(FType),TRecId), |
1255 | | % For each constructor, we generate a definition and a condition for the |
1256 | | % comprehension set above |
1257 | | foldl(create_properties_for_constructor(Id,FEnv,Element,TRecId,NonGroundExceptions), |
1258 | | Constructors,Defs,Conds,E1,Eout), |
1259 | | conjunct_predicates(Conds,ECond), |
1260 | | conjunct_predicates([FDef|Defs],Properties). |
1261 | | |
1262 | | /* create_properties_for_constructor(+Env,+Element,+RecId,+NGE,+Constructor,-Def,-Cond,Ein,Eout) |
1263 | | Env: Type environment |
1264 | | Element: A typed identifier "e" that is used in the definition of the freetype set: |
1265 | | ft = {e | e has_freetype_constructor x => e:...} |
1266 | | RecId: The typed identifier that can be used to refer to the freetype set (ft in the |
1267 | | example above |
1268 | | NGE: "Non ground exceptions", needed for type checking when having a parametrized machine |
1269 | | Constructor: The constructor expression as it comes from the parser |
1270 | | (constructor(Pos,Name,ArgSet) or element(Pos,Name)) |
1271 | | Def: The predicate that defines the constant for the constructor, |
1272 | | e.g. "cons = {i,o | i:NAT & o = freetype(cons,i)}" |
1273 | | Cond: The predicate that checks the argument of a freetype in the freetype set |
1274 | | (That would be the part "e:..." in the example above. |
1275 | | Ein,Eout: Used for type checker errors |
1276 | | */ |
1277 | | create_properties_for_constructor(FID,Env,Element,RecId,NonGroundExceptions, |
1278 | | Constructor,Def,Cond,Ein,Eout) :- |
1279 | | constructor_name(Constructor,Name), |
1280 | | env_lookup_type(Name,Env,CType), |
1281 | | create_properties_for_constructor2(Constructor,Env,NonGroundExceptions,FID, |
1282 | | Element,RecId,CDef,Cond,Ein,Eout), |
1283 | | get_texpr_type(CDef,CType), |
1284 | | create_typed_id(Name,CType,CId), |
1285 | | create_texpr(equal(CId,CDef),pred,[],Def). |
1286 | | constructor_name(element(_Pos,Name),Name). |
1287 | | constructor_name(constructor(_Pos,Name,_Domain),Name). |
1288 | | create_properties_for_constructor2(element(_Pos,Name),_Env,_NonGroundExceptions,FID, |
1289 | | _Element,_RecId,CDef,Cond,Ein,Ein) :- |
1290 | | create_texpr(value(freeval(FID,Name,term(Name))),freetype(FID),[],CDef), |
1291 | | create_texpr(truth,pred,[],Cond). |
1292 | | create_properties_for_constructor2(constructor(_Pos,Name,RArg),Env,NonGroundExceptions, |
1293 | | FID,Element,RecId,CDef,Cond,Ein,Eout) :- |
1294 | | % First, type check the given set of the domain: |
1295 | | btype_ground_dl([RArg],Env,NonGroundExceptions,[set(DType)],[TDomain],Ein,Eout), |
1296 | | % then create the RHS of "c = {i,o | i:Domain & o=freetype_constructor(Name,i)}" |
1297 | | create_definition_for_constructor(Name,TDomain,FID,CDef), |
1298 | | % The check in the freetype comprehension set is of the form |
1299 | | % e "of_freetype_case" Name => "content_of"(e) : Domain |
1300 | | create_texpr(implication(IsCase,DomainTest),pred,[],Cond), |
1301 | | create_texpr(freetype_case(FID,Name,Element),pred,[],IsCase), |
1302 | | create_texpr(freetype_destructor(FID,Name,Element),DType,[],Content), |
1303 | | % all references to the freetype itself are replaced by the recursive reference |
1304 | | replace_id_by_expr(TDomain,FID,RecId,TDomain2), |
1305 | | create_texpr(member(Content,TDomain2),pred,[],DomainTest). |
1306 | | |
1307 | | :- use_module(bsyntaxtree,[get_texpr_set_type/2]). |
1308 | | |
1309 | | /* The constructor is a function to the freetype, defined with a comprehension set: |
1310 | | The general form is "c = {i,o | i:Domain & o=freetype_constructor(Name,i)}" |
1311 | | create_definition_for_constructor(+Name,+DType,+FID,-CType,-CDef) :- |
1312 | | Name: Name of the constructor |
1313 | | TDomain: The user-specified domain |
1314 | | FID: The name of the free type |
1315 | | CDef: The RHS of the definition "Name = CDef" |
1316 | | */ |
1317 | | create_definition_for_constructor(Name,TDomain,FID,CDef) :- |
1318 | | % get the type of the domain: |
1319 | | get_texpr_set_type(TDomain,DType), |
1320 | | % create argument and result identifiers: |
1321 | | unique_typed_id("_constr_arg",DType,TArgId), % was constructor_arg and constructor_res |
1322 | | unique_typed_id("_constr_res",freetype(FID),TResId), |
1323 | | % The comprehension set as a whole |
1324 | | CType = set(couple(DType,freetype(FID))), |
1325 | | create_texpr(comprehension_set([TArgId,TResId],Pred),CType, |
1326 | | [prob_annotation('SYMBOLIC')],CDef), |
1327 | | create_texpr(conjunct(DomainCheck,Construction),pred,[],Pred), |
1328 | | % "i:Domain" |
1329 | | create_texpr(member(TArgId,TDomain),pred,[],DomainCheck), |
1330 | | % "o=freetype_constructor(i) |
1331 | | create_texpr(freetype_constructor(FID,Name,TArgId),freetype(FID),[],FreetypeCons), |
1332 | | create_texpr(equal(TResId,FreetypeCons),pred,[],Construction). |
1333 | | |
1334 | | % Operations |
1335 | | create_operations_sections(RawMachine,Old,New) :- |
1336 | | write_section(promoted,OperationIdentifiers,Old,New0), |
1337 | | get_operation_identifiers(RawMachine,operations,OperationIdentifiers), |
1338 | | (allow_local_op_calls, |
1339 | | get_operation_identifiers(RawMachine,local_operations,LocOpIDs), |
1340 | | LocOpIDs \= [] |
1341 | | -> append_to_section(unpromoted,LocOpIDs,New0,New) % TODO: check that it does not yet exist |
1342 | | ; New = New0 |
1343 | | ). |
1344 | | |
1345 | | get_operation_identifiers(RawMachine,OperationIdentifiers) :- |
1346 | | get_operation_identifiers(RawMachine,operations,OperationIdentifiers). |
1347 | | get_operation_identifiers(RawMachine,SECTION,OperationIdentifiers) :- |
1348 | | optional_rawmachine_section(SECTION,RawMachine,[],Ops), |
1349 | | maplist(extract_operation_identifier,Ops,OperationIdentifiers). |
1350 | | extract_operation_identifier(Ext,TId) :- |
1351 | | remove_pos(Ext, operation(ExtId,_,_,_)),!, |
1352 | | ext2int(ExtId,identifier(I),_,op(_,_),identifier(op(I)),Infos,TId), |
1353 | | operation_infos(Infos). |
1354 | | extract_operation_identifier(Ext,TId) :- |
1355 | | remove_pos(Ext, refined_operation(ExtId,_,_,RefinesOp,_)),!, |
1356 | | ext2int(ExtId,identifier(I),_,op(_,_),identifier(op(I)),[refines_operation(RefinesOp)|Infos],TId), |
1357 | | operation_infos(Infos). |
1358 | | extract_operation_identifier(Ext,_) :- add_internal_error('Unknown operation node:',Ext),fail. |
1359 | | |
1360 | | % VALUES section: |
1361 | | % process_values_section(MachineType,RawMachine,NonGroundExceptions,Min/Ein/RefMIn,Mout/Eout/RefMOut): |
1362 | | % Type-check the VALUES section and change the type of valuated deferred sets, if necessary |
1363 | | % MachineType, RawMachine, NonGroundExceptions: as usual, see other comments |
1364 | | % Min/Mout: The currently constructed machine |
1365 | | % Ein/Eout: The difference list of errors |
1366 | | % RefMin/RefMout: The list of already typechecked machines. These typechecked machines can be |
1367 | | % altered by this predicate because if a deferred set is valuated by an integer set or |
1368 | | % other deferred set, all occurrences of the original type are replaced by the new type, |
1369 | | % even for the already typed machines. |
1370 | | process_values_section(MachineType,RawMachine,NonGroundExceptions,Min/Ein/RefMIn,Mout/Eout/RefMOut) :- |
1371 | | optional_rawmachine_section(values,RawMachine,[],RawValues), |
1372 | | process_values_section_aux(RawValues,MachineType,NonGroundExceptions, |
1373 | | Min/Ein/RefMIn,Mout/Eout/RefMOut). |
1374 | | process_values_section_aux([],_MachineType,_NonGroundExceptions,In,Out) :- !,In=Out. |
1375 | | process_values_section_aux(RawValues,MachineType,NonGroundExceptions, |
1376 | | Min/Ein/RefMin,Mout/Eout/RefMout) :- |
1377 | | debug_stats('TYPING VALUES'), |
1378 | | type_values_section(MachineType,RawValues,RefMin,NonGroundExceptions,Min/Ein,Mout/Eout), |
1379 | | % will be added as additional_property in bmachine |
1380 | | RefMin=RefMout. |
1381 | | |
1382 | | type_values_section(MachineType,RawValues,RefMachines,NonGroundExceptions,Min/Ein,Mout/Eout) :- |
1383 | | write_section(values,Values,Min,Mout), |
1384 | | visible_env(MachineType,values_expression,RefMachines,Env,Ein,E1), |
1385 | | % We have to pass an environment that can be modified because in each |
1386 | | % valuation the previously valuated constants can be used. |
1387 | | foldl(extract_values_entry(NonGroundExceptions),RawValues,Values,Env/E1,_ResultingEnv/Eout). |
1388 | | |
1389 | | extract_values_entry(NonGroundExceptions, values_entry(POS,ValueID,ValueExpr), Entry, |
1390 | | EnvIn/Ein,EnvOut/Eout) :- |
1391 | | % TODO: There seem to be a lot of additional constraints for valuations that are not |
1392 | | % yet handled here |
1393 | | btype_ground_dl([ValueExpr],EnvIn,NonGroundExceptions,[Type],[TExpr],Ein,Eout), |
1394 | | create_identifier(ValueID,POS,Type,[valuated_constant],TypedID), |
1395 | | create_texpr(values_entry(TypedID,TExpr),values_entry,[nodeid(POS)],Entry), |
1396 | | debug_println(9,valued_constant(ValueID)), |
1397 | | add_identifiers_to_environment([TypedID],EnvIn,EnvOut). |
1398 | | |
1399 | | % type_section_with_single_predicate(+Sec,+Name,+SectionsToType,+MachineType,+Sections, |
1400 | | % +RefMachines,+NonGroundExceptions,+Ein,-Eout,+Old,-New): |
1401 | | % Type a section such as INVARIANT, PROPERTIES, CONSTRAINTS with a single predicate |
1402 | | % Sec: section name in the raw (untyped) machine (e.g. invariant) |
1403 | | % Name: name of machine from which this section comes |
1404 | | % SectionsToType: list of section names that contain identifiers that must be typed |
1405 | | % by this predicate (e.g. all variables must be typed by the invariant) |
1406 | | % MachineType: machine type (machine, refinement, ...) |
1407 | | % Sections: list of sections representing the raw (untyped) machine |
1408 | | % RefMachines: list of already typed machines |
1409 | | % NonGroundExceptions: list of types that may be not ground because the concrete type |
1410 | | % is determinded by machine parameter |
1411 | | % Ein/Eout: difference list of errors |
1412 | | % Old/New: the new typed section is added (by conjunction) to the machine |
1413 | | type_section_with_single_predicate(Sec,Name,SectionsToType,MachineType,Sections, |
1414 | | RefMachines,NonGroundExceptions,Ein,Eout,Old,New) :- |
1415 | | ~~mnf(optional_rawmachine_section(Sec,Sections,truth(none),Predicate)), |
1416 | | ( Predicate = truth(_) -> |
1417 | | % even if there is no content, we must check if all identifiers are typed |
1418 | | check_if_all_ids_are_typed(SectionsToType,RefMachines,NonGroundExceptions,Ein,Eout), |
1419 | | Old=New |
1420 | | ; otherwise -> |
1421 | | get_machine_infos(Sections,Infos), |
1422 | | toplevel_raw_predicate_sanity_check(Sec,Name,Predicate,Infos), |
1423 | | type_predicates(Sec,SectionsToType,MachineType,[Predicate],RefMachines,NonGroundExceptions, |
1424 | | [Typed],Ein,Eout), |
1425 | | conjunct_to_section(Sec,Typed,Old,New) |
1426 | | ), |
1427 | | !. |
1428 | | type_section_with_single_predicate(Sec,Name,SectsToType,MchType,_,_,_,_,_,_,_) :- |
1429 | | add_internal_error('type_section_with_single_predicate failed', |
1430 | | type_section_with_single_predicate(Sec,Name,SectsToType,MchType,_,_,_,_,_,_,_)), |
1431 | | fail. |
1432 | | |
1433 | | % get some infos relevant for sanity check: |
1434 | | get_machine_infos(Sections,Infos) :- |
1435 | ? | ((rawmachine_section_exists(concrete_variables,Sections) ; rawmachine_section_exists(abstract_variables,Sections)) |
1436 | | -> Infos = [has_variables|I1] ; Infos = I1), |
1437 | | ((rawmachine_section_exists(concrete_constants,Sections) ; rawmachine_section_exists(abstract_constants,Sections)) |
1438 | | -> I1 = [has_constants] ; I1 = []). |
1439 | | |
1440 | | % Type a section with multiple predicates, such as ASSERTIONS |
1441 | | type_section_with_predicate_list(Sec,SectionsToType,MachineType,Sections, |
1442 | | RefMachines,NonGroundExceptions,Ein,Eout,Old,New) :- |
1443 | | write_section(Sec,Typed,Old,New), |
1444 | | ~~mnf(optional_rawmachine_section(Sec,Sections,[],Predicates)), |
1445 | | type_predicates(Sec,SectionsToType,MachineType,Predicates,RefMachines,NonGroundExceptions,Typed,Ein,Eout), |
1446 | | !. |
1447 | | type_section_with_predicate_list(Sec,SectsToType,MchType,Sects,RefMchs,NonGrndExc,Ein,Eout,Old,New) :- |
1448 | | add_internal_error('type_section_with_predicate_list failed', |
1449 | | type_section_with_predicate_list(Sec,SectsToType,MchType,Sects,RefMchs,NonGrndExc,Ein,Eout,Old,New)), |
1450 | | fail. |
1451 | | |
1452 | | |
1453 | | type_predicates(_Sec,SectionsToType,_MachineType,[],RefMachines,NonGroundExceptions,Typed,Ein,Eout) :- |
1454 | | !,Typed=[], |
1455 | | check_if_all_ids_are_typed(SectionsToType,RefMachines,NonGroundExceptions,Ein,Eout). |
1456 | | type_predicates(Sec,SectionsToType,MachineType,Predicates,RefMachines,NonGroundExceptions,Typed,Ein,Eout) :- |
1457 | | debug:debug_println(9,typing(Sec,MachineType)), |
1458 | | visible_env(MachineType, Sec, RefMachines, Env, Ein, E1), |
1459 | | same_length(Predicates,Types),maplist(is_pred_type,Types), |
1460 | | debug:debug_println(9,btype(Sec,MachineType)), |
1461 | | btype_ground_dl_in_section(Sec,Predicates,Env,NonGroundExceptions,Types,Typed1,E1,E2), |
1462 | | mark_with_section(Sec,Typed1,Typed), |
1463 | | debug:debug_println(9,check_all_ids_typed), |
1464 | | check_if_all_ids_are_typed(SectionsToType,RefMachines,NonGroundExceptions,E2,Eout), |
1465 | | debug:debug_println(9,finished_check_all_ids_typed). |
1466 | | |
1467 | | % check if the identifiers that should be typed by this section are completly typed |
1468 | | check_if_all_ids_are_typed([],_RefMachines,_NonGroundExceptions,Ein,Eout) :- !,Ein=Eout. |
1469 | | check_if_all_ids_are_typed(SectionsToType,RefMachines,NonGroundExceptions,Ein,Eout) :- |
1470 | | memberchk(ref(local,Local),RefMachines), |
1471 | | get_all_identifiers(SectionsToType,Local,IdentifiersToType), |
1472 | | check_ground_types_dl(IdentifiersToType, NonGroundExceptions, Ein, Eout). |
1473 | | |
1474 | | |
1475 | | mark_with_section(Sec,In,Out) :- |
1476 | | maplist(mark_with_section2(Sec),In,Out). |
1477 | | mark_with_section2(Sec,In,Out) :- |
1478 | | remove_bt(In,conjunct(A,B),conjunct(NA,NB),Out),!, |
1479 | | mark_with_section2(Sec,A,NA), mark_with_section2(Sec,B,NB). |
1480 | | mark_with_section2(Sec,In,Out) :- |
1481 | | add_texpr_infos(In,[section(Sec)],Out). |
1482 | | |
1483 | | type_initialisation_section(Sections,Name,MType,RefMachines,NonGroundExceptions,Ein,Eout,Old,New) :- |
1484 | | write_section(initialisation,Initialisation,Old,New), |
1485 | | ( rawmachine_section(initialisation,Sections,Init) -> |
1486 | | visible_env(MType, initialisation, RefMachines, InitEnv, Ein, E1), |
1487 | | btype_ground_dl([Init],InitEnv,NonGroundExceptions,[subst],[TypedInit],E1,Eout), |
1488 | | Initialisation=[init(Name,TypedInit)] |
1489 | | ; otherwise -> |
1490 | | Ein=Eout, |
1491 | | Initialisation=[]). |
1492 | | |
1493 | | :- use_module(library(ugraphs)). |
1494 | | |
1495 | | type_operations_section(Sections,MType,RefMachines,NonGroundExceptions,Ein,Eout,Old,New) :- |
1496 | | ~~mnf( write_section(operation_bodies,Operations,Old,New) ), |
1497 | | ~~mnf( visible_env(MType, operation_bodies, RefMachines, Env, Ein, E1) ), |
1498 | | ~~mnf( optional_rawmachine_section(operations,Sections,[],Ops1) ), |
1499 | | optional_rawmachine_section(local_operations,Sections,[], Ops2), |
1500 | | append(Ops2,Ops1,Ops), |
1501 | | topological_sort(Ops,Env,SOps), |
1502 | | (debug_mode(off) -> true ; length(SOps,NrOps),debug_stats(finished_topological_sorting(NrOps))), |
1503 | | same_length(SOps,Types), maplist(is_op_type,Types), |
1504 | | ~~mnf( btype_ground_dl(SOps,Env,NonGroundExceptions,Types,Operations,E1,Eout) ),!. |
1505 | | |
1506 | | allow_local_op_calls :- |
1507 | | (get_preference(allow_local_operation_calls,true) -> true ; get_preference(allow_operation_calls_in_expr,true)). |
1508 | | % perform a topological sort of the operations: treat called operation before calling operation |
1509 | | % only relevant when allow_local_operation_calls is set to true |
1510 | | topological_sort(Ops,Env,SortedOps) :- |
1511 | | allow_local_op_calls, |
1512 | | findall(OtherID-ID, (member(Op,Ops),op_calls_op(Op,Env,ID,OtherID)),Edges), |
1513 | | % print(edges(Edges)),nl, |
1514 | | % TO DO: maybe only store edges where OtherID also appears in Ops (i.e., call within same machine) |
1515 | | Edges \= [], |
1516 | | !, |
1517 | | findall(ID,(member(operation(_,RawID,_,_,_),Ops),raw_id(RawID,ID)),Vertices), %print(vertices(Vertices)),nl, |
1518 | | vertices_edges_to_ugraph(Vertices,Edges,Graph), |
1519 | | (top_sort(Graph,Sorted) |
1520 | | -> sort_ops(Sorted,Ops,SortedOps) |
1521 | | ; get_preference(allow_operation_calls_in_expr,true) -> |
1522 | | add_warning(topological_sort,'Mutual recursion or cycle in the (local) operation call graph, this may cause problems computing reads information: ',Edges), |
1523 | | SortedOps=Ops |
1524 | | % not a problem, because operations called in expressions are not allowed to modify the state |
1525 | | % TODO: however, we may have an issue with computing reads information correctly for mutual recursion !? |
1526 | | % direct recursion should be ok |
1527 | | ; add_error(topological_sort,'Cycle in the (local) operation call graph: ',Edges), |
1528 | | SortedOps=Ops). |
1529 | | topological_sort(Ops,_,Ops). |
1530 | | |
1531 | | sort_ops([],Ops,Ops). % Ops should be [] |
1532 | | sort_ops([OpID|T],Ops,Res) :- |
1533 | | raw_op_id(Op1,OpID), |
1534 | | (select(Op1,Ops,TOps) |
1535 | | -> Res = [Op1|TSOps], sort_ops(T,TOps,TSOps) |
1536 | | ; % operation from another machine |
1537 | | % print(could_not_find(OpID,Ops)),nl, |
1538 | | sort_ops(T,Ops,Res) |
1539 | | ). |
1540 | | |
1541 | | is_op_type(op(_,_)). |
1542 | | is_pred_type(pred). |
1543 | | |
1544 | | % compute which other operations are directly called |
1545 | | op_calls_op(operation(_,RawID,_,_,RawBody),Env,ID,OtherID) :- raw_id(RawID,ID), |
1546 | | raw_body_calls_operation(RawBody,ID,Env,OtherID). |
1547 | | |
1548 | | raw_body_calls_operation(RawBody,ID,Env,OtherID) :- |
1549 | | raw_member(OpCall,RawBody), |
1550 | | raw_op_call(OpCall,ID,Env,RawOtherID), raw_id(RawOtherID,OtherID). |
1551 | | |
1552 | | raw_op_call(operation_call(_,RawOtherID,_,_),_,_, RawOtherID). |
1553 | | raw_op_call(operation_call_in_expr(_,RawOtherID,_),ID,_, RawOtherID) :- |
1554 | | \+ raw_id(RawOtherID,ID). % we do not look direct recursion: it poses no problem for computing reads/writes info |
1555 | | raw_op_call(function(_,RawOtherID,_), ID, Env, RawOtherID) :- % function calls possibly not yet translated to operation_call_in_expr |
1556 | | get_preference(allow_operation_calls_in_expr,true), |
1557 | | \+ raw_id(RawOtherID,ID), % direct recursion ok |
1558 | | btypechecker:is_operation_call(RawOtherID,Env). |
1559 | | |
1560 | | raw_op_id(operation(_,RawID,_,_,_RawBody),ID) :- raw_id(RawID,ID). |
1561 | | raw_id(identifier(_,ID),ID). |
1562 | | |
1563 | | % a utility function to work on the raw AST Functor(POS,Arg1,...,Argn) |
1564 | | % this will not be able to look inside DEFINITIONS ! |
1565 | | % TO DO: deal with more raw substitutions which have list arguments |
1566 | | raw_member(X,X). |
1567 | | raw_member(X,parallel(_,List)) :- !, member(Term,List), raw_member(X,Term). |
1568 | | raw_member(X,sequence(_,List)) :- !, member(Term,List), raw_member(X,Term). |
1569 | | raw_member(X,[H|T]) :- !, (raw_member(X,H) ; raw_member(X,T)). |
1570 | | raw_member(X,Term) :- compound(Term), Term =.. [_Functor,_Pos|Args], |
1571 | | member(A,Args), raw_member(X,A). |
1572 | | |
1573 | | |
1574 | | create_section_identifiers(Section,DestSection,RawMachine,Old,New) :- |
1575 | | ~~mnf(write_section(DestSection,Vars,Old,New)), |
1576 | | ~~mnf(optional_rawmachine_section(Section,RawMachine,[],Identifiers)), |
1577 | | ~~mnf(create_section_ids2(Identifiers,[],Vars,DestSection,New)). |
1578 | | |
1579 | | create_section_ids2([],_,[],_,_). |
1580 | | create_section_ids2([Ext|Rest],Infos,Res,DestSection,MachSections) :- |
1581 | | expand_definition_to_variable_list(Ext,MachSections,List),!, |
1582 | | append(List,Rest,NewList), |
1583 | | create_section_ids2(NewList,Infos,Res,DestSection,MachSections). |
1584 | | create_section_ids2([Ext|Rest],Infos,Res,DestSection,MachSections) :- |
1585 | | create_section_id(Ext,Infos,DestSection,TId), |
1586 | | ( TId = error(Msg,Term,Pos) -> |
1587 | | Res = TRest, add_error(bmachine_construction,Msg,Term,Pos) |
1588 | | ; otherwise -> |
1589 | | Res = [TId|TRest]), |
1590 | | create_section_ids2(Rest,Infos,TRest,DestSection,MachSections). |
1591 | | create_section_id(Ext,Infos,DestSection,TId) :- |
1592 | | I=identifier(_), |
1593 | | ( ext2int(Ext,I,_Pos,_Type,I,Infos,TId) -> |
1594 | | true |
1595 | | ; unwrap_pragma(Ext,ID,PragmaInfos) -> append(PragmaInfos,Infos,FullInfos), |
1596 | | ext2int(ID,I,_,_,I,FullInfos,TId) |
1597 | | ; Ext = definition(POSINFO,ID,_) -> |
1598 | | TId = error('Trying to use DEFINITION name as identifier: ', |
1599 | | (ID,within(DestSection)), POSINFO) |
1600 | | ; otherwise -> |
1601 | | TId = error('Illegal identifier: ', |
1602 | | (Ext,within(DestSection)), Ext) |
1603 | | ). |
1604 | | |
1605 | | % support using DEFINITIONS which are variable lists; |
1606 | | % currently for ProB parser you need to write VARS == (x,y,..) for Atelier-B: VARS == x,y,.. |
1607 | | expand_definition_to_variable_list(definition(_POSINFO,ID,_),MachSections,List) :- |
1608 | | get_section(definitions,MachSections,Defs), |
1609 | | member(definition_decl(ID,expression,_InnerPos,[],RawExpr,_Deps),Defs), |
1610 | | extract_identifier_list(RawExpr,List,[]). |
1611 | | |
1612 | | % convert a raw tuple into a raw identifier list: |
1613 | | extract_identifier_list(identifier(Pos,ID)) --> [identifier(Pos,ID)]. |
1614 | | extract_identifier_list(couple(_,List)) --> |
1615 | | extract_identifier_list(List). |
1616 | | extract_identifier_list([]) --> []. |
1617 | | extract_identifier_list([H|T]) --> extract_identifier_list(H), extract_identifier_list(T). |
1618 | | |
1619 | | |
1620 | | unwrap_pragma(description(_,D,ID),ID,[description(D)]). |
1621 | | unwrap_pragma(unit(_,U,ID),ID,[unit(U)]). |
1622 | | unwrap_pragma(new_unit(_,U,ID),ID,[new_unit(U)]). |
1623 | | unwrap_pragma(inferred_unit(_,_,ID),ID,[]). % currently ignored |
1624 | | unwrap_pragma(inferredunit(_,_,ID),ID,[]). % currently ignored |
1625 | | |
1626 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1627 | | % sort the machines topologically |
1628 | | |
1629 | | machine_order(Machines,Order) :- |
1630 | | machine_dependencies(Machines,Dependencies), |
1631 | | topsort(Dependencies,Order). |
1632 | | |
1633 | | % sort the machines topologically |
1634 | | topsort(Deps,Sorted) :- |
1635 | | topsort2(Deps,[],Sorted). |
1636 | | topsort2([],_,[]) :- !. |
1637 | | topsort2(Deps,Known,Sorted) :- |
1638 | | split_list(all_deps_known(Known),Deps,DAvailable,DNotAvailable), |
1639 | | DAvailable = [_|_], % we have new machines available whose dependencies are all known |
1640 | | !, |
1641 | | maplist(dep_name,DAvailable,Available), |
1642 | | append(Available,Known,NewKnown), |
1643 | | append(Available,Rest,Sorted), |
1644 | | topsort2(DNotAvailable,NewKnown,Rest). |
1645 | | topsort2(Deps,_Known,_) :- |
1646 | | member(dep(Name,NameDeps),Deps), |
1647 | | add_error(bmachine_construction,'Could not resolve machine dependencies for: ',Name:depends_on(NameDeps)), |
1648 | | fail. |
1649 | | |
1650 | ? | all_deps_known(K,dep(_Name,Deps)) :- sort(Deps,DS),sort(K,KS),subseq0(KS,DS),!. |
1651 | | dep_name(dep(Name,_Deps),Name). |
1652 | | |
1653 | | % find dependencies between machines |
1654 | | machine_dependencies(Machines,Dependencies) :- |
1655 | | maplist(machine_dependencies2,Machines,Deps), |
1656 | | sort(Deps,Dependencies). |
1657 | | machine_dependencies2(M,dep(Name,Deps)) :- |
1658 | | get_constructed_machine_name_and_body(M,Name,_,Body), |
1659 | | findall(Ref, |
1660 | | (refines(M,Ref);machine_reference(Body,Ref)), |
1661 | | Deps). |
1662 | | |
1663 | | machine_reference(MachineBody,Ref) :- |
1664 | ? | ( member(sees(_,R),MachineBody) |
1665 | | ; member(uses(_,R),MachineBody) ), |
1666 | ? | member(identifier(_,PrefixRef),R), |
1667 | | split_prefix(PrefixRef,_,Ref). |
1668 | | machine_reference(MachineBody,Ref) :- |
1669 | ? | ( member(includes(_,R),MachineBody) |
1670 | ? | ; member(extends(_,R),MachineBody) |
1671 | | ; member(imports(_,R),MachineBody) ), |
1672 | ? | member(machine_reference(_,PrefixRef,_),R), |
1673 | | split_prefix(PrefixRef,_,Ref). |
1674 | | |
1675 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1676 | | % refinements |
1677 | | merge_refinement_and_abstraction(Concrete,RefMachines,Result,Ein,Eout) :- |
1678 | | memberchk(ref(abstraction,Abstraction),RefMachines), |
1679 | | append_sections([sets,concrete_constants,concrete_variables],Abstraction,Concrete,M1), |
1680 | | append_if_new(abstract_constants,Abstraction,M1,M2), |
1681 | | get_section(properties,Abstraction,AbstractProperties), |
1682 | | conjunct_to_section(properties,AbstractProperties,M2,M3), |
1683 | | % now get current invariant Invariant and filter out gluing/linking invariant |
1684 | | % (the linking invariant talks about variables which no longer exist; hence we cannot check it anymore) |
1685 | | select_section(invariant,Invariant,FullConcreteInvariant,M3,M4), |
1686 | | write_section(linking_invariant,LinkingInvariant,M4,Merged), |
1687 | | % we now also copy from the abstraction those invariants which are still valid |
1688 | | get_section(invariant,Abstraction,AbstractInvariant), |
1689 | | filter_abstract_invariant(AbstractInvariant,Concrete,AbsInvariantStillValid), |
1690 | | filter_linking_invariant(Invariant,LinkingInvariant,ConcreteInvariant), |
1691 | | conjunct_predicates([AbsInvariantStillValid,ConcreteInvariant],FullConcreteInvariant), |
1692 | | propagate_abstract_operations(Abstraction,Merged,RefMachines,Result,Ein,Eout). |
1693 | | merge_refinement_and_abstraction(Machine,_,Machine,Errors,Errors). |
1694 | | |
1695 | | % append sections from abstract machine to concrete machine: |
1696 | | append_sections(Sections,AbsMachine,Old,New) :- |
1697 | | expand_shortcuts(Sections,AllSections), |
1698 | | append_sections2(AllSections,AbsMachine,Old,New). |
1699 | | append_sections2([],_,M,M). |
1700 | | append_sections2([Section|Rest],AbsMachine,Old,New) :- |
1701 | | get_section(Section,AbsMachine,Content), |
1702 | | append_to_section(Section,Content,Old,Inter), |
1703 | | append_sections2(Rest,AbsMachine,Inter,New). |
1704 | | |
1705 | | :- assert_must_succeed((create_machine(abs,EA), create_machine(conc,EB), |
1706 | | write_section(abstract_constants,[b(identifier(x),integer,[some_info])],EA,A), |
1707 | | write_section(abstract_constants,[b(identifier(x),integer,[other_info]), |
1708 | | b(identifier(y),integer,[info])],EB,B), |
1709 | | append_if_new(abstract_constants,A,B,ResultM), |
1710 | | get_section(abstract_constants,ResultM,ResultConst), |
1711 | | ResultConst==[b(identifier(x),integer,[other_info]), |
1712 | | b(identifier(y),integer,[info])] |
1713 | | )). |
1714 | | |
1715 | | append_if_new(Section,Machine,In,Out) :- |
1716 | | get_section(Section,Machine,Content), |
1717 | | select_section(Section,Old,New,In,Out), |
1718 | | get_texpr_ids(Old,ExistingIds), |
1719 | | exclude(is_in_existing_ids(ExistingIds),Content,NewElements), |
1720 | | append(Old,NewElements,New). |
1721 | | is_in_existing_ids(ExistingIds,TId) :- |
1722 | | get_texpr_id(TId,Id), |
1723 | | memberchk(Id,ExistingIds). |
1724 | | |
1725 | | % filter linking and concrete invaraint |
1726 | | filter_linking_invariant(Invariant,Linking,Concrete) :- |
1727 | | split_conjuncts(Invariant,Predicates), |
1728 | | split_list(contains_abstraction_refs,Predicates,Linkings,Concretes), |
1729 | | conjunct_predicates(Linkings,Linking), |
1730 | | conjunct_predicates(Concretes,Concrete). |
1731 | | |
1732 | | |
1733 | | % contains_abstraction_refs can be used on predicates of the current machine: the abstraction info field has been computed for this machine |
1734 | | contains_abstraction_refs(TExpr) :- |
1735 | | syntaxtraversion(TExpr,_,_,Infos,Subs,_), |
1736 | | ( memberchk(abstraction,Infos) % This info field comes from the last argument of visibility/6 |
1737 | | -> true |
1738 | | ; member(Sub,Subs), |
1739 | | contains_abstraction_refs(Sub)). |
1740 | | |
1741 | | :- use_module(tools_lists,[ord_member_nonvar_chk/2]). |
1742 | | % Determine which part of the abstract invariant can be imported into the refinement machine |
1743 | | filter_abstract_invariant(AbsInvariant,ConcreteMachine,ConcreteInv) :- |
1744 | | split_conjuncts(AbsInvariant,Predicates), |
1745 | | get_machine_sorted_variables(ConcreteMachine,SortedConcrVars), |
1746 | | split_list(contains_abstract_variables(SortedConcrVars),Predicates,_Discard,Concretes), |
1747 | | %print(discarded(_Discard)),nl, |
1748 | | conjunct_predicates(Concretes,ConcreteInv). %, print('COPY: '), translate:print_bexpr(Concrete),nl. |
1749 | | contains_abstract_variables(SortedConcrVars,TExpr) :- |
1750 | | syntaxtraversion(TExpr,Expr,Type,Infos,Subs,_), |
1751 | | ( memberchk(loc(_,_,abstract_variables),Infos) % are there other things that pose problems: abstract_constants ? |
1752 | | -> %print('Abs: '),translate:print_bexpr(TExpr),nl, print(SortedConcrVars),nl, |
1753 | | \+ ord_member_nonvar_chk(b(Expr,Type,_),SortedConcrVars) % otherwise variable is re-introduced with same type |
1754 | | % in some Event-B models: VARIABLES keyword is used and in refinement VARIABLES are re-listed |
1755 | | % TO DO: check what happens when variable not immediately re-introduced |
1756 | | ; member(Sub,Subs), |
1757 | | contains_abstract_variables(SortedConcrVars,Sub)), print('Filtering INV: '),translate:print_bexpr(TExpr),nl. |
1758 | | |
1759 | | get_machine_sorted_variables(Machine,SortedAllVars) :- |
1760 | | get_section(abstract_variables,Machine,AbsVars), |
1761 | | get_section(concrete_variables,Machine,ConcVars), |
1762 | | append(ConcVars,AbsVars,AllVars), |
1763 | | sort(AllVars,SortedAllVars). |
1764 | | |
1765 | | split_conjuncts(Expr,List) :- |
1766 | | split_conjuncts2(Expr,List,[]). |
1767 | | split_conjuncts2(Expr) --> |
1768 | | {get_texpr_expr(Expr,conjunct(A,B)),!}, |
1769 | | split_conjuncts2(A), |
1770 | | split_conjuncts2(B). |
1771 | | split_conjuncts2(Expr) --> [Expr]. |
1772 | | |
1773 | | % copy the abstract operations or re-use their preconditions |
1774 | | propagate_abstract_operations(Abstract,Concrete,RefMachines,Result,Ein,Eout) :- |
1775 | | get_section(promoted,Abstract,APromoted), |
1776 | | get_section(operation_bodies,Abstract,ABodies), |
1777 | | % signature: select_section(SecName,OldContent,NewContent,OldMachine,NewMachine) |
1778 | | select_section(promoted,CPromotedIn,CPromotedOut,Concrete,Concrete2), |
1779 | | select_section(operation_bodies,CBodiesIn,CBodiesOut,Concrete2,Result), |
1780 | | propagate_aops(APromoted,ABodies,RefMachines,CPromotedIn,CBodiesIn,CPromotedOut,CBodiesOut,Ein,Eout). |
1781 | | propagate_aops([],_ABodies,_RefMachines,CPromoted,CBodies,CPromoted,CBodies,Errors,Errors). |
1782 | | propagate_aops([APromoted|ApRest],ABodies,RefMachines,CPromotedIn,CBodiesIn,CPromotedOut,CBodiesOut,Ein,Eout) :- |
1783 | | get_operation(APromoted,ABodies,AbstractOp), |
1784 | | def_get_texpr_id(APromoted,op(APromotedOpName)), |
1785 | | copy_texpr_wo_info(APromoted,CProm), |
1786 | | ( member(CProm,CPromotedIn) -> |
1787 | | debug_format(19,'Refining promoted abstract operation ~w to refinement machine.~n',[APromotedOpName]), |
1788 | | extract_concrete_preconditions(AbstractOp,RefMachines,Pre), |
1789 | | change_operation(APromoted,ConcreteOpOld,ConcreteOpNew,CBodiesIn,CBodiesRest), |
1790 | | add_precondition(Pre,ConcreteOpOld,ConcreteOpNew), |
1791 | | CPromotedIn = CPromotedRest, |
1792 | | Ein = E1 |
1793 | | % TO DO: do not copy if event is refined at least once with renaming ! |
1794 | | ; is_refined_by_some_event(APromotedOpName,CPromotedIn,ConcreteOpName) -> |
1795 | | debug_format(19,'Not copying abstract operation ~w to refinement machine, as it is refined by ~w.~n',[APromotedOpName,ConcreteOpName]), |
1796 | | CPromotedRest=CPromotedIn, CBodiesRest=CBodiesIn, |
1797 | | E1=Ein |
1798 | | ; otherwise -> |
1799 | | debug_format(19,'Copying abstract operation ~w to refinement machine, as it is not refined.~n',[APromotedOpName]), |
1800 | | % TO DO: check that this is also the right thing to do for Atelier-B Event-B |
1801 | | check_copied_operation(APromoted,AbstractOp,RefMachines,Ein,E1), |
1802 | | append(CPromotedIn,[APromoted],CPromotedRest), |
1803 | | append(CBodiesIn,[AbstractOp],CBodiesRest) |
1804 | | ), |
1805 | | propagate_aops(ApRest,ABodies,RefMachines,CPromotedRest,CBodiesRest,CPromotedOut,CBodiesOut,E1,Eout). |
1806 | | |
1807 | | is_refined_by_some_event(AbstractName,CPromotedList,ConcreteName) :- |
1808 | | member(TID,CPromotedList), |
1809 | | get_texpr_info(TID,Infos), |
1810 | | memberchk(refines_operation(AbstractName),Infos), |
1811 | | def_get_texpr_id(TID,ConcreteName). |
1812 | | |
1813 | | get_operation(TId,Bodies,Operation) :- |
1814 | | select_operation(TId,Bodies,Operation,_BodiesRest). |
1815 | | change_operation(TId,OldOp,NewOp,OldBodies,[NewOp|NewBodies]) :- |
1816 | | select_operation(TId,OldBodies,OldOp,NewBodies). |
1817 | | select_operation(TId,Bodies,Operation,BodiesRest) :- |
1818 | | copy_texpr_wo_info(TId,OpId), |
1819 | | get_texpr_expr(Operation,operation(OpId,_,_,_)), |
1820 | | select(Operation,Bodies,BodiesRest),!. |
1821 | | |
1822 | | extract_concrete_preconditions(Op,RefMachines,FPre) :- |
1823 | | extract_preconditions_op(Op,Pre), |
1824 | | extract_op_arguments(Op,Args), |
1825 | | conjunction_to_list(Pre,Pres), |
1826 | | % todo: check the "machine" parameter |
1827 | | visible_env(machine,operation_bodies,RefMachines,Env1,_Errors,[]), |
1828 | | store_variables(Args,Env1,Env), |
1829 | | filter_predicates_with_unknown_identifiers(Pres,Env,FPres), |
1830 | | conjunct_predicates(FPres,FPre). |
1831 | | |
1832 | | extract_op_arguments(Op,Params) :- |
1833 | | get_texpr_expr(Op,operation(_,_,Params,_)). |
1834 | | |
1835 | | extract_preconditions_op(OpExpr,Pre) :- |
1836 | | get_texpr_expr(OpExpr,operation(_,_,_,Subst)), |
1837 | | extract_preconditions(Subst,Pres,_), |
1838 | | conjunct_predicates(Pres,Pre). |
1839 | | extract_preconditions(TExpr,Pres,Inner) :- |
1840 | | get_texpr_expr(TExpr,Expr), |
1841 | | extract_preconditions2(Expr,TExpr,Pres,Inner). |
1842 | | extract_preconditions2(precondition(Pre,Subst),_,[Pre|Pres],Inner) :- !, |
1843 | | extract_preconditions(Subst,Pres,Inner). |
1844 | | extract_preconditions2(block(Subst),_,Pres,Inner) :- !, |
1845 | | extract_preconditions(Subst,Pres,Inner). |
1846 | | extract_preconditions2(_,Inner,[],Inner). |
1847 | | |
1848 | | filter_predicates_with_unknown_identifiers([],_Env,[]). |
1849 | | filter_predicates_with_unknown_identifiers([Pred|Prest],Env,Result) :- |
1850 | | ( find_unknown_identifier(Pred,Env,_Id) -> |
1851 | | !,Result = Rrest |
1852 | | ; otherwise -> |
1853 | | Result = [Pred|Rrest]), |
1854 | | filter_predicates_with_unknown_identifiers(Prest,Env,Rrest). |
1855 | | find_unknown_identifier(Pred,Env,Id) :- |
1856 | | get_texpr_id(Pred,Id),!, |
1857 | | \+ env_lookup_type(Id,Env,_). |
1858 | | find_unknown_identifier(Pred,Env,Id) :- |
1859 | | syntaxtraversion(Pred,_,_,_,Subs,Names), |
1860 | | store_variables(Names,Env,Subenv), |
1861 | | find_unknown_identifier_l(Subs,Subenv,Id). |
1862 | | find_unknown_identifier_l([S|_],Env,Id) :- |
1863 | | find_unknown_identifier(S,Env,Id),!. |
1864 | | find_unknown_identifier_l([_|Rest],Env,Id) :- |
1865 | | find_unknown_identifier_l(Rest,Env,Id). |
1866 | | |
1867 | | add_precondition(Pre,Old,New) :- |
1868 | | remove_bt(Old,operation(Id,Res,Params,Subst),operation(Id,Res,Params,NewSubst),New), |
1869 | | extract_preconditions(Subst,OldPres,Inner), |
1870 | | conjunct_predicates([Pre|OldPres],NewPre), |
1871 | | create_texpr(precondition(NewPre,Inner),subst,[],NewSubst). |
1872 | | |
1873 | | |
1874 | | check_copied_operation(OpRef,Op,RefMachines,Ein,Eout) :- |
1875 | | % todo: check the "refinement" parameter |
1876 | | visible_env(refinement,operation_bodies,RefMachines,Env1,_Errors,[]), |
1877 | | get_texpr_id(OpRef,OpId),get_texpr_type(OpRef,OpType), |
1878 | | env_store(OpId,OpType,[],Env1,Env), |
1879 | | findall(U, find_unknown_identifier(Op,Env,U), Unknown1), |
1880 | | sort(Unknown1,Unknown), |
1881 | | ( Unknown=[] -> Ein=Eout |
1882 | | ; otherwise -> |
1883 | | op(OpName) = OpId, |
1884 | | join_ids(Unknown,IdList), |
1885 | | (Unknown = [_] -> Plural=[]; Plural=['s']), |
1886 | | append([['Operation ',OpName, |
1887 | | ' was copied from abstract machine but the identifier'], |
1888 | | Plural, |
1889 | | [' '], |
1890 | | IdList, |
1891 | | [' cannot be seen anymore']],Msgs), |
1892 | | ajoin(Msgs,Msg), Ein = [error(Msg,none)|Eout] |
1893 | | ). |
1894 | | join_ids([I],[Msg]) :- !,opname(I,Msg). |
1895 | | join_ids([A|Rest],[Msg,','|Mrest]) :- opname(A,Msg),join_ids(Rest,Mrest). |
1896 | | opname(op(Id),Id) :- !. |
1897 | | opname(Id,Id). |
1898 | | |
1899 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1900 | | % split an identifier into a (possible empty) prefix and the name itself |
1901 | | % e.g. split_prefix('path.to.machine', 'path.to', 'machine'). |
1902 | | split_prefix(Term,Prefix,Main) :- |
1903 | | one_arg_term(Functor,Arg,Term),!, |
1904 | | one_arg_term(Functor,MArg,Main), |
1905 | | split_prefix(Arg,Prefix,MArg). |
1906 | | split_prefix(PR,Prefix,Main) :- %print(split_prefix(PR,Prefix,Main)),nl,trace, |
1907 | | safe_atom_chars(PR,Chars,split_prefix1), |
1908 | | split_prefix2(Chars,Chars,[],CPrefix,CMain), |
1909 | | safe_atom_chars(Main,CMain,split_prefix2), |
1910 | | safe_atom_chars(Prefix,CPrefix,split_prefix3). |
1911 | | split_prefix2([],Main,_,[],Main). |
1912 | | split_prefix2([C|Rest],Previous,PrefBefore,Prefix,Main) :- |
1913 | | ( C='.' -> |
1914 | | append(PrefBefore,RestPrefix,Prefix), |
1915 | | split_prefix2(Rest,Rest,[C],RestPrefix,Main) |
1916 | | ; otherwise -> |
1917 | | append(PrefBefore,[C],NextPref), |
1918 | | split_prefix2(Rest,Previous,NextPref,Prefix,Main)). |
1919 | | |
1920 | | rawmachine_section(Elem,List,Result) :- % |
1921 | | functor(Pattern,Elem,2), |
1922 | | arg(2,Pattern,Result), |
1923 | ? | select(Pattern,List,Rest),!, |
1924 | | (functor(Pattern2,Elem,2),member(Pattern2,Rest) |
1925 | | -> arg(1,Pattern2,Pos), |
1926 | | add_error(bmachine_construction,'Multiple sections for: ',Elem,Pos) |
1927 | | ; true). |
1928 | | |
1929 | | optional_rawmachine_section(Elem,List,Default,Result) :- |
1930 | | ( rawmachine_section(Elem,List,Result1) -> true |
1931 | | ; Result1=Default), |
1932 | | Result1=Result. |
1933 | | |
1934 | | one_arg_term(Functor,Arg,Term) :- %print(one_arg_term(Functor,Arg,Term)),nl, |
1935 | | functor(Term,Functor,1),arg(1,Term,Arg). |
1936 | | |
1937 | | % check if a rawmachine section list has a given section |
1938 | | rawmachine_section_exists(Elem,List) :- % |
1939 | | functor(Pattern,Elem,2), |
1940 | ? | (member(Pattern,List) -> true). |
1941 | | |
1942 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1943 | | % visibility rules |
1944 | | |
1945 | | % the visibility/5 predicate declares what a part of a machine can see |
1946 | | % visibility(MType, Scope, Section, Access, Info) means: |
1947 | | % In a machine of type MType (machine, refinement, implementation), |
1948 | | % an expression in a Section (like invariant) can see |
1949 | | % the identifiers in the Access sections. Access is a list of |
1950 | | % sections, where shortcuts are allowed (see shortcut/2, e.g., shortcut(operations,[unpromoted,promoted]).). |
1951 | | % Scope defines where (in relation to the Section) |
1952 | | % the section in Access are (local: same machine, included: in an |
1953 | | % included machine, etc.) |
1954 | | % Info is a list of additional information that is added to each |
1955 | | % identifier in the environment to build up. E.g. in an operation |
1956 | | % definition, constants are marked readonly. |
1957 | | visibility(machine, local, constraints, [parameters],[]). |
1958 | | visibility(machine, local, includes, [parameters,sets,constants],[]). |
1959 | | visibility(machine, local, properties, [sets,constants],[]). |
1960 | | visibility(machine, local, invariant, [parameters,sets,constants,variables],[]). |
1961 | | visibility(machine, local, operation_bodies, [parameters,sets,constants],[readonly]). |
1962 | | visibility(machine, local, operation_bodies, [operations],Info) :- |
1963 | | (get_preference(allow_operation_calls_in_expr,true) -> Info = [inquiry] |
1964 | | ; Info =[readonly,dontcall] ). |
1965 | | visibility(machine, local, operation_bodies, [variables],[]). |
1966 | ? | visibility(machine, local, initialisation, Access,[not_initialised|Info]) :- visibility(machine,local,operation_bodies,Access,Info). |
1967 | | |
1968 | | visibility(machine, Scope, assertions, [Allow],[inquiry]) :- |
1969 | | (Scope = local -> Allow=operations ; Allow=promoted), |
1970 | | get_preference(allow_operation_calls_in_expr,true). % if we allow calling operations in expressions |
1971 | | visibility(machine, Scope, invariant, [promoted],[inquiry]) :- Scope \= local, |
1972 | | get_preference(allow_operation_calls_in_expr,true). |
1973 | | |
1974 | | visibility(machine, included, properties, [sets,constants],[]). |
1975 | | visibility(machine, included, invariant, [sets,constants,variables],[]). |
1976 | | visibility(machine, included, operation_bodies, [sets,constants,variables],[readonly]). |
1977 | | visibility(machine, included, operation_bodies, [promoted],Info) :- get_operation_call_info(Info). |
1978 | | visibility(machine, included, initialisation, [sets,constants,variables,promoted],[readonly]). |
1979 | | |
1980 | | visibility(machine, used, properties, [sets,constants],[]). |
1981 | | visibility(machine, used, invariant, [parameters,sets,constants,variables],[]). |
1982 | | visibility(machine, used, operation_bodies, [parameters,sets,constants,variables],[readonly]). |
1983 | | visibility(machine, used, initialisation, [parameters,sets,constants,variables],[readonly]). |
1984 | | visibility(machine, used, operation_bodies, [operations],[inquiry]). %% added by leuschel |
1985 | | |
1986 | | visibility(machine, seen, includes, [sets,constants],[]). |
1987 | | visibility(machine, seen, properties, [sets,constants],[]). |
1988 | | visibility(machine, seen, invariant, [sets,constants],[]). |
1989 | | visibility(machine, seen, operation_bodies, [sets,constants,variables],[readonly]). |
1990 | | visibility(machine, seen, initialisation, [sets,constants,variables],[readonly]). |
1991 | | visibility(machine, seen, operation_bodies, [operations],[inquiry]). %% added by leuschel |
1992 | | |
1993 | | visibility(refinement, local, Section, Access, Info) :- |
1994 | | Section \= assertions, % assertions are handled below |
1995 | | visibility(machine, local, Section, Access, Info). |
1996 | | |
1997 | | visibility(refinement, abstraction, includes, [sets,concrete_constants],[]). |
1998 | | visibility(refinement, abstraction, properties, [sets,constants],[]). |
1999 | | visibility(refinement, abstraction, invariant, [sets,constants,concrete_variables],[]). |
2000 | | visibility(refinement, abstraction, invariant, [abstract_variables],[abstraction]). |
2001 | | visibility(refinement, abstraction, operation_bodies, [sets,concrete_constants],[readonly]). |
2002 | | visibility(refinement, abstraction, operation_bodies, [concrete_variables],[]). |
2003 | | |
2004 | | visibility(refinement, included, properties, [sets,constants],[]). |
2005 | | visibility(refinement, included, invariant, [sets,constants,variables],[]). |
2006 | | visibility(refinement, included, operation_bodies, [sets,constants,variables,promoted],[re]). % What is re ??? TO DO: investigate |
2007 | | |
2008 | | visibility(refinement, seen, includes, [sets,constants],[]). |
2009 | | visibility(refinement, seen, properties, [sets,constants],[]). |
2010 | | visibility(refinement, seen, invariant, [sets,constants],[]). |
2011 | | visibility(refinement, seen, operation_bodies, [sets,constants,variables],[readonly]). |
2012 | | visibility(refinement, seen, operation_bodies, [operations],[inquiry]). |
2013 | | |
2014 | | visibility(refinement, Ref, initialisation, Access, [not_initialised|Info]) :- |
2015 | | visibility(refinement,Ref,operation_bodies,Access,Info). |
2016 | | |
2017 | | % assertions have same visibility as invariant |
2018 | | visibility(MType, Ref, assertions, Part, Access) :- |
2019 | ? | visibility(MType,Ref,invariant,Part,Access). |
2020 | | |
2021 | | visibility(implementation, Ref, Section, Part, Access) :- |
2022 | | visibility(refinement, Ref, Section, Part, Access). |
2023 | | visibility(implementation, included, values_expression, [concrete_constants,sets],[]). |
2024 | | visibility(implementation, seen, values_expression, [concrete_constants,sets],[]). |
2025 | | |
2026 | | % For predicates over pre- and poststates |
2027 | | visibility(MType, Rev, prepost, Access, Info) :- |
2028 | ? | visibility(MType, Rev, invariant, Access, Info). |
2029 | | visibility(MType, Rev, prepost, Access, [primed,poststate|Info]) :- |
2030 | ? | visibility(MType, Rev, invariant, Access, Info). |
2031 | | |
2032 | | % add error messages for some common mistakes (access to parameters in the properties) |
2033 | | visibility(machine, local, properties, [parameters], [error('a parameter cannot be accessed in the PROPERTIES section')]). |
2034 | | % the following rule should be helpful for the user, but additionally it also |
2035 | | % provides a mechanism to re-introduce abstract variables in the INVARIANT |
2036 | | % section of a while loop (to enable this, an identifier is also marked with "abstraction") |
2037 | | visibility(refinement, abstraction, operation_bodies, [abstract_variables], [error('illegal access to an abstract variable in an operation'),abstraction]). |
2038 | | |
2039 | | get_operation_call_info(Info) :- |
2040 | | (get_preference(allow_operation_calls_in_expr,true) -> Info = [inquiry] ; Info=[readonly]). |
2041 | | |
2042 | | % lookups up all identifier sections that are accessible from |
2043 | | % the given section, removes all shortcuts and removes |
2044 | | % duplicate entries |
2045 | | % |
2046 | | % Returns a list of vis(Section,Info) where Section is the |
2047 | | % identifier section that can be seen with Info as a list |
2048 | | % of additional information |
2049 | | expanded_visibility(MType, Ref, Part, Access) :- |
2050 | | findall(vis(Section,Infos), |
2051 | | ( visibility(MType,Ref,Part,Sections1,Infos), |
2052 | | expand_shortcuts(Sections1,Sections), |
2053 | | member(Section,Sections)), |
2054 | | Access1), |
2055 | | sort(Access1,Access). |
2056 | | |
2057 | | % visible_env/6 creates a type environment for a certain |
2058 | | % part of a machine by looking up which identifier should |
2059 | | % be visible from there and what additional information should |
2060 | | % be added (e.g. to restrict access to read-only) |
2061 | | % |
2062 | | % The visibility/6 facts are used to define which identifier are visible |
2063 | | % |
2064 | | % MType: machine type (machine, refinement, ...) |
2065 | | % Part: part of the machine for which the environment should be created |
2066 | | % RefMachines: referred machines that are already typed |
2067 | | % Env: The created environment |
2068 | | % Errors: errors might be added if multiple variables with the same identifier |
2069 | | % are declared |
2070 | | visible_env(MType, Part, RefMachines, Env, Err_in, Err_out) :- |
2071 | | env_empty(Init), |
2072 | | visible_env(MType, Part, RefMachines, Init, Env, Err_in, Err_out). |
2073 | | % visible_env/7 is like visible_env/6, but an initial environment |
2074 | | % can be given in "In" |
2075 | | visible_env(MType, Part, RefMachines, In, Out, Err_in, Err_out) :- |
2076 | | foldl(visible_env2(MType,Part),RefMachines,In/Err_in,Out/Err_out). |
2077 | | visible_env2(MType,Part,ref(Scope,Machine),InEnvErr,OutEnvErr) :- |
2078 | | get_machine_name(Machine,MName), |
2079 | | debug_println(9,visible_env2(MName,Scope)), |
2080 | | ( Scope == local -> |
2081 | | ~~mnf(get_section(definitions,Machine,Defs)), |
2082 | | %nl,print(adding_defs(Defs)),nl,nl, |
2083 | | foldl(env_add_def(MName),Defs,InEnvErr,InterEnvErr) |
2084 | | ; otherwise -> |
2085 | | InEnvErr=InterEnvErr), |
2086 | | ~~mnf(expanded_visibility(MType, Scope, Part, Access)), |
2087 | | foldl(create_vis_env(Scope,MName,Machine),Access,InterEnvErr,OutEnvErr). |
2088 | | env_add_def(MName,definition_decl(Name,PType,Pos,Params,Body,_Deps),InEnvErr,OutEnvErr) :- |
2089 | | Type = definition(PType,Params,Body), |
2090 | | Info = [nodeid(Pos),loc(local,MName,definitions)], |
2091 | | create_texpr(identifier(Name),Type,Info,TExpr), |
2092 | | add_unique_variable(TExpr,InEnvErr,OutEnvErr),!. |
2093 | | env_add_def(MName,Def,EnvErr,EnvErr) :- |
2094 | | add_internal_error('Cannot deal with DEFINITION: ',env_add_def(MName,Def,EnvErr,EnvErr)). |
2095 | | |
2096 | | create_vis_env(Scope,MName,IDS,vis(Section,Infos),InEnvErr,OutEnvErr) :- |
2097 | | ~~mnf(get_section(Section,IDS,Identifiers1)), |
2098 | | maplist(add_infos_to_identifier([loc(Scope,MName,Section)|Infos]),Identifiers1,Identifiers), |
2099 | | foldl(add_unique_variable,Identifiers,InEnvErr,OutEnvErr). |
2100 | | |
2101 | | add_unique_variable(Var1,Old/Err_in,New/Err_out) :- |
2102 | | optionally_rewrite_id(Var1,Var), |
2103 | | get_texpr_id(Var,Id), |
2104 | | get_texpr_type(Var,Type), |
2105 | | get_texpr_info(Var,InfosOfNew),!, |
2106 | | ( env_lookup_type(Id,Old,_) -> |
2107 | | % we have a collision of two identifiers |
2108 | | handle_collision(Var,Id,Type,Old,InfosOfNew,New,Err_in,Err_out) |
2109 | | ; otherwise -> |
2110 | | % no collision, just introduce the new identifier |
2111 | | env_store(Id,Type,InfosOfNew,Old,New), |
2112 | | Err_in=Err_out |
2113 | | ). |
2114 | | add_unique_variable(Var,Env/Err,Env/Err) :- |
2115 | | ( Var = b(definition(DEFNAME,_),_,INFO) -> |
2116 | | add_error(add_unique_variable,'DEFINITION used in place of Identifier: ',DEFNAME,INFO) |
2117 | | ; otherwise -> |
2118 | | add_error(add_unique_variable,'Expected Identifier, but got: ',Var,Var) |
2119 | | ). |
2120 | | |
2121 | | add_infos_to_identifier(NewInfos,In,Out) :- |
2122 | | add_texpr_infos(In,NewInfos,Out). |
2123 | | |
2124 | | % get the ID of the variable, prime it if the infos contain "primed" |
2125 | | optionally_rewrite_id(Var,NewVar) :- |
2126 | | get_texpr_info(Var,InfosIn), |
2127 | | ( selectchk(primed,InfosIn,InfosOut) -> |
2128 | | get_texpr_id(Var,Id1), |
2129 | | get_texpr_type(Var,Type), |
2130 | | atom_concat(Id1,'\'',Id), |
2131 | | create_texpr(identifier(Id),Type,InfosOut,NewVar) |
2132 | | ; otherwise -> |
2133 | | Var = NewVar). |
2134 | | |
2135 | | :- use_module(translate,[translate_span/2]). |
2136 | | % in case of a collision, we have three options: |
2137 | | % - overwrite the old identifier, |
2138 | | % - ignore the new identifier or |
2139 | | % - generate an error message |
2140 | | handle_collision(Var,Name,Type,OldEnv,InfosOfNew,NewEnv,Ein,Eout) :- |
2141 | | env_lookup_infos(Name,OldEnv,InfosOfExisting), |
2142 | | ( collision_precedence(Name,InfosOfExisting,InfosOfNew) -> |
2143 | | % this identifier should be ignored |
2144 | | OldEnv = NewEnv, |
2145 | | Ein = Eout |
2146 | | ; collision_precedence(Name,InfosOfNew,InfosOfExisting) -> |
2147 | | % the existing should be overwritten |
2148 | | env_store(Name,Type,InfosOfNew,OldEnv,NewEnv), |
2149 | | Ein = Eout |
2150 | | ; otherwise -> |
2151 | | % generate error and let the environment unchanged |
2152 | | (Name = op(IName) -> true; Name=IName), |
2153 | | get_texpr_pos(Var,Pos1), |
2154 | | safe_get_info_pos(InfosOfExisting,Pos2), |
2155 | | ( double_inclusion_allowed(Pos1,Pos2,InfosOfExisting) |
2156 | | -> print(double_inclusion_of_id_allowed(Name,Type,Pos1,OldEnv,InfosOfExisting)),nl, |
2157 | | OldEnv=NewEnv, Ein=Eout |
2158 | | ; (better_pos(Pos2,Pos1), \+ better_pos(Pos1,Pos2) |
2159 | | -> Pos = Pos2 |
2160 | | ; Pos = Pos1), |
2161 | | translate_span(Pos1,PS1), translate_span(Pos2,PS2), |
2162 | | ajoin(['Identifier \'', IName, '\' declared twice at (Line:Col[:File]) ', PS1, ' and ', PS2],Msg), |
2163 | | Ein = [error(Msg,Pos)|Eout], |
2164 | | OldEnv = NewEnv |
2165 | | ) |
2166 | | ). |
2167 | | |
2168 | | % SEE ISSUE PROB-403 |
2169 | | % Correct behaviour related to multiple instantiation is specified in |
2170 | | % B Reference Manual (AtelierB 4.2.1), 8.3 B Project/Instantiating and renaming. |
2171 | | % => Constants and Sets defined in machines instantiated multiple times CAN be used in the including machine with their original (non-prefixed names). |
2172 | | % we do not use this code yet: this leads to the constants being added multiple times |
2173 | | double_inclusion_allowed(Pos1,Pos2,InfosOfExisting) :- fail, % DISABLE AT THE MOMENT; TO DO: FIX |
2174 | | Pos1==Pos2, |
2175 | | print(chk(InfosOfExisting)),nl, |
2176 | | member(loc(LOC,_,Section),InfosOfExisting), |
2177 | | section_can_be_included_multiple_times_nonprefixed(Section), |
2178 | | % check that we are in a context of an included machine identifier: |
2179 | | (inclusion_directive(LOC) |
2180 | | -> true |
2181 | | ; %LOC is probably local |
2182 | | member(origin([INCL/_MACHINE|_]),InfosOfExisting), inclusion_directive(INCL) |
2183 | | ). |
2184 | | |
2185 | | inclusion_directive(included). |
2186 | | inclusion_directive(used). |
2187 | | inclusion_directive(seen). % imports ?? |
2188 | | |
2189 | | section_can_be_included_multiple_times_nonprefixed(abstract_constants). |
2190 | | section_can_be_included_multiple_times_nonprefixed(concrete_constants). |
2191 | | section_can_be_included_multiple_times_nonprefixed(sets). |
2192 | | section_can_be_included_multiple_times_nonprefixed(enumerated_sets). |
2193 | | section_can_be_included_multiple_times_nonprefixed(enumerated_elements). |
2194 | | |
2195 | | |
2196 | | % decide which position info is better: prefer info in main file (highlighting) |
2197 | | :- use_module(bmachine,[b_get_main_filenumber/1]). |
2198 | | better_pos(pos(_,Filenumber,_Srow,_Scol,_Erow,_Ecol),_) :- b_get_main_filenumber(Filenumber). |
2199 | | better_pos(_,none). |
2200 | | |
2201 | | safe_get_info_pos(Info,Pos) :- (get_info_pos(Info,Pos) -> true ; Pos=none). |
2202 | | |
2203 | | % collision_precedence/3 decides if the first variable takes |
2204 | | % precedence over the second in case of a collision |
2205 | | % the decision is made by the additional information of both |
2206 | | % variables |
2207 | | collision_precedence(_Name,PreferredVarInfos,DroppedVarInfos) :- |
2208 | | % in case of a re-introduced variable from the abstraction, |
2209 | | % we prefer the concrete variable to the abstract one. |
2210 | | is_abstraction(DroppedVarInfos,PreferredVarInfos),!. |
2211 | | collision_precedence(_Name,PreferredVarInfos,DroppedVarInfos) :- |
2212 | | % We are checking an Event-B model with multi-level support |
2213 | | % and have the same variable in two different refinement levels. |
2214 | | % Then the one in the more refined module takes precedence |
2215 | | member(level(L1),PreferredVarInfos), |
2216 | | member(level(L2),DroppedVarInfos), |
2217 | | % Level 0 is the abstract level, level 1 the first refinement, etc. |
2218 | | L1 > L2,!. |
2219 | | collision_precedence(Name,PreferredVarInfos,DroppedVarInfos) :- |
2220 | | % A local definition takes precedence over a non-local identifier |
2221 | | % TODO: |
2222 | | member(loc(local,_DefMachine,definitions),PreferredVarInfos), |
2223 | ? | member(loc(_,_VarMachine,Section),DroppedVarInfos), |
2224 | | Section \= definitions,!, |
2225 | | (preferences:get_preference(warn_if_definition_hides_variable,true) |
2226 | | -> ((get_info_pos(PreferredVarInfos,Pos1), Pos1 \= none, |
2227 | | get_info_pos(DroppedVarInfos,Pos2), Pos2 \= none) |
2228 | | -> translate:translate_span(Pos1,Pos1Str), translate:translate_span(Pos2,Pos2Str), |
2229 | | ajoin(['Warning: DEFINITION of ', Name, ' at ', Pos1Str, |
2230 | | ' hides variable with same name at', Pos2Str, '.'], Msg) |
2231 | | ; ajoin(['Warning: DEFINITION of ', Name, ' hides variable with same name.'], Msg) |
2232 | | ), |
2233 | | add_warning(Msg) |
2234 | | ; true |
2235 | | ). |
2236 | | |
2237 | | % is_abstraction/2 tries to find out if (in case of a name clash of |
2238 | | % two variables) the second variable is just the re-introduced abstract |
2239 | | % variable in a refinement. |
2240 | | % InfosAbs is the list of information about the abstract variable |
2241 | | % InfosConc is the list of information about the concrete variable |
2242 | | is_abstraction(InfosAbs,InfosConc) :- |
2243 | | % one variable is an abstract variable, introduced in the abstraction |
2244 | | member(loc(abstraction,_,abstract_variables),InfosAbs), |
2245 | | % the other is either an abstract or concrete variable, |
2246 | | member(VarType,[abstract_variables,concrete_variables]), |
2247 | | % introduced either locally or in an included machine |
2248 | | member(Scope,[local,included]), |
2249 | | member(loc(Scope,_,VarType),InfosConc). |
2250 | | % and the same for constants: |
2251 | | is_abstraction(InfosAbs,InfosConc) :- |
2252 | | % one variable is an abstract variable, introduced in the abstraction |
2253 | | member(loc(abstraction,_,abstract_constants),InfosAbs), |
2254 | | % the other is either an abstract or concrete variable, |
2255 | | member(VarType,[abstract_constants,concrete_constants]), |
2256 | | % introduced either locally or in an included machine |
2257 | | member(Scope,[local,included]), |
2258 | | member(loc(Scope,_,VarType),InfosConc). |
2259 | | |
2260 | | % shortcuts for sections, to ease the use of typical combinations of |
2261 | | % sections |
2262 | | shortcut(all_parameters,[parameters,internal_parameters]). |
2263 | | shortcut(sets,[deferred_sets,enumerated_sets,enumerated_elements]). |
2264 | | shortcut(constants,[abstract_constants,concrete_constants]). |
2265 | | shortcut(variables,[abstract_variables,concrete_variables]). |
2266 | | shortcut(operations,[unpromoted,promoted]). |
2267 | | shortcut(identifiers,[all_parameters,sets,constants,variables,operations]). |
2268 | | |
2269 | | expand_shortcuts(Sections,Expanded) :- |
2270 | | foldl(expand_shortcut,Sections,Expanded,[]). |
2271 | | expand_shortcut(Section,Sections,RSections) :- |
2272 | | ( shortcut(Section,Expanded) -> |
2273 | | foldl(expand_shortcut,Expanded,Sections,RSections) |
2274 | | ; valid_section(Section) -> |
2275 | | Sections = [Section|RSections] |
2276 | | ; otherwise -> |
2277 | | add_internal_error('invalid section',expand_shortcut(Section,Sections,RSections)),fail). |
2278 | | |
2279 | | % find sections that can see given sections |
2280 | | find_relevant_sections(RSecs,MTypes,Result) :- |
2281 | | expand_shortcuts(RSecs,Sections), |
2282 | | % print(find_rel_sections_for(Sections)),nl, |
2283 | | findall(R, |
2284 | | ( member(MType,MTypes), |
2285 | | visibility(MType,local,R,SAccess,_), |
2286 | | expand_shortcuts(SAccess,Access), |
2287 | | member(S,Sections), |
2288 | | member(S,Access), |
2289 | | valid_section(R)), |
2290 | | Result1), |
2291 | | sort(Result1,Result). |
2292 | | |
2293 | | |
2294 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2295 | | % renaming |
2296 | | |
2297 | | rename_relevant_sections(DefSecs,Renamings,Machine,New) :- |
2298 | | find_relevant_sections(DefSecs,[machine],Relevant), |
2299 | | rename_in_sections(Relevant,Renamings,Machine,New). |
2300 | | rename_in_sections([],_,M,M). |
2301 | | rename_in_sections([Section|Rest],Renamings,Old,New) :- |
2302 | | select_section_texprs(Section,OTExprs,NTExprs,Old,Inter), |
2303 | | rename_bt_l(OTExprs,Renamings,NTExprs), |
2304 | | rename_in_sections(Rest,Renamings,Inter,New). |
2305 | | |
2306 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2307 | | % cleaning up machine |
2308 | | clean_up_machine(In,RefMachines,Out) :- |
2309 | | extract_parameter_types([ref(local,In)|RefMachines],NonGroundExceptions), |
2310 | | clean_up_machine2(NonGroundExceptions,In,Out). |
2311 | | clean_up_machine2(NonGroundExceptions) --> |
2312 | | clean_up_section(constraints,NonGroundExceptions), |
2313 | | clean_up_section(properties,NonGroundExceptions), |
2314 | | clean_up_section(invariant,NonGroundExceptions), |
2315 | | clean_up_section(initialisation,NonGroundExceptions), |
2316 | | clean_up_section(assertions,NonGroundExceptions), |
2317 | | clean_up_section(operation_bodies,NonGroundExceptions). |
2318 | | :- load_files(library(system), [when(compile_time), imports([environ/2])]). |
2319 | | :- if(environ(prob_safe_mode,true)). |
2320 | | clean_up_section(Section,NonGroundExceptions,In,Out) :- |
2321 | | select_section_texprs(Section,Old,New,In,Out), |
2322 | | %format('Cleaning up and optimizing section ~w~n',[Section]), %maplist(check_ast,Old), % this will raise errors |
2323 | | clean_up_l_with_optimizations(Old,NonGroundExceptions,New,Section), |
2324 | | %format('Checking result of clean_up section ~w~n',[Section]), |
2325 | | maplist(check_ast(true),New), |
2326 | | formatsilent('Finished checking section ~w~n',[Section]). |
2327 | | :- else. |
2328 | | clean_up_section(Section,NonGroundExceptions,In,Out) :- |
2329 | | % debug_stats(cleaning_up(Section)), |
2330 | | select_section_texprs(Section,Old,New,In,Out), |
2331 | | clean_up_l_with_optimizations(Old,NonGroundExceptions,New,Section). |
2332 | | :- endif. |
2333 | | |
2334 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2335 | | % type expressions in context of an already type-checked machine |
2336 | | % TO DO: maybe do some performance optimisations for identifiers, values, ... to avoid creating scope, see evaluate_formula_typecheck2 optimisation |
2337 | | type_in_machine_l(Exprs,Scope,Machine,Types,TExprs,Errors) :- |
2338 | | MType = machine, |
2339 | | create_scope(Scope,MType,Machine,Env,Errors,E1), % this can be expensive for big B machines |
2340 | | %runtime_profiler:profile_single_call(create_scope,unknown, |
2341 | | % bmachine_construction:create_scope(Scope,MType,Machine,Env,Errors,E1)), |
2342 | | btype_ground_dl(Exprs,Env,[],Types,TExprUncleans,E1,[]), |
2343 | | maplist(clean_up_pred_or_expr([]),TExprUncleans,TExprs). |
2344 | | |
2345 | | % note prob_ids(visible) scope is expanded somewhere else |
2346 | | create_scope(Scope,MType,Machine,Env,Ein,Eout) :- |
2347 | | env_empty(Init), |
2348 | | add_theory_operators(Machine,Init,WithOperators), |
2349 | | foldl(create_scope2(MType,Machine),Scope,WithOperators/Ein,Env/Eout). |
2350 | | add_theory_operators(Machine,Ein,Eout) :- |
2351 | | get_section(operators,Machine,Operators), |
2352 | | keys_and_values(Operators,Ids,Ops), |
2353 | | foldl(env_store_operator,Ids,Ops,Ein,Eout). |
2354 | | create_scope2(MType,Machine,Scope,In/Ein,Out/Eout) :- |
2355 | | ( Scope = constants -> |
2356 | | ~~mnf( visible_env(MType,properties,[ref(local,Machine)],In,Out,Ein,Eout) ) |
2357 | | ; Scope = variables -> |
2358 | | ~~mnf( visible_env(MType,invariant,[ref(local,Machine)],In,Out,Ein,Eout) ) |
2359 | | ; Scope = assertions_scope -> |
2360 | | ~~mnf( visible_env(MType,assertions,[ref(local,Machine)],In,Out,Ein,Eout) ) |
2361 | | ; Scope = prepost -> |
2362 | | ~~mnf( visible_env(MType,prepost,[ref(local,Machine)],In,Out,Ein,Eout) ) |
2363 | | ; Scope = operation_bodies -> |
2364 | | ~~mnf( visible_env(MType,operation_bodies,[ref(local,Machine)],In,Out,Ein,Eout) ) |
2365 | | ; Scope = operation(Op) -> |
2366 | | create_operation_scope(Op,Machine,In,Out), Ein=Eout |
2367 | | ; Scope = env(ExplicitEnv) -> |
2368 | | ExplicitEnv = Out, Ein=Eout |
2369 | | ; Scope = identifier(Ids) -> |
2370 | | ~~mnf( store_variables(Ids,In,Out) ), Ein=Eout |
2371 | | ; otherwise -> |
2372 | | add_error(bmachine_construction, 'invalid scope', Scope),fail). |
2373 | | create_operation_scope(Op,Machine,In,Out) :- |
2374 | | get_section(operation_bodies,Machine,OpBodies), |
2375 | | get_texpr_id(OpId,op(Op)), |
2376 | | get_texpr_expr(TOp,operation(OpId,Results,Params,_)), |
2377 | | ( member(TOp,OpBodies) -> |
2378 | | append(Results,Params,LocalVariables), |
2379 | | store_variables(LocalVariables,In,Out) |
2380 | | ; otherwise -> |
2381 | | ajoin(['operation \'',Op,'\' not found for building scope'], Msg), |
2382 | | add_error(bmachine_construction,Msg),fail). |
2383 | | store_variables(Ids,In,Out) :- foldl(store_variable,Ids,In,Out). |
2384 | | store_variable(Id,In,Out) :- |
2385 | | get_texpr_id(Id,Name),get_texpr_type(Id,Type), |
2386 | | env_store(Name,Type,[],In,Out). |
2387 | | |
2388 | | type_open_predicate_with_quantifier(OptionalOuterQuantifier,Predicate,Scope,Machine,Result,Errors) :- |
2389 | | type_open_formula(Predicate,Scope,false,Machine,pred,Identifiers,TPred,Errors), |
2390 | | ( Identifiers = [] -> |
2391 | | Result = TPred |
2392 | | ; OptionalOuterQuantifier=forall -> |
2393 | | create_forall(Identifiers,TPred,Result) |
2394 | | ; OptionalOuterQuantifier=no_quantifier -> |
2395 | | Result = TPred |
2396 | | ; otherwise -> create_exists(Identifiers,TPred,Result) |
2397 | | ). |
2398 | | |
2399 | | type_open_formula(Raw,Scope,AllowOpenIdsinExpressions,Machine,Type,Identifiers,Result,Errors) :- |
2400 | | create_scope(Scope,machine,Machine,Env1,Errors,E1), |
2401 | | ( Identifiers==[] -> Mode=closed, Env1=Env |
2402 | | ; otherwise -> Mode=open, openenv(Env1,Env)), |
2403 | | btype_ground_dl([Raw],Env,[],[Type],[TExprUnclean],E1,E2), |
2404 | | ( Mode=closed -> true |
2405 | | ; otherwise -> |
2406 | | openenv_identifiers(Env,Identifiers), |
2407 | | check_ground_types_dl(Identifiers,[],E2,E3) |
2408 | | ), |
2409 | | %print(type_open_formula(Identifiers,TExprUnclean)),nl, |
2410 | | mark_outer_quantifier_ids(TExprUnclean,TExprUnclean2), |
2411 | | clean_up_pred_or_expr([],TExprUnclean2,TResult), |
2412 | | ( Identifiers = [] -> % no newly introduced identifiers, no problem |
2413 | | E3 = [] |
2414 | | ; Type = pred -> % newly introduced identifiers in a predicate - ok |
2415 | | E3 = [] |
2416 | | ; AllowOpenIdsinExpressions=true -> % we explicitly allow open ids in expressions |
2417 | | E3 = [] |
2418 | | ; otherwise -> % newly introduced identifiers in expression make no sense |
2419 | | % (is that so?) |
2420 | | foldl(add_unknown_identifier_error,Identifiers,E3,[]) |
2421 | | ), |
2422 | | Result = TResult. |
2423 | | add_unknown_identifier_error(TId,[error(Msg,Pos)|E],E) :- |
2424 | | get_texpr_id(TId,Id), |
2425 | | ajoin(['Unknown identifier ',Id,'.'],Msg), |
2426 | | get_texpr_pos(TId,Pos). |
2427 | | |
2428 | | % mark outermost identfiers so that they don't get optimized away |
2429 | | % e.g., ensure that we print the solution for something like #(y2,x2).(x2 : 0..10 & y2 : 0..10 & x2 = 10 & y2 = 10) |
2430 | | mark_outer_quantifier_ids(b(exists(IDS,Pred),pred,Info),Res) :- |
2431 | | maplist(mark_id,IDS,MIDS),!, |
2432 | | Res = b(exists(MIDS,Pred),pred,Info). |
2433 | | mark_outer_quantifier_ids(b(let_predicate(IDS,Exprs,Pred),pred,Info),Res) :- |
2434 | | maplist(mark_id,IDS,MIDS),!, |
2435 | | Res = b(let_predicate(MIDS,Exprs,Pred),pred,Info). |
2436 | | mark_outer_quantifier_ids(b(forall(IDS,LHS,RHS),pred,Info),Res) :- |
2437 | | maplist(mark_id,IDS,MIDS),!, |
2438 | | Res = b(forall(MIDS,LHS,RHS),pred,Info). |
2439 | | mark_outer_quantifier_ids(X,X). |
2440 | | |
2441 | | mark_id(b(identifier(ID),TYPE,INFO),b(identifier(ID),TYPE,[do_not_optimize_away|INFO])). |
2442 | | |
2443 | | |
2444 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2445 | | % Warnings |
2446 | | |
2447 | | :- dynamic warnings/1. |
2448 | | clear_warnings :- |
2449 | | retractall( warnings(_) ). |
2450 | | show_warnings :- |
2451 | | warnings(Warning), |
2452 | | add_warning(bmachine_construction, Warning), |
2453 | | fail. |
2454 | | show_warnings. |
2455 | | |
2456 | | |
2457 | | add_warning(Warning) :- |
2458 | | ( warnings(Warning) -> true |
2459 | | ; otherwise -> assert( warnings(Warning) ) ). |