Library
FSharp.Data.Rocq has no dependencies beyond FSharp.Core. The examples on this page run against the Tally project.
dotnet add package FSharp.Data.Rocq --prerelease
open FSharp.Data.Rocq.Project
open FSharp.Data.Rocq.Structure
What a project holds
| Value | Holds |
|---|---|
RocqProject.Files |
Each file's digests, parsed .glob and Model |
RocqFile.Model |
Tokens, sentences, comments, headings, declarations, scopes, imports, notations, symbols, binders, references and diagnostics |
RocqProject.Declarations, Symbols |
Those tables numbered across the project |
RocqProject.CrossReferences |
Mentions between declarations and symbols |
RocqProject.FileGraph |
Dependencies between files |
An index or string field that can be absent is an option.
Declarations and proofs
RocqProject.Declarations lists every declaration in project order. Category is a closed union, so a match covers the kinds you care about.
let listTheorems (project: RocqProject) =
for entry in project.Declarations do
let declaration = entry.Declaration
match declaration.Category with
| Category.Theorem ->
printfn
"%-24s %-12s %d proof, %d doc comment"
(defaultArg declaration.Name declaration.Id)
project.Files[entry.File].Module
declaration.Proofs.Length
declaration.Docs.Length
| _ -> ()
add_regroup Tally.Laws 1 proof, 1 doc comment
total_append Tally.Laws 1 proof, 1 doc comment
total_append_empty Tally.Laws 1 proof, 1 doc comment
size_stable Tally.Laws 1 proof, 1 doc comment
size_append Tally.Laws 1 proof, 1 doc comment
totals_differ_falsified Tally.Laws 1 proof, 1 doc comment
generated_sum Tally.Laws 1 proof, 1 doc comment
titled_theorem Tally.Prose 1 proof, 1 doc comment
Source text
Spans are half-open byte offsets into Model.Bytes. A span gives back the exact bytes of the .v file, so a statement read this way is the statement as written.
let textOf (file: RocqFile) (span: Span) =
Encoding.UTF8.GetString (file.Model.Bytes, span.Start, span.End - span.Start)
let printStatement (project: RocqProject) (name: string) =
for entry in project.Declarations do
if entry.Declaration.Name = Some name then
let file = project.Files[entry.File]
printfn "keyword: %A" entry.Declaration.Keyword
match entry.Declaration.Statement with
| ValueSome statement -> printfn "statement: %s" (textOf file statement)
| ValueNone -> ()
printfn "extent:\n%s" (textOf file entry.Declaration.Extent)
The result for total_append:
keyword: Some "Theorem"
statement: (a b : tally) : total (append a b) = total a + total b
extent:
Theorem total_append (a b : tally) : total (append a b) = total a + total b.
Proof.
induction a as [| n rest IH].
- reflexivity.
- simpl. rewrite IH. symmetry. apply add_regroup.
Qed.
Comments and headings
A comment records its format and the declaration it documents. These are the first comments of Laws.v, with its headings after them:
let printComments (file: RocqFile) =
for comment in file.Model.Comments |> Array.truncate 6 do
let lines = (textOf file comment.BodySpan).Trim().Split '\n'
let firstLine = lines[0].Trim()
let attachedTo =
comment.AttachedTo
|> Option.bind (fun declaration -> file.Model.Declarations[declaration].Name)
printfn "%-5s %-22s %s" (commentFormatName comment.Format) (defaultArg attachedTo "-") firstLine
for heading in file.Model.Headings do
printfn "heading %d: %s" heading.Level heading.Text
md - # Laws
md - ## Arithmetic
doc add_regroup Addition regroups.
md - ## Appending
md total_append **Appending adds totals.**
md total_append_empty **The empty tally changes nothing.**
heading 1: Laws
heading 2: Arithmetic
heading 2: Appending
heading 2: Assumed and unfinished
AttachedTo is the link the navigator uses to place documentation beside a declaration. Prose syntax shows how each format renders.
Stale files
A .glob records the digest of the source it came from. Editing a .v without compiling makes the two disagree, and references would then point at the wrong bytes. So load refuses, and loadWith { AllowStale = true } keeps the lexical data instead.
This example copies the project and adds one comment to Core.v:
let printStaleLoad (projectFile: string) =
match load projectFile with
| Ok _ -> printfn "loaded"
| Error failures ->
for failure in failures do
printfn "refused: %s" (describeFailure failure)
match loadWith { AllowStale = true } projectFile with
| Ok project ->
for file in project.Files do
printfn "%-12s %A, %d symbols" file.Module file.GlobStatus file.Model.Symbols.Length
| Error _ -> ()
refused: theories/Core.v: stale .glob: it records digest 9ebb8b3ee52a6dfc8589a5e887011937, and the source has 09894747aa4d13e80757c112cfa9bd08. Recompile, or pass --allow-stale for lexical data only
Tally.Core Stale, 0 symbols
Tally.Laws Current, 8 symbols
Tally.Prose Current, 7 symbols
The stale file keeps its tokens, sentences and comments, and has no symbols or references.