feat: add evalCases

This commit is contained in:
Leonardo de Moura 2020-03-05 09:20:03 -08:00
parent 5bdc54d344
commit b5ec4ef2bd
2 changed files with 69 additions and 49 deletions

View file

@ -6,6 +6,7 @@ Authors: Leonardo de Moura, Sebastian Ullrich
prelude
import Init.Lean.Meta.RecursorInfo
import Init.Lean.Meta.Tactic.Induction
import Init.Lean.Meta.Tactic.Cases
import Init.Lean.Elab.Tactic.ElabTerm
import Init.Lean.Elab.Tactic.Generalize
@ -149,6 +150,35 @@ match recInfo? with
(fun _ => throwError ref ("invalid recursor name '" ++ baseRecName ++ "'"))
| _ => throwError ref ("invalid recursor name '" ++ baseRecName ++ "'")
/- Create `RecInfo` assuming builtin recursor -/
private def getRecInfoDefault (ref : Syntax) (major : Expr) (withAlts : Syntax) : TacticM RecInfo := do
indVal ← getInductiveValFromMajor ref major;
let recName := mkRecFor indVal.name;
if withAlts.isNone then pure { recName := recName }
else do
let ctorNames := indVal.ctors;
let alts := getAlts withAlts;
checkAltCtorNames alts ctorNames;
(altVars, altRHSs, remainingAlts, _) ← ctorNames.foldlM
(fun (result : Array (List Name) × Array Syntax × Array Syntax × Option Syntax) (ctorName : Name) => do
let (altVars, altRHSs, remainingAlts, prevAnonymousAlt?) := result;
match remainingAlts.findIdx? (fun alt => (getAltName alt).isSuffixOf ctorName) with
| some idx =>
let newAlt := remainingAlts.get! idx;
pure (altVars.push (getAltVarNames newAlt).toList, altRHSs.push (getAltRHS newAlt), remainingAlts.eraseIdx idx, prevAnonymousAlt?)
| none => match remainingAlts.findIdx? (fun alt => getAltName alt == `_) with
| some idx =>
let newAlt := remainingAlts.get! idx;
pure (altVars.push (getAltVarNames newAlt).toList, altRHSs.push (getAltRHS newAlt), remainingAlts.eraseIdx idx, some newAlt)
| none => match prevAnonymousAlt? with
| some alt =>
pure (altVars.push (getAltVarNames alt).toList, altRHSs.push (getAltRHS alt), remainingAlts, prevAnonymousAlt?)
| none => throwError ref ("alternative for constructor '" ++ toString ctorName ++ "' is missing"))
(#[], #[], alts, none);
unless remainingAlts.isEmpty $
throwError (remainingAlts.get! 0) "unused alternative";
pure { recName := recName, altVars := altVars, altRHSs := altRHSs }
/-
Recall that
```
@ -162,32 +192,7 @@ let ref := stx;
let usingRecStx := stx.getArg 2;
let withAlts := stx.getArg 4;
if usingRecStx.isNone then do
indVal ← getInductiveValFromMajor ref major;
let recName := mkRecFor indVal.name;
if withAlts.isNone then pure { recName := recName }
else do
let ctorNames := indVal.ctors;
let alts := getAlts withAlts;
checkAltCtorNames alts ctorNames;
(altVars, altRHSs, remainingAlts, _) ← ctorNames.foldlM
(fun (result : Array (List Name) × Array Syntax × Array Syntax × Option Syntax) (ctorName : Name) => do
let (altVars, altRHSs, remainingAlts, prevAnonymousAlt?) := result;
match remainingAlts.findIdx? (fun alt => (getAltName alt).isSuffixOf ctorName) with
| some idx =>
let newAlt := remainingAlts.get! idx;
pure (altVars.push (getAltVarNames newAlt).toList, altRHSs.push (getAltRHS newAlt), remainingAlts.eraseIdx idx, prevAnonymousAlt?)
| none => match remainingAlts.findIdx? (fun alt => getAltName alt == `_) with
| some idx =>
let newAlt := remainingAlts.get! idx;
pure (altVars.push (getAltVarNames newAlt).toList, altRHSs.push (getAltRHS newAlt), remainingAlts.eraseIdx idx, some newAlt)
| none => match prevAnonymousAlt? with
| some alt =>
pure (altVars.push (getAltVarNames alt).toList, altRHSs.push (getAltRHS alt), remainingAlts, prevAnonymousAlt?)
| none => throwError ref ("alternative for constructor '" ++ toString ctorName ++ "' is missing"))
(#[], #[], alts, none);
unless remainingAlts.isEmpty $
throwError (remainingAlts.get! 0) "unused alternative";
pure { recName := recName, altVars := altVars, altRHSs := altRHSs }
getRecInfoDefault ref major withAlts
else do
let baseRecName := (usingRecStx.getIdAt 1).eraseMacroScopes;
recInfo ← getRecFromUsing ref major baseRecName;
@ -220,6 +225,26 @@ else do
throwError (remainingAlts.get! 0) "unused alternative";
pure { recName := recName, altVars := altVars, altRHSs := altRHSs }
private def processResult (ref : Syntax) (recInfo : RecInfo) (result : Array Meta.InductionSubgoal) : TacticM Unit := do
if recInfo.altRHSs.isEmpty then
setGoals $ result.toList.map $ fun s => s.mvarId
else do
unless (recInfo.altRHSs.size == result.size) $
throwError ref ("mistmatch on the number of subgoals produced (" ++ toString result.size ++ ") and " ++
"alternatives provided (" ++ toString recInfo.altRHSs.size ++ ")");
result.size.forM $ fun i => do
let subgoal := result.get! i;
let rhs := recInfo.altRHSs.get! i;
let mvarId := subgoal.mvarId;
withMVarContext mvarId $ do
mvarDecl ← getMVarDecl mvarId;
val ← elabTerm rhs mvarDecl.type;
val ← ensureHasType rhs mvarDecl.type val;
assignExprMVar mvarId val;
gs' ← collectMVars rhs val;
tagUntaggedGoals mvarDecl.userName `induction gs';
appendGoals gs'
@[builtinTactic «induction»] def evalInduction : Tactic :=
fun stx => focusAux stx $ do
let h? := getAuxHypothesisName stx;
@ -229,24 +254,20 @@ fun stx => focusAux stx $ do
recInfo ← getRecInfo stx major;
(mvarId, _) ← getMainGoal stx;
result ← liftMetaM stx $ Meta.induction mvarId major.fvarId! recInfo.recName recInfo.altVars;
if recInfo.altRHSs.isEmpty then
setGoals $ result.toList.map $ fun s => s.mvarId
else do
unless (recInfo.altRHSs.size == result.size) $
throwError stx ("mistmatch on the number of subgoals produced (" ++ toString result.size ++ ") and " ++
"alternatives provided (" ++ toString recInfo.altRHSs.size ++ ")");
result.size.forM $ fun i => do
let subgoal := result.get! i;
let rhs := recInfo.altRHSs.get! i;
let mvarId := subgoal.mvarId;
withMVarContext mvarId $ do
mvarDecl ← getMVarDecl mvarId;
val ← elabTerm rhs mvarDecl.type;
val ← ensureHasType rhs mvarDecl.type val;
assignExprMVar mvarId val;
gs' ← collectMVars rhs val;
tagUntaggedGoals mvarDecl.userName `induction gs';
appendGoals gs'
processResult stx recInfo result
@[builtinTactic «cases»] def evalCases : Tactic :=
fun stx => focusAux stx $ do
-- parser! nonReservedSymbol "cases " >> majorPremise >> withAlts
let h? := getAuxHypothesisName stx;
major ← elabMajor stx h? (getMajor stx);
major ← generalizeMajor stx major;
(mvarId, _) ← getMainGoal stx;
let withAlts := stx.getArg 2;
recInfo ← getRecInfoDefault stx major withAlts;
result ← liftMetaM stx $ Meta.cases mvarId major.fvarId! recInfo.altVars;
let result := result.map (fun s => s.toInductionSubgoal);
processResult stx recInfo result
end Tactic
end Elab

View file

@ -97,11 +97,8 @@ withMVarContext mvarId $ do
}
| _ => throwTacticEx `generalizeIndices mvarId "inductive type expected"
structure CasesSubgoal :=
structure CasesSubgoal extends InductionSubgoal :=
(ctorName : Name)
(mvarId : MVarId)
(fields : Array FVarId := #[])
(subst : FVarSubst := {})
namespace Cases
@ -162,7 +159,9 @@ end Cases
def cases (mvarId : MVarId) (majorFVarId : FVarId) (givenNames : Array (List Name) := #[]) (useUnusedNames := false) :
MetaM (Array CasesSubgoal) :=
throw $ arbitrary _ -- TODO
withMVarContext mvarId $ do
checkNotAssigned mvarId `cases;
throwTacticEx `cases mvarId ("WIP " ++ Format.line ++ MessageData.ofGoal mvarId)
end Meta
end Lean