| 1 | % (c) 2009-2024 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, | |
| 2 | % Heinrich Heine Universitaet Duesseldorf | |
| 3 | % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) | |
| 4 | ||
| 5 | :- module(state_as_dot_graph,[ | |
| 6 | print_cstate_graph/2, print_cstate_graph/1]). | |
| 7 | ||
| 8 | :- use_module(probsrc(module_information)). | |
| 9 | :- module_info(group,dot). | |
| 10 | :- module_info(description,'Display canonical form of state graph using dot.'). | |
| 11 | ||
| 12 | :- use_module(extrasrc(state_graph_canon),[state_graph_for_dot/2]). | |
| 13 | ||
| 14 | ||
| 15 | /* ---------------------------- */ | |
| 16 | /* DOT generation */ | |
| 17 | /* ---------------------------- */ | |
| 18 | ||
| 19 | ||
| 20 | :- use_module(probsrc(error_manager)). | |
| 21 | :- use_module(probsrc(preferences)). | |
| 22 | :- use_module(probsrc(state_space),[current_expression/2]). | |
| 23 | :- use_module(probsrc(tools),[ajoin/2, string_escape/2]). | |
| 24 | ||
| 25 | print_cstate_graph(File) :- | |
| 26 | current_expression(_,State), | |
| 27 | print_cstate_graph(State,File). | |
| 28 | print_cstate_graph(State,File) :- | |
| 29 | state_graph_for_dot(State,GCE), | |
| 30 | open(File,write,Stream,[encoding(utf8)]), | |
| 31 | (graph2dot(Stream,GCE) -> close(Stream) | |
| 32 | ; close(Stream), | |
| 33 | add_error(graph_canon,'Dot conversion failed: ',graph2dot(File,GCE)) | |
| 34 | ). | |
| 35 | ||
| 36 | ||
| 37 | graph2dot(Stream,G) :- reset_dot, | |
| 38 | (get_preference(dot_horizontal_layout,true) -> RDir='LR'; RDir='TB'), | |
| 39 | PSize='', %(get_preference(dot_with_page_size,true) -> PSize='page="8.5, 11",ratio=fill,size="7.5,10",' ; PSize=''), | |
| 40 | format(Stream,'digraph state {\n graph [~wfontsize=12]\nrankdir=~w;~n',[PSize,RDir]), | |
| 41 | edges2dot(G,Stream), | |
| 42 | nodes2dot(Stream), | |
| 43 | findall(cluster(Ty,El),cluster(Ty,El),T), | |
| 44 | clusters2dot(T,Stream), | |
| 45 | format(Stream,'}~n',[]). | |
| 46 | ||
| 47 | :- use_module(probsrc(b_global_sets)). | |
| 48 | cluster(Type,Elements) :- b_global_set(Type), | |
| 49 | % compute all elements of global sets which are used in current state graph | |
| 50 | findall(Translation, (enum_global_type_limited(El,Type), node_col(El,Translation,_,_)),Elements). | |
| 51 | % maybe add boolean ?? | |
| 52 | edges2dot([],_). | |
| 53 | edges2dot([(A,B,C)|T],Stream) :- /* edge from A -> C with label B */ | |
| 54 | translate_node_value(A,AID), | |
| 55 | (B = abs_cn(BB,ColBB) | |
| 56 | -> translate_abs_cn_node(BB,ColBB,EdgeLabel) | |
| 57 | ; EdgeLabel = B), | |
| 58 | translate_node_value(C,CID), | |
| 59 | get_col_assoc(edge,EdgeLabel,EdgCol), | |
| 60 | format(Stream,'\"~w\" -> \"~w\" [label = \"~w\", color = \"~w\"];\n', | |
| 61 | [AID,CID,EdgeLabel,EdgCol]), | |
| 62 | edges2dot(T,Stream). | |
| 63 | ||
| 64 | reset_dot :- retractall(col_association(_,_,_)), | |
| 65 | retractall(node_col(_,_,_,_)), | |
| 66 | retractall(cur_col(_,_)), | |
| 67 | assertz(cur_col(node,0)), assertz(cur_col(edge,99)). | |
| 68 | ||
| 69 | :- use_module(probsrc(translate),[translate_bvalue_for_dot/2]). | |
| 70 | translate_node_value(abs_cn(AA,ColAA), Translation) :- !, | |
| 71 | translate_abs_cn_node(AA,ColAA,Translation). | |
| 72 | translate_node_value(sg_root,Translation) :- !, | |
| 73 | Translation = 'ROOT-NODE', add_node(sg_root,Translation). | |
| 74 | translate_node_value(Val,Translation) :- | |
| 75 | translate_bvalue_for_dot(Val,Translation), | |
| 76 | add_node(Val,Translation). | |
| 77 | ||
| 78 | nodes2dot(Stream) :- node_col(_,Translation,Col,Shape), | |
| 79 | (Shape=record(Label) | |
| 80 | -> format(Stream,'\"~w\" [shape=record, label=\"~w\", color = \"~w\", style = \"filled, solid\"]\n', | |
| 81 | [Translation,Label,Col]) | |
| 82 | ; format(Stream,'\"~w\" [color = \"~w\", style = \"filled, solid\", shape = \"~w\"]\n',[Translation,Col,Shape]) | |
| 83 | ), | |
| 84 | fail. | |
| 85 | nodes2dot(_). | |
| 86 | ||
| 87 | :- use_module(probsrc(kernel_reals),[is_real/2]). | |
| 88 | :- dynamic node_col/4. | |
| 89 | add_node(X,_) :- node_col(X,_,_,_),!. % already processed | |
| 90 | add_node(sg_root,Translation) :- | |
| 91 | assertz(node_col(sg_root,Translation,lightblue,diamond)). | |
| 92 | add_node(fd(X,Type),Translation) :- !, | |
| 93 | get_col_assoc(node,Type,Col), | |
| 94 | get_preference(dot_normal_node_shape,Shape), | |
| 95 | assertz(node_col(fd(X,Type),Translation,Col,Shape)). | |
| 96 | add_node(int(X),Translation) :- !, | |
| 97 | Col = 'wheat3', %get_col_assoc(node,integer,Col), | |
| 98 | get_preference(dot_normal_node_shape,Shape), | |
| 99 | assertz(node_col(int(X),Translation,Col,Shape)). | |
| 100 | add_node(string(X),Translation) :- !, | |
| 101 | Col = 'khaki1', %'lavender' 'burlywood', %get_col_assoc(node,integer,Col), | |
| 102 | get_preference(dot_normal_node_shape,Shape), | |
| 103 | assertz(node_col(string(X),Translation,Col,Shape)). | |
| 104 | add_node(pred_true,T) :- !, assertz(node_col(pred_true,T,'OliveDrab4',ellipse)). | |
| 105 | add_node(pred_false,T) :- !, assertz(node_col(pred_false,T,brown,ellipse)). | |
| 106 | add_node(RecordOrPair,Translation) :- get_fields(RecordOrPair,Fields),!, | |
| 107 | translate_fields(Fields,Atoms), | |
| 108 | ajoin(['|{ ' | Atoms], RecTranslation), | |
| 109 | assertz(node_col(RecordOrPair,Translation,burlywood,record(RecTranslation))). % TO DO: maybe use dot record | |
| 110 | add_node(Val,Translation) :- is_set(Val),!, | |
| 111 | Col = 'LightSteelBlue1', %get_col_assoc(node,integer,Col), | |
| 112 | get_preference(dot_normal_node_shape,Shape), | |
| 113 | assertz(node_col(Val,Translation,Col,Shape)). | |
| 114 | add_node(Term,Translation) :- is_real(Term,_),!, | |
| 115 | Col = 'wheat2', %get_col_assoc(node,integer,Col), | |
| 116 | get_preference(dot_normal_node_shape,Shape), | |
| 117 | assertz(node_col(Term,Translation,Col,Shape)). | |
| 118 | add_node(_Val,_Translation). | |
| 119 | ||
| 120 | % copied from state_graph_canon: | |
| 121 | list_set([]). | |
| 122 | list_set([_|_]). | |
| 123 | is_set(avl_set(_)). | |
| 124 | is_set(X) :- list_set(X). | |
| 125 | ||
| 126 | get_fields(rec(Fields),Fields). | |
| 127 | get_fields((A,B),Fields) :- get_pair_fields((A,B),Fields,[]). | |
| 128 | ||
| 129 | get_pair_fields((A,B)) --> !, get_pair_fields(A), get_pair_fields(B). | |
| 130 | get_pair_fields(A) --> [field(prj,A)]. | |
| 131 | ||
| 132 | translate_fields([],[' }|']). | |
| 133 | translate_fields([field(Name,Value)],Res) :- !, | |
| 134 | trans_field(Name,VS,Res,[' }|']), | |
| 135 | translate_bvalue_for_dot(Value,VS). | |
| 136 | translate_fields([field(Name,Value)|T],Res) :- | |
| 137 | trans_field(Name,VS,Res,['|' | Rest]), | |
| 138 | translate_bvalue_for_dot(Value,VS), | |
| 139 | translate_fields(T,Rest). | |
| 140 | ||
| 141 | trans_field(prj,VS, [' { ', VS, ' } ' | Rest], Rest) :- !. | |
| 142 | trans_field(Name,VS, [' { ',Name,' | ', VS, ' } ' | Rest], Rest). | |
| 143 | ||
| 144 | :- dynamic col_association/3. % store colours used for types, edge labels,... | |
| 145 | get_col_assoc(Domain,T,Col) :- col_association(Domain,T,Col),!. | |
| 146 | get_col_assoc(Domain,T,Col) :- get_next_col(Domain,Col), | |
| 147 | assertz(col_association(Domain,T,Col)). | |
| 148 | ||
| 149 | get_next_col(Type,Col) :- retract(cur_col(Type,N)),!, N1 is N+1, | |
| 150 | (default_colours(N1,Col) -> NX=N1 | |
| 151 | ; NX = 1, default_colours(1,Col)), | |
| 152 | assertz(cur_col(Type,NX)). | |
| 153 | get_next_col(_T,red). | |
| 154 | ||
| 155 | :- dynamic cur_col/2. | |
| 156 | cur_col(node,0). | |
| 157 | cur_col(edge,99). | |
| 158 | default_colours(1,'#efdf84'). | |
| 159 | default_colours(2,'#bdef6b'). | |
| 160 | default_colours(3,'#5863ee'). | |
| 161 | default_colours(4,'LightSteelBlue1'). | |
| 162 | default_colours(5,gray). | |
| 163 | ||
| 164 | default_colours(100,firebrick). | |
| 165 | default_colours(101,sienna). | |
| 166 | default_colours(102,'SlateBlue4'). | |
| 167 | default_colours(103,black). | |
| 168 | ||
| 169 | translate_abs_cn_node(AA,ColAA,Translation) :- | |
| 170 | tools:string_concatenate('_',AA,AID1), | |
| 171 | tools:string_concatenate(ColAA,AID1,AID2), | |
| 172 | tools:string_concatenate('abs',AID2,Translation). | |
| 173 | ||
| 174 | clusters2dot([],_). | |
| 175 | clusters2dot([cluster(Name,Elements)|T],Stream) :- | |
| 176 | string_escape(Name,EscName), | |
| 177 | format(Stream,'subgraph \"cluster_~w\" {node [style=filled,color=white]; label=\"~w\"; style=filled;color=lightgrey; ',[EscName,EscName]), | |
| 178 | cnodes2dot(Elements,Stream), | |
| 179 | clusters2dot(T,Stream). | |
| 180 | ||
| 181 | :- use_module(probsrc(tools),[string_escape/2]). | |
| 182 | cnodes2dot([],Stream) :- format(Stream,'}~n',[]). | |
| 183 | cnodes2dot([H|T],Stream) :- | |
| 184 | translate:translate_params_for_dot([H],HP), | |
| 185 | string_escape(HP,HPE), | |
| 186 | format(Stream,'~w; ',[HPE]), | |
| 187 | cnodes2dot(T,Stream). |