Skip to content

Commit 436f1d0

Browse files
authored
Merge pull request #1075 from ejgallego/metanejo
[plugin] Plugin for extracting data from Rocq documents.
2 parents 662a34d + 60efca5 commit 436f1d0

File tree

4 files changed

+387
-0
lines changed

4 files changed

+387
-0
lines changed

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@
7676
it. Documentation is treated as Markdown. This feature is
7777
experimental and limited to documentation comments on the same file.
7878
(@ejgallego, #590)
79+
- [plugins] New plugin to dump document data (@ejgallego, Stefania
80+
Dumbrava, #1075)
7981

8082
# coq-lsp 0.2.4: (W)Activation
8183
------------------------------

plugins/metanejo/dune

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
(library
2+
(name Metanejo_plugin)
3+
(public_name coq-lsp.plugin.metanejo)
4+
(preprocess
5+
(pps ppx_deriving_yojson))
6+
(libraries coq-lsp.fleche coq-lsp.lsp))

plugins/metanejo/main.ml

Lines changed: 379 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,379 @@
1+
module Lsp = Fleche_lsp
2+
open Fleche
3+
4+
(** TODO:
5+
6+
- new edge "defined_in" object file
7+
- new edge "uses" for Require
8+
- new attributes "uses" "in_body" / "in_type" *)
9+
10+
(* Put these in an utility function for plugins *)
11+
(* Duplicated with rq_document *)
12+
let _of_execution ~io ~what (v : (_, _) Coq.Protect.E.t) =
13+
match v with
14+
| { r; feedback = _ } -> (
15+
match r with
16+
| Coq.Protect.R.Completed (Ok goals) -> goals
17+
| Coq.Protect.R.Completed (Error (Anomaly { msg; _ }))
18+
| Coq.Protect.R.Completed (Error (User { msg; _ })) ->
19+
let lvl = Io.Level.Error in
20+
Io.Report.msg ~io ~lvl "error when retrieving %s: %a" what Pp.pp_with msg;
21+
None
22+
| Coq.Protect.R.Interrupted -> None)
23+
24+
module Kind = struct
25+
type t =
26+
| Notation
27+
| Definition
28+
| Theorem
29+
| Other of string
30+
| Unknown
31+
| Lemma
32+
[@@deriving to_yojson]
33+
34+
let to_string = function
35+
| Notation -> "Notation"
36+
| Definition -> "Definition"
37+
| Theorem -> "Theorem"
38+
| Other s -> "Other(" ^ s ^ ")"
39+
| Unknown -> "Unknown"
40+
| Lemma -> "Lemma"
41+
42+
let of_rocq_logic_kind = function
43+
| Decls.Theorem -> Theorem
44+
| Lemma -> Lemma
45+
| Fact -> Other "Fact"
46+
| Remark -> Other "Remark"
47+
| Property -> Other "Property"
48+
| Proposition -> Other "Proposition"
49+
| Corollary -> Other "Corollary"
50+
51+
let of_rocq_decl_kind = function
52+
| Decls.Definition -> Definition
53+
| Coercion -> Other "Coercion"
54+
| SubClass -> Other "SubClass"
55+
| CanonicalStructure -> Other "CanonicalStructure"
56+
| Example -> Other "Example"
57+
| Fixpoint -> Other "Fixpoint"
58+
| CoFixpoint -> Other "CoFixpoint"
59+
| Scheme -> Other "Scheme"
60+
| StructureComponent -> Other "StructureComponent"
61+
| IdentityCoercion -> Other "IdentityCoercion"
62+
| Instance -> Other "Instance"
63+
| Method -> Other "Method"
64+
| Let -> Other "Let"
65+
| LetContext -> Other "LetContext"
66+
67+
(* For Lang.Ast.Info.t *)
68+
let _of_detail = function
69+
| Some "Notation" -> Notation
70+
| Some "Definition" -> Definition
71+
| Some "Lemma" -> Lemma
72+
| Some "Theorem" -> Theorem
73+
| Some s -> Other s
74+
| None -> Unknown
75+
end
76+
77+
(* extract refs and notations *)
78+
let rec analyze_constr_expr_acc () depl (e : Constrexpr.constr_expr) :
79+
string list =
80+
let open Constrexpr in
81+
(match e.v with
82+
| CRef (qid, _) -> [ Libnames.string_of_qualid qid ]
83+
| CFix (_, _) -> []
84+
| CCoFix (_, _) -> []
85+
| CProdN (_, _) -> []
86+
| CLambdaN (_, _) -> []
87+
| CLetIn (_, _, _, _) -> []
88+
| CAppExpl (_, _) -> []
89+
| CApp (_, _) -> []
90+
| CProj (_, _, _, _) -> []
91+
| CRecord _ -> []
92+
| CCases (_, _, _, _) -> []
93+
| CLetTuple (_, _, _, _) -> []
94+
| CIf (_, _, _, _) -> []
95+
| CHole _ -> []
96+
| CGenarg _ -> []
97+
| CGenargGlob _ -> []
98+
| CPatVar _ -> []
99+
| CEvar (_, _) -> []
100+
| CSort _ -> []
101+
| CCast (_, _, _) -> []
102+
| CNotation (_, (_, ntn_key), (l, ll, _, _)) ->
103+
(* Constrexpr_ops.fold_constr_expr_with_binders doesn't recurse properly
104+
here *)
105+
let f =
106+
Constrexpr_ops.fold_constr_expr_with_binders
107+
(fun _ () -> ())
108+
analyze_constr_expr_acc () []
109+
in
110+
[ ntn_key ] @ List.concat_map f (l @ List.flatten ll)
111+
| CGeneralization (_, _) -> []
112+
| CPrim _ -> []
113+
| CDelimiters (_, _, _) -> []
114+
| CArray (_, _, _, _) -> [])
115+
|> List.append depl
116+
117+
let analyze_constr_expr (e : Constrexpr.constr_expr) =
118+
Constrexpr_ops.fold_constr_expr_with_binders
119+
(fun _ () -> ())
120+
analyze_constr_expr_acc () [] e
121+
122+
(* *)
123+
let analyze_definition_expr de =
124+
let open Vernacexpr in
125+
let ty =
126+
match de with
127+
| ProveBody (_bl, e) -> e
128+
| DefineBody (_bl, _rr, e, _) -> e
129+
in
130+
analyze_constr_expr ty
131+
132+
let name_to_string = function
133+
| Names.Anonymous -> None
134+
| Names.Name id -> Some (Names.Id.to_string id)
135+
136+
(* Better Rocq analysis, for example extract the list of notations *)
137+
let analyze (CAst.{ loc = _; v } : Vernacexpr.vernac_control) : _ option =
138+
let open Vernacexpr in
139+
let { control = _; attrs = _; expr } = v in
140+
match expr with
141+
| VernacSynterp e -> (
142+
match e with
143+
| VernacLoad (_, _) -> None
144+
| VernacReservedNotation (_, _) -> None
145+
| VernacNotation (_, _) -> None
146+
| VernacDeclareCustomEntry _ -> None
147+
| VernacBeginSection _ -> None
148+
| VernacEndSegment _ -> None
149+
| VernacRequire (_, _, _) -> None
150+
| VernacImport (_, _) -> None
151+
| VernacDeclareModule (_, _, _, _) -> None
152+
| VernacDefineModule (_, _, _, _, _) -> None
153+
| VernacDeclareModuleType (_, _, _, _) -> None
154+
| VernacInclude _ -> None
155+
| VernacDeclareMLModule _ -> None
156+
| VernacChdir _ -> None
157+
| VernacExtraDependency (_, _, _) -> None
158+
| VernacSetOption (_, _, _) -> None
159+
| VernacProofMode _ -> None
160+
| VernacExtend (_, _) -> None)
161+
| VernacSynPure e -> (
162+
match e with
163+
| VernacOpenCloseScope (_, _) -> None
164+
| VernacDeclareScope _ -> None
165+
| VernacDelimiters (_, _) -> None
166+
| VernacBindScope (_, _) -> None
167+
| VernacEnableNotation (_, _, _, _, _) -> None
168+
| VernacDefinition ((_, kind), (name, _), expr) ->
169+
let name = name_to_string name.v in
170+
let kind = Kind.of_rocq_decl_kind kind in
171+
let deps = analyze_definition_expr expr in
172+
Some (name, kind, deps)
173+
| VernacStartTheoremProof (_, []) -> None
174+
| VernacStartTheoremProof (kind, ((name, _), (_, ty)) :: _) ->
175+
let name = Some (Names.Id.to_string name.v) in
176+
let kind = Kind.of_rocq_logic_kind kind in
177+
let deps = analyze_constr_expr ty in
178+
Some (name, kind, deps)
179+
| VernacEndProof _ -> None
180+
| VernacExactProof _ -> None
181+
| VernacAssumption (_, _, _) -> None
182+
| VernacSymbol _ -> None
183+
| VernacInductive (_, _) -> None
184+
| VernacFixpoint (_, _) -> None
185+
| VernacCoFixpoint (_, _) -> None
186+
| VernacScheme _ -> None
187+
| VernacSchemeEquality (_, _) -> None
188+
| VernacCombinedScheme (_, _) -> None
189+
| VernacUniverse _ -> None
190+
| VernacSort _ -> None
191+
| VernacConstraint _ -> None
192+
| VernacAddRewRule (_, _) -> None
193+
| VernacCanonical _ -> None
194+
| VernacCoercion (_, _) -> None
195+
| VernacIdentityCoercion (_, _, _) -> None
196+
| VernacNameSectionHypSet (_, _) -> None
197+
| VernacInstance (_, _, _, _, _) -> None
198+
| VernacDeclareInstance (_, _, _, _) -> None
199+
| VernacContext _ -> None
200+
| VernacExistingInstance _ -> None
201+
| VernacExistingClass _ -> None
202+
| VernacResetName _ -> None
203+
| VernacResetInitial -> None
204+
| VernacBack _ -> None
205+
| VernacCreateHintDb (_, _) -> None
206+
| VernacRemoveHints (_, _) -> None
207+
| VernacHints (_, _) -> None
208+
| VernacAbbreviation (_, _, _, _) -> None
209+
| VernacArguments (_, _, _, _) -> None
210+
| VernacReserve _ -> None
211+
| VernacGeneralizable _ -> None
212+
| VernacSetOpacity (_, _) -> None
213+
| VernacSetStrategy _ -> None
214+
| VernacMemOption (_, _) -> None
215+
| VernacPrintOption _ -> None
216+
| VernacCheckMayEval (_, _, _) -> None
217+
| VernacGlobalCheck _ -> None
218+
| VernacDeclareReduction (_, _) -> None
219+
| VernacPrint _ -> None
220+
| VernacSearch (_, _, _) -> None
221+
| VernacLocate _ -> None
222+
| VernacRegister (_, _) -> None
223+
| VernacPrimitive (_, _, _) -> None
224+
| VernacComments _ -> None
225+
| VernacAttributes _ -> None
226+
| VernacAbort -> None
227+
| VernacAbortAll -> None
228+
| VernacRestart -> None
229+
| VernacUndo _ -> None
230+
| VernacUndoTo _ -> None
231+
| VernacFocus _ -> None
232+
| VernacUnfocus -> None
233+
| VernacUnfocused -> None
234+
| VernacBullet _ -> None
235+
| VernacSubproof _ -> None
236+
| VernacEndSubproof -> None
237+
| VernacShow _ -> None
238+
| VernacCheckGuard -> None
239+
| VernacValidateProof -> None
240+
| VernacProof (_, _) -> None
241+
| VernacAddOption (_, _) -> None
242+
| VernacRemoveOption (_, _) -> None)
243+
244+
let analyze (node : Doc.Node.t) =
245+
match node.ast with
246+
| Some { v; _ } -> analyze (Coq.Ast.to_coq v)
247+
| _ -> None
248+
249+
(* We output a record for each object we can recognize in the document, linear
250+
order. *)
251+
module Node_info = struct
252+
(* Just to bring the serializers in scope *)
253+
module Lang = Lsp.JLang
254+
module Coq = Lsp.JCoq
255+
256+
type t =
257+
{ uri : Lang.LUri.File.t
258+
; range : Lang.Range.t
259+
; kind : Kind.t
260+
; name : string
261+
; raw : string
262+
; deps : string list
263+
}
264+
[@@deriving to_yojson]
265+
266+
let of_node ~io:_ ~token:_ ~uri ~(contents : Contents.t) (node : Doc.Node.t) =
267+
match analyze node with
268+
| None -> None
269+
| Some (name, kind, deps) ->
270+
(* let uuid = "TODO" in *)
271+
let range = node.range in
272+
let name =
273+
match name with
274+
| Some name -> name
275+
| None -> "anonymous"
276+
in
277+
let deps = List.sort_uniq String.compare deps in
278+
let raw = Fleche.Contents.extract_raw ~contents ~range in
279+
Some { uri; range; kind; name; raw; deps }
280+
end
281+
282+
module GDB = struct
283+
(* Object identifier *)
284+
module Id = struct
285+
type t = string [@@deriving to_yojson]
286+
end
287+
288+
module Attr = struct
289+
type t = string list [@@deriving to_yojson]
290+
end
291+
292+
module Node = struct
293+
type t =
294+
{ id : Id.t
295+
; attrs : Attr.t
296+
}
297+
[@@deriving to_yojson]
298+
end
299+
300+
module Edge = struct
301+
type t =
302+
{ from : Id.t
303+
; to_ : Id.t [@key "to"]
304+
; attrs : Attr.t
305+
; label : string
306+
}
307+
[@@deriving to_yojson]
308+
end
309+
310+
module Labels = struct
311+
type t = (Id.t * string) list
312+
313+
let to_yojson l =
314+
let f (id, label) = (id, `String label) in
315+
let fields = List.map f l in
316+
`Assoc fields
317+
end
318+
end
319+
320+
module Meta = struct
321+
open GDB
322+
323+
type t = Node.t list * Edge.t list * Labels.t
324+
325+
let mk_edges ~from ~deps ~attrs =
326+
let f to_ = { Edge.from; to_; attrs; label = "USES" } in
327+
List.map f deps
328+
329+
let rec to_graph_db (nl, el, ll) (l : Node_info.t list) : t =
330+
match l with
331+
| [] -> (List.rev nl, List.rev el, List.rev ll)
332+
| n :: l ->
333+
let { Node_info.uri = _; range = _; kind; name; raw = _; deps } = n in
334+
let attrs = [] in
335+
let nn = { Node.id = name; attrs } in
336+
let ne = mk_edges ~from:name ~deps ~attrs in
337+
let nll = (name, Kind.to_string kind) in
338+
to_graph_db (nn :: nl, ne @ el, nll :: ll) l
339+
340+
let to_graph_db (l : Node_info.t list) : t = to_graph_db ([], [], []) l
341+
342+
(* Create nodes for orphan deps in the graph *)
343+
let to_graph_db l =
344+
let nl, el, ll = to_graph_db l in
345+
(nl, el, ll)
346+
347+
let pp fmt (nl, el, l) =
348+
let nl = `List (List.map Node.to_yojson nl) in
349+
let el = `List (List.map Edge.to_yojson el) in
350+
let l = Labels.to_yojson l in
351+
let obj = `Assoc [ ("edges", el); ("nodes", nl); ("node_labels", l) ] in
352+
let obj = `Assoc [ ("graph", obj) ] in
353+
Format.fprintf fmt "@[%a@]@\n" (Yojson.Safe.pretty_print ~std:true) obj
354+
end
355+
356+
let dump_meta ~io ~token ~out_file ~(doc : Doc.t) =
357+
let uri, contents = (doc.uri, doc.contents) in
358+
let ll =
359+
List.filter_map (Node_info.of_node ~io ~token ~uri ~contents) doc.nodes
360+
in
361+
let ll = Meta.to_graph_db ll in
362+
let f fmt meta = Meta.pp fmt meta in
363+
Coq.Compat.format_to_file ~file:out_file ~f ll
364+
365+
let dump_meta ~io ~token ~(doc : Doc.t) =
366+
let uri = doc.uri in
367+
let uri_str = Lang.LUri.File.to_string_uri uri in
368+
let lvl = Io.Level.Info in
369+
Io.Report.msg ~io ~lvl "[metanejo plugin] dumping metadata for %s ..." uri_str;
370+
let out_file_j = Lang.LUri.File.to_string_file uri ^ ".meta.json" in
371+
let () = dump_meta ~io ~token ~out_file:out_file_j ~doc in
372+
(* let out_file_s = Lang.LUri.File.to_string_file uri ^ ".sexp.goaldump" in *)
373+
(* let () = dump_goals ~out_file:out_file_s ~doc pp_sexp in *)
374+
Io.Report.msg ~io ~lvl
375+
"[metanejo plugin] dumping metadata for %s was completed!" uri_str;
376+
()
377+
378+
let main () = Theory.Register.Completed.add dump_meta
379+
let () = main ()

plugins/metanejo/main.mli

Whitespace-only changes.

0 commit comments

Comments
 (0)