feat: Elementarized tactics with motives, congruence, and absurdity #72

Merged
aniva merged 41 commits from goal/mapply into dev 2024-06-12 13:52:46 -07:00
6 changed files with 100 additions and 40 deletions
Showing only changes of commit 92acf7782c - Show all commits

View File

@ -6,73 +6,80 @@ namespace Pantograph.Tactic
def congruenceArg: Elab.Tactic.TacticM Unit := do def congruenceArg: Elab.Tactic.TacticM Unit := do
let goal ← Elab.Tactic.getMainGoal let goal ← Elab.Tactic.getMainGoal
let .some (beta, _, _) := (← goal.getType).eq? | throwError "Goal is not an Eq" let .some (β, _, _) := (← goal.getType).eq? | throwError "Goal is not an Eq"
-- Create the descendant goals let userName := (← goal.getDecl).userName
let nextGoals ← goal.withContext do let nextGoals ← goal.withContext do
let u ← Meta.mkFreshLevelMVar let u ← Meta.mkFreshLevelMVar
let alpha ← Meta.mkFreshExprMVar (.some $ mkSort u) .natural .anonymous let α ← Meta.mkFreshExprMVar (.some $ mkSort u)
let f ← Meta.mkFreshExprMVar (.some <| .forallE .anonymous alpha beta .default) .natural (userName := userName ++ `α)
.synthetic (userName := goal.name ++ `f) let f ← Meta.mkFreshExprMVar (.some <| .forallE .anonymous α β .default)
let a₁ ← Meta.mkFreshExprMVar (.some alpha) .synthetic (userName := userName ++ `f)
.synthetic (userName := goal.name ++ `a₁) let a₁ ← Meta.mkFreshExprMVar (.some α)
let a₂ ← Meta.mkFreshExprMVar (.some alpha) .synthetic (userName := userName ++ `a₁)
.synthetic (userName := goal.name ++ `a₂) let a₂ ← Meta.mkFreshExprMVar (.some α)
let h ← Meta.mkEq a₁ a₂ .synthetic (userName := userName ++ `a₂)
let h ← Meta.mkFreshExprMVar (.some $ ← Meta.mkEq a₁ a₂)
.synthetic (userName := userName ++ `h)
let conduitType ← Meta.mkEq (← Meta.mkEq (.app f a₁) (.app f a₂)) (← goal.getType) let conduitType ← Meta.mkEq (← Meta.mkEq (.app f a₁) (.app f a₂)) (← goal.getType)
let conduit ← Meta.mkFreshExprMVar conduitType let conduit ← Meta.mkFreshExprMVar conduitType
.synthetic (userName := goal.name ++ `conduit) .synthetic (userName := userName ++ `conduit)
goal.assign $ ← Meta.mkEqMP conduit (← Meta.mkCongrArg f h) goal.assign $ ← Meta.mkEqMP conduit (← Meta.mkCongrArg f h)
return [alpha, a₁, a₂, f, h, conduit] return [α, a₁, a₂, f, h, conduit]
Elab.Tactic.setGoals <| nextGoals.map (·.mvarId!) Elab.Tactic.setGoals <| nextGoals.map (·.mvarId!)
def congruenceFun: Elab.Tactic.TacticM Unit := do def congruenceFun: Elab.Tactic.TacticM Unit := do
let goal ← Elab.Tactic.getMainGoal let goal ← Elab.Tactic.getMainGoal
let .some (beta, _, _) := (← goal.getType).eq? | throwError "Goal is not an Eq" let .some (β, _, _) := (← goal.getType).eq? | throwError "Goal is not an Eq"
-- Create the descendant goals let userName := (← goal.getDecl).userName
let nextGoals ← goal.withContext do let nextGoals ← goal.withContext do
let u ← Meta.mkFreshLevelMVar let u ← Meta.mkFreshLevelMVar
let alpha ← Meta.mkFreshExprMVar (.some $ mkSort u) .natural .anonymous let α ← Meta.mkFreshExprMVar (.some $ mkSort u)
let fType := .forallE .anonymous alpha beta .default .natural (userName := userName ++ `α)
let fType := .forallE .anonymous α β .default
let f₁ ← Meta.mkFreshExprMVar (.some fType) let f₁ ← Meta.mkFreshExprMVar (.some fType)
.synthetic (userName := goal.name ++ `f₁) .synthetic (userName := userName ++ `f₁)
let f₂ ← Meta.mkFreshExprMVar (.some fType) let f₂ ← Meta.mkFreshExprMVar (.some fType)
.synthetic (userName := goal.name ++ `f₂) .synthetic (userName := userName ++ `f₂)
let a ← Meta.mkFreshExprMVar (.some alpha) let a ← Meta.mkFreshExprMVar (.some α)
.synthetic (userName := goal.name ++ `a) .synthetic (userName := userName ++ `a)
let h ← Meta.mkEq f₁ f₂ let h ← Meta.mkFreshExprMVar (.some $ ← Meta.mkEq f₁ f₂)
.synthetic (userName := userName ++ `h)
let conduitType ← Meta.mkEq (← Meta.mkEq (.app f₁ a) (.app f₂ a)) (← goal.getType) let conduitType ← Meta.mkEq (← Meta.mkEq (.app f₁ a) (.app f₂ a)) (← goal.getType)
let conduit ← Meta.mkFreshExprMVar conduitType let conduit ← Meta.mkFreshExprMVar conduitType
.synthetic (userName := goal.name ++ `conduit) .synthetic (userName := userName ++ `conduit)
goal.assign $ ← Meta.mkEqMP conduit (← Meta.mkCongrFun h a) goal.assign $ ← Meta.mkEqMP conduit (← Meta.mkCongrFun h a)
return [alpha, f₁, f₂, h, a, conduit] return [α, f₁, f₂, h, a, conduit]
Elab.Tactic.setGoals <| nextGoals.map (·.mvarId!) Elab.Tactic.setGoals <| nextGoals.map (·.mvarId!)
def congruence: Elab.Tactic.TacticM Unit := do def congruence: Elab.Tactic.TacticM Unit := do
let goal ← Elab.Tactic.getMainGoal let goal ← Elab.Tactic.getMainGoal
let .some (beta, _, _) := (← goal.getType).eq? | throwError "Goal is not an Eq" let .some (β, _, _) := (← goal.getType).eq? | throwError "Goal is not an Eq"
-- Create the descendant goals let userName := (← goal.getDecl).userName
let nextGoals ← goal.withContext do let nextGoals ← goal.withContext do
let u ← Meta.mkFreshLevelMVar let u ← Meta.mkFreshLevelMVar
let alpha ← Meta.mkFreshExprMVar (.some $ mkSort u) .natural .anonymous let α ← Meta.mkFreshExprMVar (.some $ mkSort u)
let fType := .forallE .anonymous alpha beta .default .natural (userName := userName ++ `α)
let fType := .forallE .anonymous α β .default
let f₁ ← Meta.mkFreshExprMVar (.some fType) let f₁ ← Meta.mkFreshExprMVar (.some fType)
.synthetic (userName := goal.name ++ `f₁) .synthetic (userName := userName ++ `f₁)
let f₂ ← Meta.mkFreshExprMVar (.some fType) let f₂ ← Meta.mkFreshExprMVar (.some fType)
.synthetic (userName := goal.name ++ `f₂) .synthetic (userName := userName ++ `f₂)
let a₁ ← Meta.mkFreshExprMVar (.some alpha) let a₁ ← Meta.mkFreshExprMVar (.some α)
.synthetic (userName := goal.name ++ `a₁) .synthetic (userName := userName ++ `a₁)
let a₂ ← Meta.mkFreshExprMVar (.some alpha) let a₂ ← Meta.mkFreshExprMVar (.some α)
.synthetic (userName := goal.name ++ `a₂) .synthetic (userName := userName ++ `a₂)
let h₁ ← Meta.mkEq f₁ f₂ let h₁ ← Meta.mkFreshExprMVar (.some $ ← Meta.mkEq f₁ f₂)
let h₂ ← Meta.mkEq a₁ a₂ .synthetic (userName := userName ++ `h₁)
let h₂ ← Meta.mkFreshExprMVar (.some $ ← Meta.mkEq a₁ a₂)
.synthetic (userName := userName ++ `h₂)
let conduitType ← Meta.mkEq (← Meta.mkEq (.app f₁ a₁) (.app f₂ a₂)) (← goal.getType) let conduitType ← Meta.mkEq (← Meta.mkEq (.app f₁ a₁) (.app f₂ a₂)) (← goal.getType)
let conduit ← Meta.mkFreshExprMVar conduitType let conduit ← Meta.mkFreshExprMVar conduitType
.synthetic (userName := goal.name ++ `conduit) .synthetic (userName := userName ++ `conduit)
goal.assign $ ← Meta.mkEqMP conduit (← Meta.mkCongr h₁ h₂) goal.assign $ ← Meta.mkEqMP conduit (← Meta.mkCongr h₁ h₂)
return [alpha, f₁, f₂, a₁, a₂, h₁, h₂, conduit] return [α, f₁, f₂, a₁, a₂, h₁, h₂, conduit]
Elab.Tactic.setGoals <| nextGoals.map (·.mvarId!) Elab.Tactic.setGoals <| nextGoals.map (·.mvarId!)
end Pantograph.Tactic end Pantograph.Tactic

View File

@ -27,6 +27,7 @@ def Goal.devolatilize (goal: Goal): Goal :=
name := "", name := "",
} }
deriving instance DecidableEq, Repr for Name
deriving instance DecidableEq, Repr for Expression deriving instance DecidableEq, Repr for Expression
deriving instance DecidableEq, Repr for Variable deriving instance DecidableEq, Repr for Variable
deriving instance DecidableEq, Repr for Goal deriving instance DecidableEq, Repr for Goal
@ -65,9 +66,23 @@ def runTermElabMInMeta { α } (termElabM: Lean.Elab.TermElabM α): Lean.MetaM α
def exprToStr (e: Expr): Lean.MetaM String := toString <$> Meta.ppExpr e def exprToStr (e: Expr): Lean.MetaM String := toString <$> Meta.ppExpr e
def parseSentence (s: String): MetaM Expr := do
let recursor ← match Parser.runParserCategory
(env := ← MonadEnv.getEnv)
(catName := `term)
(input := s)
(fileName := filename) with
| .ok syn => pure syn
| .error error => throwError "Failed to parse: {error}"
runTermElabMInMeta $ Elab.Term.elabTerm (stx := recursor) .none
def runTacticOnMVar (tacticM: Elab.Tactic.TacticM Unit) (goal: MVarId): Elab.TermElabM (List MVarId) := do def runTacticOnMVar (tacticM: Elab.Tactic.TacticM Unit) (goal: MVarId): Elab.TermElabM (List MVarId) := do
let (_, newGoals) ← tacticM { elaborator := .anonymous } |>.run { goals := [goal] } let (_, newGoals) ← tacticM { elaborator := .anonymous } |>.run { goals := [goal] }
return newGoals.goals return newGoals.goals
def mvarUserNameAndType (mvarId: MVarId): MetaM (Name × String) := do
let name := (← mvarId.getDecl).userName
let t ← exprToStr (← mvarId.getType)
return (name, t)
end Test end Test

View File

@ -49,6 +49,7 @@ def main (args: List String) := do
("Metavar", Metavar.suite env_default), ("Metavar", Metavar.suite env_default),
("Proofs", Proofs.suite env_default), ("Proofs", Proofs.suite env_default),
("Serial", Serial.suite env_default), ("Serial", Serial.suite env_default),
("Tactic/Congruence", Tactic.Congruence.suite env_default),
("Tactic/Motivated Apply", Tactic.MotivatedApply.suite env_default), ("Tactic/Motivated Apply", Tactic.MotivatedApply.suite env_default),
("Tactic/No Confuse", Tactic.NoConfuse.suite env_default), ("Tactic/No Confuse", Tactic.NoConfuse.suite env_default),
] ]

View File

@ -1,2 +1,3 @@
import Test.Tactic.Congruence
import Test.Tactic.MotivatedApply import Test.Tactic.MotivatedApply
import Test.Tactic.NoConfuse import Test.Tactic.NoConfuse

View File

@ -0,0 +1,36 @@
import LSpec
import Lean
import Test.Common
open Lean
open Pantograph
namespace Pantograph.Test.Tactic.Congruence
def test_congr_arg (env: Environment): IO LSpec.TestSeq :=
let expr := "λ (n m: Nat) (h: n = m) => n * n = m * m"
runMetaMSeq env do
let expr ← parseSentence expr
Meta.lambdaTelescope expr $ λ _ body => do
let mut tests := LSpec.TestSeq.done
let target ← Meta.mkFreshExprSyntheticOpaqueMVar body
let test ← runTermElabMInMeta do
let newGoals ← runTacticOnMVar Tactic.congruenceArg target.mvarId!
pure $ LSpec.check "goals" ((← newGoals.mapM (λ x => mvarUserNameAndType x)) =
[
(`α, "Sort ?u.70"),
(`a₁, "?α"),
(`a₂, "?α"),
(`f, "?α → Nat"),
(`h, "?a₁ = ?a₂"),
(`conduit, "(?f ?a₁ = ?f ?a₂) = (n * n = m * m)"),
])
tests := tests ++ test
return tests
def suite (env: Environment): List (String × IO LSpec.TestSeq) :=
[
("congrArg", test_congr_arg env),
]
end Pantograph.Test.Tactic.Congruence

View File

@ -92,9 +92,9 @@ def test_list (env: Environment): IO LSpec.TestSeq :=
def suite (env: Environment): List (String × IO LSpec.TestSeq) := def suite (env: Environment): List (String × IO LSpec.TestSeq) :=
[ [
("nat", test_nat env), ("Nat", test_nat env),
("nat_fail", test_nat_fail env), ("Nat fail", test_nat_fail env),
("list", test_list env), ("List", test_list env),
] ]
end Pantograph.Test.Tactic.NoConfuse end Pantograph.Test.Tactic.NoConfuse