Skip to content

Commit cb79a21

Browse files
committed
Represent combinators as diamond nodes in Mermaid diagram
Multi-input combinators (join, union, fixpoint) are now shown as separate diamond-shaped nodes in the diagram: left --> combinator{join} right --> combinator combinator --> result This makes the diagram more faithful to the actual data flow.
1 parent 393c4c2 commit cb79a21

File tree

2 files changed

+177
-83
lines changed

2 files changed

+177
-83
lines changed

analysis/reactive/src/Reactive.ml

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ module Registry = struct
9797

9898
let nodes : (string, node_info) Hashtbl.t = Hashtbl.create 64
9999
let edges : (string * string, string) Hashtbl.t = Hashtbl.create 128
100+
(* Combinator nodes: (combinator_id, (shape, inputs, output)) *)
101+
let combinators : (string, string * string list * string) Hashtbl.t =
102+
Hashtbl.create 32
100103
let dirty_nodes : string list ref = ref []
101104

102105
let register ~name ~level ~process ~stats =
@@ -123,6 +126,10 @@ module Registry = struct
123126
| Some info -> info.upstream <- from_name :: info.upstream
124127
| None -> ()
125128

129+
(** Register a multi-input combinator (rendered as diamond in Mermaid) *)
130+
let add_combinator ~name ~shape ~inputs ~output =
131+
Hashtbl.replace combinators name (shape, inputs, output)
132+
126133
let mark_dirty name =
127134
match Hashtbl.find_opt nodes name with
128135
| Some info when not info.dirty ->
@@ -133,33 +140,62 @@ module Registry = struct
133140
let clear () =
134141
Hashtbl.clear nodes;
135142
Hashtbl.clear edges;
143+
Hashtbl.clear combinators;
136144
dirty_nodes := []
137145

138146
(** Generate Mermaid diagram of the pipeline *)
139147
let to_mermaid () =
140148
let buf = Buffer.create 256 in
141149
Buffer.add_string buf "graph TD\n";
150+
(* Collect edges that are part of combinators *)
151+
let combinator_edges = Hashtbl.create 64 in
152+
Hashtbl.iter
153+
(fun comb_name (_, inputs, output) ->
154+
List.iter
155+
(fun input -> Hashtbl.replace combinator_edges (input, output) comb_name)
156+
inputs)
157+
combinators;
158+
(* Output regular nodes *)
142159
Hashtbl.iter
143160
(fun name info ->
144-
(* Node with level annotation *)
145161
Buffer.add_string buf
146-
(Printf.sprintf " %s[%s L%d]\n" name name info.level);
147-
(* Edges with labels *)
162+
(Printf.sprintf " %s[%s L%d]\n" name name info.level))
163+
nodes;
164+
(* Output combinator nodes (diamond shape) *)
165+
Hashtbl.iter
166+
(fun comb_name (shape, _inputs, _output) ->
167+
Buffer.add_string buf (Printf.sprintf " %s{%s}\n" comb_name shape))
168+
combinators;
169+
(* Output edges *)
170+
Hashtbl.iter
171+
(fun name info ->
148172
List.iter
149173
(fun downstream ->
150-
let label =
151-
match Hashtbl.find_opt edges (name, downstream) with
152-
| Some l -> l
153-
| None -> ""
154-
in
155-
if label = "" then
156-
Buffer.add_string buf
157-
(Printf.sprintf " %s --> %s\n" name downstream)
158-
else
174+
(* Check if this edge is part of a combinator *)
175+
match Hashtbl.find_opt combinator_edges (name, downstream) with
176+
| Some comb_name ->
177+
(* Edge goes to combinator node instead *)
159178
Buffer.add_string buf
160-
(Printf.sprintf " %s -->|%s| %s\n" name label downstream))
179+
(Printf.sprintf " %s --> %s\n" name comb_name)
180+
| None ->
181+
let label =
182+
match Hashtbl.find_opt edges (name, downstream) with
183+
| Some l -> l
184+
| None -> ""
185+
in
186+
if label = "" then
187+
Buffer.add_string buf
188+
(Printf.sprintf " %s --> %s\n" name downstream)
189+
else
190+
Buffer.add_string buf
191+
(Printf.sprintf " %s -->|%s| %s\n" name label downstream))
161192
info.downstream)
162193
nodes;
194+
(* Output edges from combinators to their outputs *)
195+
Hashtbl.iter
196+
(fun comb_name (_shape, _inputs, output) ->
197+
Buffer.add_string buf (Printf.sprintf " %s --> %s\n" comb_name output))
198+
combinators;
163199
Buffer.contents buf
164200

165201
(** Print timing stats for all nodes *)
@@ -711,6 +747,8 @@ let join ~name (left : ('k1, 'v1) t) (right : ('k2, 'v2) t) ~key_of ~f ?merge ()
711747
in
712748
Registry.add_edge ~from_name:left.name ~to_name:name ~label:"join";
713749
Registry.add_edge ~from_name:right.name ~to_name:name ~label:"join";
750+
Registry.add_combinator ~name:(name ^ "_join") ~shape:"join"
751+
~inputs:[left.name; right.name] ~output:name;
714752

715753
(* Subscribe to sources: just accumulate *)
716754
left.subscribe (fun delta ->
@@ -844,6 +882,8 @@ let union ~name (left : ('k, 'v) t) (right : ('k, 'v) t) ?merge () : ('k, 'v) t
844882
in
845883
Registry.add_edge ~from_name:left.name ~to_name:name ~label:"union";
846884
Registry.add_edge ~from_name:right.name ~to_name:name ~label:"union";
885+
Registry.add_combinator ~name:(name ^ "_union") ~shape:"union"
886+
~inputs:[left.name; right.name] ~output:name;
847887

848888
(* Subscribe to sources: just accumulate *)
849889
left.subscribe (fun delta ->
@@ -1046,6 +1086,8 @@ let fixpoint ~name ~(init : ('k, unit) t) ~(edges : ('k, 'k list) t) () :
10461086
in
10471087
Registry.add_edge ~from_name:init.name ~to_name:name ~label:"roots";
10481088
Registry.add_edge ~from_name:edges.name ~to_name:name ~label:"edges";
1089+
Registry.add_combinator ~name:(name ^ "_fp") ~shape:"fixpoint"
1090+
~inputs:[init.name; edges.name] ~output:name;
10491091

10501092
(* Subscribe to sources: just accumulate *)
10511093
init.subscribe (fun delta ->
Lines changed: 122 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,127 +1,179 @@
11
graph TD
22
annotations[annotations L2]
3-
annotations -->|join| solver.incorrect_dead_decls
4-
annotations -->|join| liveness.annotated_roots
53
exc_refs.exception_decls[exc_refs.exception_decls L3]
6-
exc_refs.exception_decls -->|join| exc_refs.resolved_refs
74
type_deps.same_path_refs[type_deps.same_path_refs L4]
8-
type_deps.same_path_refs -->|union| type_deps.u1
95
file_collection[file_collection L0]
10-
file_collection -->|flatMap| file_data_collection
116
decl_refs.value_decl_refs[decl_refs.value_decl_refs L7]
12-
decl_refs.value_decl_refs -->|join| decl_refs.with_value_refs
137
type_deps.combined_refs_to[type_deps.combined_refs_to L7]
14-
type_deps.combined_refs_to -->|flatMap| type_deps.all_type_refs_from
158
type_deps.all_type_refs_from[type_deps.all_type_refs_from L8]
16-
type_deps.all_type_refs_from -->|union| liveness.type_refs_from
179
type_deps.impl_needing_path2[type_deps.impl_needing_path2 L4]
18-
type_deps.impl_needing_path2 -->|join| type_deps.impl_to_intf_refs_path2
1910
exc_refs.resolved_refs_from[exc_refs.resolved_refs_from L5]
20-
exc_refs.resolved_refs_from -->|union| liveness.value_refs_from
2111
exc_refs.resolved_refs[exc_refs.resolved_refs L4]
22-
exc_refs.resolved_refs -->|flatMap| exc_refs.resolved_refs_from
2312
type_deps.impl_to_intf_refs_path2[type_deps.impl_to_intf_refs_path2 L5]
24-
type_deps.impl_to_intf_refs_path2 -->|union| type_deps.u2
2513
file_deps_map[file_deps_map L2]
2614
decl_refs.with_value_refs[decl_refs.with_value_refs L8]
27-
decl_refs.with_value_refs -->|join| decl_refs.combined
2815
type_deps.u1[type_deps.u1 L5]
29-
type_deps.u1 -->|union| type_deps.u2
3016
cross_file_items[cross_file_items L2]
31-
cross_file_items -->|flatMap| exception_refs_collection
3217
decl_refs.decls_by_file[decl_refs.decls_by_file L3]
33-
decl_refs.decls_by_file -->|join| decl_refs.type_decl_refs
34-
decl_refs.decls_by_file -->|join| decl_refs.value_decl_refs
3518
type_deps.impl_to_intf_refs[type_deps.impl_to_intf_refs L4]
36-
type_deps.impl_to_intf_refs -->|union| type_deps.u1
3719
solver.issues_by_file[solver.issues_by_file L17]
38-
solver.issues_by_file -->|flatMap| solver.modules_with_reported
3920
liveness.annotated_roots[liveness.annotated_roots L3]
40-
liveness.annotated_roots -->|union| liveness.all_roots
4121
solver.incorrect_dead_decls[solver.incorrect_dead_decls L16]
4222
type_deps.intf_to_impl_refs[type_deps.intf_to_impl_refs L4]
43-
type_deps.intf_to_impl_refs -->|union| type_deps.combined_refs_to
4423
type_deps.decl_by_path[type_deps.decl_by_path L3]
45-
type_deps.decl_by_path -->|join| type_deps.intf_to_impl_refs
46-
type_deps.decl_by_path -->|join| type_deps.impl_to_intf_refs_path2
47-
type_deps.decl_by_path -->|join| type_deps.impl_needing_path2
48-
type_deps.decl_by_path -->|join| type_deps.impl_to_intf_refs
49-
type_deps.decl_by_path -->|flatMap| type_deps.same_path_refs
5024
type_deps.u2[type_deps.u2 L6]
51-
type_deps.u2 -->|union| type_deps.combined_refs_to
5225
solver.live_decls[solver.live_decls L15]
53-
solver.live_decls -->|join| solver.incorrect_dead_decls
54-
solver.live_decls -->|flatMap| solver.modules_with_live
5526
type_deps.impl_decls[type_deps.impl_decls L3]
56-
type_deps.impl_decls -->|join| type_deps.impl_needing_path2
57-
type_deps.impl_decls -->|join| type_deps.impl_to_intf_refs
5827
liveness.all_roots[liveness.all_roots L12]
59-
liveness.all_roots -->|roots| liveness.live
6028
solver.dead_modules[solver.dead_modules L17]
61-
solver.dead_modules -->|join| solver.dead_module_issues
6229
liveness.external_type_refs[liveness.external_type_refs L10]
63-
liveness.external_type_refs -->|union| liveness.externally_referenced
6430
decl_refs.combined[decl_refs.combined L12]
65-
decl_refs.combined -->|flatMap| liveness.edges
6631
type_refs_from[type_refs_from L2]
67-
type_refs_from -->|union| liveness.type_refs_from
6832
liveness.type_refs_from[liveness.type_refs_from L9]
69-
liveness.type_refs_from -->|join| liveness.external_type_refs
70-
liveness.type_refs_from -->|join| decl_refs.type_decl_refs
7133
solver.dead_decls_by_file[solver.dead_decls_by_file L16]
72-
solver.dead_decls_by_file -->|flatMap| solver.issues_by_file
7334
liveness.external_value_refs[liveness.external_value_refs L7]
74-
liveness.external_value_refs -->|union| liveness.externally_referenced
7535
liveness.value_refs_from[liveness.value_refs_from L6]
76-
liveness.value_refs_from -->|join| liveness.external_value_refs
77-
liveness.value_refs_from -->|join| decl_refs.value_decl_refs
7836
value_refs_from[value_refs_from L2]
79-
value_refs_from -->|union| liveness.value_refs_from
8037
solver.modules_with_dead[solver.modules_with_dead L16]
81-
solver.modules_with_dead -->|join| solver.dead_modules
8238
solver.dead_decls[solver.dead_decls L15]
83-
solver.dead_decls -->|flatMap| solver.dead_decls_by_file
84-
solver.dead_decls -->|flatMap| solver.modules_with_dead
8539
exception_refs_collection[exception_refs_collection L3]
86-
exception_refs_collection -->|join| exc_refs.resolved_refs
8740
type_deps.intf_decls[type_deps.intf_decls L3]
88-
type_deps.intf_decls -->|join| type_deps.intf_to_impl_refs
8941
file_data_collection[file_data_collection L1]
90-
file_data_collection -->|flatMap| files
91-
file_data_collection -->|flatMap| file_deps_map
92-
file_data_collection -->|flatMap| cross_file_items
93-
file_data_collection -->|flatMap| type_refs_from
94-
file_data_collection -->|flatMap| value_refs_from
95-
file_data_collection -->|flatMap| annotations
96-
file_data_collection -->|flatMap| decls
9742
solver.dead_module_issues[solver.dead_module_issues L19]
9843
decl_refs.with_type_refs[decl_refs.with_type_refs L11]
99-
decl_refs.with_type_refs -->|join| decl_refs.combined
10044
solver.modules_with_live[solver.modules_with_live L16]
101-
solver.modules_with_live -->|join| solver.dead_modules
10245
decl_refs.type_decl_refs[decl_refs.type_decl_refs L10]
103-
decl_refs.type_decl_refs -->|join| decl_refs.with_type_refs
10446
files[files L2]
10547
solver.modules_with_reported[solver.modules_with_reported L18]
106-
solver.modules_with_reported -->|join| solver.dead_module_issues
10748
liveness.externally_referenced[liveness.externally_referenced L11]
108-
liveness.externally_referenced -->|union| liveness.all_roots
10949
liveness.edges[liveness.edges L13]
110-
liveness.edges -->|edges| liveness.live
11150
liveness.live[liveness.live L14]
112-
liveness.live -->|join| solver.live_decls
113-
liveness.live -->|join| solver.dead_decls
11451
decls[decls L2]
115-
decls -->|join| solver.live_decls
116-
decls -->|join| solver.dead_decls
117-
decls -->|join| liveness.annotated_roots
118-
decls -->|join| liveness.external_type_refs
119-
decls -->|join| liveness.external_value_refs
120-
decls -->|join| decl_refs.with_type_refs
121-
decls -->|join| decl_refs.with_value_refs
52+
type_deps.intf_to_impl_refs_join{join}
53+
liveness.external_value_refs_join{join}
54+
type_deps.impl_to_intf_refs_path2_join{join}
55+
solver.incorrect_dead_decls_join{join}
56+
liveness.value_refs_from_union{union}
57+
exc_refs.resolved_refs_join{join}
58+
type_deps.combined_refs_to_union{union}
59+
liveness.externally_referenced_union{union}
60+
solver.dead_modules_join{join}
61+
liveness.all_roots_union{union}
62+
solver.dead_module_issues_join{join}
63+
solver.dead_decls_join{join}
64+
liveness.annotated_roots_join{join}
65+
decl_refs.value_decl_refs_join{join}
66+
type_deps.impl_to_intf_refs_join{join}
67+
solver.live_decls_join{join}
68+
decl_refs.with_value_refs_join{join}
69+
liveness.type_refs_from_union{union}
70+
type_deps.impl_needing_path2_join{join}
71+
liveness.external_type_refs_join{join}
72+
liveness.live_fp{fixpoint}
73+
decl_refs.type_decl_refs_join{join}
74+
type_deps.u2_union{union}
75+
decl_refs.combined_join{join}
76+
type_deps.u1_union{union}
77+
decl_refs.with_type_refs_join{join}
78+
annotations --> solver.incorrect_dead_decls_join
79+
annotations --> liveness.annotated_roots_join
80+
exc_refs.exception_decls --> exc_refs.resolved_refs_join
81+
type_deps.same_path_refs --> type_deps.u1_union
82+
file_collection -->|flatMap| file_data_collection
83+
decl_refs.value_decl_refs --> decl_refs.with_value_refs_join
84+
type_deps.combined_refs_to -->|flatMap| type_deps.all_type_refs_from
85+
type_deps.all_type_refs_from --> liveness.type_refs_from_union
86+
type_deps.impl_needing_path2 --> type_deps.impl_to_intf_refs_path2_join
87+
exc_refs.resolved_refs_from --> liveness.value_refs_from_union
88+
exc_refs.resolved_refs -->|flatMap| exc_refs.resolved_refs_from
89+
type_deps.impl_to_intf_refs_path2 --> type_deps.u2_union
90+
decl_refs.with_value_refs --> decl_refs.combined_join
91+
type_deps.u1 --> type_deps.u2_union
92+
cross_file_items -->|flatMap| exception_refs_collection
93+
decl_refs.decls_by_file --> decl_refs.type_decl_refs_join
94+
decl_refs.decls_by_file --> decl_refs.value_decl_refs_join
95+
type_deps.impl_to_intf_refs --> type_deps.u1_union
96+
solver.issues_by_file -->|flatMap| solver.modules_with_reported
97+
liveness.annotated_roots --> liveness.all_roots_union
98+
type_deps.intf_to_impl_refs --> type_deps.combined_refs_to_union
99+
type_deps.decl_by_path --> type_deps.intf_to_impl_refs_join
100+
type_deps.decl_by_path --> type_deps.impl_to_intf_refs_path2_join
101+
type_deps.decl_by_path --> type_deps.impl_needing_path2_join
102+
type_deps.decl_by_path --> type_deps.impl_to_intf_refs_join
103+
type_deps.decl_by_path -->|flatMap| type_deps.same_path_refs
104+
type_deps.u2 --> type_deps.combined_refs_to_union
105+
solver.live_decls --> solver.incorrect_dead_decls_join
106+
solver.live_decls -->|flatMap| solver.modules_with_live
107+
type_deps.impl_decls --> type_deps.impl_needing_path2_join
108+
type_deps.impl_decls --> type_deps.impl_to_intf_refs_join
109+
liveness.all_roots --> liveness.live_fp
110+
solver.dead_modules --> solver.dead_module_issues_join
111+
liveness.external_type_refs --> liveness.externally_referenced_union
112+
decl_refs.combined -->|flatMap| liveness.edges
113+
type_refs_from --> liveness.type_refs_from_union
114+
liveness.type_refs_from --> liveness.external_type_refs_join
115+
liveness.type_refs_from --> decl_refs.type_decl_refs_join
116+
solver.dead_decls_by_file -->|flatMap| solver.issues_by_file
117+
liveness.external_value_refs --> liveness.externally_referenced_union
118+
liveness.value_refs_from --> liveness.external_value_refs_join
119+
liveness.value_refs_from --> decl_refs.value_decl_refs_join
120+
value_refs_from --> liveness.value_refs_from_union
121+
solver.modules_with_dead --> solver.dead_modules_join
122+
solver.dead_decls -->|flatMap| solver.dead_decls_by_file
123+
solver.dead_decls -->|flatMap| solver.modules_with_dead
124+
exception_refs_collection --> exc_refs.resolved_refs_join
125+
type_deps.intf_decls --> type_deps.intf_to_impl_refs_join
126+
file_data_collection -->|flatMap| files
127+
file_data_collection -->|flatMap| file_deps_map
128+
file_data_collection -->|flatMap| cross_file_items
129+
file_data_collection -->|flatMap| type_refs_from
130+
file_data_collection -->|flatMap| value_refs_from
131+
file_data_collection -->|flatMap| annotations
132+
file_data_collection -->|flatMap| decls
133+
decl_refs.with_type_refs --> decl_refs.combined_join
134+
solver.modules_with_live --> solver.dead_modules_join
135+
decl_refs.type_decl_refs --> decl_refs.with_type_refs_join
136+
solver.modules_with_reported --> solver.dead_module_issues_join
137+
liveness.externally_referenced --> liveness.all_roots_union
138+
liveness.edges --> liveness.live_fp
139+
liveness.live --> solver.live_decls_join
140+
liveness.live --> solver.dead_decls_join
141+
decls --> solver.live_decls_join
142+
decls --> solver.dead_decls_join
143+
decls --> liveness.annotated_roots_join
144+
decls --> liveness.external_type_refs_join
145+
decls --> liveness.external_value_refs_join
146+
decls --> decl_refs.with_type_refs_join
147+
decls --> decl_refs.with_value_refs_join
122148
decls -->|flatMap| decl_refs.decls_by_file
123149
decls -->|flatMap| exc_refs.exception_decls
124150
decls -->|flatMap| type_deps.intf_decls
125151
decls -->|flatMap| type_deps.impl_decls
126152
decls -->|flatMap| type_deps.decl_by_path
153+
type_deps.intf_to_impl_refs_join --> type_deps.intf_to_impl_refs
154+
liveness.external_value_refs_join --> liveness.external_value_refs
155+
type_deps.impl_to_intf_refs_path2_join --> type_deps.impl_to_intf_refs_path2
156+
solver.incorrect_dead_decls_join --> solver.incorrect_dead_decls
157+
liveness.value_refs_from_union --> liveness.value_refs_from
158+
exc_refs.resolved_refs_join --> exc_refs.resolved_refs
159+
type_deps.combined_refs_to_union --> type_deps.combined_refs_to
160+
liveness.externally_referenced_union --> liveness.externally_referenced
161+
solver.dead_modules_join --> solver.dead_modules
162+
liveness.all_roots_union --> liveness.all_roots
163+
solver.dead_module_issues_join --> solver.dead_module_issues
164+
solver.dead_decls_join --> solver.dead_decls
165+
liveness.annotated_roots_join --> liveness.annotated_roots
166+
decl_refs.value_decl_refs_join --> decl_refs.value_decl_refs
167+
type_deps.impl_to_intf_refs_join --> type_deps.impl_to_intf_refs
168+
solver.live_decls_join --> solver.live_decls
169+
decl_refs.with_value_refs_join --> decl_refs.with_value_refs
170+
liveness.type_refs_from_union --> liveness.type_refs_from
171+
type_deps.impl_needing_path2_join --> type_deps.impl_needing_path2
172+
liveness.external_type_refs_join --> liveness.external_type_refs
173+
liveness.live_fp --> liveness.live
174+
decl_refs.type_decl_refs_join --> decl_refs.type_decl_refs
175+
type_deps.u2_union --> type_deps.u2
176+
decl_refs.combined_join --> decl_refs.combined
177+
type_deps.u1_union --> type_deps.u1
178+
decl_refs.with_type_refs_join --> decl_refs.with_type_refs
127179

0 commit comments

Comments
 (0)