1 | | % (c) 2009-2024 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(record_detection,[replace_sets_by_records/2]). |
6 | | |
7 | | |
8 | | :- use_module(tools). |
9 | | :- use_module(self_check). |
10 | | |
11 | | :- use_module(module_information,[module_info/2]). |
12 | | :- module_info(group,typechecker). |
13 | | :- module_info(description,'This module detects bijections between deferred sets and cartesian products, and compiles them away.'). |
14 | | |
15 | | |
16 | | :- use_module(library(lists)). |
17 | | :- use_module(library(ordsets)). |
18 | | |
19 | | :- use_module(bmachine_structure). |
20 | | :- use_module(bsyntaxtree). |
21 | | :- use_module(preferences,[get_preference/2]). |
22 | | :- use_module(b_ast_cleanup,[clean_up/3]). |
23 | | :- use_module(kernel_freetypes,[get_freetype_type_parameters/3]). |
24 | | :- use_module(b_global_sets,[register_replaced_global_set/2]). |
25 | | |
26 | | replace_sets_by_records(Machine,ResultMachine) :- |
27 | | % replace until a fixpoint is reached |
28 | ? | replace_sets_by_records2(Machine,RMachine),!, |
29 | | replace_sets_by_records(RMachine,ResultMachine). |
30 | | /* we could do something like this for the values clause for deferred sets mapped to integer intervals: |
31 | | it would have to be done for all deferred sets + the problem is that the type checker runs before this code ! |
32 | | replace_sets_by_records(Machine,ResultMachine) :- |
33 | | bmachine:get_section(values,Machine,Values), |
34 | | member(b(values_entry(TID,TVal),_,Info),Values), |
35 | | get_texpr_id(TID,Set), |
36 | | bmachine:get_section(deferred_sets,Machine,Sets), |
37 | | get_texpr_id(TExpr,Set),member(TExpr,Sets), |
38 | | get_texpr_type(TVal,Type), |
39 | | !, |
40 | | Domain = TVal, |
41 | | replace_type_in_machine(GlobalSet,Type,Domain,Machine,ResultMachine). |
42 | | */ |
43 | | replace_sets_by_records(Machine,Machine). |
44 | | |
45 | | replace_sets_by_records2(Machine,ResultMachine) :- |
46 | | get_preference(use_record_construction,true), |
47 | | has_deferred_sets(Machine), |
48 | | get_section(deferred_sets,Machine,DeferredSets), |
49 | | select_section(properties,Properties,NewProperties,Machine,ConsMachine), |
50 | ? | select_constructor_axiom(Properties,DeferredSets,Set,Constructor,Cons,Domain,RestList1), |
51 | | (debug:debug_mode(on) -> format('Record detected for deferred set ~w~n',[Set]) ; true), |
52 | | check_record_construction_ok(Set,Domain), |
53 | | replace_sets_by_records3(NewProperties,ConsMachine,Set,Constructor,Cons,Domain,RestList1,ResultMachine). |
54 | | |
55 | | |
56 | | :- use_module(typing_tools,[is_infinite_type/1]). |
57 | | :- use_module(specfile,[classical_b_mode/0]). |
58 | | :- use_module(error_manager,[add_message/4]). |
59 | | check_record_construction_ok(Set,Domain) :- |
60 | ? | classical_b_mode, % classical B |
61 | | get_texpr_type(Domain,Type), |
62 | | is_infinite_type(Type), |
63 | | % now we know that we have detected a record |
64 | | % Section 5.2.6 of \cite{Abrial:BBook} (page 281) and Section 7.13 of \atelierb{} handbook says that given sets are finite |
65 | | !, |
66 | | get_preference(disprover_mode,false), % fail in disprover mode for classical B |
67 | | ajoin(['Translating deferred set ',Set,' to possibly infinite set (set USE_RECORD_CONSTRUCTION to FALSE to prevent this) : '],Msg), |
68 | | add_message(record_detection,Msg,Domain,Domain). |
69 | | check_record_construction_ok(_,_). |
70 | | |
71 | | :- use_module(error_manager,[add_internal_error/2, add_error/4]). |
72 | | :- use_module(translate,[print_bexpr/1]). |
73 | | replace_sets_by_records3(NewProperties,ConsMachine,Set,Constructor1,Cons,Domain,RestList1,ResultMachine) :- |
74 | | Cons = constructor(ConstructorDomain,RecDomainType,_), |
75 | | add_texpr_infos(Constructor1,[record_detection(constructor)],Constructor), |
76 | | create_constructor_definition(Constructor,ConstructorDomain,ConsDef), |
77 | | (debug:debug_mode(on) -> format(' Constructor for ~w: ',[Set]),print_bexpr(ConsDef),nl ; true), %% |
78 | | create_recordset_definition(Set,Domain,SetDef), |
79 | | (debug:debug_mode(on) -> format(' New definition for ~w: ',[Set]),print_bexpr(SetDef),nl ; true), %% |
80 | | create_optional_field_access(Set,Domain,Constructor,RecordFunIds,RestList1,RestList), |
81 | | conjunct_predicates([ConsDef,SetDef|RestList],NewProperties), |
82 | | move_deferred_set(Set,ConsMachine,SetMachine), |
83 | | % replace the constants (constructor,accessors,update functions) by versions that |
84 | | % have an info field that indicate their function (needed later to exclude them when |
85 | | % adding additional constraints): |
86 | | foldl(replace_constant,[Constructor|RecordFunIds],SetMachine,M2), |
87 | | %% print(replacing(Set,RecDomainType)),nl, %% |
88 | | replace_type_in_machine(Set,RecDomainType,Domain,M2,ResultMachine), |
89 | | !, |
90 | | register_replaced_global_set(Set,Domain). |
91 | | replace_sets_by_records3(NewProperties,ConsMachine,Set,Constructor,Cons,Domain,RestList1,ResultMachine) :- |
92 | | add_internal_error('Replacing record failed ',replace_sets_by_records3(NewProperties,ConsMachine,Set,Constructor,Cons,Domain,RestList1,ResultMachine)),fail. |
93 | | |
94 | | has_deferred_sets(Machine) :- |
95 | | get_section(deferred_sets,Machine,DefSets), |
96 | | DefSets = [_|_]. |
97 | | |
98 | | % replace_constant(+NewConstant,+In,-Out): |
99 | | % replace a constant by a new one of the same name |
100 | | replace_constant(NewConstant,In,Out) :- |
101 | | % try the replacement in abstract and concrete constants: |
102 | | ( replace_id_in_section(NewConstant,abstract_constants,In,Out) -> true |
103 | | ; replace_id_in_section(NewConstant,concrete_constants,In,Out) -> true |
104 | | ; add_error(record_detection,'Could not replace constant: ',NewConstant,NewConstant), |
105 | | fail |
106 | | ). |
107 | | replace_id_in_section(NewId,Section,In,Out) :- |
108 | | get_texpr_id(NewId,Id), get_texpr_id(OldId,Id), |
109 | | select_section(Section,OldIds,NewIds,In,Out), |
110 | | selectchk(OldId,OldIds,NewId,NewIds). |
111 | | |
112 | | % select_constructor_axiom(+Properties,+Sets,-Set,-Constructor,-Cons,-Domain,-RestList) |
113 | | % Chooses a constructor axiom if any exists |
114 | | % Properties: The properties (a typed predicate) of the machine |
115 | | % Sets: The list of deferred sets (a list of typed identifiers) |
116 | | % Set,Constructor,Cons,Domain: see is_constructor_axiom/6 below |
117 | | % RestList: A list of remaining predicates after removing the constructor axiom |
118 | | select_constructor_axiom(Properties,Sets,Set,Constructor,Cons,Domain,RestList) :- |
119 | | conjunction_to_list(Properties,PList), |
120 | ? | select(Prop,PList,RestList), |
121 | ? | is_constructor_axiom(Prop,Sets,Set,Constructor,Cons,Domain). |
122 | | |
123 | | create_constructor_definition(Constructor,Domain,ConsDef) :- |
124 | | create_texpr(equal(Constructor,Identity),pred,[],ConsDef), |
125 | | get_texpr_type(Constructor,Type), |
126 | | create_texpr(identity(Domain),Type,[],Identity). |
127 | | |
128 | | create_recordset_definition(SetName,Domain,SetDef) :- |
129 | | create_texpr(equal(Set,Domain),pred,[],SetDef), |
130 | | get_texpr_type(Domain,Type), |
131 | | create_texpr(identifier(SetName),Type,[],Set). |
132 | | |
133 | | % move_deferred_set(Set+,OldMachine+,NewMachine-) :- |
134 | | % Moves a deferred set to the concrete_constants section |
135 | | % Set: The (untyped) id of the set |
136 | | % OldMachine: The complete B machine where the set is a deferred set |
137 | | % NewMachine: The same B machine, but the deferred set is now a constant |
138 | | move_deferred_set(Set,OldMachine,NewMachine) :- |
139 | | select_section(deferred_sets,Sets,NewSets,OldMachine,Machine1), |
140 | | get_texpr_id(TSet,Set), |
141 | | selectchk(TSet,Sets,NewSets), |
142 | | select_section(concrete_constants,Constants,[TSet|Constants],Machine1,NewMachine). |
143 | | |
144 | | % is_constructor_axiom(+TAxiom,+Sets,-GType,-TConstructor,-Cons,-TDom) |
145 | | % TAxiom: A typed predicate |
146 | | % Sets: A list of typed identifiers, the deferred sets |
147 | | % GType: The (untyped) ID of the set that represents the record |
148 | | % TConstructor: The constructor function (a bijection from a domain to the record type) |
149 | | % Cons: a term of the form constructor(Domain,DomainType,Kind) where Kind is either constructor or destructor |
150 | | % TDom: The domain of the constructor |
151 | | % The constructor can actually be a destructor, i.e. a bijection from the record to the domain. |
152 | | % The third argument in the Cons term indicates which case has been encountered |
153 | | is_constructor_axiom(TAxiom,Sets,GType,TConstructor,Cons,TDom) :- |
154 | | % The axiom has the form c : Dom >->> Set, where |
155 | | % c and Set are identifiers |
156 | | get_texpr_expr(TAxiom,member(TConstructor,TBijection)), |
157 | | get_texpr_type(TConstructor,set(couple(FromType,ToType))), |
158 | | get_texpr_id(TConstructor,_), % just make sure that TConstructor is an identifer |
159 | | FromType \= ToType, % otherwise bijection maps to itself |
160 | | (get_texpr_expr(TBijection,total_bijection(TDom,TSet)), |
161 | | Cons=constructor(TDom,FromType,constructor) |
162 | | ; get_texpr_expr(TBijection,total_bijection(TSet,TDom)), |
163 | | % we have the bijection the other way; does not really matter for a bijection anyway |
164 | | Cons = constructor(TSet,ToType,destructor)), |
165 | | get_texpr_type(TSet,set(global(GType))), |
166 | | get_texpr_id(TSet,GType), |
167 | | % check if the set is a deferred set, not an enumerated set |
168 | | get_texpr_id(SetTest,GType), |
169 | | memberchk(SetTest,Sets), |
170 | | % in Dom, there should be no reference to Set |
171 | | no_reference(TDom,GType). |
172 | | |
173 | | |
174 | | no_reference(TExpr,Type) :- |
175 | | syntaxtraversion(TExpr,Expr,_,_,Subs,_), |
176 | | no_reference2(Expr,Type), |
177 | | no_reference_l(Subs,Type). |
178 | | no_reference2(identifier(Type),Type) :- !,fail. |
179 | | no_reference2(_,_). |
180 | | no_reference_l([],_). |
181 | | no_reference_l([Expr|Rest],Type) :- |
182 | | no_reference(Expr,Type), |
183 | | no_reference_l(Rest,Type). |
184 | | |
185 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
186 | | create_optional_field_access(SetName,Domain,Constructor,UpAc,Properties,RestList) :- |
187 | | get_texpr_id(Set,SetName), |
188 | | find_and_remove_field_declaration(Set,Domain,Constructor,Fields,Properties,P4), |
189 | | !, |
190 | | replace_update_definitions(Fields,Set,Constructor,Updates,P4,P5), |
191 | | maplist(create_field_definition(Domain),Fields,Accessors,FieldDefs), |
192 | | append(FieldDefs,P5,RestList), |
193 | | % A list of identifiers that are used at update and accessor functions: |
194 | | append(Updates,Accessors,UpAc). |
195 | | create_optional_field_access(_Set,_Domain,_Constructor,[],Properties,Properties). |
196 | | |
197 | | find_and_remove_field_declaration(Set,Domain,Constructor,Fields,Pin,Pout) :- |
198 | | % F1 >< ... >< Fn : Set -->> Domain |
199 | | bsyntax_pattern(member(FieldProduct,total_surjection(-Set,-Domain)), SurjMemb), |
200 | | selectchk(SurjMemb,Pin,P1), |
201 | | % the injection is only defined for closed records |
202 | | % (F1 >< ...) >< Fn : Set >-> Domain |
203 | | bsyntax_pattern(member(-FieldProduct,total_injection(-Set,-Domain)), InjMemb), |
204 | | selectchk(InjMemb,P1,P2), |
205 | | access_locations(FieldProduct,Domain,Fields), |
206 | | % for each field accessor fx, there should be a declaration |
207 | | % fx : Record --> Domain, remove them |
208 | | remove_field_declarations(Fields,Set,P2,P3), |
209 | | % for each field accessor fx, there should be a predicate |
210 | | % !(v1,...,...), remove it |
211 | | remove_field_quantifiers(Fields,Constructor,P3,Pout). |
212 | | |
213 | | % find the field accessor functions |
214 | | access_locations(TExpr,TDomain,Fields) :- |
215 | | access_locations1(TExpr,TDomain,[],Fields). |
216 | | access_locations1(TExpr,TDomain,Path,Fields) :- |
217 | | get_texpr_expr(TExpr,Expr), |
218 | | access_locations2(Expr,TExpr,TDomain,Path,Fields). |
219 | | access_locations2(identifier(_Id),TExpr,TDomain,Path,[fieldloc(TExpr,TDomain,RPath)]) :- reverse(Path,RPath). |
220 | | access_locations2(direct_product(F1,F2),_TExpr,TCart,Path,Fields) :- |
221 | | get_texpr_expr(TCart,cartesian_product(D1,D2)), |
222 | | access_locations1(F1,D1,[left |Path],Fields1), |
223 | | access_locations1(F2,D2,[right|Path],Fields2), |
224 | | append(Fields1,Fields2,Fields). |
225 | | |
226 | | create_field_definition(RecordDomain,fieldloc(AccessorId1,Domain,Path),AccessorId,Equal) :- |
227 | | add_texpr_infos(AccessorId1,[record_detection(accessor)],AccessorId), |
228 | | create_texpr(equal(AccessorId,AccessorDef),pred,[],Equal), |
229 | | % the accessor function maps a record to the domain |
230 | | get_texpr_type(RecordDomain,set(RecordTupleType)), |
231 | | create_accessor_function(Domain,Path,RecordTupleType,AccessorDef). |
232 | | |
233 | | create_accessor_function(Domain,Path,RecordTupleType,AccessDefinition) :- |
234 | | % fieldAccess = {r,v | v = prj...(r)} |
235 | | create_texpr(identifier(r),RecordTupleType,[],RecordVar), |
236 | | get_texpr_type(Domain,set(FieldType)), |
237 | | create_texpr(identifier(v),FieldType,[],ValueVar), |
238 | | create_texpr(equal(ValueVar,Projection),pred,[],Equals), |
239 | | create_projection(Path,RecordVar,Projection), |
240 | | %translate:print_bexpr(Projection),nl, |
241 | | create_symbolic_comprehension_set([RecordVar,ValueVar],Equals, |
242 | | [record_detection(accessor)],AccessDefinition). |
243 | | |
244 | | create_symbolic_comprehension_set(Ids,Pred,Info,Result) :- |
245 | | create_comprehension_set(Ids,Pred,Info,CompSet), |
246 | | mark_bexpr_as_symbolic(CompSet,Result). |
247 | | |
248 | | create_projection(Path,RecordVar,ProjectionResult) :- |
249 | | get_texpr_type(RecordVar,InnerType), |
250 | | create_projection_aux(Path,InnerType,RecordVar,ProjectionResult). |
251 | | |
252 | | create_projection_aux([],_InnerType,SubExpression,SubExpression). % return record as is |
253 | | create_projection_aux([Loc|Path],couple(A,B),SubExpression,ProjectionResult) :- |
254 | | ( Loc==left -> Expr=first_of_pair(SubExpression), InnerType=A |
255 | | ; Loc==right -> Expr=second_of_pair(SubExpression),InnerType=B), |
256 | | create_texpr(Expr,InnerType,[],NewSubExpression), |
257 | | create_projection_aux(Path,InnerType,NewSubExpression,ProjectionResult). |
258 | | |
259 | | |
260 | | % remove fieldx : RecSet --> Domx |
261 | | remove_field_declarations([],_RecSet,Predicates,Predicates). |
262 | | remove_field_declarations([fieldloc(AccessorId,Domain,_Path)|Frest],RecSet,Pin,Pout) :- |
263 | | remove_field_declaration(AccessorId,Domain,RecSet,Pin,Pinter), |
264 | | remove_field_declarations(Frest,RecSet,Pinter,Pout). |
265 | | remove_field_declaration(AccessorId,Domain,RecSet,Pin,Pout) :- |
266 | | bsyntax_pattern(member(-AccessorId,total_function(-RecSet,-Domain)),Pattern), |
267 | | selectchk(Pattern,Pin,Pout). |
268 | | |
269 | | % remove !(f1,...,fn). ( f1:T1 & ... & fn:Tn => fieldx(make_rec(f1|->f2|->f3)) = fx) |
270 | | remove_field_quantifiers(Fields,Constructor,Pin,Pout) :- |
271 | | remove_field_quantifiers2(Fields,0,Constructor,Pin,Pout). |
272 | | |
273 | | remove_field_quantifiers2(Fields,N,Constructor,Pin,Pout) :- |
274 | | ( nth0(N,Fields,fieldloc(Field,_Dom,_Path)) -> |
275 | | remove_field_quantifier(Field,N,Fields,Constructor,Pin,Pinter), |
276 | | N2 is N+1, |
277 | | remove_field_quantifiers2(Fields,N2,Constructor,Pinter,Pout) |
278 | | ; |
279 | | Pin=Pout). |
280 | | remove_field_quantifier(Field,N,Fields,Constructor,Pin,Pout) :- |
281 | | make_field_identifiers(Fields,FSkels), |
282 | | get_texpr_ids(FSkels,Ids), |
283 | | nth0(N,FSkels,Ref), |
284 | | bsyntax_pattern(forall(FSkels,_,equal(function(-Field,function(-Constructor,ConstArg)),-Ref)),ForAll), |
285 | ? | select(ForAll,Pin,Pout), |
286 | | are_mappings(ConstArg,Ids),!. |
287 | | are_mappings(TExpr,Ids) :- |
288 | | are_mappings2(TExpr,Ids,[]). |
289 | | are_mappings2(TExpr) --> |
290 | | {get_texpr_expr(TExpr,Expr)}, |
291 | | are_mappings3(Expr). |
292 | | are_mappings3(identifier(Id)) --> [Id]. |
293 | | are_mappings3(couple(A,B)) --> |
294 | | are_mappings2(A), |
295 | | are_mappings2(B). |
296 | | |
297 | | make_field_identifiers([],[]). |
298 | | make_field_identifiers([FieldAcc|FRest],[Id|Irest]) :- |
299 | | make_field_identifier(FieldAcc,Id), |
300 | | make_field_identifiers(FRest,Irest). |
301 | | make_field_identifier(fieldloc(FieldAcc,_,_),Id) :- |
302 | | get_texpr_type(FieldAcc,set(couple(_,FType))), |
303 | | create_texpr(identifier(_),FType,_,Id). |
304 | | |
305 | | replace_update_definitions(Fields,RecSet,Constructor,Updates,Pin,Pout) :- |
306 | | replace_update_definitions2(Fields,Fields,RecSet,Constructor,Updates,Pin,Pout). |
307 | | replace_update_definitions2([],_Fields,_RecSet,_Constructor,[],Predicates,Predicates). |
308 | | replace_update_definitions2([fieldloc(AccessorId,FDomain,Path)|Frest],Fields,RecSet, |
309 | | Constructor,[UpdateId|URest],Pin,Pout) :- |
310 | | replace_update_definition(AccessorId,FDomain,Path,Fields,RecSet,Constructor,UpdateId,Pin,Pinter), |
311 | | replace_update_definitions2(Frest,Fields,RecSet,Constructor,URest,Pinter,Pout). |
312 | | |
313 | | % Identify two predicates of the form |
314 | | % updatex : Record ** Domain --> Record |
315 | | % !(Rec,New).(_ => updatex(Rec,New) = Constructor(..)) |
316 | | % and replace them by |
317 | | % updatex = {i,o | #(n,f1,...,fn).(i=(f1,...,fn)|->n & o=(f1,...,n,..,fn))} |
318 | | replace_update_definition(FieldAccessorId,FieldDomain,Path,Fields, |
319 | | RecSet,Constructor,Update,Pin,[Equal|Pout]) :- |
320 | | % Updatex : Record ** Domain --> Record |
321 | | bsyntax_pattern(member(Update1,total_function(cartesian_product(-RecSet,-FieldDomain),-RecSet)),Memb), |
322 | ? | select(Memb,Pin,P1), |
323 | | % !(Rec,New).(_ => Updatex(Rec,New) = Constructor(..)) |
324 | | % TODO: Implication and constructor arguments are missing |
325 | | bsyntax_pattern(forall([Rec,New],_,equal(function(-Update1,couple(-Rec,-New)), |
326 | | function(-Constructor,_))), ForAll), |
327 | ? | select(ForAll,P1,Pout), |
328 | | !, |
329 | | add_texpr_infos(Update1,[record_detection(update)],Update), |
330 | | % Updatex = {i,o | #(i,r,f1,...,fn).(i=r|->n & ) } |
331 | | create_texpr(equal(Update,Function),pred,[],Equal), |
332 | | create_update_function(FieldAccessorId,FieldDomain,Path,Fields,Function). |
333 | | |
334 | | create_update_function(FieldAccessorId,FieldDomain,Path,Fields,Function) :- |
335 | | % {i,o | #n,f1,...,fn. ( i=( (f1,...,fn) |-> n ) ) & o=(f1,...,n,...,fn) } |
336 | | get_texpr_type(FieldAccessorId,set(couple(RecType,FieldType))), |
337 | | InType = couple(RecType,FieldType), |
338 | | create_texpr(identifier(update__in),InType,[],In), |
339 | | create_texpr(identifier(update__out),RecType,[],Out), |
340 | | create_symbolic_comprehension_set([In,Out],Pred,[record_detection(update)],CompSet), |
341 | | |
342 | | % #(n,f1,...,fn).(...&...) |
343 | | %create_exists([NewValue|FieldVars],ExistsPred,Pred), % moved below; see comment |
344 | | create_var_for_field(FieldAccessorId,FieldDomain,'f$n__',NewValue), |
345 | | maplist(create_field_var,Fields,FieldVars), |
346 | | create_texpr(conjunct(InEqual,OutEqual),pred,[],ExistsPred), |
347 | | |
348 | | % i = ( (f1,...,fn) |-> n ) |
349 | | create_texpr(equal(In,InPair),pred,[],InEqual), |
350 | | create_texpr(couple(OldFields,NewValue),InType,[],InPair), |
351 | | create_record_tuple(Fields,FieldVars,OldFields), |
352 | | |
353 | | % o = (f1,...,n,...,fn) |
354 | | create_texpr(equal(Out,NewFields),pred,[],OutEqual), |
355 | | update_record_tuple(Path,NewValue,OldFields,NewFields), |
356 | | |
357 | | % the creation of the exists has to be deferred until here as used identifiers are computed: |
358 | | create_exists([NewValue|FieldVars],ExistsPred,Pred), |
359 | | %translate:print_bexpr(CompSet),nl, |
360 | | |
361 | | % we need to call the clean-up functions because exists(...) needs more |
362 | | % information about which identifier are used and that information |
363 | | % is added by clean_up/3. |
364 | | clean_up(CompSet,[],Function). |
365 | | |
366 | | % for a fields, create an identifier with the corresponding type |
367 | | create_field_var(fieldloc(AccessorId,Domain,_Path),TId) :- |
368 | | create_var_for_field(AccessorId,Domain,'f$__',TId). |
369 | | create_var_for_field(AccessorId,Domain,Prefix,TId) :- |
370 | | get_texpr_id(AccessorId,FId), |
371 | | get_texpr_type(Domain,set(Type)), |
372 | | atom_concat(Prefix,FId,VId), |
373 | | create_texpr(identifier(VId),Type,[],TId). |
374 | | |
375 | | % for a list of fields and identifiers f1,...,fn, create a tuple (f1,...,fn) |
376 | | create_record_tuple([],[],_). |
377 | | create_record_tuple([fieldloc(_,_,Path)|Frest],[FieldVar|Vrest],Tuple) :- |
378 | | create_record_tuple2(Path,FieldVar,Tuple), |
379 | | create_record_tuple(Frest,Vrest,Tuple). |
380 | | create_record_tuple2([],Expr,Expr). |
381 | | create_record_tuple2([Loc|Path],Expr,Tuple) :- |
382 | | ( Loc==left -> create_texpr(couple(Sub,_),couple(SubType,_),[],Tuple) |
383 | | ; Loc==right -> create_texpr(couple(_,Sub),couple(_,SubType),[],Tuple)), |
384 | | get_texpr_type(Sub,SubType), |
385 | | create_record_tuple2(Path,Expr,Sub). |
386 | | |
387 | | % in a tuple representing a record, replace one field |
388 | | update_record_tuple([],NewValue,_OldFields,NewValue). |
389 | | update_record_tuple([Loc|Path],NewValue,OldFields,NewFields) :- |
390 | | get_texpr_expr(OldFields,couple(A,B)), |
391 | | get_texpr_type(OldFields,Type), |
392 | | create_texpr(couple(X,Y),Type,[],NewFields), |
393 | | ( Loc==left -> X=NewSub,A=OldSub,Y=B |
394 | | ; Loc==right -> Y=NewSub,B=OldSub,X=A), |
395 | | update_record_tuple(Path,NewValue,OldSub,NewSub). |
396 | | |
397 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
398 | | % replace_type_in_machine(+T1,+T2,+Domain,+OldMachine,-NewMachine): |
399 | | % T1: The name of the global set that refers to the old type |
400 | | % T2: The new type that should replace global(T1) |
401 | | % Domain: A B expression that describes the domain of the record, used to |
402 | | % constrain introduced identifiers |
403 | | % OldMachine: The B machine before replacement |
404 | | % NewMachine: The resulting B machine |
405 | | replace_type_in_machine(T1,T2,Domain,OldMachine,NewMachine) :- |
406 | | extract_constants_that_are_types(OldMachine,RTT), |
407 | | (is_just_type(Domain,RTT) -> Domain2 = simple ; Domain2 = Domain), |
408 | | % add predicates to constrain variables or constants, if needed |
409 | | foldl(add_type_constraint_to_section(T1,Domain2), |
410 | | [decl([abstract_constants,concrete_constants],properties), |
411 | | decl([abstract_variables,concrete_variables],invariant)], |
412 | | OldMachine,Machine1), |
413 | | % replace the type in the expressions of the machine (identifier declarations |
414 | | % can be treated as expressions, too) |
415 | | foldl(replace_type_in_machine2(T1,T2,Domain2), |
416 | | [abstract_constants,concrete_constants, |
417 | | abstract_variables,concrete_variables, |
418 | | promoted, unpromoted, |
419 | | constraints, properties, invariant, assertions, |
420 | | initialisation, operation_bodies], |
421 | | Machine1,NewMachine). |
422 | | replace_type_in_machine2(T1,T2,Domain,Sec,In,Out) :- |
423 | | select_section_texprs(Sec,Old,New,In,Out), |
424 | | replace_type_in_exprs(Old,T1,T2,Domain,New). |
425 | | replace_type_in_expr(TExpr,T1,T2,Domain,New) :- |
426 | | % if identifiers (e.g. i) are declared in an expression (like in forall), we may have |
427 | | % to add an predicate (e.g. i:1..4) |
428 | | ( Domain=simple -> % the domain is a whole type, no additional constraint needed |
429 | | TExpr2 = TExpr |
430 | ? | ; has_declared_identifier(TExpr,Ids),contains_identifier_with_type(Ids,T1) -> |
431 | | % identifiers are introduced and one of them contains the record type |
432 | | add_type_declarations(TExpr,global(T1),Domain,Ids,TExpr2) |
433 | | ; % no identifiers introduced that use the record type |
434 | | TExpr = TExpr2 |
435 | | ), |
436 | | create_texpr(Expr,EType,Info,TExpr2), % de-assemble old expression |
437 | | replace_type(EType,T1,T2,NType), % replace type global(T1) by T2 in Type |
438 | | create_texpr(NExpr2,NType,Info,New), % assemble new expression with new type |
439 | | syntaxtransformation(Expr,Subs,_Names,NSubs,NExpr1), % continue with sub-expressions |
440 | | replace_type_in_exprs(Subs,T1,T2,Domain,NSubs), |
441 | | replace_hidden_type_paras(NExpr1,T1,T2,NExpr2). |
442 | | replace_type_in_exprs([],_T1,_T2,_Domain,[]). |
443 | | replace_type_in_exprs([Sub|Irest],T1,T2,Domain,[NSub|Orest]) :- |
444 | | replace_type_in_expr(Sub,T1,T2,Domain,NSub), |
445 | | replace_type_in_exprs(Irest,T1,T2,Domain,Orest). |
446 | | |
447 | | |
448 | | replace_hidden_type_paras(freetype_constructor(A,Case,Expr),T1,T2,freetype_constructor(RA,Case,Expr)) :- !, |
449 | | replace_freetype_paras(A,T1,T2,RA). |
450 | | replace_hidden_type_paras(freetype_destructor(A,Case,Expr),T1,T2,freetype_destructor(RA,Case,Expr)) :- !, |
451 | | replace_freetype_paras(A,T1,T2,RA). |
452 | | replace_hidden_type_paras(E,_,_,E). |
453 | | |
454 | | |
455 | | contains_identifier_with_type(Ids,T1) :- |
456 | | get_texpr_type(TId,Type), |
457 | ? | member(TId,Ids), |
458 | ? | contains_type(global(T1),Type). |
459 | | |
460 | | add_type_constraint_to_section(_ToReplace,simple,_Decl,In,Out) :- |
461 | | !,In=Out. |
462 | | add_type_constraint_to_section(ToReplace,Domain,decl(IdSecs,PredSec),In,Out) :- |
463 | | maplist(aux_get_section(In),IdSecs,IdsL),append(IdsL,Ids1), |
464 | | % do not add constraints for accessor and update functions for records: |
465 | | exclude(is_record_detection_expr,Ids1,Ids), |
466 | ? | ( contains_identifier_with_type(Ids,ToReplace) -> |
467 | | create_type_predicate(global(ToReplace),Domain,Ids,P), |
468 | | ( is_truth(P) -> In=Out % Trivial, nothing to do |
469 | | ; % Add the type constraint to the relevant predicate section |
470 | | select_section(PredSec,OldPred,NewPred,In,Out), |
471 | | conjunct_predicates([P,OldPred],NewPred) |
472 | | ) |
473 | | ; % Not applicable, nothing to do |
474 | | In=Out). |
475 | | aux_get_section(Machine,Sec,Content) :- % just to rearrange the parameters |
476 | | get_section(Sec,Machine,Content). |
477 | | |
478 | | is_record_detection_expr(Expr) :- |
479 | | get_texpr_info(Expr,Info),memberchk(record_detection(_),Info). |
480 | | |
481 | | replace_type(global(Type),Type,NewType,Result) :- !, NewType=Result. |
482 | | replace_type(couple(A,B),T1,T2,couple(RA,RB)) :- |
483 | | !, replace_type(A,T1,T2,RA), replace_type(B,T1,T2,RB). |
484 | | replace_type(set(A),T1,T2,set(RA)) :- |
485 | | !, replace_type(A,T1,T2,RA). |
486 | | replace_type(seq(A),T1,T2,seq(RA)) :- |
487 | | !, replace_type(A,T1,T2,RA). |
488 | | replace_type(freetype(A),T1,T2,freetype(RA)) :- |
489 | | !, replace_freetype_paras(A,T1,T2,RA). |
490 | | replace_type(T,_,_,T). % just skip types like integer, string, etc. |
491 | | |
492 | | % replace type inside freetype type parameters (relevant for Event-B polymorphic theories) |
493 | | replace_freetype_paras(A,T1,T2,RA) :- |
494 | | get_freetype_type_parameters(A,Func,TypeParas), |
495 | | maplist(mreplace(T1,T2),TypeParas,NewTypeParas), |
496 | | get_freetype_type_parameters(RA,Func,NewTypeParas). |
497 | | |
498 | | mreplace(T1,T2,Type,ReplacedType) :- replace_type(Type,T1,T2,ReplacedType). |
499 | | |
500 | | contains_type(Type,couple(A,_)) :- contains_type(Type,A). |
501 | | contains_type(Type,couple(_,B)) :- contains_type(Type,B). |
502 | ? | contains_type(Type,set(A)) :- contains_type(Type,A). |
503 | | contains_type(Type,seq(A)) :- contains_type(Type,A). |
504 | | contains_type(Type,freetype(A)) :- get_freetype_type_parameters(A,_,TypeParas), |
505 | | member(TP,TypeParas), contains_type(Type,TP). |
506 | | contains_type(Type,Type). |
507 | | |
508 | | add_type_declarations(TExpr,ToReplace,Set,Ids,Result) :- |
509 | | create_type_predicate(ToReplace,Set,Ids,P), |
510 | | ( is_truth(P) -> Result = TExpr % Nothing to do |
511 | | ; add_declaration_for_identifier(TExpr,P,Result)). |
512 | | |
513 | | create_type_predicate(ToReplace,Set,Ids,P) :- |
514 | | include(expr_contains_type(ToReplace),Ids,RelevantIds), |
515 | | maplist(create_type_membership(ToReplace,Set),RelevantIds,Preds), |
516 | | conjunct_predicates(Preds,P1), |
517 | | clean_up(P1,[],P). |
518 | | |
519 | | expr_contains_type(ToReplace,TExpr) :- |
520 | | get_texpr_type(TExpr,Type), |
521 | ? | contains_type(ToReplace,Type). |
522 | | |
523 | | create_type_membership(ToReplace,Set,TId,Membership) :- |
524 | | get_texpr_type(TId,Type), |
525 | | create_set_for_type(Type,ToReplace,Set,GenSet), |
526 | | create_texpr(member(TId,GenSet),pred,[],Membership). |
527 | | |
528 | | create_set_for_type(T,ToReplace,Set,R) :- |
529 | | T=ToReplace,!,R=Set. |
530 | | create_set_for_type(couple(A,B),ToReplace,Set,R) :- !, |
531 | | create_set_for_type(A,ToReplace,Set,SA), |
532 | | create_set_for_type(B,ToReplace,Set,SB), |
533 | | ( is_typeset(SA),is_typeset(SB) -> create_typeset(couple(A,B),R) |
534 | | ; create_texpr(cartesian_product(SA,SB),set(couple(A,B)),[],R)). |
535 | | create_set_for_type(set(A),ToReplace,Set,R) :- !, |
536 | | create_set_for_type(A,ToReplace,Set,SA), |
537 | | ( is_typeset(SA) -> create_typeset(set(A),R) |
538 | | ; create_texpr(pow_subset(SA),set(set(A)),[],R)). |
539 | | create_set_for_type(seq(A),ToReplace,Set,R) :- !, |
540 | | % for our purpose, we can ignore other constraints on sequences |
541 | | create_set_for_type(set(couple(integer,A)),ToReplace,Set,R). |
542 | | create_set_for_type(Type,_ToReplace,_Set,R) :- create_typeset(Type,R). |
543 | | is_typeset(Expr) :- get_texpr_expr(Expr,typeset). |
544 | | |
545 | | create_typeset(Type,Set) :- |
546 | | create_texpr(typeset,set(Type),[],Set). |
547 | | |
548 | | % extract_constants_that_are_types(+Machine,-RTT): |
549 | | % gives a list of identifiers of those constants that are defined to be the whole type. |
550 | | % This works only for constants that are defined with an equality, e.g. |
551 | | % c = BOOL**BOOL |
552 | | extract_constants_that_are_types(Machine,RTT) :- |
553 | | get_all_equalities_from_machine(Machine,Equalities), |
554 | | extract_type_references(Equalities,[],RTT). |
555 | | % get_all_equalities_from_properties(+Machine,-Equalities): |
556 | | % extract all equalities from the properties. |
557 | | % Equalities is a list of terms equal(Id,Expr) where Id denotes a |
558 | | % constant and Expr the expr on the other side of the equation. |
559 | | get_all_equalities_from_machine(Machine,Equalities) :- |
560 | | get_all_constants_from_machine(Machine,Constants), |
561 | | get_section(properties,Machine,Properties), |
562 | | conjunction_to_list(Properties,PList), |
563 | | convlist(is_equality_left(Constants), PList,LEqualities), |
564 | | convlist(is_equality_right(Constants),PList,REqualities), |
565 | | append(LEqualities,REqualities,Equalities). |
566 | | get_all_constants_from_machine(Machine,Constants) :- |
567 | | get_section(abstract_constants,Machine,AC), |
568 | | get_section(concrete_constants,Machine,CC), |
569 | | append(AC,CC,Constants). |
570 | | % extract_type_references(+Equalities,+RTT,-Result): |
571 | | % Equalities: A list of term of the form equal(Id,Expr), see get_all_equalities_from_properties/2 |
572 | | % RTT: An ordered list of already known constants that refer to types |
573 | | % Result: A ordered list of constants (their id, no types) that refer to types |
574 | | % The predicate works recursively because a definition like a = B**INTEGER can denote a |
575 | | % type if B also denotes a type which must be detected first. |
576 | | extract_type_references(Equalities,RTT,Result) :- |
577 | | convlist(equality_is_typedef(RTT),Equalities,ReferencesToTypes), |
578 | | list_to_ord_set(ReferencesToTypes,Sorted), |
579 | | ord_subtract(Sorted,RTT,Unique), |
580 | | ( Unique = [] -> Result=RTT % no new definitions found |
581 | | ; % new definitions found, continue recursively |
582 | | ord_union(Unique,RTT,NewRTT), |
583 | | extract_type_references(Equalities,NewRTT,Result) |
584 | | ). |
585 | | % Pred is of form Id=Expr where Id is a constant |
586 | | is_equality_left(Constants,Pred,equal(Id,Expr)) :- |
587 | | get_texpr_expr(Pred,equal(TId,Expr)), is_equality_aux(TId,Constants,Id). |
588 | | % Pred is of form Expr=Id where Id is a constant |
589 | | is_equality_right(Constants,Pred,equal(Id,Expr)) :- |
590 | | get_texpr_expr(Pred,equal(Expr,TId)), is_equality_aux(TId,Constants,Id). |
591 | | is_equality_aux(TId,Constants,Id) :- |
592 | | get_texpr_id(TId,Id),get_texpr_id(Constant,Id),memberchk(Constant,Constants). |
593 | | equality_is_typedef(RTT,equal(Id,Expr),Id) :- |
594 | | is_just_type(Expr,RTT). |