1010from pyk .cli .args import KCLIArgs
1111from pyk .cterm .show import CTermShow
1212from pyk .kast .pretty import PrettyPrinter
13+ from pyk .kdist import kdist
1314from pyk .proof .reachability import APRProof
1415from pyk .proof .show import APRProofShow
1516from pyk .proof .tui import APRProofViewer
1617
17- from .build import HASKELL_DEF_DIR , LLVM_LIB_DIR
1818from .cargo import CargoProject
1919from .kmir import KMIR , KMIRAPRNodePrinter
2020from .linker import link
@@ -54,7 +54,14 @@ def _kmir_run(opts: RunOpts) -> None:
5454 smir_info = cargo .smir_for_project (clean = False )
5555
5656 def run (target_dir : Path ):
57- kmir = KMIR .from_kompiled_kore (smir_info , symbolic = opts .haskell_backend , target_dir = target_dir )
57+ kmir = KMIR .from_kompiled_kore (
58+ smir_info ,
59+ target_dir = target_dir ,
60+ symbolic = opts .symbolic ,
61+ haskell_target = opts .haskell_target ,
62+ llvm_lib_target = opts .llvm_lib_target ,
63+ llvm_target = opts .llvm_target ,
64+ )
5865 result = kmir .run_smir (smir_info , start_symbol = opts .start_symbol , depth = opts .depth )
5966 print (kmir .kore_to_pretty (result ))
6067
@@ -73,7 +80,10 @@ def _kmir_prove_rs(opts: ProveRSOpts) -> None:
7380
7481
7582def _kmir_view (opts : ViewOpts ) -> None :
76- kmir = KMIR (HASKELL_DEF_DIR , LLVM_LIB_DIR )
83+ kmir = KMIR (
84+ definition_dir = kdist .which (opts .haskell_target or 'mir-semantics.haskell' ),
85+ llvm_library_dir = kdist .which (opts .llvm_lib_target or 'mir-semantics.llvm-library' ),
86+ )
7787 proof = APRProof .read_proof_data (opts .proof_dir , opts .id )
7888 printer = PrettyPrinter (kmir .definition )
7989 omit_labels = ('<currentBody>' ,) if opts .omit_current_body else ()
@@ -84,14 +94,53 @@ def _kmir_view(opts: ViewOpts) -> None:
8494 viewer .run ()
8595
8696
97+ def _write_to_module (kmir : KMIR , proof : APRProof , to_module_path : Path ) -> None :
98+ """Write proof KCFG as a K module to the specified path."""
99+ import json
100+
101+ from pyk .kast .manip import remove_generated_cells
102+ from pyk .kast .outer import KRule
103+
104+ # Generate K module using KCFG.to_module with defunc_with for proper function inlining
105+ module_name = proof .id .upper ().replace ('.' , '-' ).replace ('_' , '-' ) + '-SUMMARY'
106+ k_module = proof .kcfg .to_module (module_name = module_name , defunc_with = kmir .definition )
107+
108+ if to_module_path .suffix == '.json' :
109+ # JSON format for --add-module: keep <generatedTop> for Kore conversion
110+ # Note: We don't use minimize_rule_like here because it creates partial configs
111+ # with dots that cannot be converted back to Kore
112+ to_module_path .write_text (json .dumps (k_module .to_dict (), indent = 2 ))
113+ else :
114+ # K text format for human readability: remove <generatedTop> and <generatedCounter>
115+ def _process_sentence (sent ): # type: ignore[no-untyped-def]
116+ if isinstance (sent , KRule ):
117+ sent = sent .let (body = remove_generated_cells (sent .body ))
118+ return sent
119+
120+ k_module_readable = k_module .let (sentences = [_process_sentence (sent ) for sent in k_module .sentences ])
121+ k_module_text = kmir .pretty_print (k_module_readable )
122+ to_module_path .write_text (k_module_text )
123+ _LOGGER .info (f'Module written to: { to_module_path } ' )
124+
125+
87126def _kmir_show (opts : ShowOpts ) -> None :
88127 from pyk .kast .pretty import PrettyPrinter
89128
90129 from .kprint import KMIRPrettyPrinter
91130
92- kmir = KMIR (HASKELL_DEF_DIR , LLVM_LIB_DIR )
131+ kmir = KMIR (
132+ definition_dir = kdist .which (opts .haskell_target or 'mir-semantics.haskell' ),
133+ llvm_library_dir = kdist .which (opts .llvm_lib_target or 'mir-semantics.llvm-library' ),
134+ )
93135 proof = APRProof .read_proof_data (opts .proof_dir , opts .id )
94136
137+ # Minimize proof KCFG if requested
138+ if opts .minimize_proof :
139+ _LOGGER .info ('Minimizing proof KCFG...' )
140+ proof .minimize_kcfg ()
141+ proof .write_proof_data ()
142+ _LOGGER .info ('Proof KCFG minimized and saved' )
143+
95144 # Use custom KMIR printer by default, switch to standard printer if requested
96145 if opts .use_default_printer :
97146 printer = PrettyPrinter (kmir .definition )
@@ -119,6 +168,7 @@ def _kmir_show(opts: ShowOpts) -> None:
119168 nodes = opts .nodes or (),
120169 node_deltas = effective_node_deltas ,
121170 omit_cells = tuple (all_omit_cells ),
171+ to_module = opts .to_module is not None ,
122172 )
123173 if opts .statistics :
124174 if lines and lines [- 1 ] != '' :
@@ -132,7 +182,12 @@ def _kmir_show(opts: ShowOpts) -> None:
132182 lines .append ('' )
133183 lines .extend (render_leaf_k_cells (proof , node_printer .cterm_show ))
134184
135- print ('\n ' .join (lines ))
185+ # Handle --to-module output
186+ if opts .to_module :
187+ _write_to_module (kmir , proof , opts .to_module )
188+ print (f'Module written to: { opts .to_module } ' )
189+ else :
190+ print ('\n ' .join (lines ))
136191
137192
138193def _kmir_prune (opts : PruneOpts ) -> None :
@@ -156,7 +211,14 @@ def _kmir_section_edge(opts: SectionEdgeOpts) -> None:
156211
157212 smir_info = SMIRInfo .from_file (target_path / 'smir.json' )
158213
159- kmir = KMIR .from_kompiled_kore (smir_info , symbolic = True , bug_report = opts .bug_report , target_dir = target_path )
214+ kmir = KMIR .from_kompiled_kore (
215+ smir_info ,
216+ target_dir = target_path ,
217+ bug_report = opts .bug_report ,
218+ symbolic = True ,
219+ haskell_target = opts .haskell_target ,
220+ llvm_lib_target = opts .llvm_lib_target ,
221+ )
160222
161223 source_id , target_id = opts .edge
162224 _LOGGER .info (f'Attempting to add { opts .sections } sections from node { source_id } to node { target_id } ' )
@@ -229,7 +291,10 @@ def _arg_parser() -> ArgumentParser:
229291 run_parser .add_argument (
230292 '--start-symbol' , type = str , metavar = 'SYMBOL' , default = 'main' , help = 'Symbol name to begin execution from'
231293 )
232- run_parser .add_argument ('--haskell-backend' , action = 'store_true' , help = 'Run with the haskell backend' )
294+ run_parser .add_argument ('--symbolic' , action = 'store_true' , help = 'Run with the symbolic backend' )
295+ run_parser .add_argument ('--haskell-target' , metavar = 'TARGET' , help = 'Haskell target to use' )
296+ run_parser .add_argument ('--llvm-lib-target' , metavar = 'TARGET' , help = 'LLVM lib target to use' )
297+ run_parser .add_argument ('--llvm-target' , metavar = 'TARGET' , help = 'LLVM target to use' )
233298
234299 info_parser = command_parser .add_parser (
235300 'info' , help = 'Show information about a SMIR JSON file' , parents = [kcli_args .logging_args ]
@@ -239,6 +304,8 @@ def _arg_parser() -> ArgumentParser:
239304
240305 prove_args = ArgumentParser (add_help = False )
241306 prove_args .add_argument ('--proof-dir' , metavar = 'DIR' , help = 'Proof directory' )
307+ prove_args .add_argument ('--haskell-target' , metavar = 'TARGET' , help = 'Haskell target to use' )
308+ prove_args .add_argument ('--llvm-lib-target' , metavar = 'TARGET' , help = 'LLVM lib target to use' )
242309 prove_args .add_argument ('--bug-report' , metavar = 'PATH' , help = 'path to optional bug report' )
243310 prove_args .add_argument ('--max-depth' , metavar = 'DEPTH' , type = int , help = 'max steps to take between nodes in kcfg' )
244311 prove_args .add_argument (
@@ -370,6 +437,8 @@ def _arg_parser() -> ArgumentParser:
370437 action = 'store_false' ,
371438 help = 'Display the <currentBody> cell completely.' ,
372439 )
440+ display_args .add_argument ('--haskell-target' , metavar = 'TARGET' , help = 'Haskell target to use' )
441+ display_args .add_argument ('--llvm-lib-target' , metavar = 'TARGET' , help = 'LLVM lib target to use' )
373442
374443 show_parser = command_parser .add_parser (
375444 'show' , help = 'Show proof information' , parents = [kcli_args .logging_args , proof_args , display_args ]
@@ -410,6 +479,17 @@ def _arg_parser() -> ArgumentParser:
410479 )
411480
412481 show_parser .add_argument ('--rules' , metavar = 'EDGES' , help = 'Comma separated list of edges in format "source:target"' )
482+ show_parser .add_argument (
483+ '--to-module' ,
484+ type = Path ,
485+ metavar = 'FILE' ,
486+ help = 'Output path for K module file (.k for readable, .json for --add-module)' ,
487+ )
488+ show_parser .add_argument (
489+ '--minimize-proof' ,
490+ action = 'store_true' ,
491+ help = 'Minimize the proof KCFG before exporting to module' ,
492+ )
413493
414494 command_parser .add_parser (
415495 'view' , help = 'View proof information' , parents = [kcli_args .logging_args , proof_args , display_args ]
@@ -429,6 +509,8 @@ def _arg_parser() -> ArgumentParser:
429509 section_edge_parser .add_argument (
430510 '--sections' , type = int , default = 2 , help = 'Number of sections to make from edge (>= 2, default: 2)'
431511 )
512+ section_edge_parser .add_argument ('--haskell-target' , metavar = 'TARGET' , help = 'Haskell target to use' )
513+ section_edge_parser .add_argument ('--llvm-lib-target' , metavar = 'TARGET' , help = 'LLVM lib target to use' )
432514
433515 prove_rs_parser = command_parser .add_parser (
434516 'prove-rs' , help = 'Prove a rust program' , parents = [kcli_args .logging_args , prove_args ]
@@ -443,6 +525,12 @@ def _arg_parser() -> ArgumentParser:
443525 prove_rs_parser .add_argument (
444526 '--start-symbol' , type = str , metavar = 'SYMBOL' , default = 'main' , help = 'Symbol name to begin execution from'
445527 )
528+ prove_rs_parser .add_argument (
529+ '--add-module' ,
530+ type = Path ,
531+ metavar = 'FILE' ,
532+ help = 'K module file to include (.json format from --to-module)' ,
533+ )
446534
447535 link_parser = command_parser .add_parser (
448536 'link' , help = 'Link together 2 or more SMIR JSON files' , parents = [kcli_args .logging_args ]
@@ -464,7 +552,7 @@ def _parse_args(ns: Namespace) -> KMirOpts:
464552 target_dir = ns .target_dir ,
465553 depth = ns .depth ,
466554 start_symbol = ns .start_symbol ,
467- haskell_backend = ns .haskell_backend ,
555+ symbolic = ns .symbolic ,
468556 )
469557 case 'info' :
470558 return InfoOpts (smir_file = Path (ns .smir_file ), types = ns .types )
@@ -474,6 +562,8 @@ def _parse_args(ns: Namespace) -> KMirOpts:
474562 id = ns .id ,
475563 full_printer = ns .full_printer ,
476564 smir_info = Path (ns .smir_info ) if ns .smir_info else None ,
565+ haskell_target = ns .haskell_target ,
566+ llvm_lib_target = ns .llvm_lib_target ,
477567 omit_current_body = ns .omit_current_body ,
478568 nodes = ns .nodes ,
479569 node_deltas = ns .node_deltas ,
@@ -492,6 +582,8 @@ def _parse_args(ns: Namespace) -> KMirOpts:
492582 ns .id ,
493583 full_printer = ns .full_printer ,
494584 smir_info = ns .smir_info ,
585+ haskell_target = ns .haskell_target ,
586+ llvm_lib_target = ns .llvm_lib_target ,
495587 omit_current_body = ns .omit_current_body ,
496588 )
497589 case 'prune' :
@@ -501,7 +593,14 @@ def _parse_args(ns: Namespace) -> KMirOpts:
501593 if ns .proof_dir is None :
502594 raise ValueError ('Must pass --proof-dir to section-edge command' )
503595 proof_dir = Path (ns .proof_dir )
504- return SectionEdgeOpts (proof_dir , ns .id , ns .edge , ns .sections )
596+ return SectionEdgeOpts (
597+ proof_dir ,
598+ ns .id ,
599+ ns .edge ,
600+ sections = ns .sections ,
601+ haskell_target = ns .haskell_target ,
602+ llvm_lib_target = ns .llvm_lib_target ,
603+ )
505604 case 'prove-rs' :
506605 return ProveRSOpts (
507606 rs_file = Path (ns .rs_file ),
@@ -530,6 +629,7 @@ def _parse_args(ns: Namespace) -> KMirOpts:
530629 break_every_terminator = ns .break_every_terminator ,
531630 break_every_step = ns .break_every_step ,
532631 terminate_on_thunk = ns .terminate_on_thunk ,
632+ add_module = ns .add_module ,
533633 )
534634 case 'link' :
535635 return LinkOpts (
0 commit comments