| 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 %%%%%%%%% |