1 % (c) 2021-2026 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_intelligent_trace_replay, [get_transition_details/6,
6 perform_single_replay_step/5,
7 replay_json_trace_file/2, replay_json_trace_file/5,
8 read_json_trace_file/3,
9 tcltk_replay_json_trace_file/3,
10 replay_prolog_trace_file/1,
11
12 % API for interactive JSON trace replay:
13 load_json_trace_file_for_ireplay/1,
14 tk_get_stored_json_trace_description/1,
15 get_stored_json_replay_steps/1,
16 get_ireplay_status/3,
17 replay_of_current_step_is_possible/5,
18 replay_of_current_step_is_possible_with_trans/5, replay_of_current_step_is_possible_with_trans/6,
19 replay_current_step/1,
20 replay_current_step_with_trans/6,
21 ireplay_fast_forward/1,
22 ireplay_fast_forward_with_trans/6, ireplay_fast_forward_with_trans/7,
23 skip_current_ireplay_step/1]).
24
25 :- use_module(module_information,[module_info/2]).
26 :- module_info(group,testing).
27 :- module_info(description,'Replay saved (JSON) traces in a flexible way.').
28 % successor to b_trace_checking
29
30 :- meta_predicate exclude_and_collect_errors(2,-,-,-,-).
31
32 :- use_module(state_space,[current_state_id/1, transition/4, visited_expression/2]). % transition(CurID,Term,TransId,DestID)
33
34 :- use_module(tools_strings,[ajoin/2,ajoin_with_sep/3]).
35 :- use_module(specfile,[extract_variables_from_state/2, get_state_for_b_formula/3, b_or_z_mode/0, xtl_mode/0,
36 get_local_states_for_operation_transition/4, create_local_store_for_operation/4,
37 get_operation_name/2]).
38 :- use_module(error_manager).
39 :- use_module(debug).
40 :- use_module(probsrc(state_space), [extend_trace_by_transition_ids/1]).
41
42 % A single step is a list of informations about the step
43 % name/OperationName
44 % paras/List of =(Name,Value)
45
46 % TO DO: for match_spec use mutable counter for number of matches instead of used parameter
47
48 :- use_module(tcltk_interface,[compute_all_transitions_if_necessary/2]).
49
50 :- use_module(probsrc(bmachine),[b_get_operation_non_det_modifies/2, b_is_operation_name/1]).
51 :- use_module(probsrc(bsyntaxtree), [def_get_texpr_id/2, get_texpr_ids/2, get_texpr_id/2, get_texpr_type/2]).
52 :- use_module(probsrc(tools_matching), [get_possible_fuzzy_matches_completions_and_inner_msg/3]).
53 :- use_module(probsrc(b_interpreter),[b_test_boolean_expression_for_ground_state/5]).
54
55 perform_single_replay_step(FromID,TransID,DestID,MatchSpec,TransSpec) :-
56 %format('* FROM: ~w ',[FromID]),portray_match_spec(MatchSpec),nl,
57 ? (match_spec_has_optimize_field(MatchSpec)
58 -> findall(sol(MM,TID,DID),
59 perform_single_replay_step_statespace(FromID,TID,DID,MatchSpec,TransSpec,MM),
60 Sols),
61 mark_match_spec_as_used(MatchSpec),
62 length(Sols,NrSols),
63 min_member(sol(Mismatches,TransID,DestID),Sols),
64 debug_format(19,'Min. nr of mismatches found ~w among ~w candidate steps (starting from state ~w). ~n',[Mismatches,NrSols,FromID])
65 ; % just find first solution:
66 ? perform_single_replay_step_statespace(FromID,TransID,DestID,MatchSpec,TransSpec,_)
67 ).
68 perform_single_replay_step(FromID,TransID,DestID,MatchSpec,TransSpec) :-
69 ? perform_single_replay_step_by_pred(FromID,TransID,DestID,MatchSpec,TransSpec).
70
71
72
73
74 % Assumption: ParaStore and ResultStore are sorted
75 % returns the number of mismatches for those Keys marked as optimize
76 perform_single_replay_step_statespace(FromID,TransID,DestID,
77 MatchSpec,
78 transition_spec(OpName, _, ParaStore, ResultStore, DestStore, UnchangedVars, Preds, _Postconditions),
79 Mismatches) :-
80 mark_match_spec_as_used(MatchSpec),
81 get_opt_match_spec_val(opname,MatchSpec,OpMatch),
82 compute_all_transitions_if_necessary(FromID,false), % could be made optional
83 (OpMatch==match -> TOpName=OpName ; true),
84 ? get_sorted_transition_details(FromID,TransID,DestID,TOpName,FullParaStore,FullResultStore),
85 (xtl_mode
86 -> transition(FromID,OpTerm,TransID,DestID),
87 ground(OpTerm) % if not ground: fail here and try to instantiate non-ground params by replay by predicate
88 ; true),
89 (TOpName=OpName -> MM0 = 0 ; MM0=1, assert_mismatch(OpMatch)),
90 count_store_mismatches(FullParaStore,ParaStore,paras,MatchSpec,MM0,MM1), % check if parameters match
91 count_store_mismatches(FullResultStore,ResultStore,results,MatchSpec,MM1,MM2), % check if operation return values match
92 get_unchanged_store(FromID,UnchangedVars,UnchangedStore),
93 (DestStore=[], UnchangedStore=[]
94 -> MM6=MM2
95 ; % check if variables in destination state match
96 visited_expression(DestID,DestState),
97 get_dest_store(DestState,FullDestStore),
98 count_store_mismatches(FullDestStore,DestStore,dest,MatchSpec,MM2,MM3),
99 count_store_mismatches(FullDestStore,UnchangedStore,unchanged,MatchSpec,MM3,MM4),
100 (b_or_z_mode, get_match_spec_val(nondet_vars,MatchSpec,_)
101 -> b_get_op_non_det_modifies(OpName,NonDetModifies),
102 include(b_intelligent_trace_replay:bind_id_is_element_of(NonDetModifies),DestStore,D1),
103 count_store_mismatches(FullDestStore,D1,nondet_vars,MatchSpec,MM4,MM5),
104 include(b_intelligent_trace_replay:bind_id_is_element_of(NonDetModifies),UnchangedStore,D2),
105 count_store_mismatches(FullDestStore,D2,nondet_vars,MatchSpec,MM5,MM6)
106 ; MM6=MM4
107 )
108 ),
109 Mismatches = MM6,
110 (Preds=[] -> true
111 ; get_match_spec_val(preds,MatchSpec,match)
112 -> bsyntaxtree:conjunct_predicates(Preds,Pred),
113 format('Testing preds in destination state ~w after ~w: ',[FromID,DestID]),translate:print_bexpr(Pred),nl,
114 % TODO: insert_before_substitution_variables for $0 vars; check that it is consistent with Tcl/Tk and ProB2-UI
115 % maybe use annotate_becomes_such_vars or find_used_primed_ids
116 append(FullResultStore,FullParaStore,LocalStore),
117 b_test_boolean_expression_for_ground_state(Pred,LocalStore,FullDestStore,'trace replay', OpName)
118 ; true
119 ).
120
121 node_not_fully_explored(FromID,OpName) :-
122 (max_reached_for_node(FromID)
123 ; not_all_transitions_added(FromID)
124 ; not_interesting(FromID)
125 ; time_out_for_node(FromID,OpName,_)).
126
127 :- use_module(probsrc(preferences),[get_time_out_preference_with_factor/2]).
128 % lookup in state space failed; try perform by predicate
129 % Note: does not use optimize keys (yet), only match keys
130 perform_single_replay_step_by_pred(FromID,TransID,DestID,
131 MatchSpec,
132 transition_spec(OpName, _, ParaStore, ResultStore, DestStore, UnchangedVars, Preds,
133 _Postconditions)) :-
134 nonvar(OpName), % we currently cannot execute by predicate without knowing OpName
135 ? ((node_not_fully_explored(FromID,OpName) ; xtl_mode) -> true % TODO: improve max_reached_for_node for xtl_mode
136 ; debug_format(19,'Node ~w fully explored for ~w; no use in attempting execute by predicate~n',[FromID,OpName]),
137 fail
138 ),
139 mark_match_spec_as_used(MatchSpec),
140 !,
141 (ParaStore \= [], % check parameters of operation
142 get_match_spec_val(paras,MatchSpec,match)
143 -> b_get_operation_typed_paras(OpName,Paras1),
144 (xtl_mode, Paras1=[] % XTL special case: no custom param names found; use default from ParaStore: para1, para2, ...
145 -> findall(b(identifier(Para),string,[]),(member(B,ParaStore),b_intelligent_trace_replay:is_bind(B,Para,_)),Paras2)
146 ; Paras2=Paras1),
147 ? generate_predicates_from_store(operation_parameters,Paras2,ParaStore,ParaPreds)
148 ; ParaPreds = []),
149 (ResultStore \= [], % check return values of operation
150 get_match_spec_val(results,MatchSpec,match)
151 -> b_get_operation_typed_results(OpName,Results),
152 generate_predicates_from_store(operation_results,Results,ResultStore,ResultPreds)
153 ; ResultPreds = []),
154 get_machine_identifiers(OpName,TVars),
155 (DestStore = [] -> DestPreds=[]
156 ; get_match_spec_val(dest,MatchSpec,match)
157 ? -> generate_predicates_from_store(dest_variables,TVars,DestStore,DestPreds)
158 ; get_match_spec_val(nondet_vars,MatchSpec,match)
159 -> b_get_op_non_det_modifies(OpName,NonDetModifies),
160 include(b_intelligent_trace_replay:id_is_element_of(NonDetModifies),TVars,NDVars),
161 generate_predicates_from_store(non_det_vars,NDVars,DestStore,DestPreds)
162 ; DestPreds=[]
163 ),
164 get_unchanged_store(FromID,UnchangedVars,UnchangedStore),
165 (UnchangedStore = [] -> UnchangedPreds=[]
166 ; get_match_spec_val(unchanged,MatchSpec,match) ->
167 ? generate_predicates_from_store(unchanged_vars,TVars,UnchangedStore,UnchangedPreds)
168 ; get_match_spec_val(nondet_vars,MatchSpec,match) ->
169 include(b_intelligent_trace_replay:id_is_element_of(NonDetModifies),TVars,NDVars),
170 generate_predicates_from_store(non_det_vars,NDVars,UnchangedStore,UnchangedPreds)
171 ; UnchangedPreds=[]
172 ),
173 (get_match_spec_val(preds,MatchSpec,match) -> AddPreds=Preds ; AddPreds=[]),
174 append([ParaPreds,ResultPreds,DestPreds,UnchangedPreds,AddPreds],AllPreds),
175 conjunct_predicates(AllPreds,Pred),
176 (debug_mode(off) -> true
177 ; format('Trying to execute ~w in state ~w by predicate: ',[OpName,FromID]),
178 translate:print_bexpr(Pred),nl,flush_output),
179 get_time_out_preference_with_factor(5,TO), % TODO: store this in meta JSON info or options
180 safe_time_out(tcltk_interface:tcltk_add_user_executed_operation_typed(OpName,FromID,_,Pred,TransID,DestID),
181 TO, TimeOutRes),
182 (TimeOutRes = time_out
183 -> format_with_colour(user_output,[orange],'==> Timeout when executing ~w by predicate in state ~w~n',[OpName,FromID]),fail
184 ; true),
185 (xtl_mode
186 -> % In XTL mode we cannot describe the dest state using the DestPreds from above because 'xtl_state' is not a valid identifier.
187 % Preliminary solution: force a precise state match for dest/match as in perform_single_replay_step_statespace.
188 visited_expression(DestID,DestState),
189 get_dest_store(DestState,FullDestStore),
190 count_store_mismatches(FullDestStore,DestStore,dest,MatchSpec,_,_) % fails if there is a mismatch for dest/match
191 ; true).
192
193 get_unchanged_store(_,[],UnchangedStore) :- !, UnchangedStore=[].
194 get_unchanged_store(FromID,UnchangedVars,UnchangedStore) :-
195 % copy old values to UnchangedStore
196 visited_expression(FromID,FromState),
197 extract_variables_from_state(FromState,FullFromStore),
198 sort(FullFromStore,SortedStore),
199 % TODO: generate warning when unchanged variable does not exist?
200 include(b_intelligent_trace_replay:bind_id_is_element_of(UnchangedVars),SortedStore,UnchangedStore).
201
202 :- use_module(library(codesio),[write_term_to_codes/3]).
203 generate_predicate_from_bind(Kind,TypedIDs,json_bind(ID,Val,Type,Pos),b(Res,pred,[])) :-
204 ? (member(b(identifier(ID),ExpectedType,_),TypedIDs) ->
205 (unify_types_strict(ExpectedType,Type)
206 -> TID = b(identifier(ID),ExpectedType,[]),
207 (xtl_mode
208 -> (write_term_to_codes(Val,ValC,[quoted(true),ignore_ops(true)]),
209 atom_codes(ValA,ValC),
210 TVal = b(external_function_call('STRING_TO_TERM',[b(string(ValA),string,[])]),ExpectedType,[]))
211 ; TVal = b(value(Val),ExpectedType,[])),
212 Res = equal(TID,TVal)
213 ; pretty_type(ExpectedType,ETS), pretty_type(Type,TS), write(clash(Kind,ID,ETS,TS)),nl,
214 % error should probably be caught earlier:
215 add_warning(b_intelligent_trace_replay,'Ignoring value for stored identifier due to type clash: ',ID,Pos),
216 Res = truth
217 )
218 ; % the identifier is not in the list and should be ignored here; sanity checks are made somewhere else
219 Res = truth
220 ).
221 generate_predicate_from_bind(Kind,TypedIDs,bind(ID,Val),R) :- % bind without type infos, e.g., from unchanged store
222 ? generate_predicate_from_bind(Kind,TypedIDs,json_bind(ID,Val,any,unknown),R).
223
224 generate_predicates_from_store(Kind,TVars,DestStore,DestPreds) :-
225 ? maplist(b_intelligent_trace_replay:generate_predicate_from_bind(Kind,TVars),DestStore,DestPreds).
226
227 get_machine_identifiers(_,[]) :- \+ b_or_z_mode, !.
228 get_machine_identifiers(Op,TConsts) :- is_setup_constants_op(Op), !, b_get_machine_constants(TConsts).
229 get_machine_identifiers(_,TVars) :- b_get_machine_variables(TVars).
230
231 is_setup_constants_op('$setup_constants').
232 is_setup_constants_op('$partial_setup_constants').
233
234 b_get_op_non_det_modifies(_,[]) :- \+ b_or_z_mode, !.
235 b_get_op_non_det_modifies(Op,NonDetModifies) :- is_setup_constants_op(Op),
236 !,% return all constants as non-det modifies
237 b_get_machine_constants(TConsts),get_texpr_ids(TConsts,ND),
238 sort(ND,NonDetModifies).
239 b_get_op_non_det_modifies(OpName,NonDetModifies) :- b_get_operation_non_det_modifies(OpName,NonDetModifies).
240
241 fix_operation_name(OpName,OpName2) :- \+ b_is_operation_name(OpName),
242 fix_operation_name2(OpName,OpName2).
243 fix_operation_name2('SETUP_CONSTANTS','$setup_constants').
244 fix_operation_name2('INITIALISATION','$initialise_machine').
245 % TODO: maybe provide more automatic fixes, list of automatic re-names when parameters stay identical,...
246
247 % wrappers to deal with a few special transitions; TO DO: extend for CSP||B
248 b_get_operation_typed_results('$setup_constants',Results) :- !,
249 Results=[]. % for trace replay we assume setup_constants to have no result variables
250 b_get_operation_typed_results('$initialise_machine',Results) :- !, Results=[]. % ditto
251 b_get_operation_typed_results('$partial_setup_constants',Results) :- !, Results=[]. % ditto
252 b_get_operation_typed_results(OpName,Results) :- b_or_z_mode, !, b_get_machine_operation_typed_results(OpName,Results).
253 b_get_operation_typed_results(_,[]).
254
255 b_get_operation_typed_paras('$setup_constants',Paras) :- !,
256 Paras=[]. % for trace replay we assume setup_constants to have no parameters
257 b_get_operation_typed_paras('$initialise_machine',Paras) :- !, Paras=[]. % ditto
258 b_get_operation_typed_paras('$partial_setup_constants',Paras) :- !, Paras=[]. % ditto
259 b_get_operation_typed_paras(OpName,Paras) :- b_or_z_mode,!,
260 b_get_machine_operation_typed_parameters_for_animation(OpName,Paras).
261 b_get_operation_typed_paras(OpName,Paras) :- xtl_mode,!,
262 xtl_interface:get_xtl_paras_as_identifiers(OpName,Paras).
263 b_get_operation_typed_paras(OpName,[]) :-
264 add_message(replay_json_trace_file,'Not in B mode, cannot obtain parameter info for: ',OpName).
265
266 % -------------------
267
268 % now a version with multiple MatchSpecs to be tried in order
269 % Flag can be used to see how many alternatives were tried
270 flexible_perform_single_replay_step(FromID,TransID,DestID,[MatchSpec1|TMS],TransitionSpec,MName) :-
271 skip_match_spec(FromID,TransitionSpec,MatchSpec1),!,
272 debug_println(9,skipping_redundant_failing_check(MatchSpec1)),
273 flexible_perform_single_replay_step(FromID,TransID,DestID,TMS,TransitionSpec,MName).
274 flexible_perform_single_replay_step(FromID,TransID,DestID,[MatchSpec1|TMS],TransitionSpec,MName) :-
275 ? if(perform_single_replay_step(FromID,TransID,DestID,MatchSpec1,TransitionSpec),
276 get_match_spec_txt(MatchSpec1,MName),
277 ? flexible_perform_single_replay_step(FromID,TransID,DestID,TMS,TransitionSpec,MName)
278 ).
279
280 % -------------------
281
282
283 % we only assert mismatches; if a variable remains untouched we matched perfectly
284 assert_mismatch(Var) :- var(Var),!, Var=optimize.
285 assert_mismatch(require_mismatch). % probably not useful; difficult to support by predicate
286 assert_mismatch(optimize).
287
288 precise_match_spec(match_spec(_,precise,KeyVals)) :-
289 KeyVals = [dest/match,opname/match,paras/match,preds/match,results/match,unchanged/match].
290 ignore_dest_match_spec(match_spec(_,params_and_results,KeyVals)) :-
291 KeyVals = [opname/match,paras/match,preds/match,results/match,nondet_vars/match,dest/optimize,unchanged/optimize].
292 %ignore_return_match_spec(match_spec(_,parameters_only,KeyVals)) :-
293 % KeyVals = [opname/match,paras/match,results/optimize,nondet_vars/optimize].
294 opname_optimize_match_spec(match_spec(_,keep_name,KeyVals)) :-
295 KeyVals = [opname/match,paras/optimize,results/optimize,nondet_vars/optimize,dest/optimize,unchanged/optimize].
296
297 % conditions on when to skip certain match_specs
298 % (we assume that the precise_match_spec was tried before)
299 skip_match_spec(root,TS,match_spec(_,params_and_results,_)) :- get_transition_spec_op(TS,'$setup_constants').
300 % for setup_constants: nondet_vars are all constants; so this is equivalent to precise
301 % for initialise_machine: paras are all variables
302 skip_match_spec(_,TS,match_spec(_,MS,_)) :- MS \= precise,
303 get_transition_spec_meta(TS,Meta),
304 % for an unknown operation we only try a precise replay (e.g., if operation just renamed), otherwise we skip it
305 member(unknown_operation/_,Meta).
306 % TODO: skip parameters_only if an operation has not results and no nondet_vars
307
308 match_spec_was_used(match_spec(UsedFlag,_,_)) :- UsedFlag==used.
309 mark_match_spec_as_used(match_spec(used,_,_)).
310
311 get_match_spec_txt(match_spec(_,Name,_),Name).
312 ?get_match_spec_val(Key,match_spec(_,_,List),Res) :- member(Key/Val,List),!,Res=Val.
313
314 get_opt_match_spec_val(Key,MS,Res) :- get_match_spec_val(Key,MS,Val),!, Res=Val.
315 get_opt_match_spec_val(_,_,optimize).
316
317 % check if it is useful to optimize the mismatches
318 ?match_spec_has_optimize_field(match_spec(_,_,KeyVals)) :- member(_/optimize,KeyVals).
319
320 :- public valid_match_spec_key/1.
321 valid_match_spec_key(dest).
322 valid_match_spec_key(nondet_vars).
323 valid_match_spec_key(opname).
324 valid_match_spec_key(paras).
325 valid_match_spec_key(results).
326 valid_match_spec_key(unchanged).
327
328 :- public portray_match_spec/1.
329 portray_match_spec(match_spec(UsedFlag,Name,List)) :-
330 (UsedFlag==used -> U=used ; U=not_yet_used),
331 format('~w (~w): ~w~n',[Name,U,List]).
332
333 % -------------------
334
335 get_dest_store(concrete_constants(C),SC) :- !, sort(C,SC).
336 get_dest_store(Store,Vars) :- b_or_z_mode,!, extract_variables_from_state(Store,Vars).
337 get_dest_store(State,XTLState) :- xtl_mode, !, XTLState = [bind(xtl_state,State)]. % special identifier xtl_state only exists in JSON traces
338 get_dest_store(_,[]). % CSP,... has no concept of variables
339
340
341 % count the number of mismatches for a given key and MatchSpec
342 % if MatchSpec requires match (perfect match) it will fail if Mismatches are 0
343 % it accumulates the global number of mismatches in a DCG style accumulator
344 count_store_mismatches(FullStore,PartialStore,Key,MatchSpec,MismatchesIn,MismatchesOut) :-
345 get_match_spec_val(Key,MatchSpec,MatchVal), !,
346 (MatchVal=match
347 -> MismatchesIn=MismatchesOut,
348 count_mismatches(FullStore,Key,PartialStore,0)
349 ; count_mismatches(FullStore,Key,PartialStore,Mismatches),
350 MismatchesOut is MismatchesIn+Mismatches,
351 %print(new_mm(MismatchesOut,Key,MatchVal,Mismatches)),nl,
352 (Mismatches=0 -> true
353 ; assert_mismatch(MatchVal))
354 ).
355 count_store_mismatches(_,_,_Key,_,M,M). % key does not exist; no matching required
356
357 % for maplist, include, exclude:
358 ?bind_id_is_element_of(Vars,Bind) :- is_bind(Bind,ID,_), member(ID,Vars). % we could use ord_member
359 id_is_element_of(Vars,TID) :- def_get_texpr_id(TID,ID), member(ID,Vars).
360
361 is_bind(bind(ID,Val),ID,Val).
362 is_bind(json_bind(ID,Val,_,_),ID,Val).
363
364 %check_no_mismatches(FullStore,Key,PartialStore) :- count_mismatches(FullStore,Key,PartialStore,0).
365
366 count_mismatches(FullStore,Key,PartialStore,Mismatches) :-
367 count_mismatches_aux(FullStore,Key,PartialStore,0,Mismatches).
368 :- use_module(probsrc(translate), [translate_bvalue_with_limit/3]).
369 % count mismatches in FullStore compared to partial reference store
370 % if result is set to 0, it will fail after first mismatch
371 count_mismatches_aux(_,_,[],Acc,Res) :- !,Res=Acc.
372 count_mismatches_aux([],Key,PartialStore,_,_) :- b_or_z_mode, !,
373 ajoin(['Saved trace step contains unknown bindings for ',Key,': '],Msg),
374 add_error(b_intelligent_trace_replay,Msg,PartialStore),
375 fail.
376 count_mismatches_aux([],[_|_],_,Acc,Res) :- xtl_mode, !, % in XTL mode we can have multiple transitions with the same name => no error, try next for param match
377 inc_mismatches(Acc,Acc1,Res),
378 count_mismatches_aux(_,_,_,Acc1,Res).
379 count_mismatches_aux([Bind1|T],Key,[Bind2|T2],Acc,Res) :-
380 is_bind(Bind1,ID,Val), is_bind(Bind2,ID,Val2),!,
381 ? (check_value_equal(ID,Val,Val2)
382 -> count_mismatches_aux(T,Key,T2,Acc,Res)
383 ; ((debug_mode(off) ; \+ b_or_z_mode) -> true
384 ; translate_bvalue_with_limit(Val,200,V1), translate_bvalue_with_limit(Val2,200,V2),
385 formatsilent_with_colour(user_output,[red],'==> Mismatch for ~w ~w:~n ~w~n (trace) ~w~n',[Key,ID,V1,V2])
386 %nl,print(Val),nl,nl,print(Val2),nl,nl,
387 ),
388 inc_mismatches(Acc,Acc1,Res),
389 count_mismatches_aux(T,Key,T2,Acc1,Res)
390 ).
391 count_mismatches_aux([_ID|T],Key,PartialStore,Acc,Res) :- count_mismatches_aux(T,Key,PartialStore,Acc,Res).
392
393 inc_mismatches(X,_,Res) :- number(Res),X>Res,!,fail. % we will never reach Res; could be 0 for perfect match
394 inc_mismatches(Acc,Acc1,_) :- Acc1 is Acc+1.
395
396 % check if saved value and actual value is identical
397 :- use_module(kernel_objects,[equal_object/3]).
398 check_value_equal(ID,Val1,Val2) :-
399 temporary_set_preference(allow_enumeration_of_infinite_types,true,OldValueOfPref),
400 ? call_cleanup(check_value_equal_aux(ID,Val1,Val2),
401 reset_temporary_preference(allow_enumeration_of_infinite_types,OldValueOfPref)).
402
403 % what if trace was saved with different SYMBOLIC pref value?
404 :- use_module(b_ast_cleanup, [clean_up/3]).
405 :- use_module(custom_explicit_sets, [same_closure/2]).
406 :- use_module(debug, [debug_mode/1]).
407 check_value_equal_aux(ID,closure(P1,T1,B1),C2) :- C2 = closure(P2,T2,B2),
408 C1 = closure(P1,T1,B1),
409 !, % we have two symbolic values
410 (same_closure(C1,C2)
411 -> true
412 ; % simple comparison failed, now try and normalize the symbolic values and compare again
413 temporary_set_preference(normalize_ast,true,CHANGE),
414 % normalize_ast_sort_commutative should probably be false, unless we improve the sorting
415 %print(compiling_cur_value(ID)),nl,
416 clean_up(B1,[],B1C),
417 %we could call: b_compiler:b_optimize_closure(closure(P1,T1,B1C),closure(P12,T12,B12),WF),
418 %print(compiling_trace_value(ID)),nl,
419 clean_up(B2,[],B2C),
420 reset_temporary_preference(normalize_ast,CHANGE),
421 (same_closure(closure(P1,T1,B1C),closure(P2,T2,B2C))
422 -> true
423 ; Val=closure(P1,T1,B1C), Val2 = closure(P2,T2,B2C),
424 debug_mode(on),
425 translate_bvalue_with_limit(Val,500,V1), translate_bvalue_with_limit(Val2,500,V2),
426 formatsilent_with_colour(user_output,[red],'==> Symbolic Mismatch for ~w:~n ~w~n (trace) ~w~n',[ID,V1,V2]),
427 % trace, same_closure(closure(P1,T1,B1C),closure(P2,T2,B2C)),
428 fail
429 )
430 ).
431 %check_value_equal_aux(ID,Val1,Val2) :- !, equal_object(Val1,Val2,ID).
432 check_value_equal_aux(ID,Val1,Val2) :-
433 catch(
434 equal_object_time_out(Val1,Val2,ID,2500),
435 enumeration_warning(_A,_B,_C,_D,_E),
436 (format_with_colour(user_output,[red],'==> Enumeration warning when comparing values for ~w~n',[ID]),fail)
437 ).
438 check_value_equal_aux(_ID,Val1,Val2) :- xtl_mode, !,
439 Val1 = Val2.
440
441 :- use_module(tools_meta,[safe_time_out/3]).
442 equal_object_time_out(Val1,Val2,ID,TO) :-
443 safe_time_out(equal_object(Val1,Val2,ID),TO,TimeOutRes),
444 (TimeOutRes = time_out
445 -> format_with_colour(user_output,[red],'==> Timeout when comparing values for ~w~n',[ID]),fail
446 ; true).
447
448
449 :- use_module(specfile,[get_operation_internal_name/2,
450 state_corresponds_to_set_up_constants_only/2]).
451 :- use_module(bmachine,[b_get_machine_operation_parameter_names_for_animation/2,
452 b_get_machine_operation_typed_parameters_for_animation/2,
453 b_get_machine_operation_result_names/2,
454 b_get_machine_operation_typed_results/2,
455 b_get_machine_variables/1, b_get_machine_constants/1,
456 bmachine_is_precompiled/0, b_top_level_operation/1,
457 b_machine_name/1, b_is_variable/1, b_is_variable/2, b_is_constant/1, b_is_constant/2]).
458 :- use_module(probsrc(bsyntaxtree), [conjunct_predicates/2]).
459
460 :- use_module(probsrc(state_space),[max_reached_for_node/1, not_all_transitions_added/1,
461 time_out_for_node/3, not_interesting/1,
462 try_set_trace_by_transition_ids/1]).
463
464 :- use_module(library(lists)).
465
466 % get the information of a B state_space transition in more detailed form
467 % we get a store of parameter values and a store of result values and the operation name
468 get_transition_details(FromID,TransID,DestID,OpName,ParaStore,ResultStore) :-
469 ? transition(FromID,OperationTerm,TransID,DestID),
470 get_operation_internal_name(OperationTerm,OpName),
471 get_transition_details_aux(OpName,OperationTerm,DestID,ParaStore,ResultStore).
472
473 get_transition_details_aux('$setup_constants',_,DestID,ParaStore,ResultStore) :- !,
474 ResultStore=[],
475 visited_expression(DestID,DestState),
476 state_corresponds_to_set_up_constants_only(DestState,ParaStore).
477 get_transition_details_aux('$initialise_machine',_,DestID,ParaStore,ResultStore) :- !,
478 ResultStore=[],
479 visited_expression(DestID,DestState),
480 extract_variables_from_state(DestState,ParaStore).
481 get_transition_details_aux(OpName,OperationTerm,_,ParaStore,ResultStore) :-
482 get_local_states_for_operation_transition(OpName,OperationTerm,ParaStore,ResultStore).
483
484 get_transition_name(FromID,TransID,OpName) :-
485 transition(FromID,OperationTerm,TransID,_),
486 get_operation_name(OperationTerm,OpName).
487
488
489
490 % a variation where the two stores are sorted according to Prolog order:
491 get_sorted_transition_details(FromID,TransID,DestID,TOpName,SortedPS,SortedRS) :-
492 ? get_transition_details(FromID,TransID,DestID,TOpName,FullParaStore,FullResultStore),
493 sort(FullParaStore,SortedPS),
494 sort(FullResultStore,SortedRS).
495
496 % ------------------------
497
498 get_transition_spec_op(transition_spec(OpName, _, _, _, _, _, _, _),OpName).
499 get_transition_spec_meta(transition_spec(_, Meta, _, _, _, _, _, _),Meta).
500
501 % get textual representation of transition spec (portray/translate)
502 get_transition_spec_txt(transition_spec(OpName, Meta, ParaStore, ResultStore,
503 _DestStore, _Unchanged, _Preds, _Postconditions),Txt) :-
504 ? (member(description/Desc,Meta) -> ajoin([Desc,' :: '],DescTxt) ; DescTxt = ''),
505 (nonvar(OpName), OpName \= '' -> OpTxt=OpName
506 ; member(unknown_operation/op(OldOpName,OldParas,OldResults),Meta)
507 -> translate_unknown_operation(OldOpName,OldParas,OldResults,OpTxt)
508 ; OpTxt='?'),
509 (ParaStore = [] -> ParaText1='', ParaText2=''
510 ; ParaText1=' paras: ',
511 maplist(get_bind_txt,ParaStore,Paras),
512 ajoin_with_sep(Paras,',',ParaText2)
513 ),
514 (ResultStore = [] -> ResultText1='', ResultText2=''
515 ; ResultText1=' results: ',
516 maplist(get_bind_txt,ResultStore,Results),
517 ajoin_with_sep(Results,',',ResultText2)
518 ),!,
519 ajoin([DescTxt,OpTxt,ParaText1,ParaText2,ResultText1,ResultText2],Txt).
520 get_transition_spec_txt(TS,'???') :- add_internal_error('Unknown transition spec:',TS).
521
522
523 translate_unknown_operation(OldOpName,OldParas,[],OpTxt) :- !,
524 translate_bindings(OldParas,OPS),
525 append(OPS,[')'],OpParaAtoms),
526 ajoin(['?',OldOpName,'('|OpParaAtoms],OpTxt).
527 translate_unknown_operation(OldOpName,OldParas,OldResults,OpTxt) :-
528 translate_bindings(OldResults,OPR),
529 translate_unknown_operation(OldOpName,OldParas,[],Op1),
530 ajoin([Op1,'-->'|OPR],OpTxt).
531
532 % translate a list of json_bind terms into a list for use with ajoin for pretty-priting
533 translate_bindings([],[]).
534 translate_bindings([json_bind(ID,Val,_Type,_Pos)|TJ],[ID,'=',TVal|TT]) :-
535 translate_bvalue_with_limit(Val,50,TVal),
536 (TJ = [] -> TT=[]
537 ; TT = [','|TT2], translate_bindings(TJ,TT2)).
538
539
540 get_bind_txt(Bind,Txt) :- is_bind(Bind,Id,Val),
541 ? simple_val(Val), !, % TODO: add parameter for short/long text
542 translate_bvalue_with_limit(Val,100,V1),
543 ajoin([Id,'=',V1],Txt).
544 get_bind_txt(Bind,Id) :- is_bind(Bind,Id,_).
545
546 :- use_module(probsrc(custom_explicit_sets),[singleton_set/2]).
547 simple_val(V) :- var(V),!,fail.
548 simple_val(int(_)).
549 simple_val(term(floating(_))).
550 simple_val(pred_false).
551 simple_val(pred_true).
552 simple_val(string(_)).
553 simple_val(fd(_,_)).
554 ?simple_val((A,B)) :- simple_val(A), simple_val(B).
555 simple_val([]).
556 ?simple_val(S) :- singleton_set(S,V), simple_val(V).
557
558
559
560 % perform some static checks on a transition spec: check if operations, parameters, variables exist
561 check_and_adapt_trace_step(transition_spec(OpName, Meta, ParaStore, _, DestStore, UnchangedVars,Preds, Postconditions), Step,
562 transition_spec(OpName, Meta, ParaStore2, [], DestStore2,UnchangedVars2,Preds, Postconditions)) -->
563 {\+ b_or_z_mode},!,
564 ? exclude_and_collect_errors(unknown_para_binding(OpName,_,'parameter',Step),ParaStore,ParaStore2),
565 ? exclude_and_collect_errors(unknown_variable_binding(Step,OpName),DestStore,DestStore2),
566 ? exclude_and_collect_errors(unknown_variable(Step,OpName),UnchangedVars,UnchangedVars2).
567 check_and_adapt_trace_step(transition_spec(OpName, Meta, ParaStore,ResultStore, DestStore, UnchangedVars,Preds, Postconditions),
568 Step,
569 transition_spec(OpName2,Meta2,ParaStore2,ResultStore2,DestStore2,UnchangedVars2,Preds2, Postconditions2)) -->
570 ({fix_operation_name(OpName,OpName2)}
571 -> add_replay_error('Fixed unknown operation name to: ',OpName2) ; {OpName2=OpName}),
572 {b_get_operation_typed_results(OpName2,TOpResults)},
573 {b_get_operation_typed_paras(OpName2,TOpParas)},
574 !,
575 {Meta2=Meta}, % TO DO: include excluded infos
576 {Preds2=Preds}, %TO DO: check if all identifiers bound
577 {Postconditions2=Postconditions}, % TODO Check operation names and predicate identifiers
578 ? exclude_and_collect_errors(unknown_para_binding(OpName2,TOpParas,'parameter',Step),ParaStore,ParaStore2),
579 ? exclude_and_collect_errors(unknown_para_binding(OpName2,TOpResults,'result variable',Step),ResultStore,ResultStore2),
580 ({is_setup_constants_op(OpName2)}
581 ? -> exclude_and_collect_errors(unknown_constant_binding(Step),DestStore,DestStore2),
582 {UnchangedVars2 = []},
583 ({UnchangedVars = []} -> []
584 ; {add_error(b_intelligent_trace_replay,'Illegal unchanged info for SETUP_CONSTANTS',UnchangedVars)}
585 )
586 ? ; exclude_and_collect_errors(unknown_variable_binding(Step,OpName2),DestStore,DestStore2),
587 ? exclude_and_collect_errors(unknown_variable(Step,OpName2),UnchangedVars,UnchangedVars2)
588 ).
589 check_and_adapt_trace_step(transition_spec('', Meta, PS, RS, DestStore, UnchangedVars,Preds, Postconditions), Step,
590 transition_spec(_, Meta2, [], [], DestStore2, UnchangedVars2,Preds, Postconditions)) -->
591 % Empty Operation name, treat as wildcard
592 !,
593 {Meta2 = [unknown_operation/op('',PS,RS) | Meta]},
594 ( {PS=[]} -> "" ; add_replay_error('Parameters cannot be checked for unknown operation name: ',PS)),
595 ( {RS=[]} -> "" ; add_replay_error('Result values cannot be checked for unknown operation name: ',PS)),
596 exclude_and_collect_errors(unknown_variable_binding(Step,OpName),DestStore,DestStore2),
597 exclude_and_collect_errors(unknown_variable(Step,OpName),UnchangedVars,UnchangedVars2).
598 check_and_adapt_trace_step(transition_spec(OpName, Meta, PS, RS, DestStore, UnchangedVars,Preds, Postconditions), Step,
599 transition_spec(_, Meta2, [], [], DestStore2, UnchangedVars2,Preds, Postconditions)) -->
600 add_replay_error('Unknown operation: ',OpName), % TODO treat $JUMP
601 {Meta2 = [unknown_operation/op(OpName,PS,RS) | Meta],
602 findall(KOpid,b_top_level_operation(KOpid),Ops),
603 (get_possible_fuzzy_matches_completions_and_inner_msg(OpName,Ops,FMsg)
604 -> ajoin(['Unknown operation in replay step ',Step,' (did you mean the operation ',FMsg,' ?) : '], Msg)
605 ; ajoin(['Unknown operation in replay step ',Step,': '], Msg)),
606 (member(pos/Pos,Meta) -> true ; Pos=unknown),
607 add_error(b_intelligent_trace_replay,Msg,OpName,Pos)
608 },
609 % TODO: maybe do a fuzzy match and check if a new operation not used in the trace file exists
610 exclude_and_collect_errors(unknown_variable_binding(Step,OpName),DestStore,DestStore2),
611 exclude_and_collect_errors(unknown_variable(Step,OpName),UnchangedVars,UnchangedVars2).
612
613 check_step_postconditions(transition_spec(_, _, _, _, _, _, _, Postconditions),StateID) -->
614 check_postconditions(Postconditions,1,StateID).
615
616 check_postconditions([],_,_) --> [].
617 check_postconditions([Postcondition|Postconditions],Nr,StateID) -->
618 check_postcondition(Postcondition,Nr,StateID),
619 {Nr1 is Nr+1},
620 check_postconditions(Postconditions,Nr1,StateID).
621
622 check_postcondition(state_predicate(Pred),Nr,StateID) -->
623 {get_state_for_b_formula(StateID,Pred,State)},
624 ({b_test_boolean_expression_for_ground_state(Pred,[],State,'trace replay postconditions',Nr)} ->
625 []
626 ;
627 add_replay_error('Failed postcondition (predicate):',Nr)
628 ).
629 check_postcondition(operation_enabled(OpName,Pred,Enabled),Nr,StateID) -->
630 {
631 precise_match_spec(MatchSpec),
632 TransitionSpec = transition_spec(OpName,[],[],[],[],[],[Pred],[]),
633 (perform_single_replay_step(StateID,_,_,MatchSpec,TransitionSpec) -> Actual = enabled ; Actual = disabled)
634 },
635 ({Enabled == Actual} ->
636 []
637 ;
638 {ajoin(['Failed postcondition (operation ',OpName,' should be ',Enabled,'):'],Msg)},
639 add_replay_error(Msg,Nr)
640 ).
641
642 % a version of exclude which also collects errors
643 exclude_and_collect_errors(_Pred,[],[]) --> [].
644 exclude_and_collect_errors(Pred,[H|T],Res) --> {call(Pred,H,Error)},!,
645 [Error],
646 exclude_and_collect_errors(Pred,T,Res).
647 exclude_and_collect_errors(Pred,[H|T],[H|Res]) --> % include item
648 ? exclude_and_collect_errors(Pred,T,Res).
649
650
651 add_replay_error(Msg,Term) --> {gen_replay_error(Msg,Term,Err)}, [Err].
652 gen_replay_error(Msg,Term,rerror(FullMSg)) :- ajoin([Msg,Term],FullMSg).
653 replay_error_occured(Errors) :- member(rerror(_),Errors).
654 get_replay_error(rerror(Msg),Msg).
655
656 :- use_module(probsrc(btypechecker), [unify_types_strict/2]).
657 :- use_module(probsrc(kernel_objects), [infer_value_type/2]).
658 :- use_module(probsrc(translate), [pretty_type/2]).
659 :- use_module(probsrc(kernel_freetypes),[registered_freetype/2, freetype_case_db/3]).
660
661 % check which parameter bindings should be ignored, not checked from trace file
662 ignore_operation_parameter('$setup_constants',ID) :-
663 ( registered_freetype(ID,_) -> add_message(replay_json_trace_file,'Not loading FREETYPE from trace file: ',ID)
664 ; freetype_case_db(ID,_,_) -> add_message(replay_json_trace_file,'Not loading FREETYPE case from trace file: ',ID)
665 ).
666
667 unknown_para_binding(OpName,TParas,Kind,Step,json_bind(ID,Value,ValType,ValPos),ErrorTerm) :- b_or_z_mode, !,
668 ( get_texpr_id(TID,ID),
669 ? member(TID,TParas), get_texpr_type(TID,Type)
670 -> illegal_type(ID,Type,Value,ValType,ValPos,Kind,Step,ErrorTerm)
671 ; ajoin(['Ignoring unknown ',Kind,' for operation ',OpName,' at step ', Step, ': '],Msg),
672 gen_replay_error(Msg,ID,ErrorTerm)
673 ).
674 unknown_para_binding(OpName,_,Kind,Step,json_bind(ID,_,any,_),ErrorTerm) :- xtl_mode, !,
675 (xtl_interface:xtl_transition_parameters(OpName,ParaIDs)
676 -> \+ memberchk(ID,ParaIDs)
677 ; \+ is_xtl_param(ID)),
678 ajoin(['Ignoring unknown ',Kind,' for operation ',OpName,' at step ', Step, ': '],Msg),
679 gen_replay_error(Msg,ID,ErrorTerm).
680 unknown_variable_binding(Step,_OpName,json_bind(Var,Value,ValType,ValPos),ErrorTerm) :- b_or_z_mode, b_is_variable(Var,Type),!,
681 illegal_type(Var,Type,Value,ValType,ValPos,'variable',Step,ErrorTerm).
682 unknown_variable_binding(Step,OpName,json_bind(Var,_,_,_),ErrorTerm) :- unknown_variable(Step,OpName,Var,ErrorTerm).
683 unknown_variable(Step,OpName,Var,ErrorTerm) :- xtl_mode, !,
684 Var \= xtl_state,
685 ajoin(['Ignoring unknown variable at step ', Step, ' for ', OpName, ': '],Msg),
686 gen_replay_error(Msg,Var,ErrorTerm).
687 unknown_variable(Step,OpName,Var,ErrorTerm) :- \+ b_is_variable(Var),
688 (b_is_constant(Var)
689 -> ajoin(['Ignoring constant at step ', Step, ' for ', OpName, ' (a variable is expected here): '],Msg)
690 ; b_get_machine_variables(TVars),get_texpr_ids(TVars,Vars),
691 get_possible_fuzzy_matches_completions_and_inner_msg(Var,Vars,FMsg)
692 -> ajoin(['Ignoring unknown variable (did you mean ',FMsg,' ?) at step ', Step, ' for ', OpName, ': '],Msg)
693 ; ajoin(['Ignoring unknown variable at step ', Step, ' for ', OpName, ': '],Msg)
694 ),
695 gen_replay_error(Msg,Var,ErrorTerm).
696 unknown_constant_binding(Step,json_bind(Var,Value,ValType,ValPos),ErrorTerm) :- b_is_constant(Var,Type),!,
697 illegal_type(Var,Type,Value,ValType,ValPos,'constant',Step,ErrorTerm).
698 unknown_constant_binding(Step,json_bind(Var,_,_,_),ErrorTerm) :-
699 (b_is_variable(Var)
700 -> ajoin(['Ignoring variable at step ', Step, ' (a constant is expected here): '],Msg)
701 ; b_get_machine_constants(TVars),get_texpr_ids(TVars,Vars),
702 get_possible_fuzzy_matches_completions_and_inner_msg(Var,Vars,FMsg)
703 -> ajoin(['Ignoring unknown constant (did you mean ',FMsg,' ?) at step ', Step, ': '],Msg)
704 ; ajoin(['Ignoring unknown constant at step ', Step, ': '],Msg)
705 ),
706 gen_replay_error(Msg,Var,ErrorTerm).
707
708
709 illegal_type(Var,Type,_Value,ValType,_ValPos,Kind,Step,ErrorTerm) :-
710 \+ unify_types_strict(Type,ValType),
711 pretty_type(ValType,VTS), pretty_type(Type,TS),
712 ajoin(['Ignoring ',Kind, ' at step ', Step,
713 ' due to unexpected type of value (', VTS, ' instead of ',TS,') for: '],ErrMsg),
714 gen_replay_error(ErrMsg,Var,ErrorTerm).
715
716 is_xtl_param(ID) :- atom_chars(ID,['p','a','r','a'|T]), number_chars(_,T).
717 /*
718 | ?- perform_single_replay_step(X,TID,Dest,Match,transition_spec(Op,[],[],[],[active])).
719 X = 3,
720 TID = 86,
721 Dest = 5,
722 Match = match_spec(match,match,match,match,match),
723 Op = new ?
724 yes
725
726 */
727
728 % ------------------
729 %precise_replay_trace(Trace,FromID,TransIds,DestID) :-
730 % precise_match_spec(MatchSpec), % require precise replay
731 % replay_trace(Trace,[MatchSpec],[],1,FromID,TransIds,DestID,[],_). % Todo : check errors
732
733 :- use_module(tools_printing,[format_with_colour/4, check_mark_symbol_codes/1]).
734 :- use_module(probsrc(debug),[formatsilent_with_colour/4]).
735 % Note if we leave RestSpecs as a variable this will always do deterministic replay
736 % to achieve backtracking RestSpecs must be set to []
737 replay_trace([],_MatchSpecs,_Opts,_,ID,[],ID,[],[]).
738 replay_trace([TransSpec|T],MatchSpecs,Options,Step,FromID,TransIds,DestID,RestSpecs,
739 [replay_step(MatchInfo,Errors)|OtherMatches]) :-
740 get_transition_spec_txt(TransSpec,TTxt),
741 formatsilent_with_colour(user_output,[blue],'==> Replay step ~w: from state ~w for ~w~n',[Step,FromID,TTxt]),
742 check_mark_symbol_codes(CheckMark),
743 statistics(walltime,[W1|_]),
744 % first perform static check of step:
745 ? phrase(check_and_adapt_trace_step(TransSpec,Step,CorrectedTransSpec),Errors,Errors1),
746 if((TransIds=[TransID|TTrans],
747 ? flexible_perform_single_replay_step(FromID,TransID,ID2,MatchSpecs,CorrectedTransSpec,MatchInfo)),
748 (phrase(check_step_postconditions(CorrectedTransSpec,ID2),Errors1),
749 statistics(walltime,[W2|_]), WTime is W2-W1,
750 get_transition_name(FromID,TransID,OpName), % show operation name used as feedback in case errors occur
751 (Errors == [] ->
752 formatsilent_with_colour(user_output,[green],' ~s Replay step ~w successful (~w, ~w ms) leading to state ~w~n',[CheckMark,Step,MatchInfo,WTime,ID2])
753 ; Errors = [rerror(OneErr)] ->
754 formatsilent_with_colour(user_output,[red,bold],'==> Replay step ~w successful WITH ERROR (~w, ~w, ~w ms) leading to state ~w via ~w~n',[Step,MatchInfo,OneErr,WTime,ID2,OpName])
755 ;
756 length(Errors,NrErrors), Errors = [rerror(OneErr)|_],
757 formatsilent_with_colour(user_output,[red,bold],'==> Replay step ~w successful WITH ERRORS (~w, ~w errors [~w,...], ~w ms) leading to state ~w via ~w~n',[Step,MatchInfo,NrErrors,OneErr,WTime,ID2,OpName])
758 ;
759 length(Errors,NrErrors),
760 formatsilent_with_colour(user_output,[red,bold],'==> Replay step ~w successful WITH ERRORS (~w, ~w errors, ~w ms) leading to state ~w via ~w~n',[Step,MatchInfo,NrErrors,WTime,ID2,OpName])
761 ),
762 (get_preference(deterministic_trace_replay,true) -> !
763 % TO DO: use info from MatchSpec? (e.g., det for perfect match)
764 ; true
765 ; formatsilent_with_colour(user_output,[orange],'==> Backtracking replay step ~w (~w) leading to state ~w~n',[Step,MatchInfo,ID2])),
766 S1 is Step+1,
767 replay_trace(T,MatchSpecs,Options,S1,ID2,TTrans,DestID,RestSpecs,OtherMatches)
768 ),
769 (format_with_colour(user_output,[red,bold],'==> Replay step ~w FAILED~n',[Step]),
770 get_transition_spec_txt(TransSpec,Txt), formatsilent_with_colour(user_output,[red,bold],' ~w~n',[Txt]),
771 Errors1=[],
772 MatchInfo=failed,
773
774 (T = [_|_], nonmember(stop_at_failure,Options)
775 -> % try and skip this step and continue replay
776 RestSpecs=[TransSpec|RT],
777 TransIds=[skip|TTrans], % -1 signifies skipped transition
778 S1 is Step+1,
779 replay_trace(T,MatchSpecs,Options,S1,FromID,TTrans,DestID,RT,OtherMatches)
780 ; RestSpecs=[TransSpec|T], % the steps that were not replayed
781 TransIds=[], DestID=FromID,
782 OtherMatches=[]
783 )
784 )
785 ).
786
787 % ------------------
788
789
790 tcltk_replay_json_trace_file(FileName,ReplayStatus,list([Header|Entries])) :-
791 replay_json_trace_file_with_check(FileName,TransSpecs,ReplayStatus,TransIds,MatchInfoList),
792 try_set_trace_by_transition_ids(TransIds),
793 Header = list(['Step', 'TraceFile','Replayed', 'Match','Mismatches','Errors','State ID']),
794 (tk_get_trace_info(TransSpecs,root,1,TransIds,MatchInfoList,Entries)
795 -> true
796 ; add_internal_error('Could not compute replay table:',TransSpecs), Entries=[]).
797
798
799 :- use_module(probsrc(translate),[translate_event_with_limit/3]).
800
801 tk_get_trace_info([],_,_,_,_,[]).
802 tk_get_trace_info([TransSpec|TS2],CurID,Step,TransIds,MatchInfoList,
803 [list([Step,Txt,OpTxt,MI,list(DeltaList),list(Errors),CurID])|RestInfo]) :-
804 get_from_match_list(MatchInfoList,MI,Errors,MIL2),
805 get_transition_spec_txt(TransSpec,Txt),
806 (TransIds=[TID1|TI2], transition(CurID,OperationTerm,TID1,ToID)
807 -> %get_operation_internal_name(OperationTerm,OpName)
808 translate_event_with_limit(OperationTerm,30,OpTxt),
809 analyse_step_match(TransSpec,CurID,TID1,DeltaList)
810 ; TransIds=[TID1|TI2],
811 (number(TID1) -> TID1<0 ; true) % not a valid transition number; -1 or skip
812 -> ToID=CurID, OpTxt='skipped', DeltaList=[]
813 ; TI2=[], ToID=CurID, OpTxt='-', DeltaList=[]
814 ),
815 Step1 is Step+1,
816 tk_get_trace_info(TS2,ToID,Step1,TI2,MIL2,RestInfo).
817
818 get_from_match_list([replay_step(MI,Errors)|T],MatchInfo,TkErrors,T) :-
819 maplist(get_replay_error,Errors,TkErrors),
820 (MI=precise,TkErrors=[_|_] -> MatchInfo=precise_with_errs ; MatchInfo=MI).
821 get_from_match_list([],'-',['-'],[]).
822
823 % analyse how good a step matches the transition spec
824 % useful after replay to provide explanations to the user
825 analyse_step_match(TransSpec,FromID,TransID,DeltaList) :-
826 TransSpec = transition_spec(OpName, _, ParaStore, ResultStore, DestStore, _UnchangedVars, _Preds, _Postconditions),
827 get_sorted_transition_details(FromID,TransID,DestID,TOpName,FullParaStore,FullResultStore),
828 (TOpName=OpName -> DeltaList=DL1 ; DeltaList=['Name'|DL1]),
829 delta_store_match(FullParaStore,paras,ParaStore,DL1,DL2),
830 delta_store_match(FullResultStore,results,ResultStore,DL2,DL3),
831 visited_expression(DestID,DestState),
832 get_dest_store(DestState,FullDestStore),
833 delta_store_match(FullDestStore,dest,DestStore,DL3,DL4),
834 % TO DO: UnchangedVars and _Preds
835 DL4=[],!.
836 analyse_step_match(TransSpec,FromID,TransID,DeltaList) :-
837 add_internal_error('Call failed:',analyse_step_match(TransSpec,FromID,TransID,DeltaList)),
838 DeltaList=['??ERROR??'].
839
840 delta_store_match(_,_,[]) --> !.
841 delta_store_match([],_Key,Rest)
842 --> add_rest(Rest). % these bindings were probably filtered out during replay and error messages were generated
843 delta_store_match([Bind1|T],Key,[Bind2|T2]) -->
844 {is_bind(Bind1,ID,Val), is_bind(Bind2,ID,Val2)},
845 !,
846 ({check_value_equal(ID,Val,Val2)}
847 -> []
848 ; %translate_bvalue_with_limit(Val,100,V1), translate_bvalue_with_limit(Val2,100,V2),
849 [ID] % TO DO: provided detailed explanation using values
850 ),delta_store_match(T,Key,T2).
851 delta_store_match([_ID|T],Key,PartialStore) --> delta_store_match(T,Key,PartialStore).
852
853 add_rest([]) --> [].
854 add_rest([Bind|T]) --> {is_bind(Bind,ID,_)}, [ID], add_rest(T).
855
856 % -----------------------
857
858 replay_json_trace_file(FileName,ReplayStatus) :-
859 replay_json_trace_file_with_check(FileName,_,ReplayStatus,TransIds,_),
860 try_set_trace_by_transition_ids(TransIds).
861
862
863 % generate error/warning for imperfect or partial replay
864 replay_json_trace_file_with_check(FileName,Trace,ReplayStatus,TransIds,MatchInfoList) :-
865 replay_json_trace_file(FileName,Trace,ReplayStatus,TransIds,MatchInfoList),
866 length(TransIds,Steps),
867 length(Trace,AllSteps),
868 check_replay_status(ReplayStatus,Steps,AllSteps).
869
870 check_replay_status(imperfect,Steps,_) :- !,
871 ajoin(['Imperfect replay, steps replayed: '],Msg),
872 add_warning(replay_json_trace_file,Msg, Steps).
873 check_replay_status(partial,Steps,AllSteps) :- !,
874 ajoin(['Replay of all ',AllSteps,' steps not possible, steps replayed: '],Msg),
875 add_error(replay_json_trace_file,Msg, Steps).
876 check_replay_status(perfect,Steps,_) :-
877 add_message(replay_json_trace_file,'Perfect replay possible, steps replayed: ', Steps).
878
879 % ------------
880
881 :- use_module(tools,[start_ms_timer/1, stop_ms_timer_with_silent_msg/2,
882 stop_ms_timer_with_debug_msg/2]).
883 :- use_module(bmachine_construction,[dummy_machine_name/2]).
884
885 replay_json_trace_file(FileName,Trace,ReplayStatus,TransIds,MatchInfoList) :- \+ bmachine_is_precompiled,!,
886 add_error(replay_json_trace_file,'No specification loaded, cannot replay trace file:',FileName),
887 Trace=[], TransIds=[], ReplayStatus=partial, MatchInfoList=[].
888 replay_json_trace_file(FileName,Trace,ReplayStatus,TransIds,MatchInfoList) :-
889 start_ms_timer(T1),
890 read_json_trace_file(FileName,ModelName,Trace),
891 stop_ms_timer_with_debug_msg(T1,'loading JSON trace file'),
892 precise_match_spec(MatchSpec), % require precise replay
893 ignore_dest_match_spec(MS2),
894 opname_optimize_match_spec(MS3),
895 % was ignore_return_match_spec(MS3), % TODO: when no return and no non-det vars: do not try MS3
896 start_ms_timer(T2),
897 temporary_set_preference(deterministic_trace_replay,true,CHNG),
898 replay_trace(Trace,[MatchSpec,MS2,MS3],[],1,root,TransIds,_DestID,RestTrace,MatchInfoList),
899 reset_temporary_preference(deterministic_trace_replay,CHNG),
900 stop_ms_timer_with_silent_msg(T2,'replaying JSON trace file'),
901 !,
902 (RestTrace = []
903 -> ((match_spec_was_used(MS3) % Grade=3
904 ; match_spec_was_used(MS2) %Grade=2
905 ? ; member(replay_step(_,Errs),MatchInfoList), replay_error_occured(Errs)
906 )
907 -> ReplayStatus=imperfect,
908 check_model_name(ModelName)
909 ; ReplayStatus=perfect
910 )
911 ; ReplayStatus=partial,
912 check_model_name(ModelName)
913 ).
914
915 :- use_module(specfile,[currently_opened_specification_name/1]).
916 check_model_name(ModelName) :-
917 currently_opened_specification_name(CurModelName),!,
918 (CurModelName=ModelName -> true
919 ; dummy_machine_name(ModelName,CurModelName) % CurModelName = MAIN_MACHINE_FOR_...
920 -> true
921 ; ModelName = 'dummy(uses)'
922 -> true % if modelName is "null" this is the value used
923 ; prob2_ui_suffix(ModelName,CurModelName) ->
924 % happens when ProB2-UI saves trace files; sometimes it adds (2), ... suffix, see issue #243
925 add_message(replay_json_trace_file, 'JSON trace file model name has a ProB2-UI suffix: ', ModelName)
926 ; ajoin(['JSON trace file model name ',ModelName,' does not match current model name: '],MMsg),
927 add_warning(replay_json_trace_file, MMsg, CurModelName)
928 ).
929 check_model_name(ModelName) :-
930 add_warning(replay_json_trace_file, 'Cannot determine current model name to check stored name:', ModelName).
931
932 :- set_prolog_flag(double_quotes, codes).
933 :- use_module(self_check).
934 :- assert_must_succeed(b_intelligent_trace_replay:prob2_ui_suffix('b_mch', 'b')).
935 :- assert_must_succeed(b_intelligent_trace_replay:prob2_ui_suffix('b_mch (2)', 'b')).
936 :- assert_must_succeed(b_intelligent_trace_replay:prob2_ui_suffix('b (2)', 'b')).
937 :- assert_must_fail(b_intelligent_trace_replay:prob2_ui_suffix('b', 'bc')).
938 :- assert_must_fail(b_intelligent_trace_replay:prob2_ui_suffix('b (2)', 'bc')).
939 % check if name matches current model name catering for ProB2-UI quirks
940 prob2_ui_suffix(JSONModelName,CurModelName) :-
941 atom_codes(JSONModelName,JCodes),
942 atom_codes(CurModelName,TargetCodes),
943 append(TargetCodes,After,JCodes),
944 (append("_mch",After2,After)
945 -> true % .eventb package file name
946 ; append(".mch",After2,After) -> true
947 ; After2=After),
948 valid_prob2_ui_suffix(After2).
949
950 valid_prob2_ui_suffix([]) :- !.
951 valid_prob2_ui_suffix([32|_]). % we could check that we have (2), ... after; but is it necessary?
952
953
954 error_occured_during_replay(MatchInfoList) :-
955 member(replay_step(_,Errs),MatchInfoList), replay_error_occured(Errs),!.
956
957 % ---------------------------------
958
959 replay_prolog_trace_file(FileName) :-
960 start_ms_timer(T1),
961 read_prolog_trace_file(FileName,_ModelName,Trace),
962 stop_ms_timer_with_debug_msg(T1,'loading Prolog trace file'),
963 replay_prolog2(FileName,Trace).
964
965 replay_prolog2(FileName,Trace) :-
966 precise_match_spec(MatchSpec),
967 start_ms_timer(T2),
968 replay_trace(Trace,[MatchSpec],[stop_at_failure],1,root,TransIds,_DestID,RestTrace,MatchInfoList),
969 stop_ms_timer_with_silent_msg(T2,'replaying JSON trace file'),
970 RestTrace=[], % will initiate backtracking
971 try_set_trace_by_transition_ids(TransIds),
972 (error_occured_during_replay(MatchInfoList)
973 -> add_error(replay_prolog_trace_file,'Errors occurred during replay of file:',FileName)
974 ; true).
975 replay_prolog2(FileName,_Trace) :-
976 add_error(replay_prolog_trace_file,'Could not fully replay file:',FileName).
977
978 read_prolog_trace_file(FileName,ModelName,Trace) :-
979 open(FileName,read,Stream,[encoding(utf8)]),
980 call_cleanup(parse_prolog_trace_file(FileName,Stream,ModelName,Trace),
981 close(Stream)).
982
983 parse_prolog_trace_file(File,Stream,ModelName,Trace) :-
984 safe_read_stream(Stream,0,Term),!,
985 (Term = end_of_file
986 -> Trace = [], ModelName = 'dummy(uses)',
987 add_warning(read_prolog_trace_file,'Empty trace file: ',File)
988 ; (Term = machine(ModelName)
989 -> Trace = T
990 ; Trace = [Term|T],
991 add_warning(read_prolog_trace_file,'File does not start with a machine/1 fact: ',Term)
992 ),
993 parse_prolog_trace_file_body(Stream,1,T)
994 ).
995
996 parse_prolog_trace_file_body(Stream,Step,Trace) :-
997 safe_read_stream(Stream,Step,Term),!,
998 (Term = end_of_file
999 -> Trace = []
1000 ; skip_prolog_term(Term)
1001 -> add_message(read_prolog_trace_file,'Skipping: ',Term),
1002 parse_prolog_trace_file_body(Stream,Step,Trace)
1003 ; Trace = [TransSpec|T],
1004 convert_prolog_trace_step(Term,TransSpec),
1005 S1 is Step + 1,
1006 parse_prolog_trace_file_body(Stream,S1,T)
1007 ).
1008
1009 safe_read_stream(Stream,Step,T) :-
1010 catch(read(Stream,T), E, (
1011 ajoin(['Exception while reading step ', Step, 'of trace file: '], Msg),
1012 add_error(read_prolog_trace_file,Msg,[E]),
1013 T=end_of_file
1014 )).
1015
1016 skip_prolog_term('$check_value'(_ID,_Val)).
1017 convert_prolog_trace_step(Fact,
1018 transition_spec(OpName,Meta,ParaStore,ResultStore,DestStore,Unchanged,PredList,[])) :-
1019 PredList=[], Unchanged=[], Meta=[], DestStore = [],
1020 decompose_operation(Fact,OpName,ParaStore,ResultStore).
1021 % TODO: deal with '$check_value'(ID,Val)
1022 % decompose an operation term into name, parameter store and result store
1023 decompose_operation('-->'(OpTerm,Results),OpName,ParaStore,ResultStore) :- !,
1024 decompose_operation2(OpTerm,OpName,ParaStore),
1025 (b_get_machine_operation_result_names(OpName,ResultNames)
1026 -> create_sorted_store(ResultNames,Results,OpName,ResultStore)
1027 ; ResultStore = []).
1028 decompose_operation(OpTerm,OpName,ParaStore,[]) :- decompose_operation2(OpTerm,OpName,ParaStore).
1029
1030 is_setup_or_init('$initialise_machine','$initialise_machine').
1031 is_setup_or_init(initialise_machine,'$initialise_machine'). % old style
1032 is_setup_or_init(setup_constants,'$setup_constants'). % old style
1033 is_setup_or_init(Op,Op) :- is_setup_constants_op(Op).
1034
1035 decompose_operation2(OpTerm,OpName,ParaStore) :-
1036 functor(OpTerm,Functor,Arity),
1037 is_setup_or_init(Functor,OpName),
1038 !,
1039 % the order of constants, variables etc has changed in ProB;
1040 % in general we cannot reconstruct the association of the arguments to variables or constants
1041 (Arity=0 -> true
1042 ; add_message(b_intelligent_trace_replay,'Ignoring parameters of:',OpName)),
1043 ParaStore=[].
1044 decompose_operation2(OpTerm,OpName,ParaStore) :-
1045 OpTerm =.. [OpName|Paras],
1046 (b_get_machine_operation_parameter_names_for_animation(OpName,ParaNames)
1047 -> create_sorted_store(ParaNames,Paras,OpName,ParaStore)
1048 ; ParaStore = [],
1049 add_error(read_prolog_trace_file,'Unknown operation in trace file:',OpName)
1050 ).
1051
1052
1053 create_sorted_store([],Paras,OpName,SortedParaStore) :- Paras = [_|_],
1054 get_preference(show_eventb_any_arguments,false),!,
1055 add_message(b_intelligent_trace_replay,'Prolog trace file contains values for virtual parameters (set SHOW_EVENTB_ANY_VALUES to TRUE to better replay this trace file): ',OpName),
1056 SortedParaStore = [].
1057 create_sorted_store(ParaNames,[],_OpName,SortedParaStore) :- ParaNames = [_|_],
1058 get_preference(show_eventb_any_arguments,true),!,
1059 add_message(b_intelligent_trace_replay,'Prolog trace file contains no values for parameters (maybe SHOW_EVENTB_ANY_VALUES was FALSE when trace file was created): ',ParaNames),
1060 SortedParaStore = [].
1061 create_sorted_store(ParaNames,Paras,OpName,SortedParaStore) :-
1062 create_local_store_for_operation(ParaNames,Paras,OpName,ParaStore),
1063 sort(ParaStore,SortedParaStore).
1064
1065
1066
1067 % ------------------------
1068
1069 :- use_module(extrasrc(json_parser),[json_parse_file/3]).
1070
1071 % read a JSON ProB2-UI trace file and extract model name and transition_spec list
1072 read_json_trace_file(FileName,ModelName,Trace) :-
1073 json_parse_file(FileName,Term,[rest(_),position_infos(true),strings_as_atoms(false)]),
1074 %nl,print(Term),nl,nl,
1075 !,
1076 ? (extract_json_model_name(Term,M) -> ModelName=M ; ModelName = 'dummy(uses)'),
1077 (translate_json_trace_term(Term,FileName,Trace) -> true
1078 ; add_error(read_json_trace_file,'Could not translate JSON transitionList: ',Term),
1079 Trace = []).
1080
1081 % small JSON utilities; to do: merge with VisB utilities and factor out
1082 ?get_json_attribute(Attr,ObjList,Value) :- member(Equality,ObjList),
1083 is_json_equality_attr(Equality,Attr,Value).
1084 get_json_attribute_with_pos(Attr,ObjList,File,Value,Pos) :-
1085 ? member(Equality,ObjList),
1086 is_json_equality_attr_with_pos(Equality,File,Attr,Value,Pos).
1087
1088 is_json_equality_attr('='(Attr,Val),Attr,Val).
1089 is_json_equality_attr('='(Attr,Val,_Pos),Attr,Val). % we have position infos
1090
1091 is_json_equality_attr_with_pos('='(Attr,Val),_File,Attr,Val,unknown).
1092 is_json_equality_attr_with_pos('='(Attr,Val,JPos),File,Attr,Val,ProBPos) :- create_position(JPos,File,ProBPos).
1093
1094 create_position(From-To,File,ProBPos) :-
1095 ProBPos=src_position_with_filename_and_ec(From,1,To,1,File).
1096 % --------
1097
1098 :- use_module(probsrc(preferences), [reset_temporary_preference/2,temporary_set_preference/3, get_preference/2]).
1099 translate_json_trace_term(json(ObjList),FileName,Trace) :-
1100 ? get_json_key_list(transitionList,ObjList,List),
1101 List \= [], % optimization, dont set preferences
1102 !,
1103 % TODO: why is this not a call_cleanup?
1104 temporary_set_preference(repl_cache_parsing,true,CHNG),
1105 eval_strings:turn_normalising_off,
1106 maplist(translate_json_operation(FileName),List,Trace),
1107 eval_strings:turn_normalising_on,
1108 reset_temporary_preference(repl_cache_parsing,CHNG).
1109
1110 % no transitionList => just use empty list
1111 translate_json_trace_term(json(_),_,[]) :- !.
1112
1113 % extract model name from metadata which looks like this
1114 /*
1115 "metadata": {
1116 "fileType": "Trace",
1117 "formatVersion": 1,
1118 "savedAt": "2021-10-13T13:38:02Z",
1119 "creator": "tcltk (leuschel)",
1120 "proBCliVersion": "1.11.1-nightly",
1121 "proBCliRevision": "3cb800bbadfeaf4f581327245507a55ae5a5e66d",
1122 "modelName": "scheduler",
1123 "modelFile": "/Users/bourkaki/B/Benchmarks/scheduler.mch"
1124 }
1125 */
1126
1127 extract_json_model_name(json(ObjList),MachineName) :-
1128 get_json_key_object(metadata,ObjList,List),
1129 ? get_json_attribute(modelName,List,string(MC)),
1130 atom_codes(MachineName,MC).
1131
1132
1133 % translate a single JSON transition entry into a transition_spec term for replay_trace
1134 /* here is a typical entry for the scheduler model:
1135 {
1136 "name": "ready",
1137 "params": {
1138 "rr": "process3"
1139 },
1140 "results": {
1141 },
1142 "destState": {
1143 "active": "{process3}",
1144 "waiting": "{}"
1145 },
1146 "destStateNotChanged": [
1147 "ready"
1148 ],
1149 "preds": null
1150 },
1151 */
1152 translate_json_operation(FileName,json(Json),
1153 transition_spec(OpName,Meta,
1154 ParaStore,ResultStore,DestStore,Unchanged,PredList,Postconditions) ) :-
1155 ? (get_json_attribute_with_pos(name,Json,FileName,string(OpNameC),Position)
1156 -> atom_codes(OpName,OpNameC)
1157 ; get_json_attribute_with_pos(_SomeOtherAttr,Json,FileName,_,Position)
1158 -> add_message(translate_json_operation,'Transition has no JSON "name" attribute','',Position),
1159 OpName = ''
1160 ; OpName = '', Position=unknown,
1161 add_error(translate_json_operation,'Transition has no JSON "name" or other attribute in file: ',FileName,Position)
1162 ),
1163 (debug_mode(off) -> true ; add_message(translate_json_operation,'Processing operation: ',OpName,Position)),
1164 ? (get_json_key_object(params,Json,Paras)
1165 -> translate_json_paras(Paras,params(OpName),FileName,OpName,Bindings),
1166 sort(Bindings,ParaStore) % put the parameters into the standard Prolog order
1167 ; ParaStore = []
1168 ),
1169 ? (get_json_key_object(results,Json,ResParas)
1170 -> translate_json_paras(ResParas,results(OpName),FileName,OpName,Bindings2),
1171 sort(Bindings2,ResultStore)
1172 ; ResultStore = []
1173 ),
1174 ? (get_json_key_object(destState,Json,DestState)
1175 -> translate_json_paras(DestState,destState,FileName,OpName,Bindings3),
1176 sort(Bindings3,DestStore)
1177 ; DestStore = []
1178 ),
1179 ? (get_json_key_list(destStateNotChanged,Json,UnchList)
1180 -> maplist(translate_json_string,UnchList,UnchAtoms),
1181 sort(UnchAtoms,Unchanged)
1182 ; Unchanged = []
1183 ),
1184 ? (get_json_key_list(preds,Json,JPredList)
1185 -> (maplist(translate_json_pred,JPredList,PredList) -> true
1186 ; add_error(translate_json_operation,'Unable to parse predicates for operation:',OpName,Position),
1187 PredList = []
1188 )
1189 ; PredList = []
1190 ),
1191 ? (get_json_key_list(postconditions,Json,PostconditionList)
1192 -> maplist(translate_postcondition,PostconditionList,Postconditions)
1193 ; Postconditions = []
1194 ),
1195 ? (get_json_attribute(description,Json,string(DescCodes))
1196 -> atom_codes(Desc,DescCodes), Meta = [description/Desc,pos/Position]
1197 ; Meta = [pos/Position]
1198 ).
1199
1200 translate_postcondition(Json,Postcondition) :-
1201 get_json_attribute(kind,Json,string(KindCodes)),
1202 atom_codes(Kind,KindCodes),
1203 (translate_postcondition_kind(Kind,Json,Postcondition) -> true ; add_error(translate_postcondition,'translate_postcondition_kind failed',Json), fail).
1204
1205 translate_postcondition_kind('PREDICATE',Json,state_predicate(TPred)) :-
1206 !,
1207 get_json_attribute(predicate,Json,PredString),
1208 translate_json_pred(PredString,TPred).
1209 translate_postcondition_kind(Kind,Json,operation_enabled(OpName,TPred,Enabled)) :-
1210 enabled_kind(Kind,Enabled),
1211 !,
1212 get_json_attribute(operation,Json,string(OpNameCodes)),
1213 atom_codes(OpName,OpNameCodes),
1214 get_json_attribute(predicate,Json,PredString),
1215 (PredString = string([]) -> TPred = b(truth,pred,[])
1216 ; translate_json_pred(PredString,TPred)
1217 ).
1218
1219 enabled_kind('ENABLEDNESS',enabled).
1220 enabled_kind('DISABLEDNESS',disabled).
1221
1222
1223 get_json_key_object(Key,JSON,Object) :-
1224 ? get_json_attribute(Key,JSON,JObject),
1225 JObject \= @(null),
1226 (JObject = json(Object) -> true
1227 ; add_internal_error('Illegal JSON object for key:',Key:JObject),
1228 fail
1229 ).
1230 get_json_key_list(Key,JSON,List) :-
1231 ? get_json_attribute(Key,JSON,JList),
1232 JList \= @(null),
1233 (JList = array(List) -> true
1234 ; add_internal_error('Illegal JSON list for key:',Key:JList),
1235 fail
1236 ).
1237
1238 translate_json_string(string(AtomCodes),Atom) :- atom_codes(Atom,AtomCodes).
1239
1240 translate_json_paras([],_,_,_,R) :- !, R=[].
1241 translate_json_paras([Eq|T],Kind,FileName,OpName,[Bind|BT]) :-
1242 translate_json_para(Eq,Kind,FileName,OpName,Bind),!,
1243 translate_json_paras(T,Kind,FileName,OpName,BT).
1244 translate_json_paras([_|T],Kind,FileName,OpName,BT) :- translate_json_paras(T,Kind,FileName,OpName,BT).
1245
1246
1247 :- use_module(probsrc(tools),[safe_read_term_from_codes/2]).
1248 :- use_module(probsrc(b_global_sets),[add_prob_deferred_set_elements_to_store/3]).
1249 translate_json_para(Equality,_Kind,FileName,OpName,json_bind(Name,Value,any,Pos)) :-
1250 xtl_mode,
1251 is_json_equality_attr_with_pos(Equality,FileName,Name,string(ExprCodes),Pos),
1252 !,
1253 (safe_read_term_from_codes(ExprCodes,Value)
1254 -> true
1255 ; add_parameter_error('read term from JSON failed',Name,ExprCodes,OpName,Pos), Value = '').
1256 % TO DO: using eval_strings is very ugly, use a better API predicate
1257 translate_json_para(Equality,Kind,FileName,OpName,json_bind(Name,Value,Type,Pos)) :-
1258 \+ xtl_mode,
1259 is_json_equality_attr_with_pos(Equality,FileName,Name,string(ExpressionCodes),Pos),
1260 !,
1261 (ignore_operation_parameter(OpName,Name) -> fail
1262 ; eval_strings:repl_parse_expression(ExpressionCodes,Typed,Type,Error)
1263 -> (Error \= none -> add_parameter_error(Error,Name,ExpressionCodes,OpName,Pos)
1264 ; Type = pred -> add_parameter_error('use of predicate instead of expression',Name,ExpressionCodes,OpName,Pos)
1265 ; Type = subst -> add_parameter_error('unexpected substitution',Name,ExpressionCodes,OpName,Pos)
1266 ; (add_prob_deferred_set_elements_to_store([],EState,visible), % value should not depend on any state
1267 eval_strings:eval_expression_direct(Typed,EState,Value)
1268 -> \+ illegal_json_binding_type(Kind,Name,Type,Pos)
1269 ; add_parameter_error('evaluation error',Name,ExpressionCodes,OpName,Pos)
1270 )
1271 )
1272 ; add_parameter_error('parsing failed error',Name,ExpressionCodes,OpName,Pos)
1273 ).
1274 translate_json_para(Para,_,_,_,_) :-
1275 add_error(translate_json_para,'Unknown JSON para:',Para),fail.
1276
1277 :- use_module(specfile,[translate_operation_name/2]).
1278 add_parameter_error(Error,Name,ExpressionCodes,OpName,Pos) :-
1279 translate_operation_name(OpName,TOp),
1280 ajoin(['Ignoring JSON value for parameter ',Name,' of ',TOp,' due to ',Error,':'], Msg),
1281 atom_codes(A,ExpressionCodes),
1282 add_error(translate_json_para,Msg,A,Pos),fail.
1283
1284 %evaluate_codes_value(ExpressionCodes,Type,Value) :-
1285 % eval_strings:repl_parse_expression(ExpressionCodes,Typed,Type,Error), Error=none,
1286 % eval_strings:eval_expression_direct(Typed,Value).
1287
1288 ?illegal_json_binding_type(destState,ID,Type,Pos) :- get_expected_type(ID,Kind,ExpectedType),!,
1289 \+ unify_types_strict(Type,ExpectedType), pretty_type(Type,TS), pretty_type(ExpectedType,ETS),
1290 ajoin(['Ignoring JSON destState value for ',Kind,' ',ID,' due to illegal type ',TS, ', expected:'], Msg),
1291 add_error(translate_json_para,Msg,ETS,Pos).
1292 illegal_json_binding_type(destState,ID,_Type,Pos) :-
1293 b_get_machine_constants(TConsts),get_texpr_ids(TConsts,CVars),
1294 b_get_machine_variables(TVars),get_texpr_ids(TVars,VVars),
1295 append(CVars,VVars,All), sort(All,SAll),
1296 get_possible_fuzzy_matches_completions_and_inner_msg(ID,SAll,FMsg),!,
1297 ajoin(['Ignoring JSON destState value for unknown identifier (did you mean ',FMsg,' ?):'],Msg),
1298 add_error(translate_json_para,Msg,ID,Pos).
1299 illegal_json_binding_type(destState,ID,_Type,Pos) :-
1300 add_error(translate_json_para,'Ignoring JSON destState value for unknown identifier:',ID,Pos).
1301 % unknown operations are now dealt with later in check_and_adapt_trace_step
1302 %illegal_json_binding_type(params(Op),ID,_Type,Pos) :-
1303 % b_or_z_mode, % otherwise no types available
1304 % \+ b_top_level_operation(Op), !,
1305 % findall(KOpid,b_top_level_operation(KOpid),Ops),
1306 % (get_possible_fuzzy_matches_completions_and_inner_msg(Op,Ops,FMsg)
1307 % -> ajoin(['Ignoring JSON value for parameter ',ID,' of unknown operation (did you mean the operation ',FMsg,' ?) : '], Msg)
1308 % ; ajoin(['Ignoring JSON value for parameter ',ID,' of unknown operation: '], Msg)),
1309 % add_error(translate_json_para,Msg,Op,Pos).
1310 illegal_json_binding_type(params(Op),ID,Type,Pos) :-
1311 b_or_z_mode, % otherwise no types available
1312 b_top_level_operation(Op),
1313 b_get_machine_operation_typed_parameters_for_animation(Op,Params),
1314 ? member(b(identifier(ID),ExpectedType,_),Params),!,
1315 \+ unify_types_strict(Type,ExpectedType), pretty_type(Type,TS), pretty_type(ExpectedType,ETS),
1316 ajoin(['Ignoring JSON value for parameter ',ID,' of operation ', Op, ' due to illegal type ',TS, ', expected:'], Msg),
1317 add_error(translate_json_para,Msg,ETS,Pos).
1318 illegal_json_binding_type(params(Op),ID,_Type,Pos) :-
1319 b_or_z_mode, % otherwise show_eventb_any_arguments makes no sense
1320 b_top_level_operation(Op),
1321 (b_get_machine_operation_typed_parameters_for_animation(Op,[]),
1322 get_preference(show_eventb_any_arguments,false)
1323 % TODO: check if ID is a valid virtual parameter, or if trace file was generated with preference set to true
1324 -> ajoin(['Ignoring JSON value for parameter ',ID,', operation has no parameters (setting SHOW_EVENTB_ANY_VALUES to TRUE may help):'],Msg)
1325 ; ajoin(['Ignoring JSON value for unknown parameter ',ID,' for:'],Msg)
1326 ),
1327 add_error(translate_json_para,Msg,Op,Pos).
1328 illegal_json_binding_type(results(Op),ID,Type,Pos) :-
1329 b_or_z_mode, % otherwise there are no typred results available
1330 b_get_machine_operation_typed_results(Op,Results), member(b(identifier(ID),ExpectedType,_),Results),!,
1331 \+ unify_types_strict(Type,ExpectedType), pretty_type(Type,TS), pretty_type(ExpectedType,ETS),
1332 ajoin(['Ignoring JSON value for result ',ID,' of operation ', Op, ' due to illegal type ',TS], Msg),
1333 add_error(translate_json_para,Msg,ETS,Pos).
1334 % unknown operations are now dealt with later in check_and_adapt_trace_step
1335 %illegal_json_binding_type(results(_Op),ID,_Type,Pos) :-
1336 % add_error(translate_json_para,'Ignoring JSON value for unknown operation result:',ID,Pos).
1337
1338 get_expected_type(ID,variable,ExpectedType) :- bmachine_is_precompiled, b_is_variable(ID,ExpectedType).
1339 get_expected_type(ID,constant,ExpectedType) :- bmachine_is_precompiled, b_is_constant(ID,ExpectedType).
1340
1341 % TODO: already check results, ...
1342
1343 translate_json_pred(string(PredCodes),TPred) :-
1344 OuterQuantifier = no_quantifier,
1345 % TO DO: parse in context of operation ! otherwise we get type error for pp=pp for example where pp is a parameter
1346 eval_strings:repl_parse_predicate(PredCodes,OuterQuantifier,TPred,_TypeInfo). % , print(pred_ok(TPred)),nl.
1347
1348 /*
1349 after reading a JSON ProB2-UI file looks like this:
1350
1351 json([description=string([70,105,108,101,32,99,114,101,97,116,101,100,32,98,121,32,80,114,111,66,32,84,99,108,47,84,107]),transitionList=array([json([name=string([36,105,110,105,116,105,97,108,105,115,101,95,109,97,99,104,105,110,101]),params=json([]),results=json([]),destState=json([active=string([123,125]),ready=string([123,125]),waiting=string([123,125])]),destStateNotChanged=array([]),preds=@(null)]),json([name=string([110,101,119]),params=json([pp=string([112,114,111,99,101,115,115,51])]),results=json([]),destState=json([waiting=string([123,112,114,111,99,101,115,115,51,125])]),destStateNotChanged=array([string([97,99,116,105,118,101]),string([114,101,97,100,121])]),preds=@(null)]),json([name=string([114,101,97,100,121]),params=json([rr=string([112,114,111,99,101,115,115,51])]),results=json([]),destState=json([active=string([123,112,114,111,99,101,115,115,51,125]),waiting=string([123,125])]),destStateNotChanged=array([string([114,101,97,100,121])]),preds=@(null)]),json([name=string([115,119,97,112]),params=json([]),results=json([]),destState=json([active=string([123,125]),waiting=string([123,112,114,111,99,101,115,115,51,125])]),destStateNotChanged=array([string([114,101,97,100,121])]),preds=@(null)]),json([name=string([110,101,119]),params=json([pp=string([112,114,111,99,101,115,115,50])]),results=json([]),destState=json([waiting=string([123,112,114,111,99,101,115,115,50,44,112,114,111,99,101,115,115,51,125])]),destStateNotChanged=array([string([97,99,116,105,118,101]),string([114,101,97,100,121])]),preds=@(null)])]),metadata=json([fileType=string([84,114,97,99,101]),formatVersion=number(1),savedAt=string([50,48,50,49,45,49,48,45,49,51,84,49,51,58,51,56,58,48,50,90]),creator=string([116,99,108,116,107,32,40,108,101,117,115,99,104,101,108,41]),proBCliVersion=string([49,46,49,49,46,49,45,110,105,103,104,116,108,121]),proBCliRevision=string([51,99,98,56,48,48,98,98,97,100,102,101,97,102,52,102,53,56,49,51,50,55,50,52,53,53,48,55,97,53,53,97,101,53,97,53,101,54,54,100]),modelName=string([115,99,104,101,100,117,108,101,114]),modelFile=string([47,85,115,101,114,115,47,108,101,117,115,99,104,101,108,47,103,105,116,95,114,111,111,116,47,112,114,111,98,95,101,120,97,109,112,108,101,115,47,112,117,98,108,105,99,95,101,120,97,109,112,108,101,115,47,66,47,66,101,110,99,104,109,97,114,107,115,47,115,99,104,101,100,117,108,101,114,46,109,99,104])])])
1352
1353 */
1354
1355 % -------------------------
1356
1357 % Interactive Trace Replay API
1358
1359 % load a trace file and store it for interactive replay
1360 load_json_trace_file_for_ireplay(FileName) :-
1361 read_json_trace_file(FileName,ModelName,Trace),
1362 reset_json_trace_replay,
1363 store_json_trace(Trace,0,Len),
1364 assert(current_replay_step(1)),
1365 assert(loaded_json_trace_file(FileName,ModelName,Len)).
1366
1367 :- dynamic loaded_json_trace_file/3, json_trace_replay_step/3.
1368 :- dynamic current_replay_step/1, json_trace_replayed_info/2.
1369
1370
1371 :- use_module(eventhandling,[register_event_listener/3]).
1372 :- register_event_listener(clear_specification,reset_json_trace_replay,
1373 'Reset interactive trace replay.').
1374
1375 reset_json_trace_replay :-
1376 retractall(current_replay_step(_)),
1377 retractall(loaded_json_trace_file(_,_,_)),
1378 retractall(json_trace_replay_step(_,_,_)),
1379 retractall(json_trace_replayed_info(_,_)).
1380
1381 store_json_trace([],L,L).
1382 store_json_trace([TransSpec|T],StepNr,Len) :-
1383 S1 is StepNr + 1,
1384 get_transition_spec_txt(TransSpec,TTxt),
1385 formatsilent_with_colour(user_output,[blue],'==> Trace step ~w: ~w~n',[S1,TTxt]),
1386 phrase(check_and_adapt_trace_step(TransSpec,S1,CorrectedTransSpec),StaticErrors),
1387 assert(json_trace_replay_step(S1,CorrectedTransSpec,StaticErrors)),
1388 store_json_trace(T,S1,Len).
1389
1390 get_trace_step_info(StepNr,StepDescr) :-
1391 json_trace_replay_step(StepNr,TransSpec,StaticErrors),
1392 get_transition_spec_txt(TransSpec,TTxt),
1393 (StaticErrors = [] -> StaticTT=[]
1394 ; length(StaticErrors,NrStaticErrors),
1395 StaticTT = [' (static errors: ',NrStaticErrors,')']
1396 ),
1397 (json_trace_replayed_info(StepNr,Info),
1398 get_replay_info_text(Info,TextAtoms)
1399 -> append(StaticTT,TextAtoms,TT),
1400 ajoin([StepNr,': ',TTxt | TT],StepDescr)
1401 ; ajoin([StepNr,': ',TTxt | StaticTT],StepDescr)
1402 ).
1403
1404 get_replay_info_text(replay(FromID,TransID,MatchInfo,Errors),TextAtoms) :-
1405 transition(FromID,OperationTerm,TransID,_ToID),
1406 translate_event_with_limit(OperationTerm,40,OpTxt),
1407 !,
1408 (Errors=[] -> TextAtoms = [' * REPLAYED (', MatchInfo,') : ',OpTxt]
1409 ; length(Errors,NrErrors),
1410 TextAtoms = [' * REPLAYED (', MatchInfo, ', errors: ',NrErrors,') : ', OpTxt]
1411 ).
1412 get_replay_info_text(skipped,[' SKIPPED']) :- !.
1413 get_replay_info_text(X,[X]).
1414
1415 tk_get_stored_json_trace_description(list(List)) :-
1416 findall(StepDescr,get_trace_step_info(_,StepDescr),List).
1417
1418 get_stored_json_replay_steps(List) :-
1419 findall(ireplay_step(StepNr,TTxt,StaticErrors),
1420 (json_trace_replay_step(StepNr,TransSpec,RErrors),
1421 maplist(get_rerror,RErrors,StaticErrors),
1422 get_transition_spec_txt(TransSpec,TTxt)),
1423 List).
1424
1425 try_replay_next_step(MatchSpecs,CurStepNr,FromID,TransID,DestId,MatchInfo,TkTransInfos,TkErrors) :-
1426 (var(CurStepNr) -> current_replay_step(CurStepNr) ; true), % allow to pass a fixed Nr from ProB2
1427 (var(FromID) -> current_state_id(FromID) ; true), % allow to pass a fixed ID from ProB2
1428 json_trace_replay_step(CurStepNr,TransSpec,StaticErrors),
1429 flexible_perform_single_replay_step(FromID,TransID,DestId,MatchSpecs,TransSpec,MatchInfo),
1430 phrase(check_step_postconditions(TransSpec,DestId),Errors,StaticErrors),
1431 get_transition_name(FromID,TransID,OpName),
1432 length(Errors,NrErrs),
1433 formatsilent_with_colour(user_output,[green], '==> Replay step ~w (~w) ~w leading to state ~w~n',[CurStepNr,MatchInfo,OpName,DestId]),
1434 (NrErrs>0 -> formatsilent_with_colour(user_output,[orange],' Errors (~w): ~w~n',[NrErrs,Errors]) ; true),
1435 findall(TInfo,get_transition_info(TransSpec,TInfo),TkTransInfos),
1436 maplist(get_rerror,Errors,TkErrors). % make errors atomic for Tk
1437
1438 get_rerror(Err,Msg) :- get_replay_error(Err,Msg),!.
1439 get_rerror(Err,Msg) :- atom(Err),!,Msg=Err.
1440 get_rerror(Err,F) :- functor(Err,F,_).
1441
1442 :- use_module(translate,[translate_bexpression_with_limit/3]).
1443 % get text descriptions for a transition_spec, to be shown to user e.g. in Tk listbox:
1444 get_transition_info(transition_spec(_Op, _Meta, Paras, Results, DestStore,_UnchangedVars,_Preds,_Post),InfoTxt) :-
1445 (Kind=para, List=Paras
1446 ; Kind=result, List=Results
1447 ; Kind=dest, List=DestStore),
1448 get_binding_txt(List,Txt),
1449 ajoin([Kind,' : ',Txt],InfoTxt).
1450 get_transition_info(transition_spec(_Op, _, _, _, _,UnchangedVars,_,_),InfoTxt) :-
1451 member(ID,UnchangedVars),
1452 ajoin(['unchanged : ',ID],InfoTxt).
1453 get_transition_info(transition_spec(_Op, _, _, _, _,_,Preds,_),InfoTxt) :-
1454 member(TP,Preds),
1455 translate_bexpression_with_limit(TP,250,TPS),
1456 ajoin(['pred : ',TPS],InfoTxt).
1457
1458 get_binding_txt(List,BindingText) :-
1459 member(json_bind(Var,Value,_ValType,_ValPos),List),
1460 translate_bvalue_with_limit(Value,200,VS),
1461 ajoin([Var,'=',VS],BindingText).
1462
1463 default_match_specs([MatchSpec,MS2,MS3]) :-
1464 precise_match_spec(MatchSpec), % require precise replay
1465 ignore_dest_match_spec(MS2),
1466 opname_optimize_match_spec(MS3).
1467
1468 % expected options: precise, params_and_results, keep_name
1469 get_match_specs(precise,MS) :- !,
1470 precise_match_spec(MS1),
1471 MS=[MS1]. % allow only precise replay
1472 get_match_specs(params_and_results,MS) :- !,
1473 precise_match_spec(MS1),
1474 ignore_dest_match_spec(MS2),
1475 MS=[MS1,MS2]. % allow precise and params_and_results
1476 get_match_specs(_,MS) :- default_match_specs(MS). % allow all match_specs, including keep_name
1477
1478 %:- public ireplay/0.
1479 %ireplay :- default_match_specs(MS),
1480 % ireplay(MS).
1481 %
1482 %
1483 %ireplay(MatchSpecs) :-
1484 % replay_current_step(MatchSpecs,_),!,
1485 % ireplay(MatchSpecs).
1486 %ireplay(MatchSpecs) :-
1487 % skip_current_ireplay_step(_),
1488 % ireplay(MatchSpecs).
1489
1490 % get information about how the status of replaying a loaded trace is:
1491 get_ireplay_status(CurStepNr,Steps,Finished) :-
1492 loaded_json_trace_file(_,_,Steps),
1493 current_replay_step(CurStepNr),
1494 (CurStepNr =< Steps
1495 -> Finished=not_finished
1496 ; Finished=finished).
1497
1498 replay_of_current_step_is_possible(CurStepNr,OpName,MatchInfo,list(TransInfos),list(Errors)) :- % Tk
1499 default_match_specs(MS),
1500 try_replay_next_step(MS,CurStepNr,FromID,TransID,_DestId,MatchInfo,TransInfos,Errors),
1501 get_transition_name(FromID,TransID,OpName).
1502
1503 replay_of_current_step_is_possible_with_trans(CurStepNr,FromID,OpTerm,MatchInfo,list(Errors)) :-
1504 default_match_specs(MS),
1505 replay_of_current_step_is_possible_with_trans_aux(MS,CurStepNr,FromID,OpTerm,MatchInfo,list(Errors)).
1506
1507 replay_of_current_step_is_possible_with_trans(MatchOption,CurStepNr,FromID,OpTerm,MatchInfo,list(Errors)) :- % ProB2
1508 get_match_specs(MatchOption,MS),
1509 replay_of_current_step_is_possible_with_trans_aux(MS,CurStepNr,FromID,OpTerm,MatchInfo,list(Errors)).
1510
1511 replay_of_current_step_is_possible_with_trans_aux(MatchSpec,CurStepNr,FromID,OpTerm,MatchInfo,list(Errors)) :-
1512 try_replay_next_step(MatchSpec,CurStepNr,FromID,TransID,DestID,MatchInfo,_TransInfos,Errors),
1513 create_op_transition(FromID,TransID,DestID,OpTerm).
1514
1515 create_op_transition(FromID,TransID,DestID,op(TransID,OpName,FromID,DestID)) :-
1516 get_transition_name(FromID,TransID,OpName).
1517
1518 % try and replay as much as possible
1519 ireplay_fast_forward(NrReplayed) :-
1520 default_match_specs(MS), ireplay_fast_forward(MS,0,NrReplayed).
1521
1522 ireplay_fast_forward(MatchSpecs,Nr,NrReplayed) :-
1523 replay_current_step(MatchSpecs,_),!,
1524 N1 is Nr+1,
1525 ireplay_fast_forward(MatchSpecs,N1,NrReplayed).
1526 ireplay_fast_forward(_,NrReplayed,NrReplayed).
1527
1528 ireplay_fast_forward_with_trans(CurStepNr,FromID,NrReplayed,Transitions,MatchInfos,Errors) :-
1529 default_match_specs(MS),
1530 ireplay_fast_forward_with_trans_aux(MS,CurStepNr,FromID,0,NrReplayed,Transitions,MatchInfos,Errors).
1531
1532 ireplay_fast_forward_with_trans(MatchOption,CurStepNr,FromID,NrReplayed,Transitions,MatchInfos,Errors) :- % for ProB2
1533 get_match_specs(MatchOption,MS),
1534 ireplay_fast_forward_with_trans_aux(MS,CurStepNr,FromID,0,NrReplayed,Transitions,MatchInfos,Errors).
1535
1536 ireplay_fast_forward_with_trans_aux(MS,CurStepNr,FromID,Nr,NrReplayed,[OpTerm|T1],[MatchInfo|M1],[Errors|E1]) :-
1537 replay_current_step_with_trans(MS,CurStepNr,FromID,OpTerm,MatchInfo,Errors),!,
1538 OpTerm = op(_,_,_,DestID),
1539 N1 is Nr+1,
1540 NextStepNr is CurStepNr+1,
1541 ireplay_fast_forward_with_trans_aux(MS,NextStepNr,DestID,N1,NrReplayed,T1,M1,E1).
1542 ireplay_fast_forward_with_trans_aux(_,_,_,NrReplayed,NrReplayed,[],[],[]).
1543
1544 % replay the currently selected step of the JSON trace, matching one step of the trace with one animation step
1545 replay_current_step(CurStepNr) :- % Tk
1546 default_match_specs(MS), replay_current_step(MS,CurStepNr).
1547
1548 % try and replay current step according to match specifications
1549 replay_current_step(MatchSpecs,CurStepNr) :-
1550 try_replay_next_step(MatchSpecs,CurStepNr,FromID,TransID,_DestID,MatchInfo,_,Errors),!,
1551 extend_trace_by_transition_ids([TransID]), % update state space
1552 assert(json_trace_replayed_info(CurStepNr,replay(FromID,TransID,MatchInfo,Errors))),
1553 increase_step_nr.
1554
1555 %replay_current_step_with_trans(FromID,OpTerm,MatchInfo,Errors) :- % ProB2
1556 % default_match_specs(MS),
1557 % replay_current_step_with_trans(MS,FromID,OpTerm,MatchInfo,Errors).
1558 replay_current_step_with_trans(MatchSpecs,CurStepNr,FromID,OpTerm,MatchInfo,Errors) :- % ProB2
1559 try_replay_next_step(MatchSpecs,CurStepNr,FromID,TransID,DestID,MatchInfo,_,Errors),!,
1560 create_op_transition(FromID,TransID,DestID,OpTerm).
1561
1562 % skip the current replay step and go to the next one
1563 skip_current_ireplay_step(CurStepNr) :-
1564 current_replay_step(CurStepNr),
1565 json_trace_replay_step(CurStepNr,TransSpec,_StaticErrors),
1566 get_transition_spec_txt(TransSpec,TTxt),
1567 formatsilent_with_colour(user_output,[orange],'Skipping replay step ~w : ~w~n',[CurStepNr,TTxt]),
1568 assert(json_trace_replayed_info(CurStepNr,skipped)),
1569 increase_step_nr.
1570
1571 increase_step_nr :-
1572 retract(current_replay_step(CurStepNr)),
1573 S1 is CurStepNr+1,
1574 assert(current_replay_step(S1)).
1575
1576 % :- b_intelligent_trace_replay:load_json_trace_file_for_interactive_replay('/Users/leuschel/git_root/prob_examples/public_examples/B/CBC/ConstantsAndVars/MyAwesomeLift.prob2trace'), b_intelligent_trace_replay:ireplay.
1577
1578
1579