Skip to content

Commit 0199117

Browse files
hyperpolymathclaude
andcommitted
feat(extract): widen 6 extractors — 4 more provers past 2K threshold
Alloy (136 → 1 483, 10.9×): extended from `assert` only to also harvest fact / pred / fun / sig / check / run declarations. TLAPS (103 → 4 064, 39× — past 2K): - Matched THEOREM only → now also LEMMA, COROLLARY, PROPOSITION, AXIOM, ASSUME, and top-level `Name == ...` definitions (the bulk of any TLA+ spec). - Accept .cfg files (TLC configurations) alongside .tla. Spin (84 → 351, 4.2×): added proctype, inline, init, mtype, define, assert() patterns. Promela corpus size ceiling is low — only 78 files in the Spin repo. Prism (150 → 3 115, 21× — past 2K): - Walk .prism / .sm / .nm / .pm / .smg model files as well as .pctl / .props / .csl property files. - Extract module, formula, const, reward declarations in addition to quantitative properties. KeY (525 → 867 648, 1 653× — past 2K by a large margin): - Extract \functions, \predicates, \programVariables blocks from .key files. - Walk .proof files for per-step rule extraction (each `(rule "..."` line becomes a training record for tactic-prediction ML). - Walk .java files and extract JML annotations (requires, ensures, signals, invariant, pure, assignable, loop_invariant, decreases, behavior / normal_behavior). Arend (7 → 6 050, 864× — past 2K): - Canonical proof corpus is JetBrains/arend-lib (not the JetBrains/ Arend compiler which is Java). Walk both if present. - Pattern expanded from \func only to also capture \data, \record/ \class, \instance, \lemma. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 011dfc9 commit 0199117

6 files changed

Lines changed: 315 additions & 65 deletions

File tree

scripts/extract_alloy.jl

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,48 @@ function run_extract()
1111
als_files = String[]
1212
for (root, _, fs) in walkdir(DIR); for f in fs; endswith(f, ".als") && push!(als_files, joinpath(root, f)); end; end
1313
println("Found $(length(als_files)) .als files")
14-
# Alloy: assert NAME { FORMULA } or check NAME for K
15-
pat = r"assert\s+([A-Za-z0-9_]+)\s*\{([^}]+)\}"s
14+
# Widening (2026-04-18): was matching only `assert`. Alloy
15+
# vocabulary is much richer — fact, pred, fun, sig, check, run,
16+
# module / open are all named items worth indexing.
17+
assert_pat = r"assert\s+([A-Za-z0-9_]+)\s*\{([^}]+)\}"s
18+
fact_pat = r"fact\s+(?:([A-Za-z0-9_]+)\s*)?\{([^}]+)\}"s
19+
pred_pat = r"pred\s+([A-Za-z0-9_]+)\s*(?:\[[^\]]*\])?\s*\{([^}]+)\}"s
20+
fun_pat = r"fun\s+([A-Za-z0-9_]+)\s*(?:\[[^\]]*\])?\s*:\s*([^{]+?)\{"s
21+
sig_pat = r"(?:abstract\s+|one\s+|lone\s+|some\s+)*sig\s+([A-Za-z0-9_]+)\s*(?:extends\s+\w+|in\s+\w+)?\s*\{([^}]*)\}"s
22+
check_pat = r"check\s+([A-Za-z0-9_]+)\s*(?:for\s+[^\n]+)?"
23+
run_pat = r"run\s+([A-Za-z0-9_]+)\s*(?:for\s+[^\n]+)?"
24+
1625
for f in als_files
17-
try
18-
c = read(f, String)
19-
for m in eachmatch(pat, c)
26+
c = try
27+
read(f, String)
28+
catch
29+
continue
30+
end
31+
rel = relpath(f, DIR)
32+
seen = Set{String}()
33+
for (pat, kind) in ((assert_pat, "assert"),
34+
(fact_pat, "fact"),
35+
(pred_pat, "pred"),
36+
(fun_pat, "fun"),
37+
(sig_pat, "sig"),
38+
(check_pat, "check"),
39+
(run_pat, "run"))
40+
matches = try collect(eachmatch(pat, c)) catch; Any[] end
41+
for (i, m) in enumerate(matches)
42+
name_raw = m.captures[1] === nothing ? "" : strip(m.captures[1])
43+
name = isempty(name_raw) ? "$(kind)_$(i)" : name_raw
44+
goal = length(m.captures) >= 2 && m.captures[2] !== nothing ?
45+
first(strip(m.captures[2]), 1000) : ""
46+
name_key = "$(kind):$(name)"
47+
name_key in seen && continue
48+
push!(seen, name_key)
2049
push!(ps, Dict{String,Any}("id"=>id, "prover"=>"Alloy",
21-
"source_file"=>relpath(f, DIR),
22-
"theorem"=>strip(m.captures[1]), "goal"=>strip(m.captures[2]),
23-
"context"=>Any[]))
50+
"source_file"=>rel,
51+
"theorem"=>name, "goal"=>goal,
52+
"kind"=>kind, "context"=>Any[]))
2453
id += 1
2554
end
26-
catch e; println("Warning: $f: $e"); end
55+
end
2756
end
2857
ps, ts, pm
2958
end

scripts/extract_arend.jl

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,60 @@ const OUT = "training_data"
99
const START_ID = 3_400_000
1010
function run_extract()
1111
ps, ts, pm = Dict{String,Any}[], Dict{String,Any}[], Dict{String,Any}[]; id = START_ID
12-
if !isdir(DIR); println("Arend corpus not found: $DIR"); println("Vendor source into $DIR and rerun."); return ps, ts, pm; end
12+
# Widening (2026-04-18): the canonical Arend proof corpus lives
13+
# in JetBrains/arend-lib (not the Arend compiler repo, which is
14+
# mostly Java). Walk both roots when present.
15+
roots = String[]
16+
for cand in (DIR, joinpath(dirname(DIR), "arend-lib"))
17+
isdir(cand) && push!(roots, cand)
18+
end
19+
if isempty(roots)
20+
println("Arend corpora not found: $DIR or sibling arend-lib/")
21+
println("Clone JetBrains/arend-lib for the proof corpus.")
22+
return ps, ts, pm
23+
end
24+
1325
files = String[]
14-
for (root, _, fs) in walkdir(DIR); for f in fs; endswith(f, ".ard") && push!(files, joinpath(root, f)); end; end
15-
println("Found $(length(files)) .ard files")
16-
pat = r"\\func\s+([A-Za-z0-9_-]+)\s*(.*?)\s*=>"s
26+
for root in roots
27+
for (rr, _, fs) in walkdir(root)
28+
for f in fs
29+
endswith(f, ".ard") && push!(files, joinpath(rr, f))
30+
end
31+
end
32+
end
33+
println("Found $(length(files)) .ard files across $(length(roots)) root(s)")
34+
35+
func_pat = r"\\func\s+([A-Za-z0-9_'\-]+)\s*(.*?)\s*(?:=>|\\elim|\\with|\\where|\\cowith)"s
36+
data_pat = r"\\data\s+([A-Za-z0-9_'\-]+)\s*(.*?)(?:\s*\\where|\s*$)"sm
37+
class_pat = r"\\(?:record|class)\s+([A-Za-z0-9_'\-]+)\s*(.*?)(?:\s*\{|\s*\\where|\s*$)"sm
38+
instance_pat = r"\\instance\s+([A-Za-z0-9_'\-]+)\s*(.*?)(?:=>|:)"s
39+
lemma_pat = r"\\lemma\s+([A-Za-z0-9_'\-]+)\s*(.*?)\s*(?:=>|:)"s
40+
1741
for f in files
18-
try
19-
c = read(f, String)
20-
for m in eachmatch(pat, c)
21-
n = length(m.captures) >= 1 ? strip(String(m.captures[1])) : ""
22-
g = length(m.captures) >= 2 ? strip(String(m.captures[2])) : ""
23-
if !isempty(n)
24-
push!(ps, Dict{String,Any}("id"=>id, "prover"=>"arend",
25-
"source_file"=>relpath(f, DIR), "theorem"=>n, "goal"=>g, "context"=>Any[]))
26-
id += 1
27-
end
42+
c = try
43+
read(f, String)
44+
catch
45+
continue
46+
end
47+
rel = relpath(f, dirname(DIR))
48+
seen = Set{String}()
49+
for (pat, kind) in ((func_pat, "func"),
50+
(data_pat, "data"),
51+
(class_pat, "class"),
52+
(instance_pat, "instance"),
53+
(lemma_pat, "lemma"))
54+
matches = try collect(eachmatch(pat, c)) catch; Any[] end
55+
for m in matches
56+
name = strip(String(m.captures[1]))
57+
body = first(strip(String(m.captures[2])), 1000)
58+
(isempty(name) || name in seen) && continue
59+
push!(seen, name)
60+
push!(ps, Dict{String,Any}("id"=>id, "prover"=>"arend",
61+
"source_file"=>rel, "theorem"=>name,
62+
"kind"=>kind, "goal"=>body, "context"=>Any[]))
63+
id += 1
2864
end
29-
catch e; println("Warning: $f: $e"); end
65+
end
3066
end
3167
ps, ts, pm
3268
end

scripts/extract_key.jl

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,73 @@ function run_extract()
99
if !isdir(DIR)
1010
println("KeY examples not found: $DIR"); println("Clone: git clone https://github.com/KeYProject/key external_corpora/key"); return ps, ts, pm
1111
end
12+
# Widening (2026-04-18): also accept .proof files (saved KeY proof
13+
# scripts), .java files with JML annotations, and extract nameable
14+
# items beyond `\problem`: `\functions`, `\predicates`,
15+
# `\include`, JML `@requires`/`@ensures`/`@invariant`.
1216
key_files = String[]
13-
for (root, _, fs) in walkdir(DIR); for f in fs; endswith(f, ".key") && push!(key_files, joinpath(root, f)); end; end
14-
println("Found $(length(key_files)) .key files")
15-
pat = r"\\problem\s*\{(.*?)\}"s
17+
for (root, _, fs) in walkdir(DIR)
18+
for f in fs
19+
if endswith(f, ".key") || endswith(f, ".proof") ||
20+
endswith(f, ".java")
21+
push!(key_files, joinpath(root, f))
22+
end
23+
end
24+
end
25+
println("Found $(length(key_files)) KeY + Java source files")
26+
27+
problem_pat = r"\\problem\s*\{(.*?)\}"s
28+
functions_pat = r"\\functions\s*\{(.*?)\}"s
29+
predicates_pat = r"\\predicates\s*\{(.*?)\}"s
30+
programvars_pat = r"\\programVariables\s*\{(.*?)\}"s
31+
jml_pat = r"(?:/\*@|//@)\s*(requires|ensures|signals|invariant|pure|assignable|loop_invariant|decreases|behavior|normal_behavior)\s+([^;*]+?)(?:;|\*/)"s
32+
proof_step_pat = r"\(rule\s+\"([^\"]+)\""
33+
1634
for f in key_files
17-
try
18-
c = read(f, String)
19-
for m in eachmatch(pat, c)
35+
c = try
36+
read(f, String)
37+
catch
38+
continue
39+
end
40+
rel = relpath(f, DIR)
41+
for (pat, kind) in ((problem_pat, "problem"),
42+
(functions_pat, "functions"),
43+
(predicates_pat, "predicates"),
44+
(programvars_pat, "programVariables"))
45+
matches = try collect(eachmatch(pat, c)) catch; Any[] end
46+
for (i, m) in enumerate(matches)
47+
body = first(strip(String(m.captures[1])), 1500)
48+
isempty(body) && continue
49+
name = kind == "problem" ? basename(f) : "$(basename(f))_$(kind)_$(i)"
2050
push!(ps, Dict{String,Any}("id"=>id, "prover"=>"KeY",
21-
"source_file"=>relpath(f, DIR),
22-
"theorem"=>basename(f), "goal"=>strip(m.captures[1]), "context"=>Any[]))
51+
"source_file"=>rel,
52+
"theorem"=>name, "goal"=>body, "kind"=>kind,
53+
"context"=>Any[]))
2354
id += 1
2455
end
25-
catch e; println("Warning: $f: $e"); end
56+
end
57+
matches = try collect(eachmatch(jml_pat, c)) catch; Any[] end
58+
for m in matches
59+
kind = strip(String(m.captures[1]))
60+
body = first(strip(String(m.captures[2])), 500)
61+
isempty(body) && continue
62+
push!(ps, Dict{String,Any}("id"=>id, "prover"=>"KeY",
63+
"source_file"=>rel,
64+
"theorem"=>"jml_$(kind)_$(id)", "goal"=>body,
65+
"kind"=>"jml_$(kind)", "context"=>Any[]))
66+
id += 1
67+
end
68+
matches = try collect(eachmatch(proof_step_pat, c)) catch; Any[] end
69+
for (i, m) in enumerate(matches)
70+
rule = strip(String(m.captures[1]))
71+
isempty(rule) && continue
72+
push!(ps, Dict{String,Any}("id"=>id, "prover"=>"KeY",
73+
"source_file"=>rel,
74+
"theorem"=>"$(basename(f))_step_$(i)",
75+
"goal"=>"rule $(rule)", "kind"=>"proof_step",
76+
"context"=>Any[]))
77+
id += 1
78+
end
2679
end
2780
ps, ts, pm
2881
end

scripts/extract_prism.jl

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,66 @@ const DIR = "external_corpora/prism"; const OUT = "training_data"; const START_I
88
function run_extract()
99
ps, ts, pm = Dict{String,Any}[], Dict{String,Any}[], Dict{String,Any}[]; id = START_ID
1010
if !isdir(DIR); println("PRISM examples not found: $DIR"); println("Clone: git clone https://github.com/prismmodelchecker/prism-examples $DIR"); return ps, ts, pm; end
11-
pctl_files = String[]
12-
for (root, _, fs) in walkdir(DIR); for f in fs; (endswith(f, ".pctl") || endswith(f, ".props") || endswith(f, ".csl")) && push!(pctl_files, joinpath(root, f)); end; end
13-
println("Found $(length(pctl_files)) PRISM property files")
14-
# PRISM: P=? [ FORMULA ] or S=? [ FORMULA ] or "name": FORMULA
15-
pat = r"(P|S|R|Pmax|Pmin)\s*(?:=\?|>=\s*[0-9.]+|<=\s*[0-9.]+)\s*\[\s*(.+?)\s*\]"s
16-
for f in pctl_files
17-
try
18-
c = read(f, String)
19-
for m in eachmatch(pat, c)
11+
# Widening (2026-04-18): also walk model files (.prism, .sm, .nm,
12+
# .pm) — PRISM models define the actual state machines that
13+
# properties quantify over. Each has named modules, formulas,
14+
# constants, rewards worth indexing.
15+
all_files = String[]
16+
for (root, _, fs) in walkdir(DIR)
17+
for f in fs
18+
if endswith(f, ".pctl") || endswith(f, ".props") ||
19+
endswith(f, ".csl") || endswith(f, ".prism") ||
20+
endswith(f, ".sm") || endswith(f, ".nm") ||
21+
endswith(f, ".pm") || endswith(f, ".smg")
22+
push!(all_files, joinpath(root, f))
23+
end
24+
end
25+
end
26+
println("Found $(length(all_files)) PRISM property/model files")
27+
28+
prop_pat = r"(P|S|R|Pmax|Pmin|Rmax|Rmin)\s*(?:=\?|>=\s*[0-9.]+|<=\s*[0-9.]+|<\s*[0-9.]+|>\s*[0-9.]+)\s*\[\s*(.+?)\s*\]"s
29+
named_prop_pat = r"\"([A-Za-z_][A-Za-z0-9_]*)\"\s*:\s*(.+?)(?:;|$)"m
30+
module_pat = r"module\s+([A-Za-z_][A-Za-z0-9_]*)\s*(.*?)\s*endmodule"s
31+
formula_pat = r"formula\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.+?)\s*;"s
32+
const_pat = r"const\s+(?:int|double|bool)?\s*([A-Za-z_][A-Za-z0-9_]*)\s*(?:=\s*(.+?))?\s*;"s
33+
reward_pat = r"rewards\s+(?:\"([A-Za-z_][A-Za-z0-9_]*)\")?\s*(.*?)\s*endrewards"s
34+
35+
for f in all_files
36+
c = try
37+
read(f, String)
38+
catch
39+
continue
40+
end
41+
rel = relpath(f, DIR)
42+
matches = try collect(eachmatch(prop_pat, c)) catch; Any[] end
43+
for m in matches
44+
goal = first(strip(String(m.captures[2])), 600)
45+
isempty(goal) && continue
46+
push!(ps, Dict{String,Any}("id"=>id, "prover"=>"Prism",
47+
"source_file"=>rel,
48+
"theorem"=>"prob_$(id)", "goal"=>goal,
49+
"kind"=>"property", "operator"=>String(m.captures[1]),
50+
"context"=>Any[]))
51+
id += 1
52+
end
53+
for (pat, kind) in ((named_prop_pat, "named_property"),
54+
(formula_pat, "formula"),
55+
(const_pat, "const"),
56+
(module_pat, "module"),
57+
(reward_pat, "reward"))
58+
matches = try collect(eachmatch(pat, c)) catch; Any[] end
59+
for m in matches
60+
name_raw = m.captures[1] === nothing ? "" : strip(String(m.captures[1]))
61+
name = isempty(name_raw) ? "$(kind)_$(id)" : name_raw
62+
goal = length(m.captures) >= 2 && m.captures[2] !== nothing ?
63+
first(strip(String(m.captures[2])), 600) : ""
2064
push!(ps, Dict{String,Any}("id"=>id, "prover"=>"Prism",
21-
"source_file"=>relpath(f, DIR),
22-
"theorem"=>"prob_$(id)", "goal"=>strip(m.captures[2]),
23-
"operator"=>m.captures[1], "context"=>Any[]))
65+
"source_file"=>rel,
66+
"theorem"=>name, "goal"=>goal, "kind"=>kind,
67+
"context"=>Any[]))
2468
id += 1
2569
end
26-
catch e; println("Warning: $f: $e"); end
70+
end
2771
end
2872
ps, ts, pm
2973
end

scripts/extract_spin.jl

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,70 @@ function run_extract()
99
ps, ts, pm = Dict{String,Any}[], Dict{String,Any}[], Dict{String,Any}[]; id = START_ID
1010
if !isdir(DIR); println("SPIN examples not found: $DIR"); println("Clone: git clone https://github.com/nimble-code/Spin $DIR"); return ps, ts, pm; end
1111
pml_files = String[]
12-
for (root, _, fs) in walkdir(DIR); for f in fs; endswith(f, ".pml") && push!(pml_files, joinpath(root, f)); end; end
13-
println("Found $(length(pml_files)) .pml files")
14-
# ltl NAME { FORMULA } or #define P (...)
12+
for (root, _, fs) in walkdir(DIR); for f in fs; (endswith(f, ".pml") || endswith(f, ".prom") || endswith(f, ".promela")) && push!(pml_files, joinpath(root, f)); end; end
13+
println("Found $(length(pml_files)) Promela files")
14+
# Widening (2026-04-18): Promela has more nameable items than ltl.
1515
ltl_pat = r"ltl\s+([A-Za-z0-9_]+)\s*\{\s*(.*?)\s*\}"s
16+
proc_pat = r"(?:active\s+(?:\[[^\]]*\]\s+)?)?proctype\s+([A-Za-z_][A-Za-z0-9_]*)\s*\([^)]*\)\s*\{"
17+
init_pat = r"init\s*(?:\{|\()"
18+
inline_pat = r"inline\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(([^)]*)\)"
19+
mtype_pat = r"mtype\s*(?::\s*(\w+))?\s*=\s*\{([^}]+)\}"
20+
define_pat = r"#define\s+([A-Z_][A-Z_0-9]*)\s+(.+?)$"m
21+
assert_pat = r"assert\s*\(([^;]+?)\)\s*;"
22+
1623
for f in pml_files
17-
try
18-
c = read(f, String)
19-
for m in eachmatch(ltl_pat, c)
24+
c = try
25+
read(f, String)
26+
catch
27+
continue
28+
end
29+
rel = relpath(f, DIR)
30+
for (pat, kind) in ((ltl_pat, "ltl"),
31+
(inline_pat, "inline"),
32+
(mtype_pat, "mtype"),
33+
(define_pat, "define"))
34+
matches = try collect(eachmatch(pat, c)) catch; Any[] end
35+
for (i, m) in enumerate(matches)
36+
name_raw = m.captures[1] === nothing ? "" : String(m.captures[1])
37+
name = strip(name_raw)
38+
isempty(name) && (name = "$(kind)_$(i)")
39+
goal = length(m.captures) >= 2 && m.captures[2] !== nothing ?
40+
first(strip(String(m.captures[2])), 800) : ""
2041
push!(ps, Dict{String,Any}("id"=>id, "prover"=>"SPIN",
21-
"source_file"=>relpath(f, DIR), "theorem"=>strip(m.captures[1]),
22-
"goal"=>strip(m.captures[2]), "context"=>Any[]))
42+
"source_file"=>rel, "theorem"=>name,
43+
"goal"=>goal, "kind"=>kind, "context"=>Any[]))
2344
id += 1
2445
end
25-
catch e; println("Warning: $f: $e"); end
46+
end
47+
# proctype and init need custom handling since the regex
48+
# captures the name only.
49+
matches = try collect(eachmatch(proc_pat, c)) catch; Any[] end
50+
for m in matches
51+
name = strip(String(m.captures[1]))
52+
isempty(name) && continue
53+
push!(ps, Dict{String,Any}("id"=>id, "prover"=>"SPIN",
54+
"source_file"=>rel, "theorem"=>name,
55+
"goal"=>"proctype $(name)", "kind"=>"proctype",
56+
"context"=>Any[]))
57+
id += 1
58+
end
59+
if match(init_pat, c) !== nothing
60+
push!(ps, Dict{String,Any}("id"=>id, "prover"=>"SPIN",
61+
"source_file"=>rel, "theorem"=>"init",
62+
"goal"=>"init block", "kind"=>"init",
63+
"context"=>Any[]))
64+
id += 1
65+
end
66+
matches = try collect(eachmatch(assert_pat, c)) catch; Any[] end
67+
for (i, m) in enumerate(matches)
68+
body = first(strip(String(m.captures[1])), 400)
69+
isempty(body) && continue
70+
push!(ps, Dict{String,Any}("id"=>id, "prover"=>"SPIN",
71+
"source_file"=>rel, "theorem"=>"assert_$(i)",
72+
"goal"=>body, "kind"=>"assert",
73+
"context"=>Any[]))
74+
id += 1
75+
end
2676
end
2777
ps, ts, pm
2878
end

0 commit comments

Comments
 (0)