-
Notifications
You must be signed in to change notification settings - Fork 68
Expand file tree
/
Copy pathProblemExtraction.lean
More file actions
186 lines (140 loc) · 7.29 KB
/
Copy pathProblemExtraction.lean
File metadata and controls
186 lines (140 loc) · 7.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
module
public import ProblemExtraction.Core
public meta import ProblemExtraction.Core
/-!
Special commands to aid in "problem extraction".
For the math problems that we archive, we aim to include proofs in-line.
Sometimes, however, we want to present the problems without giving away
information about the solutions.
Therefore, we have "problem extraction" -- a means of stripping solutions.
During problem extraction, all declarations are removed
except those that have been tagged with one of the below command wrappers.
-/
public meta section
namespace ProblemExtraction
open Lean Elab
/-- Top-level command to mark that a file should participate in problem extraction.
This should be at the top of the file (after imports); content above it is ignored
during problem extraction (except for imports). -/
syntax (name := problemFile) "problem_file " (term)? : command
def elabProblemFile (tk : Syntax) (md : Option (TSyntax `term)) : Command.CommandElabM Unit := do
let .some startPos := (match md with
| .some md => md.raw.getTailPos?
| .none => tk.getTailPos?) | throwError "problem_file syntax has no tail pos"
let src := (←read).fileMap.source
let startPos := ⟨startPos.byteIdx + 1⟩ -- HACK: add one to consume unwanted newline
let mod := (←getEnv).header.mainModule
modifyEnv fun env =>
problemExtractionExtension.addEntry env ⟨mod, EntryVariant.file src startPos⟩
modifyEnv fun env =>
solutionExtractionExtension.addEntry env ⟨mod, EntryVariant.file src startPos⟩
let mut mdv ← match md with
| some stx => Lean.Elab.Command.liftTermElabM do
unsafe Lean.Elab.Term.evalTerm ProblemFileMetadata (mkConst ``ProblemFileMetadata) stx
| .none => pure {}
let mdv' :=
if mdv.authors.isEmpty
then { mdv with authors := parseAuthors src }
else mdv
let mdv' := { mdv' with copyrightHeader := parseCopyrightHeader src }
modifyEnv fun env => problemMetadataExtension.addEntry env ⟨mod, mdv'⟩
elab_rules : command
| `(command| problem_file%$tk) => elabProblemFile tk none
| `(command| problem_file%$tk $md) => elabProblemFile tk md
/-- Starts a group of commands that will be discarded by problem extraction. -/
syntax (name := snipBegin) "snip " &"begin" : command
/-- Ends a group of commands that will be discarded by problem extraction. -/
syntax (name := snipEnd) "snip " &"end" : command
elab_rules : command
| `(command| snip%$tk0 begin%$tk1) => do
let .some startPos := tk0.getPos? | throwError "snip syntax has no start pos"
let .some endPos := tk1.getTailPos? | throwError "snip syntax has no tail pos"
let startPos := ⟨startPos.byteIdx - 1⟩ -- HACK: subtract one to consume unwanted newline
let mod := (←getEnv).header.mainModule
let ext := problemExtractionExtension
modifyEnv fun env => ext.addEntry env ⟨mod, EntryVariant.snip_begin startPos⟩
modifyEnv fun env => solutionExtractionExtension.addEntry env
⟨mod, EntryVariant.replace ⟨startPos, endPos, ""⟩⟩
| `(command| snip%$tk1 end%$tk2) => do
let .some startPos := tk1.getPos? | throwError "snip syntax has no start pos"
let .some endPos := tk2.getTailPos? | throwError "snip syntax has no end pos"
let endPos := ⟨endPos.byteIdx + 1⟩ -- HACK: add one to consume unwanted newline
let mod := (←getEnv).header.mainModule
let ext := problemExtractionExtension
modifyEnv fun env => ext.addEntry env ⟨mod, EntryVariant.snip_end endPos⟩
modifyEnv fun env => solutionExtractionExtension.addEntry env
⟨mod, EntryVariant.replace ⟨startPos, endPos, ""⟩⟩
/--
A synonym for `theorem`. Indicates that a declaration is a problem statement.
During problem extraction, we strip prefixed underscores for unused hypotheses, and the proof is replaced by a `sorry`.
-/
syntax (name := problem) declModifiers "problem " declId ppIndent(declSig) declVal : command
elab_rules : command
| `(command| $dm:declModifiers problem%$pb $di:declId $ds:declSig $dv:declVal) => do
let mod := (←getEnv).header.mainModule
let (.some pStartPos, .some pEndPos) := (pb.getPos?, pb.getTailPos?)
| throwError "failed to get problem syntax"
modifyEnv fun env => problemExtractionExtension.addEntry env ⟨mod,
EntryVariant.replace ⟨pStartPos, pEndPos, "theorem"⟩⟩
modifyEnv fun env => solutionExtractionExtension.addEntry env ⟨mod,
EntryVariant.replace ⟨pStartPos, pEndPos, "theorem"⟩⟩
let ⟨binders, typeSpec⟩ := expandDeclSig ds
for binder in binders.getArgs do
let (Syntax.ident info str val preresolved) := binder[1][0]
| continue
let (.some str') := str.dropPrefix? "_"
| continue
-- removes underscore from problem file to avoid hinting that a hypothesis is unused
modifyEnv fun env => problemExtractionExtension.addEntry env ⟨mod,
EntryVariant.replace ⟨str.startPos, str.stopPos, str'.toString⟩⟩
let (.some vStartPos, .some vEndPos) := (dv.raw.getPos?, dv.raw.getTailPos?)
| throwError "failed to get declVal syntax"
modifyEnv fun env => problemExtractionExtension.addEntry env ⟨mod,
EntryVariant.replace ⟨vStartPos, vEndPos, ":= sorry"⟩⟩
let cmd ← `(command | $dm:declModifiers theorem $di:declId $ds:declSig $dv:declVal)
Lean.Elab.Command.elabCommand cmd
/--
A synonym for `abbrev`. Marks data that is intended to be filled in as part of
a solution. During problem extraction, the body of the decl is replaced by a `sorry`.
During judging, a human will inspect the filled-in body
to see whether it is reasonable.
-/
syntax (name := determine)
declModifiers "determine " declId ppIndent(optDeclSig) declVal : command
elab_rules : command
| `(command| $dm:declModifiers determine%$dt $di:declId $ds:optDeclSig $dv:declVal) => do
let mod := (←getEnv).header.mainModule
let (.some dStartPos, .some dEndPos) := (dt.getPos?, dt.getTailPos?)
| throwError "failed to get problem syntax"
modifyEnv fun env => problemExtractionExtension.addEntry env ⟨mod,
EntryVariant.replace ⟨dStartPos, dEndPos, "/- determine -/ abbrev"⟩⟩
modifyEnv fun env => solutionExtractionExtension.addEntry env ⟨mod,
EntryVariant.replace ⟨dStartPos, dEndPos, "/- determine -/ abbrev"⟩⟩
let (.some vStartPos, .some vEndPos) := (dv.raw.getPos?, dv.raw.getTailPos?)
| throwError "failed to get declVal syntax"
modifyEnv fun env => problemExtractionExtension.addEntry env ⟨mod,
EntryVariant.replace ⟨vStartPos, vEndPos, ":= sorry"⟩⟩
let cmd ← `(command | set_option linter.unusedVariables false in
$dm:declModifiers abbrev $di:declId $ds:optDeclSig $dv:declVal)
Lean.Elab.Command.elabCommand cmd
match di with
| `(Lean.Parser.Command.declId | $i:ident) =>
let name ← Lean.resolveGlobalConstNoOverload i
modifyEnv fun env => determineDeclsExtension.addEntry env name
| _ => throwError "explicit universes in `determine` are currently unsupported"
/--
Prints the current contents of the Problem Extraction extension.
-/
syntax (name := showExtraction) "#show_problem_extraction" : command
elab_rules : command
| `(command| #show_problem_extraction) => do
let ext := problemExtractionExtension
let env ← getEnv
let st := ext.getState env
IO.println s!"ProblemExtraction st.size = {st.size}"
for ⟨filename, _⟩ in st do
IO.println s!"{filename}"
let st := determineDeclsExtension.getState env
IO.println s!"Determine decls:"
for n in st do
IO.println s!"{n}"