1 | % (c) 2009-2024 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, | |
2 | % Heinrich Heine Universitaet Duesseldorf | |
3 | % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) | |
4 | ||
5 | % tcltk_interface.pl | |
6 | :- module(tcltk_interface,[ | |
7 | tcltk_goto_state/2, | |
8 | tcltk_add_new_transition_transid/6, | |
9 | tcltk_add_new_transition/5, | |
10 | tcltk_get_options/1, tcltk_get_options_dest_info/1, tcltk_get_state/1, tcltk_get_state/2, | |
11 | tcltk_get_options_or_candidates/1, tcltk_get_options_candidate_nr_name/2, | |
12 | potentially_enabled_operation/4, | |
13 | tcltk_get_options_nr_description/2, | |
14 | tcltk_get_history_nr_description/2, | |
15 | tcltk_get_history_nr_origin_str/2, tcltk_edit_history_nr_origin_str/1, | |
16 | ||
17 | tcltk_get_state_errors/1, tcltk_get_detailed_state_error/6, | |
18 | tcltk_open_state_error_in_editor/1, | |
19 | tcltk_get_line_col/5, | |
20 | tcltk_current_state_invariant_violated/0, | |
21 | tcltk_get_status/3, | |
22 | tcltk_get_ops_with_virtual_timeout/1, | |
23 | tcltk_get_ops_with_real_timeout/1, | |
24 | tcltk_get_ops_user_interrupt_occurred/0, | |
25 | ||
26 | tcltk_compute_options/2, | |
27 | can_generate_dot_from_last_state_error/0, | |
28 | tcltk_initialise/0, tcltk_reset/0, | |
29 | ||
30 | tcltk_perform_nr/1, tcltk_random_perform/0, tcltk_random_perform/1, | |
31 | tcltk_perform_nr_option/3, | |
32 | tcltk_perform_action_term/2, | |
33 | tcltk_is_sync_event/1, | |
34 | ||
35 | tcltk_exists_an_open_node/0, tcltk_goto_an_open_node/0, | |
36 | tcltk_goto_max_reached_node/0, tcltk_goto_timeout_node/0, | |
37 | tcltk_state_exists_with_invariant_violated/0, | |
38 | tcltk_goto_an_invariant_violation/0, tcltk_goto_node_with_id/1, | |
39 | tcltk_try_goto_node_with_id/1, | |
40 | tcltk_current_node_has_no_real_transition/0, tcltk_current_node_has_real_transition/0, | |
41 | tcltk_current_node_has_partial_transition/0, | |
42 | tcltk_goto_a_non_deterministic_node/1, tcltk_goto_a_non_deterministic_output_node/1, | |
43 | tcltk_goto_a_branching_node/0, tcltk_goto_a_non_resetable_node/0, | |
44 | tcltk_state_space_only_has_root_node/0, tcltk_no_constants_or_no_inititalisation_found/0, | |
45 | tcltk_goto_event_list_property_violation/3, tcltk_goto_state_enabling_operation/2, | |
46 | tcltk_find_max_reached_node/0, tcltk_find_max_reached_node/1, | |
47 | tcltk_search_among_existing_nodes/6, | |
48 | find_invariant_violation_among_not_checked_nodes/1, | |
49 | ||
50 | tcltk_model_check/12, do_model_check/13, | |
51 | tcltk_set_dbf_mode/1, tcltk_dbf_modes/1, | |
52 | tcltk_mark_current_node_to_be_recomputed_wo_timeout/0, | |
53 | tcltk_mark_current_node_to_be_recomputed_with_random_enum/0, | |
54 | tcltk_finish_current_node_to_be_recomputed_with_random_enum/0, | |
55 | tcltk_hash_model_checking_imprecise/0, | |
56 | tcltk_clear_state_space_and_refocus_to_current_state/0, | |
57 | ||
58 | tcltk_constraint_based_check/1, | |
59 | tcltk_constraint_based_check_op/2, tcltk_constraint_based_check_op/3, | |
60 | tcltk_constraint_based_check/2, tcltk_constraint_based_check_with_timeout/2, | |
61 | tcltk_cbc_find_trace/2, tcltk_cbc_find_trace/4, | |
62 | tcltk_cbc_refinement_check/2, | |
63 | tcltk_constraint_find_valid_state/0, | |
64 | tcltk_constraint_find_maximal_valid_state/0, | |
65 | tcltk_constraint_find_dynamic_assertion_violation/0, | |
66 | tcltk_constraint_find_valid_state_with_pred/3, | |
67 | tcltk_constraint_find_deadlock_state/1, | |
68 | tcltk_constraint_find_deadlock_state_with_goal/2, | |
69 | tcltk_constraint_find_deadlock_state_with_goal/3, | |
70 | tcltk_constraint_find_static_assertion_violation/1, | |
71 | tcltk_check_if_feasible_operation/4, | |
72 | cbc_constraint_find_static_assertion_violation/2, | |
73 | ||
74 | tcltk_can_backtrack/0, tcltk_can_backtrack/1, | |
75 | tcltk_can_forward/0, tcltk_can_forward/1, | |
76 | tcltk_prepare_for_reload/0, | |
77 | tcltk_backtrack/0, tcltk_backtrack/1, tcltk_fast_forward/0, tcltk_forward/0, | |
78 | tcltk_execute_trace_to_current_node/0, tcltk_find_shortest_trace_to_current_node/1, | |
79 | tcltk_execute_longest_trace_from/1, | |
80 | tcltk_get_history/1, | |
81 | tcltk_perform_action_string/3, | |
82 | tcltk_perform_action/2, | |
83 | tcltk_perform_action/4, | |
84 | ||
85 | tcltk_open_xtl_file/1, | |
86 | tcltk_open_z_file/1, tcltk_open_z_tex_file/1, | |
87 | tcltk_open_alloy_file/1, tcltk_open_alloy_prolog_ast_file/1, | |
88 | tcltk_open_cspm_file/1, | |
89 | tcltk_add_csp_file/1, | |
90 | tcltk_load_packaged_eventb_file/1, | |
91 | ||
92 | tcltk_verify_alloy_cmd/4, | |
93 | tcltk_get_alloy_cmd_names/1, | |
94 | tcltk_load_alloy_cmd_in_current_translation/1, | |
95 | ||
96 | tcltk_machine_has_assertions/0, | |
97 | tcltk_unique_top_level_operation/1, tcltk_top_level_operations/1, | |
98 | ||
99 | compute_all_transitions_if_necessary/0, | |
100 | compute_all_transitions_if_necessary/2, | |
101 | compute_state_information_if_necessary/0, % compute transitions and invariant | |
102 | compute_invariant_if_necessary/3, | |
103 | check_invariantKO/2, | |
104 | check_invariantKO/3, | |
105 | catch_clpfd_overflow_call_for_state/4, | |
106 | compute_ample_set/3, | |
107 | compute_all_transitions/2, | |
108 | add_transition/4, | |
109 | add_trans_id/5, | |
110 | ||
111 | tcltk_has_eventtrace/1, tcltk_show_eventtrace/2, | |
112 | tcltk_execute_model/3, | |
113 | tcltk_execute_trace_to_node/1, | |
114 | find_shortest_trace_to_node/4, | |
115 | ||
116 | tcltk_explore_csp_process/2, tcltk_visualize_csp_process/2, | |
117 | get_csp_process_id/2, get_csp_process_stats/1, | |
118 | ||
119 | tcltk_add_user_executed_operation/2, tcltk_add_user_executed_operation/3, | |
120 | tcltk_add_user_executed_operation_typed/4, tcltk_add_user_executed_operation_typed/6, | |
121 | tcltk_add_user_executed_statement/3, | |
122 | tcltk_add_user_modify_variable_transition/2, tcltk_add_user_modify_variable_transition/4, | |
123 | ||
124 | tcltk_analyse_option/2, | |
125 | tcltk_analyse/6, | |
126 | tcltk_show_typing_of_variables_and_constants/1, | |
127 | tcltk_show_current_state/1, | |
128 | tcltk_eval/4, interruptable_tcltk_eval/4, | |
129 | tcltk_eval_as_table/2, | |
130 | tcltk_find_value_as_table/3, find_value_in_cur_state/4, | |
131 | tcltk_time_call/1, eval_elapsed_time/1, | |
132 | tcltk_check_csp_assertions/2, tcltk_check_csp_assertion/4, tcltk_check_csp_assertion/5, | |
133 | checkAssertion/5, | |
134 | tcltk_get_vacuous_guards/1, | |
135 | tcltk_get_vacuous_invariants/1, tcltk_get_vacuous_invariants_table/1, | |
136 | tcltk_operations_covered_info/3, operation_name_covered/1, | |
137 | tcltk_show_expression_as_dot/2, | |
138 | generate_dot_from_formula/2, | |
139 | generate_dot_from_invariant/1, generate_dot_from_properties/1, generate_dot_from_assertions/1, | |
140 | generate_dot_from_deadlock_po/1, generate_dot_from_goal/1, generate_dot_from_last_span_predicate/1, | |
141 | generate_dot_from_operation/2, generate_dot_from_operation_with_params/4, | |
142 | write_dot_file_for_pred_expr/2, | |
143 | write_dot_file_for_value_as_tree/2, | |
144 | evaluate_expression_over_history_to_csv_file/2, tcltk_evaluate_expression_over_history/2, | |
145 | translate_error_for_tclk/2, | |
146 | tcltk_set_initial_machine/0, tcltk_clear_machine/0, | |
147 | % set_tcltk_cspm_mode/0, unset_tcltk_cspm_mode/0, | |
148 | tcltk_open_b_file/1, tcltk_open_b_file_for_minor_mode/2, | |
149 | tcltk_run_ltsmin/5, | |
150 | tcltk_save_history_as_trace_file/1, | |
151 | tcltk_execute_until/1, | |
152 | ||
153 | tcltk_bv_get_tops/1, | |
154 | tcltk_bv_get_structure/3, | |
155 | tcltk_bv_is_leaf/2, | |
156 | tcltk_bv_get_values/3, | |
157 | tcltk_bv_get_value/3, | |
158 | tcltk_bv_show_formula_as_dot_tree/2, | |
159 | tcltk_bv_show_formula_as_dot_graph/2, | |
160 | tcltk_bv_show_value_as_dot_tree/2, | |
161 | tcltk_bv_get_pp_formula_string/2, | |
162 | tcltk_show_identifier_value_as_dot_tree/2, % a version of the above independent of bvisual | |
163 | tcltk_bv_get_unlimited_value/3, tcltk_bv_get_unlimited_value_atom/2, | |
164 | ||
165 | tcltk_get_constants_predicate/2, | |
166 | ||
167 | get_cbc_data_base_id_checks/1, get_cbc_data_base_text_checks/1, | |
168 | ||
169 | tcltk_write_uml_sequence_chart/1, | |
170 | ||
171 | get_ltl_formulas_from_file/2, | |
172 | tcltk_mcts_auto_play/0, tcltk_mcts_auto_play/1, mcts_auto_play_available/0, | |
173 | tcltk_simplify_predicate/2 % not used | |
174 | ||
175 | ]). | |
176 | ||
177 | ||
178 | :- meta_predicate call_pred_on_expanded_state(3,-,-,-). | |
179 | :- meta_predicate map_over_history(3,-). | |
180 | :- meta_predicate tcltk_time_call(0). | |
181 | :- meta_predicate catch_clpfd_overflow_call_for_state(-,-,0,0). | |
182 | :- meta_predicate add_csp_process_id1(-,-,1). | |
183 | ||
184 | %prob_use_module(X) :- load_files(X,[if(changed),load_type(source),compilation_mode(compile)]). | |
185 | ||
186 | /* load all necessary modules */ | |
187 | :- use_module(library(random)). | |
188 | :- use_module(library(lists)). | |
189 | ||
190 | :- use_module(library(ordsets)). | |
191 | :- use_module(library(avl)). | |
192 | ||
193 | :- use_module(module_information). | |
194 | :- module_info(group,tcltk). | |
195 | :- module_info(description,'Interface between the Tcl/Tk GUI, the CLI and the internal modules.'). | |
196 | ||
197 | :- use_module(typechecker). | |
198 | :- use_module(self_check). | |
199 | :- use_module(probsrc(preferences)). | |
200 | :- use_module(probsrc(debug)). | |
201 | :- use_module(error_manager). | |
202 | :- use_module(kernel_waitflags). | |
203 | :- use_module(probsrc(tools)). | |
204 | :- use_module(probsrc(specfile)). | |
205 | :- use_module(probsrc(state_space_exploration_modes)). | |
206 | :- use_module(probsrc(translate)). | |
207 | :- use_module(probsrc(b_state_model_check)). | |
208 | :- use_module(probsrc(bmachine)). | |
209 | :- use_module(probsrc(b_interpreter)). | |
210 | :- use_module(probsrc(b_trace_checking)). | |
211 | :- use_module(probsrc(bsyntaxtree)). | |
212 | :- use_module(kernel_objects). | |
213 | :- use_module(probsrc(state_space)). | |
214 | :- use_module(extension('plspec/plspec/plspec_core'),[set_error_handler/1]). | |
215 | :- use_module(extension('plspec/plspec/prettyprinter'),[pretty_print_error/1]). | |
216 | prob_plspec_error_handler(ErrTerm) :- pretty_print_error(ErrTerm), | |
217 | add_internal_error('Error detected by plspec',ErrTerm). | |
218 | :- set_error_handler(tcltk_interface:prob_plspec_error_handler). | |
219 | :- use_module(model_checker). | |
220 | :- use_module(dotsrc(visualize_graph)). | |
221 | :- use_module(dotsrc(state_space_reduction)). | |
222 | :- use_module(dotsrc(state_custom_dot_graph)). | |
223 | :- use_module(succeed_max). | |
224 | %:- use_module(testcase_generator). | |
225 | :- use_module(prozsrc(proz),[open_proz_file/2]). | |
226 | :- use_module(dotsrc(reduce_graph_state_space)). | |
227 | :- use_module(b_read_write_info). | |
228 | %:- use_module(flow). | |
229 | :- use_module(probltlsrc(ltl),[parse_and_preprocess_formula/3, parse_ltlfile/2, | |
230 | pp_ltl_formula/2, ltl_model_check_with_ce1/5, | |
231 | is_fairness_implication/1, tcltk_play_ltl_counterexample/2]). | |
232 | :- use_module(probltlsrc(ctl),[ctl_model_check_with_ce/6, tcltk_play_ctl_counterexample/2]). | |
233 | %:- use_module('promela/promela_ncprinter'). | |
234 | :- use_module(extrasrc(bvisual2),[bv_get_top_level/1, bv_expand_formula/3, bv_get_values/3, bv_get_btvalue/4, | |
235 | bv_get_stored_formula_expr/2, bv_is_explanation_node/1, bv_get_value_unlimited/3]). | |
236 | :- use_module(b_show_history). | |
237 | :- use_module(probcspsrc(haskell_csp),[parse_single_csp_declaration/3]). | |
238 | %:- use_module(probcspsrc(slicer_csp),[slice_from_program_point/14]). | |
239 | :- use_module(value_persistance, [save_constants/1,add_new_transitions_to_cache/1]). | |
240 | :- use_module(cbcsrc(cbc_path_solver),[create_testcase_path_nondet/4,verify_alloy_command/5]). | |
241 | :- use_module(dotsrc(uml_generator),[write_uml_sequence_chart/1]). | |
242 | ||
243 | /* main program | |
244 | * ============ | |
245 | */ | |
246 | ||
247 | ||
248 | /* | |
249 | Start server for LTSmin | |
250 | */ | |
251 | ||
252 | :- use_module(extension('ltsmin/ltsmin'),[start_ltsmin/4]). | |
253 | ||
254 | ||
255 | % Backend = symbolic or sequential | |
256 | tcltk_run_ltsmin(Backend,NoDead,NoInv,UsePOR,Res) :- | |
257 | % in case of POR we can also enable/disable use_cbc_analysis preference | |
258 | (UsePOR == true -> MoreFlags = [por] ; MoreFlags=[]), | |
259 | start_ltsmin(Backend, [NoDead, NoInv], MoreFlags,Result), | |
260 | get_check_name(NoDead,NoInv,Kind), | |
261 | process_ltsmin_result(Result,Kind,Res). | |
262 | ||
263 | get_check_name(true,false,Kind) :- Kind = 'INVARIANT'. | |
264 | get_check_name(false,true,Kind) :- Kind = 'DEADLOCK'. | |
265 | ||
266 | :- use_module(tools_printing,[format_with_colour_nl/4]). | |
267 | :- use_module(extension('ltsmin/ltsmin_trace'),[csv_to_trace/3]). | |
268 | process_ltsmin_result(ltsmin_model_checking_ok,Kind,no_counter_example_found) :- | |
269 | format_with_colour_nl(user_output,green,'LTSMin found no ~w Counter Example',Kind). | |
270 | process_ltsmin_result(ltsmin_counter_example_found(CsvFile),Kind,counter_example_found) :- | |
271 | format_with_colour_nl(user_output,[red,bold],'LTSMin found ~w Counter Example',Kind), | |
272 | csv_to_trace(CsvFile,States,Transitions), | |
273 | print('*** REPLAYING TRACE: '),nl,print(Transitions),nl, | |
274 | %print(States),nl, | |
275 | length(Transitions,TLen), length(States,SLen), print(len(TLen,SLen)),nl, | |
276 | (replay_ltsmin_trace(States,Transitions) -> true | |
277 | ; add_error(process_ltsmin_result,'Could not replay trace:',Transitions)). | |
278 | ||
279 | replay_ltsmin_trace([],[]). | |
280 | replay_ltsmin_trace([State|TS],[TransName|TT]) :- | |
281 | (find_successor_state(State,TransName) | |
282 | -> replay_ltsmin_trace(TS,TT) | |
283 | ; add_error(replay_ltsmin_trace,'Could not execute:',TransName),fail). | |
284 | ||
285 | :- use_module(state_space,[visited_expression/2]). | |
286 | find_successor_state(EState,'$init_state') :- !, | |
287 | compute_all_inits, | |
288 | is_initial_state_id(NewID), | |
289 | state_space:visited_expression(NewID,State), | |
290 | %print(try_init(NewID,State)),nl, print(EState),nl, | |
291 | expand_const_and_vars_to_full_store(State,EState1), | |
292 | %print(EState1),nl, | |
293 | EState1=EState, | |
294 | tcltk_reset, | |
295 | tcltk_execute_trace_to_node(NewID). | |
296 | find_successor_state(EState,TransName) :- | |
297 | tcltk_get_options(_), | |
298 | current_options(Options), % print(Options),nl, | |
299 | member( (_,_Action,ActionOpAsTerm,NewID), Options), | |
300 | get_operation_name(ActionOpAsTerm,TransName), | |
301 | visited_expression(NewID,State), | |
302 | expand_const_and_vars_to_full_store(State,EState), % TO DO: only compare variables | |
303 | tcltk_goto_state(ActionOpAsTerm,NewID). | |
304 | ||
305 | % compute all INITIALISATIONS | |
306 | compute_all_inits :- is_concrete_constants_state_id(ID), compute_all_transitions_if_necessary(ID),fail. | |
307 | compute_all_inits. | |
308 | ||
309 | /* -------------------------------------------------------------------- */ | |
310 | ||
311 | %tcltk_find_untyped_vars(Vs) :- find_untyped_vars(Vs), Vs \== []. | |
312 | ||
313 | %tcltk_find_untyped_consts(Vs) :- find_untyped_consts(Vs), Vs \== []. | |
314 | ||
315 | ||
316 | tcltk_find_max_reached_node :- tcltk_find_max_reached_node(_). | |
317 | tcltk_find_max_reached_node(ID) :- max_reached_or_timeout_for_node(ID). | |
318 | ||
319 | ||
320 | tcltk_current_node_has_no_real_transition :- | |
321 | \+ tcltk_current_node_has_real_transition. | |
322 | tcltk_current_node_has_real_transition :- | |
323 | current_state_id(CurID), | |
324 | transition(CurID,Trans,_), | |
325 | (CurID \= root -> true | |
326 | ; Trans = '$partial_setup_constants' | |
327 | -> properties_were_filtered(Nr), Nr>0 % we probably have the partial transition due to prob-ignore pragmas | |
328 | % TODO: detect whether any of the other non-ignored conjuncts were false/unknown | |
329 | ; true),!. | |
330 | tcltk_current_node_has_partial_transition :- | |
331 | current_state_id(CurID), | |
332 | transition(CurID,'$partial_setup_constants',_),!. | |
333 | ||
334 | /* --------------------------------------------------------------------- */ | |
335 | tcltk_goto_state(ActionAsTerm,StateID) :- | |
336 | (var(StateID) -> print_message(var_state(tcltk_goto_state(ActionAsTerm,StateID))) ; true), | |
337 | (visited_expression_id(StateID) | |
338 | -> true | |
339 | ; ajoin(['State ',StateID,' does not exist. Could be due to insufficient memory. Transition: '],Msg), | |
340 | add_warning(tcltk_goto_state,Msg,ActionAsTerm), | |
341 | fail | |
342 | ), | |
343 | (current_state_id(CurID) -> true ; print('*** no_current_state_id ***'),nl), | |
344 | ((transition(CurID,ActionAsTerm,OpID,StateID) | |
345 | ; ActionAsTerm=jump, % jump is used by ProB2 and eclipse interface; TODO: use $jump or something similar to avoid confusion with real operation names | |
346 | transition(CurID,_,OpID,StateID) | |
347 | ) | |
348 | -> /* ok: proper transition */ | |
349 | set_current_state_id(StateID), | |
350 | add_to_op_trace_ids(OpID), | |
351 | add_id_to_history(CurID) | |
352 | ; (CurID=StateID | |
353 | -> true | |
354 | ; ActionAsTerm=jump, find_id_in_history(StateID,Pos) | |
355 | -> tcltk_backtrack(Pos) | |
356 | ; ActionAsTerm=jump, find_id_in_forward_history(StateID,Pos) | |
357 | -> tcltk_forward(Pos) | |
358 | ; set_current_state_id(StateID), | |
359 | add_to_op_trace_ids(jump(StateID)), | |
360 | add_id_to_history(CurID) | |
361 | ) | |
362 | ). | |
363 | ||
364 | find_id_in_history(StateId,Position) :- history(H), nth1(Position,H,StateId). | |
365 | find_id_in_forward_history(StateId,Position) :- forward_history(H), nth1(Position,H,forward(StateId,_)). | |
366 | ||
367 | add_id_to_history(CurID) :- | |
368 | (retract(history(History))->true ; add_warning(add_id_to_history,'No history for: ',CurID),History=[]), | |
369 | (History=[],CurID\=root | |
370 | -> add_message(add_id_to_history,'Adding missing root to history: ',CurID), | |
371 | assertz(history([CurID,root])) % fix error in synchronisation between ProB2UI and prolog; should be fixed now | |
372 | ; assertz(history([CurID|History])) | |
373 | ), | |
374 | retractall(forward_history(_)). | |
375 | ||
376 | ||
377 | ||
378 | tcltk_add_new_transition(FromID,Action,ToID,ToTempl,TransInfo) :- | |
379 | tcltk_add_new_transition_transid(FromID,Action,ToID,ToTempl,TransInfo,_TransId). | |
380 | ||
381 | tcltk_add_new_transition_transid(FromID,Action,ToID,ToTempl,TransInfo,TransId) :- | |
382 | add_new_transition_transid_with_infos(FromID,[],Action,ToID,ToTempl,TransInfo,TransId). | |
383 | ||
384 | % variation with precomputed infos for the initial id (FromID) | |
385 | add_new_transition_transid_with_infos(FromID,PrecomputedInfos,Action,ToID,ToTempl,TransInfo,TransId) :- | |
386 | get_operation_internal_name(Action,OpName), % will use $initialise_machine; important for test 961 and PROOF_INFO | |
387 | get_id_of_node_and_add_if_required_with_skip_check(ToTempl,ToID,Exists,FromID,OpName,PrecomputedInfos), | |
388 | %% print_message(tcltk_add_new_transition(FromID,Action,ToID,Exists)), %% | |
389 | ( Exists=exists, | |
390 | (preferences:preference(store_only_one_incoming_transition,true), | |
391 | \+ operation_not_yet_covered(OpName) % otherwise store at least one transition to achieve coverage | |
392 | %-> transition(FromID,_,_,_) % add transition to prevent deadlock being detected | |
393 | ; transition(FromID,Action,TransId,ToID) | |
394 | ) | |
395 | -> true % // the transition already exists | |
396 | ; % we have a new transition: | |
397 | (preferences:preference(forget_state_space,true) | |
398 | -> (any_transition(FromID,_,_) -> true | |
399 | ; store_transition(FromID,'$NO_DEADLOCK',FromID,TransId)) /* just add dummy transition */ | |
400 | ; store_transition(FromID,Action,ToID,TransId | |
401 | ), | |
402 | (store_transition_infos(TransInfo,TransId) -> true | |
403 | ; add_internal_error('storing transition info failed', store_transition_infos(TransInfo,TransId))) | |
404 | ), | |
405 | mark_operation_as_covered(OpName), | |
406 | (FromID \= ToID, b_or_z_mode, not_all_transitions_added(ToID) | |
407 | -> add_additional_destination_infos(FromID,OpName,ToID) % e.g., PROOF_INF | |
408 | ; true | |
409 | ) | |
410 | ). | |
411 | ||
412 | % a version that checks for query/skip operations and then simply returns old id | |
413 | get_id_of_node_and_add_if_required_with_skip_check(_ToTempl,ToID,Exists,FromID,OpName,_PrecomputedInfos) :- | |
414 | animation_mode(b), % csp_and_b mode is *not* ok ; see test 1701 for example | |
415 | b_operation_cannot_modify_state(OpName), | |
416 | !, | |
417 | Exists=exists,ToID=FromID. | |
418 | get_id_of_node_and_add_if_required_with_skip_check(ToTempl,ToID,Exists,FromID,OpName,PrecomputedInfos) :- | |
419 | get_id_of_node_and_add_if_required(ToTempl,ToID,Exists,FromID,OpName,PrecomputedInfos). | |
420 | ||
421 | ||
422 | ||
423 | % will only be called for ToID different from FromID | |
424 | add_additional_destination_infos(FromID,ActionName,ToID) :- %print(add_dest(FromID,ActionName,ToID)),nl, | |
425 | ( b_operation_preserves_invariant(ActionName,Full), | |
426 | \+ invariant_still_to_be_checked(FromID), | |
427 | \+ invariant_violated(FromID) % if Invariant does not hold in previous state, we have no idea about invariants in the new state (proof assumes invariant holds before) | |
428 | -> | |
429 | (retract(specialized_inv(ToID,OldAVL)) -> true ; empty_avl(OldAVL)), | |
430 | (Full=full_invariant | |
431 | -> assertz(specialized_inv(ToID,invariant_preserved)) | |
432 | % unfortunately we cannot yet retract invariant_not_yet_checked | |
433 | ; OldAVL=invariant_preserved | |
434 | -> assertz(specialized_inv(ToID,invariant_preserved)) | |
435 | ; avl_store(ActionName,OldAVL,nothing,NewAVL), | |
436 | %print(specialized_inv(ToID,NewAVL)),nl, | |
437 | assertz(specialized_inv(ToID,NewAVL)) | |
438 | ) | |
439 | ; true | |
440 | ). | |
441 | /* | |
442 | Store names of incoming transitions in an AVL tree | |
443 | Used to calculate invariant in presence of proof information | |
444 | */ | |
445 | % we could store this information to allow us to quickly generate traces from root to any state | |
446 | %add_additional_destination_infos(FromID,ActionName,ToID) :- | |
447 | % (reached_via(ToID,_,_) -> true ; %TODO: probably we should also store transition id | |
448 | % assertz(reached_via(ToID,ActionName,FromID))). | |
449 | % TO DO: we could store pge disabling information here disabled_operations | |
450 | ||
451 | ||
452 | ||
453 | ||
454 | :- use_module(state_space_exploration_modes,[set_depth_breadth_first_mode/1,dbf_modes/3]). | |
455 | ||
456 | ||
457 | tcltk_set_dbf_mode(Nr) :- dbf_modes(Nr,Mode,_),!, | |
458 | set_depth_breadth_first_mode(Mode). | |
459 | tcltk_set_dbf_mode(Nr) :- add_error(tcltk_set_dbf_mode,'Unknown mode: ',Nr). | |
460 | ||
461 | tcltk_dbf_modes(list(Names)) :- findall(N,dbf_modes(_,_,N),Names). | |
462 | ||
463 | ||
464 | :- dynamic prev_randomise_enumeration_order/1. | |
465 | ||
466 | ||
467 | tcltk_mark_current_node_to_be_recomputed_wo_timeout :- | |
468 | current_state_id(CurID), print(recomputing(CurID)),nl, | |
469 | tcltk_mark_node_for_recomputed(CurID), | |
470 | (use_no_timeout(CurID) -> true ; assertz(use_no_timeout(CurID))). | |
471 | ||
472 | % used for recomputing a state with random enumeration | |
473 | tcltk_mark_current_node_to_be_recomputed_with_random_enum :- | |
474 | current_state_id(CurID), print(recomputing(CurID)),nl, | |
475 | tcltk_mark_node_for_recomputed(CurID), | |
476 | % retractall(transition(CurID,_,_,_)), % we no longer delete the transitions; this can cause problems with the history | |
477 | % (if CurID appears earlier in the history and we remove the transition) and lead to error messages | |
478 | % also: for the user it may be good to add new transitions incrementally | |
479 | temporary_set_preference(randomise_enumeration_order,true,PREV), | |
480 | assertz(prev_randomise_enumeration_order(PREV)). | |
481 | ||
482 | tcltk_finish_current_node_to_be_recomputed_with_random_enum :- | |
483 | retract(prev_randomise_enumeration_order(PREV)), | |
484 | reset_temporary_preference(randomise_enumeration_order,PREV). | |
485 | ||
486 | tcltk_mark_node_for_recomputed(ID) :- | |
487 | (not_all_transitions_added(ID) -> true | |
488 | ; add_id_at_front(ID)), | |
489 | retractall(max_reached_for_node(ID)), | |
490 | retractall(time_out_for_node(ID,_,_)), | |
491 | % TODO: clear b_operation_cache:operation_cached_time_out for relevant nodes | |
492 | (transition(ID,'$partial_setup_constants',ID2) | |
493 | -> delete_node(ID2) ; true), | |
494 | retractall(state_space:transition(ID,'$partial_setup_constants',_,_)). | |
495 | ||
496 | transitions_computed_for_node(ID) :- | |
497 | \+(not_all_transitions_added(ID)), | |
498 | \+ is_not_interesting(ID). | |
499 | ||
500 | ||
501 | ||
502 | ||
503 | tcltk_set_initial_machine :- | |
504 | tcltk_clear_machine, | |
505 | b_set_initial_machine, | |
506 | set_animation_mode(b). | |
507 | ||
508 | % new profiler | |
509 | %:- use_module('../extensions/profiler/profiler.pl'). | |
510 | % old profiler | |
511 | %:- use_module(runtime_profiler,[reset_runtime_profiler/0]). | |
512 | ||
513 | % the following clears the entire state space and uses the current state as now starting initial state | |
514 | tcltk_clear_state_space_and_refocus_to_current_state :- | |
515 | Op = 'REFOCUS-ANIMATOR', | |
516 | current_expression(_CurID,CurState), | |
517 | (CurState = const_and_vars(ConstID,VarState) -> | |
518 | visited_expression(ConstID,ConstState), | |
519 | transition(root,SetupConstantAction,_,ConstID) | |
520 | ; ConstState=[]), | |
521 | announce_event(reset_specification), | |
522 | (ConstState=[] | |
523 | -> add_trans_id(root,Op,CurState,NewID,_TransId) | |
524 | ; add_trans_id(root,SetupConstantAction,ConstState,NewConstID,_), | |
525 | add_trans_id(NewConstID,Op,const_and_vars(NewConstID,VarState),NewID,_), | |
526 | (retract_open_node(NewConstID) -> true ; true), | |
527 | tcltk_goto_state(Op,NewConstID) | |
528 | ), | |
529 | tcltk_goto_state(Op,NewID), | |
530 | (retract_open_node(root) -> true ; true). | |
531 | ||
532 | :- use_module(eventhandling, [announce_event/1]). | |
533 | tcltk_clear_machine :- | |
534 | announce_event(reset_specification), | |
535 | announce_event(clear_specification), | |
536 | reset_tcltk_interface, | |
537 | set_animation_mode(b), | |
538 | bmachine:b_set_empty_machine, | |
539 | reset_errors, | |
540 | % reset_dynamics, % new profiler | |
541 | % Now done via event handling: b_interpreter:reset_b_interpreter,reset_model_checker, | |
542 | garbage_collect_atoms. % reclaim atoms if possible | |
543 | ||
544 | :- use_module(prob2_interface, [start_animation/0, update_preferences_from_spec/0]). | |
545 | tcltk_initialise :- | |
546 | update_preferences_from_spec, | |
547 | start_animation, | |
548 | tcltk_try_reload. | |
549 | ||
550 | tcltk_reset :- reset_errors, | |
551 | history(H), op_trace_ids(T), | |
552 | current_state_id(CurID), | |
553 | (forward_history(FH) -> true ; FH=[]), | |
554 | state_space_reset, | |
555 | gen_forward_history(T,[CurID|H],FH,FwdHist), | |
556 | retractall(forward_history(_)), | |
557 | assertz(forward_history(FwdHist)). | |
558 | ||
559 | gen_forward_history([],H,R,R) :- (H=[root] -> true ; print(unexpected_remaining_hist(H)),nl). | |
560 | gen_forward_history([A|TT],[ID|TH],Acc,Res) :- | |
561 | gen_forward_history(TT,TH,[forward(ID,A)|Acc],Res). | |
562 | ||
563 | ||
564 | ||
565 | /* ---- getting available options ----- */ | |
566 | ||
567 | tcltk_get_options_dest_info(L) :- | |
568 | tcltk_get_options_dest_info(L,[]). %[no_query_operations]). | |
569 | tcltk_get_options_dest_info(list(DestinationInfos),Options) :- | |
570 | debug_println(9,start_tcltk_get_options_dest_info), | |
571 | % get a list of all options which are skip events | |
572 | current_state_id(CurID), | |
573 | compute_all_transitions_if_necessary(CurID), | |
574 | tcltk_get_candidates_dest_info(List1), | |
575 | findall(Res, (transition(CurID,ActionAsTerm,ID), % check no_query_operations | |
576 | (member(no_query_operations,Options) -> \+ query_op_transition(ActionAsTerm) ; true), | |
577 | option_dest_info(ID,ActionAsTerm,CurID,Res)), | |
578 | DestinationInfos,List1). | |
579 | ||
580 | option_dest_info(DestID,ActionAsTerm,CurID,Res) :- DestID=CurID,!, | |
581 | (query_op_transition(ActionAsTerm) -> Res=query ; Res=skip). | |
582 | option_dest_info(DestID,_,_,Res) :- not_all_transitions_added(DestID),!, Res=open. | |
583 | option_dest_info(DestID,_,_,Res) :- invariant_violated(DestID),!,Res=invariant_violated. | |
584 | option_dest_info(DestID,_,_,Res) :- is_deadlocked(DestID),!,Res=deadlock. | |
585 | option_dest_info(DestID,_,_,Res) :- node_satisfies_goal(DestID),!,Res=goal. | |
586 | option_dest_info(_DestID,_,_,unknown). | |
587 | ||
588 | query_op_transition(Term) :- b_or_z_mode, | |
589 | get_operation_name(Term,OpName), | |
590 | b_operation_cannot_modify_state(OpName). | |
591 | ||
592 | tcltk_get_options(O) :- | |
593 | tcltk_get_options(O,[]). %[no_query_operations]). | |
594 | tcltk_get_options(list(Options),Parameters) :- | |
595 | current_state_id(CurID), debug_println(9,start_tcltk_get_options(CurID)), | |
596 | catch(tcltk_compute_options(CurID,TransSpecs), user_interrupt_signal, ( | |
597 | assert_time_out_for_node(CurID,'unspecified_operation_interrupted_by_user',user_interrupt_signal), | |
598 | tcltk_get_computed_options(CurID,TransSpecs) | |
599 | )), | |
600 | extract_actions(TransSpecs,CurID,Parameters,ActionsAndIDs,Actions), | |
601 | set_current_options(ActionsAndIDs), | |
602 | %print(Actions),nl, | |
603 | debug_println(9,finished_tcltk_get_options(CurID)), | |
604 | Actions = Options. | |
605 | ||
606 | :- dynamic current_candidate_options/1. | |
607 | ||
608 | % a newer version of tcltk_options which returns list of potentially enabled events | |
609 | % in case MAX_OPERATIONS is set to 0; these virtual events are appended at the end of the | |
610 | % current transitions (which at the start are empty; but can be populated by executed by predicate) | |
611 | tcltk_get_options_or_candidates(list(List)) :- | |
612 | retractall(current_candidate_options(_)), | |
613 | tcltk_get_options(list(List1)), | |
614 | current_state_id(CurID), | |
615 | (\+ \+ max_operations_zero_for_operation(_), %preference(maxNrOfEnablingsPerOperation,0), | |
616 | CurID \= root, | |
617 | \+ is_concrete_constants_state_id(CurID) % we could provide setup_constants or initialisation in other states | |
618 | -> tcltk_get_candidate_operations(Candidates), | |
619 | assert(current_candidate_options(Candidates)), | |
620 | maplist(get_cand_name,Candidates,List2), | |
621 | append(List1,List2,List) | |
622 | ; List=List1). | |
623 | get_cand_name(candidate(OpName,TO),OpNameRes) :- | |
624 | (TO==true -> ajoin([OpName,' (*timeout*)'],OpNameRes) ; OpNameRes=OpName). | |
625 | ||
626 | ||
627 | :- use_module(specfile,[max_operations_zero_for_operation/1]). | |
628 | % in case of MAX_OPERATIONS=0 just show the potentially enabled operations | |
629 | tcltk_get_candidate_operations(Candidates) :- | |
630 | current_state_id(CurID), | |
631 | findall(candidate(OpName,TO), (max_operations_zero_for_operation(OpName), | |
632 | potentially_enabled_operation(CurID,OpName,TO,_Precise)),Candidates). | |
633 | ||
634 | :- use_module(tools_timeout,[time_out_with_factor_call/4]). | |
635 | % get operations with MAX_OPERATIONS=0 which are potentially enabled | |
636 | potentially_enabled_operation(StateId,OpName,TimeOutOccurred,PreciseGuard) :- b_or_z_mode, | |
637 | b_top_level_operation(OpName), | |
638 | max_operations_zero_for_operation(OpName), | |
639 | Simplify=true, | |
640 | get_quantified_operation_enabling_condition(OpName, Guard, _, PreciseGuard, Simplify), | |
641 | (time_out_with_factor_call( | |
642 | eclipse_interface:test_boolean_expression_in_node(StateId,Guard,'operation Guard'), | |
643 | 0.1, [max_time_out(2000), | |
644 | silent], | |
645 | (format('TIME-OUT checking if ~w is enabled; assuming it could be.~n',[OpName]),TimeOutOccurred=true) | |
646 | ) | |
647 | -> (var(TimeOutOccurred) -> TimeOutOccurred=false ; true)). | |
648 | ||
649 | % get the name of a currently stored candidate option/operation | |
650 | % the option list was empty and we inserted a list of candidate operations | |
651 | % (e.g., because MAX_OPERATIONS was 0) | |
652 | tcltk_get_options_candidate_nr_name(Nr,OpName) :- | |
653 | current_candidate_options(List), | |
654 | current_options(List1), length(List1,Len), | |
655 | N1 is Nr-Len, % the candidate of potentially enabled events are listed after the regular options | |
656 | nth1(N1,List,candidate(OpName,_)). | |
657 | ||
658 | % get destination info for candidate operations in the Operations View | |
659 | tcltk_get_candidates_dest_info(L) :- current_state_id(CurID), | |
660 | current_candidate_options(Cands),!, | |
661 | maplist(get_cand_dest(CurID),Cands,L). | |
662 | tcltk_get_candidates_dest_info([]). | |
663 | get_cand_dest(_,candidate(_,true),R) :- !, R=time_out. | |
664 | get_cand_dest(CurID,candidate(OpName,_),Res) :- | |
665 | transition(CurID,ActionTerm,_ActId,_DestID), | |
666 | get_operation_name(ActionTerm,OpName),!, | |
667 | % it is a candidate for which we already have a transition | |
668 | Res = reached_candidate. | |
669 | get_cand_dest(_,_,candidate). | |
670 | ||
671 | :- use_module(tools_timeout,[time_out_call/2]). | |
672 | :- use_module(translate,[translate_event_with_src_and_target_id/4]). | |
673 | extract_actions([],_,_,[],[]). | |
674 | extract_actions([(_,Term,_)|T],CurID,Parameters,Srest,ET) :- | |
675 | member(no_query_operations,Parameters), | |
676 | query_op_transition(Term), | |
677 | !, | |
678 | extract_actions(T,CurID,Parameters,Srest,ET). | |
679 | extract_actions([(ActionId,Term,Dst)|T],CurID,Parameters,[(ActionId,Str,Term,Dst)|Srest],[Str|ET]) :- | |
680 | time_out_call(translate_event_with_src_and_target_id(Term,CurID,Dst,Str), | |
681 | Str='**TIMEOUT**'), | |
682 | extract_actions(T,CurID,Parameters,Srest,ET). | |
683 | ||
684 | % compute transitions and invariant info | |
685 | compute_state_information_if_necessary :- | |
686 | current_state_id(CurID), | |
687 | compute_state_information_if_necessary(CurID). | |
688 | compute_state_information_if_necessary(CurID) :- | |
689 | compute_all_transitions_if_necessary(CurID,true). % CheckInvariant=true | |
690 | ||
691 | tcltk_compute_options(CurID,ActionsAndIDs) :- | |
692 | compute_state_information_if_necessary(CurID), | |
693 | %print(found_transitions(CurID)),nl, | |
694 | tcltk_get_computed_options(CurID,ActionsAndIDs). | |
695 | ||
696 | tcltk_get_computed_options(CurID,ActionsAndIDs) :- | |
697 | findall((ActId,ActionAsTerm,DestID), | |
698 | transition(CurID,ActionAsTerm,ActId,DestID), | |
699 | % TO DO: sort or group, especially if we randomize transition/3 | |
700 | ActionsAndIDs). | |
701 | ||
702 | tcltk_get_status(INVVIOLATED,MAXREACHED,TIMEOUT) :- debug_println(9,start_tcltk_get_status), | |
703 | current_state_id(CurID), | |
704 | (time_out_for_invariant(CurID) -> INVVIOLATED = 2 | |
705 | ; get_invariant_violated(CurID) -> INVVIOLATED = 1 | |
706 | ; not_invariant_checked(CurID) -> INVVIOLATED = 3 | |
707 | ; not_interesting(CurID) -> INVVIOLATED = 3 | |
708 | ; INVVIOLATED = 0 | |
709 | ), | |
710 | (max_reached_for_node(CurID) -> MAXREACHED = 1 | |
711 | ; not_interesting(CurID) -> MAXREACHED = 2 | |
712 | ; MAXREACHED = 0), | |
713 | (time_out_for_node(CurID,_,time_out) -> TIMEOUT = 1 | |
714 | ; time_out_for_node(CurID,_,user_interrupt_signal) -> TIMEOUT = 3 % CTRL-C | |
715 | ; time_out_for_node(CurID,_,virtual_time_out(kodkod_timeout)) -> TIMEOUT = 1 % KODKOD; regular time-out | |
716 | ; time_out_for_node(CurID,_,_) -> TIMEOUT = 2 % virtual_time_out | |
717 | ; TIMEOUT = 0), | |
718 | debug_println(9,got_status(INVVIOLATED,MAXREACHED,TIMEOUT)). | |
719 | ||
720 | tcltk_get_ops_with_virtual_timeout(OpNameList) :- | |
721 | current_state_id(StateId), | |
722 | ops_with_timeout(StateId,OpNameList,virtual_time_out(_)). | |
723 | tcltk_get_ops_with_real_timeout(OpNameList) :- | |
724 | current_state_id(StateId), | |
725 | ops_with_timeout(StateId,OpNameList,time_out). | |
726 | % Note: possibilities are currently: time_out, user_interrupt_signal, virtual_time_out(_) | |
727 | tcltk_get_ops_user_interrupt_occurred :- | |
728 | current_state_id(StateId), | |
729 | time_out_for_node(StateId,_,user_interrupt_signal). | |
730 | ops_with_timeout(StateId,OpNameList,Type) :- | |
731 | findall(OpName, time_out_for_node(StateId,OpName,Type), OpNameList). | |
732 | ||
733 | ||
734 | %%fd_copy_term(X,Y,true) :- copy_term(X,Y). | |
735 | ||
736 | ||
737 | ||
738 | /* --------------------------------------------------------------------- */ | |
739 | ||
740 | tcltk_get_history(list(StringHistory)) :- debug_println(9,start_tcltk_get_trace), | |
741 | get_action_trace(History), | |
742 | convert_trace_to_list_of_strings(History,StringHistory). | |
743 | ||
744 | convert_trace_to_list_of_strings([],[]). | |
745 | convert_trace_to_list_of_strings([action(AS,_)|T],[AS|CT]) :- !, | |
746 | %truncate_atom(AS,1000,TruncatedAS), % especially SETUP_CONSTANTS, INITIALISATION can be very large; leading to performance issues | |
747 | % get_action_trace now truncates | |
748 | convert_trace_to_list_of_strings(T,CT). | |
749 | convert_trace_to_list_of_strings([H|T],[H|CT]) :- convert_trace_to_list_of_strings(T,CT). | |
750 | ||
751 | tcltk_write_uml_sequence_chart(File) :- | |
752 | write_uml_sequence_chart(File). | |
753 | ||
754 | /* --------------------------------------------------------------------- */ | |
755 | ||
756 | :- dynamic cached_state_as_strings/2. | |
757 | :- use_module(probsrc(state_space),[try_compute_depth_of_state_id/2]). | |
758 | %tcltk_get_state(list(List)) :- !,List=[]. % comment in to disable State Properties View in ProB Tcl/Tk | |
759 | tcltk_get_state(list(List)) :- debug_println(9,start_tcltk_get_state), | |
760 | current_expression(CurID,CurState), | |
761 | compute_state_information_if_necessary(CurID), | |
762 | (time_out_for_invariant(CurID) -> List = [invariant_time_out|List1] | |
763 | ; invariant_violated(CurID) -> List = [invariant_violated|List1] | |
764 | ; invariant_not_yet_checked(CurID) -> List = [invariant_not_checked|List1] | |
765 | ; CurID=root -> List = List1 | |
766 | ; CurState = concrete_constants(_) -> List = List1 | |
767 | ; specfile:b_or_z_mode -> List=[invariant_ok|List1] | |
768 | % only in b_or_z_mode do we add not_invariant_checked facts and check invariant while model checking/animating | |
769 | ; List=List1), | |
770 | (try_compute_depth_of_state_id(CurID,Depth), | |
771 | translate:translate_properties_with_limit(['='(depth,Depth)],[DS]) | |
772 | -> List1 = [DS|StateAsStrings] | |
773 | ; List1 = StateAsStrings), | |
774 | (cached_state_as_strings(CurID,StateAsStrings) % TO DO: separate constant / variable properties ? | |
775 | -> true | |
776 | ; retractall(cached_state_as_strings(_,_)), | |
777 | time_out_call(tcltk_get_state(StateAsStrings,CurState), | |
778 | StateAsStrings = ['***TIMEOUT-PROPERTY***']), | |
779 | assertz(cached_state_as_strings(CurID,StateAsStrings)) | |
780 | ), | |
781 | debug_println(9,finished_tcltk_get_state). | |
782 | ||
783 | tcltk_get_state(StateAsStrings,State) :- | |
784 | %print(get_state),nl, | |
785 | findall(Prop,property(State,Prop),StateProps), | |
786 | %print(translating(StateProps)),nl, | |
787 | translate:translate_properties_with_limit(StateProps,StateAsStrings). | |
788 | %print(translated(StateAsStrings)),nl. | |
789 | ||
790 | ||
791 | tcltk_get_state_errors(list(ErrorsAsStrings)) :- | |
792 | get_current_state_errors(_CurID,Errors), | |
793 | translate_state_errors(Errors,ErrorsAsStrings). | |
794 | ||
795 | get_current_state_errors(CurID,Errors) :- | |
796 | current_state_id(CurID), | |
797 | findall(Error,( state_error(CurID,_,Error), Error \== invariant_violated ), Errors). | |
798 | ||
799 | :- dynamic last_detailed_error/3. | |
800 | ||
801 | reset_tcltk_interface :- debug_println(19,reset_tcltk), | |
802 | retractall(cbc_inv_error_found(_,_)), reset_nr_cbc_sols, | |
803 | retractall(find_flag(_,_)), | |
804 | retractall(resetable_node(_)), | |
805 | retractall(non_det_constant(_)), | |
806 | retractall(current_candidate_options(_)), | |
807 | retractall(cached_state_as_strings(_,_)), | |
808 | retractall(last_detailed_error(_,_,_)). | |
809 | ||
810 | tcltk_get_detailed_state_error(Num,ErrorMsg,Srow,Scol,Erow,Ecol) :- | |
811 | retractall(last_detailed_error(_,_,_)), | |
812 | get_current_state_errors(CurID,Errors), | |
813 | nth0(Num,Errors,Error), | |
814 | !, | |
815 | explain_state_error(Error,Span,ErrorMsg), | |
816 | assertz(last_detailed_error(CurID,Span,ErrorMsg)), % save info, so that we can debug if Span contains a span_predicate | |
817 | %(generate_dot_from_last_span_predicate('~/Desktop/err.dot') -> true ; true), | |
818 | (extract_line_col_for_main_file(Span,Srow,Scol,Erow,Ecol) | |
819 | -> true | |
820 | ; Srow= -1, Scol= -1, Erow= -2, Ecol= -2). | |
821 | ||
822 | ||
823 | :- use_module(probsrc(tools_commands),[edit_file/2]). | |
824 | :- use_module(specfile,[b_absolute_file_name_relative_to_opened_file/2]). | |
825 | % show location of state error in external editor | |
826 | tcltk_open_state_error_in_editor(Num) :- | |
827 | get_current_state_errors(_,Errors), | |
828 | nth0(Num,Errors,Error), | |
829 | (get_state_error_span(Error,Span) | |
830 | -> (extract_file_line_col(Span,FILE,LINE,_COL,_Erow,_Ecol) | |
831 | -> b_absolute_file_name_relative_to_opened_file(FILE,AFILE), | |
832 | edit_file(AFILE,LINE) | |
833 | ; add_error(tcltk_open_state_error_in_editor,'Could not extract file position: ',Span) | |
834 | ) | |
835 | ; add_error(tcltk_open_state_error_in_editor,'Could not extract source location for state error: ',Num) | |
836 | ). | |
837 | ||
838 | can_generate_dot_from_last_state_error :- | |
839 | last_detailed_error(_CurID,ErrSpan,_),!, | |
840 | get_inner_span(ErrSpan,span_predicate(_,_,_)). | |
841 | can_generate_dot_from_last_state_error :- | |
842 | current_state_id(CurID), state_error(CurID,_,_). % not sure if it is really possible to extract span predicate though | |
843 | generate_dot_from_last_span_predicate(FileName) :- | |
844 | current_state_id(CurID), | |
845 | (last_detailed_error(CurID,ErrSpan,_ErrorMsg) % also works with expressions | |
846 | -> true | |
847 | ; tcltk_get_detailed_state_error(0,_,_,_,_,_), % try and generate error description | |
848 | last_detailed_error(CurID,ErrSpan,_) | |
849 | ), | |
850 | get_inner_span(ErrSpan,span_predicate(P,LS,State)), | |
851 | (State=[], visited_expression(CurID,S), | |
852 | state_corresponds_to_initialised_b_machine(S,FullState) | |
853 | -> true % span predicates from event trace do not have the current state, add state | |
854 | ; FullState=State), | |
855 | write_dot_file_for_pred_expr_and_state(P, LS, FullState,FileName). | |
856 | ||
857 | get_inner_span(pos_context(Span1,_,_),SP) :- !, | |
858 | get_inner_span(Span1,SP). | |
859 | get_inner_span(SP,SP). | |
860 | ||
861 | tcltk_current_state_invariant_violated :- | |
862 | current_state_id(CurID), | |
863 | get_invariant_violated(CurID). | |
864 | ||
865 | tcltk_state_exists_with_invariant_violated :- | |
866 | (invariant_violated(_) -> true). % would be too expensive for XTL mode | |
867 | ||
868 | % also checks for other formalisms whether something similar to an invariant violation holds | |
869 | get_invariant_violated(CurID) :- xtl_mode,!, | |
870 | visited_expression(CurID,CurState), | |
871 | xtl_invariant_violated(CurState). | |
872 | get_invariant_violated(CurID) :- invariant_violated(CurID). | |
873 | ||
874 | /* --------------------------------------------------------------------- */ | |
875 | ||
876 | :- use_module(b_trace_checking, [tcltk_save_history_as_trace_file/2]). | |
877 | tcltk_save_history_as_trace_file(File) :- | |
878 | tcltk_save_history_as_trace_file(prolog,File). | |
879 | ||
880 | ||
881 | /* --------------------------------------------------------------------- */ | |
882 | ||
883 | :- use_module(translate, [get_bexpression_column_template/4]). | |
884 | evaluate_bexpr_over_state(Stream,Expr,ValueTemplate,Columns,ResultColStrings,BState,OpName) :- | |
885 | format(Stream,'~w,',[OpName]), | |
886 | b_compute_expression_with_prob_ids(Expr,BState,Value), | |
887 | copy_term((ValueTemplate,Columns),(Value,ResultCols)), | |
888 | my_print_csv_value(ResultCols,Stream), | |
889 | maplist(translate:translate_bvalue,ResultCols,ResultColStrings), | |
890 | nl(Stream). | |
891 | ||
892 | % evaluate an expression over the history and save the result to a CSV file | |
893 | evaluate_expression_over_history_to_csv_file(Expr,File) :- | |
894 | parse_machine_expression(Expr,TypedExpr), | |
895 | get_bexpression_column_template(TypedExpr,ValueTemplate,ColHeaders,Columns), | |
896 | format('Writing CSV file: ~w~n',[File]), | |
897 | open(File,write,Stream), | |
898 | format(Stream,'OPERATION,',[]), | |
899 | my_print_csv_atoms(ColHeaders,Stream), | |
900 | call_cleanup(map_over_history(evaluate_bexpr_over_state(Stream,TypedExpr,ValueTemplate,Columns),_), | |
901 | close(Stream)), | |
902 | format('Finished writing CSV file: ~w~n',[File]). | |
903 | ||
904 | ||
905 | tcltk_evaluate_expression_over_history(Expr,list([list(['OPERATION','State Id'|ColHeaders])|ColumnLists])) :- | |
906 | parse_machine_expression(Expr,TypedExpr), | |
907 | get_bexpression_column_template(TypedExpr,ValueTemplate,ColHeaders,Columns), | |
908 | map_over_history(evaluate_bexpr_over_state(user_output,TypedExpr,ValueTemplate,Columns), | |
909 | ColumnLists). | |
910 | ||
911 | map_over_history(Pred,ResColumnLists) :- | |
912 | history(H), current_state_id(CurID), | |
913 | reverse([CurID|H],RH), | |
914 | get_action_trace(T), reverse(T,RT), | |
915 | maplist(call_pred_on_expanded_state(Pred),RH,[action(root,root)|RT],ColumnLists), | |
916 | prune_list(ColumnLists,ResColumnLists). | |
917 | % remove unknown elements at front for Tk | |
918 | prune_list([unknown|T],Res) :- !, prune_list(T,Res). | |
919 | prune_list(R,R). | |
920 | ||
921 | call_pred_on_expanded_state(_Pred,root,_,ColList) :- !, ColList=unknown. | |
922 | call_pred_on_expanded_state(Pred,StateId,action(_,ActionTerm),ResColList) :- print(StateId),nl, | |
923 | get_operation_name(ActionTerm,OpName), | |
924 | format('Processing state ~w (reached via ~w)~n',[StateId,OpName]), | |
925 | visited_expression(StateId,State), | |
926 | (state_corresponds_to_initialised_b_machine(State,BState) | |
927 | -> call(Pred,ColList,BState,OpName), ResColList = list([OpName,StateId|ColList]) | |
928 | ; ResColList = unknown ). | |
929 | ||
930 | :- use_module(eval_let_store,[extend_state_with_probids_and_lets/2]). | |
931 | b_compute_expression_with_prob_ids(Expr,BState,Value) :- | |
932 | extend_state_with_probids_and_lets(BState,BState2), | |
933 | b_interpreter:b_compute_expression_nowf(Expr,[],BState2,Value,'Tcl/Tk',0). | |
934 | ||
935 | ||
936 | my_print_csv_atoms([A,B|T],Stream) :- !, format(Stream,'"~w",',[A]), | |
937 | my_print_csv_atoms([B|T],Stream). | |
938 | my_print_csv_atoms([A],Stream) :- format(Stream,'"~w"~n',[A]). | |
939 | ||
940 | my_print_csv_value([A,B|T],Stream) :- !, | |
941 | my_print_csv_value([A],Stream), | |
942 | write(Stream,','), | |
943 | my_print_csv_value([B|T],Stream). | |
944 | my_print_csv_value([V],Stream) :- | |
945 | (no_quotes_necessary(V) -> true ; write(Stream,'"')), | |
946 | translate:print_bvalue_stream(Stream,V), | |
947 | (no_quotes_necessary(V) -> true ; write(Stream,'"')). | |
948 | ||
949 | no_quotes_necessary(int(_)). | |
950 | no_quotes_necessary(pred_false). | |
951 | no_quotes_necessary(pred_true). | |
952 | ||
953 | ||
954 | /* --------------------------------------------------------------------- */ | |
955 | ||
956 | :- use_module(probltlsrc(ltl_tools),[temporal_parser/3]). | |
957 | :- use_module(probltlsrc(ltl),[preprocess_formula/2]). | |
958 | ||
959 | tcltk_execute_until(FormulaAsAtom) :- | |
960 | (temporal_parser(FormulaAsAtom,ltl,Ltl) -> | |
961 | true | |
962 | ; | |
963 | add_error(ltl,'LTL Parser failed: ',FormulaAsAtom),fail), | |
964 | ( Ltl=syntax_error -> | |
965 | add_error(ltl,'Syntax error while parsing: ',FormulaAsAtom),fail | |
966 | ; | |
967 | preprocess_formula(Ltl,Ltl2) | |
968 | ), | |
969 | current_state_id(CurID), | |
970 | prob2_interface: find_trace1(CurID,Ltl2,no_loop,1000,Trace,_Result), | |
971 | %TODO: check if result is deadlock. Will not fail. | |
972 | update_history_from_trace(Trace). | |
973 | ||
974 | update_history_from_trace([]). % TODO: Can we get an empty trace from find_trace predicate? | |
975 | update_history_from_trace([op(OpID,_Name,CurID,NextID)]) :- | |
976 | add_id_to_history(CurID), | |
977 | add_to_op_trace_ids(OpID), | |
978 | add_id_to_history(NextID), | |
979 | set_current_state_id(NextID). | |
980 | update_history_from_trace([op(OpID,_Name,CurID,NextID)|T]) :- | |
981 | add_id_to_history(CurID), | |
982 | add_to_op_trace_ids(OpID), | |
983 | add_id_to_history(NextID), | |
984 | update_history_from_trace(T). | |
985 | ||
986 | ||
987 | tcltk_random_perform :- tcltk_random_perform2(_,fully_random). | |
988 | tcltk_random_perform(Kind) :- tcltk_random_perform2(_,Kind). | |
989 | ||
990 | % Kind can be fully_random, no_self_loops, explore_open | |
991 | % TODO: other options like out_degree, ... | |
992 | tcltk_random_perform2(ActionId,Kind) :- | |
993 | current_options(Opts), | |
994 | current_state_id(CurID), | |
995 | (Kind=explore_open, exclude(is_already_explored,Opts,Options2), Options2 = [_|_] -> true % try and find unexplored successor | |
996 | ; is_heuristic(Kind), | |
997 | maplist(compute_heuristic(Kind),Opts,HOpts), | |
998 | sort(HOpts,SHOpts), | |
999 | keep_min_heuristic(SHOpts,Opts1) | |
1000 | -> | |
1001 | (exclude(is_already_explored,Opts1,Options2), Options2 = [_|_] | |
1002 | -> true % among the nodes with the lowest heuristic value choose open nodes first; to try find new values | |
1003 | ; Options2=Opts1) | |
1004 | ; Kind\=fully_random, | |
1005 | exclude(is_self_loop(CurID),Opts,Options2), Options2 = [_|_] -> true % try find different node | |
1006 | ; Options2=Opts), | |
1007 | length(Options2,Len), | |
1008 | (Len>0 | |
1009 | -> L1 is Len+1, | |
1010 | random(1,L1,RanChoice), | |
1011 | tcltk_perform_nr_option(RanChoice,Options2,ActionId) | |
1012 | ; true | |
1013 | ). | |
1014 | ||
1015 | is_already_explored((_Id,_Action,_,NextId)) :- \+ not_all_transitions_added(NextId). | |
1016 | is_self_loop(CurId,(_Id,_Action,_,CurId)). | |
1017 | ||
1018 | is_heuristic(heuristic). | |
1019 | is_heuristic(min_out_degree). | |
1020 | :- use_module(probsrc(state_space_exploration_modes),[compute_heuristic_function_for_state_id/2]). | |
1021 | :- use_module(probsrc(state_space),[out_degree/2, compute_transitions_if_necessary/1]). | |
1022 | compute_heuristic(Kind,(Id,Action,ActTerm,NextId),heurval(HeurVal,Id,Action,ActTerm,NextId)) :- | |
1023 | compute_h2(Kind,NextId,HeurVal). | |
1024 | compute_h2(heuristic,NextId,HeurVal) :- | |
1025 | compute_heuristic_function_for_state_id(NextId,HeurVal). | |
1026 | compute_h2(min_out_degree,NextId,HeurVal) :- | |
1027 | compute_transitions_if_necessary(NextId), | |
1028 | out_degree(NextId,HeurVal). | |
1029 | %compute_h2(out_degree,NextId,HeurVal) :- | |
1030 | ||
1031 | % only keep the maximal value | |
1032 | keep_min_heuristic([heurval(HeurVal,Id,Action,ActTerm,NextId)|HT], | |
1033 | [(Id,Action,ActTerm,NextId)|T]) :- keep_min2(HT,HeurVal,T). | |
1034 | keep_min2([heurval(HeurVal,Id,Action,ActTerm,NextId)|HT],HeurVal, | |
1035 | [(Id,Action,ActTerm,NextId)|T]) :- !, keep_min2(HT,HeurVal,T). | |
1036 | keep_min2(_,_,[]). | |
1037 | ||
1038 | ||
1039 | tcltk_get_line_col(Nr,list(StartLine),list(StartCol),list(EndLine),list(EndCol)) :- /* just get the location in the source code */ | |
1040 | current_options(Options), | |
1041 | nth1(Nr,Options,(_Id,_Action,ActionAsTerm,_NewID)), | |
1042 | %print(get_source_text(ActionAsTerm)),nl, | |
1043 | time_out_call(get_source_text_positions(ActionAsTerm,StartLine,StartCol,EndLine,EndCol), | |
1044 | add_error_and_fail(tcltk_get_line_col,'Timeout determining source code position: ',ActionAsTerm)), | |
1045 | StartLine \= []. % otherwise fail | |
1046 | % print(startlines(StartLine)),nl,print(startcols(StartCol)),nl, | |
1047 | % print(endlines(EndLine)),nl, print(endcol(EndCol)),nl. | |
1048 | ||
1049 | tcltk_is_sync_event(Nr) :- | |
1050 | current_options(Options), | |
1051 | nth1(Nr,Options,(_Id,_Action,ActionAsTerm,_NewID)), | |
1052 | extract_span_from_event(ActionAsTerm,SPAN,_,_), | |
1053 | extract_span_info(SPAN,Info), | |
1054 | % print(extracted_span_info_for_sync(Info,SPAN)),nl, | |
1055 | member(sharing,Info). | |
1056 | ||
1057 | % get textual description of nth option in current options | |
1058 | % is derived from description pragma attached to operations | |
1059 | tcltk_get_options_nr_description(Nr,Desc) :- | |
1060 | current_state_id(CurID), | |
1061 | current_options(Options), | |
1062 | nth1(Nr,Options,(_ActionId,_Action,ActionAsTerm,NewID)), | |
1063 | tcltk_get_op_desc_for_trans(CurID,ActionAsTerm,NewID,Desc). | |
1064 | ||
1065 | :- use_module(probsrc(bmachine),[source_code_for_identifier/6]). | |
1066 | % get textual description for entry Nr in the Tk history view | |
1067 | tcltk_get_history_nr_description(Nr,Desc) :- | |
1068 | get_history_trans(Nr, CurID,ActionAsTerm,DestID), | |
1069 | tcltk_get_op_desc_for_trans(CurID,ActionAsTerm,DestID,Desc). | |
1070 | ||
1071 | get_history_trans(Nr, CurID,ActionAsTerm,DestID) :- | |
1072 | op_trace_ids(OpTrace), | |
1073 | nth0(Nr,OpTrace,TransId), | |
1074 | history(StateIds), | |
1075 | nth0(Nr,StateIds,CurID), | |
1076 | transition(CurID,ActionAsTerm,TransId,DestID). | |
1077 | ||
1078 | :- use_module(probsrc(bmachine),[source_code_for_identifier/6]). | |
1079 | % show source code origin of entry number Nr in the Tk history view | |
1080 | tcltk_get_history_nr_origin_str(Nr,Desc) :- | |
1081 | get_history_trans(Nr, _CurID,ActionAsTerm,_DestID), | |
1082 | get_operation_name(ActionAsTerm,OpID), | |
1083 | source_code_for_identifier(OpID,operation,subst,OriginStr,_,_), | |
1084 | Desc=OriginStr. %ajoin([OpID,' ', OriginStr],Desc). | |
1085 | ||
1086 | ||
1087 | :- use_module(probsrc(tools_commands),[edit_file/2]). | |
1088 | :- use_module(probsrc(error_manager),[extract_file_line_col/6]). | |
1089 | % open source code in external editor for origin of entry number Nr in the history view | |
1090 | tcltk_edit_history_nr_origin_str(Nr) :- | |
1091 | get_history_trans(Nr, _CurID,ActionAsTerm,_DestID), | |
1092 | get_operation_name(ActionAsTerm,OpID), | |
1093 | source_code_for_identifier(OpID,operation,subst,_OriginStr,OriginTerm,_), | |
1094 | extract_file_line_col(OriginTerm,FILE,LINE,_COL,_Erow,_Ecol), | |
1095 | edit_file(FILE,LINE). | |
1096 | ||
1097 | :- use_module(probsrc(state_space_exploration_modes), | |
1098 | [compute_heuristic_function_for_state_id/2]). | |
1099 | tcltk_get_op_desc_for_trans(CurID,ActionAsTerm,DestID,Desc) :- | |
1100 | (b_or_z_mode, | |
1101 | get_operation_description_for_transition(CurID,ActionAsTerm,DestID,Desc) -> true | |
1102 | ; compute_heuristic_function_for_state_id(DestID,int(HeuristicVal)), | |
1103 | ajoin(['Heuristic=',HeuristicVal],Desc)). % TODO: more infos, like deadlock, GOAL, ... | |
1104 | ||
1105 | % counter-part to tcltk_get_options, to execute the option/operation number Nr in that list | |
1106 | tcltk_perform_nr(Nr) :- | |
1107 | current_options(Options), | |
1108 | tcltk_perform_nr_option(Nr,Options,_). | |
1109 | ||
1110 | tcltk_perform_nr_option(Nr,Options,ActionId) :- | |
1111 | (nth1(Nr,Options,(ActionId,_Action,ActionAsTerm,NewID)) | |
1112 | -> (forward_history([forward(NewID,ActionId)|_FwdHist]) | |
1113 | -> tcltk_forward | |
1114 | ; tcltk_perform_action_term(ActionAsTerm,NewID), | |
1115 | retractall(forward_history(_)) | |
1116 | ) | |
1117 | ; tcltk_backtrack /* interpret as backtrack */ | |
1118 | ). | |
1119 | ||
1120 | ||
1121 | tcltk_can_backtrack :- history([_|_]). | |
1122 | tcltk_can_backtrack(Len) :- history(His), length(His,Len),Len>0. | |
1123 | ||
1124 | tcltk_backtrack :- \+ tcltk_can_backtrack,!, | |
1125 | print_message('Cannot backtrack'),fail. | |
1126 | tcltk_backtrack :- | |
1127 | remove_from_op_trace_ids(LastTraceH), % remove last operation id from op_id trace | |
1128 | retract(history(History)), | |
1129 | retract(current_state_id(CurID)),!, | |
1130 | History= [LastID|EHist], | |
1131 | assertz(history(EHist)), | |
1132 | %visited_expression(LastID,LastExpr,LastBody), | |
1133 | assertz(current_state_id(LastID)), | |
1134 | (retract(forward_history(FwdHist)) -> true ; FwdHist=[]), | |
1135 | assertz(forward_history([forward(CurID,LastTraceH)|FwdHist])). | |
1136 | ||
1137 | % used for right-clicks in history | |
1138 | tcltk_backtrack(N) :- N =< 0, !. | |
1139 | tcltk_backtrack(N) :- % TO DO: more efficient version | |
1140 | tcltk_backtrack, N1 is N-1, tcltk_backtrack(N1). | |
1141 | ||
1142 | :- dynamic saved_forward_history/2. | |
1143 | :- volatile saved_forward_history/2. | |
1144 | ||
1145 | ||
1146 | % save certain information to restore when reload is completed: | |
1147 | tcltk_prepare_for_reload :- | |
1148 | saved_forward_history([_|_],Mode), % tcltk_try_reload not executed; probably reloading failed | |
1149 | animation_mode(Mode), | |
1150 | current_state_id(root), %user has not performed another trace | |
1151 | get_action_trace([]),!, | |
1152 | format('Keeping saved forward history~n',[]). % so that user can fix error and re-load again | |
1153 | tcltk_prepare_for_reload :- | |
1154 | retractall(saved_forward_history(_,_)), | |
1155 | get_action_term_trace(TrA),reverse(TrA,Tr), | |
1156 | %print(prepare_reloading(Tr)),nl, | |
1157 | maplist(gen_unknown_forward,Tr,SavedForwardHistory), | |
1158 | animation_mode(Mode), | |
1159 | % TO DO: add current forward history as well and restore when doing a reload with fastforward | |
1160 | % forward_history(ForwHis), maplist(remove_forward_id,ForwHis,SavedPrevForwHis), | |
1161 | assertz(saved_forward_history(SavedForwardHistory,Mode)). | |
1162 | ||
1163 | %remove_forward_id(forward(_,Step),forward('$UNKNOWN',Step)). | |
1164 | gen_unknown_forward(Step,forward('$UNKNOWN',Step)). % the forward ID is unknown | |
1165 | ||
1166 | % try reloading certain information from previous load of same model | |
1167 | tcltk_try_reload :- | |
1168 | retract(saved_forward_history(SavedForwardHistory,Mode)), | |
1169 | animation_mode(Mode), % we are in the same mode as when saved | |
1170 | !, | |
1171 | % TO DO: also retractall in other places | |
1172 | %print(reloading(SavedForwardHistory)),nl, | |
1173 | retractall(forward_history(_)), | |
1174 | assertz(forward_history(SavedForwardHistory)). | |
1175 | tcltk_try_reload. | |
1176 | ||
1177 | ||
1178 | % go forward while possible | |
1179 | tcltk_fast_forward :- tcltk_can_forward,!, tcltk_forward, | |
1180 | current_state_id(CurID), | |
1181 | compute_state_information_if_necessary(CurID), | |
1182 | tcltk_fast_forward. | |
1183 | tcltk_fast_forward. | |
1184 | ||
1185 | tcltk_can_forward :- forward_history([_|_]). | |
1186 | tcltk_can_forward(Len) :- forward_history(His), length(His,Len), Len>0. | |
1187 | ||
1188 | ||
1189 | tcltk_forward(N) :- N =< 0, !. | |
1190 | tcltk_forward(N) :- % TO DO: more efficient version | |
1191 | tcltk_forward, N1 is N-1, tcltk_forward(N1). | |
1192 | ||
1193 | ||
1194 | tcltk_forward :- \+ tcltk_can_forward,!, | |
1195 | print_message('Cannot go forward'),fail. | |
1196 | tcltk_forward :- | |
1197 | retract(forward_history([forward(FwdID,LastTraceH)|FwdHist])), | |
1198 | retract(history(EHist)), | |
1199 | retract(current_state_id(CurID)),!, | |
1200 | go_forward(FwdID,LastTraceH,FwdHist,CurID,EHist). | |
1201 | go_forward('$UNKNOWN',ActionAsTerm,FwdHist,CurID,EHist) :- | |
1202 | transition(CurID,ActionAsTerm,TransitionID,FwdID),!, | |
1203 | %print(found(CurID,ActionAsTerm,TransitionID,FwdID)),nl, | |
1204 | go_forward(FwdID,TransitionID,FwdHist,CurID,EHist). | |
1205 | % TO DO: try and execute by predicate in case max_reached is true ! | |
1206 | go_forward('$UNKNOWN',ActionAsTerm,FwdHist,CurID,EHist) :- | |
1207 | is_initialisation_op(ActionAsTerm), | |
1208 | % maybe PROPERTIES/INITIALISATION has changed; try and replay with other values | |
1209 | transition(CurID,ActionAsTerm2,TransitionID,FwdID), | |
1210 | is_initialisation_op(ActionAsTerm2), | |
1211 | !, | |
1212 | %print(found(CurID,ActionAsTerm,TransitionID,FwdID)),nl, | |
1213 | translate_event(ActionAsTerm,Action), | |
1214 | format('Could not replay ~w.~nUsing other possible transition instead.~n',[Action]), | |
1215 | go_forward(FwdID,TransitionID,FwdHist,CurID,EHist). | |
1216 | go_forward('$UNKNOWN',ActionAsTerm,_FwdHist,CurID,EHist) :- !, | |
1217 | assertz(forward_history([])), | |
1218 | assertz(history(EHist)), | |
1219 | assertz(current_state_id(CurID)), | |
1220 | translate_event(ActionAsTerm,Action), | |
1221 | add_error(tcltk_forward,'Cannot replay step after reload: ',Action). | |
1222 | go_forward(FwdID,LastTraceH,FwdHist,CurID,EHist) :- | |
1223 | assertz(forward_history(FwdHist)), | |
1224 | assertz(history([CurID|EHist])), | |
1225 | add_to_op_trace_ids(LastTraceH), | |
1226 | assertz(current_state_id(FwdID)), | |
1227 | re_execute_transition_if_necessary(CurID,LastTraceH,FwdID). | |
1228 | %visited_expression(FwdID,FwdExpr,FwdBody). | |
1229 | ||
1230 | is_initialisation_op(Op) :- | |
1231 | (functor(Op,'$setup_constants',_) ;functor(Op,'$initialise_machine',_)). | |
1232 | ||
1233 | % re-execute operation with side-effects if necessary and if preference set | |
1234 | % TO DO: tie into tcltk_perform so that not only the forward button but also double click | |
1235 | % in operations pane works | |
1236 | re_execute_transition_if_necessary(FromID,TransitionID,ToID) :- | |
1237 | b_or_z_mode, | |
1238 | get_preference(re_execute_operations_with_side_effects,true), | |
1239 | bmachine_construction:external_procedure_used(_), % TO DO: check if operation itself has side-effects | |
1240 | !, | |
1241 | (re_execute_transition(FromID,TransitionID,ToID) | |
1242 | -> true | |
1243 | ; add_error(tcltk_forward,'Could not re-execute operation: ',TransitionID)). | |
1244 | re_execute_transition_if_necessary(_,_,_). | |
1245 | ||
1246 | re_execute_transition(FromID,TransitionID,ToID) :- | |
1247 | transition(FromID,ActionTerm,TransitionID,ToID), | |
1248 | %%print(trans(ActionTerm)),nl, | |
1249 | get_operation_name(ActionTerm,OpName), | |
1250 | debug_println(9,re_executing_operation(OpName,FromID,TransitionID,ToID)), | |
1251 | visited_expression(FromID,InState), | |
1252 | specfile:b_trans(InState,OpName,ActionTerm,_NewState,_TransInfo), | |
1253 | debug_println(9,re_executed(OpName)). | |
1254 | ||
1255 | % a version of tcltk_perform_action_term which can be given only the String (e.g., for trace checking) | |
1256 | tcltk_perform_action_string(ActionString,ActionAsTerm,NewID) :- | |
1257 | current_state_id(CurID), | |
1258 | transition(CurID,ActionAsTerm,_,NewID), | |
1259 | translate_event_with_src_and_target_id(ActionAsTerm,CurID,NewID,ActionString), | |
1260 | tcltk_goto_state(ActionAsTerm,NewID). | |
1261 | % translate_event_with_limit(ActionAsTerm,5000,ActionString). % should use same limit as below; otherwise trace checking will fail; ideally we should check ActionString is prefix | |
1262 | %translate_event(ActionAsTerm,ActionString). | |
1263 | ||
1264 | % perform the Action with term ActionAsTerm leading to new state NewID | |
1265 | tcltk_perform_action_term(ActionAsTerm,NewID) :- %print(tcltk_perform_action_term(Action,ActionAsTerm,NewID)),nl, | |
1266 | (var(ActionAsTerm) | |
1267 | -> add_internal_error('Illegal call: ',tcltk_perform_action_term(ActionAsTerm,NewID)) ; true), | |
1268 | tcltk_goto_state(ActionAsTerm,NewID). | |
1269 | ||
1270 | /* allows one to execute an action in term form, from the current state: */ | |
1271 | %tcltk_perform_action(T,Cur) :- print_message(perf(T,Cur)),fail. | |
1272 | ||
1273 | tcltk_perform_action(ActionAsTerm,NewID) :- | |
1274 | current_state_id(CurID), | |
1275 | tcltk_perform_action(CurID,ActionAsTerm,NewID). | |
1276 | tcltk_perform_action(CurID,ActionAsTerm,NewID) :- | |
1277 | tcltk_perform_action(CurID,ActionAsTerm,NewID,_). | |
1278 | tcltk_perform_action(CurID,ActionAsTerm,TransitionID,NewStateID) :- | |
1279 | transition(CurID,ActionAsTerm,TransitionID,NewStateID), | |
1280 | tcltk_goto_state(ActionAsTerm,NewStateID). | |
1281 | ||
1282 | tcltk_no_constants_or_no_inititalisation_found :- | |
1283 | (current_expression(root,_) ; current_expression(_,concrete_constants(_))), | |
1284 | current_options([]). | |
1285 | ||
1286 | /* ------------ */ | |
1287 | /* tcltk_open/1 */ | |
1288 | /* ------------ */ | |
1289 | ||
1290 | ||
1291 | %prolog_check_load_errors :- | |
1292 | % (tcltk_find_untyped_consts(ErrRes) | |
1293 | % -> add_error(prolog_open,'These constants were not given a proper type:', ErrRes) | |
1294 | % ; true), | |
1295 | % (tcltk_find_untyped_vars(ErrRes) | |
1296 | % -> add_error(prolog_open,'These variables were not given a proper type:', ErrRes) | |
1297 | % ; true). | |
1298 | ||
1299 | tcltk_open_b_file_for_minor_mode(File,Minor) :- tcltk_open_b_file(File), | |
1300 | set_animation_minor_mode(Minor). | |
1301 | ||
1302 | tcltk_open_b_file(File) :- | |
1303 | tcltk_clear_machine, | |
1304 | set_animation_mode(b), | |
1305 | print_message(loading_b_file(File)),flush_output, | |
1306 | set_currently_opening_file(File), | |
1307 | % to do: b_load_machine_probfile | |
1308 | (b_load_aux(File) | |
1309 | -> print_message(loaded), | |
1310 | set_currently_opened_b_file(File) | |
1311 | ; tcltk_set_failed_to_open_file(File), | |
1312 | fail | |
1313 | ). | |
1314 | ||
1315 | tcltk_set_failed_to_open_file(File) :- | |
1316 | set_failed_to_open_file(File), | |
1317 | copy_current_errors_to_state(root,loading_context(File)). | |
1318 | ||
1319 | :- use_module(probsrc(bmachine), [b_load_machine_from_file/1,b_load_machine_probfile/1]). | |
1320 | :- use_module(tools,[get_filename_extension/2]). | |
1321 | b_load_aux(File) :- get_filename_extension(File,'prob'),!, | |
1322 | b_load_machine_probfile(File). | |
1323 | b_load_aux(File) :- | |
1324 | b_load_machine_from_file(File). | |
1325 | ||
1326 | :- use_module(xtl_interface,[open_xtl_file/1]). | |
1327 | tcltk_open_xtl_file(File) :- % TODO: redundant wrt load_xtl_spec_from_prolog_file | |
1328 | tcltk_clear_machine, | |
1329 | set_animation_mode(xtl), | |
1330 | set_currently_opening_file(File), | |
1331 | (open_xtl_file(File) | |
1332 | -> set_currently_opened_file(File) | |
1333 | ; tcltk_set_failed_to_open_file(File), fail). | |
1334 | ||
1335 | ||
1336 | tcltk_open_cspm_file(File) :- | |
1337 | tcltk_clear_machine, | |
1338 | set_animation_mode(cspm), | |
1339 | set_currently_opening_file(File), | |
1340 | (xtl_interface:open_cspm_file(File) | |
1341 | -> set_currently_opened_file(File) | |
1342 | ; tcltk_set_failed_to_open_file(File), fail). | |
1343 | ||
1344 | :- use_module(xtl_interface). | |
1345 | %tcltk_open_promela_file(File) :- | |
1346 | % tcltk_clear_machine, | |
1347 | % set_animation_mode(promela), set_currently_opening_file(File), | |
1348 | % xtl_interface:open_promela_file(File), set_currently_opened_file(File). | |
1349 | ||
1350 | % disabled because Java parser seems broken | |
1351 | %tcltk_open_smv_file(File) :- | |
1352 | % tcltk_clear_machine, | |
1353 | % set_animation_mode(smv), set_currently_opening_file(File), | |
1354 | % xtl_interface:open_smv_file(File), set_currently_opened_file(File). | |
1355 | ||
1356 | :- use_module(specfile,[type_check_csp_and_b/0]). | |
1357 | :- use_module(library(file_systems),[file_exists/1,delete_file/1]). | |
1358 | ||
1359 | tcltk_add_csp_file(File) :- | |
1360 | \+ file_exists(File),!, | |
1361 | add_error(csp_guide,'CSP guide file does not exist:',File). | |
1362 | tcltk_add_csp_file(File) :- | |
1363 | unset_animation_minor_modes(L), % we could be in z or tla or eventb mode | |
1364 | set_animation_mode(csp_and_b), | |
1365 | reset_animation_minor_modes(L), | |
1366 | xtl_interface:open_cspm_file(File), | |
1367 | notify_change_of_animation_mode, | |
1368 | type_check_csp_and_b. | |
1369 | ||
1370 | notify_change_of_animation_mode :- | |
1371 | announce_event(change_of_animation_mode). | |
1372 | % clear_applicable_flag. % no done via event handling | |
1373 | ||
1374 | ||
1375 | tcltk_open_z_file(File) :- | |
1376 | tcltk_clear_machine, | |
1377 | tcltk_open_z_file2(File). | |
1378 | tcltk_open_z_file2(File) :- | |
1379 | set_currently_opening_file(File), | |
1380 | open_proz_file(File,BMachine), | |
1381 | set_animation_mode(b), set_animation_minor_mode(z), | |
1382 | b_set_typed_machine(BMachine,File), | |
1383 | !, | |
1384 | set_currently_opened_b_file(File). | |
1385 | tcltk_open_z_file2(File) :- tcltk_set_failed_to_open_file(File),fail. | |
1386 | ||
1387 | :- use_module(parsercall,[call_fuzz_parser/2]). | |
1388 | tcltk_open_z_tex_file(TexFile) :- | |
1389 | tcltk_clear_machine, | |
1390 | call_fuzz_parser(TexFile,FuzzFile), | |
1391 | tcltk_open_z_file2(FuzzFile). | |
1392 | ||
1393 | ||
1394 | :- use_module(eclipse_interface,[load_eventb_file/1]). | |
1395 | tcltk_load_packaged_eventb_file(File) :- | |
1396 | tcltk_clear_machine, | |
1397 | load_eventb_file(File). | |
1398 | ||
1399 | :- use_module(parsercall,[call_alloy2pl_parser/2]). | |
1400 | :- use_module(probsrc('alloy2b/alloy2b')). | |
1401 | tcltk_open_alloy_file(AlloyFile) :- | |
1402 | tcltk_clear_machine, | |
1403 | start_ms_timer(T1), | |
1404 | formatsilent('Parsing Alloy file: ~w~n',[AlloyFile]), | |
1405 | set_currently_opening_file(AlloyFile), | |
1406 | call_alloy2pl_parser(AlloyFile,PrologFile), | |
1407 | (silent_mode(off) -> stop_ms_walltimer_with_msg(T1,'parsing and type checking: ') ; true), | |
1408 | %printsilent(generated_prolog_ast(PrologFile)),nls, | |
1409 | start_ms_timer(T2), | |
1410 | ( load_alloy_ast_prolog_file(PrologFile) | |
1411 | -> true | |
1412 | ; delete_file(PrologFile), % delete parsed Prolog file if model couldn't be loaded | |
1413 | fail % since it's potentially empty due to the failure | |
1414 | ), | |
1415 | !, | |
1416 | (silent_mode(off) -> stop_ms_walltimer_with_msg(T2,'loading and translation to B: ') ; true), | |
1417 | set_currently_opened_b_file(AlloyFile). | |
1418 | tcltk_open_alloy_file(AlloyFile) :- tcltk_set_failed_to_open_file(AlloyFile),fail. | |
1419 | ||
1420 | ||
1421 | tcltk_open_alloy_prolog_ast_file(AlloyPrologFile) :- | |
1422 | tcltk_clear_machine, | |
1423 | start_ms_timer(T2), | |
1424 | load_alloy_ast_prolog_file(AlloyPrologFile), | |
1425 | !, | |
1426 | (silent_mode(off) -> stop_ms_walltimer_with_msg(T2,'loading B translation of Alloy: ') ; true), | |
1427 | set_currently_opened_b_file(AlloyPrologFile). | |
1428 | ||
1429 | %% tcltk_load_alloy_cmd_in_current_translation(+CmdName). | |
1430 | tcltk_load_alloy_cmd_in_current_translation(CmdName) :- | |
1431 | alloy2b:load_command_in_current_translation(CmdName). | |
1432 | ||
1433 | %% tcltk_get_alloy_cmd_names(-CmdNames). | |
1434 | tcltk_get_alloy_cmd_names(CmdNames) :- | |
1435 | alloy2b:get_command_names(CmdNames). | |
1436 | ||
1437 | %% tcltk_verify_alloy_cmd(+CmdName, +SolverName, -CmdIsValid, -IsCheckCmd). | |
1438 | tcltk_verify_alloy_cmd(CmdName, SolverName, CmdIsValid, IsCheckCmd) :- | |
1439 | verify_alloy_command(CmdName, SolverName, CmdIsValid, IsCheckCmd, Res), | |
1440 | ( Res = solution(Bindings) | |
1441 | -> bindings_to_state(Bindings, State), | |
1442 | tcltk_add_cbc_state(State, CmdName) | |
1443 | ; true | |
1444 | ). | |
1445 | ||
1446 | bindings_to_state([], []). | |
1447 | bindings_to_state([Binding|T], [Bind|NT]) :- | |
1448 | (Binding = binding(Id,Val,_); Binding = bind(Id,Val)), | |
1449 | Bind = bind(Id,Val), | |
1450 | bindings_to_state(T, NT). | |
1451 | ||
1452 | /* --------------------------------------------------------------------- */ | |
1453 | ||
1454 | tcltk_exists_an_open_node :- | |
1455 | not_all_transitions_added(_) ; not_interesting(_). | |
1456 | tcltk_goto_an_open_node :- | |
1457 | (not_all_transitions_added(ID) ; not_interesting(ID)),!, | |
1458 | tcltk_goto_state(find_open_node,ID). | |
1459 | tcltk_goto_max_reached_node :- | |
1460 | (max_reached_for_node(ID) ; time_out_for_node(ID)),!, | |
1461 | tcltk_execute_trace_to_node(ID). | |
1462 | tcltk_goto_timeout_node :- | |
1463 | time_out_for_node(ID),!, | |
1464 | tcltk_execute_trace_to_node(ID). | |
1465 | % tcltk_goto_state(find_max_reached_node,ID). | |
1466 | ||
1467 | tcltk_goto_an_invariant_violation :- | |
1468 | invariant_violated(ID),!, | |
1469 | tcltk_goto_state(find_inv_violation,ID). | |
1470 | ||
1471 | ||
1472 | tcltk_goto_node_with_id(ToID) :- | |
1473 | current_state_id(ToID),!. | |
1474 | tcltk_goto_node_with_id(ID) :- | |
1475 | visited_expression_id(ID), | |
1476 | tcltk_execute_trace_to_node(ID). | |
1477 | ||
1478 | tcltk_try_goto_node_with_id(ID) :- | |
1479 | (tcltk_goto_node_with_id(ID) -> true | |
1480 | ; add_internal_error('Call failed:',tcltk_goto_node_with_id(ID))). | |
1481 | ||
1482 | /* --------------------------------------------------------------------- */ | |
1483 | ||
1484 | % TO DO: clean up and move into model_checker module | |
1485 | ||
1486 | tcltk_state_space_only_has_root_node :- transition(root,_,ID), | |
1487 | \+ not_all_transitions_added(ID),!,fail. | |
1488 | tcltk_state_space_only_has_root_node. | |
1489 | ||
1490 | tcltk_search_among_existing_nodes(TclTkRes,FindDeadlocks,FindInvViolations, | |
1491 | FindGoal,FindAssViolations,FindStateErrors) :- | |
1492 | search_among_existing_nodes(Res,FindDeadlocks,FindInvViolations, | |
1493 | FindGoal,FindAssViolations,FindStateErrors), | |
1494 | translate_error_for_tclk(Res,TclTkRes). | |
1495 | ||
1496 | % if tcltk_state_space_only_has_root_node is true: then we should call search_among_existing_nodes before doing open_search to ensure that we uncover state_errors in the root node | |
1497 | % TODO: process mc_continue_after_error preference | |
1498 | search_among_existing_nodes(invariant_violation,_FindDeadlocks,FindInvViolations, | |
1499 | _FindGoal,_FindAssViolations,_FindStateErrors) :- | |
1500 | (get_state_space_stats(_,_,PrN), PrN>100 | |
1501 | -> print_message('Search Among Processed Nodes'), print_message(number_of_nodes(PrN)) ; true), | |
1502 | FindInvViolations=1, | |
1503 | (invariant_violated(ResID) -> true | |
1504 | ; find_invariant_violation_among_not_checked_nodes(ResID)), | |
1505 | tcltk_execute_trace_to_node(ResID). | |
1506 | search_among_existing_nodes(Error,_FindDeadlocks,FindInvViolations, | |
1507 | _FindGoal,_FindAssViolations,_FindStateErrors) :- | |
1508 | FindInvViolations=1, | |
1509 | find_xtl_csp_invariant_violation_among_all_existing_nodes(ResID,Error), % could be xtl_error | |
1510 | tcltk_execute_trace_to_node(ResID). | |
1511 | search_among_existing_nodes(DEADLOCK,FindDeadlocks,_,_,_,_) :- | |
1512 | FindDeadlocks=1, | |
1513 | visited_expression_id(ResID), | |
1514 | transitions_computed_for_node(ResID), | |
1515 | is_deadlocked(ResID), | |
1516 | (time_out_for_node(ResID) | |
1517 | -> DEADLOCK = possible_deadlock_timeout | |
1518 | ; get_preference(store_only_one_incoming_transition,true) -> DEADLOCK = possible_deadlock | |
1519 | ; DEADLOCK = deadlock), | |
1520 | print_message(deadlock_among_existing_nodes(ResID)), | |
1521 | tcltk_execute_trace_to_node(ResID). | |
1522 | search_among_existing_nodes(goal_found,_,_,FindGoal,_,_) :- | |
1523 | FindGoal=1, | |
1524 | %visited_expression(ResID,_CurState), | |
1525 | %\+(not_all_transitions_added(ResID)), % if commented out: will also look at open nodes | |
1526 | node_satisfies_goal(ResID), | |
1527 | tcltk_execute_trace_to_node(ResID). | |
1528 | search_among_existing_nodes(assertion_violation,_,_,_,FindAssViolations,_) :- | |
1529 | FindAssViolations=1, | |
1530 | b_machine_has_assertions, | |
1531 | visited_expression(ResID,CurState), | |
1532 | transitions_computed_for_node(ResID), | |
1533 | state_violates_assertions(ResID,CurState), | |
1534 | tcltk_execute_trace_to_node(ResID). | |
1535 | search_among_existing_nodes(ERR,_,_,_,_,FindStateErrors) :- FindStateErrors=1, | |
1536 | state_space:state_error(ResID,_,Error), Error \== invariant_violated, | |
1537 | (Error = abort_error(ERR,_,_,_) -> true ; ERR = state_error(Error)), | |
1538 | tcltk_execute_trace_to_node(ResID). | |
1539 | search_among_existing_nodes(all,_,_,_,_,_). | |
1540 | ||
1541 | ||
1542 | compute_all_transitions_if_necessary :- | |
1543 | current_state_id(CurID), | |
1544 | compute_all_transitions_if_necessary(CurID,false). | |
1545 | :- public compute_all_transitions_if_necessary/1. | |
1546 | compute_all_transitions_if_necessary(CurID) :- | |
1547 | compute_all_transitions_if_necessary(CurID,false). | |
1548 | ||
1549 | compute_all_transitions_if_necessary(CurID,CheckInvariant) :- | |
1550 | (retract_open_node(CurID) % will assert not_invariant_checked | |
1551 | -> set_context_state(CurID), | |
1552 | % first check invariant, relevant if we want to use specialized_inv proof information | |
1553 | visited_expression(CurID,State), | |
1554 | % TO DO: this does not expand concrete_constants: the expansion may be done twice below: | |
1555 | % once for compute_invariant (corresponds_to_b...) and once for compute_all_transitions (prepare_state_for_specfile_trans) | |
1556 | prepare_state_for_specfile_trans(State,CurID,PreparedState), | |
1557 | (CheckInvariant==true | |
1558 | -> compute_invariant_if_necessary_and_requested(CurID,PreparedState) ; true), | |
1559 | compute_all_transitions(CurID,PreparedState), | |
1560 | clear_context_state | |
1561 | ; true). | |
1562 | %compute_all_transitions_if_necessary(CurID,CurState) :- | |
1563 | % ((not_all_transitions_added(CurID);not_interesting(CurID)) | |
1564 | % -> compute_all_transitions(CurID,CurState) ; true). | |
1565 | ||
1566 | ||
1567 | compute_invariant_if_necessary_and_requested(ID,State) :- | |
1568 | enter_new_error_scope(ErrScID,compute_invariant_if_necessary_and_requested), % here we setup a new error scope; see comment for force_check_invariantKO below | |
1569 | compute_invariant_if_necessary(ID,State), | |
1570 | exit_error_scope(ErrScID,_ErrOcc,compute_invariant_if_necessary_and_requested). % should we do something with error occured result ? | |
1571 | ||
1572 | compute_invariant_if_necessary(ID,State) :- | |
1573 | animation_mode(MODE),compute_invariant_if_necessary(ID,State,MODE). | |
1574 | compute_invariant_if_necessary(ID,State,MODE) :- | |
1575 | ( check_invariantKO(MODE,ID,State) -> true ; true). | |
1576 | ||
1577 | check_invariantKO(ID,State) :- | |
1578 | animation_mode(MODE),check_invariantKO(MODE,ID,State). | |
1579 | check_invariantKO(b,ID,State) :- | |
1580 | check_invariantKO2(ID,State). | |
1581 | check_invariantKO(csp_and_b,ID,State) :- | |
1582 | check_invariantKO2(ID,State). | |
1583 | % for XTL we do not use not_invariant_checked/1 facts; one should call xtl_invariant_violated/1 | |
1584 | ||
1585 | % check invariant for B state | |
1586 | check_invariantKO2(ID,_) :- invariant_violated(ID),!. | |
1587 | check_invariantKO2(ID,CurState) :- | |
1588 | set_invariant_checked(ID), % only succeeds if we haven't checked invariant for ID yet | |
1589 | % print(check_inv(ID)),nl, | |
1590 | state_corresponds_to_initialised_b_machine(CurState), | |
1591 | ( preferences:preference(do_invariant_checking,false) -> assertz(not_invariant_checked(ID)) | |
1592 | ; force_check_invariantKO(ID,CurState)). | |
1593 | ||
1594 | ||
1595 | force_check_invariantKO(ID,CurBState) :- | |
1596 | % Note: this checks the invariant without setting up a new error scope | |
1597 | % (Reason: in the model checker it would be relatively expensive to set up an error scope for each state | |
1598 | % the model checker stops anyway when the first error is found and then exits its error scope) | |
1599 | catch_clpfd_overflow_call_for_state(ID, 'checking invariants', | |
1600 | b_state_violates_invariant(ID,CurBState),true) -> | |
1601 | %(b_state_violates_invariant(ID,CurBState) -> | |
1602 | set_invariant_violated(ID). | |
1603 | % TO DO ?: call state_satisfies_negation_of_invariant if double_evaluation_when_analysing is true ? | |
1604 | ||
1605 | % try and find an invariant violation among those nodes that have not yet been checked | |
1606 | find_invariant_violation_among_not_checked_nodes(ID) :- | |
1607 | retract(not_invariant_checked(ID)), %print(checking_invariant(ID)),nl, | |
1608 | visited_expression(ID,State), | |
1609 | state_corresponds_to_initialised_b_machine(State), | |
1610 | prepare_state_for_specfile_trans(State,ID,CurBState), | |
1611 | force_check_invariantKO(ID,CurBState). | |
1612 | ||
1613 | find_xtl_csp_invariant_violation_among_all_existing_nodes(ID,Error) :- | |
1614 | animation_mode(MODE), | |
1615 | (MODE=xtl ; MODE=csp), % invariant violations not stored, hence we need to re-check every node | |
1616 | visited_expression(ID,State), | |
1617 | find_invariant_error(MODE,ID,State,Error). % csp_error or xtl_error | |
1618 | ||
1619 | :- use_module(probporsrc(ample_sets),[compute_ample_set2/3]). | |
1620 | % computing ample sets (model_checker.pl) | |
1621 | compute_ample_set(CurID,CurState,POROptions) :- | |
1622 | catch_clpfd_overflow_call_for_state(CurID,'computing ample sets for POR', | |
1623 | compute_ample_set2(CurID,CurState,POROptions),clear_context_state). | |
1624 | ||
1625 | ||
1626 | %compute_all_transitions(CurID,CurState) :- get_preference(use_clpfd_solver,false),!, | |
1627 | % compute_all_transitions2(CurID,CurState). % in principle this overflow cannot/should not happen with CLPFD FALSE | |
1628 | % but no performance benefit can be measured by removing the catch | |
1629 | compute_all_transitions(CurID,CurState) :- | |
1630 | catch_clpfd_overflow_call_for_state(CurID,'computing all enabled transitions', | |
1631 | compute_all_transitions2(CurID,CurState),clear_context_state). | |
1632 | ||
1633 | ||
1634 | %catch_clpfd_overflow_call_for_state(_CurID, Call,_ExtraCleanUpCode) :- | |
1635 | % preferences:preference(use_clpfd_solver,false),!, % Overflow should not occur | |
1636 | % call(Call). | |
1637 | :- use_module(clpfd_interface,[catch_clpfd_overflow_call2/2]). | |
1638 | catch_clpfd_overflow_call_for_state(CurID, Context, Call,ExtraCleanUpCode) :- | |
1639 | catch_clpfd_overflow_call2(Call, | |
1640 | ( store_state_error(CurID,clpfd_overflow_error(Context),_), | |
1641 | call(ExtraCleanUpCode)) ). | |
1642 | ||
1643 | ||
1644 | ||
1645 | ||
1646 | :- use_module(probsrc(succeed_max),[reset_max_reached/0,max_reached/0, max_reached/1]). | |
1647 | compute_all_transitions2(CurID,CurState) :- /* compute all outgoing transitions for a given node */ | |
1648 | % set_context_state(CurID), % error_context is set for each operation below anyway. | |
1649 | reset_max_reached, | |
1650 | prepare_state_for_specfile_trans(CurState,CurID,PreparedCurState), % will perform unpacking of constants just once, for example | |
1651 | ((time_out_preference_disabled % e.g., when using probcli -disable_time_out | |
1652 | ; use_no_timeout(CurID)) | |
1653 | -> add_transitions_fail_loop(CurID,PreparedCurState) | |
1654 | ; preferences:preference(time_out,CurTO), | |
1655 | add_transitions__with_timeout_fail_loop(CurID,PreparedCurState,CurTO) | |
1656 | ), | |
1657 | add_partial_transitions(CurID,PreparedCurState), % also copies unsat component infos | |
1658 | (max_reached -> assert_max_reached_for_node(CurID),MaxReached=true ; MaxReached=false), | |
1659 | % TO DO: use max_reached(OpID) | |
1660 | (CurID==root -> save_constants(MaxReached) ; add_new_transitions_to_cache(CurID)) | |
1661 | . %,clear_context_state. | |
1662 | ||
1663 | :- use_module(error_manager,[get_virtual_time_out_from_exception/2]). % used by time_out_with_enum_warning_for_findall | |
1664 | % loop to compute successor states without a time_out | |
1665 | add_transitions_fail_loop(CurID,CurState) :- | |
1666 | catch( | |
1667 | add_transitions_fail_loop2(CurID,CurState,TimeOutRes), | |
1668 | Exc, | |
1669 | get_virtual_time_out_from_exception(Exc,TimeOutRes)), | |
1670 | (nonvar(TimeOutRes) | |
1671 | -> assert_time_out_for_node(CurID,'unspecified_operation',TimeOutRes) | |
1672 | ; true). | |
1673 | ||
1674 | add_transitions_fail_loop2(CurID,CurState,_) :- | |
1675 | possible_trans_name_for_successors(CurState,ActionName), | |
1676 | % we could check pge info here (e.g., store one Predecessor ID and look if ActionName can be enabled after) | |
1677 | set_error_context(operation(ActionName,CurID)), | |
1678 | add_transition(CurID,CurState,ActionName,_NewId), % TO DO: transmit animation_mode value? | |
1679 | fail. | |
1680 | add_transitions_fail_loop2(_,_,TimeOutRes) :- | |
1681 | (enumeration_warning_occured_in_error_scope | |
1682 | -> (virtual_time_out_occured_in_error_scope(TimeOutRes) | |
1683 | -> true % i.e., critical enumeration warning occured | |
1684 | ; true), | |
1685 | clear_enumeration_warnings | |
1686 | ; true % no warnings: sunshine case | |
1687 | ), | |
1688 | clear_error_context. | |
1689 | ||
1690 | possible_trans_name_for_successors(CurState,ActionName) :- | |
1691 | specfile_possible_trans_name_for_successors(CurState,ActionName), | |
1692 | (preference(store_only_one_incoming_transition,true), % SAFETY_MODEL_CHECK | |
1693 | nonvar(ActionName), | |
1694 | b_or_z_mode, | |
1695 | b_operation_cannot_modify_state(ActionName) % we have a skip operation | |
1696 | -> operation_not_yet_covered(ActionName) | |
1697 | % if operation covered: all we could is create a self-loop, which would not be added to the state space anyway | |
1698 | ; true). | |
1699 | ||
1700 | ||
1701 | :- use_module(library(timeout),[time_out/3]). | |
1702 | my_timeout_test(1000000) :- write('.'), !, my_timeout_test(0). | |
1703 | my_timeout_test(X) :- X1 is X+1, my_timeout_test(X1). | |
1704 | :- public my_timeout_test/0. | |
1705 | my_timeout_test :- time_out(my_timeout_test(0),500,TO), nl, | |
1706 | print(time_out_result(TO)),nl, TO==time_out. | |
1707 | :- assert_must_succeed(tcltk_interface:my_timeout_test). | |
1708 | ||
1709 | add_transitions__with_timeout_fail_loop(CurID,CurState,CurTO) :- | |
1710 | enter_new_error_scope(Level,add_transitions__with_timeout_fail_loop), | |
1711 | call_cleanup(add_transitions__with_timeout_fail_loop2(Level,CurID,CurState,CurTO), | |
1712 | exit_error_scope(Level,_,add_transitions__with_timeout_fail_loop)). | |
1713 | ||
1714 | % old profiler | |
1715 | :- use_module(runtime_profiler,[profile_failure_driven_loop/2]). | |
1716 | ||
1717 | % the loop to compute all successors of state CurID with a time-out | |
1718 | add_transitions__with_timeout_fail_loop2(Level,CurID,CurState,CurTO) :- % statistics(runtime,_), | |
1719 | % TO DO: prepare computing the state: expanding const and vars, unpacking, ... | |
1720 | possible_trans_name_for_successors(CurState,ActionName), | |
1721 | profile_failure_driven_loop(ActionName,CurID), | |
1722 | % Note: we enumerate possible transition names so that we can catch the time-out per name ! | |
1723 | % statistics(runtime,[T1,DeltaT]), print(delta_time(ActionName,DeltaT)),nl, %% | |
1724 | % first enumerate possible Operations/... to obtain timeout per operation/... | |
1725 | % TO DO: divide CurTO by number of transitions ??? | |
1726 | % We could also decide to have one time-out per state, only if that one is triggered move to | |
1727 | % finer-grained timeouts | |
1728 | % For probcli ../prob_examples/public_examples/B/Benchmarks/Cruise_finite1.mch --model-check, there are 35360 calls to timeout/3 (one per state the model checker reached and per transition group), with -disable-timeout none | |
1729 | % In SICS 4.3.5 the runtime increased from 1.480 to 2.270 seconds = 0.022 ms per timeout call | |
1730 | % In SICS 4.4.1 the runtime increased from 1.494 to 3.033 seconds. = 0.044 ms per timeout call. | |
1731 | %% statistics(runtime,[T1,_]), print(try(ActionName,T1)),nl,%% | |
1732 | set_error_context(operation(ActionName,CurID)), | |
1733 | time_out_with_enum_warning_for_findall_in_current_error_scope(Level, | |
1734 | add_transition(CurID,CurState,ActionName,_NewId),CurTO,TimeOutRes), | |
1735 | %% statistics(runtime,[T2,_]), TDiff is T2-T1, print(added_transition(CurID,ActionName,TDiff,TimeOutRes)),nl, %% | |
1736 | %% add_transition(CurID,CurState,ActionName,_NewId) , | |
1737 | ((TimeOutRes==time_out ; nonvar(TimeOutRes),TimeOutRes=virtual_time_out(_), \+ max_reached(ActionName)) | |
1738 | % virtual time outs not relevant if we have already stopped earlier anyway; TO DO: does not seem to work for INITIALISATION | |
1739 | -> (TimeOutRes=virtual_time_out(_) -> true | |
1740 | ; print_message('TIME OUT: '),print_message(CurID), | |
1741 | print_message(CurTO),print_message(ActionName) | |
1742 | ), | |
1743 | (var(ActionName) | |
1744 | -> assert_time_out_for_node(CurID,'unspecified_operation',TimeOutRes) | |
1745 | ; assert_time_out_for_node(CurID,ActionName,TimeOutRes)) % TO DO: assert info per ActionSkeleton ? | |
1746 | ; true | |
1747 | ),fail. | |
1748 | add_transitions__with_timeout_fail_loop2(_,_,_,_) :- clear_error_context. | |
1749 | % statistics(runtime,[_,DeltaT]), print(delta_time(end,DeltaT)),nl. | |
1750 | ||
1751 | % compute and add transition to next states | |
1752 | add_transition(CurID,CurState,ActionName,NewId) :- | |
1753 | /* ActionName maybe pre-instantiated */ | |
1754 | extract_infos_from_prepared_state(CurState,PrecomputedInfos), | |
1755 | specfile_trans(CurState,ActionName,Act,NewExpression,TransInfo,Residue), | |
1756 | %% print_bt_message(specfile_trans(CurState,ActionName,Act,NewExpression,TransInfo,Residue)), %% | |
1757 | add_trans_id_with_infos(CurID,PrecomputedInfos,Act,NewExpression,Residue,NewId,TransInfo,_). | |
1758 | ||
1759 | ||
1760 | % after being computed, add a new transition in the state space; adding the destination state if required | |
1761 | add_trans_id_with_infos(CurID,PrecomputedInfos,Act,NewExpression,Residue,NewID,TransInfo,TransId) :- | |
1762 | (Residue=[] -> true | |
1763 | ; add_error(add_trans_id,'Residue: ',CurID:Act:Residue), | |
1764 | tools_printing:print_goal(Residue),nl | |
1765 | ), | |
1766 | add_new_transition_transid_with_infos(CurID,PrecomputedInfos,Act,NewID,NewExpression,TransInfo,TransId). | |
1767 | ||
1768 | add_trans_id(CurID,Act,NewExpression,NewID,TransId) :- | |
1769 | add_trans_id_with_infos(CurID,[],Act,NewExpression,[],NewID,[],TransId). | |
1770 | ||
1771 | add_partial_transitions(root,CurState) :- | |
1772 | /* for the moment the only state where partial transitions are computed */ | |
1773 | \+ any_transition(root,_,_), /* adding normal transitions was not successful */ | |
1774 | partial_trans(CurState,Act,NewExpression,Residue), | |
1775 | add_trans_id_with_infos(root,[],Act,NewExpression,Residue,_NewID,[],_),fail. | |
1776 | add_partial_transitions(_CurID,_). | |
1777 | ||
1778 | /* --------------------------------------------------------------------- */ | |
1779 | ||
1780 | /* A bit like AO* algorithm: expands open nodes (i.e., states whose transitions | |
1781 | have not yet been computed */ | |
1782 | ||
1783 | :- use_module(model_checker). | |
1784 | ||
1785 | tcltk_model_check(Nr,TclTkRes,FindDeadlocks,FindInvViolations,FindGoal,FindAssViolations, | |
1786 | FindStateErrors,StopFullCoverage,PartialOrderReduction,PartialGuardsEvaluation,TIMELIMIT,TotalTime) :- | |
1787 | statistics(walltime,[CurTime,_]), /* get current time in ms */ | |
1788 | LimitTime is CurTime+TIMELIMIT, | |
1789 | %debug_println(10,start_tcltk_model_check(Nr,TIMELIMIT)), | |
1790 | InspectExistingNodes = 0, | |
1791 | (do_model_check(Nr,_A,LimitTime,Res,FindDeadlocks,FindInvViolations,FindGoal, | |
1792 | FindAssViolations,FindStateErrors,StopFullCoverage, | |
1793 | PartialOrderReduction,PartialGuardsEvaluation,InspectExistingNodes) | |
1794 | -> translate_error_for_tclk(Res,TclTkRes) | |
1795 | ; add_internal_error('Call failed: ',do_model_check(Nr,_A,LimitTime,Res,FindDeadlocks,FindInvViolations,FindGoal, | |
1796 | FindAssViolations,FindStateErrors,StopFullCoverage, | |
1797 | PartialOrderReduction,PartialGuardsEvaluation,InspectExistingNodes)), | |
1798 | TclTkRes=error), | |
1799 | statistics(walltime,[CurTime2,_]), | |
1800 | TotalTime is CurTime2-CurTime, | |
1801 | debug_println(10,tcltk_model_check(Nr,TotalTime,Res)). | |
1802 | ||
1803 | translate_error_for_tclk([timeout,Nr],Res) :- !, Res=[timeout,Nr]. | |
1804 | translate_error_for_tclk(state_error(S),String) :- !, translate_state_err_for_tcltk(S,String). | |
1805 | translate_error_for_tclk(S,R) :- atomic(S),!,R=S. | |
1806 | translate_error_for_tclk(S,R) :- nonvar(S), add_error(translate_error_for_tclk,'Unknown Model Check result: ',S), R=unknown. | |
1807 | ||
1808 | translate_state_err_for_tcltk(abort_error(TYPE,_,_,_),ERR) :- !, ERR = TYPE. | |
1809 | translate_state_err_for_tcltk(eventerror(Event,Error,_),ERR) :- !, | |
1810 | functor(Error,F,_), | |
1811 | ajoin(['event_error:',Event,':',F],ERR). | |
1812 | translate_state_err_for_tcltk(Error,ERR) :- | |
1813 | functor(Error, StateError, _), atom_concat('state_error:',StateError,ERR). | |
1814 | ||
1815 | ||
1816 | ||
1817 | do_model_check(_Nr,NodesAnalysed,_LimitTime,Res,FindDeadlocks,FindInvViolations,FindGoal, | |
1818 | FindAssViolations,FindStateErrors,_StopFullCoverage, | |
1819 | _PartialOrderReduction,_PartialGuardsEvaluation,ForceInspectExistingNodes) :- | |
1820 | (tcltk_state_space_only_has_root_node -> true % Then also look at the root node | |
1821 | ; ForceInspectExistingNodes == 1), | |
1822 | search_among_existing_nodes(Res,FindDeadlocks,FindInvViolations,FindGoal, | |
1823 | FindAssViolations,FindStateErrors), | |
1824 | Res \=all, NodesAnalysed=0. | |
1825 | do_model_check(LimitNr,NodesAnalysed,LimitTime,Res,FindDeadlocks,FindInvViolations,FindGoal, | |
1826 | FindAssViolations,FindStateErrors,StopFullCoverage,PartialOrderReduction,PartialGuardsEvaluation,_) :- | |
1827 | update_ass(FindAssViolations,FindAssViolations2), | |
1828 | open_search(LimitNr,NodesAnalysed,LimitTime,Res,FindDeadlocks,FindInvViolations,FindGoal, | |
1829 | FindAssViolations2,FindStateErrors,StopFullCoverage,PartialOrderReduction,PartialGuardsEvaluation). | |
1830 | ||
1831 | update_ass(1,FindAssViol) :- \+ b_machine_has_assertions,!, % no need to check assertions | |
1832 | FindAssViol=0. | |
1833 | update_ass(X,X). | |
1834 | ||
1835 | /* --------------------------------------------------- */ | |
1836 | ||
1837 | /* constraint-based checking of refinements */ | |
1838 | :- use_module(symbolic_model_checker(cbc_refinement_checks), [cbc_refinement_check/2]). | |
1839 | tcltk_cbc_refinement_check(list(List),ErrorOccured) :- | |
1840 | cbc_refinement_check(List,ErrorOccured). | |
1841 | ||
1842 | /* constraint-based/model search for invariant violations */ | |
1843 | ||
1844 | :- dynamic cbc_inv_error_found/2. | |
1845 | ||
1846 | tcltk_constraint_based_check(L) :- tcltk_constraint_based_check(L,_). | |
1847 | tcltk_constraint_based_check(list(List),ErrorsOrTimeoutWereFound) :- | |
1848 | retractall(cbc_inv_error_found(_,_)), | |
1849 | findall(Res, (tcltk_constraint_based_check_with_timeout(OpName,ErrorDesc), | |
1850 | (ErrorDesc=ok -> true ; assertz(cbc_inv_error_found(OpName,ErrorDesc))), | |
1851 | string_concatenate(' == ',ErrorDesc,Res1), | |
1852 | string_concatenate(OpName,Res1,Res)), List), | |
1853 | %%print(list(List)),nl, %% | |
1854 | (cbc_inv_error_found(_,_) -> | |
1855 | (cbc_inv_error_found(_,X),X\=time_out | |
1856 | -> ErrorsOrTimeoutWereFound=true ; ErrorsOrTimeoutWereFound=time_out | |
1857 | ) | |
1858 | ; ErrorsOrTimeoutWereFound = false). | |
1859 | ||
1860 | % can also be called with uninstantiated OpName | |
1861 | tcltk_constraint_based_check_with_timeout(OpName,ErrorDesc) :- | |
1862 | preferences:get_computed_preference(debug_time_out,DebugTimeOut), | |
1863 | valid_operation_or_init(OpName,IOpName), | |
1864 | (time_out_with_enum_warning_one_solution(tcltk_constraint_based_check_op(IOpName,ErrorDesc),DebugTimeOut,TimeOutRes) | |
1865 | -> (is_time_out_result(TimeOutRes) | |
1866 | -> print_message('TIME OUT: '), print_message(OpName), print_message(DebugTimeOut), | |
1867 | ErrorDesc = timeout | |
1868 | ; true | |
1869 | )). | |
1870 | valid_operation_or_init(OpName,R) :- OpName=='@INITIALISATION',!,R='$initialise_machine'. | |
1871 | valid_operation_or_init(OpName,OpName) :- b_get_machine_operation(OpName,_,_,_). | |
1872 | valid_operation_or_init('INITIALISATION','$initialise_machine'). % used e.g. in test 1821: -cbc INITIALISATION | |
1873 | ||
1874 | tcltk_constraint_based_check_op(OpName,ErrorDesc) :- | |
1875 | tcltk_constraint_based_check_op(OpName,invariant,ErrorDesc). | |
1876 | ||
1877 | tcltk_constraint_based_check_op(TclOpName,InvOrAssertion,ErrorDesc) :- | |
1878 | adapt_tcl_operation_name(TclOpName,OpName), | |
1879 | tcltk_constraint_based_check_op2(OpName,InvOrAssertion,ErrorDesc). | |
1880 | ||
1881 | % code below because names with $ cause problems in Tcl with eval | |
1882 | adapt_tcl_operation_name(X,R) :- var(X),!,R=X. | |
1883 | adapt_tcl_operation_name('@PROPERTIES','$setup_constants') :- !. | |
1884 | adapt_tcl_operation_name('@INITIALISATION','$initialise_machine') :- !. | |
1885 | adapt_tcl_operation_name(X,X). | |
1886 | ||
1887 | % a version that allows either invariant or assertions check after an Operation OpName | |
1888 | tcltk_constraint_based_check_op2(OpName,InvOrAssertion,ErrorDesc) :- | |
1889 | call_cbc_command(state_model_check(OpName,InvOrAssertion,State,Operation,NewState)),!, | |
1890 | %fd_copy_term(NewState,ResTempl,ResBody), print(ResTempl),print(ResBody),nl, | |
1891 | %fd_copy_term(State,PriorTempl,PriorBody), | |
1892 | tcltk_add_new_transition_transid(root,'$JUMP'(constraint_based_check),PriorID,State,[],TransId1), | |
1893 | translate_event(Operation,Action), | |
1894 | tcltk_add_new_transition_transid(PriorID,Operation,NewID,NewState,[],TransId2), | |
1895 | set_current_state_id(NewID), | |
1896 | reset_op_trace_ids, | |
1897 | add_to_op_trace_ids(TransId1), add_to_op_trace_ids(TransId2), | |
1898 | retractall(history(_)), | |
1899 | retractall(forward_history(_)), | |
1900 | assertz(history([PriorID,root])), | |
1901 | ajoin([InvOrAssertion,'_violated_after(',Action,')'],ErrorDesc). | |
1902 | tcltk_constraint_based_check_op2(_,_,'ok'). | |
1903 | ||
1904 | ||
1905 | % try to find constants, parameters that make a given trace feasible | |
1906 | tcltk_cbc_find_trace(TraceToBeFound,Res) :- | |
1907 | tcltk_cbc_find_trace(TraceToBeFound,'',first_solution,Res). | |
1908 | ||
1909 | %:- public tcltk_cbc_find_invariant_violation/2. | |
1910 | %tcltk_cbc_find_invariant_violation(TraceToBeFound,Res) :- | |
1911 | % tcltk_cbc_find_trace(TraceToBeFound,'#not_invariant',first_solution,Res). | |
1912 | % could also call tcltk_constraint_based_check_op('$initialise_machine',invariant,Res) | |
1913 | ||
1914 | tcltk_cbc_find_trace(TraceToBeFound,TargetPredString,FindAll,Res) :- | |
1915 | b_parse_optional_machine_predicate(TargetPredString,TargetPred), | |
1916 | formatsilent('Looking for solution for trace ~w~n',[TraceToBeFound]), | |
1917 | split_atom(TraceToBeFound,[',',';',' '],TraceAsList), | |
1918 | (maplist(check_valid_operation,TraceAsList) | |
1919 | -> start_ms_timer(Timer), | |
1920 | tcltk_cbc_find_trace_aux(Timer,TraceAsList,TargetPred,FindAll,Res) | |
1921 | ; Res = error). | |
1922 | tcltk_cbc_find_trace_aux(Timer,TraceAsList,TargetPred,FindAll,Res) :- | |
1923 | preferences:get_computed_preference(debug_time_out,DebugTimeOut), | |
1924 | reset_nr_cbc_sols, | |
1925 | time_out_with_enum_warning_one_solution( | |
1926 | cbc_path_solver:create_testcase_path_nondet(init,TraceAsList,TargetPred,Transitions),DebugTimeOut,TimeOutRes), | |
1927 | stop_ms_timer(Timer), | |
1928 | (is_time_out_result(TimeOutRes) | |
1929 | -> !, | |
1930 | print_message('*** TIME OUT: '), print_message(DebugTimeOut), Res = time_out | |
1931 | ; FindAll = findall -> inc_nr_cbc_sols, | |
1932 | print('% *** FOUND PATH: '), maplist(print_trans,Transitions),nl, | |
1933 | fail % backtrack to find further solutions | |
1934 | ; !,Res = ok, | |
1935 | print('% *** FOUND PATH: '), maplist(print_trans,Transitions),nl, | |
1936 | maplist(extract_trans_id,Transitions,TransIds), | |
1937 | set_trace_by_transition_ids(TransIds) | |
1938 | ). | |
1939 | tcltk_cbc_find_trace_aux(Timer,_TraceToBeFound,_TargetPred,findall,R) :- !, stop_ms_timer(Timer), | |
1940 | R=nr_cbc_sols(Res), | |
1941 | nr_cbc_sols(Res), | |
1942 | print_message('*** ALL SOLUTIONS FOUND '). | |
1943 | tcltk_cbc_find_trace_aux(Timer,_TraceToBeFound,_TargetPred,_FindAll,no_solution_found) :- | |
1944 | stop_ms_timer(Timer), | |
1945 | print_message('*** NO SOLUTION FOUND'),nl. | |
1946 | ||
1947 | :- dynamic nr_cbc_sols/1. | |
1948 | nr_cbc_sols(0). | |
1949 | reset_nr_cbc_sols :- retractall(nr_cbc_sols(_)), assertz(nr_cbc_sols(0)). | |
1950 | inc_nr_cbc_sols :- | |
1951 | retract(nr_cbc_sols(N)), N1 is N+1, assertz(nr_cbc_sols(N1)). | |
1952 | extract_trans_id( (TransId,_,_,_), TransId). | |
1953 | print_trans( (_,Event,_,_)) :- translate_event(Event,S), print(S), print(' '). | |
1954 | ||
1955 | :- use_module(probsrc(tools_matching), [get_possible_top_level_event_matches_msg/2]). | |
1956 | check_valid_operation(Op) :- | |
1957 | (get_possible_language_specific_top_level_event(Op,_,_) -> true | |
1958 | ; (get_possible_top_level_event_matches_msg(Op,FMsg) | |
1959 | -> ajoin(['Unknown operation/event (did you mean ',FMsg,' ?):'],Msg) | |
1960 | ; Msg='Unknown operation/event:' | |
1961 | ), | |
1962 | add_error(cbc_find_sequence,Msg,Op), | |
1963 | fail | |
1964 | ). | |
1965 | ||
1966 | ||
1967 | ||
1968 | % ------------ | |
1969 | ||
1970 | :- use_module(bsyntaxtree). | |
1971 | tcltk_constraint_find_valid_state_with_pred(ParameterBindList,RestPreCond,UseConstantsFromStateID) :- | |
1972 | parse_tclk_parameter_values_and_pred('$initialise_machine',ParameterBindList,RestPreCond,CustomPredicate), | |
1973 | UseInvariant=true, | |
1974 | call_cbc_command( | |
1975 | b_state_model_check:b_set_up_valid_state_with_pred(State,CustomPredicate, | |
1976 | UseInvariant,UseConstantsFromStateID)), | |
1977 | tcltk_add_cbc_state(State,find_valid_state). | |
1978 | tcltk_constraint_find_valid_state :- | |
1979 | call_cbc_command(b_set_up_valid_state(State)), | |
1980 | tcltk_add_cbc_state(State,find_valid_state). | |
1981 | ||
1982 | :- use_module(extrasrc(optimizing_solver),[b_set_up_maximally_valid_state/2]). | |
1983 | tcltk_constraint_find_maximal_valid_state :- | |
1984 | current_state_id(ID), | |
1985 | call_cbc_command(b_set_up_maximally_valid_state(ID,State)), | |
1986 | tcltk_add_cbc_state(State,find_maximal_valid_state). | |
1987 | ||
1988 | ||
1989 | tcltk_constraint_find_dynamic_assertion_violation :- | |
1990 | call_cbc_command(b_find_dynamic_assertion_violation(State)), | |
1991 | tcltk_add_cbc_state(State,cbc_dynamic_assertions_check). | |
1992 | ||
1993 | :- use_module(clpfd_interface,[catch_clpfd_overflow_call1/1]). | |
1994 | :- use_module(error_manager,[call_in_fresh_error_scope_for_one_solution/1]). | |
1995 | % catch CLPFD overflows and catch other errors | |
1996 | call_cbc_command(Command) :- | |
1997 | call_in_fresh_error_scope_for_one_solution( | |
1998 | (catch_clpfd_overflow_call1(Command) -> true | |
1999 | ; translate_events_in_current_scope_to_warnings(cbc,'Warning: '),fail | |
2000 | % Maybe we should raise error when Command fails and enumeration warnings are there ? | |
2001 | )). | |
2002 | ||
2003 | ||
2004 | tcltk_constraint_find_deadlock_state(Res) :- | |
2005 | check_we_are_in_b_mode(cbc_deadlock_freedom_check), | |
2006 | create_texpr(truth,pred,[],True), | |
2007 | call_cbc_command(cbc_deadlock_freedom_check(State,True,0)), | |
2008 | (State == time_out -> Res = time_out | |
2009 | ; tcltk_add_cbc_state(State,cbc_deadlock), Res=deadlock). | |
2010 | ||
2011 | % if Filter=1 we remove guards which are inconsitent with goal | |
2012 | tcltk_constraint_find_deadlock_state_with_goal(Filter,Res) :- | |
2013 | check_we_are_in_b_mode(cbc_deadlock_freedom_check), | |
2014 | (b_get_machine_goal(Goal) -> true ; create_texpr(truth,pred,[],Goal)), | |
2015 | call_cbc_command(cbc_deadlock_freedom_check(State,Goal,Filter)), | |
2016 | (State == time_out -> Res = time_out | |
2017 | ; tcltk_add_cbc_state(State,cbc_deadlock), Res=deadlock). | |
2018 | ||
2019 | check_we_are_in_b_mode(_) :- b_or_z_mode,!. | |
2020 | check_we_are_in_b_mode(Cmd) :- add_error(check_we_are_in_b_mode,'This command can only be performed in B,Z or TLA+ mode: ',Cmd),fail. | |
2021 | ||
2022 | tcltk_constraint_find_deadlock_state_with_goal(true,_Filter,Res) :- | |
2023 | !,tcltk_constraint_find_deadlock_state(Res). | |
2024 | tcltk_constraint_find_deadlock_state_with_goal(GoalPred,Filter,Res) :- | |
2025 | bmachine:b_set_machine_goal(GoalPred), | |
2026 | tcltk_constraint_find_deadlock_state_with_goal(Filter,Res). | |
2027 | ||
2028 | :- use_module(cbcsrc(enabling_analysis),[check_if_feasible_operation/5]). | |
2029 | tcltk_check_if_feasible_operation(OpName,UseInvariant,TimeOutFactor,Result) :- % UseInvariant currently always 1 from Tcl | |
2030 | check_if_feasible_operation(OpName,UseInvariant,TimeOutFactor,Result,ResultState), | |
2031 | tcltk_add_cbc_state(ResultState,feasibility_check(OpName)). | |
2032 | ||
2033 | % add a state found by constraint-based verification | |
2034 | tcltk_add_cbc_state('$UNKNOWN_STATE',_) :- !. | |
2035 | tcltk_add_cbc_state(State,OpName) :- | |
2036 | seperate_state_into_const_and_vars(State,Constants,Variables), | |
2037 | !, | |
2038 | % store the constants separately: important for memory consumption when continuing animation/model checking | |
2039 | tcltk_add_new_transition_transid(root,'$setup_constants',CstID,concrete_constants(Constants),[],TransId1), | |
2040 | FullState = const_and_vars(CstID,Variables), | |
2041 | % TODO: if Variables are [] we could simply create an initialisation transition | |
2042 | (Variables=[] -> Op='$initialise_machine' ; Op = '$JUMP'(OpName)), | |
2043 | tcltk_add_new_transition_transid(CstID,Op,NewID,FullState,[],TransId2), | |
2044 | set_cbc_state(NewID,[TransId1,TransId2],[CstID,root]). | |
2045 | tcltk_add_cbc_state(State,OpName) :- | |
2046 | tcltk_add_new_transition_transid(root,'$JUMP'(OpName),NewID,State,[],TransId), | |
2047 | set_cbc_state(NewID,[TransId],[root]). | |
2048 | ||
2049 | set_cbc_state(NewID,TransIds,His) :- | |
2050 | set_current_state_id(NewID), | |
2051 | reset_op_trace_ids, | |
2052 | maplist(add_to_op_trace_ids,TransIds), | |
2053 | retractall(history(_)), | |
2054 | retractall(forward_history(_)), | |
2055 | assertz(history(His)). %, print(State),nl. | |
2056 | ||
2057 | is_constant_binding(SortedCsts,bind(ID,_)) :- | |
2058 | ord_member(ID,SortedCsts). | |
2059 | ||
2060 | :- use_module(bsyntaxtree, [get_texpr_ids/2]). | |
2061 | seperate_state_into_const_and_vars(concrete_constants(C),Constants,Variables) :- !, | |
2062 | Constants=C, Variables=[]. | |
2063 | seperate_state_into_const_and_vars(State,Constants,Variables) :- | |
2064 | b_get_machine_constants(TCs), get_texpr_ids(TCs,Cs), | |
2065 | sort(Cs,SortedCsts), | |
2066 | include(is_constant_binding(SortedCsts),State,Constants), | |
2067 | Constants \= [], | |
2068 | exclude(is_constant_binding(SortedCsts),State,Variables). | |
2069 | ||
2070 | ||
2071 | ||
2072 | tcltk_constraint_find_static_assertion_violation(TclResult) :- | |
2073 | cbc_constraint_find_static_assertion_violation(Result,[]), | |
2074 | functor(Result,TclResult,_). | |
2075 | cbc_constraint_find_static_assertion_violation(Result,Options) :- | |
2076 | call_cbc_command(cbc_static_assertions_check(State,Options)), | |
2077 | ( State = counterexample_found(CE) -> | |
2078 | Result = counterexample_found, | |
2079 | tcltk_add_cbc_state(CE,static_assertion_check) | |
2080 | ; | |
2081 | Result = State). | |
2082 | ||
2083 | /* --------------------------------------------------- */ | |
2084 | ||
2085 | ||
2086 | :- use_module(error_manager,[extract_all_line_col/5]). | |
2087 | :- use_module(probcspsrc(haskell_csp),[extract_span_from_event/4,extract_span_info/2]). | |
2088 | ||
2089 | get_source_text_positions(Event,Line,Col,EndLine,EndCol) :- | |
2090 | extract_span_from_action(Event,SPAN), | |
2091 | extract_all_line_col(SPAN,Line,Col,EndLine,EndCol). | |
2092 | ||
2093 | extract_span_from_action(Event,Span) :- csp_mode, | |
2094 | extract_span_from_event(Event,Span,_,_). | |
2095 | extract_span_from_action(Event,Pos) :- b_or_z_mode, | |
2096 | get_operation_name(Event,ID), | |
2097 | b_get_machine_operation(ID,_Res,_TParas,_Body,_OType,Pos). | |
2098 | ||
2099 | ||
2100 | /* -------------------------------------------------------------------- */ | |
2101 | ||
2102 | get_current_option_eventtrace(OptionNum,Trace) :- | |
2103 | % the option number starts with 0 (coming from a TK listbox) | |
2104 | current_options(ActionsAndIDs), | |
2105 | nth0(OptionNum,ActionsAndIDs,(Id,_,_,_)), | |
2106 | transition_info(Id,eventtrace(Trace)),!. | |
2107 | ||
2108 | ||
2109 | tcltk_has_eventtrace(OptionNum) :- | |
2110 | get_current_option_eventtrace(OptionNum,_Trace). | |
2111 | ||
2112 | tcltk_show_eventtrace(OptionNum,Desc) :- | |
2113 | get_current_option_eventtrace(OptionNum,Trace), | |
2114 | explain_event_trace(Trace,Desc,_Span). | |
2115 | ||
2116 | ||
2117 | ||
2118 | /* --------------------------------------------------- */ | |
2119 | ||
2120 | :- use_module(prob2_interface,[execute_model/5]). | |
2121 | ||
2122 | tcltk_execute_model(MaxNrSteps,ExecutedSteps,Result) :- | |
2123 | current_state_id(CurID), | |
2124 | execute_model(CurID,MaxNrSteps,_TransitionInfo,ExecutedSteps,Result). | |
2125 | ||
2126 | ||
2127 | tcltk_find_shortest_trace_to_current_node(list(StringList)) :- | |
2128 | find_trace_to_current_node(OpL), | |
2129 | translate_events(OpL,StringList). | |
2130 | ||
2131 | ||
2132 | tcltk_execute_trace_to_current_node :- | |
2133 | current_state_id(CurID), | |
2134 | tcltk_execute_trace_to_node(CurID). | |
2135 | ||
2136 | tcltk_execute_trace_to_node(ToID) :- /* can be useful for TestCase Generation */ | |
2137 | set_target_id(ToID), | |
2138 | tcltk_execute_trace_from_to_found_predicate(root,_,_). | |
2139 | ||
2140 | % execcute longest loop-free trace (no repeated state) | |
2141 | tcltk_execute_longest_trace_from(From) :- | |
2142 | set_longest_trace_target, | |
2143 | tcltk_execute_trace_from_to_found_predicate(From,_,_). | |
2144 | ||
2145 | % OpL is list of operation ids, IDList is list of state ids | |
2146 | tcltk_execute_trace_from_to_found_predicate(From,OpL,IDList) :- | |
2147 | debug_println(19,finding_trace_from_to(From)), flush_output(user), | |
2148 | find_shortest_path_from_to_found_predicate(From,OpL,IDList,ToID), !, | |
2149 | (current_state_id(From) -> true ; state_space_reset), | |
2150 | debug_println(9,executing_trace(From,OpL,ToID)), flush_output(user), | |
2151 | execute_id_trace_from_current(ToID,OpL,IDList). | |
2152 | ||
2153 | ||
2154 | find_trace_to_current_node(Trace) :- | |
2155 | current_state_id(CurID), | |
2156 | find_shortest_path_from_to(root,CurID,L,_), | |
2157 | extract_term_trace_from_transition_ids(L,Trace). | |
2158 | ||
2159 | :- use_module(state_space_dijkstra). | |
2160 | ||
2161 | find_shortest_trace_to_node(FromID,ToID,OpIDListe,StateIDList) :- | |
2162 | find_shortest_path_from_to(FromID,ToID,OpIDListe,StateIDList). | |
2163 | ||
2164 | ||
2165 | /* --------------------------------------------------- */ | |
2166 | ||
2167 | ||
2168 | ||
2169 | tcltk_goto_a_non_deterministic_node(Msg) :- /* same transition leads to different resulting state */ | |
2170 | visited_expression_id(ID), | |
2171 | external_functions:non_det_transition(ID,_OpName,TID1,TID2), | |
2172 | tcltk_execute_trace_to_node(ID), | |
2173 | transition(ID,Op1,TID1,ID1), | |
2174 | transition(ID,_Op2,TID2,ID2), | |
2175 | translate_event_with_src_and_target_id(Op1,ID,ID1,OpStr), | |
2176 | ajoin(['Operation leads to at least two different states (ids ', ID1,', ',ID2, | |
2177 | ') for same arguments: ', OpStr], Msg). | |
2178 | ||
2179 | tcltk_goto_a_non_deterministic_output_node(Msg) :- /* same transition leads to different outputs */ | |
2180 | visited_expression_id(ID), | |
2181 | external_functions:non_det_output_transition(ID,_OpName,TID1,TID2), | |
2182 | tcltk_execute_trace_to_node(ID), | |
2183 | transition(ID,Op1,TID1,ID1), | |
2184 | transition(ID,Op2,TID2,ID2), | |
2185 | translate_event_with_src_and_target_id(Op1,ID,ID1,OpStr1), | |
2186 | translate_event_with_src_and_target_id(Op2,ID,ID2,OpStr2), | |
2187 | ajoin(['Operation has at least two different results for same arguments: ', OpStr1, | |
2188 | '\n and ',OpStr2], Msg). | |
2189 | ||
2190 | dif_ID(ID1,ID2) :- (number(ID1),number(ID2) -> ID2>ID1 ; ID1 \= ID2). | |
2191 | ||
2192 | tcltk_goto_a_branching_node :- /* two different events/operations enabled */ | |
2193 | transition(ID,X,ID2), get_operation_name(X,NX), | |
2194 | transition(ID,Y,ID3), dif_ID(ID2,ID3), get_operation_name(Y,NY), NX\=NY, | |
2195 | print_message(found_branching_op(ID,NX,NY)), | |
2196 | %tcltk_goto_state(find_branching_node,ID), | |
2197 | tcltk_execute_trace_to_node(ID). | |
2198 | ||
2199 | tcltk_goto_a_non_resetable_node :- | |
2200 | find_resetable_nodes, | |
2201 | visited_expression_id(ID), | |
2202 | \+(resetable_node(ID)), | |
2203 | \+(not_all_transitions_added(ID)), !, | |
2204 | %tcltk_goto_state(find_non_resetable_node,ID). | |
2205 | tcltk_execute_trace_to_node(ID). | |
2206 | ||
2207 | :- use_module(bmachine,[b_get_machine_operation_for_animation/4]). | |
2208 | operation_name_covered(OpName) :- operation_name_covered(OpName,_). | |
2209 | operation_name_covered(OpName,Template) :- | |
2210 | b_get_machine_operation_for_animation(OpName,Results,Par,_), | |
2211 | \+ operation_not_yet_covered(OpName), | |
2212 | length(Par,Arity), | |
2213 | functor(Op,OpName,Arity), | |
2214 | (Results\=[] -> Template = '-->'(Op,_) ; Template = Op). | |
2215 | ||
2216 | tcltk_goto_event_list_property_violation(Property,EventSelection,list(OperationList)) :- | |
2217 | % first translate Tk EventSelection Number list into Events | |
2218 | sap:get_selected_events(EventSelection,_AllEvents,OperationList), | |
2219 | include(operation_name_covered,OperationList,_,EnabledOpList), % only keep operations that can be enabled anyway | |
2220 | maplist(operation_name_covered,EnabledOpList,TemplateList), % replace by template | |
2221 | print(find_property_violation_for_template(Property,TemplateList)),nl, | |
2222 | visited_expression(ID,S), ID \= root, S\= concrete_constants(_), | |
2223 | \+(not_all_transitions_added(ID)), | |
2224 | \+( check_event_list_property(Property,ID,TemplateList) ), | |
2225 | print(found_event_list_property_violation(Property,ID)),nl, | |
2226 | tcltk_execute_trace_to_node(ID). | |
2227 | ||
2228 | check_event_list_property(relative_deadlock_freedom,ID,TemplateList) :- | |
2229 | relative_deadlock_freedom(ID,TemplateList). | |
2230 | check_event_list_property(valid_controller,ID,TemplateList) :- | |
2231 | is_valid_controller_state(ID,TemplateList). | |
2232 | check_event_list_property(det_controller,ID,TemplateList) :- | |
2233 | is_det_controller_state(ID,TemplateList). | |
2234 | ||
2235 | relative_deadlock_freedom(ID,TemplateList) :- member(Template,TemplateList), | |
2236 | transition(ID,Template,_). | |
2237 | is_valid_controller_state(ID,TemplateList) :- % check that exactly one event from TemplateList is enabled | |
2238 | append(_,[LiveEvent|RestTemplate],TemplateList), | |
2239 | transition(ID,LiveEvent,_),!, % we found one enabled event | |
2240 | \+ relative_deadlock_freedom(ID,RestTemplate). % all other events from list must be disabled | |
2241 | is_det_controller_state(ID,TemplateList) :- % check that *at most* one event from TemplateList is enabled | |
2242 | append(_,[LiveEvent|RestTemplate],TemplateList), | |
2243 | transition(ID,LiveEvent,_),!, % we found one enabled event from list | |
2244 | \+ relative_deadlock_freedom(ID,RestTemplate). % all other events from list must be disabled | |
2245 | is_det_controller_state(_,_) :- true. % no event enabled is also ok | |
2246 | ||
2247 | ||
2248 | tcltk_goto_state_enabling_operation(TclOpName,FromCurrent) :- | |
2249 | adapt_tcl_operation_name(TclOpName,OpName), | |
2250 | operation_name_covered(OpName,Template), % does cater for Event-B any vars | |
2251 | set_found_predicate(ID, transition(ID,Template,_)), | |
2252 | print_message('Computing trace to node'), | |
2253 | (FromCurrent=1 | |
2254 | -> current_state_id(FromID) % inefficient: better to do one dijkstra from FromID ! | |
2255 | ; FromID = root | |
2256 | ), | |
2257 | tcltk_execute_trace_from_to_found_predicate(FromID,_,_). | |
2258 | % tcltk_goto_state(find_enabled(OpName),ID). | |
2259 | ||
2260 | ||
2261 | :- use_module(tools_lists,[count_occurences/2]). | |
2262 | % quickly compute which operations have already been covered and which ones not | |
2263 | tcltk_operations_covered_info(list([list(['Operation','Covered'])|OpCov]),list(Occurences),Precise) :- | |
2264 | findall(list([OpName,Cov]), | |
2265 | (specfile:get_possible_event(OpName), | |
2266 | (operation_name_not_yet_covered(OpName) | |
2267 | -> not_yet_covered_info(Precise,OpName,Cov) | |
2268 | ; Cov = 'yes')),OpCov), | |
2269 | (maplist(get_cov,OpCov,Cov2), | |
2270 | count_occurences(Cov2,Occ2), | |
2271 | maplist(construct_list,Occ2,Occurences) -> true ; Occurences=[]). | |
2272 | ||
2273 | get_cov(list([_,Cov|_]),Cov). | |
2274 | construct_list(Kind-Nr,list([Kind,Nr])). | |
2275 | ||
2276 | :- use_module(cbcsrc(enabling_analysis),[feasible_operation/2]). | |
2277 | not_yet_covered_info(precise,OpName,Cov) :- | |
2278 | feasible_operation(OpName,Res),!, Res=Cov. | |
2279 | not_yet_covered_info(precise,_,R) :- !, R='unknown'. | |
2280 | not_yet_covered_info(_,_,'uncovered'). | |
2281 | ||
2282 | :- dynamic resetable_node/1. | |
2283 | ||
2284 | resetable_node(a). | |
2285 | ||
2286 | :- public find_resetable_nodes/0. | |
2287 | :- dynamic find_flag/2. | |
2288 | find_resetable_nodes :- | |
2289 | retractall(find_flag(_,_)), | |
2290 | retractall(resetable_node(_)), | |
2291 | any_transition(root,_,ID), /* add nodes reachable from root */ | |
2292 | assertz(find_flag(ID,0)), | |
2293 | fail. | |
2294 | find_resetable_nodes :- | |
2295 | visited_expression(CID,concrete_constants(_C)), | |
2296 | any_transition(CID,_,ID), /* add nodes reachable after a setup_constants */ | |
2297 | assertz(find_flag(ID,0)), | |
2298 | fail. | |
2299 | find_resetable_nodes :- find_resetable_nodes_loop, | |
2300 | print_message('Not resetable:'), | |
2301 | visited_expression_id(ID), | |
2302 | \+(resetable_node(ID)), | |
2303 | print_message(ID), | |
2304 | fail. | |
2305 | find_resetable_nodes :- | |
2306 | print_message('Done'). | |
2307 | ||
2308 | ||
2309 | find_resetable_nodes_loop :- | |
2310 | retract(find_flag(ID,N)), !, find_resetable_nodes_loop(ID,N). | |
2311 | find_resetable_nodes_loop. | |
2312 | ||
2313 | find_resetable_nodes_loop(ID,N) :- N1 is N+1, | |
2314 | assertz(resetable_node(ID)), | |
2315 | any_transition(NewID,_,ID), | |
2316 | \+(resetable_node(NewID)), | |
2317 | \+(find_flag(NewID,_)), | |
2318 | assertz(find_flag(NewID,N1)), | |
2319 | fail. | |
2320 | find_resetable_nodes_loop(_,_) :- find_resetable_nodes_loop. | |
2321 | ||
2322 | ||
2323 | :- use_module(specfile, [get_current_state_for_b_formula/2]). | |
2324 | ||
2325 | tcltk_show_expression_as_dot(VorE,File) :- | |
2326 | parse_machine_expression(VorE,TypedExpr), | |
2327 | tcltk_show_typed_expression_as_dot_graph(TypedExpr,File). | |
2328 | tcltk_show_expression_as_dot(_VorE,File) :- | |
2329 | add_error(tcltk_show_expression_as_dot,'Computing Value Dot Representation Failed for: ',File), | |
2330 | fail. | |
2331 | ||
2332 | :- use_module(dotsrc(state_as_dot_graph),[print_cstate_graph/2]). | |
2333 | :- use_module(dotsrc(state_custom_dot_graph),[tcltk_generate_state_custom_dot_graph_for_expr/2, is_valid_custom_dot_graph_record/1]). | |
2334 | ||
2335 | tcltk_show_typed_expression_as_dot_graph(TypedExpr,File) :- | |
2336 | is_valid_custom_dot_graph_record(TypedExpr), | |
2337 | !, % use the more powerful custom_dot_graph mechanism | |
2338 | tcltk_generate_state_custom_dot_graph_for_expr(TypedExpr,File). | |
2339 | tcltk_show_typed_expression_as_dot_graph(TypedExpr,File) :- | |
2340 | get_current_state_for_b_formula(TypedExpr,BState), | |
2341 | extend_state_with_probids_and_lets(BState,BState1), | |
2342 | decompose_expression_for_dot(TypedExpr,ExprList,[]), % decompose pairs so that user can provide multiple values/graphs | |
2343 | compute_for_dot(ExprList,BState1,GraphState), % compute value for each sub-component | |
2344 | format('Writing value to DOT file: ~w~n',[File]), | |
2345 | print_cstate_graph(GraphState,File), | |
2346 | format('Finished writing value to DOT file: ~w~n',[File]). | |
2347 | ||
2348 | ||
2349 | :- use_module(eval_let_store,[extend_typing_scope_for_stored_lets/2]). | |
2350 | :- use_module(bmachine, [b_parse_machine_expression_from_codes_with_prob_ids/2, type_with_errors/4]). | |
2351 | parse_machine_expression(RawAST,TypedExpr) :- compound(RawAST),!, % allow to pass raw AST as well | |
2352 | extend_typing_scope_for_stored_lets([prob_ids(visible),variables,external_library(safe_available_libraries)],Scope), | |
2353 | type_with_errors(RawAST,Scope,Type,TypedExpr), | |
2354 | % TODO: get_stored_let_typing_scope and add values to state returned by get_current_state_for_b_formula | |
2355 | ((Type=pred ; Type=subst) | |
2356 | -> add_error(parse_machine_expression,'Expected expression formula but obtained: ',Type),fail | |
2357 | ; true ). | |
2358 | parse_machine_expression(VorE,TypedExpr) :- | |
2359 | atom_codes(VorE,VorECodes), | |
2360 | b_parse_machine_expression_from_codes_with_prob_ids(VorECodes,TypedExpr). | |
2361 | ||
2362 | compute_for_dot([],_,[]). | |
2363 | compute_for_dot([String,TypedExpr|Tail],BState,[bind(Str,Value)|TS]) :- | |
2364 | % allow user to specify labels for graphs ("lbl1", e1, "lbl2", e2, ...) | |
2365 | get_texpr_expr(String,string(Str)),!, | |
2366 | translate:translate_bexpression_with_limit(TypedExpr,100,EStr), | |
2367 | format('Computing value for expression: ~w~n',[EStr]), | |
2368 | b_compute_expression_with_prob_ids(TypedExpr,BState,Value), | |
2369 | compute_for_dot(Tail,BState,TS). | |
2370 | compute_for_dot([TypedExpr|Tail],BState,[bind(Str,Value)|TS]) :- | |
2371 | translate:translate_bexpression_with_limit(TypedExpr,100,Str), | |
2372 | format('Computing value for expression: ~w~n',[Str]), | |
2373 | b_compute_expression_with_prob_ids(TypedExpr,BState,Value), | |
2374 | compute_for_dot(Tail,BState,TS). | |
2375 | ||
2376 | :- use_module(bsyntaxtree, [replace_ids_by_exprs/4]). | |
2377 | ||
2378 | % decompose_expression_for_dot ensures that | |
2379 | % ("label1",graph1,"lbl2",graph2) or rec(label:graph) is decomposed and we can associate labels with multiple graphs | |
2380 | decompose_expression_for_dot(b(couple(A,B),_,_)) --> !, | |
2381 | decompose_expression_for_dot(A), decompose_expression_for_dot(B). | |
2382 | decompose_expression_for_dot(b(rec(Fields),_,_)) --> !,decompose_fields_for_dot(Fields). | |
2383 | decompose_expression_for_dot(b(let_expression(Ids,Exprs,Body),couple(_,_),_)) --> !, | |
2384 | % this can happen when OPTIMIZE_AST is set to FALSE and the Jupyter kernel adds LETs; lift string out of let | |
2385 | % :dot expr_as_graph ("h",h,"hh",(h;h)) | |
2386 | {replace_ids_by_exprs(Body,Ids,Exprs,Body2)}, | |
2387 | decompose_expression_for_dot(Body2). | |
2388 | decompose_expression_for_dot(X) --> [X]. | |
2389 | ||
2390 | ||
2391 | decompose_fields_for_dot([]) --> []. | |
2392 | decompose_fields_for_dot([field(Name,Val)|T]) --> [b(string(Name),string,[]),Val], decompose_fields_for_dot(T). | |
2393 | ||
2394 | /* ------------------------------------------------------------------ */ | |
2395 | ||
2396 | % aka Execute by Predicate | |
2397 | tcltk_add_user_executed_operation(TclOpName,CustomPredicate) :- | |
2398 | tcltk_add_user_executed_operation(TclOpName,[],CustomPredicate). | |
2399 | ||
2400 | tcltk_add_user_executed_operation(TclOpName,ParameterBindList,RestPreCond) :- | |
2401 | adapt_tcl_operation_name(TclOpName,OpName), | |
2402 | parse_tclk_parameter_values_and_pred(OpName,ParameterBindList,RestPreCond,CustomPredicate), | |
2403 | current_state_id(CurID), | |
2404 | b_trans_one_solution_with_pred(OpName,CustomPredicate,CurID,_,_,_). | |
2405 | ||
2406 | parse_tclk_parameter_values_and_pred(OpName,ParameterBindList,RestPreCond,CustomPredicate) :- | |
2407 | GenParseErrors=true, ExtraScope=[], | |
2408 | b_parse_machine_operation_pre_post_predicate(RestPreCond,ExtraScope,RestTypedPred,OpName,GenParseErrors), | |
2409 | get_animation_operation_parameters(OpName,Parameters), | |
2410 | % print(tcltk_add_user_executed_operation(OpName,ParameterBindList,Parameters)),nl, | |
2411 | parse_parameter_values(ParameterBindList,Parameters,List), | |
2412 | conjunct_predicates([RestTypedPred|List],CustomPredicate). | |
2413 | ||
2414 | :- use_module(bmachine,[b_get_machine_operation_for_animation/7]). | |
2415 | get_animation_operation_parameters('$setup_constants',Parameters) :- | |
2416 | b_get_machine_operation_typed_parameters('$setup_constants',Parameters). | |
2417 | get_animation_operation_parameters(OpName,FullParameters) :- | |
2418 | TopLevel=true, % otherwise ANY parameters not added when show_eventb_any_arguments is true | |
2419 | b_get_machine_operation_for_animation(OpName,_Results,Parameters,_Body,_OType,TopLevel,_OpPos), | |
2420 | b_get_operation_non_det_modifies(OpName,NonDetVars), % also allow setting non-deterministically assigned vars | |
2421 | b_get_machine_variables(Vars), | |
2422 | include(non_det_modified(NonDetVars),Vars,ParaVars), | |
2423 | append(ParaVars,Parameters,FullParameters). | |
2424 | ||
2425 | non_det_modified(NonDetVars,TID) :- get_texpr_id(TID,ID), ord_member(ID,NonDetVars). | |
2426 | ||
2427 | % parse a list of variable names and expressions and turn into equal predicates | |
2428 | % Motivation: this can also deal with Z-style identifiers like in?, ... which cannot be parsed | |
2429 | :- use_module(btypechecker, [unify_types_strict/2, coerce_type/4]). | |
2430 | parse_parameter_values([],_,Res) :- !,Res=[]. | |
2431 | parse_parameter_values(['='(ParameterID,ExpressionStr)|T],Parameters,[Pred|PT]) :- | |
2432 | atom_codes(ExpressionStr,Codes), | |
2433 | format('Parsing for parameter ~w : ~w~n',[ParameterID,ExpressionStr]), | |
2434 | !, | |
2435 | b_parse_machine_expression_from_codes_with_prob_ids(Codes,TypedExpr), | |
2436 | get_texpr_type(TypedExpr,Type), | |
2437 | TID = b(identifier(ParameterID),ExpectedType,[]), | |
2438 | (member(b(identifier(ParameterID),ExpectedType,_),Parameters) | |
2439 | -> (unify_types_strict(Type,ExpectedType) | |
2440 | -> safe_create_texpr(equal(TID,TypedExpr),pred,Pred), | |
2441 | parse_parameter_values(T,Parameters,PT) | |
2442 | ; coerce_type(Type,ExpectedType,TypedExpr,NewTypedExpr) | |
2443 | -> safe_create_texpr(equal(TID,NewTypedExpr),pred,Pred), | |
2444 | add_message(parse_parameter_values,'Coercion of provided parameter value: ',NewTypedExpr), | |
2445 | parse_parameter_values(T,Parameters,PT) | |
2446 | ; | |
2447 | pretty_type(Type,String1), pretty_type(ExpectedType,String2), | |
2448 | ajoin(['Value for parameter ',ParameterID,' has unexpected type ',String1,', expected: '],Msg), | |
2449 | add_error(parse_parameter_values,Msg,String2), | |
2450 | fail | |
2451 | ) | |
2452 | ; add_error(parse_parameter_values,'Unknown operation parameter: ',ParameterID), | |
2453 | fail). | |
2454 | parse_parameter_values(PB,P,_) :- add_internal_error('Illegal parameter binding: ',parse_parameter_values(PB,P,_)),fail. | |
2455 | ||
2456 | ||
2457 | % just as above but with typed predicate | |
2458 | tcltk_add_user_executed_operation_typed(OpName,Operation,TypedCustomPredicate,NewID) :- | |
2459 | current_state_id(CurID), | |
2460 | tcltk_add_user_executed_operation_typed(OpName,CurID,Operation,TypedCustomPredicate,_,NewID). | |
2461 | tcltk_add_user_executed_operation_typed(OpName,CurID,Operation,TypedCustomPredicate,TransID,NewID) :- | |
2462 | b_trans_one_solution_with_pred(OpName,TypedCustomPredicate,CurID,Operation,TransID,NewID). | |
2463 | ||
2464 | % allows the user to arbitrarily change variable values | |
2465 | % Attention: this can of course violate the invariant | |
2466 | tcltk_add_user_modify_variable_transition(VarID,ValueStr) :- | |
2467 | current_state_id(CurID), | |
2468 | tcltk_add_user_modify_variable_transition(CurID,VarID,ValueStr,_). | |
2469 | tcltk_add_user_modify_variable_transition(CurID,VarID,ValueStr,NewID) :- | |
2470 | get_id_type(VarID,Kind,Type),!, | |
2471 | format('Trying to update ~w ~w to new value: ~w~n',[Kind,VarID,ValueStr]), | |
2472 | eval_string_in_cur_state(ValueStr,ValTyped,Value), | |
2473 | get_texpr_type(ValTyped,ValType), | |
2474 | check_types(Kind,VarID,Type,ValType), | |
2475 | visited_expression(CurID,CurState), | |
2476 | update_variable(CurState,VarID,Value,NewState), | |
2477 | Residue=[], TransInfo=[], | |
2478 | Operation = '$modify'(string(VarID),string(ValueStr)), | |
2479 | add_trans_id_with_infos(CurID,[],Operation,NewState,Residue,NewID,TransInfo,_), | |
2480 | tcltk_goto_state(Operation,NewID). | |
2481 | ||
2482 | check_types(_,_,Type,ValType) :- | |
2483 | unify_types_strict(Type,ValType),!. | |
2484 | check_types(Kind,VarID,Type,ValType) :- | |
2485 | pretty_type(ValType,VTS), pretty_type(Type,TS), | |
2486 | ajoin(['Unexpected type of value (', VTS, ' instead of ',TS,') for ',Kind,' :'],Msg), | |
2487 | add_error(check_types,Msg,VarID), | |
2488 | fail. | |
2489 | ||
2490 | :- use_module(bmachine,[b_is_variable/2, b_is_constant/2]). | |
2491 | get_id_type(ID,variable,Type) :- b_is_variable(ID,Type). | |
2492 | get_id_type(ID,constant,Type) :- b_is_constant(ID,Type). | |
2493 | % update variable value or constant value in concrete_constants state | |
2494 | update_variable([],VarID,_,[]) :- add_error(update_variable,'Identifier does not exist in state:',VarID),fail. | |
2495 | update_variable([bind(VarID,_)|T],VarID,NewVal,Res) :- !, Res = [bind(VarID,NewVal)|T]. | |
2496 | update_variable([H|T],VarID,NewVal,[H|UT]) :- !, update_variable(T,VarID,NewVal,UT). | |
2497 | update_variable(const_and_vars(ConstID,Old),VarID,NewVal,const_and_vars(ConstID,New)) :- !, | |
2498 | update_variable(Old,VarID,NewVal,New). | |
2499 | update_variable(concrete_constants(Old),VarID,NewVal,concrete_constants(New)) :- !, | |
2500 | update_variable(Old,VarID,NewVal,New). % here we allow updating constants | |
2501 | ||
2502 | % execute operation by predicate finding just one solution | |
2503 | b_trans_one_solution_with_pred(OpName,TypedCustomPredicate,CurID,Operation,TransId,NewID) :- | |
2504 | catch( | |
2505 | ( | |
2506 | call_cleanup(b_trans_one_solution_with_pred2(OpName,TypedCustomPredicate,CurID,Operation,TransId,NewID), | |
2507 | bmachine:reset_temp_predicate) | |
2508 | ), | |
2509 | enumeration_warning(Cause,_,_,_,_), | |
2510 | ( | |
2511 | add_warning(b_trans_one_solution_with_pred, | |
2512 | 'Enumeration warning prevented finding solution for operation: ',Cause), | |
2513 | fail | |
2514 | ) | |
2515 | ). | |
2516 | ||
2517 | % find one solution from current state using OpName and respecting temp_predicate | |
2518 | b_trans_one_solution_with_pred2(OpName,TypedCustomPredicate,CurID,Operation,TransId,NewID) :- | |
2519 | b_top_level_operation(OpName), | |
2520 | !, | |
2521 | visited_expression(CurID,InState), | |
2522 | set_context_state(CurID,b_trans_one_solution_with_pred), | |
2523 | debug_format(19,'Executing ~w in state ~w by predicate~n',[OpName,CurID]), | |
2524 | call_cleanup(execute_operation_by_predicate_in_state(InState,OpName,TypedCustomPredicate,Operation,NewState), | |
2525 | clear_context_state), | |
2526 | !, | |
2527 | debug_format(19,'Executed by predicate: ~w~n',[OpName]), | |
2528 | %debug_format(19,'Executed ~w: ~w~nOut=~w~n',[OpName,Operation,NewState]), | |
2529 | add_trans_id(CurID,Operation,NewState,NewID,TransId), | |
2530 | tcltk_goto_state(Operation,NewID). | |
2531 | b_trans_one_solution_with_pred2(OpName,TypedCustomPredicate,CurID,Operation,TransId,NewID) :- | |
2532 | assert_temp_predicate_with_prob_def_set_elements(TypedCustomPredicate), | |
2533 | visited_expression(CurID,InState), | |
2534 | set_context_state(CurID,b_trans_one_solution_with_pred), | |
2535 | temporary_set_preference(maxNrOfEnablingsPerOperation,1,Max), | |
2536 | temporary_set_preference(maxNrOfInitialisations,1,MaxI), | |
2537 | start_ms_timer(Timer), | |
2538 | call_cleanup((specfile:b_trans(InState,OpName,Operation,NewState,TransInfo) -> true), | |
2539 | (stop_ms_timer(Timer), | |
2540 | reset_temporary_preference(maxNrOfEnablingsPerOperation,Max), | |
2541 | reset_temporary_preference(maxNrOfInitialisations,MaxI), | |
2542 | clear_context_state) | |
2543 | ), | |
2544 | debug_println(9,found_solution(NewState)),nl, | |
2545 | % now add a transition to the state space: | |
2546 | Residue=[], | |
2547 | add_trans_id_with_infos(CurID,[],Operation,NewState,Residue,NewID,TransInfo,TransId), | |
2548 | tcltk_goto_state(Operation,NewID). | |
2549 | ||
2550 | :- use_module(b_global_sets,[inline_prob_deferred_set_elements_into_bexpr/2]). | |
2551 | assert_temp_predicate_with_prob_def_set_elements(CustomPredicate) :- | |
2552 | inline_prob_deferred_set_elements_into_bexpr(CustomPredicate,CompiledPred), | |
2553 | bmachine:assert_temp_typed_predicate(CompiledPred). | |
2554 | ||
2555 | ||
2556 | % allow user to execute any statement | |
2557 | % TO DO: catch operation_calls and route them differently | |
2558 | % translate operation_call arguments into predicate and call tcltk_add_user_executed_operation_typed(OpName,Operation,TypedCustomPredicate,NewID) | |
2559 | tcltk_add_user_executed_statement(Statement,Updates,NewID) :- | |
2560 | current_expression(CurID,InState), | |
2561 | state_corresponds_to_initialised_b_machine(InState,BState), | |
2562 | set_context_state(CurID,tcltk_add_user_executed_statement), | |
2563 | %start_ms_timer(Timer), | |
2564 | call_cleanup((tcltk_interface:b_full_execute_top_level_statement_with_probids(Statement,BState,Updates,NewState)->true), | |
2565 | clear_context_state), | |
2566 | % TO DO: do we need to catch enumeration warnings ? | |
2567 | %stop_ms_timer(Timer), | |
2568 | Residue=[], TransInfo = [], | |
2569 | debug_println(4,executed(Updates,NewState)), | |
2570 | compute_all_transitions_if_necessary(CurID), % first ensure we have already computed outgoing transitions as usual, so that we can detect if a new state is added | |
2571 | (get_id_of_node_and_add_if_required(NewState,NewID,Exists,CurID,'$unknown_operation',[]), | |
2572 | Exists=exists, | |
2573 | transition(CurID,Action,TransId,NewID) | |
2574 | -> debug_println(4,transition_exists_already(Action,TransId)) | |
2575 | ; translate:translate_substitution(Statement,OpLabel), | |
2576 | add_trans_id_with_infos(CurID,[],'$USER'(OpLabel),NewState,Residue,NewID,TransInfo,_), | |
2577 | format('Added new user-initiated transition to ~w via: ~w~n',[NewID,OpLabel]) | |
2578 | ), | |
2579 | tcltk_goto_state(OpLabel,NewID). | |
2580 | ||
2581 | :- use_module(eval_let_store,[extend_state_with_probids_and_lets/2]). | |
2582 | :- use_module(kernel_waitflags,[init_wait_flags_with_call_stack/2, ground_wait_flags/1]). | |
2583 | %% allows one to execute any statement (not just operations). | |
2584 | b_full_execute_top_level_statement_with_probids(Body,InState,Updates,NewState) :- | |
2585 | extend_state_with_probids_and_lets(InState,InState1), | |
2586 | init_wait_flags_with_call_stack(WF,[prob_command_context(user_executed_statement,unknown)]), | |
2587 | b_execute_top_level_statement(Body,[],InState1,Updates,WF,_Path1,output_not_required), | |
2588 | ground_wait_flags(WF), | |
2589 | store:store_updates_and_normalise(Updates,InState,NewState). | |
2590 | ||
2591 | % -------------------------------------------------------------------------------------- | |
2592 | ||
2593 | ||
2594 | :- use_module(bmachine). | |
2595 | :- use_module(specfile). | |
2596 | :- use_module(tools). | |
2597 | :- use_module(library(lists)). | |
2598 | :- use_module(store). | |
2599 | :- use_module(self_check). | |
2600 | :- use_module(b_global_sets). | |
2601 | :- use_module(translate). | |
2602 | ||
2603 | ||
2604 | /* --------------------------------------------------------- */ | |
2605 | /* --------------------------------------------------------- */ | |
2606 | ||
2607 | ||
2608 | /* ----------------------------------*/ | |
2609 | /* b_analyse_conjunction */ | |
2610 | /* ----------------------------------*/ | |
2611 | ||
2612 | ||
2613 | /* provide list of possible options for analysis */ | |
2614 | tcltk_analyse_option(invariant,'INVARIANT') :- b_or_z_mode. | |
2615 | tcltk_analyse_option(properties,S) :- b_or_z_mode, | |
2616 | get_specification_description(properties,S). | |
2617 | tcltk_analyse_option(assertions,S) :- (b_or_z_mode ; csp_mode), | |
2618 | get_specification_description(assertions,S). | |
2619 | tcltk_analyse_option(deadlock,'DEADLOCKED') :- b_or_z_mode. | |
2620 | ||
2621 | ||
2622 | :- use_module(predicate_evaluator). | |
2623 | /* perform the various analysis */ | |
2624 | tcltk_analyse(Mode,Type,ListWithResults,Total,False,Unknown) :- | |
2625 | (Mode == unicode | |
2626 | -> set_unicode_mode, | |
2627 | call_cleanup(tcltk_analyse2(Type,ListWithResults,Summary),unset_unicode_mode) | |
2628 | ; tcltk_analyse2(Type,ListWithResults,Summary)), | |
2629 | (member(total/Total,Summary) -> true ; Total=0), | |
2630 | (member(false/False,Summary) -> true ; False=0), | |
2631 | (member(unknown/Unknown,Summary) -> true ; Unknown=0). | |
2632 | tcltk_analyse2(invariant,L,Summary) :- !,tcltk_analyse_invariant(L,Summary). | |
2633 | tcltk_analyse2(properties,L,Summary) :- !,tcltk_analyse_properties(L,Summary). | |
2634 | tcltk_analyse2(assertions,L,Summary) :- !,tcltk_analyse_assertions(L,Summary). | |
2635 | tcltk_analyse2(deadlock,L,Summary) :- !,tcltk_analyse_deadlock(L,Summary). | |
2636 | tcltk_analyse2(X,L,Summary) :- add_error(tcltk_analyse, 'Unknown type of formula: ', X), | |
2637 | L=[], Summary=[]. | |
2638 | ||
2639 | ||
2640 | /* --------------------------------- */ | |
2641 | ||
2642 | tcltk_show_current_state(TransBState) :- | |
2643 | current_b_expression(B), | |
2644 | translate_bstate(B,TransBState). | |
2645 | ||
2646 | ||
2647 | tcltk_show_typing_of_variables_and_constants(list(List)) :- | |
2648 | b_get_machine_variables(TypedVarList), | |
2649 | convert_typed_varlist(TypedVarList,VarList,Types), | |
2650 | generate_typing_list(VarList,Types,VList,VarCard), | |
2651 | length(VarList,NrVars), | |
2652 | format_to_codes(' VARIABLES [nr=~w,card=~w]',[NrVars,VarCard],Codes1),atom_codes(VARAtom,Codes1), | |
2653 | ||
2654 | b_get_machine_all_constants(TypedCstList), | |
2655 | convert_typed_varlist(TypedCstList,CVarList,CTypes), | |
2656 | find_non_det_constants, | |
2657 | generate_typing_list(CVarList,CTypes,CList,CCard), | |
2658 | ( CList = [] | |
2659 | -> List = [VARAtom|VList] | |
2660 | ; length(CVarList,NrCsts), | |
2661 | format_to_codes(' CONSTANTS [nr=~w,card=~w]',[NrCsts,CCard],Codes2),atom_codes(CstAtom,Codes2), | |
2662 | append([CstAtom|CList],[VARAtom|VList],List) | |
2663 | ),!. | |
2664 | tcltk_show_typing_of_variables_and_constants(list(['TYPE ERROR in MACHINE'])). | |
2665 | ||
2666 | ||
2667 | %%% Used to get the constant values for the currently loaded B machine in form of a B predicate. | |
2668 | %%% In case that the constants in the model has not been set up then all possible constants' | |
2669 | %%% evaluations will be provided in form of a predicate in Disjunctive Normal Form. | |
2670 | %%% Predicate used for setting up constants when model checking with TLC. (TLA) | |
2671 | tcltk_get_constants_predicate(P,N) :- | |
2672 | current_state_id(CurID), | |
2673 | (CurID==root | |
2674 | -> get_constants_as_disjoint_predicate(N,P) | |
2675 | ; prob2_interface:get_state(CurID,EState), | |
2676 | filter_constants_bindings(EState,X), % this will only get current constants values | |
2677 | translate_bstate_to_pred(X,P) | |
2678 | ). | |
2679 | ||
2680 | get_constants_as_disjoint_predicate(N,Res) :- | |
2681 | findall(R,get_set_up_constants_solution(R),L), | |
2682 | get_a_part_of_the_found_solutions(L,N,Part), | |
2683 | add_or_between_constant_predicates(Part,Res). | |
2684 | ||
2685 | get_a_part_of_the_found_solutions(L,N,Part) :- | |
2686 | length(L,Length), | |
2687 | (N>=Length | |
2688 | -> Part=L | |
2689 | ; sublist(L,Part,_B,N,_A)). | |
2690 | ||
2691 | translate_bstate_to_pred(State,P) :- | |
2692 | temporary_set_preference(expand_avl_upto,-1,CHNG2), | |
2693 | translate: translate_bstate(State,P), | |
2694 | reset_temporary_preference(expand_avl_upto,CHNG2). | |
2695 | ||
2696 | get_set_up_constants_solution(R) :- | |
2697 | compute_transitions_if_necessary(root), | |
2698 | %\+ max_reached_or_timeout_for_node(root),!, % then lookup constants rather than computing them | |
2699 | transition(root,_,Dst), | |
2700 | visited_expression(Dst,concrete_constants(X)), | |
2701 | translate_bstate_to_pred(X,R). | |
2702 | %get_set_up_constants_solution(R) :- | |
2703 | % b_interpreter: b_set_up_concrete_constants(X,_), | |
2704 | % translate:translate_bstate(X,R). | |
2705 | ||
2706 | add_or_between_constant_predicates([],'1=1'). | |
2707 | add_or_between_constant_predicates(L,Res) :- | |
2708 | tools: ajoin_with_sep(L,' or ',Res). | |
2709 | ||
2710 | filter_constants_bindings(EState,R) :- | |
2711 | b_get_machine_all_constants(C), | |
2712 | convert_typed_varlist(C,CVarList,_CTypes), | |
2713 | filter_constants_bindings1(EState,CVarList,R). | |
2714 | ||
2715 | filter_constants_bindings1([],_,[]). | |
2716 | filter_constants_bindings1([bind(Ident,Val)|T],CVarList,Res) :- | |
2717 | (member(Ident,CVarList) -> Res = [bind(Ident,Val)|Rest]; Res = Rest), | |
2718 | filter_constants_bindings1(T,CVarList,Rest). | |
2719 | ||
2720 | convert_typed_varlist([],[],[]). | |
2721 | convert_typed_varlist([b(identifier(ID),Type,_)|T],[ID|IT],[Type|TT]) :- | |
2722 | %print(ID), print(' : '), print(Type),nl, | |
2723 | % db : set(couple(global(Name),global(Code))) a : set(integer) boolean | |
2724 | convert_typed_varlist(T,IT,TT). | |
2725 | %-------------------------------------- | |
2726 | ||
2727 | ||
2728 | :- use_module(symsrc(symmetry_marker),[type_allows_for_precise_marker/1]). | |
2729 | :- use_module(bmachine,[b_is_unused_constant/1]). | |
2730 | :- use_module(library(codesio),[format_to_codes/3]). | |
2731 | :- use_module(kernel_card_arithmetic,[safe_mul/3]). | |
2732 | generate_typing_list([],[],[],1). | |
2733 | generate_typing_list([Var|VT],[BasicType|TT],[Translation|Rest],TotCard) :- | |
2734 | pretty_type(BasicType,PrettyType), | |
2735 | (kernel_objects:max_cardinality(BasicType,Card) -> true ; Card='??'), | |
2736 | (b_get_constant_represented_inside_global_set(Var,BasicType) | |
2737 | -> Imp='(treated as enumerated) ' | |
2738 | ; (preference(symmetry_mode,hash), | |
2739 | \+type_allows_for_precise_marker(BasicType)) | |
2740 | -> Imp = '(IMPRECISE for HASH) ' | |
2741 | ; Imp = '' | |
2742 | ), | |
2743 | (non_det_constant(Var) | |
2744 | -> MulSol ='[multiple solutions in SETUP_CONSTANTS]' | |
2745 | ; MulSol=''), | |
2746 | (b_is_unused_constant(Var) -> Unused=' (unused)' ; Unused=''), | |
2747 | % translate:translate_bexpression(b(member(identifier(Var),BasicType),pred,[]),Translation), | |
2748 | format_to_codes('~w:~w [card=~w]~w~w~w',[Var,PrettyType,Card,MulSol,Imp,Unused],Codes), | |
2749 | atom_codes(Translation,Codes), | |
2750 | generate_typing_list(VT,TT,Rest,RestCard), | |
2751 | safe_mul(RestCard,Card,TotCard). | |
2752 | ||
2753 | :- use_module(symsrc(symmetry_marker),[hash_model_checking_imprecise/2]). | |
2754 | :- use_module(translate,[pretty_type/2]). | |
2755 | ||
2756 | tcltk_hash_model_checking_imprecise :- | |
2757 | get_preference(symmetry_mode,hash), | |
2758 | hash_model_checking_imprecise(ID,BasicType),!, | |
2759 | print(' * Note: Hashing may be imprecise for: '), | |
2760 | print(ID), print(' : '),translate:pretty_type(BasicType,S),print(S), print(' !'),nl. | |
2761 | ||
2762 | ||
2763 | /* find constants which are assigned multiple values */ | |
2764 | :- dynamic non_det_constant/1. | |
2765 | find_non_det_constants :- | |
2766 | retractall(non_det_constant(_)), | |
2767 | findall(Csts,visited_expression(_,concrete_constants(Csts),_),CL), | |
2768 | (CL=[Valuation1|O],O=[_|_] -> find_non_det_constants1(Valuation1,O) | |
2769 | ; true /* 0 or 1 valuation only */ | |
2770 | ). | |
2771 | ||
2772 | find_non_det_constants1([],_). | |
2773 | find_non_det_constants1([bind(C,Value)|T],Other) :- check_cst(Other, C, Value, PeeledOther), | |
2774 | find_non_det_constants1(T,PeeledOther). | |
2775 | ||
2776 | check_cst([],_,_,[]). | |
2777 | check_cst([ [bind(C,OtherVal)|T1] | TT], C, Value, [ T1 | PTT]) :- | |
2778 | (Value=OtherVal -> check_cst(TT,C,Value,PTT) | |
2779 | ; assertz(non_det_constant(C)), peel2(TT,PTT)). | |
2780 | peel2([],[]). | |
2781 | peel2([ [_|T1] | TT], [ T1 | PTT]) :- peel2(TT,PTT). | |
2782 | ||
2783 | ||
2784 | /* --------------------------------- */ | |
2785 | ||
2786 | ||
2787 | /* --------------------------------- */ | |
2788 | ||
2789 | %% | |
2790 | % Visualize Invariant... | |
2791 | % | |
2792 | % Siehe bvisual.pl | |
2793 | ||
2794 | :- use_module(dotsrc(bvisual),[get_tree_from_expr/4, write_dot_graph_to_file/2]). | |
2795 | ||
2796 | ||
2797 | generate_dot_from_invariant(FileName):- | |
2798 | bmachine:b_get_invariant_from_machine(BExpr), | |
2799 | write_dot_file_for_pred_expr(BExpr,FileName). | |
2800 | ||
2801 | ||
2802 | :- use_module(b_operation_guards,[get_quantified_operation_enabling_condition/5]). | |
2803 | generate_dot_from_operation(TclOpName,FileName) :- | |
2804 | adapt_tcl_operation_name(TclOpName,OpName), | |
2805 | Simplify=false, | |
2806 | get_quantified_operation_enabling_condition(OpName, BExpr, _BecomesSuchVars, _Precise, Simplify), | |
2807 | write_dot_file_for_pred_expr(BExpr,FileName). | |
2808 | %generate_tree_from_boolean_expression(BExpr, Tree,BecomesSuchVars), | |
2809 | %write_dot_graph_to_file(Tree,FileName). | |
2810 | ||
2811 | % a version with additional parameters and additional pre condition / guard predicate | |
2812 | generate_dot_from_operation_with_params(TclOpName,ParameterBindList,RestPreCond, FileName) :- | |
2813 | adapt_tcl_operation_name(TclOpName,OpName), | |
2814 | Simplify=false, | |
2815 | get_quantified_operation_enabling_condition(OpName, BExpr, BecomesSuchVars,_Precise,Simplify), | |
2816 | parse_tclk_parameter_values_and_pred(OpName,ParameterBindList,RestPreCond,CustomPredicate), | |
2817 | insert_custom_predicate_into_guard(BExpr,CustomPredicate,FullPred), | |
2818 | %translate:nested_print_bexpr(FullPred),nl, | |
2819 | % TODO: probably allow all variables to be primed in CustomPredicate: | |
2820 | b_ast_cleanup:annotate_becomes_such_vars(BecomesSuchVars,CustomPredicate,BSV2), | |
2821 | write_dot_file_for_pred_expr(FullPred,FileName,BSV2). | |
2822 | ||
2823 | insert_custom_predicate_into_guard(b(exists(Para,Body),pred,Info),Pred,Res) :- !, | |
2824 | Res = b(exists(Para,BodyRes),pred,Info), % TODO: update wd infos | |
2825 | insert_custom_predicate_into_guard(Body,Pred,BodyRes). | |
2826 | insert_custom_predicate_into_guard(Guard,CustomPred,FullPred) :- | |
2827 | % put CustomPred first so that bvisual prioritises solutions coming from user values/predicate | |
2828 | % TODO: we could try and force equalities in CustomPred before calling bvisual | |
2829 | conjunct_predicates([CustomPred,Guard],FullPred). | |
2830 | ||
2831 | :- use_module(bmachine, [ | |
2832 | b_get_properties_from_machine/1, | |
2833 | b_get_machine_constants/1, | |
2834 | b_machine_has_constants_or_properties/0 ]). | |
2835 | :- use_module(bsyntaxtree,[create_or_merge_exists/3]). | |
2836 | get_properties(BExpr,CreateExists):- | |
2837 | b_get_properties_from_machine(Condition), | |
2838 | ( (CreateExists=true,b_machine_has_constants_or_properties) -> | |
2839 | b_get_machine_constants(IDs), | |
2840 | create_or_merge_exists(IDs, Condition, BExpr) | |
2841 | ; | |
2842 | BExpr = Condition | |
2843 | ). | |
2844 | ||
2845 | generate_dot_from_properties(FileName) :- | |
2846 | (current_state_corresponds_to_fully_setup_b_machine -> CreateExists=false; CreateExists=true), | |
2847 | get_properties(BExpr,CreateExists), | |
2848 | write_dot_file_for_pred_expr(BExpr,FileName). | |
2849 | ||
2850 | :- use_module(bsyntaxtree,[conjunct_predicates/2]). | |
2851 | ||
2852 | generate_dot_from_assertions(FileName) :- | |
2853 | (current_state_corresponds_to_setup_constants_b_machine, | |
2854 | bmachine:b_get_dynamic_assertions_from_machine(BExpr), BExpr \=[] | |
2855 | -> true | |
2856 | ; bmachine:b_get_static_assertions_from_machine(BExpr) | |
2857 | ), | |
2858 | conjunct_predicates(BExpr,BE), | |
2859 | write_dot_file_for_pred_expr(BE,FileName). | |
2860 | ||
2861 | generate_dot_from_goal(FileName) :- | |
2862 | b_get_machine_goal(BExpr),!, | |
2863 | write_dot_file_for_pred_expr(BExpr,FileName,all_primed_vars). | |
2864 | generate_dot_from_goal(FileName) :- | |
2865 | write_dot_file_for_pred_expr_and_state(b(string('No GOAL DEFINITION available'),string,[]),[],[],FileName). | |
2866 | ||
2867 | generate_dot_from_deadlock_po(FileName) :- | |
2868 | b_state_model_check:get_unsorted_all_guards_false_pred(BExpr), | |
2869 | %print('% CHECKING: '),translate_bexpression(BExpr,PT), print(PT),nl, | |
2870 | write_dot_file_for_pred_expr(BExpr,FileName). % we do not check it is a predicate anymore | |
2871 | ||
2872 | ||
2873 | generate_dot_from_formula(Pred,FileName) :- | |
2874 | debug_format(19,'Parsing formula ~w~n',[Pred]), | |
2875 | parse_machine_formula(Pred,TypedExpr), | |
2876 | debug_format(19,'Writing dot formula ~w~n',[Pred]), | |
2877 | write_dot_file_for_pred_expr(TypedExpr,FileName,all_primed_vars). | |
2878 | ||
2879 | ||
2880 | :- use_module(bmachine, [b_parse_machine_formula/3, type_with_errors/4, get_primed_machine_variables/1]). | |
2881 | parse_machine_formula(RawAST,TypedPred) :- compound(RawAST),!, % allow to pass raw AST as well | |
2882 | get_primed_machine_variables(PV), | |
2883 | type_with_errors(RawAST,[prob_ids(visible),identifier(PV),variables],_Type,TypedPred). | |
2884 | parse_machine_formula(PredStr,TypedExpr) :- | |
2885 | get_primed_machine_variables(PV), | |
2886 | b_parse_machine_formula(PredStr,[prob_ids(visible),identifier(PV),variables],TypedExpr). | |
2887 | ||
2888 | ||
2889 | write_dot_file_for_pred_expr(BExpr,FileName) :- | |
2890 | write_dot_file_for_pred_expr(BExpr,FileName,[]). | |
2891 | write_dot_file_for_pred_expr(BExpr,FileName,BecomesSuchVars) :- | |
2892 | generate_tree_in_cur_state(BExpr, Tree,BecomesSuchVars), | |
2893 | printsilent('% Writing to: '), printsilent(FileName),nls, | |
2894 | write_dot_graph_to_file(Tree,FileName). | |
2895 | write_dot_file_for_pred_expr_and_state(BExpr, LocalState, State,FileName) :- | |
2896 | get_tree_from_expr(Tree, BExpr, LocalState, State), | |
2897 | printsilent('% Writing to: '), printsilent(FileName),nls, | |
2898 | write_dot_graph_to_file(Tree,FileName). | |
2899 | ||
2900 | :- use_module(probsrc(state_space),[get_current_predecessor_state_id/1]). | |
2901 | :- use_module(probsrc(specfile),[extract_variables_from_state_as_primed/2]). | |
2902 | ||
2903 | generate_tree_in_cur_state(BExpr,Tree,all_primed_vars) :- | |
2904 | (get_current_predecessor_state_id(PredID), | |
2905 | visited_expression(PredID,SrcState), | |
2906 | extract_variables_from_state_as_primed(SrcState,PrimedVarBindings) | |
2907 | -> true | |
2908 | ; PrimedVarBindings=[] | |
2909 | ), | |
2910 | get_current_state_for_b_formula(BExpr,CurState0), | |
2911 | extend_state_with_probids_and_lets(CurState0,CurState1), | |
2912 | append(PrimedVarBindings,CurState1,CurState), | |
2913 | get_tree_from_expr(Tree, BExpr, [], CurState). | |
2914 | generate_tree_in_cur_state(BExpr,Tree,BecomesSuchVars) :- | |
2915 | get_current_state_for_b_formula(BExpr,CurState0), | |
2916 | extend_state_with_probids_and_lets(CurState0,CurState1), | |
2917 | % print(cur(CurState1)),nl, | |
2918 | insert_before_substitution_variables(BecomesSuchVars,[],CurState1,CurState1,CurState), | |
2919 | !, | |
2920 | get_tree_from_expr(Tree, BExpr, [], CurState). | |
2921 | ||
2922 | % generate a dot tree representation for a value; mostly useful for symbolic values | |
2923 | write_dot_file_for_value_as_tree(Value,FileName) :- | |
2924 | transform_value_to_expr(Value,BExpr), | |
2925 | temporary_set_preference(formula_tree_minimal_timeout,5,Prev1), | |
2926 | temporary_set_preference(formula_tree_maximal_timeout,30,Prev2), | |
2927 | call_cleanup(generate_tree_in_cur_state(BExpr, Tree,[]), | |
2928 | (reset_temporary_preference(formula_tree_minimal_timeout,Prev1), | |
2929 | reset_temporary_preference(formula_tree_maximal_timeout,Prev2))), | |
2930 | printsilent('% Writing to: '), printsilent(FileName),nls, | |
2931 | write_dot_graph_to_file(Tree,FileName). | |
2932 | ||
2933 | :- use_module(probsrc(bsyntaxtree),[create_typed_ids/3]). | |
2934 | :- use_module(library(lists),[maplist/4]). | |
2935 | transform_value_to_expr(closure(P,T,B),E) :- | |
2936 | create_typed_ids(P,T,Parameters),!, | |
2937 | E=b(comprehension_set(Parameters,B),set(any),[]). % TO DO: we could create typing | |
2938 | transform_value_to_expr(Val,b(value(Val),any,[])). | |
2939 | ||
2940 | :- use_module(tools,[maplist5/5]). | |
2941 | tcltk_bv_get_tops(Tops) :- | |
2942 | bv_get_top_level(Tops). | |
2943 | tcltk_bv_get_structure(Id,Label,list(Subs)) :- | |
2944 | bv_expand_formula(Id,Label,Subs). | |
2945 | tcltk_bv_is_leaf(Id,Label) :- | |
2946 | bv_expand_formula(Id,Label,[]). | |
2947 | tcltk_bv_get_values(Ids,list(RTexts),list(Tags)) :- | |
2948 | current_state_id(StateId), | |
2949 | bv_get_values(Ids,StateId,Values), | |
2950 | maplist5(tcltk_interface:retrieve_tag_adapted_text_for_id,Ids,Values,Tags,RTexts). | |
2951 | tcltk_bv_get_value(Id,RText,Tag) :- | |
2952 | tcltk_bv_get_values([Id],list([RText]),list([Tag])). | |
2953 | tcltk_bv_show_formula_as_dot_tree(Id,File) :- | |
2954 | bv_get_stored_formula_expr(Id,TExpr), | |
2955 | write_dot_file_for_pred_expr(TExpr,File). | |
2956 | tcltk_bv_get_pp_formula_string(Id,PPFormula) :- | |
2957 | bv_get_stored_formula_expr(Id,TExpr), | |
2958 | translate:translate_bexpression(TExpr,PPFormula). | |
2959 | ||
2960 | tcltk_bv_show_formula_as_dot_graph(Id,File) :- | |
2961 | bv_get_stored_formula_expr(Id,TExpr), | |
2962 | get_texpr_type(TExpr,Type), | |
2963 | Type \= pred, Type \= subst, | |
2964 | % tcltk_show_typed_expression_as_dot_graph decomposes expressions and shows value as graph: could be useful for pre-configured DEFINITIONS | |
2965 | tcltk_show_typed_expression_as_dot_graph(TExpr,File). | |
2966 | ||
2967 | tcltk_bv_show_value_as_dot_tree(Id,File) :- % useful for symbolic values | |
2968 | current_state_id(StateId), | |
2969 | bv_get_btvalue(Id,StateId,_E,Value), | |
2970 | write_dot_file_for_value_as_tree(Value,File). | |
2971 | ||
2972 | % a variation of tcltk_bv_show_value_as_dot_tree without dependence on bvisual2 | |
2973 | tcltk_show_identifier_value_as_dot_tree(RawId,File) :- % this also works if bvisual2 is not set up and entry active | |
2974 | get_identifier(RawId,Id), | |
2975 | current_expression(_ID,CurState), | |
2976 | expand_const_and_vars_to_full_store(CurState,EState), | |
2977 | (EState=[] -> add_error(tcltk_show_identifier_value_as_dot_tree,'Initialise machine first, cannot retrieve value for identifier:',Id) | |
2978 | ; lookup_value_for_existing_id(Id,EState,Value) | |
2979 | -> write_dot_file_for_value_as_tree(Value,File) | |
2980 | ; add_error(tcltk_show_identifier_value_as_dot_tree,'Cannot retrieve value for identifier:',Id)). | |
2981 | ||
2982 | get_identifier(AtomicID,Res) :- atom(AtomicID), valid_id(AtomicID), !,Res=AtomicID. | |
2983 | get_identifier(identifier(_,ID),Res) :- valid_id(ID),!, Res=ID. % raw ID from prob2_interface | |
2984 | get_identifier(RawFormula,_) :- | |
2985 | add_error(tcltk_show_identifier_value_as_dot_tree,'Please provide valid identifier of constant or variable:',RawFormula), | |
2986 | fail. | |
2987 | valid_id(ID) :- b_is_variable(ID). | |
2988 | valid_id(ID) :- b_is_constant(ID). | |
2989 | ||
2990 | retrieve_tag_adapted_text_for_id(Id,Value,Tag,RText) :- | |
2991 | retrieve_tag_text_for_id(Id,Value,Tag,Text), | |
2992 | (Text = '[]' -> RText = '[ ]' ; RText=Text). % for some reason the empty sequence translation [] gets transformed into '' by the tcltk library | |
2993 | ||
2994 | retrieve_tag_text_for_id(Id,Value,FullTag,Text) :- | |
2995 | retrieve_tag_text(Value,Tag,Text), | |
2996 | (bv_is_explanation_node(Id) -> convert_explain_tag(Tag,FullTag) ; FullTag=Tag). | |
2997 | retrieve_tag_text(i, inac, ''). | |
2998 | retrieve_tag_text(e(Text), error, Text). | |
2999 | retrieve_tag_text(v(Text), value, Text). | |
3000 | retrieve_tag_text(p(true), ptrue, true). | |
3001 | retrieve_tag_text(p(false),pfalse, false). | |
3002 | retrieve_tag_text(bv_info(Text), inac, Text). | |
3003 | ||
3004 | ||
3005 | tcltk_bv_get_unlimited_value(Id,Text,Tag) :- | |
3006 | current_state_id(StateId), | |
3007 | bv_get_value_unlimited(Id,StateId,Value), | |
3008 | retrieve_tag_text_for_id(Id,Value,Tag,Text1), | |
3009 | atom_codes(Text1,Text). % TODO: In the end, the result was converted from codes to atom and back | |
3010 | ||
3011 | % a version which returns just the value as an atom: | |
3012 | tcltk_bv_get_unlimited_value_atom(Id,AtomText) :- | |
3013 | current_state_id(StateId), | |
3014 | bv_get_value_unlimited(Id,StateId,Value), | |
3015 | retrieve_tag_text_for_id(Id,Value,_Tag,AtomText). | |
3016 | ||
3017 | convert_explain_tag(ptrue,explaintrue). | |
3018 | convert_explain_tag(pfalse,explainfalse). | |
3019 | convert_explain_tag(X,X). | |
3020 | ||
3021 | % ----------------------------------------- | |
3022 | ||
3023 | % STILL TO DO: refactor eval from here and probcli into separate module | |
3024 | ||
3025 | :- dynamic eval_elapsed_time/1. | |
3026 | tcltk_time_call(Call) :- | |
3027 | cputime(T1),Call,cputime(T2),ElapsedTime is T2-T1, | |
3028 | retractall(eval_elapsed_time(_)),assertz(eval_elapsed_time(ElapsedTime)). | |
3029 | ||
3030 | :- use_module(eval_strings). | |
3031 | ||
3032 | ||
3033 | tcltk_eval(String,StringResult,EnumWarning,Solution) :- | |
3034 | get_computed_preference(debug_time_out,DTO), | |
3035 | %print(debug_time_out(DTO)),nl, | |
3036 | time_out_with_enum_warning_one_solution(eval_string(String,StringResult,EnumWarning,LocalState),DTO,TimeOutRes), | |
3037 | (LocalState=[] -> Solution='' ; translate_bstate(LocalState,Solution)), | |
3038 | (is_time_out_result(TimeOutRes) -> | |
3039 | StringResult = '**** TIME-OUT ****', print(StringResult), print(' ('),print(DTO), print('ms)'),nl, | |
3040 | EnumWarning = no | |
3041 | ; print_last_info). | |
3042 | ||
3043 | :- public tcltk_eval_string_in_cur_state/2. | |
3044 | tcltk_eval_string_in_cur_state(String,TclValue) :- | |
3045 | eval_string_in_cur_state(String,_,Value), | |
3046 | convert_value_to_tcl(Value,TclValue). | |
3047 | convert_value_to_tcl(int(X),R) :- !, R=X. | |
3048 | convert_value_to_tcl(pred_true,R) :- !, R=1. | |
3049 | convert_value_to_tcl(pred_false,R) :- !, R=0. | |
3050 | convert_value_to_tcl(string(X),R) :- !, R=X. | |
3051 | % TO DO: add sets,... | |
3052 | convert_value_to_tcl(R,R) :- print(cannot_convert_to_tcl(R,R)),nl. | |
3053 | ||
3054 | eval_string_in_cur_state(String,Typed,Value) :- | |
3055 | %format('Parsing : "~w"~n',[String]), | |
3056 | bmachine:parse_expression_raw_or_atom_with_prob_ids(String,Typed), | |
3057 | current_state_id(CurID), prob2_interface:get_state(CurID,EState), | |
3058 | %print(evaluating_in_state(CurID)),nl, | |
3059 | b_compute_expression_with_prob_ids(Typed,EState,Value). | |
3060 | ||
3061 | % code to translate an expression into a list of lists that can be displayed as a table in Tk | |
3062 | % TO DO: make more robust and add more fancy translations | |
3063 | :- use_module(extrasrc(table_tools),[expand_and_translate_to_table_for_expr/5]). | |
3064 | tcltk_eval_as_table(String,Res) :- | |
3065 | eval_string_in_cur_state(String,Typed,Value), | |
3066 | Res = list([list(Header)|Table]), | |
3067 | expand_and_translate_to_table_for_expr(Typed,Value,Header,Table,['no-row-numbers']). | |
3068 | ||
3069 | :- use_module(user_interrupts,[interruptable_call/1]). | |
3070 | interruptable_tcltk_eval(String,StringResult,EnumWarning,Solution) :- | |
3071 | if(interruptable_call(tcltk_eval(String,StringResult,EnumWarning,Solution)),true, | |
3072 | (StringResult = 'Error or User-Interrupt', EnumWarning=unknown, Solution='')). | |
3073 | ||
3074 | % ----------------------------------------- | |
3075 | ||
3076 | tcltk_find_value_as_table(String,Options,list([Header|Matches])) :- | |
3077 | eval_string_in_cur_state(String,Typed,GoalValue), | |
3078 | Header = list(['ID','Kind','MatchPath']), | |
3079 | get_texpr_type(Typed,Type), | |
3080 | findall(list([ID,Kind,MatchMsg]), | |
3081 | (find_value_in_cur_state(GoalValue,Type,Options,match(Kind,ID,Path)), | |
3082 | format_to_codes('~w',[Path],MatchMsgC), | |
3083 | format('* match inside ~w ~w @ path: ~w~n',[Kind,ID,Path]), | |
3084 | atom_codes(MatchMsg,MatchMsgC)),Matches). | |
3085 | ||
3086 | % find a given value somewhere in a constant or variable value | |
3087 | ||
3088 | find_value_in_cur_state(GoalValue,Type,Options,match(Kind,ID,Path)) :- | |
3089 | translate:translate_bvalue(GoalValue,VS), | |
3090 | get_current_state_for_b_formula(requires_variables,State), % produce full state | |
3091 | member(bind(ID,Value),State), | |
3092 | ( bmachine:b_is_variable(ID,IDType), Kind=variable | |
3093 | ; bmachine:b_is_constant(ID,IDType), Kind=constant), | |
3094 | debug_format(19,'Searching id ~w for ~w~n',[ID,VS]), | |
3095 | % TODO check if Type occurs in type of ID | |
3096 | find_value(Value,IDType,GoalValue,Type,Options,Path). | |
3097 | ||
3098 | ||
3099 | :- use_module(probsrc(kernel_freetypes),[registered_freetype/2]). | |
3100 | :- use_module(bsyntaxtree,[is_set_type/2]). | |
3101 | :- use_module(kernel_strings,[to_b_string/2]). | |
3102 | find_value(Val,Type,GoalVal,Type,Options,Path) :- !, % target and goal type match; to do: unify_types | |
3103 | (kernel_objects:equal_object(Val,GoalVal) -> Path = '*' | |
3104 | ; fuzzy_find_value(Val,Type,GoalVal,Type,Options,Path)). | |
3105 | find_value((A,B),couple(TA,TB),GoalVal,Type,Options,Path) :- !, | |
3106 | ( find_value(A,TA,GoalVal,Type,Options,PA), | |
3107 | Path=','(PA,BS), translate:translate_bvalue(B,BS) | |
3108 | ; find_value(B,TB,GoalVal,Type,Options,PB), | |
3109 | Path=','(AS,PB), translate:translate_bvalue(A,AS)). | |
3110 | find_value(rec(Fields),record(FieldTypes),GoalVal,Type,Options,rec(Field,Path)) :- !, | |
3111 | member(field(Field,Val),Fields), | |
3112 | member(field(Field,TVal),FieldTypes), | |
3113 | find_value(Val,TVal,GoalVal,Type,Options,Path). | |
3114 | find_value(avl_set(AVL),AType,GoalVal,Type,Options,'{}'(Path)) :- !, | |
3115 | is_set_type(AType,TA), | |
3116 | custom_explicit_sets:avl_member_opt(Value,AVL), % TODO: provide a more proper interface predicate | |
3117 | find_value(Value,TA,GoalVal,Type,Options,Path). | |
3118 | find_value([H|T],set(TA),GoalVal,Type,Options,'{}'(Path)) :- !, | |
3119 | member(Value,[H|T]), | |
3120 | find_value(Value,TA,GoalVal,Type,Options,Path). | |
3121 | find_value(freeval(ID,Case,Val),freetype(ID),GoalVal,Type,Options,freeval(Case,Path)) :- !, | |
3122 | (registered_freetype(ID,Cases), | |
3123 | member(case(Case,TVal),Cases) -> true | |
3124 | ; add_error(find_value,'Illegal freeval:',freeval(ID,Case,Val)),fail | |
3125 | ), | |
3126 | find_value(Val,TVal,GoalVal,Type,Options,Path). | |
3127 | find_value(Val,TypeVal,GoalVal,Type,Options,Path) :- | |
3128 | fuzzy_find_value(Val,TypeVal,GoalVal,Type,Options,Path). | |
3129 | ||
3130 | fuzzy_find_value(Val,_Type,string(GoalAtom),string,[Option],'**'(TargetAtom)) :- | |
3131 | GoalAtom \= '', | |
3132 | simple_value_to_match(Val), | |
3133 | to_b_string(Val,string(TargetAtom)), | |
3134 | find_match_string(Option,TargetAtom,GoalAtom). | |
3135 | ||
3136 | simple_value_to_match(fd(_,_)). | |
3137 | simple_value_to_match(string(_)). | |
3138 | simple_value_to_match(int(_)). | |
3139 | simple_value_to_match(term(_)). | |
3140 | simple_value_to_match(freeval(_,_Case,Val)) :- ground(Val), Val=term(_). | |
3141 | ||
3142 | :- use_module(tools_matching,[fuzzy_match_codes_lower_case/2]). | |
3143 | ||
3144 | % check whether TargetAtom matches GoalAtom given match option | |
3145 | find_match_string(exact,GoalAtom,GoalAtom). | |
3146 | find_match_string(prefix,TargetAtom,GoalAtom) :- | |
3147 | atom_concat(GoalAtom,_,TargetAtom). % use sub_atom ? | |
3148 | find_match_string(suffix,TargetAtom,GoalAtom) :- | |
3149 | atom_concat(_,GoalAtom,TargetAtom). | |
3150 | find_match_string(infix,TargetAtom,GoalAtom) :- | |
3151 | sub_atom(TargetAtom,_Before,Length,_After,GoalAtom), Length>0. | |
3152 | find_match_string(fuzzy,TargetAtom,GoalAtom) :- | |
3153 | atom_codes(TargetAtom,TC), | |
3154 | atom_codes(GoalAtom,GC), | |
3155 | fuzzy_match_codes_lower_case(TC,GC). | |
3156 | % TODO: regex | |
3157 | ||
3158 | % ----------------------------------------- | |
3159 | % B Simplifier | |
3160 | :- use_module(probporsrc(b_simplifier),[tcltk_simplify_b_predicate/2]). | |
3161 | tcltk_simplify_predicate(String,StringResult) :- | |
3162 | tcltk_simplify_b_predicate(String,StringResult). | |
3163 | ||
3164 | ||
3165 | % ----------------------------------------- | |
3166 | % CBC Check | |
3167 | ||
3168 | get_cbc_data_base_id_checks(IDs) :- | |
3169 | findall(ID,b_state_model_check: cbc_check(ID,_Text,_Call,_Res,_Ok),IDs). | |
3170 | get_cbc_data_base_text_checks(Strings) :- | |
3171 | findall(Text1,(b_state_model_check: cbc_check(_ID,Text,_Call,_Res,_Ok),tools: string_concatenate(Text,';',Text1)),Strings). | |
3172 | ||
3173 | ||
3174 | % ----------------------------------------- | |
3175 | ||
3176 | % CSP Refinement Search | |
3177 | ||
3178 | %:- dynamic tcltk_cspm_mode/0. | |
3179 | %set_tcltk_cspm_mode :- assertz(tcltk_cspm_mode). | |
3180 | %unset_tcltk_cspm_mode :- retractall(tcltk_cspm_mode). | |
3181 | ||
3182 | ||
3183 | %get_new_csp_process_id(E,File,ID) :- | |
3184 | % ( parse_single_csp_expression_file(E,File,Proc) -> | |
3185 | % haskell_csp: check_compiled_term(Proc), add_csp_process_id1(Proc,ID,haskell_csp:animatable_process) | |
3186 | % ; add_error(parse_single_csp_expression_file,'Invalid process expression: ',E),fail | |
3187 | % ). | |
3188 | ||
3189 | %parse_and_analyze_process_expression(ProcExp,File,Proc) :- | |
3190 | % ( parse_single_csp_expression_file(ProcExp,File,Proc) -> true | |
3191 | % ; add_error(parse_single_csp_expression_file,'Invalid process expression: ',ProcExp),fail | |
3192 | % ). | |
3193 | ||
3194 | get_csp_process_id(Process,ID) :- get_start_transition_term(Process,Trans), | |
3195 | transition(root,Trans,ID). | |
3196 | ||
3197 | :- use_module(probcspsrc(haskell_csp),[animatable_process_without_arguments/1,animatable_process_cli/1]). | |
3198 | add_csp_process_id(Process,NewID) :- | |
3199 | add_csp_process_id1(Process,NewID,animatable_process_cli). | |
3200 | ||
3201 | add_csp_process_id1(Process,NewID,Pred) :- | |
3202 | (call(Pred,Process) | |
3203 | -> get_start_transition_term(Process,Trans), | |
3204 | (transition(root,Trans,_,DestID) -> NewID=DestID % process already set up | |
3205 | ; xtl_interface:get_start_expr(Process,NewState), | |
3206 | add_trans_id(root,Trans,NewState,NewID,_) | |
3207 | ) | |
3208 | ; add_error_fail(add_csp_process_id,'Not an animatable CSP Process: ',Process)). | |
3209 | ||
3210 | get_start_transition_term('MAIN',R) :- !, R= start_cspm_MAIN. | |
3211 | get_start_transition_term(agent_call(_Src,'MAIN',[]),R) :- !, R= start_cspm_MAIN. | |
3212 | get_start_transition_term(P,start_cspm(P)). | |
3213 | ||
3214 | add_csp_general_process_id(val_of(Process,_),ID) :- !, | |
3215 | ( animatable_process_without_arguments(Process) % in case the called processes is one without arguments, then we don't have to parse the expression | |
3216 | -> add_csp_process_id(Process,ID) | |
3217 | % code below not covered; loaded_main_file only defined for probcli entry point ! | |
3218 | %; animation_mode(cspm), | |
3219 | % loaded_main_file(CSPFile), | |
3220 | % parse_and_analyze_process_expression(Process,CSPFile,Proc), | |
3221 | % (transition(root,start_cspm(Proc),_TransID,ID) -> true | |
3222 | % ; otherwise -> add_trans_id(root,start_cspm(Proc),Proc,ID,_) | |
3223 | % ) | |
3224 | ). | |
3225 | add_csp_general_process_id(GenProcExpr,ID) :- | |
3226 | % stripping out the source-loc info | |
3227 | haskell_csp_analyzer: clear_span(GenProcExpr,GenProcExpr1), | |
3228 | get_start_transition_term(GenProcExpr1,Trans), | |
3229 | (transition(root,Trans,_TransID,ID) | |
3230 | -> true | |
3231 | ; add_trans_id(root,Trans,GenProcExpr1,ID,_) | |
3232 | ). | |
3233 | ||
3234 | :- public tcltk_csp_in_situ_refinement/4. | |
3235 | tcltk_csp_in_situ_refinement(ImplProcess,AbsProcess,ResTrace,Style) :- | |
3236 | format('Checking ~w ~w ~w~n',[AbsProcess,Style,ImplProcess]), | |
3237 | tcltk_csp_in_situ_refinement(ImplProcess,AbsProcess,ResTrace,Style,10000000). | |
3238 | csp_ref_style('[T=',trace). % Trace Refinement | |
3239 | csp_ref_style('T',trace). % Trace Refinement | |
3240 | csp_ref_style('[SF=',singleton_failures). % Singleton Failures Refinement | |
3241 | csp_ref_style('[F=',failures). % Failures Refinement | |
3242 | csp_ref_style('[R=',refusals). % Refusals Refinement | |
3243 | csp_ref_style('[RD=',refusals_div). % Refusals-Divergence Refinement | |
3244 | csp_ref_style('F',failures). % Failures Refinement | |
3245 | csp_ref_style('[FD=',X) :- csp_ref_style('FailureDivergence',X). | |
3246 | csp_ref_style('FD',X) :- csp_ref_style('FailureDivergence',X). | |
3247 | csp_ref_style('Trace',trace). | |
3248 | csp_ref_style('Failure',failures). | |
3249 | csp_ref_style('FailureDivergence',failure_divergences). | |
3250 | csp_ref_style('RefusalTesting',refusals). | |
3251 | csp_ref_style('RefusalTestingDiv',refusals_div). | |
3252 | csp_ref_style('RevivalTesting',revival) :- add_error(unsupported_refinement_operator,'[V= refinement not yet supported.'). | |
3253 | csp_ref_style('RevivalTestingDiv',revival_div) :- add_error(unsupported_refinement_operator,'[VD= refinement not yet supported.'). | |
3254 | ||
3255 | translate_csp_ref_style(Style,RefStyle) :- (csp_ref_style(Style,RefStyle) -> true ; | |
3256 | add_error_fail(csp,'Unknown refinement style: ',Style)). | |
3257 | ||
3258 | :- use_module(extrasrc(refinement_checker),[in_situ_ref_search/5]). | |
3259 | ||
3260 | % tcltk_csp_in_situ_refinement('P','Q',R,0,1000). tcltk_csp_in_situ_refinement('Q','P',R,1000). | |
3261 | tcltk_csp_in_situ_refinement(ImplProcess,AbsProcess,ResTrace,Style,MaxNrOfNewNodes) :- | |
3262 | add_csp_process_id(ImplProcess,ImplNodeID), | |
3263 | add_csp_process_id(AbsProcess,SpecNodeID), | |
3264 | translate_csp_ref_style(Style,RefStyle), | |
3265 | in_situ_ref_search(ImplNodeID,SpecNodeID,ResTrace,RefStyle,MaxNrOfNewNodes). | |
3266 | ||
3267 | :- use_module(probcspsrc(haskell_csp),[get_csp_assertions/1,translate_csp_assertion/2]). | |
3268 | ||
3269 | tcltk_check_csp_assertions(List,Summary) :- get_csp_assertions(Assertions), | |
3270 | treat_csp_ass(Assertions,List,0,0,0,Summary). % TO DO: display _List | |
3271 | ||
3272 | treat_csp_ass([],[],T,F,U,[total/Tot, true/T, false/F, unknown/U]) :- Tot is T+F+U. | |
3273 | treat_csp_ass([Assertion|T],['='(PP,Ok)|TPP],True,False,U,Summary) :- !, | |
3274 | (checkAssertion(Assertion,PP,_,Ok,_) | |
3275 | -> ( Ok=true -> T1 is True+1, F1=False, U1=U | |
3276 | ; Ok=false -> T1=True, F1 is False+1, U1=U | |
3277 | ; T1=True, F1=False, U1 is U+1) | |
3278 | ; U1 is U+1, T1=True, PP = '??', | |
3279 | F1=False,Ok=unknown | |
3280 | ), | |
3281 | treat_csp_ass(T,TPP,T1,F1,U1,Summary). | |
3282 | treat_csp_ass([H|T],TP,T1,F1,U,Summary) :- | |
3283 | add_error(treat_csp_ass,'Unknown CSP assertion: ',H), | |
3284 | U1 is U+1, treat_csp_ass(T,TP,T1,F1,U1,Summary). | |
3285 | ||
3286 | % assertModelCheckExt(False,val_of(P3,src_span(25,8,25,10,562,2)),DeadlockFree,F) | |
3287 | ||
3288 | :- use_module(extrasrc(refinement_checker),[in_situ_model_check/5, reset_refinement_checker/0]). | |
3289 | ||
3290 | run_assertion_check(assertModelCheckExt(Negated,Spec,Type,ModelStyle),Negated,ResTrace) :- | |
3291 | haskell_csp_analyzer: compile_body(Spec,'assertModelCheck',[],[],CSpec), | |
3292 | debug_println(9,assertModelCheckExt(Negated,CSpec,Type,ModelStyle)), | |
3293 | add_csp_general_process_id(CSpec,SpecNodeID), | |
3294 | translate_csp_ref_style(ModelStyle,RefStyle), | |
3295 | in_situ_model_check(SpecNodeID,ResTrace,Type,RefStyle,10000000), | |
3296 | formatsilent('Result trace for assertModelCheck: ~w~n',[ResTrace]). | |
3297 | run_assertion_check(assertModelCheck(Negated,Spec,Style),Negated,ResTrace) :- | |
3298 | run_assertion_check(assertModelCheckExt(Negated,Spec,Style,'FailureDivergence'),Negated,ResTrace). | |
3299 | run_assertion_check(assertRef(Negated,Spec,Style,Impl,Span),Negated,ResTrace) :- | |
3300 | haskell_csp_analyzer: compile_body(Spec,'assertRef',[],[],CSpec), | |
3301 | haskell_csp_analyzer: compile_body(Impl,'assertRef',[],[],CImpl), | |
3302 | debug_println(9,assertRef(Negated,CSpec,Style,CImpl,Span)), | |
3303 | add_csp_general_process_id(CSpec,SpecNodeID), | |
3304 | add_csp_general_process_id(CImpl,ImplNodeID), | |
3305 | translate_csp_ref_style(Style,RefStyle), | |
3306 | refinement_checker:in_situ_ref_search(ImplNodeID,SpecNodeID,ResTrace,RefStyle,10000000). | |
3307 | run_assertion_check(assertLtl(Negated,Spec,FormulaAsAtom,_Span),Negated,ResTrace) :- | |
3308 | add_csp_process_id_normalized(Spec,'assertLtl',CSpec,CSpecNodeID), | |
3309 | %% print(add_csp_general_process_id(Spec,CSpec,SpecNodeID)),nl, | |
3310 | tcltk_reset, | |
3311 | ltl_model_check_with_ce1(CSpec,FormulaAsAtom,-1,specific_node(CSpecNodeID),Result), | |
3312 | translate_ltl_result(Result,ResTrace), | |
3313 | debug_println(9,ltl_result(Negated,CSpec,CSpecNodeID,FormulaAsAtom,Result)). | |
3314 | run_assertion_check(assertCtl(Negated,Spec,FormulaAsAtom,_Span),Negated,ResTrace) :- | |
3315 | add_csp_process_id_normalized(Spec,'assertCtl',CSpec,CSpecNodeID), | |
3316 | tcltk_reset, | |
3317 | ctl_model_check_with_ce(CSpec,FormulaAsAtom,-1,specific_node(CSpecNodeID),Result,Trace), | |
3318 | debug_println(9,ctl_result(Negated,CSpec,CSpecNodeID,FormulaAsAtom,Result)), | |
3319 | translate_ctl_result(Result,Trace,ResTrace). | |
3320 | ||
3321 | checkAssertion(Assertion,PP,Negated,Ok,ResTrace) :- | |
3322 | translate_csp_assertion(Assertion,PP), | |
3323 | formatsilent('CHECKING ~w~n',[PP]), | |
3324 | debug_println(9,checkAssertion(Assertion)), | |
3325 | reset_refinement_checker, | |
3326 | (run_assertion_check(Assertion,Negated,ResTrace) | |
3327 | -> debug_println(9,result(ResTrace)), | |
3328 | (ResTrace=no_counter_example | |
3329 | -> (Negated = 'False' -> Ok=true ; Ok=false) | |
3330 | ; ResTrace=no_counter_example_found | |
3331 | -> Ok=interrupted | |
3332 | ; ResTrace=[interrupted|_] | |
3333 | -> Ok=interrupted, print('*** Execution interrupted by user ***'),nl | |
3334 | ; Negated = 'False' -> Ok=false | |
3335 | ; Ok=true | |
3336 | ), | |
3337 | (Ok=true -> printsilent('OK'),nls | |
3338 | ; Ok=interrupted -> print('*** UNKNOWN ***'),nl | |
3339 | ; print('*** FALSE ***'),nl | |
3340 | ) | |
3341 | ; add_error_fail(checkAssertion,'Internal Error, Assertion Checking Failed for: ',PP) | |
3342 | ). | |
3343 | ||
3344 | add_csp_process_id_normalized(Spec,Functor,CSpec,CSpecNodeID) :- | |
3345 | % stripping out the src_span info | |
3346 | haskell_csp_analyzer: clear_span(Spec,SpecNoSpan), | |
3347 | haskell_csp_analyzer: compile_body(SpecNoSpan,Functor,[],[],CSpec), | |
3348 | add_csp_general_process_id(CSpec,CSpecNodeID). | |
3349 | ||
3350 | translate_ltl_result(ok,Res) :- !, | |
3351 | Res = no_counter_example. | |
3352 | translate_ltl_result(no,Res) :- !, | |
3353 | Res = no. | |
3354 | translate_ltl_result(ce(Atom),Res) :- | |
3355 | !, % we have a counter-example for the LTL formula | |
3356 | Res=Atom. | |
3357 | translate_ltl_result(interrupted,Res) :- | |
3358 | !, | |
3359 | Res=[interrupted]. | |
3360 | translate_ltl_result(Status,_Res) :- | |
3361 | add_error_fail(ltl_assertions_result, 'Internal Error: unknown LTL assertion result ', Status). | |
3362 | ||
3363 | translate_ctl_result(true,_Trace,ResTrace) :- !, | |
3364 | ResTrace = no_counter_example. | |
3365 | translate_ctl_result(false,ce(Atom),ResTrace) :- !, | |
3366 | ResTrace = Atom. | |
3367 | translate_ctl_result(interrupted,_TRace,ResTrace) :- | |
3368 | !, | |
3369 | ResTrace=[interrupted]. | |
3370 | translate_ctl_result(Status,_Trace,_ResTrace) :- | |
3371 | add_error_fail(ctl_assertions_result, 'Internal Error: unknown CTL assertion result ', Status). | |
3372 | ||
3373 | ||
3374 | tcltk_check_csp_assertion(Assertion,CSPFile,Negated,Result) :- | |
3375 | tcltk_check_csp_assertion(Assertion,CSPFile,Negated,_PlTerm,Result). | |
3376 | ||
3377 | tcltk_check_csp_assertion(Assertion,CSPFile,Negated,PlTerm,Result) :- | |
3378 | parse_single_csp_declaration(Assertion,CSPFile,PlTerm), | |
3379 | checkAssertion(PlTerm,_PP,Negated,_Ok,Result). | |
3380 | ||
3381 | :- public tcltk_play_ltl_ctl_counterexample_trace/4. | |
3382 | tcltk_play_ltl_ctl_counterexample_trace(Assertion,CSPFile,Type,FairnessChecked) :- | |
3383 | announce_event(play_counterexample), | |
3384 | ( Type == ltl -> Functor = assertLtl | |
3385 | ; Type == ctl -> Functor = assertCtl | |
3386 | ; add_error_fail(ltl_ctl_counterexample, 'Internal Error: Model checker type unknown: ',Type)), | |
3387 | % constructing the prolog declaration term | |
3388 | PlDeclTerm =.. [Functor,_Negated,Spec,FormulaAsAtom,_Span], | |
3389 | parse_single_csp_declaration(Assertion,CSPFile,PlDeclTerm), | |
3390 | haskell_csp_analyzer: clear_span(Spec,SpecNoSpan), | |
3391 | haskell_csp_analyzer: compile_body(SpecNoSpan,'assertLtl',[],[],CSpec), | |
3392 | % parsing and preprocessing the LTL/CTL formula | |
3393 | parse_and_preprocess_formula(FormulaAsAtom,Type,PF), | |
3394 | (is_fairness_implication(PF) -> FairnessChecked='yes'; FairnessChecked='no'), | |
3395 | % need to reset history and current state before loading the counter-example | |
3396 | tcltk_reset, | |
3397 | tcltk_play_counterexample(Type,CSpec,PF). | |
3398 | ||
3399 | tcltk_play_counterexample(ltl,Spec,Formula) :- !, | |
3400 | tcltk_play_ltl_counterexample(Spec,Formula). | |
3401 | tcltk_play_counterexample(ctl,Spec,Formula) :- !, | |
3402 | tcltk_play_ctl_counterexample(Spec,Formula). | |
3403 | tcltk_play_counterexample(Type,Spec,Formula) :- | |
3404 | add_error_fail(ltl_ctl_counterexample, 'Internal Error: Unknown domain type: ', (Type,Spec,Formula)). | |
3405 | ||
3406 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
3407 | ||
3408 | ||
3409 | %%%% Parse and get LTL formulas from LTL File %%%% | |
3410 | get_ltl_formulas_from_file(Filename,Text) :- | |
3411 | parse_ltlfile(Filename, Formulas), print(Formulas),nl, | |
3412 | translate_ltl_formulas(Formulas,Text). | |
3413 | ||
3414 | translate_ltl_formulas(Formulas,Text) :- | |
3415 | maplist(translate_ltl_formula,Formulas,Strings), | |
3416 | haskell_csp: concatenate_string_list(Strings,Text). | |
3417 | ||
3418 | translate_ltl_formula(formula(_Name,Formula),Text) :- !, | |
3419 | pp_ltl_formula(Formula,T), | |
3420 | string_concatenate(T,'$',Text). | |
3421 | translate_ltl_formula(F,'') :- add_error(get_ltl_formulas_from_file,'Illegal formula: ',F). | |
3422 | ||
3423 | ||
3424 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
3425 | ||
3426 | ||
3427 | tcltk_explore_csp_process(ProcString,SpecNodeID) :- | |
3428 | haskell_csp: parse_single_csp_expression(ltl,ProcString,Res), | |
3429 | haskell_csp_analyzer: clear_span(Res,ResNoSpan), | |
3430 | haskell_csp_analyzer: compile_body(ResNoSpan,'assertLtl',[],[],CRes), | |
3431 | add_csp_general_process_id(CRes,SpecNodeID), | |
3432 | refinement_checker:dfs_check(SpecNodeID,_ResTrace,false,false). | |
3433 | ||
3434 | tcltk_visualize_csp_process(SpecNodeID,F) :- | |
3435 | visualize_graph: tcltk_print_csp_process_states_for_dot(F,SpecNodeID). | |
3436 | ||
3437 | :- dynamic nr_csp_process_state/1. | |
3438 | get_csp_process_stats(NrNodes) :- | |
3439 | assertz(nr_csp_process_state(0)), | |
3440 | count_nodes, | |
3441 | nr_csp_process_state(NrNodes), | |
3442 | retractall(nr_csp_process_state(_)). | |
3443 | ||
3444 | count_nodes :- | |
3445 | refinement_checker: dvisited(_NodeID), | |
3446 | nr_csp_process_state(NrNodes), | |
3447 | NewNrNodes is NrNodes +1, | |
3448 | retractall(nr_csp_process_state(_)), | |
3449 | assertz(nr_csp_process_state(NewNrNodes)), | |
3450 | fail. | |
3451 | count_nodes. | |
3452 | ||
3453 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
3454 | ||
3455 | tcltk_machine_has_assertions :- | |
3456 | ( b_get_static_assertions_from_machine([_|_]); b_get_dynamic_assertions_from_machine([_|_]) ), !. | |
3457 | ||
3458 | tcltk_unique_top_level_operation(Name) :- b_top_level_operation(Name), | |
3459 | \+ (b_top_level_operation(N2), N2 \= Name). | |
3460 | ||
3461 | tcltk_top_level_operations(list(Res)) :- | |
3462 | findall(Name,b_top_level_operation(Name),Names), | |
3463 | length(Names,L), | |
3464 | (L>5 -> sort(Names,Res) ; Res=Names). | |
3465 | ||
3466 | tcltk_get_vacuous_guards(list(R)) :- | |
3467 | eclipse_interface:get_vacuous_guards(L), % to do: add source column | |
3468 | (L=[] -> R = ['No vacuous guards'] | |
3469 | ; R=L). | |
3470 | ||
3471 | tcltk_get_vacuous_invariants(list(L)) :- | |
3472 | eclipse_interface:get_vacuous_invariants(LP), | |
3473 | (LP=[] -> L = ['No vacuous invariants'] | |
3474 | ; maplist(translate:translate_bexpression,LP,L)). | |
3475 | ||
3476 | tcltk_get_vacuous_invariants_table(list([list(['LHS','Op','RHS','Source'])|L])) :- | |
3477 | eclipse_interface:get_vacuous_invariants(LP), | |
3478 | LP \= [], !, | |
3479 | maplist(get_expr_and_source,LP,L). | |
3480 | tcltk_get_vacuous_invariants_table(list(['No vacuous invariants'])). | |
3481 | ||
3482 | :- use_module(translate,[translate_bexpression_with_limit/3]). | |
3483 | :- use_module(error_manager,[get_tk_table_position_info/2]). | |
3484 | get_expr_and_source(P,list([TS1,Op,TS2,Src])) :- | |
3485 | decompose(P,LHS,Op,RHS), | |
3486 | translate_bexpression_with_limit(LHS,150,TS1), | |
3487 | translate_bexpression_with_limit(RHS,60,TS2), | |
3488 | get_tk_table_position_info(P,Src). | |
3489 | ||
3490 | decompose(b(implication(LHS,RHS),pred,_),LHS,'=>',RHS) :- !. | |
3491 | decompose(b(disjunction(LHS,RHS),pred,_),LHS,'or',RHS) :- !. | |
3492 | decompose(LHS,LHS,'?',b(truth,pred,[])). | |
3493 | ||
3494 | ||
3495 | % ------------------- | |
3496 | ||
3497 | ||
3498 | :- use_module(extrasrc(mcts_game_play), [mcts_auto_play/4, mcts_auto_play_available/0]). | |
3499 | ||
3500 | tcltk_mcts_auto_play :- tcltk_mcts_auto_play(_). | |
3501 | tcltk_mcts_auto_play(TransID) :- | |
3502 | mcts_auto_play_available, | |
3503 | current_state_id(CurID), | |
3504 | mcts_auto_play(CurID,ActionAsTerm,TransID,State2), | |
3505 | (tcltk_perform_action(CurID,ActionAsTerm,TransID,State2) | |
3506 | -> true | |
3507 | ; add_error(tcltk_mcts_auto_play,'Cannot perform chosen action:',ActionAsTerm),fail). |