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