1 % (c) 2014-2023 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(before_after_predicates, [tcltk_before_after_predicate_for_operation/2,
6 before_after_predicate_for_operation/2,
7 before_after_predicate_for_operation_no_equalities/5,
8 before_after_predicate_with_equalities/3,
9 before_after_predicate_list_conjunct_with_equalities/3,
10 before_after_predicate_list_disjunct_with_equalities/3,
11 before_after_predicate_no_equalities/3,
12 add_missing_equalities/3,
13 prime_bexpr_for_ids/4,
14 list_renamings_to_primed_vars/2]).
15
16 :- use_module(probsrc(module_information),[module_info/2]).
17 :- module_info(group,cbc).
18 :- module_info(description,'This module computes before-after-predicates of B and Event-B actions.').
19 % NOTE: for the moment it assumes no clashes between operation parameters and variables, constants, ...
20
21 :- use_module(library(lists)).
22
23 :- use_module(probsrc(bsyntaxtree), [get_texpr_expr/2,
24 get_texpr_id/2,
25 is_truth/1,
26 safe_create_texpr/3,
27 get_texpr_type/2,
28 conjunct_predicates/2,
29 conjunction_to_list/2,
30 create_implication/3,
31 create_couple/3,
32 disjunct_predicates/2,
33 find_typed_identifier_uses/2,
34 rename_bt/3,
35 create_exists/3, create_or_merge_exists/3,
36 create_negation/2]).
37 :- use_module(probsrc(error_manager), [add_error_fail/3]).
38 :- use_module(probsrc(bmachine),[b_get_machine_operation/4,
39 b_get_machine_variables/1,
40 b_get_machine_constants/1]).
41 %:- use_module(probsrc(specfile,[animation_minor_mode/1]).
42 :- use_module(probsrc(tools),[split_list/4]).
43 :- use_module(probsrc(translate),[translate_bexpression/2]).
44 :- use_module(symbolic_model_checker(predicate_handling),
45 [prime_id_atom/2, remove_prime_id_atom/2, is_primed_id_atom/1,
46 prime_typed_identifier/2,remove_prime_typed_identifier/2,
47 is_primed_typed_identifier/1]).
48
49
50 tcltk_before_after_predicate_for_operation(OpName,list(Res)) :-
51 before_after_predicate_for_operation(OpName,BAPredicate),
52 %translate:print_bexpr(BAPredicate),nl,
53 conjunction_to_list(BAPredicate,LP),
54 maplist(translate_bexpression,LP,Res).
55
56 before_after_predicate_for_operation(OpName,BAPredicate) :-
57 ? b_get_machine_operation(OpName,_Results,_Parameters,OpBody),
58 %print(op(OpName)),nl,
59 b_get_machine_variables(Variables),
60 %b_get_machine_constants(Constants), append(Variables,Constants,VarsAndConsts),
61 before_after_predicate_with_equalities(OpBody,Variables,BAPredicate).
62
63 % ----------------
64
65 :- use_module(probsrc(translate), [generate_typing_predicates/2]).
66
67 % without equalities for UNCHANGED vars
68 before_after_predicate_for_operation_no_equalities(OpName,Parameters,Results,BAPredicate,ChangedIds) :-
69 b_get_machine_operation(OpName,Results,Parameters,OpBody),
70 % TO DO: rename parameters if clashing
71 generate_typing_predicates(Parameters,Typings1),
72 generate_typing_predicates(Results,Typings2),
73 before_after_predicate_no_equalities_tl4(top_level,OpBody,BAPred,ChangedIds),
74 append([Typings1,Typings2,[BAPred]],LP),
75 conjunct_predicates(LP,BAPredicate).
76 % predicate contains new open identifiers: Results and Parameters
77
78 % :- before_after_predicates:before_after_predicate_for_operation_no_equalities(Op,_,_,P,C), format('Op ~w (writing ~w):~n',[Op,C]), translate:print_bexpr(P),nl,nl,fail.
79
80
81
82 :- use_module(probsrc(bsyntaxtree),[transform_bexpr_with_scoping/3]).
83 % prime those variables inside a formula which are in the ChangedIds list
84 prime_bexpr_for_ids(TExpr,ChangedIds,PrimedTExpr,ChangesPerformed) :-
85 sort(ChangedIds,SortedCIds),
86 transform_bexpr_with_scoping(before_after_predicates:prime_changed_id(SortedCIds,Changed),TExpr,PrimedTExpr),
87 (var(Changed) -> ChangesPerformed=false ; ChangesPerformed=true).
88
89 :- use_module(library(ordsets),[ord_member/2,ord_nonmember/2]).
90 :- use_module(probsrc(btypechecker),[prime_id/2]).
91 prime_changed_id(ChangedIds,Changed,b(identifier(ID),Type,Info),Result,LocalIds) :-
92 ord_member(ID,ChangedIds),
93 ord_nonmember(ID,LocalIds), % The ID is not overriden by a locally quantified variable
94 prime_id(ID,PrimedID),
95 Changed=true, % change was performed
96 Result = b(identifier(PrimedID),Type,Info).
97
98 % ----------------
99
100 %! before_after_predicate_with_equalities(+Action,+AllIdentifiers,-Predicate)
101 % these add missing y' = y equalities for unchanged variables
102 before_after_predicate_with_equalities(Action,AllIdentifiers,Predicate) :-
103 before_after_predicate_list_conjunct_with_equalities([Action],AllIdentifiers,Predicate).
104 before_after_predicate_list_conjunct_with_equalities(Actions,AllIdentifiers,BAPredicateWithEq) :-
105 maplist(before_after_predicate_no_equalities_tl3(top_level),Actions,AllPredicates),
106 conjunct_predicates(AllPredicates,BAPredicate),
107 add_missing_equalities(AllIdentifiers,BAPredicate,BAPredicateWithEq).
108 before_after_predicate_list_disjunct_with_equalities(Actions,AllIdentifiers,BAPredicate) :-
109 maplist(before_after_predicate_no_equalities_tl3(top_level),Actions,AllPredicates),
110 maplist(add_missing_equalities(AllIdentifiers),AllPredicates,BAPredicates),
111 disjunct_predicates(BAPredicates,BAPredicate).
112
113 % ----------------
114
115 % does not add missing y' = y equalities for unchanged variables
116 % top-level version: has a special rule for PRE
117 before_after_predicate_no_equalities_tl3(TopLevel,Action,Predicate) :-
118 before_after_predicate_no_equalities_tl4(TopLevel,Action,Predicate,_).
119 before_after_predicate_no_equalities_tl4(TopLevel,Action,Predicate,ChangedIDs) :-
120 get_texpr_expr(Action,Expr),
121 before_after_predicate_no_equalities_top_level_aux(Expr,Predicate,ChangedIDs,TopLevel).
122
123 :- use_module(probsrc(preferences),[get_preference/2]).
124 before_after_predicate_no_equalities_top_level_aux(precondition(Pre,Body),Predicate,ChangedIDs,top_level) :-
125 get_preference(treat_outermost_pre_as_select,true),
126 !,
127 before_after_predicate_no_equalities(Body,BABody,ChangedIDs),
128 conjunct_predicates([Pre,BABody],Predicate). % May sometimes have to be an implication (depending on how BA-predicate is used) !
129 before_after_predicate_no_equalities_top_level_aux(Expr,Pred,ChangedIDs,_) :-
130 before_after_predicate_no_equalities_aux(Expr,Pred,ChangedIDs).
131
132 % ----------------
133
134 % does not add missing y' = y equalities for unchanged variables
135 before_after_predicate_no_equalities(Action,Predicate,ChangedIDs) :-
136 get_texpr_expr(Action,Expr),
137 before_after_predicate_no_equalities_aux(Expr,Predicate,ChangedIDs).
138
139 get_changed_id(b(function(FUN,_),_,_),ID) :- !, get_changed_id(FUN,ID).
140 get_changed_id(ID,ID).
141
142 % TO DO: return list of Predicates rather than single predicate; avoid conjunct_predicates and create flat conj at end
143 before_after_predicate_no_equalities_aux(assign_single_id(Id,Pred),Predicate,ChangedIDs) :- !,
144 before_after_predicate_no_equalities_aux(assign([Id],[Pred]),Predicate,ChangedIDs).
145 before_after_predicate_no_equalities_aux(assign(Ids,Preds),Predicate,ChangedIds) :- !,
146 maplist(get_changed_id,Ids,ChangedIds),
147 maplist(ba_predicate_equal,Ids,Preds,Predicates),
148 conjunct_predicates(Predicates,Predicate).
149 before_after_predicate_no_equalities_aux(becomes_element_of(Ids,Set),Predicate,Ids) :- !,
150 maplist(ba_predicate_member(Set),Ids,Predicates),
151 % THIS IS WRONG: it asserts that each Id is an element of Set, but the tuple Ids is an element!
152 conjunct_predicates(Predicates,Predicate). % should we generate Set /= {} => Predicate
153 before_after_predicate_no_equalities_aux(becomes_such(Ids,Pred),Predicate,Ids) :- !,
154 ba_predicate_becomes_such(Ids,Pred,Predicate).
155 before_after_predicate_no_equalities_aux(choice(Choices),Predicate,ChangedIDs) :- !,
156 maplist(before_after_predicate_no_equalities,Choices,BAChoices,Changes),
157 append(Changes,ChangedIDs),
158 maplist(add_missing_equalities(ChangedIDs),BAChoices,BAChoicesWithEqualities),
159 disjunct_predicates(BAChoicesWithEqualities,Predicate).
160 before_after_predicate_no_equalities_aux(precondition(Pre,Body),Predicate,ChangedIDs) :- !,
161 before_after_predicate_no_equalities(Body,BABody,ChangedIDs),
162 create_implication(Pre,BABody,Predicate).
163 before_after_predicate_no_equalities_aux(witness_then(Pre,Body),Predicate,ChangedIDs) :- !,
164 before_after_predicate_no_equalities_aux(assertion(Pre,Body),Predicate,ChangedIDs). % TODO: check if ok, or if we should use SELECT
165 before_after_predicate_no_equalities_aux(assertion(Pre,Body),Predicate,ChangedIDs) :-
166 !,
167 before_after_predicate_no_equalities(Body,BABody,ChangedIDs),
168 create_implication(Pre,BABody,Predicate1),
169 create_negation(Pre,NotPre),
170 add_missing_equalities(ChangedIDs,b(truth,pred,[]),NotPrePredicate),
171 create_implication(NotPre,NotPrePredicate,Predicate2),
172 conjunct_predicates([Predicate1,Predicate2],Predicate).
173 before_after_predicate_no_equalities_aux(select_when(Pre,Body),Predicate,ChangedIDs) :- !,
174 before_after_predicate_no_equalities(Body,BABody,ChangedIDs),
175 conjunct_predicates([Pre,BABody],Predicate).
176 before_after_predicate_no_equalities_aux(select(Whens),Predicate,ChangedIDs) :- !,
177 maplist(before_after_predicate_no_equalities,Whens,WhensPredicates,Changes),
178 append(Changes,ChangedIDs),
179 maplist(add_missing_equalities(ChangedIDs),WhensPredicates,WhensPredicatesWithEqualities),
180 disjunct_predicates(WhensPredicatesWithEqualities,Predicate).
181 before_after_predicate_no_equalities_aux(select(Whens,Else),Predicate,ChangedIDs) :- !,
182 maplist(before_after_predicate_no_equalities,Whens,WhensPredicates,Changes),
183 maplist(add_missing_equalities(ChangedIDs),WhensPredicates,WhensPredicatesWithEqualities),
184 maplist(get_select_conditions,Whens,SelectConditions),
185 disjunct_predicates(SelectConditions,ElseTemp),
186 create_negation(ElseTemp,ElseLHS),
187 before_after_predicate_no_equalities(Else,ElseRHS,ElseChangedIDs),
188 append([ElseChangedIDs|Changes],ChangedIDs),
189 conjunct_predicates([ElseLHS,ElseRHS],ElsePred),
190 add_missing_equalities(ChangedIDs,ElsePred,ElsePredWithEqualities),
191 disjunct_predicates([ElsePredWithEqualities|WhensPredicatesWithEqualities],Predicate).
192 % TODO: case, handle while
193 before_after_predicate_no_equalities_aux(sequence(ListOfSteps),Predicate,ChangedIDs) :- !,
194 ba_predicate_sequence(ListOfSteps,Predicate,ChangedIDs).
195 before_after_predicate_no_equalities_aux(if(IfList),Predicate,ChangedIDs) :- !,
196 ba_predicate_if(IfList,Predicate,ChangedIDs).
197 before_after_predicate_no_equalities_aux(let(P,D,B),Predicate,ChangedIDs) :- !,
198 before_after_predicate_no_equalities_aux(any(P,D,B),Predicate,ChangedIDs). % TO DO: create let_predicate
199 before_after_predicate_no_equalities_aux(any(Parameters,PreCond,Body),Predicate,ChangedIDs) :- !,
200 before_after_predicate_no_equalities(Body,BABody,ChangedIDs),
201 conjunct_predicates([PreCond,BABody],Inner),
202 create_or_merge_exists(Parameters,Inner,Predicate).
203 before_after_predicate_no_equalities_aux(parallel(P),Predicate,ChangedIDs) :- !,
204 maplist(before_after_predicate_no_equalities,P,Preds,Changes),
205 append(Changes,ChangedIDs),
206 conjunct_predicates(Preds,Predicate).
207 before_after_predicate_no_equalities_aux(skip,Predicate,[]) :- !,
208 Predicate = b(truth,pred,[]).
209 before_after_predicate_no_equalities_aux(var(Parameters,Body),Predicate,ChangedIDs) :- !,
210 before_after_predicate_no_equalities(Body,BABody,ChangedIDs),
211 create_or_merge_exists(Parameters,BABody,Predicate).
212 before_after_predicate_no_equalities_aux(rlevent(_Name,_Section,_Stat,_Parameters,Guards,_Thm,Actions,_VWit,_PWit,_Unmod,_AbsEvents),Predicate,ChangedIDs) :- !,
213 maplist(before_after_predicate_no_equalities,Actions,BAActions,Changes),
214 append(Changes,ChangedIDs),
215 conjunct_predicates([Guards|BAActions],Predicate). % should probably be an implication? see precondition.
216 before_after_predicate_no_equalities_aux(operation_call(OpId,[],[]),Predicate,ChangedIDs) :- !,
217 get_texpr_id(OpId,op(OpName)),
218 b_get_machine_operation(OpName,[],[],OpBody),
219 before_after_predicate_no_equalities(OpBody,Predicate,ChangedIDs).
220 before_after_predicate_no_equalities_aux(Expr,Predicate,ChangedIDs) :-
221 add_error_fail(before_after_predicate,'before-after-predicate implementation missing: ',[Expr,Predicate,ChangedIDs]).
222
223 get_select_conditions(b(select_when(Condition,_),_,_),Condition).
224
225 ba_predicate_equal(LHS,RHS,Res) :-
226 % LHS := RHS
227 ? get_override(LHS,RHS,TPred), !,
228 %nl,translate:print_bexpr(TPred),nl,
229 Res=TPred.
230
231 :- use_module(probsrc(error_manager),[add_internal_error/2]).
232 % get override Term for f(X)(Y)... := RHS
233 get_override(b(function(FUN,ARG),_Type,_Info),ExprSoFar,Res) :- !, % FUN(ARG) := RHS
234 %print(fun(FUN)),nl, translate:print_bexpr(ExprSoFar),nl,
235 get_texpr_type(FUN,FType),
236 create_couple(ARG,ExprSoFar,Update), % ARG|-> ExprSoFar
237 safe_create_texpr(set_extension([Update]),FType,UpdateSET), % {ARG|->ExprSoFar}
238 construct_override(FUN,UpdateSET,Override), % FUN <+ {ARG |-> {...}}
239 get_override(FUN,Override,Res). % Pass Override as new RHS in case FUN itself is a function application
240 get_override(FUNID,OverrideExpr,TPred) :-
241 get_texpr_id(FUNID,_ID), %print(base(ID)),nl,
242 prime_typed_identifier(FUNID,FUNPrimeId),
243 safe_create_texpr(equal(FUNPrimeId,OverrideExpr),pred,TPred). % FUN' = FUN <+ {ARG |-> RHS}
244 get_override(FUNID,OverrideExpr,TPred) :-
245 add_internal_error('Cannot compute override: ',get_override(FUNID,OverrideExpr,TPred)),fail.
246
247 construct_override(FunID,Update,Override) :-
248 get_texpr_type(Update,Type),
249 Override = b(overwrite(FunID,Update),Type,[]).
250
251 ba_predicate_member(Set,Id,TPred) :-
252 % prime identifiers on the lhs
253 prime_typed_identifier(Id,PrimedId),
254 safe_create_texpr(member(PrimedId,Set),pred,TPred).
255
256 ba_predicate_becomes_such(Ids,Pred,Predicate) :-
257 list_renamings_to_primed_vars(Ids,RenamingList),
258 rename_bt(Pred,RenamingList,Predicate).
259
260 list_renamings_to_primed_vars([],[]).
261 list_renamings_to_primed_vars([Var|Vars],[rename(VarId,PrimedId)|T]) :-
262 get_texpr_id(Var,VarId),
263 prime_id_atom(VarId,PrimedId),
264 list_renamings_to_primed_vars(Vars,T).
265
266 % add missing UNCHANGED equalities (x'=x for all variables that are unchanged)
267 add_missing_equalities([],PredWithoutEqualities,Res) :- !, Res=PredWithoutEqualities.
268 add_missing_equalities(AllVariables,PredWithoutEqualities,PredWithEqualities) :-
269 find_typed_identifier_uses(PredWithoutEqualities,UsedIdentifiers),
270 exclude(primed_var_is_used(UsedIdentifiers),AllVariables,VariablesNotOccuringPrimed),
271 maplist(ba_predicate_equal,VariablesNotOccuringPrimed,VariablesNotOccuringPrimed,EqualityPredicates),
272 conjunct_predicates([PredWithoutEqualities|EqualityPredicates],PredWithEqualities).
273
274 :- use_module(probsrc(typing_tools),[normalize_type/2]).
275 primed_var_is_used(UsedIdentifiers,Identifier) :-
276 prime_typed_identifier(Identifier,PrimedIdentifier),
277 remove_infos_and_norm(PrimedIdentifier,PrimedIdentifierWithoutInfos),
278 ? member(PrimedIdentifierWithoutInfos,UsedIdentifiers).
279
280 remove_infos_and_norm(b(E,Type,_),b(E,NType,_)) :- normalize_type(Type,NType).
281
282 add_else_if_missing([],[DefaultElse]) :-
283 safe_create_texpr(if_elsif(b(truth,pred,[]),b(skip,subst,[])),subst,DefaultElse).
284 add_else_if_missing([If|Ifs],[If|Ifs]) :-
285 get_texpr_expr(If,if_elsif(Test,_)),
286 is_truth(Test), !.
287 add_else_if_missing([If|Ifs],[If|MoreIfs]) :-
288 add_else_if_missing(Ifs,MoreIfs).
289
290 ba_predicate_if(IF,BA,AllChangedIDs) :-
291 add_else_if_missing(IF,IFWithElse),
292 maplist(ba_predicate_if_aux,IFWithElse,BAPreds,ChangedIDs),
293 append(ChangedIDs,AllChangedIDs),
294 ba_predicate_if(IFWithElse,BAPreds,AllChangedIDs,BA).
295
296 ba_predicate_if([IF|IFs],[BAPred|BAPreds],IDs,BA) :-
297 get_texpr_expr(IF,if_elsif(Test,_)),
298 % if-part
299 add_missing_equalities(IDs,BAPred,InnerBAPredIf),
300 (is_truth(Test)
301 -> InnerBAPredIf = BAPredIf
302 ; safe_create_texpr(implication(Test,InnerBAPredIf),pred,BAPredIf)),
303 % if there are further else/elseif parts, create pos
304 (ba_predicate_if(IFs,BAPreds,IDs,ElsePred)
305 -> create_negation(Test,NegTest),
306 safe_create_texpr(implication(NegTest,ElsePred),pred,BAPredElse),
307 conjunct_predicates([BAPredIf,BAPredElse],BA)
308 ; BA = BAPredIf).
309
310 ba_predicate_if_aux(IF,BAPred,ChangedIDs) :-
311 get_texpr_expr(IF,if_elsif(_,Body)),
312 before_after_predicate_no_equalities(Body,BAPred,ChangedIDs).
313
314 ba_predicate_sequence([Last],Predicate,ChangedIDs) :- !,
315 before_after_predicate_no_equalities(Last,Predicate,ChangedIDs).
316 ba_predicate_sequence([First|SomeMore],Predicate,ChangedIDs) :-
317 before_after_predicate_no_equalities(First,FirstPred,ChangesFirst),
318 find_typed_identifier_uses(FirstPred,TypedIdsOfFirst),
319 % get primed typed identifiers
320 include(is_primed_typed_identifier,TypedIdsOfFirst,PrimedTypedIdsOfFirst),
321 % rename them if they are already used in SomeMore
322 ba_predicate_sequence(SomeMore,SecondPred,FurtherChanges),
323 maplist(find_free_id_by_priming(SecondPred),PrimedTypedIdsOfFirst,UnusedPrimedTypedIds),
324 maplist(create_id_renaming,PrimedTypedIdsOfFirst,UnusedPrimedTypedIds,RenamingOfFirst),
325 rename_bt(FirstPred,RenamingOfFirst,FirstPredAfterReplacement),
326 % rename unprimed ids in SecondPred, which are written by FirstPred
327 maplist(remove_prime_typed_identifier,PrimedTypedIdsOfFirst,UnprimedTypedIds),
328 maplist(create_id_renaming,UnprimedTypedIds,UnusedPrimedTypedIds,RenamingOfSecond),
329 rename_bt(SecondPred,RenamingOfSecond,SecondPredAfterReplacement),
330
331 conjunct_predicates([FirstPredAfterReplacement,SecondPredAfterReplacement],FullPred),
332 % create exists for typed identifiers, which are primed more than once
333 include(is_primed_more_than_once,UnusedPrimedTypedIds,TwoOrMorePrimedTypedIds),
334 conjunction_to_list(FullPred,FullPredList),
335 split_list(contains_some_typed_id(TwoOrMorePrimedTypedIds),FullPredList,ExistsPredList,RemainingPredList),
336 conjunct_predicates(ExistsPredList,ExistsPred),
337 create_exists(TwoOrMorePrimedTypedIds,ExistsPred,Exists),
338 conjunct_predicates([Exists|RemainingPredList],Predicate),
339 append(ChangesFirst,FurtherChanges,ChangedIDs).
340
341
342 find_free_id_by_priming(Pred,TypedId,FreePrimedTypedId) :-
343 find_typed_identifier_uses(Pred,TypedIds),
344 (member(TypedId,TypedIds) ->
345 prime_typed_identifier(TypedId,PrimedTypedId),
346 find_free_id_by_priming(Pred,PrimedTypedId,FreePrimedTypedId)
347 ; FreePrimedTypedId = TypedId).
348
349 contains_some_typed_id(TypedIds,Pred) :-
350 some(contains_typed_id(Pred),TypedIds).
351
352 contains_typed_id(Pred,TypedId) :-
353 find_typed_identifier_uses(Pred,TypedIds),
354 member(TypedId,TypedIds).
355
356 prime_identifiers(TExprs,Primed) :-
357 maplist(prime_typed_identifier,TExprs,Primed).
358
359 is_primed_more_than_once(TExpr) :-
360 get_texpr_id(TExpr,ID),
361 remove_prime_id_atom(ID,ID2),
362 is_primed_id_atom(ID2).
363
364 create_id_renaming(Var,RenamedVar,rename(Id,RenamedId)) :-
365 get_texpr_id(Var,Id),
366 get_texpr_id(RenamedVar,RenamedId).
367
368