Skip to content

Commit 1b84c21

Browse files
committed
Adjust callGraph program to print out JSON data
1 parent 9fad4d7 commit 1b84c21

File tree

4 files changed

+116
-10
lines changed

4 files changed

+116
-10
lines changed

beluga.opam

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ depends: [
2525
"omd" {>= "1.3.2"}
2626
"uri" {>= "4.2.0"}
2727
"ocamlformat" {= "0.25.1" & with-test}
28-
"yojson" {>= "2.0.2" & with-test}
28+
"yojson" {>= "2.0.2"}
2929
"ounit2" {>= "2.2.6" & with-test}
3030
"bisect_ppx" {>= "2.8.1" & with-test}
3131
"odoc" {>= "2.2.0" & with-doc}

dune-project

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,7 @@
4949
(= 0.25.1)
5050
:with-test))
5151
(yojson
52-
(and
53-
(>= 2.0.2)
54-
:with-test))
52+
(>= 2.0.2))
5553
(ounit2
5654
(and
5755
(>= 2.2.6)

src/beluga/callGraph.ml

Lines changed: 113 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
[@@@warning "+A-4-44"]
1+
[@@@warning "+A-4-32-44"]
22

33
open! Support
44
open Beluga
@@ -27,10 +27,13 @@ module CallsRecord : sig
2727

2828
val add_program_call : t -> Id.cid_prog -> unit
2929

30-
val has_program_call : t -> Id.cid_prog -> bool [@@warning "-32"]
30+
val has_program_call : t -> Id.cid_prog -> bool
3131

3232
(** [iter f r] iteratively applies [f] over the program calls added to [r]. *)
3333
val iter : (Id.cid_prog -> unit) -> t -> unit
34+
35+
(** [to_set r] is the set of called program IDs recorded in [r]. *)
36+
val to_set : t -> Id.Prog.Set.t
3437
end = struct
3538
type t = { mutable called_programs : Id.Prog.Set.t }
3639

@@ -43,6 +46,8 @@ end = struct
4346
Id.Prog.Set.mem prog calls.called_programs
4447

4548
let iter f calls = Id.Prog.Set.iter f calls.called_programs
49+
50+
let to_set calls = calls.called_programs
4651
end
4752

4853
type calls_record = CallsRecord.t
@@ -206,6 +211,10 @@ module CallGraphState : sig
206211
graph. *)
207212
val add_program_calls_record : t -> Id.cid_prog -> calls_record -> unit
208213

214+
(** [get_immediate_dependencies state cid] is the set of direct
215+
dependencies of theorem [cid] in [state]. *)
216+
val get_immediate_dependencies : t -> Id.cid_prog -> Id.Prog.Set.t
217+
209218
(** [compute_program_call_dependencies state cid] is the set of transitive
210219
dependencies of theorem [cid] in [state]. That is, it is the set of
211220
nodes reachable from [cid] in the call graph.
@@ -219,7 +228,6 @@ module CallGraphState : sig
219228
whenever memoized results have to be invalidated and recomputed from
220229
scratch. *)
221230
val clear_memoized_call_dependencies : t -> unit
222-
[@@warning "-32"]
223231

224232
(** [set_program_display_name state cid n] sets [n] as the name to use to
225233
refer to [cid] in [state]. This is only used for pretty-printing the
@@ -253,10 +261,19 @@ end = struct
253261
Id.Prog.Hashtbl.add state.program_calls_records cid calls;
254262
clear_memoized_call_dependencies state
255263

264+
let get_immediate_dependencies state cid =
265+
match Id.Prog.Hashtbl.find_opt state.program_calls_records cid with
266+
| Option.None -> Error.raise (Unknown_program cid)
267+
| Option.Some calls_record -> CallsRecord.to_set calls_record
268+
256269
let compute_program_call_dependencies state cid =
257270
let to_visit = Queue.create () in
258271
let visited = Stdlib.ref Id.Prog.Set.empty in
259-
Queue.push cid to_visit;
272+
(* Add direct dependencies to the [to_visit] queue *)
273+
(match Id.Prog.Hashtbl.find_opt state.program_calls_records cid with
274+
| Option.None -> Error.raise (Unknown_program cid)
275+
| Option.Some calls_record ->
276+
CallsRecord.iter (fun x -> Queue.add x to_visit) calls_record);
260277
while Bool.not (Queue.is_empty to_visit) do
261278
let current_cid = Queue.pop to_visit in
262279
if Bool.not (Id.Prog.Set.mem current_cid !visited) then (
@@ -415,6 +432,95 @@ and pp_call_graph_sgn_declaration :
415432
| Sgn.Val _ ->
416433
()
417434

435+
(** {2 Dependency Data to JSON} *)
436+
437+
let json_of_location : Location.t -> Yojson.Safe.t =
438+
fun location ->
439+
if Location.is_ghost location then `Null
440+
else
441+
`Assoc
442+
[ ("filename", `String (Location.filename location))
443+
; ("start_line", `Int (Location.start_line location))
444+
; ("start_column", `Int (Location.start_column location))
445+
; ("stop_line", `Int (Location.stop_line location))
446+
; ("stop_column", `Int (Location.stop_column location))
447+
]
448+
449+
let rec json_of_call_graph_sgn : state -> Sgn.sgn -> Yojson.Safe.t =
450+
fun state sgn ->
451+
`List
452+
(List.flatten
453+
(List1.to_list (List1.map (json_of_call_graph_sgn_file state) sgn)))
454+
455+
and json_of_call_graph_sgn_file : state -> Sgn.sgn_file -> Yojson.Safe.t list
456+
=
457+
fun state { Sgn.entries; _ } ->
458+
let programs =
459+
entries
460+
|> List.map (dependencies_to_json_call_graph_sgn_entry state)
461+
|> List.flatten
462+
in
463+
programs
464+
465+
and dependencies_to_json_call_graph_sgn_entry :
466+
state -> Sgn.entry -> Yojson.Safe.t list =
467+
fun state -> function
468+
| Sgn.Declaration { declaration; _ } ->
469+
json_of_call_graph_sgn_declaration state declaration
470+
| Sgn.Pragma _
471+
| Sgn.Comment _ ->
472+
[]
473+
474+
and json_of_cid_prog : Id.cid_prog -> Yojson.Safe.t =
475+
fun cid -> `Int (Id.Prog.to_int cid)
476+
477+
and json_of_call_graph_sgn_declaration :
478+
state -> Sgn.decl -> Yojson.Safe.t list =
479+
fun state -> function
480+
| Sgn.Theorem { cid; _ } ->
481+
let display_name =
482+
cid |> CallGraphState.lookup_program_display_name state
483+
in
484+
let immediate_dependencies =
485+
cid
486+
|> CallGraphState.get_immediate_dependencies state
487+
|> Id.Prog.Set.to_seq |> Seq.map json_of_cid_prog |> List.of_seq
488+
in
489+
let dependencies =
490+
cid
491+
|> CallGraphState.compute_program_call_dependencies state
492+
|> Id.Prog.Set.to_seq |> Seq.map json_of_cid_prog |> List.of_seq
493+
in
494+
[ `Assoc
495+
[ ("id", json_of_cid_prog cid)
496+
; ( "qualified_identifier"
497+
, `String (Qualified_identifier.show display_name) )
498+
; ( "location"
499+
, json_of_location (Qualified_identifier.location display_name)
500+
)
501+
; ("immediate_dependencies", `List immediate_dependencies)
502+
; ("transitive_dependencies", `List dependencies)
503+
]
504+
]
505+
| Sgn.Recursive_declarations { declarations; _ } ->
506+
List1.to_list declarations
507+
|> List.map (json_of_call_graph_sgn_declaration state)
508+
|> List.flatten
509+
| Sgn.Module { entries; _ } ->
510+
entries
511+
|> List.map (dependencies_to_json_call_graph_sgn_entry state)
512+
|> List.flatten
513+
| Sgn.Typ _
514+
| Sgn.Const _
515+
| Sgn.CompTyp _
516+
| Sgn.CompCotyp _
517+
| Sgn.CompConst _
518+
| Sgn.CompDest _
519+
| Sgn.CompTypAbbrev _
520+
| Sgn.Schema _
521+
| Sgn.Val _ ->
522+
[]
523+
418524
(** {2 Driver} *)
419525

420526
(** CLI usage: [dune exec beluga_call_graph ./path-to-signature.cfg] *)
@@ -425,7 +531,9 @@ let main () =
425531
| [ file ] ->
426532
let _, sgn = Load.load_fresh file in
427533
let call_graph = construct_call_graph_state sgn in
428-
pp_call_graph_sgn call_graph Format.std_formatter sgn
534+
Format.fprintf Format.std_formatter "%a@."
535+
(Yojson.Safe.pretty_print ~std:true)
536+
(json_of_call_graph_sgn call_graph sgn)
429537
| [] ->
430538
Format.fprintf Format.err_formatter
431539
"Provide the file path to the Beluga signature.@.";

src/beluga/dune

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99
(name callGraph)
1010
(public_name beluga_call_graph)
1111
(package beluga)
12-
(libraries support beluga beluga_syntax)
12+
(libraries support beluga beluga_syntax yojson)
1313
(modules callGraph))

0 commit comments

Comments
 (0)