1 % Heinrich Heine Universitaet Duesseldorf
2 % (c) 2025-2026 Lehrstuhl fuer Softwaretechnik und Programmiersprachen,
3 % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html)
4
5 :- module(sequent_prover_exports,[export_proof_for_current_state/2, export_proof/4,
6 pretty_print_pos/1]).
7
8 :- use_module(probsrc(module_information),[module_info/2]).
9 :- module_info(group,sequent_prover).
10 :- module_info(description,'This module provides export functionality for traces created by the sequent_prover.').
11
12 :- use_module(library(lists)).
13 :- use_module(library(aggregate),[forall/2]).
14
15 :- use_module(probsrc(error_manager)).
16 :- use_module(probsrc(gensym),[gensym/2]).
17 :- use_module(probsrc(specfile),[animation_minor_mode/1,get_operation_description_for_transition_id/3]).
18 :- use_module(probsrc(state_space),[get_state_id_trace/1,history/1,op_trace_ids/1,transition/4,visited_expression/2]).
19 :- use_module(probsrc(system_call),[get_temporary_filename/2]).
20 :- use_module(probsrc(translate),[translate_bexpression_to_unicode/2,transform_raw/2,with_translation_mode/2]).
21 :- use_module(probsrc(tools),[ajoin/2,ajoin_with_sep/3,get_filename_extension/2,list_difference/3,read_string_from_file/2]).
22 :- use_module(probsrc(tools_commands),[gen_dot_output/4,valid_dot_output_format/1]).
23 :- use_module(wdsrc(well_def_hyps),[normalize_expression/2,normalize_predicate/2]).
24
25 :- use_module(sequent_prover,[cont_length/2,get_continuations/2,parse_input/3,used_identifiers/2]).
26 :- use_module(prover_utils,[translate_norm_expr_term_no_limit/2,translate_norm_expr_term/2]).
27
28 export_proof_for_current_state(Mode,File) :-
29 get_state_id_trace(StateIds),
30 op_trace_ids(TransIds),
31 reverse(TransIds,RTransIds),
32 export_proof(Mode,File,StateIds,RTransIds).
33
34 export_proof(Mode,File,StateIds,TransIds) :-
35 animation_minor_mode(sequent_prover), !,
36 get_filename_extension(File,Ext),
37 (Ext=Mode -> true
38 ; ajoin(['file extension ',Ext,' does not match the proof export mode: '],Msg),
39 add_warning(sequent_prover_exports,Msg,Mode)),
40 export_proof2(Mode,File,StateIds,TransIds).
41 export_proof(_,_,_,_) :-
42 add_error(sequent_prover_exports,'Proof export is only available in sequent prover mode.'), fail.
43
44 export_proof2(html,File,StateIds,TransIds) :- !, generate_proof_html_export(File,StateIds,TransIds).
45 export_proof2(bpr, File,StateIds,TransIds) :- !, generate_proof_bpr_rodin_export(File,StateIds,TransIds).
46 export_proof2(Mode,File,StateIds,TransIds) :-
47 valid_dot_output_format(Mode), !,
48 write_proof_tree_to_file(Mode,File,StateIds,TransIds).
49 export_proof2(Mode,_,_,_) :-
50 add_error(sequent_prover_exports,'unrecognised file extension for proof export: ',Mode), fail.
51
52 %%%%%%%%% BUILD THE PROOF TREE %%%%%%%%%
53 :- dynamic proof_node/4, proof_edge/5, proof_tree/2.
54 % proof_node(ContinuationId,sequent(Hyps,Goal),Info,DotAttrList).
55 % proof_edge(PrevId,DestId,TransId,ActTerm,Label).
56 :- dynamic current_node/1, continuation/2. % auxiliary predicates used during proof tree generation
57 % continuation(ContinuationSequent,IdWhereThisContinuationWasAdded).
58
59 build_proof_tree(StateIds,TransIds) :- proof_tree(StateIds,TransIds), !. % proof tree already created
60 build_proof_tree(StateIds,TransIds) :-
61 retractall(proof_node(_,_,_,_)),
62 retractall(proof_edge(_,_,_,_,_)),
63 retractall(proof_tree(_,_)),
64 retractall(continuation(_,_)),
65 retractall(current_node(_)),
66 build_proof_tree_aux(StateIds,TransIds),
67 assertz(proof_tree(StateIds,TransIds)).
68
69 :- use_module(library(codesio),[write_to_codes/2]).
70 build_proof_tree_aux([],_).
71 build_proof_tree_aux([_StateId],_).
72 build_proof_tree_aux([PrevId,StateId|ST],[TransId|TT]) :-
73 transition(PrevId,Act,TransId,StateId),
74 Act=skip_to_cont, !, % ignore skips (just a reordering of continuations, not visible in the proof tree)
75 build_proof_tree_aux([StateId|ST],TT).
76 build_proof_tree_aux([PrevId,StateId|ST],[TransId|TT]) :-
77 transition(PrevId,Act,TransId,StateId),
78 visited_expression(StateId,State),
79 State=state(success(SNr),_), !, % proof succeeded
80 (current_node(PrevNode) -> true ; PrevNode=root),
81 (get_operation_description_for_transition_id(PrevId,TransId,EdgeLabel) -> true ; write_to_codes(Act,CAct), atom_codes(EdgeLabel,CAct)),
82 assert_node_with_edge(PrevNode,success(SNr),[],TransId,[shape/triangle,style/filled,fillcolor/'#99BF38'],Act,EdgeLabel,GenID),
83 set_current_node(GenID),
84 build_proof_tree_aux([StateId|ST],TT).
85 build_proof_tree_aux([PrevId,StateId|ST],[TransId|TT]) :-
86 transition(PrevId,Act,TransId,StateId), % TODO: is there a way to obtain this directly from the history?
87 visited_expression(PrevId,PrevState), visited_expression(StateId,state(Sequent,NxtInfo)),
88 (PrevState=state(PrevSequent,Info)
89 -> PrevSequent=sequent(PVHyps,PVGoal,_), cont_length(PrevSequent,PrevLen)
90 ; PrevState=root, PrevLen=0, Info=NxtInfo), % for root use next info (important for des_hyps)
91 Sequent=sequent(Hyps,Goal,_),
92
93 (retract(continuation(sequent(PVHyps,PVGoal),ContID)) % previous sequent has been registered as continuation before
94 -> PrevNode=ContID
95 ; (current_node(PrevNode) -> true ; PrevNode=root)),
96
97 (get_operation_description_for_transition_id(PrevId,TransId,EdgeLabel) -> true ; write_to_codes(Act,CAct), atom_codes(EdgeLabel,CAct)),
98
99 cont_length(Sequent,Len),
100 (PrevLen>Len % sequent has been proven by the current transition and disappeared
101 -> assert_node_with_edge(PrevNode,success(-1),Info,TransId,[shape/triangle,style/filled,fillcolor/'#99BF38'],Act,EdgeLabel,_GenID)
102 ; assert_node_with_edge(PrevNode,sequent(Hyps,Goal),Info,TransId,[shape/box],Act,EdgeLabel,GenID),
103 assertz(continuation(sequent(Hyps,Goal),GenID)), % store position of current node
104 set_current_node(GenID)
105 ),
106
107 get_continuations(Sequent,Conts),
108 forall(member(Cont,Conts),assert_new_continuation(Cont,PrevNode,Info,TransId,Act,EdgeLabel)),
109 build_proof_tree_aux([StateId|ST],TT).
110
111 assert_node_with_edge(PrevNode,Sequent,Info,TransId,NodeAttr,Act,EdgeLabel,GenID) :-
112 gensym('seq_prov_',GenID),
113 assertz(proof_node(GenID,Sequent,Info,NodeAttr)),
114 assertz(proof_edge(PrevNode,GenID,TransId,Act,EdgeLabel)).
115
116 assert_new_continuation(Cont,_,_,_,_,_) :-
117 continuation(Cont,_ID), !. % is already registered
118 % TODO: what happens if the same continuation occurs more than once?
119 assert_new_continuation(Cont,PrevNode,Info,TransId,Act,EdgeLabel) :-
120 assert_node_with_edge(PrevNode,Cont,Info,TransId,[shape/box],Act,EdgeLabel,CID),
121 assertz(continuation(Cont,CID)).
122
123 set_current_node(ID) :-
124 retractall(current_node(_)),
125 assertz(current_node(ID)).
126
127 %%%%%%%%% END BUILD THE PROOF TREE %%%%%%%%%
128
129 %%%%%%%%% HELPER PREDICATES FOR SEQUENT_PROVER STATES %%%%%%%%%
130 pretty_hyps(sequent(Hyps,_),PHyps) :- maplist(translate_norm_expr_term,Hyps,PHyps).
131 pretty_goal(sequent(_,Goal),PGoal) :- translate_norm_expr_term(Goal,PGoal).
132
133
134 %%%%%%%%% BEGIN PROOF TREE DOT GRAPH %%%%%%%%%
135 :- use_module(dotsrc(dot_graph_generator), [gen_dot_graph/3,use_new_dot_attr_pred/7]).
136 generate_proof_tree_graph(File,StateIds,TransIds) :-
137 (animation_minor_mode(sequent_prover) -> true
138 ; add_error_and_fail(sequent_prover_exports,'Cannot create proof tree visualisation, not in sequent prover mode (load a .pl PO file exported by the ProB Rodin Plugin)','')),
139 build_proof_tree(StateIds,TransIds),
140 gen_dot_graph(File,
141 use_new_dot_attr_pred(sequent_prover_exports:proof_tree_graph_node_predicate),
142 use_new_dot_attr_pred(sequent_prover_exports:proof_tree_graph_trans_predicate)).
143
144 :- public proof_tree_graph_node_predicate/3.
145 proof_tree_graph_node_predicate(root,none,[shape/invtriangle,label/'Select PO']).
146 proof_tree_graph_node_predicate(NodeId,none,[id/NodeId,color/'#99BF38',label/'Proven.'|Attr]) :-
147 proof_node(NodeId,success(_),_,Attr).
148 proof_tree_graph_node_predicate(NodeId,none,[id/NodeId,color/'#99BF38',label/htmllabel(Label)|Attr]) :-
149 proof_node(NodeId,Sequent,_,Attr),
150 Sequent\=success(_), NodeId\=root,
151 (proof_edge(PrevNode,NodeId,_,_,_), proof_node(PrevNode,PrevSequent,_,_)
152 -> true
153 ; PrevSequent=Sequent), % highlight no changes
154 build_html_dot_label(PrevSequent,Sequent,Label).
155
156 build_html_dot_label(PrevSequent,Sequent,Label) :-
157 pretty_hyps(PrevSequent,PPrevHyps), pretty_hyps(Sequent,PHyps),
158 pred_changed(PHyps,PPrevHyps,ChangedPHyps),
159 (ChangedPHyps=[]
160 -> HypLabel='<TR><TD><FONT COLOR="grey"><I>no hypotheses</I></FONT></TD></TR>' % no hyps
161 ; maplist(label_for_pred,ChangedPHyps,HypLabels),
162 ajoin(HypLabels,HypLabel)),
163 pretty_goal(PrevSequent,PPrevGoal), pretty_goal(Sequent,PGoal),
164 (PPrevGoal=PGoal -> Changed=not_changed; Changed=changed),
165 label_for_pred(pred_line(PGoal,Changed),GoalLabel),
166 ajoin(['<<TABLE BORDER="0" CELLBORDER="0" COLOR="black">', HypLabel, '<HR/>', GoalLabel, '</TABLE>>'],Label).
167
168 label_for_pred(pred_line(Pred,Changed),PredLabel) :-
169 tools:html_escape(Pred,EPred),
170 (Changed=changed
171 -> ajoin(['<TR><TD ALIGN="LEFT"><FONT COLOR="#de8000"><B>',EPred,'</B></FONT></TD></TR>'],PredLabel)
172 ; ajoin(['<TR><TD ALIGN="LEFT"><FONT COLOR="black">',EPred,'</FONT></TD></TR>'],PredLabel)
173 ).
174
175 pred_changed([],_,[]).
176 pred_changed([Hyp|HT],[],[pred_line(Hyp,not_changed)|RT]) :- pred_changed(HT,[],RT).
177 pred_changed([Hyp|HT],PrevHyps,[pred_line(Hyp,Changed)|RT]) :-
178 (selectchk(Hyp,PrevHyps,NPrevHyps)
179 -> Changed=not_changed
180 ; Changed=changed, NPrevHyps=PrevHyps),
181 pred_changed(HT,NPrevHyps,RT).
182
183 :- public proof_tree_graph_trans_predicate/3.
184 proof_tree_graph_trans_predicate(PrevId,SuccId,[label/Label]) :-
185 proof_edge(PrevId,SuccId,_TransId,_Act,Label).
186
187 write_proof_tree_to_file(Mode,File,StateIds,TransIds) :-
188 get_temporary_filename('_dot_proof_tree.dot',DotFile),
189 generate_proof_tree_graph(DotFile,StateIds,TransIds),
190 gen_dot_output(DotFile,dot,Mode,File).
191 %%%%%%%%% END PROOF TREE DOT GRAPH %%%%%%%%%
192
193 %%%%%%%%% BEGIN PROOF TREE HTML EXPORT %%%%%%%%%
194 :- dynamic proof_html_export_template_file_codes/2.
195
196 assert_from_template(Filename) :-
197 absolute_file_name(seqproversrc(Filename), Absolute, []),
198 read_string_from_file(Absolute,String),
199 assertz(proof_html_export_template_file_codes(Filename,String)).
200
201 write_proof_html_export_template(HtmlFile,Stream) :-
202 proof_html_export_template_file_codes(HtmlFile,Codes),
203 format(Stream,'~s~n',[Codes]).
204
205 :- assert_from_template('proof_html_export_header.html').
206 :- assert_from_template('proof_html_export_footer.html').
207
208 % TODO: maybe highlight selected step in SVG; allow to select a step by clicking in the proof tree
209 :- use_module(probsrc(preferences),[get_preference/2]).
210 :- use_module(probsrc(xtl_interface),[xtl_main_file_name/1]).
211 generate_proof_html_export(File,StateIds,TransIds) :-
212 (animation_minor_mode(sequent_prover) -> true
213 ; add_error_and_fail(sequent_prover_exports,'Cannot create proof export, not in sequent prover mode (load a .pl PO file exported by the ProB Rodin Plugin)','')),
214 build_proof_tree(StateIds,TransIds), % important for step_list
215 retractall(trans_id_done(_)),
216 retractall(html_step_nr(_)),
217 assertz(html_step_nr(1)),
218
219 tools_io:safe_open_file(File,write,Stream,[encoding(utf8)]),
220 write_proof_html_export_template('proof_html_export_header.html',Stream),
221 xtl_main_file_name(POFile),
222 (proof_edge(root,_,_,_,POLabel)
223 -> format(Stream,' <div>Proof Trace Export for <b>~w</b> from <pre>~w</pre></div>~n',[POLabel,POFile])
224 ; format(Stream,' <div>Proof Trace Export from ~w</div>~n',[POFile])),
225 format(Stream,' <hr/>~n',[]),
226 format(Stream,' <div id="boxContainer" style="display: flex; width: 100%; position:fixed; height: 90vh;">~n',[]),
227 format(Stream,' <div id="stepListBox" style="width: 30%; display: flex; flex-direction: column;">~n',[]),
228 gen_step_list(Stream,TransIds),
229 write_generation_info(Stream),
230 format(Stream,' </div>~n',[]),
231 format(Stream,' <div id="divider" class="divider"> </div>~n',[]),
232 format(Stream,' <div id="treeBox" style="width: 66.5%; height: 97%; margin-left: 5px;">~n',[]),
233 format(Stream,' <div class="box-caption">Proof Tree</div>~n',[]),
234 format(Stream,' <div text-align="left" id="proof_tree_svg_outer_container" class="svg-outer-container">~n',[]),
235 format(Stream,' <div text-align="left" id="proof_tree_svg_inner_container" class="svg-inner-container">~n',[]),
236 write_proof_tree_svg_to_stream(Stream,StateIds,TransIds), % generate proof tree dot graph (with ProB dot preferences)
237 format(Stream,' </div>~n',[]),
238 format(Stream,' </div>~n',[]),
239 format(Stream,' <button id="btnResetScale" class="visualisation-button" onclick="resetScale()">Reset View</button>~n',[]),
240 format(Stream,' </div>~n',[]), % close tree box
241 format(Stream,' </div>~n',[]), % close box container
242 write_proof_html_export_template('proof_html_export_footer.html',Stream),
243 close(Stream).
244
245 :- use_module(probsrc(tools_io),[write_file_to_stream/2]).
246 write_proof_tree_svg_to_stream(Stream,StateIds,TransIds) :-
247 get_temporary_filename('_dot_proof_tree.svg',SvgFile),
248 write_proof_tree_to_file(svg,SvgFile,StateIds,TransIds),
249 write_file_to_stream(SvgFile,Stream).
250
251 :- dynamic trans_id_done/1, html_step_nr/1.
252 gen_step_list(Stream,TransIds) :-
253 format(Stream,' <div class="box-caption" style="display: flex; justify-content: space-between;">Proof Steps~n',[]),
254 format(Stream,' <div>~n',[]),
255 format(Stream,' <button class="step-button" onclick="selectPrevStep()">< Previous Step</button>~n',[]),
256 format(Stream,' <button class="step-button" onclick="selectNextStep()">Next Step ></button>~n',[]),
257 format(Stream,' <button class="step-button" onclick="runAll()">Run Steps</button>~n',[]),
258 format(Stream,' </div>~n',[]),
259 format(Stream,' </div>~n',[]),
260 format(Stream,' <div class="step-list">~n',[]),
261 gen_step_list_entry(Stream,TransIds),
262 format(Stream,' </div>~n',[]).
263 gen_step_list_entry(Stream,TransIds) :-
264 member(TransId,TransIds),
265 proof_edge(PrevId,_Id,TransId,_,Label),
266 \+ PrevId=root,
267 \+ trans_id_done(TransId),
268 findall(Id,proof_edge(_,Id,TransId,_,_),SuccIds),
269 ajoin_with_sep([PrevId|SuccIds],'\',\'',IDList),
270 format(Stream,' <div class="step-header green" onclick="focusSequents(\'trans_~w\',[\'~w\'])">~n',[TransId,IDList]),
271 retract(html_step_nr(SNr)),
272 NSNr is SNr+1,
273 assertz(html_step_nr(NSNr)),
274 format(Stream,' <b>~w. ~w</b>~n',[SNr,Label]),
275 format(Stream,' </div>~n',[]),
276 % TODO: here we could display more proof step details in the table (then an icon for extended/not-extended would be nice):
277 format(Stream,' <div class="step-content" id="trans_~w">~n',[TransId]),
278 % format(Stream,' <b>_TODO_</b>~n',[]),
279 format(Stream,' </div>~n',[]),
280 assertz(trans_id_done(TransId)),
281 fail.
282 gen_step_list_entry(_,_).
283
284 :- use_module(library(system),[datime/1]).
285 :- use_module(probsrc(tools_strings),[number_codes_min_length/3]).
286 :- use_module(probsrc(version),[version_str/1,revision/1]).
287 write_generation_info(Stream) :-
288 version_str(VStr), revision(VRev),
289 format(Stream,' <div style="color: grey; font-size: 0.7rem;"><br>ProB Version: ~w (~w)~n',[VStr,VRev]),
290 datime(datime(Yr,Mon,Day,Hr,Min,_Sec)),
291 number_codes_min_length(Mon,2,MonC), number_codes_min_length(Min,2,MinC),
292 format(Stream,' <br>Generated on ~w/~s/~w at ~w:~s</div>~n',[Day,MonC,Yr,Hr,MinC]).
293 %%%%%%%%% END PROOF TREE HTML EXPORT %%%%%%%%%
294
295 % TODO: bpr_import? difficult as some details can be left out to the bpo file
296 %%%%%%%%% BEGIN PROOF TREE RODIN BPR EXPORT %%%%%%%%%
297 :- dynamic predicate_id_count/1, expression_id_count/1, rule_id_count/1.
298 :- dynamic prPred/3, prExpr/3, prIdent/2, prReas/2.
299
300 generate_proof_bpr_rodin_export(File,StateIds,TransIds) :-
301 (animation_minor_mode(sequent_prover)
302 -> add_message(sequent_prover_exports,'The BPR Rodin proof export is experimental and may contain errors!')
303 ; add_error_and_fail(sequent_prover_exports,'Cannot create proof export, not in sequent prover mode (load a .pl PO file exported by the ProB Rodin Plugin)','')),
304 build_proof_tree(StateIds,TransIds),
305 retractall(prPred(_,_,_)),
306 retractall(prExpr(_,_,_)),
307 retractall(prIdent(_,_)),
308 retractall(prReas(_,_)),
309 retractall(predicate_id_count(_)),
310 retractall(expression_id_count(_)),
311 retractall(rule_id_count(_)),
312 assertz(predicate_id_count(0)),
313 assertz(expression_id_count(0)),
314 assertz(rule_id_count(0)),
315 tools_io:safe_open_file(File,write,Stream,[encoding(utf8)]),
316 generate_bpr_to_stream(Stream,StateIds,TransIds),
317 % TODO: handle existing BPR file: replace only one proof, add (one/multiple) proofs to the file, ...
318 close(Stream).
319
320 % general idea of the translation: each proof_edge becomes a prRule, with a prAnte for each proof_node (continuation)
321 generate_bpr_to_stream(Stream,StateIds,TransIds) :-
322 format(Stream,'<?xml version="1.0" encoding="UTF-8" standalone="no"?>~n',[]),
323 format(Stream,'<org.eventb.core.prFile version="1">~n',[]),
324 (TransIds\=[] -> generate_bpr_proof_tree(Stream,StateIds,TransIds) ; true),
325 format(Stream,'</org.eventb.core.prFile>~n',[]), flush_output(Stream).
326
327 :- use_module(sequent_prover,[get_scope/3,po_nr_label/2,normalised_hyps/3,normalised_goal/3]).
328 generate_bpr_proof_tree(Stream,StateIds,TransIds) :-
329 StateIds=[root,S1|_], TransIds=[T1|_],
330 transition(root,start_xtl_system,T1,S1), % extract first state to determine selected PO
331 visited_expression(S1,state(sequent(NormHyps,NormGoal,success(PONr)),Info)),
332
333 member(des_hyps(DesHyps),Info),
334 append(DesHyps,NormHyps,AllHyps),
335 get_scope([NormGoal|AllHyps],Info,[identifier(Ids)]),
336 forall(member(Id,Ids),assert_identifier(Id)), % register global IDs known in the initial step (including goal)
337
338 po_nr_label(PONr,POLabel),
339 normalised_hyps(POLabel,RawPreds,NormPreds),
340 normalised_goal(POLabel,RawGoal,NormGoal),
341 get_prob_normalisation_rewrites(RawPreds,NormPreds,HypRewrites),
342 get_prob_normalisation_rewrites([RawGoal],[NormGoal],GoalRewr),
343 (GoalRewr=[hyp_action(rewrite,G1,G2)] -> true ; assert_hyp_predicate(NormGoal,G1), G1=G2), % rewrite goal if necessary
344
345 proof_edge(root,FirstNode,_,_,_),
346 format_indent(Stream,'<org.eventb.core.prProof name="~w" org.eventb.core.confidence="1000" org.eventb.core.prFresh="" org.eventb.core.prGoal="" org.eventb.core.prHyps="" org.eventb.core.psManual="true">~n',[POLabel],1),
347 format_indent(Stream,'<org.eventb.core.lang name="L"/>~n',[],2),
348 (HypRewrites=[]
349 -> write_proof_rule(Stream,FirstNode,2) % no ProB AST normalisation
350 ; assert_rule_index('org.eventb.core.seqprover.review',RID), % TODO: disable normalisation/add as reasoner?
351 format_indent(Stream,'<org.eventb.core.prRule name="~w" org.eventb.core.confidence="1000" org.eventb.core.prDisplay="ProB AST normalisation" org.eventb.core.prGoal="~w" org.eventb.core.prHyps="">~n',[RID,G1],2),
352 get_next_rule_comment(FirstNode,CommentCodes),
353 format_indent(Stream,'<org.eventb.core.prAnte name="\'" org.eventb.core.comment="~s" org.eventb.core.prGoal="~w">~n',[CommentCodes,G2],3),
354 write_hyp_actions(Stream,HypRewrites,0,4),
355 write_proof_rule(Stream,FirstNode,4),
356 format_indent(Stream,'</org.eventb.core.prAnte>~n',[],3),
357 format_indent(Stream,'</org.eventb.core.prRule>~n',[],2)),
358
359 findall('$'(Id),member(b(identifier(Id),_,_),Ids),IdsOfFirstStep),
360 write_identifiers(Stream,global,IdsOfFirstStep,2),
361 write_predicate_mapping(Stream,2),
362 write_expression_mapping(Stream,2),
363 write_reasoner_mapping(Stream,2),
364 format_indent(Stream,'</org.eventb.core.prProof>~n',[],1).
365
366 write_proof_rule(Stream,FromNode,Indent) :-
367 proof_edge(FromNode,_,_,Act,Desc), !, % generate only one prRule
368 get_rule_reasoner(Act,ReasID,RID,Conf), % TODO: add option to use without Rodin reasoner IDs?
369 proof_node(FromNode,sequent(Hyps,Goal),_,_),
370 get_hyp_ids(Act,Hyps,HypIds),
371 get_expr_ids(Act,ExprIds),
372 get_pred_ids(Act,PredIds),
373 % TODO: is it useful to print enum sets?, e.g. org.eventb.core.prSets="Beverages" (but works without)
374 get_goal_id(Act,ReasID,Goal,GoalStr),
375 xml_attribute_escape_atom(Desc,EDesc),
376 format_indent(Stream,'<org.eventb.core.prRule name="~w" org.eventb.core.confidence="~w" org.eventb.core.prDisplay="~s" ~w org.eventb.core.prHyps="~w">~n',[RID,Conf,EDesc,GoalStr,HypIds],Indent),
377 write_proof_rule2(Stream,FromNode,ReasID,ExprIds,PredIds,Indent).
378 write_proof_rule(_,_,_). % write nothing, this is an open antecedent without rule
379 write_proof_rule2(Stream,FromNode,_,_,_,Indent) :-
380 proof_edge(FromNode,NextNode,_,Act,_Desc),
381 proof_node(FromNode,sequent(Hyps,_Goal),FromInfo,_),
382 (member(des_hyps(DesHyps),FromInfo) -> true ; DesHyps=[]),
383 (proof_node(NextNode,sequent(NHyps,_NGoal),_,_)
384 -> compute_hyp_actions_for_rule(DesHyps,Hyps,NHyps,HypActions0),
385 adapt_hyp_actions_for_rule(Act,HypActions0,HypActions)
386 ; HypActions=[]), % for success(_)
387 AnteIndent is Indent+1,
388 write_antecedant(Stream,FromNode,NextNode,HypActions,AnteIndent),
389 fail.
390 write_proof_rule2(Stream,_,_,prExprRef(RefType,ExprIds),_,Indent) :- % RefType can be exprs or subst, ... depending on the rule
391 ExprIds \= [],
392 ajoin_with_sep(ExprIds,',',SepExprIds),
393 AnteIndent is Indent+1,
394 format_indent(Stream,'<org.eventb.core.prExprRef name=".~w" org.eventb.core.prRef="~w"/>~n',[RefType,SepExprIds],AnteIndent),
395 fail.
396 write_proof_rule2(Stream,_,_,_,prPredRef(RefType,PredIds),Indent) :-
397 PredIds \= [],
398 ajoin_with_sep(PredIds,',',SepPredIds),
399 AnteIndent is Indent+1,
400 format_indent(Stream,'<org.eventb.core.prPredRef name=".~w" org.eventb.core.prRef="~w"/>~n',[RefType,SepPredIds],AnteIndent),
401 fail.
402 write_proof_rule2(Stream,_,ReasID,_,_,Indent) :-
403 rule_requires_pos(ReasID,PosNr),
404 AnteIndent is Indent+1,
405 format_indent(Stream,'<org.eventb.core.prString name=".pos" org.eventb.core.prSValue="~w"/>~n',[PosNr],AnteIndent),
406 fail.
407 write_proof_rule2(Stream,_,_,_,_,Indent) :- format_indent(Stream,'</org.eventb.core.prRule>~n',[],Indent).
408
409 % prRule WITHOUT org.eventb.core.prHyps="" (null hyps) is not valid!
410 get_hyp_ids(and_l(Pred),_,HypIds) :- !, assert_hyp_predicate(Pred,HypIds). % provide only the affected Hyp, then the rule expects a forward_inf as first rewrite
411 get_hyp_ids(and_r,_,HypIds) :- !, HypIds=''. % AND_R expects null hyps
412 get_hyp_ids(eq(_,L,R),_,HypIds) :- !, assert_hyp_predicate(equal(L,R),HypIds). % provide the exact hypId
413 get_hyp_ids(imp_case(Pred),_,HypIds) :- !, assert_hyp_predicate(Pred,HypIds). % provide only the affected Hyp
414 get_hyp_ids(simplify_goal(_),_,HypIds) :- !, HypIds=''.
415 get_hyp_ids(simplify_hyp(_,Hyp),_,HypIds) :- !, assert_hyp_predicate(Hyp,HypIds).
416 get_hyp_ids(Rule,Hyps,HypIds) :-
417 rule_with_hyp_inst(Rule,HypNr,_), !,
418 nth1(HypNr,Hyps,ForallHyp),
419 assert_hyp_predicate(ForallHyp,HypIds).
420 get_hyp_ids(_,Hyps,SepHypIds) :-
421 maplist(assert_hyp_predicate,Hyps,HypIds),
422 ajoin_with_sep(HypIds,',',SepHypIds).
423
424 get_expr_ids(Rule,ExprIds) :-
425 rule_with_expr(Rule,Expr,Kind), !,
426 parse_input(Expr,Rule,TExpr),
427 normalize_expression(TExpr,NormExpr),
428 assert_expression(NormExpr,ExprId),
429 ExprIds = prExprRef(Kind,[ExprId]).
430 get_expr_ids(Rule,ExprIds) :-
431 RName='DERIV_DOM_TOTALREL'(DomExpr),
432 (Rule=simplify_hyp(RName,_) ; Rule=simplify_goal(RName)), !,
433 assert_expression(DomExpr,ExprId), % value of the domain.
434 ExprIds = prExprRef(subst,[ExprId]).
435 get_expr_ids(_,prExprRef(none,[])).
436
437 % TODO: add more rules with user input
438 rule_with_expr(exists_inst(Inst),Expr,Kind) :- !, Expr=Inst, Kind=exprs.
439 rule_with_expr(Rule,Expr,Kind) :- rule_with_hyp_inst(Rule,_,Inst), !, Expr=Inst, Kind=exprs.
440 rule_with_expr(Rule,Expr,Kind) :- rule_with_pfun_input(Rule,PFun), !, Expr=PFun, Kind=expr.
441
442 rule_with_hyp_inst(forall_inst(HypNr,Inst),HypNr,Inst).
443 rule_with_hyp_inst(forall_inst_mp(HypNr,Inst),HypNr,Inst).
444 rule_with_hyp_inst(forall_inst_mt(HypNr,Inst),HypNr,Inst).
445
446 % extensions of the PFunSetInputReasoner
447 rule_with_pfun_input(fin_fun_dom_r(PFun),PFun).
448 rule_with_pfun_input(fin_fun_img_r(PFun),PFun).
449 rule_with_pfun_input(fin_fun_ran_r(PFun),PFun).
450 rule_with_pfun_input(fin_fun1_r(PFun),PFun).
451 rule_with_pfun_input(fin_fun2_r(PFun),PFun).
452
453 get_pred_ids(Rule,PredIds) :-
454 rule_with_pred(Rule,Pred,Kind), !,
455 parse_input(Pred,Rule,TPred),
456 normalize_predicate(TPred,NormPred),
457 sequent_prover:translate_finite_expr(NormPred,NewPred),
458 assert_hyp_predicate(NewPred,PredId),
459 PredIds = prPredRef(Kind,[PredId]).
460 get_pred_ids(_,prPredRef(none,[])).
461
462 rule_with_pred(add_hyp(Hyp),Pred,Kind) :- !, Pred=Hyp, Kind=pred.
463 rule_with_pred(distinct_case(P),Pred,Kind) :- !, Pred=P, Kind=pred.
464
465 % prRule WITH org.eventb.core.prGoal="" is not a valid null goal!
466 get_goal_id(cntr,_,_,GoalStr) :- !, GoalStr=''. % CNTR produces null goal
467 get_goal_id(imp_case(_),_,_,GoalStr) :- !, GoalStr=''. % is goal independent
468 get_goal_id(simplify_hyp(_,_),ReasID,_,GoalStr) :- \+ rodin_review_reasoner(ReasID), !, GoalStr=''. % is goal independent if not reviewed
469 get_goal_id(_,_,Goal,GoalStr) :-
470 assert_hyp_predicate(Goal,GID), % should already be registered, get ID only
471 ajoin(['org.eventb.core.prGoal="',GID,'"'],GoalStr).
472
473 rodin_review_reasoner('org.eventb.core.seqprover.review').
474
475 rule_requires_pos('org.eventb.core.seqprover.ri',''). % TODO: extend list
476 rule_requires_pos('org.eventb.core.seqprover.riUniversal','').
477 rule_requires_pos('org.eventb.core.seqprover.rmL2','').
478 rule_requires_pos('org.eventb.core.seqprover.rn','').
479 rule_requires_pos('org.eventb.core.seqprover.setEqlRewrites','').
480 rule_requires_pos('org.eventb.core.seqprover.totalDom:2','1'). % always 1?
481
482 compute_hyp_actions_for_rule(_,[],[],Res) :- !, Res=[].
483 compute_hyp_actions_for_rule(DesHyps,[],[NewHyp|NT],Res) :-
484 !, % there are new hyps after the existing ones -> SELECT or new_hyp
485 select_hyp(DesHyps,NewHyp,Act),
486 compute_hyp_actions_for_rule(DesHyps,[],NT,Acts),
487 Res=[Act|Acts].
488 compute_hyp_actions_for_rule(_,PrevHyps,CurHyps,HypActs) :-
489 select(DesHyp,PrevHyps,CurHyps), !, % one hypothesis has been removed -> DESELECT
490 deselect_hyp(DesHyp,Act),
491 HypActs=[Act].
492 compute_hyp_actions_for_rule(DesHyps,PrevHyps,CurHyps,HypActs) :-
493 append(PrevHyps,AddHyps,CurHyps), !, % only new hypotheses have been added -> SELECT or new_hyp
494 maplist(select_hyp(DesHyps),AddHyps,HypActs).
495 compute_hyp_actions_for_rule(DesHyps,[PHyp|PT],[CHyp|CT],HypActs) :-
496 PHyp\=CHyp, !,
497 (PT=[NHyp|_], CT=[NHyp|_] % hyp has been rewritten; after that, the sequent is the same -> REWRITE
498 -> rewrite_hyp(PHyp,CHyp,Act0), HypActs=[Act0|HypActs1],
499 compute_hyp_actions_for_rule(DesHyps,PT,CT,HypActs1)
500 ; (PT=[CHyp|_] % previous hyp has been deselected and replaced (next prev hyp is the current) -> DESELECT
501 -> deselect_hyp(PHyp,Act1), HypActs=[Act1|HypActs1],
502 compute_hyp_actions_for_rule(DesHyps,PT,[CHyp|CT],HypActs1)
503 ; deselect_hyp(PHyp,Act2), select_hyp(DesHyps,CHyp,Act3), HypActs=[Act2,Act3|HypActs1],
504 compute_hyp_actions_for_rule(DesHyps,PT,CT,HypActs1))
505 % last row changed: we cannot determine if the action is a REWRITE -> first DESELECT previous hyp, than SELECT (or new_hyp) the current hyp
506 ).
507 compute_hyp_actions_for_rule(DesHyps,[PHyp|PT],[CHyp|CT],Acts) :-
508 PHyp=CHyp, % hyps are the same, no action
509 compute_hyp_actions_for_rule(DesHyps,PT,CT,Acts).
510
511 % Important: an action is only applicable if the EXACT hypothesis exists in the current step in Rodin! Otherwise, the action fails silently.
512 deselect_hyp(Hyp,hyp_action(deselect,HypID,HypID)) :- assert_hyp_predicate(Hyp,HypID). % TODO check Hyp is member of selected Hyps?
513 rewrite_hyp(Hyp1,Hyp2,hyp_action(rewrite,Hyp1ID,Hyp2ID)) :- % TODO check Hyp is member of de/selected Hyps?
514 assert_hyp_predicate(Hyp1,Hyp1ID),
515 assert_hyp_predicate(Hyp2,Hyp2ID).
516 select_hyp(DesHyps,Hyp,hyp_action(Act,HypID,HypID)) :-
517 (member(Hyp,DesHyps) -> Act=select ; Act=new_hyp), % check that hyp to be selected is in the set of deselected hyps, otherwise it must be added as a new hyp
518 assert_hyp_predicate(Hyp,HypID). % SELECT can fail if hyp is not available, but new_hyp is always ok (but then Rodin does not display the selection in the proof step details)
519
520 % adapt some hyp actions for rules that expect special actions:
521 adapt_hyp_actions_for_rule(_,[],Res) :- !, Res=[].
522 adapt_hyp_actions_for_rule(and_l(_),OldActs,NewActs) :- !, % expects a rewrite as first action for the selected conjunction
523 OldActs=[hyp_action(deselect,Des,Des),hyp_action(new_hyp,N1,N1),hyp_action(new_hyp,N2,N2)],
524 ajoin([N1,',',N2],Rewr),
525 NewActs=[hyp_action(rewrite,Des,Rewr),hyp_action(select,Rewr,Rewr)].
526 adapt_hyp_actions_for_rule(eq(_,_,_),OldActs,NewActs) :- !, % replace rewrites by forwards infs
527 maplist(replace_rewrite_forward_inf,OldActs,NewActs).
528 % TODO this does not cover the case if the rewritten hyps are at the end of the hyp list -> DESELECT + new_hyp (but this is also accepted by Rodin)
529 adapt_hyp_actions_for_rule(simplify_hyp(Rule,Hyp),OldActs,NewActs) :-
530 remove_membership_id(Rule), % expects a rewrite as first action
531 OldActs\=[hyp_action(rewrite,_,_)|_], !, % no replacement required if the first hyp action is already a rewrite
532 assert_hyp_predicate(Hyp,HypId),
533 member(hyp_action(deselect,HypId,HypId),OldActs), % HypId is previous hyp
534 findall(N,member(hyp_action(new_hyp,N,N),OldActs),InfHyps), % all new inferred hyps, may contain other new hyps (is ok)
535 ajoin_with_sep(InfHyps,',',SepInfHyps),
536 % replace new_hyp + deselect by rewrite + select
537 NewActs=[hyp_action(rewrite,HypId,SepInfHyps),hyp_action(select,SepInfHyps,SepInfHyps)].
538 adapt_hyp_actions_for_rule(RuleTerm,[HypAct|HT],[HypAct|NH]) :-
539 adapt_hyp_actions_for_rule(RuleTerm,HT,NH).
540
541 replace_rewrite_forward_inf(hyp_action(rewrite,H1,H2),Res) :- !, Res=hyp_action(forward_inf,H1,H2).
542 replace_rewrite_forward_inf(Act,Act).
543 % in Java: if hideOriginal: rewrite, else: forward_inf
544
545 write_antecedant(_,_,Node,_,_) :- proof_node(Node,success(_),_,_), !.
546 write_antecedant(Stream,FromNode,Node,HypActions,Indent) :-
547 proof_node(Node,sequent(Hyps,Goal),Info,_),
548 get_scope([Goal|Hyps],Info,[identifier(Ids)]),
549 (FromNode \= root
550 -> proof_node(FromNode,sequent(FromHyps,FromGoal),FromInfo,_),
551 get_scope([FromGoal|FromHyps],FromInfo,[identifier(FromIds)]),
552 list_difference(Ids,FromIds,NewIds) % find new IDs in scope, e.g. after all_r (free a variable of forall)
553 ; NewIds=[]),
554 forall(member(Hyp,Hyps),assert_hyp_predicate(Hyp,_)),
555 assert_hyp_predicate(Goal,GName),
556 findall(NewHyp,member(hyp_action(new_hyp,NewHyp,_),HypActions),NewHypIds),
557 ajoin_with_sep(NewHypIds,',',SepNewHypIds),
558 get_next_rule_comment(Node,CommentCodes),
559 format_indent(Stream,'<org.eventb.core.prAnte name="~w" org.eventb.core.comment="~s" org.eventb.core.prGoal="~w" org.eventb.core.prHyps="~w">~n',[Node,CommentCodes,GName,SepNewHypIds],Indent),
560 % each prAnte of the same prRule must have a different name, TODO: check if prAnte name can really be arbitrary
561 NIndent is Indent+1,
562 write_hyp_actions(Stream,HypActions,0,NIndent),
563 write_proof_rule(Stream,Node,NIndent),
564 forall(member(Id,NewIds),assert_identifier(Id)),
565 findall('$'(NewId),member(b(identifier(NewId),_,_),NewIds),NewIdsForPrint),
566 write_identifiers(Stream,local,NewIdsForPrint,NIndent), % add new free identifiers in the corresponding prAnte, expected e.g. for all_r
567 % TODO: check purpose of org.eventb.core.prFresh=""
568 format_indent(Stream,'</org.eventb.core.prAnte>~n',[],Indent).
569
570 get_next_rule_comment(FromNode,EDesc) :-
571 proof_edge(FromNode,_,_,_,Desc), !,
572 xml_attribute_escape_atom(Desc,EDesc).
573 get_next_rule_comment(_,[]).
574
575 write_hyp_actions(_,[],_,_).
576 write_hyp_actions(Stream,[hyp_action(new_hyp,_,_)|T],Nr,Indent) :- !, write_hyp_actions(Stream,T,Nr,Indent). % ignore new_hyps, these are added as prHyps
577 write_hyp_actions(Stream,[HypAct|T],Nr,Indent) :-
578 write_hyp_action(Stream,HypAct,Nr,Indent),
579 NNr is Nr+1, % number is important for the order of actions, e.g. REWRITE0, SELECT1, FORWARD_INF2, DESELECT3, ...
580 write_hyp_actions(Stream,T,NNr,Indent).
581
582 write_hyp_action(Stream,hyp_action(deselect,I1,_),Nr,Indent) :- !,
583 format_indent(Stream,'<org.eventb.core.prHypAction name="DESELECT~w" org.eventb.core.prHyps="~w"/>~n',[Nr,I1],Indent).
584 write_hyp_action(Stream,hyp_action(forward_inf,I1,I2),Nr,Indent) :- !, % TODO: clarify purpose
585 format_indent(Stream,'<org.eventb.core.prHypAction name="FORWARD_INF~w" org.eventb.core.prHyps="~w" org.eventb.core.prInfHyps="~w"/>~n',[Nr,I1,I2],Indent).
586 write_hyp_action(Stream,hyp_action(hide,I1,_),Nr,Indent) :- !, % TODO: difference to deselect?
587 format_indent(Stream,'<org.eventb.core.prHypAction name="HIDE~w" org.eventb.core.prHyps="~w"/>~n',[Nr,I1],Indent).
588 write_hyp_action(Stream,hyp_action(rewrite,I1,I2),Nr,Indent) :- !,
589 format_indent(Stream,'<org.eventb.core.prHypAction name="REWRITE~w" org.eventb.core.prHidden="~w" org.eventb.core.prHyps="" org.eventb.core.prInfHyps="~w"/>~n',[Nr,I1,I2],Indent). % !! fails without empty prHyps !!
590 write_hyp_action(Stream,hyp_action(select,I1,_),Nr,Indent) :- !,
591 format_indent(Stream,'<org.eventb.core.prHypAction name="SELECT~w" org.eventb.core.prHyps="~w"/>~n',[Nr,I1],Indent).
592 write_hyp_action(Stream,hyp_action(show,I1,_),Nr,Indent) :- !, % TODO: difference to select?
593 format_indent(Stream,'<org.eventb.core.prHypAction name="SHOW~w" org.eventb.core.prHyps="~w"/>~n',[Nr,I1],Indent).
594
595 get_rule_reasoner(Act,ReasID,RID,Conf) :-
596 rule_reasoner(Act,ReasID,Conf),
597 assert_rule_index(ReasID,RID).
598
599 rule_reasoner(Act,ReasID,Conf) :-
600 rodin_rule_reasoner(Act,SeqProvID), !,
601 ajoin(['org.eventb.core.seqprover.',SeqProvID],ReasID),
602 Conf=100. % is UNCERTAIN_MAX, will trigger replay
603 % TODO: same for ML/PP (but the call in Rodin differs from ours; all vs. selected hyps)
604 rule_reasoner(ml,ReasID,Conf) :- !, ReasID='com.clearsy.atelierb.provers.core.externalML:1', Conf=100.
605 rule_reasoner(prob_disprover,ReasID,Conf) :- !, ReasID='de.prob.eventb.disprover.core.disproverReasoner', Conf=100.
606 rule_reasoner(_,'org.eventb.core.seqprover.review',1000). % review reasoner as fallback, TODO: should be confidence 500
607
608 % TODO complete list
609 rodin_rule_reasoner(add_hyp(_),cut).
610 rodin_rule_reasoner(all_r,allI).
611 rodin_rule_reasoner(and_l(_),conjF).
612 rodin_rule_reasoner(and_r,'conj:0').
613 rodin_rule_reasoner(auto_mh,autoImpF).
614 rodin_rule_reasoner(card_empty_interv,cardUpTo).
615 rodin_rule_reasoner(card_interv,cardUpTo).
616 rodin_rule_reasoner(case(_),disjE).
617 rodin_rule_reasoner(cntr,contrHyps).
618 rodin_rule_reasoner(contradict_l(_),contrL1).
619 rodin_rule_reasoner(contradict_r,contrL1).
620 rodin_rule_reasoner(dbl_hyp,mngHyp).
621 rodin_rule_reasoner(def_expn_step(_),exponentiationStep).
622 rodin_rule_reasoner(deriv_equal_card,cardComparison).
623 rodin_rule_reasoner(deriv_ge_card,cardComparison).
624 rodin_rule_reasoner(deriv_gt_card,cardComparison).
625 rodin_rule_reasoner(deriv_le_card,cardComparison).
626 rodin_rule_reasoner(deriv_lt_card,cardComparison).
627 rodin_rule_reasoner(deriv_equal_interv_l,derivEqualInterv).
628 rodin_rule_reasoner(dis_binter_l,'funInterImg:1').
629 rodin_rule_reasoner(dis_binter_r,'funInterImg:1').
630 rodin_rule_reasoner(dis_setminus_l,'funSetMinusImg:1').
631 rodin_rule_reasoner(dis_setminus_r,'funSetMinusImg:1').
632 rodin_rule_reasoner(distinct_case,doCase).
633 rodin_rule_reasoner(eq(lr,_,_),'eqL2:1').
634 rodin_rule_reasoner(eq(rl,_,_),'heL2:1').
635 rodin_rule_reasoner(eqv(lr,_,_),eqvLR).
636 rodin_rule_reasoner(eqv(rl,_,_),eqvRL).
637 rodin_rule_reasoner(exists_inst(_),exI).
638 rodin_rule_reasoner(false_hyp,falseHyp).
639 rodin_rule_reasoner(fin_binter_r,finiteInter).
640 rodin_rule_reasoner(fin_bunion_r,finiteUnion).
641 rodin_rule_reasoner(fin_compset_r,finiteCompset).
642 rodin_rule_reasoner(fin_fun_dom_r(_),'finiteFunDom:0').
643 rodin_rule_reasoner(fin_fun_img_r(_),'finiteFunRelImg:0').
644 rodin_rule_reasoner(fin_fun_ran_r(_),'finiteFunRan:0').
645 rodin_rule_reasoner(fin_fun1_r(_),'finiteFunction:0').
646 rodin_rule_reasoner(fin_fun2_r(_),'finiteFunConv:0').
647 rodin_rule_reasoner(fin_ge_0,finitePositive).
648 rodin_rule_reasoner(fin_kinter_r,finiteInter).
649 rodin_rule_reasoner(fin_kunion_r,finiteUnion).
650 rodin_rule_reasoner(fin_l_lower_bound,finiteHypBoundedGoal). % two rules in Rodin: L/R
651 rodin_rule_reasoner(fin_l_upper_bound,finiteHypBoundedGoal). % two rules in Rodin: L/R
652 rodin_rule_reasoner(fin_lt_0,finiteNegative).
653 rodin_rule_reasoner(fin_qinter_r,finiteInter).
654 rodin_rule_reasoner(fin_qunion_r,finiteUnion).
655 rodin_rule_reasoner(fin_rel_img_r,finiteRelImg).
656 rodin_rule_reasoner(fin_rel_r,'finiteRelation:0').
657 rodin_rule_reasoner(fin_rel_dom_r,finiteDom).
658 rodin_rule_reasoner(fin_rel_ran_r,finiteRan).
659 rodin_rule_reasoner(fin_setminus_r,finiteSetMinus).
660 rodin_rule_reasoner(fin_subseteq_r(_),'finiteSet:0').
661 rodin_rule_reasoner(forall_inst(_,_),allD).
662 rodin_rule_reasoner(forall_inst_mp(_,_),'allmpD:0').
663 rodin_rule_reasoner(forall_inst_mt(_,_),'allmtD:0').
664 rodin_rule_reasoner(fun_goal,isFunGoal).
665 rodin_rule_reasoner(fun_image_goal,funImgGoal).
666 rodin_rule_reasoner(hm(_),'mt:2').
667 rodin_rule_reasoner(hyp,hyp).
668 rodin_rule_reasoner(hyp_or,hypOr).
669 rodin_rule_reasoner(imp_and_l(_),impAndRewrites).
670 rodin_rule_reasoner(imp_case(_),impCase).
671 rodin_rule_reasoner(imp_or_l(_),impOrRewrites).
672 rodin_rule_reasoner(imp_r,impI).
673 rodin_rule_reasoner(lower_bound_l,finiteMin).
674 rodin_rule_reasoner(lower_bound_r,finiteMin).
675 rodin_rule_reasoner(mh(_),'impE:2').
676 rodin_rule_reasoner(mon_deselect(_),mngHyp).
677 rodin_rule_reasoner(neg_in,'negEnum:0'). % two rules in Rodin: L/R
678 rodin_rule_reasoner(one_point_l,'onePointRule:2').
679 rodin_rule_reasoner(one_point_r,'onePointRule:2').
680 rodin_rule_reasoner(ov_l,'funOvr:1').
681 rodin_rule_reasoner(ov_r,'funOvr:1').
682 rodin_rule_reasoner(ov_setenum_l,'funOvr:1').
683 rodin_rule_reasoner(ov_setenum_r,'funOvr:1').
684 rodin_rule_reasoner(reselect_hyp(_),mngHyp).
685 rodin_rule_reasoner(sim_dprod_l,funDprodImg).
686 rodin_rule_reasoner(sim_dprod_r,funDprodImg).
687 rodin_rule_reasoner(sim_fcomp_l,funCompImg).
688 rodin_rule_reasoner(sim_fcomp_r,funCompImg).
689 rodin_rule_reasoner(sim_rel_image_l,funSingletonImg).
690 rodin_rule_reasoner(sim_rel_image_r,funSingletonImg).
691 rodin_rule_reasoner(sim_ov_pfun,mapOvrG).
692 rodin_rule_reasoner(sim_ov_rel,mapOvrG).
693 rodin_rule_reasoner(sim_ov_tfun,mapOvrG).
694 rodin_rule_reasoner(sim_ov_trel,mapOvrG).
695 rodin_rule_reasoner(simplify_goal(R),RID) :- rewrite_rule_id(R,RID).
696 rodin_rule_reasoner(simplify_hyp(R,_),RID) :- rewrite_rule_id(R,RID).
697 rodin_rule_reasoner(true_goal,trueGoal).
698 rodin_rule_reasoner(upper_bound_l,finiteMax).
699 rodin_rule_reasoner(upper_bound_r,finiteMax).
700 rodin_rule_reasoner(xst_l,exF).
701
702 rewrite_rule_id('DEF_BCOMP',bcompDefRewrites).
703 rewrite_rule_id('DEF_EQUAL_CARD', cardDefRewrites).
704 rewrite_rule_id('DEF_EQUAL_FUN_IMAGE', equalFunImgDefRewrites).
705 rewrite_rule_id('DEF_EQUAL_MIN', minMaxDefRewrites).
706 rewrite_rule_id('DEF_EQUAL_MAX', minMaxDefRewrites).
707 rewrite_rule_id('DEF_EQV', eqvRewrites).
708 rewrite_rule_id('DEF_FINITE', finiteDefRewrites).
709 rewrite_rule_id('DEF_OR', disjToImplRewrites).
710 rewrite_rule_id('DEF_OVERL', relOvrRewrites).
711 rewrite_rule_id('DEF_PARTITION', partitionRewrites).
712 rewrite_rule_id('DEF_SPECIAL_NOT_EQUAL', rn).
713 rewrite_rule_id('DEF_SUBSET', sir).
714 rewrite_rule_id('DEF_SUBSETEQ', ri). % removeInclusionRewriter
715 rewrite_rule_id('DERIV_DOM_TOTALREL'(_), 'totalDom:2').
716 rewrite_rule_id('DERIV_EQUAL', setEqlRewrites).
717 rewrite_rule_id('DERIV_FCOMP_DOMRES', domCompRewrites).
718 rewrite_rule_id('DERIV_FCOMP_DOMSUB', domCompRewrites).
719 rewrite_rule_id('DERIV_FCOMP_RANRES', ranCompRewrites).
720 rewrite_rule_id('DERIV_FCOMP_RANSUB', ranCompRewrites).
721 rewrite_rule_id('DERIV_IMP', doubleImplGoalRewrites).
722 rewrite_rule_id('DERIV_IMP_IMP', doubleImplHypRewrites).
723 rewrite_rule_id('DERIV_NOT_EXISTS', rn).
724 rewrite_rule_id('DERIV_NOT_FORALL', rn).
725 rewrite_rule_id('DERIV_NOT_IMP', rn).
726 rewrite_rule_id('DERIV_RELIMAGE_FCOMP', compImgRewrites).
727 rewrite_rule_id('DERIV_SUBSETEQ', riUniversal).
728 rewrite_rule_id('DERIV_SUBSETEQ_SETMINUS_L', inclusionSetMinusLeftRewrites).
729 rewrite_rule_id('DERIV_SUBSETEQ_SETMINUS_R', inclusionSetMinusRightRewrites).
730 rewrite_rule_id('DERIV_TYPE_SETMINUS_BINTER', setMinusRewrites).
731 rewrite_rule_id('DERIV_TYPE_SETMINUS_BUNION', setMinusRewrites).
732 rewrite_rule_id('DERIV_TYPE_SETMINUS_SETMINUS', setMinusRewrites).
733 rewrite_rule_id('DISTRI_AND_OR', andOrDistRewrites).
734 rewrite_rule_id('DISTRI_BINTER_BUNION', unionInterDistRewrites).
735 rewrite_rule_id('DISTRI_BUNION_BINTER', unionInterDistRewrites).
736 rewrite_rule_id('DISTRI_CONVERSE_BUNION', convRewrites).
737 rewrite_rule_id('DISTRI_DOMRES_BINTER', 'domDistLeftRewrites:0'). % TODO: split our rule in L/R, Rodin uses separate reasoner for right: domDistRightRewrites
738 rewrite_rule_id('DISTRI_DOMRES_BUNION', 'domDistLeftRewrites:0'). % TODO: ditto, two rules in Rodin: L/R
739 rewrite_rule_id('DISTRI_DOMSUB_BINTER_L', 'domDistLeftRewrites:0').
740 rewrite_rule_id('DISTRI_DOMSUB_BINTER_R', domDistRightRewrites).
741 rewrite_rule_id('DISTRI_DOMSUB_BUNION_L', 'domDistLeftRewrites:0').
742 rewrite_rule_id('DISTRI_DOMSUB_BUNION_R', domDistRightRewrites).
743 rewrite_rule_id('DISTRI_DOM_BUNION', domRanUnionDistRewrites).
744 rewrite_rule_id('DISTRI_FCOMP_BUNION', compUnionDistRewrites). % two rules in Rodin: L/R
745 rewrite_rule_id('DISTRI_NOT_AND', rn).
746 rewrite_rule_id('DISTRI_NOT_OR', rn).
747 rewrite_rule_id('DISTRI_IMP_AND', impAndRewrites).
748 rewrite_rule_id('DISTRI_IMP_OR', impOrRewrites).
749 rewrite_rule_id('DISTRI_OR_AND', andOrDistRewrites).
750 rewrite_rule_id('DISTRI_RAN_BUNION', domRanUnionDistRewrites).
751 rewrite_rule_id('DISTRI_RANSUB_BINTER_L', ranDistLeftRewrites).
752 rewrite_rule_id('DISTRI_RANSUB_BINTER_R', 'ranDistRightRewrites:0').
753 rewrite_rule_id('DISTRI_RANSUB_BUNION_L', ranDistLeftRewrites).
754 rewrite_rule_id('DISTRI_RANSUB_BUNION_R', 'ranDistRightRewrites:0').
755 rewrite_rule_id('DISTRI_RANRES_BINTER', ranDistLeftRewrites). % TODO: split our rule in L/R, Rodin uses separate reasoner for right: ranDistRightRewrites
756 rewrite_rule_id('DISTRI_RANRES_BUNION', ranDistLeftRewrites). % TODO: ditto
757 rewrite_rule_id('DISTRI_RELIMAGE_BUNION_L', relImgUnionLeftRewrites).
758 rewrite_rule_id('DISTRI_RELIMAGE_BUNION_R', relImgUnionRightRewrites).
759 rewrite_rule_id('SIMP_EQUAL_CARD', equalCardRewrites).
760 rewrite_rule_id('SIMP_FUNIMAGE_DOMRES', 'funImgSimplifies:0').
761 rewrite_rule_id('SIMP_FUNIMAGE_DOMSUB', 'funImgSimplifies:0').
762 rewrite_rule_id('SIMP_FUNIMAGE_PPROD', funPprodImg).
763 rewrite_rule_id('SIMP_FUNIMAGE_RANRES', 'funImgSimplifies:0').
764 rewrite_rule_id('SIMP_FUNIMAGE_RANSUB', 'funImgSimplifies:0').
765 rewrite_rule_id('SIMP_FUNIMAGE_SETMINUS', 'funImgSimplifies:0').
766 rewrite_rule_id('SIMP_LIT_CARD_UPTO', cardUpTo).
767 rewrite_rule_id('SIMP_MINUS_UNMINUS', 'arithRewrites:1').
768 rewrite_rule_id('SIMP_MULTI_ARITHREL_PLUS_PLUS', 'arithRewrites:1').
769 rewrite_rule_id('SIMP_MULTI_ARITHREL_PLUS_L', 'arithRewrites:1').
770 rewrite_rule_id('SIMP_MULTI_ARITHREL_PLUS_R', 'arithRewrites:1').
771 rewrite_rule_id('SIMP_MULTI_ARITHREL_MINUS_MINUS_L', 'arithRewrites:1').
772 rewrite_rule_id('SIMP_MULTI_ARITHREL_MINUS_MINUS_R', 'arithRewrites:1').
773 rewrite_rule_id('SIMP_MULTI_MINUS_PLUS_L', 'arithRewrites:1').
774 rewrite_rule_id('SIMP_MULTI_MINUS_PLUS_PLUS', 'arithRewrites:1').
775 rewrite_rule_id('SIMP_MULTI_MINUS_PLUS_R', 'arithRewrites:1').
776 rewrite_rule_id('SIMP_MULTI_PLUS_MINUS', 'arithRewrites:1').
777 rewrite_rule_id('SIMP_NOT_NOT', rn).
778 rewrite_rule_id('SIMP_TYPE_EQUAL_EMPTY', 'typeRewrites:1').
779 rewrite_rule_id('SIMP_TYPE_IN', 'typeRewrites:1').
780 rewrite_rule_id('SIMP_TYPE_SUBSETEQ', 'typeRewrites:1').
781 rewrite_rule_id('SIMP_TYPE_SUBSET_L', 'typeRewrites:1').
782 % TODO: PredicateSimplifier.java
783
784 :- use_module(probsrc(bsyntaxtree),[flatten_conjunctions/2]).
785 get_prob_normalisation_rewrites([],[],[]).
786 get_prob_normalisation_rewrites([InitPred|IT],[NormPred|NT],Rewrites) :-
787 % important: must use correctly nested conjuncts with parentheses (otherwise Rodin recognises them as a different predicate)
788 transform_raw(InitPred,TInitPred),
789 with_forced_rodin_mode(translate_bexpression_to_unicode(TInitPred,PInitPred)),
790 translate_norm_expr_term_no_limit(NormPred,PNormPred),
791 (PInitPred = PNormPred
792 -> assert_hyp_predicate(NormPred,_), Rewrites=RT % predicate is the same after normalisation -> no rewrite
793 ; assert_hyp_predicate_init_rewrite(IName,PInitPred),
794 assert_hyp_predicate(NormPred,NName),
795 Rewrites=[hyp_action(rewrite,IName,NName)|RT]), % register rewrite
796 get_prob_normalisation_rewrites(IT,NT,RT).
797
798 assert_hyp_predicate(Hyp,Name) :- with_forced_rodin_mode(translate_norm_expr_term_no_limit(Hyp,Pred)), prPred(_,Name,Pred), !. % predicate already exists
799 assert_hyp_predicate(Hyp,Name) :-
800 retract(predicate_id_count(C)),
801 ajoin([p,C],Name),
802 NextC is C+1,
803 with_forced_rodin_mode(translate_norm_expr_term_no_limit(Hyp,Pred)),
804 assertz(prPred(Hyp,Name,Pred)),
805 assertz(predicate_id_count(NextC)).
806
807 assert_expression(Expr,Name) :- with_forced_rodin_mode(translate_norm_expr_term_no_limit(Expr,PExpr)), prExpr(_,Name,PExpr), !. % expression already exists
808 assert_expression(Expr,Name) :-
809 retract(expression_id_count(C)),
810 ajoin([e,C],Name),
811 NextC is C+1,
812 with_forced_rodin_mode(translate_norm_expr_term_no_limit(Expr,PExpr)),
813 assertz(prExpr(Expr,Name,PExpr)),
814 assertz(expression_id_count(NextC)).
815
816 with_forced_rodin_mode(Call) :-
817 translate:set_force_eventb_mode, % for printing special unicodes for <+, <<->>, <<->, <->>, which we do not want to appear in the state visualisation
818 call_cleanup(Call, translate:unset_force_eventb_mode).
819
820 assert_hyp_predicate_init_rewrite(Name,PHyp) :- prPred(_,Name,PHyp), !. % predicate already exists
821 assert_hyp_predicate_init_rewrite(Name,PHyp) :-
822 retract(predicate_id_count(C)),
823 ajoin([p,C],Name),
824 NextC is C+1,
825 assertz(prPred(Name,Name,PHyp)),
826 assertz(predicate_id_count(NextC)).
827
828 assert_rule_index(ReasonerID,RuleIndex) :- prReas(RuleIndex,ReasonerID), !.
829 assert_rule_index(ReasonerID,RuleIndex) :-
830 retract(rule_id_count(C)),
831 ajoin([r,C],RuleIndex),
832 NextC is C+1,
833 assertz(prReas(RuleIndex,ReasonerID)),
834 assertz(rule_id_count(NextC)).
835
836 assert_identifier(b(identifier(Id),Type,_)) :-
837 prIdent(Id,Type), !,
838 findall(Type1,(prIdent(Id,Type1),Type1\=any),Types),
839 (Types=[Type] -> true
840 ; add_error(generate_proof_bpr_rodin_export,type_mismatch_for_prIdent(Id,Types,Type)), fail).
841 assert_identifier(b(identifier(Id),Type,_)) :-
842 assertz(prIdent(Id,Type)).
843
844 % Global=global: retract globalIDs after print to avoid printing them for all predicates/expressions
845 write_identifiers(Stream,Global,Ids,Indent) :-
846 member('$'(Id),Ids),
847 (Global=global -> retract(prIdent(Id,Type)) ; prIdent(Id,Type)),
848 Type\=any, % TODO: get rid of any types here (that's because the type check failed, e.g. for x : {})
849 with_translation_mode(unicode,translate:pretty_normalized_type(Type,PType)),
850 xml_attribute_escape_atom(PType,EType),
851 format_indent(Stream,'<org.eventb.core.prIdent name="~w" org.eventb.core.type="~s"/>~n',[Id,EType],Indent),
852 fail.
853 write_identifiers(_,_,_,_).
854
855 write_predicate_mapping(Stream,Indent) :-
856 prPred(Hyp,Name,Pred),
857 used_identifiers(Hyp,Ids), sort(Ids,SIds),
858 xml_attribute_escape_atom(Pred,EPred),
859 format_indent(Stream,'<org.eventb.core.prPred name="~w" org.eventb.core.predicate="~s">~n',[Name,EPred],Indent),
860 NIndent is Indent+1,
861 write_identifiers(Stream,local,SIds,NIndent),
862 format_indent(Stream,'</org.eventb.core.prPred>~n',[],Indent),
863 fail.
864 write_predicate_mapping(_,_).
865
866 write_expression_mapping(Stream,Indent) :-
867 prExpr(Expr,Name,PrettyExpr),
868 used_identifiers(Expr,Ids), sort(Ids,SIds),
869 xml_attribute_escape_atom(PrettyExpr,EExpr),
870 format_indent(Stream,'<org.eventb.core.prExpr name="~w" org.eventb.core.expression="~s">~n',[Name,EExpr],Indent),
871 NIndent is Indent+1,
872 write_identifiers(Stream,local,SIds,NIndent),
873 format_indent(Stream,'</org.eventb.core.prExpr>~n',[],Indent),
874 fail.
875 write_expression_mapping(_,_).
876
877 write_reasoner_mapping(Stream,Indent) :-
878 prReas(Name,RID),
879 format_indent(Stream,'<org.eventb.core.prReas name="~w" org.eventb.core.prRID="~w"/>~n',[Name,RID],Indent),
880 fail.
881 write_reasoner_mapping(_,_).
882
883 :- use_module(probsrc(tools),[xml_attribute_escape/2]).
884 xml_attribute_escape_atom(A,EC) :- \+ atom(A),!,
885 add_internal_error('Not an atom: ',xml_attribute_escape_atom(A,EC)), EC='??'.
886 xml_attribute_escape_atom(A,EC) :- atom_codes(A,C), xml_attribute_escape(C,EC).
887
888 indent_ws(_Stream,X) :- X<1,!.
889 indent_ws(Stream,X) :- format(Stream,' ',[]), X1 is X-1, indent_ws(Stream,X1).
890
891 format_indent(S,A,L,I) :- indent_ws(S,I), format(S,A,L).
892 %%%%%%%%% END PROOF TREE RODIN BPR EXPORT %%%%%%%%%
893
894 %%%%%%%%% BEGIN PRETTY PRINT %%%%%%%%%
895 :- use_module(probsrc(xtl_interface),[get_disprover_po/6]).
896 pretty_print_pos(PP) :-
897 findall(po(Lbl,G,AH,SH,Status), get_disprover_po(Lbl,_Ctx,G,AH,SH,Status), POs),
898 pp_po(POs,PP,[]).
899
900 pp_po([]) --> [].
901 pp_po([po(POLabel,RawGoal,RawAllHyps,RawSelHyps,RodinStatus)|PT]) -->
902 {atom_codes(POLabel,CLabel), atom_codes(RodinStatus,CStatus)}, CLabel, " (", CStatus, ")\n",
903 "\x2550\\x2550\\x2550\\x2550\\x2550\\x2550\\x2550\\x2550\\n",
904 pp_hyps(RawAllHyps,RawSelHyps),
905 " \x2500\\x2500\\x2500\\x2500\\x2500\\x2500\\x2500\\x2500\\x2500\\x2500\\x2500\\x2500\\x2500\\x2500\\x2500\\x2500\\n",
906 " ", translate:pp_raw_formula(RawGoal), "\n\n\n",
907 pp_po(PT).
908
909 pp_hyps([],_) --> [].
910 pp_hyps([RawHyp|AT],SelHyps) -->
911 ({member(RawHyp,SelHyps)} -> " \x2714\ " ; " "),
912 translate:pp_raw_formula(RawHyp), "\n",
913 pp_hyps(AT,SelHyps).
914 %%%%%%%%% END PRETTY PRINT %%%%%%%%%