1 | % (c) 2009-2025 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, | |
2 | % Heinrich Heine Universitaet Duesseldorf | |
3 | % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) | |
4 | ||
5 | :- module(b_machine_hierarchy,[analyse_hierarchy/2 | |
6 | ,analyse_eventb_hierarchy/2 | |
7 | ,main_machine_name/1 % find out name of main machine | |
8 | ,machine_name/1 % can be used to find out machine names | |
9 | ,machine_type/2 % machine_type(Name, R) - get the type | |
10 | % (abstract_machine, abstract_model, refinement, implementation) for the machine named Name | |
11 | ,machine_references/2 % get a list of references and the type of the reference. | |
12 | % Example: if A refines B, then machine_references('A',X) returns X= [ref(refines'B','')]. | |
13 | % The last argument in ref/3 is the prefix | |
14 | ,machine_identifiers/7 % gets Params,Sets,Abstract Variables,Concrete Variables,Abstract Constants and Concrete | |
15 | % Constants for a machine. Abstract constants/Variables are introduced in the machine | |
16 | % using the ABSTRACT_VARAIABLES/ABSTARCT_CONSTANTS keyword. The concrete versions | |
17 | % analogously via CONCRETE_VARAIBLES,CONCRETE_CONSTANTS | |
18 | % Example output for | |
19 | % MACHINE xx(T,u) | |
20 | % CONSTRAINTS u:T | |
21 | % SETS A; B={foo, bar} | |
22 | % CONCRETE_CONSTANTS cc | |
23 | % PROPERTIES cc:B | |
24 | % ABSTRACT_VARIABLES xx | |
25 | % CONCRETE_VARIABLES yy | |
26 | % INVARIANT | |
27 | % xx:INT & | |
28 | % yy : T | |
29 | % INITIALISATION xx,yy:= ({1|->2};{2|->4})(1), u | |
30 | % END | |
31 | % | |
32 | % machine_identifiers(A,B,C,D,E,F,G). | |
33 | % A = xx, | |
34 | % B = [identifier(pos(4,1,1,12,1,12),'T'),identifier(pos(5,1,1,14,1,14),u)], | |
35 | % C = [deferred_set(pos(11,1,3,6,3,6),'A'),enumerated_set(pos(12,1,3,9,3,20),'B',[identifier(pos(13,1,3,12,3,14),foo),identifier(pos(14,1,3,17,3,19),bar)])], | |
36 | % D = [identifier(pos(22,1,6,20,6,21),xx)], | |
37 | % E = [identifier(pos(24,1,7,20,7,21),yy)], | |
38 | % F = [], | |
39 | % G = [identifier(pos(16,1,4,20,4,21),cc)] | |
40 | , get_machine_identifier_names/7 % a version returning atomic identifier names | |
41 | , machine_has_constants/1 % check if machine has some constants | |
42 | , get_machine_short_desc/2 | |
43 | ,abstract_constant/2, concrete_constant/2 | |
44 | ,possibly_abstract_constant/1 | |
45 | ,machine_operations/2 % machine_operations(M, Ops) gets the names of the Operations defined in Machine M | |
46 | ,machine_operation_calls/2 | |
47 | ,machine_hash/2 % stores a hash value for the machine | |
48 | ,properties_hash/2 % computes a hash over the constants and properties | |
49 | ,operation_hash/3 % computes a hash for the operation in a machine | |
50 | ,write_dot_hierarchy_to_file/1 | |
51 | ,write_dot_op_hierarchy_to_file/1 | |
52 | ,write_dot_event_hierarchy_to_file/1 | |
53 | ,write_dot_variable_hierarchy_to_file/1 | |
54 | ,get_machine_inclusion_graph/3 | |
55 | ,get_machine_topological_order/1 | |
56 | ,print_machine_topological_order/0 | |
57 | ]). | |
58 | ||
59 | :- use_module(library(lists)). | |
60 | :- use_module(library(ordsets)). | |
61 | :- use_module(bmachine,[b_get_definition/5, get_machine_file_number/4]). | |
62 | :- use_module(bmachine_construction). | |
63 | :- use_module(debug). | |
64 | :- use_module(self_check). | |
65 | :- use_module(specfile,[get_specification_description/2]). | |
66 | :- use_module(extension('probhash/probhash'),[raw_sha_hash/2]). | |
67 | :- use_module(input_syntax_tree). | |
68 | :- use_module(dotsrc(dot_graph_generator), [gen_dot_graph/6, dot_no_same_rank/1, | |
69 | use_new_dot_attr_pred/7, get_dot_cluster_name/2]). | |
70 | ||
71 | :- use_module(value_persistance,[cache_is_activated/0]). | |
72 | :- use_module(probsrc(tools),[split_list/4]). | |
73 | ||
74 | :- use_module(module_information,[module_info/2]). | |
75 | :- module_info(group,ast). | |
76 | :- module_info(description,'This module provides functionality to visualize the dependencies of a B machine (include and sees relations, etc.).'). | |
77 | ||
78 | :- volatile | |
79 | main_machine_name/1, | |
80 | machine_type/2, | |
81 | machine_package_directory/2, | |
82 | machine_references/2, | |
83 | machine_identifiers/7, | |
84 | machine_operations/2, | |
85 | machine_operation_calls/2, | |
86 | machine_values_identifiers/2, | |
87 | refines_event/4, | |
88 | machine_has_assertions/1, | |
89 | raw_machine/2, | |
90 | machine_hash_cached/2, | |
91 | properties_hash_cached/2, operation_hash_cached/3, | |
92 | basic_operation_hash/4, | |
93 | event_refinement_change/6. | |
94 | :- dynamic | |
95 | main_machine_name/1, | |
96 | machine_type/2, | |
97 | machine_package_directory/2, | |
98 | machine_references/2, | |
99 | machine_identifiers/7, | |
100 | machine_operations/2, | |
101 | machine_operation_calls/2, | |
102 | machine_values_identifiers/2, | |
103 | refines_event/4, | |
104 | machine_has_assertions/1, | |
105 | raw_machine/2, | |
106 | machine_hash_cached/2, | |
107 | properties_hash_cached/2, operation_hash_cached/3, | |
108 | basic_operation_hash/4, | |
109 | event_refinement_change/6. | |
110 | :- volatile abstract_constant/2, concrete_constant/2. | |
111 | :- dynamic abstract_constant/2. | |
112 | :- dynamic concrete_constant/2. | |
113 | ||
114 | :- use_module(specfile,[animation_minor_mode/1]). | |
115 | possibly_abstract_constant(ID) :- | |
116 | (abstract_constant(ID,_) ; animation_minor_mode(eventb),concrete_constant(ID,_) ). | |
117 | ||
118 | reset_hierarchy :- | |
119 | retract_all(main_machine_name/1), | |
120 | retract_all(machine_type/2), | |
121 | retract_all(machine_package_directory/2), | |
122 | retract_all(machine_references/2), | |
123 | retract_all(machine_identifiers/7), | |
124 | retract_all(abstract_constant/2), | |
125 | retract_all(concrete_constant/2), | |
126 | retract_all(machine_operations/2), | |
127 | retract_all(machine_operation_calls/2), | |
128 | retract_all(machine_values_identifiers/2), | |
129 | retract_all(refines_event/4), | |
130 | retract_all(machine_has_assertions/1), | |
131 | retract_all(machine_hash_cached/2), | |
132 | retract_all(raw_machine/2), | |
133 | retract_all(properties_hash_cached/2), | |
134 | retract_all(operation_hash_cached/3), | |
135 | retract_all(basic_operation_hash/4), | |
136 | retract_all(event_refinement_change/6). | |
137 | ||
138 | machine_name(Name) :- machine_type(Name,_). | |
139 | ||
140 | :- use_module(eventhandling,[register_event_listener/3]). | |
141 | :- register_event_listener(clear_specification,reset_hierarchy, | |
142 | 'Reset B Machine Hierarchy Facts.'). | |
143 | ||
144 | retract_all(Functor/Arity) :- | |
145 | functor(Pattern,Functor,Arity), | |
146 | retractall(Pattern). | |
147 | ||
148 | analyse_hierarchy(Main,Machines) :- (var(Main) ; var(Machines)),!, | |
149 | add_internal_error('Illegal call:',analyse_hierarchy(Main,Machines)). | |
150 | analyse_hierarchy(Main,Machines) :- | |
151 | reset_hierarchy, | |
152 | assertz(main_machine_name(Main)), | |
153 | analyse_machine(Main,Machines,main). | |
154 | ||
155 | :- use_module(error_manager). | |
156 | :- use_module(tools_strings,[ajoin/2]). | |
157 | :- use_module(bmachine,[b_filenumber/4]). | |
158 | :- public analyse_machine/3. | |
159 | analyse_machine(Name,_Machines,_) :- | |
160 | % machine already analysed | |
161 | machine_type(Name,_),!. | |
162 | analyse_machine(Name,Machines,_) :- | |
163 | debug:debug_println(19,analysing_machine(Name)), | |
164 | get_machine(Name,Machines,Type,Header,Refines,Body), | |
165 | !, | |
166 | ( cache_is_activated -> % we need the machines stored for later analysis | |
167 | assert_all_machines(Machines) | |
168 | ; true), | |
169 | assertz(machine_type(Name,Type)), | |
170 | (get_machine_parent_directory(Name,Dir) -> assertz(machine_package_directory(Name,Dir)) ; true), | |
171 | ( get_raw_section(assertions,Body,_) -> | |
172 | assertz(machine_has_assertions(Name)) | |
173 | ; true), | |
174 | store_identifiers(Name,Header,Body), | |
175 | store_operations(Name,Body), | |
176 | store_values(Name,Body), | |
177 | store_references(Name,Refines,Body,Machines). | |
178 | analyse_machine(Name,Machines,RefType) :- | |
179 | get_machine_file_number(Name,_Ext,Nr,File), | |
180 | get_ref_type_name(RefType,Clause), | |
181 | !, | |
182 | ( member(M,Machines),get_constructed_machine_name_and_filenumber(M,OtherName,Nr) | |
183 | -> ajoin(['Cannot use B machine "',Name,'" within ', Clause, | |
184 | ' clause. Rename machine "', OtherName,'" to "', Name, '" in file: '],Msg) | |
185 | ; findall(MN,b_filenumber(MN,_,_,_),List), | |
186 | ajoin(['Cannot find B machine "',Name,'" within ', Clause, | |
187 | ' clause (available: ',List,'). Check that machine name matches filename in: '],Msg) | |
188 | ), | |
189 | add_error_fail(invalid_machine_reference,Msg,File). | |
190 | analyse_machine(Name,_Machines,_RefType) :- | |
191 | bmachine:portray_filenumbers, | |
192 | add_error_fail(invalid_machine_reference, | |
193 | 'Could not find machine in parsed machine list (check that your machine names match your filenames): ',Name). | |
194 | ||
195 | :- use_module(tools,[get_parent_directory_name/2]). | |
196 | get_machine_parent_directory(Name,DirName) :- | |
197 | % try and get parent directory name; useful to distinguish different packages when using package pragma | |
198 | get_machine_file_number(Name,_Ext,_Nr,File), | |
199 | get_parent_directory_name(File,DirName). | |
200 | ||
201 | ||
202 | % store the un-typed input syntax tree for later analysis | |
203 | assert_all_machines(Machines) :- | |
204 | (raw_machine(_,_) | |
205 | -> true % machines already asserted | |
206 | ; maplist(assert_raw_machine,Machines)). | |
207 | assert_raw_machine(Machine) :- | |
208 | get_raw_machine_name(Machine,Name), | |
209 | (raw_machine(Name,_) -> add_warning(b_machine_hierarchy,'Raw machine already exists: ',Name) ; true), | |
210 | assertz( raw_machine(Name,Machine) ). | |
211 | ||
212 | machine_has_constants(MachName) :- | |
213 | machine_identifiers(MachName,_,_,_,_,AConsts,CConsts), | |
214 | (AConsts=[] -> CConsts = [_|_] ; true). | |
215 | ||
216 | get_machine_short_desc(MachName,Text) :- | |
217 | machine_identifiers(MachName,Params,Sets,AVars,CVars,AConsts,CConsts), | |
218 | length(Params,PL), length(Sets,SL), length(AVars,AVL), length(CVars,CVL), VL is AVL+CVL, | |
219 | length(AConsts,ACL), length(CConsts,CCL), CL is ACL+CCL, | |
220 | (machine_operations(MachName,Ops), length(Ops,OL) -> true ; OL=0), | |
221 | ajoin(['SETS:',SL,' CONSTANTS:',CL,' VARIABLES:',VL, ' PARAMETERS:',PL, ' OPERATIONS:',OL],Text). | |
222 | ||
223 | store_identifiers(Name,Header,Body) :- | |
224 | get_parameters(Header,Params), | |
225 | get_sets(Body,Sets), | |
226 | get_identifiers([abstract_variables,variables],Body,AVars), | |
227 | get_identifiers([concrete_variables],Body,CVars), | |
228 | get_identifiers([abstract_constants],Body,AConsts), | |
229 | get_identifiers([concrete_constants,constants],Body,CConsts), % fixed abstract -> concrete | |
230 | assertz(machine_identifiers(Name,Params,Sets,AVars,CVars,AConsts,CConsts)), | |
231 | maplist(assert_raw_id_with_position(abstract_constant),AConsts), | |
232 | maplist(assert_raw_id_with_position(concrete_constant),CConsts). | |
233 | ||
234 | ||
235 | raw_id_is_identifier2(description(_Pos,_Desc,Raw),ID) :- !, raw_id_is_identifier2(Raw,ID). | |
236 | raw_id_is_identifier2(deferred_set(_,ID),ID) :- !. | |
237 | raw_id_is_identifier2(enumerated_set(_,ID,_Elements),ID) :- !. | |
238 | raw_id_is_identifier2(Raw,ID) :- raw_id_is_identifier(Raw,_,ID). | |
239 | ||
240 | get_machine_identifier_names(Name,Params,Sets,AVars,CVars,AConsts,CConsts) :- | |
241 | machine_identifiers(Name,RawParams,RawSets,RawAVars,RawCVars,RawAConsts,RawCConsts), | |
242 | maplist(raw_id_is_identifier2,RawParams,Params), | |
243 | maplist(raw_id_is_identifier2,RawSets,Sets), | |
244 | maplist(raw_id_is_identifier2,RawAVars,AVars), | |
245 | maplist(raw_id_is_identifier2,RawCVars,CVars), | |
246 | maplist(raw_id_is_identifier2,RawAConsts,AConsts), | |
247 | maplist(raw_id_is_identifier2,RawCConsts,CConsts). | |
248 | ||
249 | ||
250 | machine_hash(Name,Hash) :- | |
251 | machine_hash_cached(Name,Hash1),!,Hash=Hash1. | |
252 | machine_hash(Name,Hash) :- | |
253 | compute_machine_hash(Name,Hash1), | |
254 | assertz( machine_hash_cached(Name,Hash1) ), | |
255 | Hash=Hash1. | |
256 | compute_machine_hash(Name,Digest) :- | |
257 | if(raw_machine(Name,Machine), | |
258 | raw_sha_hash(Machine,Digest), | |
259 | add_error_and_fail(compute_machine_hash,'Machine does not exist or has not been processed:',Name) | |
260 | ). | |
261 | ||
262 | :- use_module(pathes_extensions_db, [compile_time_unavailable_extension/2]). | |
263 | :- if(\+ compile_time_unavailable_extension(probhash_extension, _)). | |
264 | store_eventb_hash(Name,ContextMachTerm) :- | |
265 | raw_sha_hash(ContextMachTerm,Digest), | |
266 | assertz(machine_hash_cached(Name,Digest)). | |
267 | :- else. | |
268 | store_eventb_hash(Name,_) :- | |
269 | assertz( (machine_hash_cached(Name,_) :- | |
270 | add_error(b_machine_hierarchy,'prob_hash_extension not available for: ',Name),fail) ). | |
271 | :- endif. | |
272 | ||
273 | :- use_module(tools_strings,[get_hex_bytes/2]). | |
274 | operation_hash(MachName,OpName,Hash) :- | |
275 | operation_hash_cached(MachName,OpName,Hash1),!,Hash=Hash1. | |
276 | operation_hash(MachName,OpName,Hash) :- | |
277 | computed_basic_operation_hashes, | |
278 | basic_operation_hash(OpName,MachName,Hash1,OpCalls), | |
279 | (OpCalls = [] | |
280 | -> FinalHash=Hash1 % no recursive call of other operations | |
281 | ; maplist(get_basic_op_hash,OpCalls,OpDigests), | |
282 | raw_sha_hash(op(Hash1,OpDigests),FinalHash) % also include hash of called operations | |
283 | ), | |
284 | assertz( operation_hash_cached(MachName,OpName,FinalHash) ), | |
285 | get_hex_bytes(FinalHash,Hex), | |
286 | debug_format(19,'value caching: operation ~w in machine ~w has hash: ~s~n',[OpName,MachName,Hex]), | |
287 | Hash=FinalHash. | |
288 | ||
289 | get_basic_op_hash(OpName,Digest) :- | |
290 | basic_operation_hash(OpName,_MachName,Digest,_OpCalls). | |
291 | ||
292 | % compute basic hash, without taking recursive operation calls into account | |
293 | computed_basic_operation_hashes :- | |
294 | basic_operation_hash(_,_,_,_),!. % already computed | |
295 | computed_basic_operation_hashes :- | |
296 | get_raw_machine_operation_hash(MachName,OpName,Digest,OpCalls), | |
297 | (basic_operation_hash(OpName,_,_,_) | |
298 | -> add_warning(computed_basic_operation_hashes,'Multiple hashes for operation: ',OpName) | |
299 | ; true), | |
300 | assertz(basic_operation_hash(OpName,MachName,Digest,OpCalls)), | |
301 | get_hex_bytes(Digest,Hex), | |
302 | debug_format(19,'value caching: basic operation hash ~w in machine ~w: ~s (calls ~w)~n', | |
303 | [OpName,MachName,Hex,OpCalls]), | |
304 | fail. | |
305 | computed_basic_operation_hashes. | |
306 | ||
307 | % first computed basic, independent operation hashes: | |
308 | % only look up used definitions, but not yet called operations | |
309 | get_raw_machine_operation_hash(MachName,OpName,Digest,SOpCalls) :- | |
310 | raw_machine(MachName,Machine), | |
311 | get_machine(MachName,[Machine],_Type,_Header,_Refines,MachBody), | |
312 | get_opt_section(operations,MachBody,Operations), | |
313 | Op = operation(_,identifier(_,OpName),_,_,_), | |
314 | member(Op,Operations), | |
315 | get_raw_operation_id_and_body(Op,identifier(_,OpName),OpBody), | |
316 | extract_used_np_definitions(Op,MachBody,UsedDefinitions,DefsWithPos), | |
317 | remove_raw_position_info(Op,RawOperation), | |
318 | raw_sha_hash(op(RawOperation,UsedDefinitions),Digest), | |
319 | findall(Id, (get_raw_operation_call_id(OpBody,Id) % extract op calls in body of operation | |
320 | ; get_def_body(Def,DefsWithPos), get_raw_operation_call_id(Def,Id) % extract op calls in definitions | |
321 | ), | |
322 | OpCalls), | |
323 | sort(OpCalls,SOpCalls). | |
324 | ||
325 | get_def_body(Body,Defs) :- member(definition(_,_DefName,_,Body),Defs). | |
326 | ||
327 | ||
328 | :- use_module(debug,[debug_println/2]). | |
329 | assert_raw_id_with_position(PredFunctor,Rid) :- | |
330 | (raw_id_is_identifier(Rid,Pos,ID) | |
331 | -> true | |
332 | ; peel_desc(Rid,PRid), PRid = definition(Pos,ID,_) | |
333 | -> add_error(illegal_definition_use,'Definition cannot be used as identifier here: ',ID,Pos) | |
334 | ; add_error_fail(assert_raw_id_with_position,'Not identifier: ',Rid) | |
335 | ), | |
336 | Fact =.. [PredFunctor,ID,Pos], | |
337 | assertz(Fact), debug_println(9,Fact). | |
338 | ||
339 | raw_id_is_identifier(identifier(Pos,ID),Pos,ID). | |
340 | raw_id_is_identifier(unit(_,_,identifier(Pos,ID)),Pos,ID). | |
341 | raw_id_is_identifier(new_unit(_,_,identifier(Pos,ID)),Pos,ID). | |
342 | raw_id_is_identifier(inferred_unit(_,_,identifier(Pos,ID)),Pos,ID). | |
343 | raw_id_is_identifier(inferredunit(_,_,identifier(Pos,ID)),Pos,ID). % the (new?) parser seems to generate the wrong pragma in the .prob file; TO DO: investigate | |
344 | raw_id_is_identifier(description(_,_,RawID),Pos,ID) :- | |
345 | raw_id_is_identifier(RawID,Pos,ID). | |
346 | ||
347 | peel_desc(description(_,_,E),R) :- !, peel_desc(E,R). | |
348 | peel_desc(R,R). | |
349 | ||
350 | get_raw_identifier(Raw,Res) :- raw_id_is_identifier(Raw,_Pos,Id),!, Res=Id. | |
351 | get_raw_identifier(definition(_Pos,DID,[]),ID) :- % see also expand_definition_to_variable_list | |
352 | atom(DID),!, | |
353 | ajoin([DID,'(DEFINITION)'],ID). | |
354 | get_raw_identifier(deferred_set(_Pos,DID),ID) :- atom(DID),!, ID=DID. | |
355 | get_raw_identifier(enumerated_set(_Pos,DID,_List),ID) :- atom(DID),!, ID=DID. | |
356 | get_raw_identifier(description(_,_,RawID),ID) :- !, | |
357 | get_raw_identifier(RawID,ID). | |
358 | get_raw_identifier(Raw,Res) :- add_internal_error('Cannot get identifier:',Raw),Res='???'. | |
359 | ||
360 | raw_identifier_member(ID,List) :- member(Raw,List), raw_id_is_identifier(Raw,_Pos,ID). | |
361 | ||
362 | store_operations(MachName,Body) :- | |
363 | get_opt_section(operations,Body,Operations), | |
364 | findall(I,(member(Op,Operations),get_raw_operation_id_and_body(Op,I,_)),Ids), | |
365 | assertz(machine_operations(MachName,Ids)), | |
366 | findall(calls(I1,I2),(member(Op,Operations),get_raw_operation_call(Op,I1,I2)),Calls), | |
367 | sort(Calls,SCalls), | |
368 | debug_println(9,machine_operation_calls(MachName,SCalls)), | |
369 | assertz(machine_operation_calls(MachName,SCalls)). | |
370 | ||
371 | get_raw_operation_id_and_body(operation(_Pos,Id,_,_,Body),Id,Body). | |
372 | get_raw_operation_id_and_body(refined_operation(_Pos,Id,_Results,_Args,_RefinesID,Body),Id,Body). | |
373 | get_raw_operation_id_and_body(description_operation(_Pos,_,Op),Id,Body) :- get_raw_operation_id_and_body(Op,Id,Body). | |
374 | ||
375 | % get operations called in body | |
376 | get_raw_operation_call(Op,Id,CallsId) :- | |
377 | get_raw_operation_id_and_body(Op,identifier(_,Id),Body), | |
378 | get_raw_operation_call(Body,identifier(_,CallsId)). | |
379 | ||
380 | :- use_module(debug,[debug_format/3]). | |
381 | get_raw_operation_call(block(_,Body),ID) :- !, get_raw_operation_call(Body,ID). | |
382 | get_raw_operation_call(precondition(_,_,Body),ID) :- !, get_raw_operation_call(Body,ID). | |
383 | get_raw_operation_call(assertion(_,_,Body),ID) :- !, get_raw_operation_call(Body,ID). | |
384 | get_raw_operation_call(witness_then(_,_,Body),ID) :- !, get_raw_operation_call(Body,ID). | |
385 | get_raw_operation_call(var(_,_,Body),ID) :- !, get_raw_operation_call(Body,ID). | |
386 | get_raw_operation_call(select_when(_,_,Body),ID) :- !, get_raw_operation_call(Body,ID). | |
387 | get_raw_operation_call(if_elsif(_,_,Body),ID) :- !, get_raw_operation_call(Body,ID). | |
388 | get_raw_operation_call(let(_,_,_,Body),ID) :- !, get_raw_operation_call(Body,ID). | |
389 | get_raw_operation_call(any(_,_,_,Body),ID) :- !, get_raw_operation_call(Body,ID). | |
390 | get_raw_operation_call(case(_,_,_,_,_,Body),ID) :- !, get_raw_operation_call(Body,ID). | |
391 | get_raw_operation_call(while(_,_Cond,Body,_,_),ID) :- !, get_raw_operation_call(Body,ID). | |
392 | get_raw_operation_call(parallel(_,List),ID) :- !,member(A,List), get_raw_operation_call(A,ID). | |
393 | get_raw_operation_call(sequence(_,List),ID) :- !,member(A,List), get_raw_operation_call(A,ID). | |
394 | get_raw_operation_call(if(_,_Test,Then,List,Else),ID) :- !,member(A,[Then,Else|List]), get_raw_operation_call(A,ID). | |
395 | get_raw_operation_call(select(_,_Cond,Body,List),ID) :- !,member(A,[Body|List]), get_raw_operation_call(A,ID). | |
396 | get_raw_operation_call(select(_,_Cond,Body,List,Else),ID) :- !,member(A,[Body,Else|List]), get_raw_operation_call(A,ID). | |
397 | get_raw_operation_call(choice(_,List),ID) :- !,member(A,List), get_raw_operation_call(A,ID). | |
398 | get_raw_operation_call(choice_or(_,Body),ID) :- !, get_raw_operation_call(Body,ID). | |
399 | get_raw_operation_call(operation_call(_,ID,_,_),ID) :- !. | |
400 | get_raw_operation_call(skip(_),_) :- !, fail. | |
401 | get_raw_operation_call(assign(_,_,_),_) :- !, fail. | |
402 | get_raw_operation_call(becomes_element_of(_,_,_),_) :- !, fail. | |
403 | get_raw_operation_call(becomes_such(_,_,_),_) :- !, fail. | |
404 | get_raw_operation_call(definition(_,Name,_),_) :- !, % TO DO: improve | |
405 | debug_format(19,'Ignoring operation calls in DEFINITION ~w for operation call diagram~n',[Name]), | |
406 | % b_get_definition(Name,_DefType,_Args,DefBody,_Deps), not yet precompiled ! | |
407 | fail. | |
408 | get_raw_operation_call(Subst,_) :- functor(Subst,F,N),print(uncovered_subst(F,N,Subst)),nl,fail. | |
409 | % we also do not find operation calls in expressions | |
410 | ||
411 | :- use_module(input_syntax_tree,[raw_operator_term/1, raw_literal_term/1, raw_special_set_term/1]). | |
412 | % avoid spurious uncovered_subst messages in a context where we are not sure we have a subst | |
413 | try_get_raw_operation_call(Term,_) :- raw_literal_term(Term),!, fail. | |
414 | try_get_raw_operation_call(Term,_) :- raw_operator_term(Term),!, fail. | |
415 | try_get_raw_operation_call(Term,_) :- raw_special_set_term(Term),!, fail. | |
416 | try_get_raw_operation_call(F,_) :- functor(F,external_function_call,_),!, fail. % definitely expression | |
417 | try_get_raw_operation_call(F,_) :- functor(F,external_pred_call,_),!, fail. % definitely predicate | |
418 | try_get_raw_operation_call(Body,ID) :- get_raw_operation_call(Body,ID). | |
419 | ||
420 | get_raw_operation_call_id(OpBody,CalledId) :- | |
421 | try_get_raw_operation_call(OpBody,identifier(_,CalledId)). | |
422 | ||
423 | store_references(Name,Refines,Body,Machines) :- | |
424 | get_refinements(Refines,Refs1), | |
425 | get_references(Body,Refs2), | |
426 | append(Refs1,Refs2,Refs), | |
427 | assertz(machine_references(Name,Refs)), | |
428 | follow_refs(Refs,Machines). | |
429 | ||
430 | get_references(Body,Refs) :- | |
431 | findrefs(Body,includes,Includes), | |
432 | findrefs(Body,extends,Extends), | |
433 | findrefs(Body,imports,Imports), | |
434 | findusessees(Body,uses,Uses), | |
435 | findusessees(Body,sees,Sees), | |
436 | append([Includes,Imports,Extends,Uses,Sees],Refs). | |
437 | ||
438 | get_refinements([],[]). | |
439 | get_refinements([Name|NRest],[ref(refines,Name,'')|RRest]) :- | |
440 | get_refinements(NRest,RRest). | |
441 | ||
442 | findrefs(Body,Type,Refs) :- | |
443 | get_opt_section(Type,Body,RawRefs), | |
444 | findrefs2(RawRefs,Type,Refs). | |
445 | findrefs2([],_Type,[]). | |
446 | findrefs2([MID|MRest],Type,[ref(Type,Name,Prefix)|RRest]) :- | |
447 | get_machine_id(MID,FullName), % should be machine_reference | |
448 | bmachine_construction:split_prefix(FullName,Prefix,Name), | |
449 | findrefs2(MRest,Type,RRest). | |
450 | ||
451 | findusessees(Body,Type,Refs) :- | |
452 | get_opt_section(Type,Body,RawRefs), | |
453 | findusessees2(RawRefs,Type,Refs). | |
454 | findusessees2([],_Type,[]). | |
455 | findusessees2([MID|MRest],Type,[ref(Type,Name,Prefix)|RRest]) :- | |
456 | get_machine_id(MID,FullName), % should be machine_reference | |
457 | % USE and SEES do not have machine_reference in AST but identifier | |
458 | bmachine_construction:split_prefix(FullName,Prefix,Name), | |
459 | findusessees2(MRest,Type,RRest). | |
460 | ||
461 | get_machine_id(identifier(_Pos,FN),FullName) :- !, FullName=FN. | |
462 | get_machine_id(machine_reference(_Pos,FN,_Params),FullName) :- !, FullName=FN. | |
463 | get_machine_id(Other,_) :- add_internal_error('Illegal machine id: ',get_machine_id(Other,_)),fail. | |
464 | ||
465 | :- use_module(probsrc(tools),[split_atom/3]). | |
466 | follow_refs([],_Machines). | |
467 | follow_refs([ref(RefType,Name,_Prefix)|Rest],Machines) :- | |
468 | analyse_machine(Name,Machines,RefType), | |
469 | follow_refs(Rest,Machines). | |
470 | ||
471 | get_parameters(machine_header(_Pos,_Name,Params),Params). | |
472 | ||
473 | get_sets(Body,Sets) :- | |
474 | get_opt_section(sets,Body,Sets). | |
475 | ||
476 | get_identifiers([],_Body,[]). | |
477 | get_identifiers([Sec|Rest],Body,Ids) :- | |
478 | get_opt_section(Sec,Body,Ids1), | |
479 | append(Ids1,IRest,Ids), | |
480 | get_identifiers(Rest,Body,IRest). | |
481 | ||
482 | get_opt_sections([],_Body,[]). | |
483 | get_opt_sections([S|Srest],Body,Contents) :- | |
484 | get_opt_section(S,Body,L),append(L,Rest,Contents), | |
485 | get_opt_sections(Srest,Body,Rest). | |
486 | ||
487 | get_opt_section(Sec,Body,Result) :- | |
488 | ( get_raw_section(Sec,Body,Content) -> Result=Content; Result=[]). | |
489 | get_raw_section(Sec,Body,Content) :- % look for Sec(_Pos,Content) in Body list | |
490 | functor(Pattern,Sec,2),arg(2,Pattern,Content), | |
491 | memberchk(Pattern,Body). | |
492 | ||
493 | get_machine(Name,Machines,Type,Header,Refines,Body) :- | |
494 | get_machine1(Name,Machines,_Machine,Type,Header,Refines,Body). | |
495 | %get_raw_machine(Name,Machines,Machine) :- | |
496 | % get_machine1(Name,Machines,Machine,_Type,_Header,_Refines,_Body). | |
497 | get_machine1(Name,Machines,Machine,Type,Header,Refines,Body) :- | |
498 | Header = machine_header(_Pos,Name,_Params), | |
499 | member(Machine,Machines), | |
500 | get_machine2(Machine,Type,Header,Refines,Body),!. | |
501 | get_machine2(abstract_machine(_Pos,MS,Header,Body),TypeOfAbstractMachine,Header,[],Body) :- | |
502 | get_abstract_machine_type(MS,TypeOfAbstractMachine). | |
503 | get_machine2(refinement_machine(_Pos,Header,Refines,Body),refinement,Header,[Refines],Body). | |
504 | get_machine2(implementation_machine(_Pos,Header,Refines,Body),implementation,Header,[Refines],Body). | |
505 | get_machine2(generated(_Pos,Machine),A,B,C,D) :- % @generated Pragma used at top of file | |
506 | get_machine2(Machine,A,B,C,D). | |
507 | get_machine2(unit_alias(_Pos,_Name,_Alias,Machine),A,B,C,D) :- | |
508 | get_machine2(Machine,A,B,C,D). | |
509 | ||
510 | get_raw_machine_name(Machine,Name) :- | |
511 | get_machine2(Machine,_,machine_header(_,Name,_),_,_). | |
512 | ||
513 | get_abstract_machine_type(machine(_Pos2),R) :- !,R=abstract_machine. | |
514 | get_abstract_machine_type(system(_Pos2),R) :- !,R=abstract_machine. | |
515 | get_abstract_machine_type(model(_Pos2),R) :- !,R=abstract_model. | |
516 | get_abstract_machine_type(X,R) :- atomic(X),!, | |
517 | add_error(get_abstract_machine_type,'Your parser seems out-dated. Assuming abstract_machine: ',X), | |
518 | R=abstract_machine. | |
519 | ||
520 | ||
521 | get_values_id(values_entry(Pos,ID,_Val),identifier(Pos,ID)). | |
522 | % Store the identifiers assigned to in VALUES clauses | |
523 | store_values(Name,Body) :- | |
524 | get_opt_section('values',Body,Values),!, | |
525 | maplist(get_values_id,Values,ValuesIDs), | |
526 | assertz(machine_values_identifiers(Name,ValuesIDs)). | |
527 | store_values(_,_). | |
528 | ||
529 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
530 | ||
531 | % write dot operation call graph | |
532 | write_dot_op_hierarchy_to_file(File) :- | |
533 | (get_preference(dot_event_hierarchy_horizontal,true) -> PageOpts=[rankdir/'LR'] ; PageOpts=[]), | |
534 | gen_dot_graph(File,PageOpts,dot_operation_node,dot_op_calls_op,dot_no_same_rank,dot_subgraph(op_hierarchy)). | |
535 | ||
536 | ||
537 | :- use_module(bmachine,[b_top_level_operation/1, b_top_level_feasible_operation/1]). | |
538 | dot_operation_node(OpName,M,Desc,Shape,Style,Color) :- | |
539 | machine_operations(M,Operations), | |
540 | (machine_promotes_operations(M,Promotes) -> true ; Promotes=[]), | |
541 | raw_identifier_member(OpName,Operations), | |
542 | (b_top_level_feasible_operation(OpName) -> Color=lightgray | |
543 | ; b_top_level_operation(OpName) -> Color='OldLace' % commented out operation | |
544 | ; raw_identifier_member(OpName,Promotes) -> Color='gray98' % promoted but not to top_level | |
545 | ; Color=white), | |
546 | Desc=OpName, Shape=box, Style=filled. | |
547 | ||
548 | dot_op_calls_op(Op1,Label,Op2,Color,Style) :- Style=solid, Color=steelblue, | |
549 | Label = '', % TO DO: different colors for local operation calls, detect op calls in expressions? | |
550 | machine_operation_calls(_,Operations), | |
551 | member(calls(Op1,Op2),Operations). | |
552 | ||
553 | ||
554 | ||
555 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
556 | % print hierarchy to dot file | |
557 | ||
558 | maxlinelength(30). | |
559 | ||
560 | :- use_module(tools_io). | |
561 | ||
562 | % write hierarchy of machines, with inclusion/refinement links | |
563 | write_dot_hierarchy_to_file(Filename) :- | |
564 | findall(M,machine_type(M,_),Machines), | |
565 | safe_open_file(Filename,write,S,[]), | |
566 | ( print_header(S), | |
567 | print_machines(S,1,Machines,Ids), | |
568 | print_refs_for_dot(S,Ids,Ids), | |
569 | print_footer(S) | |
570 | -> true | |
571 | ; add_internal_error('Command failed:',write_dot_hierarchy_to_file(Filename)) | |
572 | ), | |
573 | close(S). | |
574 | ||
575 | print_header(S) :- | |
576 | write(S,'digraph module_hierarchy {\n'), | |
577 | write(S,' graph [page="8.5, 11",ratio=fill,size="7.5,10"];\n'). | |
578 | print_footer(S) :- | |
579 | write(S,'}\n'). | |
580 | ||
581 | print_machines(_S,_Nr,[],[]). | |
582 | print_machines(S,Nr,[M|Machines],[id(M,Nr)|Ids]) :- | |
583 | (print_machine(S,Nr,M) -> true | |
584 | ; add_internal_error('Printing machine for hierarchy failed: ',print_machine(S,Nr,M)) | |
585 | ), | |
586 | Nr2 is Nr + 1, | |
587 | print_machines(S,Nr2,Machines,Ids). | |
588 | print_machine(S,Nr,M) :- | |
589 | write(S,' '),write(S,Nr), | |
590 | (main_machine_name(M) -> | |
591 | write(S,' [shape=record, style=bold, color=darkgreen, label=\"|{') | |
592 | ; write(S,' [shape=record, color=steelblue, label=\"|{')), | |
593 | print_machine_header(S,M), | |
594 | machine_identifiers(M,_Params,Sets,AVars,CVars,AConsts,CConsts), | |
595 | %print_hash(S,M), | |
596 | print_sets(S,Sets), | |
597 | print_identifiers(S,'VARIABLES',AVars), | |
598 | print_identifiers(S,'CONCRETE VARIABLES',CVars), | |
599 | print_identifiers(S,'ABSTRACT CONSTANTS',AConsts), | |
600 | print_identifiers(S,'CONSTANTS',CConsts), | |
601 | (machine_values_identifiers(M,VConsts) | |
602 | -> print_identifiers(S,'VALUES',VConsts) | |
603 | ; true), | |
604 | ( machine_has_assertions(M) -> | |
605 | get_specification_description(assertions,AssStr), | |
606 | print_title(S,AssStr) | |
607 | ; true | |
608 | ), | |
609 | (machine_promotes_operations(M,Promotes) | |
610 | -> print_identifiers(S,'PROMOTES',Promotes) ; true), | |
611 | machine_operations(M,Operations), | |
612 | delete(Operations,identifier(_,'INITIALISATION'),Operations2), | |
613 | exclude(is_refinining_event(M),Operations2,RefiningOperations), | |
614 | include(is_refinining_event(M),Operations2,NewOperations), | |
615 | ((RefiningOperations=[] ; NewOperations=[]) | |
616 | -> get_specification_description(operations,OpStr), | |
617 | print_identifiers(S,OpStr,Operations2) | |
618 | ; print_identifiers(S,'EVENTS (refining)',RefiningOperations), | |
619 | print_identifiers(S,'EVENTS (new)',NewOperations) | |
620 | ), | |
621 | ||
622 | write(S,'}|\"];\n'). | |
623 | ||
624 | is_refinining_event(M,identifier(_,Event)) :- (refines_event(M,Event,_,_) -> true). | |
625 | ||
626 | %print_hash(S,M) :- machine_hash(M,Digest),!,print_title(S,'Digest'), | |
627 | % maplist(format(S,'~16r'),Digest),write(S,'\\n'). | |
628 | %print_hash(_S,_M). | |
629 | ||
630 | %get_machine_colour(M,steelblue) :- main_machine_name(M),!. | |
631 | %get_machine_colour(_M,steelblue). | |
632 | ||
633 | print_machine_header(S,M) :- | |
634 | machine_type(M,Type), | |
635 | get_machine_type_keyw(Type,P), | |
636 | write(S,P),write(S,' '), | |
637 | write(S,M), | |
638 | (machine_package_directory(M,Dir), machine_package_directory(_M2,D2), D2 \= Dir | |
639 | -> format(S,' (~w)',[Dir]) % show the name of the directory; probably the package pragma was used | |
640 | ; true), | |
641 | write(S,'\\n'). | |
642 | get_machine_type_keyw(abstract_machine,'MACHINE'). | |
643 | get_machine_type_keyw(abstract_model,'MODEL'). | |
644 | get_machine_type_keyw(refinement,'REFINEMENT'). | |
645 | get_machine_type_keyw(implementation,'IMPLEMENTATION'). | |
646 | get_machine_type_keyw(context,'CONTEXT'). | |
647 | ||
648 | print_sets(_,[]) :- !. | |
649 | print_sets(S,Sets) :- | |
650 | write(S,'|SETS\\n'), | |
651 | print_sets2(Sets,S). | |
652 | print_sets2([],_). | |
653 | print_sets2([Set|Rest],S) :- | |
654 | print_set(Set,S), | |
655 | write(S,'\\n'), | |
656 | print_sets2(Rest,S). | |
657 | print_set(description(_Pos,_Desc,Raw),S) :- print_set(Raw,S). | |
658 | print_set(deferred_set(_Pos,Name),S) :- | |
659 | write_id(S,Name). | |
660 | print_set(enumerated_set(_Pos,Name,List),S) :- | |
661 | write_id(S,Name),write(S,' = \\{'), | |
662 | maxlinelength(Max), | |
663 | preferences:get_preference(dot_hierarchy_max_ids,MaxIdsToPrint), | |
664 | print_set_elements(List,S,Max,MaxIdsToPrint), | |
665 | write(S,'\\}'). | |
666 | print_set_elements([],_,_,_). | |
667 | print_set_elements([RawID],S,_LenSoFar,_) :- | |
668 | raw_id_is_identifier(RawID,_,Name), | |
669 | !,write_id(S,Name). | |
670 | print_set_elements([RawID,B|Rest],S,LenSoFar,MaxIdsToPrint) :- | |
671 | raw_id_is_identifier(RawID,_,Name), | |
672 | dec_atom_length(LenSoFar,Name,NewLen), | |
673 | (NewLen<0 -> maxlinelength(NL0),NL is NL0-1,write(S,'\\n ') ; NL is NewLen-1), | |
674 | write_id(S,Name),write(S,','), | |
675 | M1 is MaxIdsToPrint-1, | |
676 | (M1 < 1, Rest \= [] | |
677 | -> write(S,'...,'), | |
678 | last([B|Rest],Last), print_set_elements([Last],S,NL,M1) | |
679 | ; print_set_elements([B|Rest],S,NL,M1) | |
680 | ). | |
681 | ||
682 | :- use_module(tools,[string_escape/2]). | |
683 | % write identifier and escape it for dot | |
684 | write_id(S,Name) :- string_escape(Name,EName), write(S,EName). | |
685 | ||
686 | dec_atom_length(Prev,Atom,New) :- atom_length(Atom,Len), | |
687 | New is Prev-Len. | |
688 | ||
689 | print_title(S,Title) :- | |
690 | write(S,'|'), | |
691 | write(S,Title),write(S,'\\n'). | |
692 | print_identifiers(_S,_,[]) :- !. | |
693 | print_identifiers(S,Title,List) :- | |
694 | print_title(S,Title), | |
695 | preferences:get_preference(dot_hierarchy_max_ids,MaxIdsToPrint), % how many do we print overall; -1 means print all of them | |
696 | print_identifiers2(List,0,_,S,MaxIdsToPrint). | |
697 | print_identifiers2([],Count,Count,_,_MaxIdsToPrint). | |
698 | print_identifiers2([RawID|Rest],Count,NCount,S,MaxIdsToPrint) :- | |
699 | get_raw_identifier(RawID,Name), | |
700 | (MaxIdsToPrint = 0,Rest\=[] | |
701 | -> length(Rest,Len), RN is Len+1, | |
702 | format(S,' (~w more)',[RN]) | |
703 | ; MaxIdsToPrint1 is MaxIdsToPrint-1, | |
704 | print_identifier(Name,Count,ICount,S), | |
705 | (Rest = [] -> true; write(S,',')), | |
706 | print_identifiers2(Rest,ICount,NCount,S,MaxIdsToPrint1) | |
707 | ). | |
708 | print_identifier(Name,Count,NewCount,S) :- | |
709 | atom_codes(Name,Codes), | |
710 | length(Codes,Length), | |
711 | NewCount1 is Count+Length, | |
712 | maxlinelength(Max), | |
713 | ( Count == 0 -> NewCount1=NewCount | |
714 | ; NewCount1 =< Max -> NewCount1=NewCount | |
715 | ; NewCount=0,write(S,'\\n')), | |
716 | write_id(S,Name). | |
717 | ||
718 | print_refs_for_dot(_S,[],_). | |
719 | print_refs_for_dot(S,[id(M,Nr)|Rest],Ids) :- | |
720 | machine_references(M,Refs), | |
721 | filter_redundant_refs(Refs,Refs,UsefulRefs), | |
722 | print_refs2(UsefulRefs,M,Nr,Ids,S), | |
723 | print_refs_for_dot(S,Rest,Ids). | |
724 | print_refs2([],_,_,_,_). | |
725 | print_refs2([ref(Type,Dest,_Prefix)|Rest],M,Nr,Ids,S) :- | |
726 | member(id(Dest,DestNr),Ids),!, | |
727 | get_ref_type(Type,Ref,Dir), !, | |
728 | (Dir=reverse -> | |
729 | format(S,' ~w -> ~w ~w;~n',[DestNr,Nr,Ref]) | |
730 | ; format(S,' ~w -> ~w ~w;~n',[Nr,DestNr,Ref]) | |
731 | ), | |
732 | print_refs2(Rest,M,Nr,Ids,S). | |
733 | get_ref_type(includes,'[label=\"INCLUDES\",color=navyblue]',normal). | |
734 | get_ref_type(imports,'[label=\"IMPORTS\",color=navyblue]',normal). | |
735 | get_ref_type(extends,'[label=\"EXTENDS\",color=navyblue]',normal). | |
736 | get_ref_type(uses,'[label=\"USES\",color=navyblue,style=dashed]',normal). | |
737 | get_ref_type(sees,'[label=\"SEES\",color=navyblue,style=dashed]',normal). | |
738 | %get_ref_type(sees,'[label=\"SEES\",color=navyblue,style=dashed,dir=back]',reverse). | |
739 | %get_ref_type(refines,'[label=\"REFINES\",color=navyblue,style=bold,dir=back]',reverse). | |
740 | get_ref_type(refines,'[label=\"REFINEMENT\",color=navyblue,style=bold]',reverse). % reverse so that abstract machines are shown on top | |
741 | get_ref_type(UNKNOWN,'[label=\"UNKNOWN\",color=navyblue]',normal) :- add_internal_error('Unknown : ',get_ref_type(UNKNOWN,_,_)). | |
742 | ||
743 | get_ref_type_name(includes,'INCLUDES'). | |
744 | get_ref_type_name(imports,'IMPORTS'). | |
745 | get_ref_type_name(extends,'EXTENDS'). | |
746 | get_ref_type_name(uses,'USES'). | |
747 | get_ref_type_name(sees,'SEES'). | |
748 | get_ref_type_name(refines,'REFINEMENT'). | |
749 | get_ref_type_name(main,'MACHINE'). | |
750 | get_ref_type_name(UNKNOWN,UNKNOWN). | |
751 | ||
752 | ||
753 | % remove redundant references (has to be done after analyse_eventb_machine has asserted all facts) | |
754 | filter_redundant_refs([],_,[]). | |
755 | filter_redundant_refs([ref(sees,Dest,_)|T],All,R) :- | |
756 | machine_type(Dest,context), | |
757 | member(ref(sees,Other,_),All), | |
758 | machine_references(Other,Refs), | |
759 | member(ref(extends,Dest,_),Refs), % Dest already seen by other seen context; | |
760 | % Note: Rodin export contains transitive sees relation; | |
761 | % if the user had included Dest in the sees clause we would have a warning "Redundant seen context" | |
762 | !, | |
763 | %format(user_output,'Redundant sees of ~w (~w)~n',[Dest,Other]), | |
764 | filter_redundant_refs(T,All,R). | |
765 | filter_redundant_refs([H|T],All,[H|R]) :- | |
766 | filter_redundant_refs(T,All,R). | |
767 | ||
768 | /************************************************************************/ | |
769 | /* The same for Event-B */ | |
770 | /************************************************************************/ | |
771 | ||
772 | analyse_eventb_hierarchy(Machines,Contextes) :- | |
773 | reset_hierarchy, | |
774 | get_eventb_name(Machines,Contextes,MainName), | |
775 | assertz(main_machine_name(MainName)), | |
776 | maplist(analyse_eventb_machine,Machines), | |
777 | analyse_eventb_refinement_types(Machines), | |
778 | maplist(analyse_eventb_context,Contextes),!. | |
779 | analyse_eventb_hierarchy(Machines,Contextes) :- | |
780 | add_internal_error('Analyzing Event-B Hierarchy Failed: ',analyse_eventb_hierarchy(Machines,Contextes)). | |
781 | ||
782 | get_eventb_name([MainMachine|_AbstractMachines],_Contextes,Name) :- | |
783 | event_b_model(MainMachine,Name,_),!. | |
784 | get_eventb_name(_Machines,[MainContext|_AbstractContextes],Name) :- | |
785 | event_b_context(MainContext,Name,_). | |
786 | ||
787 | event_b_model(event_b_model(_,Name,Sections),Name,Sections). | |
788 | event_b_context(event_b_context(_,Name,Sections),Name,Sections). | |
789 | ||
790 | analyse_eventb_machine(Machine) :- | |
791 | event_b_model(Machine,Name,Sections), | |
792 | % print(analyzing(Name)),nl, | |
793 | ( memberchk(refines(_,RName),Sections) -> Type=refinement, RRefs=[ref(refines,RName,'')] | |
794 | ; Type=abstract_model, RRefs=[], RName='$none'), | |
795 | get_identifiers([variables],Sections,Vars), | |
796 | get_sees_context_refs(Sections,SRefs), | |
797 | append(RRefs,SRefs,Refs), | |
798 | get_events(Name,Sections,Events,RName), | |
799 | store_eventb_hash(Name,Machine), | |
800 | assertz(machine_type(Name,Type)), | |
801 | assertz(machine_identifiers(Name,[],[],Vars,[],[],[])), | |
802 | assertz(machine_references(Name,Refs)), | |
803 | assertz(machine_operations(Name,Events)), | |
804 | assertz(machine_operation_calls(Name,[])), % Event-B events cannot call other events | |
805 | assert_if_has_theorems(Name,Sections). | |
806 | ||
807 | ||
808 | % analyze which kinds of refinments we have between events | |
809 | analyse_eventb_refinement_types([]). | |
810 | analyse_eventb_refinement_types([RefMachine,AbsMachine|_]) :- | |
811 | event_b_model(RefMachine,RefName,RefSections), | |
812 | memberchk(refines(_,AbsName),RefSections), | |
813 | event_b_model(AbsMachine,AbsName,AbsSections), | |
814 | get_opt_section(events,RefSections,RawEvents), | |
815 | get_opt_section(events,AbsSections,AbsRawEvents), | |
816 | member(RawEvent,RawEvents), | |
817 | bmachine_eventb:raw_event(RawEvent,_,RefEvName,_St1,Ref,_Prm1,RefGrd,_Thm1,RefAct,_Wit1,_Desc1), | |
818 | Ref = [AbsEvName], | |
819 | member(AbsRawEvent,AbsRawEvents), | |
820 | bmachine_eventb:raw_event(AbsRawEvent,_,AbsEvName,_St2,_,_Prm2,AbsGrd,_Thm2,AbsAct,_Wit2,_Desc2), | |
821 | check_raw_prefix(AbsGrd,RefGrd,SameGuard), | |
822 | check_raw_prefix(AbsAct,RefAct,SameAct), | |
823 | %% format('~nEvent refinement change ~w (~w) -> ~w (~w) guard: ~w, action: ~w~n',[RefEvName,RefName,AbsEvName,AbsName,SameGuard,SameAct]), | |
824 | %print(rawgrd(RefGrd,AbsGrd)), nl, print(rawact(RefAct,AbsAct)),nl, | |
825 | assertz(event_refinement_change(RefName,RefEvName,AbsName,AbsEvName,SameGuard,SameAct)), | |
826 | fail. | |
827 | analyse_eventb_refinement_types([_|T]) :- analyse_eventb_refinement_types(T). | |
828 | ||
829 | check_raw_prefix([],[],Res) :- !, Res=unchanged. | |
830 | check_raw_prefix([],[_|_],Res) :- !, Res=extends. % the refinement has some more actions/guards | |
831 | check_raw_prefix([Abs|AT],[Ref|RT],Result) :- same_raw_expression(Abs,Ref),!, | |
832 | check_raw_prefix(AT,RT,Result). | |
833 | check_raw_prefix(_,_,refines). | |
834 | ||
835 | % TO DO: check if we have a more complete version of this predicate; to do: handle @desc description/3 terms | |
836 | same_raw_expression(identifier(_,A),RHS) :- !, RHS=identifier(_,B), A=B. | |
837 | same_raw_expression(equal(_,A,B),RHS) :- !, RHS=equal(_,A2,B2), same_raw_expression(A,A2), same_raw_expression(B,B2). | |
838 | same_raw_expression(assign(_,A1,A2),RHS) :- !, RHS=assign(_,B1,B2), | |
839 | maplist(same_raw_expression,A1,B1), maplist(same_raw_expression,A2,B2). | |
840 | same_raw_expression(A,B) :- atomic(A),!, A=B. | |
841 | same_raw_expression(A,B) :- A =.. [F,_|AA], % print(match(F,AA)),nl, | |
842 | B=.. [F,_|BB], maplist(same_raw_expression,AA,BB). | |
843 | ||
844 | ||
845 | ||
846 | get_sees_context_refs(Sections,SRefs) :- | |
847 | get_opt_section(sees,Sections,Seen), | |
848 | findall(ref(sees,I,''),member(I,Seen),SRefs). | |
849 | ||
850 | :- use_module(bmachine_eventb,[raw_event/11]). | |
851 | get_events(Name,Sections,Events,AbsMachineName) :- | |
852 | get_opt_section(events,Sections,RawEvents), | |
853 | compute_event_refines(Name,RawEvents,AbsMachineName), | |
854 | findall( identifier(Pos,EvName), | |
855 | ( member(RawEvent,RawEvents), | |
856 | raw_event(RawEvent,Pos,EvName,_St,_Rf,_Prm,_Grd,_Thm,_Act,_Wit,_Desc) ), | |
857 | Events). | |
858 | ||
859 | % TO DO: detect when an event extends another one without changing it | |
860 | compute_event_refines(MachineName,RawEvents,AbsMachineName) :- | |
861 | member(RawEvent,RawEvents), | |
862 | bmachine_eventb:raw_event(RawEvent,_Pos,EvName,_St,Refines,_Prm,_Grd,_Thm,_Act,_Wit,_Desc), | |
863 | member(RefEvent,Refines), | |
864 | % print(refines(MachineName,EvName,AbsMachineName,RefEvent)),nl, | |
865 | assertz(refines_event(MachineName,EvName,AbsMachineName,RefEvent)), | |
866 | fail. | |
867 | compute_event_refines(_,_,_). | |
868 | ||
869 | ||
870 | % try and get the name of the machine we refine | |
871 | %machine_refines(Machine,AbsMachine) :- machine_references(Machine,Refs), member(ref(refines,AbsMachine,_),Refs). | |
872 | ||
873 | analyse_eventb_context(Context) :- | |
874 | event_b_context(Context,Name,Sections), | |
875 | get_identifiers([constants],Sections,ConcreteConstants), | |
876 | get_identifiers([abstract_constants],Sections,AbstractConstants), | |
877 | append([ConcreteConstants,AbstractConstants],AllConstants), | |
878 | get_sets(Sections,Sets), | |
879 | get_extends_refs(Sections,Refs), | |
880 | store_eventb_hash(Name,Context), | |
881 | assertz(machine_type(Name,context)), | |
882 | assertz(machine_identifiers(Name,[],Sets,[],[],[],AllConstants)), | |
883 | assertz(machine_references(Name,Refs)), | |
884 | assertz(machine_operations(Name,[])), | |
885 | assertz(machine_operation_calls(Name,[])), | |
886 | maplist(assert_raw_id_with_position(concrete_constant),ConcreteConstants), | |
887 | maplist(assert_raw_id_with_position(abstract_constant),AbstractConstants), | |
888 | assert_if_has_theorems(Name,Sections). | |
889 | ||
890 | get_extends_refs(Sections,Refs) :- | |
891 | get_opt_section(extends,Sections,Extended), | |
892 | findall(ref(extends,E,''),member(E,Extended),Refs). | |
893 | ||
894 | assert_if_has_theorems(Name,Sections) :- | |
895 | get_opt_section(theorems,Sections,[_|_]), | |
896 | assertz(machine_has_assertions(Name)). | |
897 | assert_if_has_theorems(_Name,_Sections). | |
898 | ||
899 | ||
900 | % compute a hash based on constants and properties | |
901 | properties_hash(MachineName,Hash) :- | |
902 | properties_hash_cached(MachineName,Hash1),!, | |
903 | Hash=Hash1. | |
904 | properties_hash(MachineName,Hash) :- | |
905 | compute_properties_hash(MachineName,Hash1), | |
906 | assertz(properties_hash_cached(MachineName,Hash1)), | |
907 | Hash=Hash1. | |
908 | compute_properties_hash(Name,Hash) :- | |
909 | raw_machine(Name,Machine), | |
910 | get_machine(Name,[Machine],_Type,_Header,_Refines,Body), | |
911 | extract_sorted_np_sets(Body,Sets), | |
912 | extract_sorted_np_constants(Body,Constants), | |
913 | extract_np_properties(Body,Properties), | |
914 | extract_used_np_definitions_from_properties(Body,Definitions), | |
915 | ToHash = [Sets,Constants,Properties,Definitions], | |
916 | raw_sha_hash(ToHash,Hash). | |
917 | % save_properties_hash(Name,ToHash,Hash). | |
918 | ||
919 | extract_sorted_np_sets(Body,Sets) :- | |
920 | get_sets(Body,PosSets), | |
921 | remove_raw_position_info(PosSets,UnsortedSets), | |
922 | sort(UnsortedSets,Sets). | |
923 | extract_sorted_np_constants(Body,Constants) :- | |
924 | get_opt_sections([constants,concrete_constants,abstract_constants],Body,PosConstants), | |
925 | remove_raw_position_info(PosConstants,UnsortedConstants), | |
926 | sort(UnsortedConstants,Constants). | |
927 | extract_np_properties(Body,Properties) :- | |
928 | get_opt_section(properties,Body,PosProperties), | |
929 | remove_raw_position_info(PosProperties,Properties). | |
930 | extract_used_np_definitions_from_properties(Body,Definitions) :- | |
931 | get_opt_section(properties,Body,Properties), | |
932 | extract_used_np_definitions(Properties,Body,Definitions,_). | |
933 | extract_used_np_definitions(RawSyntax,Body,Definitions,PosDefinitions) :- | |
934 | extract_raw_identifiers(RawSyntax,UsedIds), | |
935 | all_definition_ids(Body,AllDefs), | |
936 | ord_intersection(UsedIds,AllDefs,UsedDefNames), | |
937 | transitive_used_definitions(UsedDefNames,AllUsedDefs), | |
938 | findall( definition(none,Name,Args,DefBody), | |
939 | ( member(Name,AllUsedDefs),b_get_definition(Name,_DefType,Args,DefBody,_Deps)), | |
940 | PosDefinitions), | |
941 | maplist(remove_raw_position_info,PosDefinitions,Definitions). | |
942 | all_definition_ids(Body,Ids) :- | |
943 | get_opt_section(definitions,Body,Definitions), | |
944 | convlist(get_definition_name,Definitions,Ids1), | |
945 | sort(Ids1,Ids). | |
946 | transitive_used_definitions(Defs,TransDefs) :- | |
947 | findall( D, reachable_definition(Defs,D), TD1), | |
948 | sort(TD1,TransDefs). | |
949 | reachable_definition(Defs,D) :- | |
950 | member(N,Defs), | |
951 | ( D=N | |
952 | ; b_get_definition(N,_DefType,_Args,_Body,Deps), | |
953 | reachable_definition(Deps,D)). | |
954 | ||
955 | ||
956 | /* just for debugging: | |
957 | save_properties_hash(MachineName,ToHash,Hash) :- | |
958 | main_machine_name(Main), | |
959 | open('/home/plagge/hashes.pl',append,S), | |
960 | writeq(S,hash(Main,MachineName,ToHash,Hash)), | |
961 | write(S,'.\n'), | |
962 | close(S). | |
963 | */ | |
964 | ||
965 | % --------------------------- | |
966 | :- dynamic event_info/3. | |
967 | analyze_extends_relation :- | |
968 | retractall(event_info(_,_,_)), | |
969 | bmachine:b_get_machine_operation(_Name,_Results,_RealParameters,TBody,_OType,_OpPos), | |
970 | treat_event_body(TBody),fail. | |
971 | analyze_extends_relation. | |
972 | ||
973 | treat_event_body(TBody) :- | |
974 | rlevent_info(TBody,EventName,Machine,Status,AbstractEvents), | |
975 | % format('Event ~w:~w ~w~n',[Machine,EventName,Status]), | |
976 | assertz(event_info(Machine,EventName,Status)), | |
977 | maplist(treat_event_body,AbstractEvents). | |
978 | ||
979 | :- use_module(bsyntaxtree,[get_texpr_expr/2]). | |
980 | rlevent_info(TBody,EventName,Machine,FStatus,AbstractEvents) :- | |
981 | get_texpr_expr(TBody,Event), | |
982 | Event = rlevent(EventName,Machine,TStatus,_Params,_Guard,_Theorems,_Actions,_VWit,_PWit,_Unmod,AbstractEvents), | |
983 | bsyntaxtree:get_texpr_expr(TStatus,Status), %ordinary, convergent, anticipated | |
984 | functor(Status,FStatus,_). | |
985 | ||
986 | ||
987 | % write the event refinement hierarchy to a dot file | |
988 | % (currently) only makes sense for Event-B models | |
989 | ||
990 | :- use_module(preferences,[get_preference/2]). | |
991 | :- use_module(tools_strings,[ajoin/2, ajoin_with_sep/3]). | |
992 | ||
993 | :- public dot_refinement_node_new/4. | |
994 | % variation for: use_new_dot_attr_pred | |
995 | dot_refinement_node_new(event_refinement,M:Ev,M,[label/Desc,shape/Shape,tooltip/Tooltip|T1]) :- | |
996 | dot_event_node(M:Ev,M,Desc,Shape,Style,Color), | |
997 | (Style=none -> T1=T2 ; T1=[style/Style|T2]), | |
998 | (Color=none -> T2=[] ; T2=[color/Color]), | |
999 | (event_info(M,Ev,Status) -> true ; Status=unknown), | |
1000 | (event_refinement_change(M,Ev,AbsName,AbsEvName,SameGuard,SameAct) | |
1001 | -> ajoin(['Event ',Ev,' in ',M, | |
1002 | '\n status: ',Status, | |
1003 | '\n refines ',AbsEvName,' in ',AbsName, | |
1004 | '\n guard: ',SameGuard, | |
1005 | '\n action: ',SameAct], Tooltip) | |
1006 | ; ajoin(['Event ',Ev,' in ',M, | |
1007 | '\n status: ',Status], Tooltip)). | |
1008 | dot_refinement_node_new(variable_refinement(With),M:Var,M,[label/Desc,shape/Shape,color/Color,tooltip/Tooltip|T1]) :- | |
1009 | (With=with_constants -> machine_ids(M,Vars) ; machine_variables(M,Vars)), | |
1010 | member(Var,Vars), | |
1011 | Desc=Var, | |
1012 | (id_exists_in_abstraction(M,Anc,Var) | |
1013 | -> get_preference(dot_event_hierarchy_unchanged_event_colour,Color), T1=[style/filled], | |
1014 | Shape = rect, % we could use plain; makes kept events smaller | |
1015 | ajoin(['Variable kept from abstraction ',Anc],Tooltip) | |
1016 | ; machine_type(M,context), machine_sets(M,Sets), member(Var,Sets) -> | |
1017 | get_preference(dot_event_hierarchy_refines_colour,Color), T1=[style/'rounded,filled'], | |
1018 | Shape = rect, | |
1019 | ajoin(['New set in ',M],Tooltip) | |
1020 | ; machine_type(M,context) -> % it must be a constant | |
1021 | get_preference(dot_event_hierarchy_new_event_colour,Color), T1=[style/rounded], | |
1022 | Shape = rect, | |
1023 | ajoin(['New constant in ',M],Tooltip) | |
1024 | ; get_preference(dot_event_hierarchy_new_event_colour,Color), T1=[], | |
1025 | Shape = rect, | |
1026 | ajoin(['New variable in ',M],Tooltip) | |
1027 | ). | |
1028 | %dot_refinement_node_new(variable_refinement(_),C:Cst,M,[label/Desc,shape/rect,color/Color,tooltip/Desc|T1]) :- | |
1029 | % new_seen_context(M,C), machine_constants(C,Csts), member(Cst,Csts), | |
1030 | % Desc=Cst, T1=[style/rounded], get_preference(dot_event_hierarchy_extends_colour,Color). | |
1031 | ||
1032 | % the node ide is M:Ev as Ev can and usually does occur multiple times | |
1033 | dot_event_node(M:Ev,M,Desc,Shape,Style,Color) :- | |
1034 | findall(showev(M,Ev),event_to_show(M,Ev),List), sort(List,SList), | |
1035 | member(showev(M,Ev),SList), | |
1036 | (event_info(M,Ev,Status) -> true ; Status=unknown), | |
1037 | (event_refinement_change(M,Ev,_,_,SameGuard,SameAction) | |
1038 | -> true ; SameGuard=unknown, SameAction=unknown), | |
1039 | (SameGuard=SameAction, SameGuard \= unknown -> ajoin([Ev,'\\n(',SameAction,')'],Ev2) | |
1040 | ; SameGuard=unchanged ,SameAction=extends-> ajoin([Ev,'\\n(same grd, extends act)'],Ev2) | |
1041 | ; SameGuard=unchanged -> ajoin([Ev,'\\n(same grd)'],Ev2) | |
1042 | ; SameGuard=extends,SameAction=unchanged -> ajoin([Ev,'\\n(same act, extends grd)'],Ev2) | |
1043 | ; SameAction=unchanged -> ajoin([Ev,'\\n(same act)'],Ev2) | |
1044 | ; SameGuard=extends -> ajoin([Ev,'\\n(extends grd)'],Ev2) | |
1045 | ; SameAction=extends -> ajoin([Ev,'\\n(extends act)'],Ev2) | |
1046 | ; Ev2=Ev), | |
1047 | (Status = convergent -> ajoin([Ev2,' (<)'],Desc) | |
1048 | ; Status = anticipated -> ajoin([Ev2, ' (<=)'],Desc) | |
1049 | ; Desc=Ev2), | |
1050 | % format(user_output,'event ~w, status:~w, same guard:~w, same action:~w~n',[Ev,Status,SameGuard,SameAction]), | |
1051 | dot_get_color_style(M,Ev,Status,SameGuard,SameAction,Shape,Color,Style). | |
1052 | ||
1053 | event_to_show(M,Ev) :- machine_operations(M,Evs), | |
1054 | raw_identifier_member(Ev,Evs), Ev \= 'INITIALISATION'. | |
1055 | event_to_show(M,Ev) :- | |
1056 | event_refinement_change(M,Ev,_,_,_,_), % for events that disappear, i.e., are not refined until bottom level | |
1057 | Ev \= 'INITIALISATION'. | |
1058 | ||
1059 | ||
1060 | dot_get_color_style(M,Ev,_Status,_,_,box,Color,Style) :- \+ refines_event(M,Ev,_,_),!, | |
1061 | get_preference(dot_event_hierarchy_new_event_colour,Color), Style=none. | |
1062 | dot_get_color_style(M,Ev,_,SameGuard,SameAction,box,Color,Style) :- | |
1063 | refines_event(M,Ev,_,Ev2), dif(Ev2,Ev),!, % changes name | |
1064 | ((SameGuard,SameAction)=(unchanged,unchanged) | |
1065 | -> get_preference(dot_event_hierarchy_rename_unchanged_event_colour,Color) | |
1066 | ; get_preference(dot_event_hierarchy_rename_event_colour,Color)), Style=filled. | |
1067 | dot_get_color_style(_M,_Ev,_,unchanged,unchanged,plain,Color,Style) :- % keeps name and adds no guard or action | |
1068 | !, | |
1069 | get_preference(dot_event_hierarchy_unchanged_event_colour,Color), Style=filled. | |
1070 | dot_get_color_style(_M,_Ev,_,_,unchanged,box,Color,Style) :- % keeps name and adds no action, but modifies guard | |
1071 | !, | |
1072 | get_preference(dot_event_hierarchy_grd_strengthening_event_colour,Color), Style=filled. | |
1073 | dot_get_color_style(_M,_Ev,_,unchanged,_,box,Color,Style) :- % keeps name and adds action but keeps guard | |
1074 | !, | |
1075 | get_preference(dot_event_hierarchy_grd_keeping_event_colour,Color), Style=filled. | |
1076 | dot_get_color_style(_M,_Ev,_,_,_,box,Color,Style) :- % keeps name but adds or modifies | |
1077 | % TO DO: distinguish extends from refines | |
1078 | get_preference(dot_event_hierarchy_refines_colour,Color), Style=filled. | |
1079 | ||
1080 | :- public dot_refines_event/4. | |
1081 | % dot transition predicate for event and variable refinement hierarchy diagram | |
1082 | dot_refines_event(event_refinement,M2:Ev2,M1:Ev1,[label/Label,color/Color,style/Style]) :- | |
1083 | Label = '', % TO DO: detect refine, extends, identical | |
1084 | refines_event(M1,Ev1,M2,Ev2), | |
1085 | Ev1 \= 'INITIALISATION', | |
1086 | (event_refinement_change(M1,Ev1,_,_,SameGuard,SameAct) | |
1087 | -> arrow_style(SameGuard,SameAct,Style,ColPref), get_preference(ColPref,Color) | |
1088 | ; Style=solid, Color=red % should not happen | |
1089 | ). | |
1090 | dot_refines_event(variable_refinement(With),M1:Var,M2:Var2,[label/Label,color/Color,style/Style|T1]) :- | |
1091 | get_preference(dot_event_hierarchy_edge_colour,Color), | |
1092 | (With=with_constants -> machine_ids(M2,Vars2) ; machine_variables(M2,Vars2)), | |
1093 | if((member(Var,Vars2), | |
1094 | id_exists_in_abstraction(M2,M1,Var)), | |
1095 | (Label='',Var2=Var,Style=dashed, T1=[]), | |
1096 | (% no variable of M2 exists in M1 | |
1097 | refines_or_extends_machine(M2,M1), | |
1098 | machine_ids(M1,[Var|_]), % get first variable of M1 | |
1099 | Vars2=[Var2|_], Style=dotted, % add virtual edge to first variable of M2 if no variable is kept | |
1100 | get_dot_cluster_name(M1,M1C), get_dot_cluster_name(M2,M2C), | |
1101 | T1 = [ltail/M1C, lhead/M2C], | |
1102 | (machine_type(M2,context) -> Label='extends' ; Label='') | |
1103 | ) | |
1104 | ). | |
1105 | dot_refines_event(variable_refinement(with_constants),M1:Var1,M2:Cst2,[label/Label,color/Color,style/Style|T1]) :- Label='sees', | |
1106 | Color=gray80, Style=solid, | |
1107 | new_seen_context(M1,M2), | |
1108 | machine_ids(M1,[Var1|_]), | |
1109 | machine_ids(M2,[Cst2|_]), | |
1110 | get_dot_cluster_name(M1,M1C), get_dot_cluster_name(M2,M2C), | |
1111 | T1 = [ltail/M1C, lhead/M2C, dir/forward]. | |
1112 | ||
1113 | arrow_style(unchanged,unchanged,Style,ColPref) :- !, | |
1114 | Style=arrowhead(none,solid), ColPref=dot_event_hierarchy_extends_colour. | |
1115 | arrow_style(refines,_,Style,ColPref) :- !, Style=solid, ColPref=dot_event_hierarchy_edge_colour. | |
1116 | arrow_style(_,refines,Style,ColPref) :- !, Style=solid, ColPref=dot_event_hierarchy_edge_colour. | |
1117 | %arrow_style(_,_,Style) :- Style = arrowhead(vee,arrowtail(box,solid)). % we have extends | |
1118 | arrow_style(_,_,Style,dot_event_hierarchy_extends_colour) :- Style = arrowhead(vee,solid). % we have extends | |
1119 | ||
1120 | ||
1121 | %dot_same_rank(SameRankVals) :- machine_operations(M,Evs), | |
1122 | % findall(M:Ev,raw_identifier_member(Ev,Evs),SameRankVals). | |
1123 | ||
1124 | dot_subgraph(Kind,sub_graph_with_attributes(M,Attrs), filled,Colour) :- | |
1125 | get_preference(dot_event_hierarchy_machine_colour,MColour), | |
1126 | Attrs = [label/Label, tooltip/ToolTip], | |
1127 | machine_operations(M,Ops), | |
1128 | (Kind=event_refinement -> Ops=[_|_] ; true), | |
1129 | (machine_type(M,context) -> IDS = 'csts', Colour=gray90 | |
1130 | ; IDS = 'vars', Colour=MColour), | |
1131 | (machine_ids(M,Vars) | |
1132 | -> length(Vars,V), | |
1133 | findall(C,new_seen_context(M,C),NewC), | |
1134 | split_list(id_exists_in_abstraction(M),Vars,_Old,NewVars), | |
1135 | length(NewVars,NewNr), | |
1136 | findall(Del,(var_exists_in_abstraction(M,Del), nonmember(Del,Vars)),DelVars), | |
1137 | length(DelVars,DelNr), | |
1138 | (Kind=event_refinement, get_preference(dot_hierarchy_show_extra_detail,false) | |
1139 | -> Label=M | |
1140 | ; ajoin([M,'\\n#',IDS,'=',V, ' (+',NewNr,',-', DelNr,')'],Label) | |
1141 | ), | |
1142 | ajoin_with_sep(NewVars,',',NV), | |
1143 | ajoin_with_sep(DelVars,',',DV), | |
1144 | ajoin_with_sep(NewC,',',NC), | |
1145 | ajoin(['machine ',M,'\\n#',IDS,'=',V, ' (+',NewNr,',-', DelNr,')', | |
1146 | '\\nnew sees=',NC, | |
1147 | '\\nnew ',IDS,'=',NV, | |
1148 | '\\ndel ',IDS,'=',DV],ToolTip) | |
1149 | ; Label=M, ToolTip=M). | |
1150 | ||
1151 | machine_variables(M,Vars) :- machine_identifiers(M,_Params,_Sets,AVars,CVars,_AConsts,_CConsts), | |
1152 | append(CVars,AVars,RVars), % for Event-B: CVars=[] | |
1153 | maplist(get_raw_identifier,RVars,Vars). | |
1154 | %machine_constants(M,Consts) :- machine_identifiers(M,_Params,_Sets,_AVars,_CVars,AConsts,CConsts), | |
1155 | % append(CConsts,AConsts,Raw), | |
1156 | % maplist(get_raw_identifier,Raw,Consts). | |
1157 | machine_ids(M,Vars) :- machine_identifiers(M,_Params,Sets,AVars,CVars,AConsts,CConsts), | |
1158 | append([Sets,CConsts,CVars,AConsts,AVars],RVars), | |
1159 | maplist(get_raw_identifier,RVars,Vars). | |
1160 | machine_sets(M,Vars) :- machine_identifiers(M,_,Sets,_,_,_,_), | |
1161 | maplist(get_raw_identifier,Sets,Vars). | |
1162 | ||
1163 | var_exists_in_abstraction(M,Var) :- | |
1164 | var_exists_in_abstraction(M,_Anc,Var). | |
1165 | var_exists_in_abstraction(M,Anc,Var) :- | |
1166 | refines_machine(M,Anc), | |
1167 | machine_variables(Anc,AncVars), | |
1168 | member(Var,AncVars). | |
1169 | ||
1170 | refines_machine(M,Anc) :- | |
1171 | machine_references(M,Refs), | |
1172 | member(ref(refines,Anc,_),Refs). | |
1173 | ||
1174 | % variable or constant exists in abstraction | |
1175 | id_exists_in_abstraction(M,Var) :- | |
1176 | id_exists_in_abstraction(M,_Anc,Var). | |
1177 | id_exists_in_abstraction(M,Anc,Var) :- | |
1178 | refines_or_extends_machine(M,Anc), | |
1179 | machine_ids(Anc,AncVars), | |
1180 | member(Var,AncVars). | |
1181 | ||
1182 | refines_or_extends_machine(M,Anc) :- | |
1183 | machine_references(M,Refs), | |
1184 | (member(ref(refines,Anc,_),Refs) -> true ; member(ref(extends,Anc,_),Refs)). | |
1185 | ||
1186 | new_seen_context(M,Context) :- machine_references(M,Refs), | |
1187 | member(ref(sees,Context,_),Refs), | |
1188 | \+ (member(ref(refines,Anc,_),Refs), | |
1189 | sees_context(Anc,Context)). | |
1190 | ||
1191 | sees_context(M,Context) :- machine_references(M,Refs), member(ref(sees,Context,_),Refs). | |
1192 | ||
1193 | ||
1194 | write_dot_event_hierarchy_to_file(File) :- | |
1195 | write_dot_ref_hierarchy_to_file(event_refinement,File). | |
1196 | write_dot_variable_hierarchy_to_file(File) :- | |
1197 | (get_preference(dot_hierarchy_show_extra_detail,false) -> With=no_constants ; With=with_constants), | |
1198 | write_dot_ref_hierarchy_to_file(variable_refinement(With),File). | |
1199 | write_dot_ref_hierarchy_to_file(Kind,File) :- | |
1200 | analyze_extends_relation, | |
1201 | (get_preference(dot_event_hierarchy_horizontal,true) | |
1202 | -> PageOpts=[compound/true,rankdir/'LR',no_page_size] | |
1203 | ; PageOpts=[compound/true]), | |
1204 | gen_dot_graph(File,PageOpts, | |
1205 | use_new_dot_attr_pred(b_machine_hierarchy:dot_refinement_node_new(Kind)), | |
1206 | use_new_dot_attr_pred(b_machine_hierarchy:dot_refines_event(Kind)), | |
1207 | dot_no_same_rank,dot_subgraph(Kind)). | |
1208 | %gen_dot_graph(File,PageOpts,dot_event_node,dot_refines_event,dot_no_same_rank,dot_subgraph). | |
1209 | ||
1210 | % -------------------- | |
1211 | ||
1212 | reference_link(FromMachine,DestMachine) :- | |
1213 | machine_references(FromMachine,Refs), | |
1214 | filter_redundant_refs(Refs,Refs,UsefulRefs), | |
1215 | member(ref(_Type,DestMachine,_Prefix),UsefulRefs). | |
1216 | ||
1217 | :- use_module(library(ugraphs),[vertices_edges_to_ugraph/3, top_sort/2, transitive_closure/2, | |
1218 | min_paths/3, min_path/5, neighbours/3, del_vertices/3]). | |
1219 | ||
1220 | % b_machine_hierarchy:get_machine_topological_order(V) | |
1221 | % get machine in topological order of inclusion | |
1222 | get_machine_topological_order(SortedVertices) :- | |
1223 | get_machine_inclusion_graph(_Vertices,_Edges,Graph), | |
1224 | top_sort(Graph,SortedVertices). | |
1225 | ||
1226 | % get machine inclusion graph in format for ugraphs (unweighted graphs) library | |
1227 | get_machine_inclusion_graph(Vertices,Edges,Graph) :- | |
1228 | findall(Mach,machine_name(Mach),Vertices), | |
1229 | findall(From-To,reference_link(From,To),Edges), | |
1230 | vertices_edges_to_ugraph(Vertices, Edges, Graph). | |
1231 | ||
1232 | print_machine_topological_order :- | |
1233 | format('Topological sorting of B machine references~n',[]), | |
1234 | get_machine_inclusion_graph(Vertices,Edges,Graph), | |
1235 | length(Vertices,LenV),format(' * number of machines: ~w~n',[LenV]), | |
1236 | length(Edges,LenE), format(' * number of reference links: ~w~n',[LenE]), | |
1237 | top_sort(Graph,SortedVertices), | |
1238 | main_machine_name(Main), | |
1239 | %min_paths(Main,Graph,MinPaths), | |
1240 | transitive_closure(Graph,TGraph), | |
1241 | maplist(top_print_machine(Main,Graph,TGraph),SortedVertices), | |
1242 | greedy_cover(Main,Graph,TGraph,GreedyCover), length(GreedyCover,GLen), | |
1243 | format(' * sufficient includes (~w) to cover all machines: ~w~n',[GLen,GreedyCover]). | |
1244 | ||
1245 | top_print_machine(Main,Graph,TGraph,M) :- | |
1246 | (machine_references(M,Refs) -> length(Refs,Len) ; Len=0), | |
1247 | neighbours(M,TGraph,N), length(N,Len2), | |
1248 | format(' ~w has ~w direct references and ~w indirect ones~n',[M,Len,Len2]), | |
1249 | min_path(Main, M, Graph, Path, Length), | |
1250 | format(' inclusion length ~w: ~w~n',[Length,Path]). | |
1251 | ||
1252 | greedy_cover(Main,Graph,TGraph,GreedyCover) :- | |
1253 | neighbours(Main,Graph,TopLevelIncludes), | |
1254 | greedy_cover(TopLevelIncludes,TGraph,GreedyCover). | |
1255 | ||
1256 | % compute a list of included machines which cover all required inclusions | |
1257 | % we try to make this inclusion minimal, using a greedy algorithm | |
1258 | greedy_cover([],_TGraph,Cover) :- !, Cover=[]. | |
1259 | greedy_cover(RemainingIncludes,TGraph, GreedyCover) :- | |
1260 | findall(candidate(NrCovered,Machine), | |
1261 | (member(Machine,RemainingIncludes), | |
1262 | neighbours(Machine,TGraph,N), length(N,NrCovered)), Cands), | |
1263 | (max_member(candidate(MaxNrCov,NextChoice),Cands) % pick the machine covering the most other ones | |
1264 | -> | |
1265 | (MaxNrCov =< 0 | |
1266 | -> GreedyCover = [] % everything is included already | |
1267 | ; GreedyCover = [NextChoice|TGreedyCover], | |
1268 | select(NextChoice,RemainingIncludes,Rem2), % this include is no longer available | |
1269 | neighbours(NextChoice,TGraph,NowCovered), | |
1270 | del_vertices(TGraph, [NextChoice|NowCovered], TGraph2), % delete all machines included by NextChoice | |
1271 | greedy_cover(Rem2,TGraph2,TGreedyCover) | |
1272 | ) | |
1273 | ; GreedyCover = [] % everything is included already; no node appears in the graph anymore | |
1274 | ). | |
1275 |