chore: Version 0.3 #136
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
|
import Test.Tactic.Congruence
|
||||||
import Test.Tactic.MotivatedApply
|
import Test.Tactic.MotivatedApply
|
||||||
import Test.Tactic.NoConfuse
|
import Test.Tactic.NoConfuse
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue