Source file pds_reachability_analysis.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
(**
This module defines the actual PDS reachability analysis.
*)
open Batteries;;
open Jhupllib;;
open Eager_nondeterminism;;
open Pp_utils;;
open Yojson_utils;;
let lazy_logger = Logger_utils.make_lazy_logger "Pds_reachability_analysis";;
module type Analysis =
sig
include Pds_reachability_types.Types;;
(** The type of edge-generating functions used in this analysis. *)
type edge_function = State.t -> (Stack_action.t list * Terminus.t) Enum.t
(** The type of functions to generate untargeted dynamic pop actions in this
analysis. *)
type untargeted_dynamic_pop_action_function =
State.t -> Untargeted_dynamic_pop_action.t Enum.t
(** The type of a reachability analysis in this module. *)
type analysis
(** The type of reachability analysis logging functions. These functions
are called whenever the associated analysis is stepped. The two arguments
are the analysis before stepping and the analysis after. *)
type analysis_logging_function = analysis -> analysis -> unit
(** The empty analysis. This analysis has no states, edges, or edge
functions. *)
val empty :
?logging_function:(analysis_logging_function option) -> unit -> analysis
(** Adds a single edge to a reachability analysis. *)
val add_edge
: State.t -> Stack_action.t list -> State.t -> analysis -> analysis
(** Adds a function to generate edges for a reachability analysis. Given a
source node, the function generates edges from that source node. The
function must be pure; for a given source node, it must generate all edges
that it can generate on the first call. *)
val add_edge_function : edge_function -> analysis -> analysis
(** Adds an untargeted pop action to a reachability analysis. Untargeted pop
action are similar to targeted pop actions except that they are not
created as an edge with a target node; instead, the target is decided in
some way by the pushed element that the untargeted dynamic pop is
consuming. *)
val add_untargeted_dynamic_pop_action
: State.t -> Untargeted_dynamic_pop_action.t -> analysis -> analysis
(** Adds a function to generate untargeted dynamic pop ations for a
reachability analysis. Given a source node, the function generates
untargeted actions from that source node. The function must be pure; for
a given source, it must generate all actions that it can generate on the
first call. *)
val add_untargeted_dynamic_pop_action_function
: untargeted_dynamic_pop_action_function -> analysis -> analysis
(** Adds a state and initial stack element to the analysis. This permits the
state to be used as the source state of a call to [get_reachable_states].
*)
val add_start_state
: State.t -> Stack_action.t list -> analysis -> analysis
(** Determines whether the reachability analysis is closed. *)
val is_closed : analysis -> bool
(** Takes a step toward closing a given reachability analysis. If the
analysis is already closed, it is returned unchanged. *)
val closure_step : analysis -> analysis
(** Fully closes the provided analysis. *)
val fully_close : analysis -> analysis
(** Determines the states which are reachable from a given state and initial
stack element. This state must have been added to the analysis
previously. If the analysis is not fully closed, then the enumeration of
reachable states may be incomplete. *)
val get_reachable_states
: State.t -> Stack_action.t list -> analysis -> State.t Enum.t
(** Pretty-printing function for the analysis. *)
val pp_analysis : analysis pretty_printer
val show_analysis : analysis -> string
(** An exception raised when a reachable state query occurs before the state
is added as a start state. *)
exception Reachability_request_for_non_start_state of State.t;;
(** Determines the size of the provided analysis in terms of both node and
edge count (respectively). *)
val get_size : analysis -> int * int
(** Determines the amount of work done on a particular analysis. *)
val get_work_count : analysis -> int
(** Extracts a subset of information about an analysis state as JSON data.
Some parts of the analysis state (such as edge functions) will be
elided as they cannot be represented. *)
val dump_yojson : analysis -> Yojson.Safe.json
(** Extracts a subset of information about an analysis state as JSON data.
This extraction generates a /difference/ between two reachability analyses,
giving values appearing in the latter but not the former. This function
assumes the latter is a strict superset of the former; any values appearing
in the former and not the latter are ignored. The format of this dump is
identical to that given by [dump_yojson]. *)
val dump_yojson_delta : analysis -> analysis -> Yojson.Safe.json
end;;
module Make
(Basis : Pds_reachability_basis.Basis)
(Dph : Pds_reachability_types_stack.Dynamic_pop_handler
with module Stack_element = Basis.Stack_element
and module State = Basis.State)
(Work_collection_template_impl :
Pds_reachability_work_collection.Work_collection_template)
: Analysis
with module State = Basis.State
and module Stack_element = Basis.Stack_element
and module Targeted_dynamic_pop_action = Dph.Targeted_dynamic_pop_action
and module Untargeted_dynamic_pop_action = Dph.Untargeted_dynamic_pop_action
and module Stack_action = Dph.Stack_action
and module Terminus = Dph.Terminus
=
struct
module Types = Pds_reachability_types.Make(Basis)(Dph);;
module Work = Pds_reachability_work.Make(Basis)(Types);;
module Work_collection_impl = Work_collection_template_impl(Work);;
module Structure = Pds_reachability_structure.Make(Basis)(Dph)(Types);;
include Types;;
open Types.Stack_action.T;;
open Types.Terminus.T;;
type edge_function = State.t -> (Stack_action.t list * Terminus.t) Enum.t;;
type untargeted_dynamic_pop_action_function =
State.t -> Untargeted_dynamic_pop_action.t Enum.t;;
exception Reachability_request_for_non_start_state of State.t;;
module State_set = struct
module Impl = Set.Make(Basis.State);;
include Impl;;
include Set_pp(Impl)(Basis.State);;
include Set_to_yojson(Impl)(Basis.State);;
end;;
module Node_set = struct
module Impl = Set.Make(Node);;
include Impl;;
include Set_pp(Impl)(Node);;
include Set_to_yojson(Impl)(Node);;
end;;
module Node_map = struct
module Impl = Map.Make(Node);;
include Impl;;
include Map_pp(Impl)(Node);;
include Map_to_yojson(Impl)(Node);;
end;;
type node_awareness =
| Seen
| Expanded
[@@deriving eq, show, to_yojson]
let _ = show_node_awareness;;
type analysis =
{ node_awareness_map : node_awareness Node_map.t
; known_states : State_set.t
; start_nodes : Node_set.t
; reachability : Structure.t
; edge_functions : edge_function list
[@printer fun formatter functions ->
Format.fprintf formatter "(length = %d)"
(List.length functions)]
; untargeted_dynamic_pop_action_functions :
untargeted_dynamic_pop_action_function list
[@printer fun formatter functions ->
Format.fprintf formatter "(length = %d)"
(List.length functions)]
; work_collection : Work_collection_impl.work_collection
; work_count : int
; logging_function : analysis_logging_function option
[@printer fun formatter fn ->
Format.fprintf formatter "logging function %s"
(if Option.is_some fn then "present" else "absent")]
}
[@@deriving show]
and analysis_logging_function = analysis -> analysis -> unit
;;
let _ = pp_analysis_logging_function;;
let _ = show_analysis_logging_function;;
let add_work work analysis =
lazy_logger `trace (fun _ ->
Printf.sprintf "Adding work: %s" (Work.show work)
);
match work with
| Work.Expand_node node ->
if Node_map.mem node analysis.node_awareness_map
then analysis
else
{ analysis with
work_collection =
Work_collection_impl.offer work analysis.work_collection
; node_awareness_map =
Node_map.add node Seen analysis.node_awareness_map
}
| Work.Introduce_edge edge ->
if Structure.has_edge edge analysis.reachability
then analysis
else
{ analysis with
work_collection =
Work_collection_impl.offer work analysis.work_collection
}
| Work.Introduce_untargeted_dynamic_pop(from_node,action) ->
if Structure.has_untargeted_dynamic_pop_action
from_node action analysis.reachability
then analysis
else
{ analysis with
work_collection =
Work_collection_impl.offer work analysis.work_collection
}
;;
let add_works works analysis = Enum.fold (flip add_work) analysis works;;
let create_work_for_path
(from_node : node)
(actions : Stack_action.t list)
(destination : intermediate_destination)
: Work.t =
match actions,destination with
| [], Static_destination to_node ->
Work.Introduce_edge {source=from_node;target=to_node;edge_action=Nop}
| [], Dynamic_destination udynpop ->
Work.Introduce_untargeted_dynamic_pop(from_node, udynpop)
| action::[], Static_destination to_node ->
Work.Introduce_edge {source=from_node;target=to_node;edge_action=action}
| action::actions, _ ->
let to_node' = Intermediate_node(destination, actions) in
Work.Introduce_edge {source=from_node;target=to_node';edge_action=action}
;;
let terminus_to_destination terminus =
match terminus with
| Static_terminus state -> Static_destination (State_node state)
| Dynamic_terminus udynpop -> Dynamic_destination udynpop
;;
let get_size analysis =
let reachability = analysis.reachability in
let node_count = Enum.count @@ Structure.enumerate_nodes reachability in
let edge_count = Enum.count @@ Structure.enumerate_edges reachability in
(node_count, edge_count)
;;
let empty ?logging_function:(log_fn=None) () =
{ node_awareness_map = Node_map.empty
; known_states = State_set.empty
; start_nodes = Node_set.empty
; reachability = Structure.empty
; edge_functions = []
; untargeted_dynamic_pop_action_functions = []
; work_collection = Work_collection_impl.empty
; work_count = 0
; logging_function = log_fn
};;
let add_edge from_state stack_action_list to_state analysis =
let from_node = State_node from_state in
let to_node = State_node to_state in
let path_work =
create_work_for_path
from_node stack_action_list (Static_destination to_node)
in
analysis
|> add_work (Work.Expand_node from_node)
|> add_work path_work
;;
let add_edge_function (edge_function : edge_function) analysis =
let work : Work.t Enum.t =
let open Nondeterminism_monad in Nondeterminism_monad.enum @@
let%bind from_state = pick_enum @@ State_set.enum analysis.known_states in
let%bind (actions,terminus) = pick_enum @@ edge_function from_state in
return @@ create_work_for_path
(State_node from_state) actions (terminus_to_destination terminus)
in
{ (add_works work analysis) with
edge_functions = edge_function :: analysis.edge_functions
}
;;
let add_untargeted_dynamic_pop_action from_state pop_action analysis =
let from_node = State_node from_state in
analysis
|> add_work (Work.Expand_node from_node)
|> add_work (Work.Introduce_untargeted_dynamic_pop(from_node, pop_action))
;;
let add_untargeted_dynamic_pop_action_function
(pop_action_fn : untargeted_dynamic_pop_action_function) analysis =
let work : Work.t Enum.t =
let open Nondeterminism_monad in Nondeterminism_monad.enum @@
let%bind from_state = pick_enum @@ State_set.enum analysis.known_states in
let%bind action = pick_enum @@ pop_action_fn from_state in
let from_node = State_node from_state in
return @@ Work.Introduce_untargeted_dynamic_pop(from_node, action)
in
{ (add_works work analysis) with
untargeted_dynamic_pop_action_functions =
pop_action_fn :: analysis.untargeted_dynamic_pop_action_functions
}
;;
let add_start_state state stack_actions analysis =
let start_node =
Intermediate_node(Static_destination(State_node(state)), stack_actions)
in
let entry =
Node_map.Exceptionless.find start_node analysis.node_awareness_map
in
if entry <> Some Expanded
then
{ (analysis |> add_work (Work.Expand_node(start_node))) with
start_nodes = analysis.start_nodes |> Node_set.add start_node
}
else
let edge_work =
let open Nondeterminism_monad in Nondeterminism_monad.enum @@
let%bind middle_node =
pick_enum @@
Structure.find_nop_edges_by_source start_node analysis.reachability
in
let%bind target_node =
pick_enum @@
Structure.find_nop_edges_by_source middle_node analysis.reachability
in
return @@ Work.Introduce_edge
({source=start_node;target=target_node;edge_action=Nop})
in
add_works edge_work analysis
;;
let is_closed analysis =
Work_collection_impl.is_empty analysis.work_collection
;;
let closure_step analysis =
let (new_work_collection, work_opt) =
Work_collection_impl.take analysis.work_collection
in
let result_analysis =
match work_opt with
| None -> analysis
| Some work ->
lazy_logger `trace
(fun () ->
Printf.sprintf "PDS reachability closure step: %s"
(Work.show work)
);
let analysis = { analysis with work_collection = new_work_collection } in
let can_expand node =
let entry =
Node_map.Exceptionless.find node analysis.node_awareness_map
in
entry <> Some Expanded
in
match work with
| Work.Expand_node node ->
begin
match node with
| State_node(state) ->
let edge_work =
analysis.edge_functions
|> List.enum
|> Enum.map (fun f -> f state)
|> Enum.concat
|> Enum.map (fun (actions,terminus) ->
create_work_for_path
(State_node state)
actions
(terminus_to_destination terminus)
)
in
let popdynu_work =
analysis.untargeted_dynamic_pop_action_functions
|> List.enum
|> Enum.map (fun f -> f state)
|> Enum.concat
|> Enum.map
(fun action ->
Work.Introduce_untargeted_dynamic_pop(node, action)
)
in
{ (analysis |> add_works edge_work |> add_works popdynu_work) with
known_states = analysis.known_states |> State_set.add state
; node_awareness_map =
analysis.node_awareness_map
|> Node_map.add node Expanded
}
| Intermediate_node(destination, actions) ->
let edge_work =
create_work_for_path node actions destination
in
{ (analysis
|> add_work edge_work
) with
node_awareness_map =
Node_map.add node Expanded analysis.node_awareness_map
}
end
| Work.Introduce_edge edge ->
let { source = from_node
; target = to_node
; edge_action = action
} = edge
in
let analysis' =
let edge_work =
match action with
| Nop ->
let push_closure_work =
let open Nondeterminism_monad in Nondeterminism_monad.enum @@
let%bind (from_node', element) =
pick_enum @@ Structure.find_push_edges_by_target
from_node analysis.reachability
in
return @@ Work.Introduce_edge(
{ source = from_node'
; target = to_node
; edge_action = Push element
}
)
in
let left_nop_work =
let open Nondeterminism_monad in
Nondeterminism_monad.enum @@
let%bind from_node' =
pick_enum @@ Structure.find_nop_edges_by_target
from_node analysis.reachability
in
[%guard (Node_set.mem from_node' analysis.start_nodes)];
return @@ Work.Introduce_edge(
{ source = from_node'
; target = to_node
; edge_action = Nop
}
)
in
let right_nop_work =
let open Nondeterminism_monad in
Nondeterminism_monad.enum @@
begin
[%guard (Node_set.mem from_node analysis.start_nodes)];
let%bind to_node' =
pick_enum @@ Structure.find_nop_edges_by_source
to_node analysis.reachability
in
return @@ Work.Introduce_edge(
{ source = from_node
; target = to_node'
; edge_action = Nop
}
)
end
in
Enum.concat @@ List.enum
[ push_closure_work
; left_nop_work
; right_nop_work
]
| Push k ->
let nop_work =
let open Nondeterminism_monad in Nondeterminism_monad.enum @@
let%bind to_node' =
pick_enum @@ Structure.find_nop_edges_by_source
to_node analysis.reachability
in
return @@ Work.Introduce_edge(
{ source = from_node
; target = to_node'
; edge_action = Push k
})
in
let pop_work =
let open Nondeterminism_monad in Nondeterminism_monad.enum @@
let%bind (to_node', element) =
pick_enum @@ Structure.find_pop_edges_by_source
to_node analysis.reachability
in
[%guard (Stack_element.equal k element)];
return @@ Work.Introduce_edge(
{ source = from_node
; target = to_node'
; edge_action = Nop
})
in
let popdynt_work =
let open Nondeterminism_monad in Nondeterminism_monad.enum @@
let%bind (to_node, popdynt_action) =
pick_enum @@
Structure.find_targeted_dynamic_pop_edges_by_source
to_node analysis.reachability
in
let%bind stack_actions =
pick_enum @@ Dph.perform_targeted_dynamic_pop k popdynt_action
in
return @@ create_work_for_path
from_node stack_actions (Static_destination to_node)
in
let popdynu_work =
let open Nondeterminism_monad in Nondeterminism_monad.enum @@
let%bind popdynu_action =
pick_enum @@
Structure.find_untargeted_dynamic_pop_actions_by_source
to_node analysis.reachability
in
let%bind (stack_actions,terminus) =
pick_enum @@ Dph.perform_untargeted_dynamic_pop
k popdynu_action
in
return @@ create_work_for_path
from_node stack_actions (terminus_to_destination terminus)
in
Enum.concat @@ List.enum
[ nop_work ; pop_work ; popdynt_work ; popdynu_work ]
| Pop k ->
let open Nondeterminism_monad in Nondeterminism_monad.enum @@
let%bind (from_node', element) =
pick_enum @@ Structure.find_push_edges_by_target
from_node analysis.reachability
in
[%guard (Stack_element.equal element k)];
return @@ Work.Introduce_edge(
{ source = from_node'
; target = to_node
; edge_action = Nop
})
| Pop_dynamic_targeted action ->
let open Nondeterminism_monad in Nondeterminism_monad.enum @@
let%bind (from_node', element) =
pick_enum @@ Structure.find_push_edges_by_target
from_node analysis.reachability
in
let%bind stack_actions =
pick_enum @@ Dph.perform_targeted_dynamic_pop element action
in
return @@ create_work_for_path
from_node' stack_actions (Static_destination to_node)
in
let expand_work =
match action with
| Pop _
| Pop_dynamic_targeted _ ->
Enum.empty ()
| _ when can_expand to_node ->
Enum.singleton (Work.Expand_node to_node)
| _ ->
Enum.empty ()
in
add_works (Enum.append edge_work expand_work) analysis
in
{ analysis' with
reachability = analysis'.reachability |> Structure.add_edge edge
}
| Work.Introduce_untargeted_dynamic_pop(from_node,action) ->
let analysis' =
let edge_work =
let open Nondeterminism_monad in Nondeterminism_monad.enum @@
let%bind (from_node', element) =
pick_enum @@ Structure.find_push_edges_by_target
from_node analysis.reachability
in
let%bind (stack_actions, terminus) =
pick_enum @@ Dph.perform_untargeted_dynamic_pop element action
in
return @@ create_work_for_path
from_node' stack_actions (terminus_to_destination terminus)
in
add_works edge_work analysis
in
{ analysis' with
reachability = analysis'.reachability
|> Structure.add_untargeted_dynamic_pop_action
from_node action
}
in
let logged_analysis =
{ result_analysis with
work_count = result_analysis.work_count + 1
}
in
begin
match logged_analysis.logging_function with
| Some f -> f analysis logged_analysis
| None -> ()
end;
logged_analysis
;;
let rec fully_close analysis =
if is_closed analysis
then analysis
else fully_close @@ closure_step analysis
;;
let get_reachable_states state stack_actions analysis =
lazy_logger `debug (fun () ->
let (nodes,edges) = get_size analysis in
Printf.sprintf
"get_reachable_states: analysis has %d nodes and %d edges"
nodes edges
);
let node =
Intermediate_node(Static_destination(State_node(state)), stack_actions)
in
if Node_set.mem node analysis.start_nodes
then
analysis.reachability
|> Structure.find_nop_edges_by_source node
|> Enum.filter_map
(fun node ->
match node with
| State_node state -> Some state
| Intermediate_node _ -> None
)
else
raise @@ Reachability_request_for_non_start_state state
;;
let get_work_count analysis = analysis.work_count;;
let dump_yojson analysis =
`Assoc
[ ( "node_awareness_map"
, Node_map.to_yojson
node_awareness_to_yojson
analysis.node_awareness_map
)
; ( "known_states"
, State_set.to_yojson analysis.known_states
)
; ( "start_nodes"
, Node_set.to_yojson analysis.start_nodes
)
; ( "reachability"
, Structure.to_yojson analysis.reachability
)
; ( "edge_function_count"
, `Int (List.length analysis.edge_functions)
)
; ( "untargeted_dynamic_pop_action_function_count"
, `Int (List.length analysis.untargeted_dynamic_pop_action_functions)
)
; ( "work_collection"
, Work_collection_impl.to_yojson analysis.work_collection
)
; ( "work_count"
, `Int analysis.work_count
)
; ( "logging_function_present"
, `Bool (Option.is_some analysis.logging_function)
)
]
;;
let dump_yojson_delta a1 a2 =
`Assoc
(List.filter_map identity
[
begin
let enum_new_mappings _ =
a2.node_awareness_map
|> Node_map.enum
|> Enum.filter
(fun (k,v) ->
match Node_map.Exceptionless.find
k a1.node_awareness_map with
| Some(v') -> not @@ equal_node_awareness v v'
| None -> true
)
in
if Enum.is_empty @@ enum_new_mappings ()
then None
else Some
( "node_awareness_map"
, Yojson_utils.map_to_yojson
Node.to_yojson
node_awareness_to_yojson
enum_new_mappings a2
)
end
;
begin
let new_known_states =
State_set.diff a2.known_states a1.known_states
in
if State_set.is_empty new_known_states
then None
else Some
( "known_states"
, State_set.to_yojson new_known_states
)
end
;
begin
let new_start_nodes =
Node_set.diff a2.start_nodes a1.start_nodes
in
if Node_set.is_empty new_start_nodes
then None
else Some
( "start_nodes"
, Node_set.to_yojson new_start_nodes
)
end
;
begin
let reachability_json =
Structure.to_yojson_delta a1.reachability a2.reachability
in
match reachability_json with
| `Assoc [] -> None
| _ -> Some ( "reachability", reachability_json )
end
;
begin
let a1_edge_function_count = List.length a1.edge_functions in
let a2_edge_function_count = List.length a2.edge_functions in
if a1_edge_function_count = a2_edge_function_count
then None
else Some ("edge_function_count", `Int a2_edge_function_count)
end
;
begin
let a1_udpa_count =
List.length a1.untargeted_dynamic_pop_action_functions
in
let a2_udpa_count =
List.length a2.untargeted_dynamic_pop_action_functions
in
if a1_udpa_count = a2_udpa_count
then None
else Some ("untargeted_dynamic_pop_action_function_count",
`Int a2_udpa_count)
end
;
begin
if Enum.equal Work.equal
(Work_collection_impl.enum a1.work_collection)
(Work_collection_impl.enum a2.work_collection)
then None
else
Some
( "work_collection"
, Work_collection_impl.to_yojson a2.work_collection
)
end
; Some ( "work_count"
, `Int a2.work_count
)
]
)
;;
end;;