|
| 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 () |
0 commit comments