55
66from graphviz import Digraph
77
8- from ..cterm import CTerm , build_claim , build_rule
98from ..kast .inner import KApply , KRewrite , top_down
109from ..kast .manip import (
1110 flatten_label ,
2221from .kcfg import KCFG
2322
2423if TYPE_CHECKING :
25- from collections .abc import Callable , Iterable
24+ from collections .abc import Iterable
2625 from pathlib import Path
2726 from typing import Final
2827
2928 from ..kast import KInner
30- from ..kast .outer import KRuleLike
29+ from ..kast .outer import KDefinition
3130 from ..ktool .kprint import KPrint
3231 from .kcfg import NodeIdLike
3332
3433_LOGGER : Final = logging .getLogger (__name__ )
3534
3635
37- class KCFGShow :
36+ class NodePrinter :
3837 kprint : KPrint
38+ full_printer : bool
39+ minimize : bool
3940
40- def __init__ (
41- self ,
42- kprint : KPrint ,
43- ):
41+ def __init__ (self , kprint : KPrint , full_printer : bool = False , minimize : bool = False ):
4442 self .kprint = kprint
43+ self .full_printer = full_printer
44+ self .minimize = minimize
45+
46+ def print_node (self , kcfg : KCFG , node : KCFG .Node ) -> list [str ]:
47+ attrs = self .node_attrs (kcfg , node )
48+ attr_str = ' (' + ', ' .join (attrs ) + ')' if attrs else ''
49+ node_strs = [f'{ node .id } { attr_str } ' ]
50+ if self .full_printer :
51+ kast = node .cterm .kast
52+ if self .minimize :
53+ kast = minimize_term (kast )
54+ node_strs .extend (' ' + line for line in self .kprint .pretty_print (kast ).split ('\n ' ))
55+ return node_strs
4556
46- @staticmethod
47- def node_attrs (kcfg : KCFG , node : KCFG .Node ) -> list [str ]:
57+ def node_attrs (self , kcfg : KCFG , node : KCFG .Node ) -> list [str ]:
4858 attrs = []
4959 if kcfg .is_init (node .id ):
5060 attrs .append ('init' )
@@ -60,25 +70,43 @@ def node_attrs(kcfg: KCFG, node: KCFG.Node) -> list[str]:
6070 attrs .append ('leaf' )
6171 if kcfg .is_split (node .id ):
6272 attrs .append ('split' )
73+ attrs .extend (['@' + alias for alias in sorted (kcfg .aliases (node .id ))])
6374 return attrs
6475
76+
77+ class KCFGShow :
78+ kprint : KPrint
79+ node_printer : NodePrinter
80+
81+ def __init__ (self , kprint : KPrint , node_printer : NodePrinter | None = None ):
82+ self .kprint = kprint
83+ self .node_printer = node_printer if node_printer is not None else NodePrinter (kprint )
84+
85+ def node_short_info (self , kcfg : KCFG , node : KCFG .Node ) -> list [str ]:
86+ return self .node_printer .print_node (kcfg , node )
87+
6588 @staticmethod
66- def node_short_info (
67- kcfg : KCFG , node : KCFG .Node , node_printer : Callable [[CTerm ], Iterable [str ]] | None = None
68- ) -> list [str ]:
69- attrs = KCFGShow .node_attrs (kcfg , node ) + ['@' + alias for alias in sorted (kcfg .aliases (node .id ))]
70- attr_string = ' (' + ', ' .join (attrs ) + ')' if attrs else ''
71- node_header = str (node .id ) + attr_string
72- node_strs = [node_header ]
73- if node_printer :
74- node_strs .extend (f' { nl } ' for nl in node_printer (node .cterm ))
75- return node_strs
89+ def hide_cells (term : KInner , omit_cells : Iterable [str ]) -> KInner :
90+ def _hide_cells (_k : KInner ) -> KInner :
91+ if type (_k ) == KApply and _k .label .name in omit_cells :
92+ return DOTS
93+ return _k
94+
95+ if omit_cells :
96+ return top_down (_hide_cells , term )
97+ return term
98+
99+ @staticmethod
100+ def simplify_config (defn : KDefinition , config : KInner , omit_cells : Iterable [str ]) -> KInner :
101+ config = inline_cell_maps (config )
102+ config = sort_ac_collections (defn , config )
103+ config = KCFGShow .hide_cells (config , omit_cells )
104+ return config
76105
77106 def pretty_segments (
78107 self ,
79108 kcfg : KCFG ,
80109 minimize : bool = True ,
81- node_printer : Callable [[CTerm ], Iterable [str ]] | None = None ,
82110 ) -> Iterable [tuple [str , Iterable [str ]]]:
83111 """Return a pretty version of the KCFG in segments.
84112
@@ -90,17 +118,8 @@ def pretty_segments(
90118 processed_nodes : list [KCFG .Node ] = []
91119 ret_lines : list [tuple [str , list [str ]]] = []
92120
93- def _bold (text : str ) -> str :
94- return '\033 [1m' + text + '\033 [0m'
95-
96- def _green (text : str ) -> str :
97- return '\033 [32m' + text + '\033 [0m'
98-
99121 def _print_node (node : KCFG .Node ) -> list [str ]:
100- short_info = KCFGShow .node_short_info (kcfg , node , node_printer = node_printer )
101- if kcfg .is_frontier (node .id ):
102- short_info [0 ] = _bold (short_info [0 ])
103- return short_info
122+ return self .node_short_info (kcfg , node )
104123
105124 def _print_edge (edge : KCFG .Edge ) -> list [str ]:
106125 if edge .depth == 1 :
@@ -170,8 +189,6 @@ def _print_subgraph(indent: str, curr_node: KCFG.Node, prior_on_trace: list[KCFG
170189 suffix = ['(looped back)' , '' ]
171190 elif processed and not kcfg .is_target (curr_node .id ):
172191 suffix = ['(continues as previously)' , '' ]
173- elif kcfg .is_stuck (curr_node .id ):
174- suffix = ['(stuck)' , '' ]
175192 else :
176193 suffix = ['' ]
177194 ret_node_lines .append (indent + elbow + ' ' + curr_node_strs [0 ])
@@ -286,17 +303,8 @@ def pretty(
286303 self ,
287304 kcfg : KCFG ,
288305 minimize : bool = True ,
289- node_printer : Callable [[CTerm ], Iterable [str ]] | None = None ,
290306 ) -> Iterable [str ]:
291- return (
292- line
293- for _ , seg_lines in self .pretty_segments (
294- kcfg ,
295- minimize = minimize ,
296- node_printer = node_printer ,
297- )
298- for line in seg_lines
299- )
307+ return (line for _ , seg_lines in self .pretty_segments (kcfg , minimize = minimize ) for line in seg_lines )
300308
301309 def show (
302310 self ,
@@ -307,37 +315,17 @@ def show(
307315 to_module : bool = False ,
308316 minimize : bool = True ,
309317 sort_collections : bool = False ,
310- node_printer : Callable [[CTerm ], Iterable [str ]] | None = None ,
311318 omit_cells : Iterable [str ] = (),
312319 ) -> list [str ]:
313320 res_lines : list [str ] = []
314- res_lines += self .pretty (cfg , minimize = minimize , node_printer = node_printer )
315-
316- def hide_cells (term : KInner ) -> KInner :
317- def _hide_cells (_k : KInner ) -> KInner :
318- if type (_k ) == KApply and _k .label .name in omit_cells :
319- return DOTS
320- return _k
321-
322- if omit_cells :
323- return top_down (_hide_cells , term )
324- return term
325-
326- def simplify_config (config : KInner ) -> KInner :
327- config = inline_cell_maps (config )
328- config = sort_ac_collections (self .kprint .definition , config )
329- config = hide_cells (config )
330- return config
331-
332- def is_ceil_condition (kast : KInner ) -> bool :
333- return type (kast ) is KApply and kast .label .name == '#Ceil'
321+ res_lines += self .pretty (cfg , minimize = minimize )
334322
335323 nodes_printed = False
336324
337325 for node_id in nodes :
338326 nodes_printed = True
339327 kast = cfg .node (node_id ).cterm .kast
340- kast = hide_cells (kast )
328+ kast = KCFGShow . hide_cells (kast , omit_cells )
341329 if minimize :
342330 kast = minimize_term (kast )
343331 res_lines .append ('' )
@@ -349,8 +337,8 @@ def is_ceil_condition(kast: KInner) -> bool:
349337
350338 for node_id_1 , node_id_2 in node_deltas :
351339 nodes_printed = True
352- config_1 = simplify_config (cfg .node (node_id_1 ).cterm .config )
353- config_2 = simplify_config (cfg .node (node_id_2 ).cterm .config )
340+ config_1 = KCFGShow . simplify_config (self . kprint . definition , cfg .node (node_id_1 ).cterm .config , omit_cells )
341+ config_2 = KCFGShow . simplify_config (self . kprint . definition , cfg .node (node_id_2 ).cterm .config , omit_cells )
354342 config_delta = push_down_rewrites (KRewrite (config_1 , config_2 ))
355343 if minimize :
356344 config_delta = minimize_term (config_delta )
@@ -367,35 +355,39 @@ def is_ceil_condition(kast: KInner) -> bool:
367355 res_lines .append ('' )
368356
369357 if to_module :
370-
371- def to_rule (edge : KCFG .Edge , * , claim : bool = False ) -> KRuleLike :
372- sentence_id = f'BASIC-BLOCK-{ edge .source .id } -TO-{ edge .target .id } '
373- init_constraints = [c for c in edge .source .cterm .constraints if not is_ceil_condition (c )]
374- init_cterm = CTerm (simplify_config (edge .source .cterm .config ), init_constraints )
375- target_constraints = [c for c in edge .target .cterm .constraints if not is_ceil_condition (c )]
376- target_cterm = CTerm (simplify_config (edge .target .cterm .config ), target_constraints )
377- rule : KRuleLike
378- if claim :
379- rule , _ = build_claim (sentence_id , init_cterm , target_cterm )
380- else :
381- rule , _ = build_rule (sentence_id , init_cterm , target_cterm , priority = 35 )
382- return rule
383-
384- rules = [to_rule (e ) for e in cfg .edges ()]
385- nd_steps = [
386- to_rule (KCFG .Edge (ndbranch .source , target , 1 ))
387- for ndbranch in cfg .ndbranches ()
388- for target in ndbranch .targets
389- ]
390- claims = [to_rule (KCFG .Edge (nd , cfg .get_unique_target (), - 1 ), claim = True ) for nd in cfg .frontier ]
391- cfg_module_name = cfgid .upper ().replace ('.' , '-' ).replace ('_' , '-' )
392- new_module = KFlatModule (f'SUMMARY-{ cfg_module_name } ' , rules + nd_steps + claims )
393- res_lines .append (self .kprint .pretty_print (new_module , sort_collections = sort_collections ))
394- res_lines .append ('' )
358+ module = self .to_module (cfgid , cfg , omit_cells = omit_cells )
359+ res_lines .append (self .kprint .pretty_print (module , sort_collections = sort_collections ))
395360
396361 return res_lines
397362
398- def dot (self , kcfg : KCFG , node_printer : Callable [[CTerm ], Iterable [str ]] | None = None ) -> str :
363+ def to_module (
364+ self ,
365+ cfgid : str ,
366+ cfg : KCFG ,
367+ module_name : str | None = None ,
368+ imports : Iterable [str ] = (),
369+ omit_cells : Iterable [str ] = (),
370+ ) -> KFlatModule :
371+ rules = [e .to_rule ('BASIC-BLOCK' ) for e in cfg .edges ()]
372+ rules = [
373+ r .let (body = KCFGShow .simplify_config (self .kprint .definition , r .body , omit_cells = omit_cells )) for r in rules
374+ ]
375+ nd_steps = [edge .to_rule ('ND-STEP' ) for ndbranch in cfg .ndbranches () for edge in ndbranch .edges ]
376+ nd_steps = [
377+ r .let (body = KCFGShow .simplify_config (self .kprint .definition , r .body , omit_cells = omit_cells ))
378+ for r in nd_steps
379+ ]
380+ claims = [KCFG .Edge (nd , cfg .get_unique_target (), - 1 ).to_rule ('UNPROVEN' , claim = True ) for nd in cfg .frontier ]
381+ claims = [
382+ c .let (body = KCFGShow .simplify_config (self .kprint .definition , c .body , omit_cells = omit_cells )) for c in claims
383+ ]
384+
385+ cfg_module_name = (
386+ module_name if module_name is not None else f'SUMMARY-{ cfgid .upper ().replace ("." , "-" ).replace ("_" , "-" )} '
387+ )
388+ return KFlatModule (cfg_module_name , rules + nd_steps + claims )
389+
390+ def dot (self , kcfg : KCFG ) -> Digraph :
399391 def _short_label (label : str ) -> str :
400392 return '\n ' .join (
401393 [
@@ -407,8 +399,8 @@ def _short_label(label: str) -> str:
407399 graph = Digraph ()
408400
409401 for node in kcfg .nodes :
410- label = '\n ' .join (KCFGShow .node_short_info (kcfg , node , node_printer = node_printer ))
411- class_attrs = ' ' .join (KCFGShow .node_attrs (kcfg , node ))
402+ label = '\n ' .join (self .node_short_info (kcfg , node ))
403+ class_attrs = ' ' .join (self . node_printer .node_attrs (kcfg , node ))
412404 attrs = {'class' : class_attrs } if class_attrs else {}
413405 graph .node (name = node .id , label = label , ** attrs )
414406
@@ -445,24 +437,17 @@ def _short_label(label: str) -> str:
445437 attrs = {'class' : 'target' , 'style' : 'solid' }
446438 graph .edge (tail_name = node .id , head_name = target_id , label = ' false' , ** attrs )
447439
448- return graph . source
440+ return graph
449441
450- def dump (
451- self ,
452- cfgid : str ,
453- cfg : KCFG ,
454- dump_dir : Path ,
455- dot : bool = False ,
456- node_printer : Callable [[CTerm ], Iterable [str ]] | None = None ,
457- ) -> None :
442+ def dump (self , cfgid : str , cfg : KCFG , dump_dir : Path , dot : bool = False ) -> None :
458443 ensure_dir_path (dump_dir )
459444
460445 cfg_file = dump_dir / f'{ cfgid } .json'
461446 cfg_file .write_text (cfg .to_json ())
462447 _LOGGER .info (f'Wrote CFG file { cfgid } : { cfg_file } ' )
463448
464449 if dot :
465- cfg_dot = self .dot (cfg , node_printer = node_printer )
450+ cfg_dot = self .dot (cfg )
466451 dot_file = dump_dir / f'{ cfgid } .dot'
467452 dot_file .write_text (cfg_dot )
468453 _LOGGER .info (f'Wrote DOT file { cfgid } : { dot_file } ' )
0 commit comments