File size: 3,864 Bytes
6c3fe2a | 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 | import Lean
import Mathlib
open Lean Elab Command Term Meta Tactic
set_option autoImplicit false
set_option relaxedAutoImplicit false
structure ProblemSol {Ξ± : Type} (P : Ξ± β Prop) where
Answer : Ξ±
Proof : P Answer
def ProblemTarget (p : Prop) : Prop := p
def getRawBody (e : Expr) (i : Nat := 2) : MetaM Expr := do
lambdaTelescope e fun args body => do
let body β whnf body
if body.isAppOf ``ProblemSol.mk then
let val := body.getAppArgs[i]!
mkLambdaFVars args val
else
throwError "Extraction failed: Body is not a ProblemSol constructor. Body: {body}"
elab "extract_ans_type " id:ident : term => do
let name β resolveGlobalConstNoOverload id
let info β getConstInfoDefn name
getRawBody info.value 0
elab "extract_predicate " id:ident : term => do
let name β resolveGlobalConstNoOverload id
let info β getConstInfoDefn name
getRawBody info.value 1
elab "extract_answer " id:ident : term => do
let name β resolveGlobalConstNoOverload id
let info β getConstInfoDefn name
getRawBody info.value 2
elab "extract_proof " id:ident : term => do
let name β resolveGlobalConstNoOverload id
let info β getConstInfoDefn name
getRawBody info.value 3
namespace FPS
scoped syntax (name := problemSyntax)
"problem" ident bracketedBinder* "find" "(" ident ":" term ")"
"s.t." bracketedBinder* ":" term ":=" term : command
scoped macro_rules
| `(problem $name:ident $vars* find ($ansId:ident : $ansType:term) s.t. $hyps* : $concl:term := $body:term) => do
let proofSpec β `(fun $ansId:ident : $ansType => β $hyps*, ProblemTarget $concl)
`(@[reducible] noncomputable def $name $vars* : ProblemSol $proofSpec := $body)
scoped syntax (name := solveSyntax) "solve" (ppSpace tacticSeq)? : term
@[term_elab solveSyntax]
def elabSolve : TermElab := fun stx expectedType? => do
let some expectedType := expectedType?
| throwError "`solve` must be used in a position of known type"
let mvar β mkFreshExprMVar expectedType
let mvarId := mvar.mvarId!
let setupTx β `(tactic|
apply ProblemSol.mk;
intros;
unfold ProblemTarget
)
let _ β Lean.Elab.Tactic.run mvarId do
withOptions (fun o => o.setBool `tactic.hygienic false) do
evalTactic setupTx
withTacticInfoContext stx[0] (pure ())
if let some seq := stx[1].getOptional? then
evalTactic seq
return mvar
end FPS
namespace DFPS
scoped syntax (name := problemSyntax)
"problem" ident bracketedBinder* "find_all" "(" ident ":" term ")"
"iff" bracketedBinder* ":" term ":=" term : command
scoped macro_rules
| `(problem $name:ident $vars* find_all ($ansId:ident : $ansType:term) iff $hyps* : $concl:term := $body:term) => do
let proofSpec β `(fun answer_predicate : Prop => β $hyps*, (ProblemTarget $concl) β (answer_predicate))
`(@[reducible] noncomputable def $name $vars* ($ansId : $ansType) : ProblemSol $proofSpec := $body)
scoped syntax (name := solveSyntax) "solve" (ppSpace tacticSeq)? : term
@[term_elab solveSyntax]
def elabSolve : TermElab := fun stx expectedType? => do
let some expectedType := expectedType?
| throwError "`solve` must be used in a position of known type"
let hAnswerName := mkIdent `_h_answer
let hConclusionName := mkIdent `_h_conclusion
let mvar β mkFreshExprMVar expectedType
let mvarId := mvar.mvarId!
let setupTx β `(tactic|
apply ProblemSol.mk;
intros;
unfold ProblemTarget;
refine @Iff.intro ?_ _ ?Forward ?Backward;
rotate_left;
intros $hAnswerName;
rotate_right;
intros $hConclusionName;
)
let _ β Lean.Elab.Tactic.run mvarId do
withOptions (fun o => o.setBool `tactic.hygienic false) do
evalTactic setupTx
withTacticInfoContext stx[0] (pure ())
if let some seq := stx[1].getOptional? then
evalTactic seq
return mvar
end DFPS
|