Compare commits
25 Commits
repl/elab-
...
dev
Author | SHA1 | Date |
---|---|---|
|
120eb90291 | |
|
7578e9e180 | |
|
df370b0bff | |
|
7b9361c72b | |
|
ad55ea1a27 | |
|
8c1cea17e3 | |
|
4db09c3abc | |
|
beef22b945 | |
|
06071c1044 | |
|
f214496c8f | |
|
b039018578 | |
|
49d06e8c05 | |
|
0a987174bc | |
|
eca7431977 | |
|
170099525c | |
|
3653465ded | |
|
a2f9d77cb5 | |
|
a88829f9be | |
|
d9c484230b | |
|
fa5d423005 | |
|
c68fed6657 | |
|
60f79f5f02 | |
|
4abe2fa72f | |
|
77b517f4c6 | |
|
4e44b147e0 |
18
Main.lean
18
Main.lean
|
@ -8,6 +8,12 @@ import Repl
|
||||||
open Pantograph.Repl
|
open Pantograph.Repl
|
||||||
open Pantograph.Protocol
|
open Pantograph.Protocol
|
||||||
|
|
||||||
|
/-- Print a string to stdout without buffering -/
|
||||||
|
def printImmediate (s : String) : IO Unit := do
|
||||||
|
let stdout ← IO.getStdout
|
||||||
|
stdout.putStr (s ++ "\n")
|
||||||
|
stdout.flush
|
||||||
|
|
||||||
/-- Parse a command either in `{ "cmd": ..., "payload": ... }` form or `cmd { ... }` form. -/
|
/-- Parse a command either in `{ "cmd": ..., "payload": ... }` form or `cmd { ... }` form. -/
|
||||||
def parseCommand (s: String): Except String Command := do
|
def parseCommand (s: String): Except String Command := do
|
||||||
match s.trim.get? 0 with
|
match s.trim.get? 0 with
|
||||||
|
@ -34,26 +40,26 @@ partial def loop : MainM Unit := do repeat do
|
||||||
| .error error =>
|
| .error error =>
|
||||||
let error := Lean.toJson ({ error := "command", desc := error }: InteractionError)
|
let error := Lean.toJson ({ error := "command", desc := error }: InteractionError)
|
||||||
-- Using `Lean.Json.compress` here to prevent newline
|
-- Using `Lean.Json.compress` here to prevent newline
|
||||||
IO.println error.compress
|
printImmediate error.compress
|
||||||
| .ok command =>
|
| .ok command =>
|
||||||
try
|
try
|
||||||
let ret ← execute command
|
let ret ← execute command
|
||||||
let str := match state.options.printJsonPretty with
|
let str := match state.options.printJsonPretty with
|
||||||
| true => ret.pretty
|
| true => ret.pretty
|
||||||
| false => ret.compress
|
| false => ret.compress
|
||||||
IO.println str
|
printImmediate str
|
||||||
catch e =>
|
catch e =>
|
||||||
let message := e.toString
|
let message := e.toString
|
||||||
let error := Lean.toJson ({ error := "main", desc := message }: InteractionError)
|
let error := Lean.toJson ({ error := "main", desc := message }: InteractionError)
|
||||||
IO.println error.compress
|
printImmediate error.compress
|
||||||
|
|
||||||
|
def main (args: List String): IO Unit := do
|
||||||
unsafe def main (args: List String): IO Unit := do
|
|
||||||
-- NOTE: A more sophisticated scheme of command line argument handling is needed.
|
-- NOTE: A more sophisticated scheme of command line argument handling is needed.
|
||||||
if args == ["--version"] then do
|
if args == ["--version"] then do
|
||||||
IO.println s!"{Pantograph.version}"
|
IO.println s!"{Pantograph.version}"
|
||||||
return
|
return
|
||||||
|
|
||||||
|
unsafe do
|
||||||
Pantograph.initSearch ""
|
Pantograph.initSearch ""
|
||||||
|
|
||||||
-- Separate imports and options
|
-- Separate imports and options
|
||||||
|
@ -62,7 +68,7 @@ unsafe def main (args: List String): IO Unit := do
|
||||||
let coreState ← Pantograph.createCoreState imports.toArray
|
let coreState ← Pantograph.createCoreState imports.toArray
|
||||||
try
|
try
|
||||||
let mainM := loop.run { coreContext } |>.run' { env := coreState.env }
|
let mainM := loop.run { coreContext } |>.run' { env := coreState.env }
|
||||||
IO.println "ready."
|
printImmediate "ready."
|
||||||
mainM
|
mainM
|
||||||
catch ex =>
|
catch ex =>
|
||||||
let message := ex.toString
|
let message := ex.toString
|
||||||
|
|
|
@ -164,7 +164,7 @@ partial def instantiateDelayedMVars (expr : Expr) : MetaM Expr :=
|
||||||
self e := instantiateDelayedMVars e
|
self e := instantiateDelayedMVars e
|
||||||
|
|
||||||
/--
|
/--
|
||||||
Convert an expression to an equiavlent form with
|
Convert an expression to an equivalent form with
|
||||||
1. No nested delayed assigned mvars
|
1. No nested delayed assigned mvars
|
||||||
2. No aux lemmas or matchers
|
2. No aux lemmas or matchers
|
||||||
3. No assigned mvars
|
3. No assigned mvars
|
||||||
|
|
|
@ -154,9 +154,7 @@ the draft tactic instead.
|
||||||
-/
|
-/
|
||||||
@[export pantograph_frontend_sorrys_to_goal_state_m]
|
@[export pantograph_frontend_sorrys_to_goal_state_m]
|
||||||
def sorrysToGoalState (sorrys : List InfoWithContext) : MetaM AnnotatedGoalState := do
|
def sorrysToGoalState (sorrys : List InfoWithContext) : MetaM AnnotatedGoalState := do
|
||||||
let env := sorrys.head? >>= (·.context?) |>.map (·.env) |>.getD (← getEnv)
|
|
||||||
assert! !sorrys.isEmpty
|
assert! !sorrys.isEmpty
|
||||||
withEnv env do
|
|
||||||
let goalsM := sorrys.mapM λ i => do
|
let goalsM := sorrys.mapM λ i => do
|
||||||
match i.info with
|
match i.info with
|
||||||
| .ofTermInfo termInfo => do
|
| .ofTermInfo termInfo => do
|
||||||
|
|
|
@ -118,6 +118,7 @@ partial def translateLCtx : MetaTranslateM LocalContext := do
|
||||||
|
|
||||||
partial def translateMVarId (srcMVarId: MVarId) : MetaTranslateM MVarId := do
|
partial def translateMVarId (srcMVarId: MVarId) : MetaTranslateM MVarId := do
|
||||||
if let .some mvarId' := (← get).mvarMap[srcMVarId]? then
|
if let .some mvarId' := (← get).mvarMap[srcMVarId]? then
|
||||||
|
trace[Pantograph.Frontend.MetaTranslate] "Existing mvar id {srcMVarId.name} → {mvarId'.name}"
|
||||||
return mvarId'
|
return mvarId'
|
||||||
let mvarId' ← Meta.withLCtx .empty #[] do
|
let mvarId' ← Meta.withLCtx .empty #[] do
|
||||||
let srcDecl := (← getSourceMCtx).findDecl? srcMVarId |>.get!
|
let srcDecl := (← getSourceMCtx).findDecl? srcMVarId |>.get!
|
||||||
|
@ -134,6 +135,7 @@ partial def translateMVarId (srcMVarId: MVarId) : MetaTranslateM MVarId := do
|
||||||
let fvars' ← mvarIdPending'.withContext $ fvars.mapM translateExpr
|
let fvars' ← mvarIdPending'.withContext $ fvars.mapM translateExpr
|
||||||
assignDelayedMVar mvarId' fvars' mvarIdPending'
|
assignDelayedMVar mvarId' fvars' mvarIdPending'
|
||||||
pure mvarId'
|
pure mvarId'
|
||||||
|
trace[Pantograph.Frontend.MetaTranslate] "Translated {srcMVarId.name} → {mvarId'.name}"
|
||||||
addTranslatedMVar srcMVarId mvarId'
|
addTranslatedMVar srcMVarId mvarId'
|
||||||
return mvarId'
|
return mvarId'
|
||||||
end
|
end
|
||||||
|
@ -148,6 +150,7 @@ def translateMVarFromTermInfo (termInfo : Elab.TermInfo) (context? : Option Elab
|
||||||
let lctx' ← translateLCtx
|
let lctx' ← translateLCtx
|
||||||
let mvar ← Meta.withLCtx lctx' #[] do
|
let mvar ← Meta.withLCtx lctx' #[] do
|
||||||
let type' ← translateExpr type
|
let type' ← translateExpr type
|
||||||
|
trace[Pantograph.Frontend.MetaTranslate] "Translating from term info {← Meta.ppExpr type'}"
|
||||||
Meta.mkFreshExprSyntheticOpaqueMVar type'
|
Meta.mkFreshExprSyntheticOpaqueMVar type'
|
||||||
return mvar.mvarId!
|
return mvar.mvarId!
|
||||||
|
|
||||||
|
|
|
@ -113,11 +113,14 @@ protected def GoalState.focus (state: GoalState) (goalId: Nat): Option GoalState
|
||||||
}
|
}
|
||||||
|
|
||||||
/-- Immediately bring all parent goals back into scope. Used in automatic mode -/
|
/-- Immediately bring all parent goals back into scope. Used in automatic mode -/
|
||||||
@[export pantograph_goal_state_immediate_resume_parent]
|
@[export pantograph_goal_state_immediate_resume]
|
||||||
protected def GoalState.immediateResume (state: GoalState) (parent: GoalState): GoalState :=
|
protected def GoalState.immediateResume (state: GoalState) (parent: GoalState): GoalState :=
|
||||||
-- Prune parents solved goals
|
-- Prune parents solved goals
|
||||||
let mctx := state.mctx
|
let mctx := state.mctx
|
||||||
let parentGoals := parent.goals.filter $ λ goal => mctx.eAssignment.contains goal
|
let parentGoals := parent.goals.filter λ goal =>
|
||||||
|
let isDuplicate := state.goals.contains goal
|
||||||
|
let isSolved := mctx.eAssignment.contains goal || mctx.dAssignment.contains goal
|
||||||
|
(¬ isDuplicate) && (¬ isSolved)
|
||||||
{
|
{
|
||||||
state with
|
state with
|
||||||
savedState := {
|
savedState := {
|
||||||
|
@ -127,19 +130,18 @@ protected def GoalState.immediateResume (state: GoalState) (parent: GoalState):
|
||||||
}
|
}
|
||||||
|
|
||||||
/--
|
/--
|
||||||
Brings into scope a list of goals
|
Brings into scope a list of goals. User must ensure `goals` is distinct.
|
||||||
-/
|
-/
|
||||||
@[export pantograph_goal_state_resume]
|
@[export pantograph_goal_state_resume]
|
||||||
protected def GoalState.resume (state: GoalState) (goals: List MVarId): Except String GoalState :=
|
protected def GoalState.resume (state: GoalState) (goals: List MVarId): Except String GoalState := do
|
||||||
if ¬ (goals.all (λ goal => state.mvars.contains goal)) then
|
if ¬ (goals.all (λ goal => state.mvars.contains goal)) then
|
||||||
let invalid_goals := goals.filter (λ goal => ¬ state.mvars.contains goal) |>.map (·.name.toString)
|
let invalid_goals := goals.filter (λ goal => ¬ state.mvars.contains goal) |>.map (·.name.toString)
|
||||||
.error s!"Goals {invalid_goals} are not in scope"
|
.error s!"Goals {invalid_goals} are not in scope"
|
||||||
else
|
|
||||||
-- Set goals to the goals that have not been assigned yet, similar to the `focus` tactic.
|
-- Set goals to the goals that have not been assigned yet, similar to the `focus` tactic.
|
||||||
let unassigned := goals.filter (λ goal =>
|
let unassigned := goals.filter λ goal =>
|
||||||
let mctx := state.mctx
|
let isSolved := state.mctx.eAssignment.contains goal || state.mctx.dAssignment.contains goal
|
||||||
¬(mctx.eAssignment.contains goal || mctx.dAssignment.contains goal))
|
¬ isSolved
|
||||||
.ok {
|
return {
|
||||||
state with
|
state with
|
||||||
savedState := {
|
savedState := {
|
||||||
term := state.savedState.term,
|
term := state.savedState.term,
|
||||||
|
@ -164,13 +166,13 @@ protected def GoalState.rootExpr? (goalState: GoalState): Option Expr := do
|
||||||
.none
|
.none
|
||||||
let expr ← goalState.mctx.eAssignment.find? goalState.root
|
let expr ← goalState.mctx.eAssignment.find? goalState.root
|
||||||
let (expr, _) := instantiateMVarsCore (mctx := goalState.mctx) (e := expr)
|
let (expr, _) := instantiateMVarsCore (mctx := goalState.mctx) (e := expr)
|
||||||
if expr.hasExprMVar then
|
|
||||||
-- Must not assert that the goal state is empty here. We could be in a branch goal.
|
|
||||||
--assert! ¬goalState.goals.isEmpty
|
|
||||||
.none
|
|
||||||
else
|
|
||||||
assert! goalState.goals.isEmpty
|
|
||||||
return expr
|
return expr
|
||||||
|
@[export pantograph_goal_state_is_solved]
|
||||||
|
protected def GoalState.isSolved (goalState : GoalState) : Bool :=
|
||||||
|
let solvedRoot := match goalState.rootExpr? with
|
||||||
|
| .some e => ¬ e.hasExprMVar
|
||||||
|
| .none => true
|
||||||
|
goalState.goals.isEmpty && solvedRoot
|
||||||
@[export pantograph_goal_state_parent_expr]
|
@[export pantograph_goal_state_parent_expr]
|
||||||
protected def GoalState.parentExpr? (goalState: GoalState): Option Expr := do
|
protected def GoalState.parentExpr? (goalState: GoalState): Option Expr := do
|
||||||
let parent ← goalState.parentMVar?
|
let parent ← goalState.parentMVar?
|
||||||
|
@ -238,7 +240,7 @@ protected def GoalState.step (state: GoalState) (goal: MVarId) (tacticM: Elab.Ta
|
||||||
/-- Response for executing a tactic -/
|
/-- Response for executing a tactic -/
|
||||||
inductive TacticResult where
|
inductive TacticResult where
|
||||||
-- Goes to next state
|
-- Goes to next state
|
||||||
| success (state: GoalState)
|
| success (state : GoalState) (messages : Array String)
|
||||||
-- Tactic failed with messages
|
-- Tactic failed with messages
|
||||||
| failure (messages : Array String)
|
| failure (messages : Array String)
|
||||||
-- Could not parse tactic
|
-- Could not parse tactic
|
||||||
|
@ -246,15 +248,12 @@ inductive TacticResult where
|
||||||
-- The given action cannot be executed in the state
|
-- The given action cannot be executed in the state
|
||||||
| invalidAction (message : String)
|
| invalidAction (message : String)
|
||||||
|
|
||||||
private def dumpMessageLog (prevMessageLength : Nat) : CoreM (Array String) := do
|
private def dumpMessageLog (prevMessageLength : Nat) : CoreM (Bool × Array String) := do
|
||||||
let newMessages ← (← Core.getMessageLog).toList.drop prevMessageLength
|
let newMessages := (← Core.getMessageLog).toList.drop prevMessageLength
|
||||||
|>.filterMapM λ m => do
|
let hasErrors := newMessages.any (·.severity == .error)
|
||||||
if m.severity == .error then
|
let newMessages ← newMessages.mapM λ m => m.toString
|
||||||
return .some $ ← m.toString
|
|
||||||
else
|
|
||||||
return .none
|
|
||||||
Core.resetMessageLog
|
Core.resetMessageLog
|
||||||
return newMessages.toArray
|
return (hasErrors, newMessages.toArray)
|
||||||
|
|
||||||
/-- Executes a `TacticM` monad on this `GoalState`, collecting the errors as necessary -/
|
/-- Executes a `TacticM` monad on this `GoalState`, collecting the errors as necessary -/
|
||||||
protected def GoalState.tryTacticM
|
protected def GoalState.tryTacticM
|
||||||
|
@ -266,13 +265,16 @@ protected def GoalState.tryTacticM
|
||||||
let nextState ← state.step goal tacticM guardMVarErrors
|
let nextState ← state.step goal tacticM guardMVarErrors
|
||||||
|
|
||||||
-- Check if error messages have been generated in the core.
|
-- Check if error messages have been generated in the core.
|
||||||
let newMessages ← dumpMessageLog prevMessageLength
|
let (hasError, newMessages) ← dumpMessageLog prevMessageLength
|
||||||
if ¬ newMessages.isEmpty then
|
if hasError then
|
||||||
return .failure newMessages
|
return .failure newMessages
|
||||||
return .success nextState
|
else
|
||||||
|
return .success nextState newMessages
|
||||||
catch exception =>
|
catch exception =>
|
||||||
match exception with
|
match exception with
|
||||||
| .internal _ => return .failure $ ← dumpMessageLog prevMessageLength
|
| .internal _ =>
|
||||||
|
let (_, messages) ← dumpMessageLog prevMessageLength
|
||||||
|
return .failure messages
|
||||||
| _ => return .failure #[← exception.toMessageData.toString]
|
| _ => return .failure #[← exception.toMessageData.toString]
|
||||||
|
|
||||||
/-- Execute a string tactic on given state. Restores TermElabM -/
|
/-- Execute a string tactic on given state. Restores TermElabM -/
|
||||||
|
@ -341,7 +343,7 @@ protected def GoalState.conv (state: GoalState) (goal: MVarId):
|
||||||
parentMVar? := .some goal,
|
parentMVar? := .some goal,
|
||||||
convMVar? := .some (convRhs, goal, otherGoals),
|
convMVar? := .some (convRhs, goal, otherGoals),
|
||||||
calcPrevRhs? := .none
|
calcPrevRhs? := .none
|
||||||
}
|
} #[]
|
||||||
catch exception =>
|
catch exception =>
|
||||||
return .failure #[← exception.toMessageData.toString]
|
return .failure #[← exception.toMessageData.toString]
|
||||||
|
|
||||||
|
@ -378,7 +380,7 @@ protected def GoalState.convExit (state: GoalState):
|
||||||
parentMVar? := .some convGoal,
|
parentMVar? := .some convGoal,
|
||||||
convMVar? := .none
|
convMVar? := .none
|
||||||
calcPrevRhs? := .none
|
calcPrevRhs? := .none
|
||||||
}
|
} #[]
|
||||||
catch exception =>
|
catch exception =>
|
||||||
return .failure #[← exception.toMessageData.toString]
|
return .failure #[← exception.toMessageData.toString]
|
||||||
|
|
||||||
|
@ -456,7 +458,7 @@ protected def GoalState.tryCalc (state: GoalState) (goal: MVarId) (pred: String)
|
||||||
},
|
},
|
||||||
parentMVar? := .some goal,
|
parentMVar? := .some goal,
|
||||||
calcPrevRhs?
|
calcPrevRhs?
|
||||||
}
|
} #[]
|
||||||
catch exception =>
|
catch exception =>
|
||||||
return .failure #[← exception.toMessageData.toString]
|
return .failure #[← exception.toMessageData.toString]
|
||||||
|
|
||||||
|
|
|
@ -123,8 +123,9 @@ def goalPrint (state: GoalState) (rootExpr: Bool) (parentExpr: Bool) (goals: Boo
|
||||||
: CoreM Protocol.GoalPrintResult := runMetaM do
|
: CoreM Protocol.GoalPrintResult := runMetaM do
|
||||||
state.restoreMetaM
|
state.restoreMetaM
|
||||||
|
|
||||||
|
let rootExpr? := state.rootExpr?
|
||||||
let root? ← if rootExpr then
|
let root? ← if rootExpr then
|
||||||
state.rootExpr?.mapM λ expr => state.withRootContext do
|
rootExpr?.mapM λ expr => state.withRootContext do
|
||||||
serializeExpression options (← instantiateAll expr)
|
serializeExpression options (← instantiateAll expr)
|
||||||
else
|
else
|
||||||
pure .none
|
pure .none
|
||||||
|
@ -143,11 +144,15 @@ def goalPrint (state: GoalState) (rootExpr: Bool) (parentExpr: Bool) (goals: Boo
|
||||||
state.withContext mvarId do
|
state.withContext mvarId do
|
||||||
let .some expr ← getExprMVarAssignment? mvarId | return {}
|
let .some expr ← getExprMVarAssignment? mvarId | return {}
|
||||||
serializeExpression options (← instantiateAll expr)
|
serializeExpression options (← instantiateAll expr)
|
||||||
|
let env ← getEnv
|
||||||
return {
|
return {
|
||||||
root?,
|
root?,
|
||||||
parent?,
|
parent?,
|
||||||
goals,
|
goals,
|
||||||
extraMVars,
|
extraMVars,
|
||||||
|
rootHasSorry := rootExpr?.map (·.hasSorry) |>.getD false,
|
||||||
|
rootHasUnsafe := rootExpr?.map (env.hasUnsafe ·) |>.getD false,
|
||||||
|
rootHasMVar := rootExpr?.map (·.hasExprMVar) |>.getD false,
|
||||||
}
|
}
|
||||||
|
|
||||||
@[export pantograph_goal_have_m]
|
@[export pantograph_goal_have_m]
|
||||||
|
|
|
@ -268,14 +268,17 @@ structure GoalTactic where
|
||||||
structure GoalTacticResult where
|
structure GoalTacticResult where
|
||||||
-- The next goal state id. Existence of this field shows success
|
-- The next goal state id. Existence of this field shows success
|
||||||
nextStateId?: Option Nat := .none
|
nextStateId?: Option Nat := .none
|
||||||
-- If the array is empty, it shows the goals have been fully resolved.
|
-- If the array is empty, it shows the goals have been fully resolved. If this
|
||||||
|
-- is .none, there has been a tactic error.
|
||||||
goals?: Option (Array Goal) := .none
|
goals?: Option (Array Goal) := .none
|
||||||
|
|
||||||
-- Existence of this field shows tactic execution failure
|
messages? : Option (Array String) := .some #[]
|
||||||
tacticErrors?: Option (Array String) := .none
|
|
||||||
|
|
||||||
-- Existence of this field shows the tactic parsing has failed
|
-- Existence of this field shows the tactic parsing has failed
|
||||||
parseError? : Option String := .none
|
parseError? : Option String := .none
|
||||||
|
|
||||||
|
hasSorry : Bool := false
|
||||||
|
hasUnsafe : Bool := false
|
||||||
deriving Lean.ToJson
|
deriving Lean.ToJson
|
||||||
structure GoalContinue where
|
structure GoalContinue where
|
||||||
-- State from which the continuation acquires the context
|
-- State from which the continuation acquires the context
|
||||||
|
@ -319,6 +322,10 @@ structure GoalPrintResult where
|
||||||
parent?: Option Expression := .none
|
parent?: Option Expression := .none
|
||||||
goals: Array Goal := #[]
|
goals: Array Goal := #[]
|
||||||
extraMVars: Array Expression := #[]
|
extraMVars: Array Expression := #[]
|
||||||
|
|
||||||
|
rootHasSorry : Bool := false
|
||||||
|
rootHasUnsafe : Bool := false
|
||||||
|
rootHasMVar : Bool := true
|
||||||
deriving Lean.ToJson
|
deriving Lean.ToJson
|
||||||
|
|
||||||
-- Diagnostic Options, not available in REPL
|
-- Diagnostic Options, not available in REPL
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
namespace Pantograph
|
namespace Pantograph
|
||||||
|
|
||||||
@[export pantograph_version]
|
@[export pantograph_version]
|
||||||
def version := "0.3.0"
|
def version := "0.3.1"
|
||||||
|
|
||||||
end Pantograph
|
end Pantograph
|
||||||
|
|
|
@ -81,7 +81,7 @@ the environment might be setup like this:
|
||||||
``` sh
|
``` sh
|
||||||
LIB="../lib"
|
LIB="../lib"
|
||||||
LIB_MATHLIB="$LIB/mathlib4/.lake"
|
LIB_MATHLIB="$LIB/mathlib4/.lake"
|
||||||
export LEAN_PATH="$LIB/mathlib4/build/lib:$LIB_MATHLIB/aesop/build/lib:$LIB_MATHLIB/Qq/build/lib:$LIB_MATHLIB/std/build/lib"
|
export LEAN_PATH="$LIB_MATHLIB:$LIB_MATHLIB/aesop/build/lib:$LIB_MATHLIB/Qq/build/lib:$LIB_MATHLIB/std/build/lib"
|
||||||
|
|
||||||
LEAN_PATH=$LEAN_PATH build/bin/pantograph $@
|
LEAN_PATH=$LEAN_PATH build/bin/pantograph $@
|
||||||
```
|
```
|
||||||
|
|
22
Repl.lean
22
Repl.lean
|
@ -5,6 +5,8 @@ namespace Pantograph.Repl
|
||||||
|
|
||||||
open Lean
|
open Lean
|
||||||
|
|
||||||
|
set_option trace.Pantograph.Frontend.MetaTranslate true
|
||||||
|
|
||||||
structure Context where
|
structure Context where
|
||||||
coreContext : Core.Context
|
coreContext : Core.Context
|
||||||
|
|
||||||
|
@ -61,6 +63,10 @@ def runCoreM { α } (coreM : CoreM α) : EMainM α := do
|
||||||
catch ex =>
|
catch ex =>
|
||||||
let desc ← ex.toMessageData.toString
|
let desc ← ex.toMessageData.toString
|
||||||
return Except.error $ ({ error := "exception", desc } : Protocol.InteractionError)
|
return Except.error $ ({ error := "exception", desc } : Protocol.InteractionError)
|
||||||
|
finally
|
||||||
|
for {msg, ..} in (← getTraceState).traces do
|
||||||
|
IO.eprintln (← msg.format.toIO)
|
||||||
|
resetTraceState
|
||||||
if let .some token := cancelTk? then
|
if let .some token := cancelTk? then
|
||||||
runCancelTokenWithTimeout token (timeout := .ofBitVec options.timeout)
|
runCancelTokenWithTimeout token (timeout := .ofBitVec options.timeout)
|
||||||
let (result, state') ← match ← (coreM'.run coreCtx coreState).toIO' with
|
let (result, state') ← match ← (coreM'.run coreCtx coreState).toIO' with
|
||||||
|
@ -149,7 +155,7 @@ def frontend_process_inner (args: Protocol.FrontendProcess): EMainM Protocol.Fro
|
||||||
.some $ step.newConstants.toArray.map λ name => name.toString
|
.some $ step.newConstants.toArray.map λ name => name.toString
|
||||||
else
|
else
|
||||||
.none
|
.none
|
||||||
let (goalStateId?, goals?, goalSrcBoundaries?) ← if step.sorrys.isEmpty then do
|
let (goalStateId?, goals?, goalSrcBoundaries?) ← if step.sorrys.isEmpty then
|
||||||
pure (.none, .none, .none)
|
pure (.none, .none, .none)
|
||||||
else do
|
else do
|
||||||
let ({ state, srcBoundaries }, goals) ← liftMetaM do
|
let ({ state, srcBoundaries }, goals) ← liftMetaM do
|
||||||
|
@ -319,12 +325,10 @@ def execute (command: Protocol.Command): MainM Json := do
|
||||||
pure $ .error error
|
pure $ .error error
|
||||||
match nextGoalState? with
|
match nextGoalState? with
|
||||||
| .error error => Protocol.throw error
|
| .error error => Protocol.throw error
|
||||||
| .ok (.success nextGoalState) => do
|
| .ok (.success nextGoalState messages) => do
|
||||||
let nextGoalState ← match state.options.automaticMode, args.conv? with
|
let nextGoalState ← match state.options.automaticMode, args.conv? with
|
||||||
| true, .none => do
|
| true, .none => do
|
||||||
let .ok result := nextGoalState.resume (nextGoalState.goals ++ goalState.goals) |
|
pure $ nextGoalState.immediateResume goalState
|
||||||
Protocol.throw $ errorIO "Resuming known goals"
|
|
||||||
pure result
|
|
||||||
| true, .some true => pure nextGoalState
|
| true, .some true => pure nextGoalState
|
||||||
| true, .some false => do
|
| true, .some false => do
|
||||||
let .some (_, _, dormantGoals) := goalState.convMVar? |
|
let .some (_, _, dormantGoals) := goalState.convMVar? |
|
||||||
|
@ -334,17 +338,21 @@ def execute (command: Protocol.Command): MainM Json := do
|
||||||
pure result
|
pure result
|
||||||
| false, _ => pure nextGoalState
|
| false, _ => pure nextGoalState
|
||||||
let nextStateId ← newGoalState nextGoalState
|
let nextStateId ← newGoalState nextGoalState
|
||||||
|
let parentExpr := nextGoalState.parentExpr?.get!
|
||||||
let goals ← runCoreM $ nextGoalState.serializeGoals (parent := .some goalState) (options := state.options) |>.run'
|
let goals ← runCoreM $ nextGoalState.serializeGoals (parent := .some goalState) (options := state.options) |>.run'
|
||||||
return {
|
return {
|
||||||
nextStateId? := .some nextStateId,
|
nextStateId? := .some nextStateId,
|
||||||
goals? := .some goals,
|
goals? := .some goals,
|
||||||
|
messages? := .some messages,
|
||||||
|
hasSorry := parentExpr.hasSorry,
|
||||||
|
hasUnsafe := (← getEnv).hasUnsafe parentExpr,
|
||||||
}
|
}
|
||||||
| .ok (.parseError message) =>
|
| .ok (.parseError message) =>
|
||||||
return { parseError? := .some message }
|
return { messages? := .none, parseError? := .some message }
|
||||||
| .ok (.invalidAction message) =>
|
| .ok (.invalidAction message) =>
|
||||||
Protocol.throw $ errorI "invalid" message
|
Protocol.throw $ errorI "invalid" message
|
||||||
| .ok (.failure messages) =>
|
| .ok (.failure messages) =>
|
||||||
return { tacticErrors? := .some messages }
|
return { messages? := .some messages }
|
||||||
goal_continue (args: Protocol.GoalContinue): EMainM Protocol.GoalContinueResult := do
|
goal_continue (args: Protocol.GoalContinue): EMainM Protocol.GoalContinueResult := do
|
||||||
let state ← getMainState
|
let state ← getMainState
|
||||||
let .some target := state.goalStates[args.target]? |
|
let .some target := state.goalStates[args.target]? |
|
||||||
|
|
|
@ -71,7 +71,7 @@ def GoalState.get! (state: GoalState) (i: Nat): MVarId := state.goals[i]!
|
||||||
def GoalState.tacticOn (state: GoalState) (goalId: Nat) (tactic: String) := state.tryTactic (state.get! goalId) tactic
|
def GoalState.tacticOn (state: GoalState) (goalId: Nat) (tactic: String) := state.tryTactic (state.get! goalId) tactic
|
||||||
|
|
||||||
def TacticResult.toString : TacticResult → String
|
def TacticResult.toString : TacticResult → String
|
||||||
| .success state => s!".success ({state.goals.length} goals)"
|
| .success state _messages => s!".success ({state.goals.length} goals)"
|
||||||
| .failure messages =>
|
| .failure messages =>
|
||||||
let messages := "\n".intercalate messages.toList
|
let messages := "\n".intercalate messages.toList
|
||||||
s!".failure {messages}"
|
s!".failure {messages}"
|
||||||
|
|
|
@ -84,12 +84,17 @@ def test_tactic : Test :=
|
||||||
step "goal.tactic" ({ stateId := 0, tactic? := .some "intro x" }: Protocol.GoalTactic)
|
step "goal.tactic" ({ stateId := 0, tactic? := .some "intro x" }: Protocol.GoalTactic)
|
||||||
({ nextStateId? := .some 1, goals? := #[goal1], }: Protocol.GoalTacticResult),
|
({ nextStateId? := .some 1, goals? := #[goal1], }: Protocol.GoalTacticResult),
|
||||||
step "goal.print" ({ stateId := 1, parentExpr? := .some true, rootExpr? := .some true }: Protocol.GoalPrint)
|
step "goal.print" ({ stateId := 1, parentExpr? := .some true, rootExpr? := .some true }: Protocol.GoalPrint)
|
||||||
({ parent? := .some { pp? := .some "fun x => ?m.11" }, }: Protocol.GoalPrintResult),
|
({
|
||||||
|
root? := .some { pp? := "fun x => ?m.11"},
|
||||||
|
parent? := .some { pp? := .some "fun x => ?m.11" },
|
||||||
|
}: Protocol.GoalPrintResult),
|
||||||
step "goal.tactic" ({ stateId := 1, tactic? := .some "intro y" }: Protocol.GoalTactic)
|
step "goal.tactic" ({ stateId := 1, tactic? := .some "intro y" }: Protocol.GoalTactic)
|
||||||
({ nextStateId? := .some 2, goals? := #[goal2], }: Protocol.GoalTacticResult),
|
({ nextStateId? := .some 2, goals? := #[goal2], }: Protocol.GoalTacticResult),
|
||||||
step "goal.tactic" ({ stateId := 1, tactic? := .some "apply Nat.le_of_succ_le" }: Protocol.GoalTactic)
|
step "goal.tactic" ({ stateId := 1, tactic? := .some "apply Nat.le_of_succ_le" }: Protocol.GoalTactic)
|
||||||
({ tacticErrors? := .some #["tactic 'apply' failed, failed to unify\n ∀ {m : Nat}, Nat.succ ?n ≤ m → ?n ≤ m\nwith\n ∀ (q : Prop), x ∨ q → q ∨ x\nx : Prop\n⊢ ∀ (q : Prop), x ∨ q → q ∨ x"] }:
|
({ messages? := .some #["tactic 'apply' failed, failed to unify\n ∀ {m : Nat}, Nat.succ ?n ≤ m → ?n ≤ m\nwith\n ∀ (q : Prop), x ∨ q → q ∨ x\nx : Prop\n⊢ ∀ (q : Prop), x ∨ q → q ∨ x"] }:
|
||||||
Protocol.GoalTacticResult)
|
Protocol.GoalTacticResult),
|
||||||
|
step "goal.tactic" ({ stateId := 0, tactic? := .some "sorry" }: Protocol.GoalTactic)
|
||||||
|
({ nextStateId? := .some 3, goals? := .some #[], hasSorry := true }: Protocol.GoalTacticResult),
|
||||||
]
|
]
|
||||||
example : (1 : Nat) + (2 * 3) = 1 + (4 - 3) + (6 - 4) + 3 := by
|
example : (1 : Nat) + (2 * 3) = 1 + (4 - 3) + (6 - 4) + 3 := by
|
||||||
simp
|
simp
|
||||||
|
@ -302,6 +307,34 @@ def test_import_open : Test :=
|
||||||
({ stateId := 3, root := "_uniq.5" }: Protocol.GoalStartResult),
|
({ stateId := 3, root := "_uniq.5" }: Protocol.GoalStartResult),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
/-- Ensure there cannot be circular references -/
|
||||||
|
def test_frontend_process_circular : Test :=
|
||||||
|
let withSorry := "theorem mystery : 1 + 2 = 2 + 3 := sorry"
|
||||||
|
[
|
||||||
|
let goal1: Protocol.Goal := {
|
||||||
|
name := "_uniq.2",
|
||||||
|
target := { pp? := .some "1 + 2 = 2 + 3" },
|
||||||
|
vars := #[],
|
||||||
|
}
|
||||||
|
step "frontend.process"
|
||||||
|
({
|
||||||
|
file? := .some withSorry,
|
||||||
|
sorrys := true,
|
||||||
|
}: Protocol.FrontendProcess)
|
||||||
|
({
|
||||||
|
units := [{
|
||||||
|
boundary := (0, withSorry.utf8ByteSize),
|
||||||
|
goalStateId? := .some 0,
|
||||||
|
goals? := .some #[goal1],
|
||||||
|
goalSrcBoundaries? := .some #[(35, 40)],
|
||||||
|
messages := #["<anonymous>:1:8: warning: declaration uses 'sorry'\n"],
|
||||||
|
}],
|
||||||
|
}: Protocol.FrontendProcessResult),
|
||||||
|
step "goal.tactic" ({ stateId := 0, tactic? := .some "exact?" }: Protocol.GoalTactic)
|
||||||
|
({ messages? := .some #["`exact?` could not close the goal. Try `apply?` to see partial suggestions."] }
|
||||||
|
: Protocol.GoalTacticResult),
|
||||||
|
]
|
||||||
|
|
||||||
def runTest (env: Lean.Environment) (steps: Test): IO LSpec.TestSeq := do
|
def runTest (env: Lean.Environment) (steps: Test): IO LSpec.TestSeq := do
|
||||||
-- Setup the environment for execution
|
-- Setup the environment for execution
|
||||||
let coreContext ← createCoreContext #[]
|
let coreContext ← createCoreContext #[]
|
||||||
|
@ -322,6 +355,7 @@ def suite (env : Lean.Environment): List (String × IO LSpec.TestSeq) :=
|
||||||
("frontend.process invocation", test_frontend_process),
|
("frontend.process invocation", test_frontend_process),
|
||||||
("frontend.process sorry", test_frontend_process_sorry),
|
("frontend.process sorry", test_frontend_process_sorry),
|
||||||
("frontend.process import", test_import_open),
|
("frontend.process import", test_import_open),
|
||||||
|
("frontend.process circular", test_frontend_process_circular),
|
||||||
]
|
]
|
||||||
tests.map (fun (name, test) => (name, runTest env test))
|
tests.map (fun (name, test) => (name, runTest env test))
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,7 @@ def main (args: List String) := do
|
||||||
("Serial", Serial.suite env_default),
|
("Serial", Serial.suite env_default),
|
||||||
("Tactic/Assign", Tactic.Assign.suite env_default),
|
("Tactic/Assign", Tactic.Assign.suite env_default),
|
||||||
("Tactic/Prograde", Tactic.Prograde.suite env_default),
|
("Tactic/Prograde", Tactic.Prograde.suite env_default),
|
||||||
|
("Tactic/Special", Tactic.Special.suite env_default),
|
||||||
]
|
]
|
||||||
let tests: List (String × IO LSpec.TestSeq) := suites.foldl (λ acc (name, suite) => acc ++ (addPrefix name suite)) []
|
let tests: List (String × IO LSpec.TestSeq) := suites.foldl (λ acc (name, suite) => acc ++ (addPrefix name suite)) []
|
||||||
LSpec.lspecEachIO [()] (λ () => runTestGroup nameFilter? tests)
|
LSpec.lspecEachIO [()] (λ () => runTestGroup nameFilter? tests)
|
||||||
|
|
|
@ -79,20 +79,20 @@ def test_m_couple: TestM Unit := do
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
let state1 ← match ← state0.tacticOn (goalId := 0) (tactic := "apply Nat.le_trans") with
|
let state1 ← match ← state0.tacticOn (goalId := 0) (tactic := "apply Nat.le_trans") with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
addTest $ LSpec.check "apply Nat.le_trans" ((← state1.serializeGoals (options := ← read)).map (·.target.pp?) =
|
addTest $ LSpec.check "apply Nat.le_trans" ((← state1.serializeGoals (options := ← read)).map (·.target.pp?) =
|
||||||
#[.some "2 ≤ ?m", .some "?m ≤ 5", .some "Nat"])
|
#[.some "2 ≤ ?m", .some "?m ≤ 5", .some "Nat"])
|
||||||
addTest $ LSpec.test "(1 root)" state1.rootExpr?.isNone
|
checkTrue "(1 root)" $ ¬ state1.isSolved
|
||||||
-- Set m to 3
|
-- Set m to 3
|
||||||
let state2 ← match ← state1.tacticOn (goalId := 2) (tactic := "exact 3") with
|
let state2 ← match ← state1.tacticOn (goalId := 2) (tactic := "exact 3") with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
addTest $ LSpec.test "(1b root)" state2.rootExpr?.isNone
|
checkTrue "(1b root)" $ ¬ state2.isSolved
|
||||||
let state1b ← match state2.continue state1 with
|
let state1b ← match state2.continue state1 with
|
||||||
| .error msg => do
|
| .error msg => do
|
||||||
addTest $ assertUnreachable $ msg
|
addTest $ assertUnreachable $ msg
|
||||||
|
@ -100,7 +100,7 @@ def test_m_couple: TestM Unit := do
|
||||||
| .ok state => pure state
|
| .ok state => pure state
|
||||||
addTest $ LSpec.check "exact 3" ((← state1b.serializeGoals (options := ← read)).map (·.target.pp?) =
|
addTest $ LSpec.check "exact 3" ((← state1b.serializeGoals (options := ← read)).map (·.target.pp?) =
|
||||||
#[.some "2 ≤ 3", .some "3 ≤ 5"])
|
#[.some "2 ≤ 3", .some "3 ≤ 5"])
|
||||||
addTest $ LSpec.test "(2 root)" state1b.rootExpr?.isNone
|
checkTrue "(2 root)" $ ¬ state1b.isSolved
|
||||||
|
|
||||||
def test_m_couple_simp: TestM Unit := do
|
def test_m_couple_simp: TestM Unit := do
|
||||||
let state? ← startProof "(2: Nat) ≤ 5"
|
let state? ← startProof "(2: Nat) ≤ 5"
|
||||||
|
@ -111,7 +111,7 @@ def test_m_couple_simp: TestM Unit := do
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
let state1 ← match ← state0.tacticOn (goalId := 0) (tactic := "apply Nat.le_trans") with
|
let state1 ← match ← state0.tacticOn (goalId := 0) (tactic := "apply Nat.le_trans") with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -122,11 +122,11 @@ def test_m_couple_simp: TestM Unit := do
|
||||||
#[#["_uniq.38"], #["_uniq.38"], #[]])
|
#[#["_uniq.38"], #["_uniq.38"], #[]])
|
||||||
|
|
||||||
let state2 ← match ← state1.tacticOn (goalId := 2) (tactic := "exact 2") with
|
let state2 ← match ← state1.tacticOn (goalId := 2) (tactic := "exact 2") with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
addTest $ LSpec.test "(1b root)" state2.rootExpr?.isNone
|
checkTrue "(1b root)" $ ¬ state2.isSolved
|
||||||
let state1b ← match state2.continue state1 with
|
let state1b ← match state2.continue state1 with
|
||||||
| .error msg => do
|
| .error msg => do
|
||||||
addTest $ assertUnreachable $ msg
|
addTest $ assertUnreachable $ msg
|
||||||
|
@ -134,9 +134,9 @@ def test_m_couple_simp: TestM Unit := do
|
||||||
| .ok state => pure state
|
| .ok state => pure state
|
||||||
addTest $ LSpec.check "exact 2" ((← state1b.serializeGoals (options := ← read)).map (·.target.pp?) =
|
addTest $ LSpec.check "exact 2" ((← state1b.serializeGoals (options := ← read)).map (·.target.pp?) =
|
||||||
#[.some "2 ≤ 2", .some "2 ≤ 5"])
|
#[.some "2 ≤ 2", .some "2 ≤ 5"])
|
||||||
addTest $ LSpec.test "(2 root)" state1b.rootExpr?.isNone
|
checkTrue "(2 root)" $ ¬ state1b.isSolved
|
||||||
let state3 ← match ← state1b.tacticOn (goalId := 0) (tactic := "simp") with
|
let state3 ← match ← state1b.tacticOn (goalId := 0) (tactic := "simp") with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -146,7 +146,7 @@ def test_m_couple_simp: TestM Unit := do
|
||||||
return ()
|
return ()
|
||||||
| .ok state => pure state
|
| .ok state => pure state
|
||||||
let state5 ← match ← state4.tacticOn (goalId := 0) (tactic := "simp") with
|
let state5 ← match ← state4.tacticOn (goalId := 0) (tactic := "simp") with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -173,7 +173,7 @@ def test_proposition_generation: TestM Unit := do
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
let state1 ← match ← state0.tacticOn (goalId := 0) (tactic := "apply PSigma.mk") with
|
let state1 ← match ← state0.tacticOn (goalId := 0) (tactic := "apply PSigma.mk") with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -184,27 +184,27 @@ def test_proposition_generation: TestM Unit := do
|
||||||
])
|
])
|
||||||
if let #[goal1, goal2] := ← state1.serializeGoals (options := { (← read) with printExprAST := true }) then
|
if let #[goal1, goal2] := ← state1.serializeGoals (options := { (← read) with printExprAST := true }) then
|
||||||
addTest $ LSpec.test "(1 reference)" (goal1.target.sexp? = .some s!"(:mv {goal2.name})")
|
addTest $ LSpec.test "(1 reference)" (goal1.target.sexp? = .some s!"(:mv {goal2.name})")
|
||||||
addTest $ LSpec.test "(1 root)" state1.rootExpr?.isNone
|
checkTrue "(1 root)" $ ¬ state1.isSolved
|
||||||
|
|
||||||
let state2 ← match ← state1.tryAssign (state1.get! 0) (expr := "λ (x: Nat) => _") with
|
let state2 ← match ← state1.tryAssign (state1.get! 0) (expr := "λ (x: Nat) => _") with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
addTest $ LSpec.check ":= λ (x: Nat), _" ((← state2.serializeGoals (options := ← read)).map (·.target.pp?) =
|
addTest $ LSpec.check ":= λ (x: Nat), _" ((← state2.serializeGoals (options := ← read)).map (·.target.pp?) =
|
||||||
#[.some "?m.30 x"])
|
#[.some "?m.30 x"])
|
||||||
addTest $ LSpec.test "(2 root)" state2.rootExpr?.isNone
|
checkTrue "(2 root)" $ ¬ state2.isSolved
|
||||||
|
|
||||||
let assign := "Eq.refl x"
|
let assign := "Eq.refl x"
|
||||||
let state3 ← match ← state2.tryAssign (state2.get! 0) (expr := assign) with
|
let state3 ← match ← state2.tryAssign (state2.get! 0) (expr := assign) with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
addTest $ LSpec.check s!":= {assign}" ((← state3.serializeGoals (options := ← read)).map (·.target.pp?) =
|
addTest $ LSpec.check s!":= {assign}" ((← state3.serializeGoals (options := ← read)).map (·.target.pp?) =
|
||||||
#[])
|
#[])
|
||||||
|
|
||||||
addTest $ LSpec.test "(3 root)" state3.rootExpr?.isSome
|
checkTrue "(3 root)" state3.isSolved
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
def test_partial_continuation: TestM Unit := do
|
def test_partial_continuation: TestM Unit := do
|
||||||
|
@ -216,7 +216,7 @@ def test_partial_continuation: TestM Unit := do
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
let state1 ← match ← state0.tacticOn (goalId := 0) (tactic := "apply Nat.le_trans") with
|
let state1 ← match ← state0.tacticOn (goalId := 0) (tactic := "apply Nat.le_trans") with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -224,7 +224,7 @@ def test_partial_continuation: TestM Unit := do
|
||||||
#[.some "2 ≤ ?m", .some "?m ≤ 5", .some "Nat"])
|
#[.some "2 ≤ ?m", .some "?m ≤ 5", .some "Nat"])
|
||||||
|
|
||||||
let state2 ← match ← state1.tacticOn (goalId := 2) (tactic := "apply Nat.succ") with
|
let state2 ← match ← state1.tacticOn (goalId := 2) (tactic := "apply Nat.succ") with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -238,23 +238,23 @@ def test_partial_continuation: TestM Unit := do
|
||||||
addTest $ assertUnreachable $ msg
|
addTest $ assertUnreachable $ msg
|
||||||
return ()
|
return ()
|
||||||
| .ok state => pure state
|
| .ok state => pure state
|
||||||
addTest $ LSpec.check "(continue)" ((← state1b.serializeGoals (options := ← read)).map (·.target.pp?) =
|
addTest $ LSpec.check "(continue 1)" ((← state1b.serializeGoals (options := ← read)).map (·.target.pp?) =
|
||||||
#[.some "2 ≤ Nat.succ ?m", .some "Nat.succ ?m ≤ 5", .some "Nat"])
|
#[.some "2 ≤ Nat.succ ?m", .some "Nat.succ ?m ≤ 5", .some "Nat"])
|
||||||
addTest $ LSpec.test "(2 root)" state1b.rootExpr?.isNone
|
checkTrue "(2 root)" state1b.rootExpr?.get!.hasExprMVar
|
||||||
|
|
||||||
-- Roundtrip
|
-- Roundtrip
|
||||||
--let coupled_goals := coupled_goals.map (λ g =>
|
--let coupled_goals := coupled_goals.map (λ g =>
|
||||||
-- { name := str_to_name $ serializeName g.name (sanitize := false)})
|
-- { name := str_to_name $ serializeName g.name (sanitize := false)})
|
||||||
let coupled_goals := coupled_goals.map (λ g => serializeName g.name (sanitize := false))
|
let coupled_goals := coupled_goals.map (·.name.toString)
|
||||||
let coupled_goals := coupled_goals.map (λ g => { name := g.toName })
|
let coupled_goals := coupled_goals.map ({ name := ·.toName })
|
||||||
let state1b ← match state2.resume (goals := coupled_goals) with
|
let state1b ← match state2.resume (goals := coupled_goals) with
|
||||||
| .error msg => do
|
| .error msg => do
|
||||||
addTest $ assertUnreachable $ msg
|
addTest $ assertUnreachable $ msg
|
||||||
return ()
|
return ()
|
||||||
| .ok state => pure state
|
| .ok state => pure state
|
||||||
addTest $ LSpec.check "(continue)" ((← state1b.serializeGoals (options := ← read)).map (·.target.pp?) =
|
addTest $ LSpec.check "(continue 2)" ((← state1b.serializeGoals (options := ← read)).map (·.target.pp?) =
|
||||||
#[.some "2 ≤ Nat.succ ?m", .some "Nat.succ ?m ≤ 5", .some "Nat"])
|
#[.some "2 ≤ Nat.succ ?m", .some "Nat.succ ?m ≤ 5", .some "Nat"])
|
||||||
addTest $ LSpec.test "(2 root)" state1b.rootExpr?.isNone
|
checkTrue "(2 root)" state1b.rootExpr?.get!.hasExprMVar
|
||||||
|
|
||||||
-- Continuation should fail if the state does not exist:
|
-- Continuation should fail if the state does not exist:
|
||||||
match state0.resume coupled_goals with
|
match state0.resume coupled_goals with
|
||||||
|
|
|
@ -89,7 +89,7 @@ def test_identity: TestM Unit := do
|
||||||
|
|
||||||
let tactic := "intro p h"
|
let tactic := "intro p h"
|
||||||
let state1 ← match ← state0.tacticOn 0 tactic with
|
let state1 ← match ← state0.tacticOn 0 tactic with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -116,7 +116,7 @@ def test_nat_add_comm (manual: Bool): TestM Unit := do
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
let state1 ← match ← state0.tacticOn 0 "intro n m" with
|
let state1 ← match ← state0.tacticOn 0 "intro n m" with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -130,7 +130,7 @@ def test_nat_add_comm (manual: Bool): TestM Unit := do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
|
|
||||||
let state2 ← match ← state1.tacticOn 0 "rw [Nat.add_comm]" with
|
let state2 ← match ← state1.tacticOn 0 "rw [Nat.add_comm]" with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -148,14 +148,14 @@ def test_delta_variable: TestM Unit := do
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
let state1 ← match ← state0.tacticOn (goalId := 0) (tactic := "intro n") with
|
let state1 ← match ← state0.tacticOn (goalId := 0) (tactic := "intro n") with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
addTest $ LSpec.check "intro n" ((← state1.serializeGoals (parent := state0) options).map (·.devolatilize) =
|
addTest $ LSpec.check "intro n" ((← state1.serializeGoals (parent := state0) options).map (·.devolatilize) =
|
||||||
#[buildGoalSelective [("n", .some "Nat")] "∀ (b : Nat), n + b = b + n"])
|
#[buildGoalSelective [("n", .some "Nat")] "∀ (b : Nat), n + b = b + n"])
|
||||||
let state2 ← match ← state1.tacticOn (goalId := 0) (tactic := "intro m") with
|
let state2 ← match ← state1.tacticOn (goalId := 0) (tactic := "intro m") with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -187,27 +187,27 @@ def test_arith: TestM Unit := do
|
||||||
|
|
||||||
let tactic := "intros"
|
let tactic := "intros"
|
||||||
let state1 ← match ← state0.tacticOn (goalId := 0) (tactic := tactic) with
|
let state1 ← match ← state0.tacticOn (goalId := 0) (tactic := tactic) with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
addTest $ LSpec.check tactic (state1.goals.length = 1)
|
addTest $ LSpec.check tactic (state1.goals.length = 1)
|
||||||
addTest $ LSpec.test "(1 root)" state1.rootExpr?.isNone
|
checkTrue "(1 root)" state1.rootExpr?.get!.hasExprMVar
|
||||||
let state2 ← match ← state1.tacticOn (goalId := 0) (tactic := "simp [Nat.add_assoc, Nat.add_comm, Nat.add_left_comm, Nat.mul_comm, Nat.mul_assoc, Nat.mul_left_comm] at *") with
|
let state2 ← match ← state1.tacticOn (goalId := 0) (tactic := "simp [Nat.add_assoc, Nat.add_comm, Nat.add_left_comm, Nat.mul_comm, Nat.mul_assoc, Nat.mul_left_comm] at *") with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
addTest $ LSpec.check "simp ..." (state2.goals.length = 1)
|
addTest $ LSpec.check "simp ..." (state2.goals.length = 1)
|
||||||
addTest $ LSpec.check "(2 root)" state2.rootExpr?.isNone
|
checkTrue "(2 root)" state2.rootExpr?.get!.hasExprMVar
|
||||||
let tactic := "assumption"
|
let tactic := "assumption"
|
||||||
let state3 ← match ← state2.tacticOn (goalId := 0) (tactic := tactic) with
|
let state3 ← match ← state2.tacticOn (goalId := 0) (tactic := tactic) with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
addTest $ LSpec.test tactic state3.goals.isEmpty
|
addTest $ LSpec.test tactic state3.goals.isEmpty
|
||||||
addTest $ LSpec.check "(3 root)" state3.rootExpr?.isSome
|
checkTrue "(3 root)" $ ¬ state3.rootExpr?.get!.hasExprMVar
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
-- Two ways to write the same theorem
|
-- Two ways to write the same theorem
|
||||||
|
@ -237,7 +237,7 @@ def test_or_comm: TestM Unit := do
|
||||||
|
|
||||||
let tactic := "intro p q h"
|
let tactic := "intro p q h"
|
||||||
let state1 ← match ← state0.tacticOn (goalId := 0) (tactic := tactic) with
|
let state1 ← match ← state0.tacticOn (goalId := 0) (tactic := tactic) with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -257,15 +257,15 @@ def test_or_comm: TestM Unit := do
|
||||||
{ name := fvH, userName := "h", type? := .some { pp? := .some "p ∨ q" } }
|
{ name := fvH, userName := "h", type? := .some { pp? := .some "p ∨ q" } }
|
||||||
]
|
]
|
||||||
}])
|
}])
|
||||||
addTest $ LSpec.check "(1 parent)" state1.parentExpr?.isSome
|
checkTrue "(1 parent)" state1.parentExpr?.isSome
|
||||||
addTest $ LSpec.check "(1 root)" state1.rootExpr?.isNone
|
checkTrue "(1 root)" $ ¬ state1.isSolved
|
||||||
|
|
||||||
let state1parent ← state1.withParentContext do
|
let state1parent ← state1.withParentContext do
|
||||||
serializeExpressionSexp (← instantiateAll state1.parentExpr?.get!)
|
serializeExpressionSexp (← instantiateAll state1.parentExpr?.get!)
|
||||||
addTest $ LSpec.test "(1 parent)" (state1parent == s!"(:lambda p (:sort 0) (:lambda q (:sort 0) (:lambda h ((:c Or) 1 0) (:subst (:mv {state1g0}) 2 1 0))))")
|
addTest $ LSpec.test "(1 parent)" (state1parent == s!"(:lambda p (:sort 0) (:lambda q (:sort 0) (:lambda h ((:c Or) 1 0) (:subst (:mv {state1g0}) 2 1 0))))")
|
||||||
let tactic := "cases h"
|
let tactic := "cases h"
|
||||||
let state2 ← match ← state1.tacticOn (goalId := 0) (tactic := tactic) with
|
let state2 ← match ← state1.tacticOn (goalId := 0) (tactic := tactic) with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -276,8 +276,8 @@ def test_or_comm: TestM Unit := do
|
||||||
let (caseL, caseR) := (state2g0.name.toString, state2g1.name.toString)
|
let (caseL, caseR) := (state2g0.name.toString, state2g1.name.toString)
|
||||||
addTest $ LSpec.check tactic ((← state2.serializeGoals (options := ← read)).map (·.name) =
|
addTest $ LSpec.check tactic ((← state2.serializeGoals (options := ← read)).map (·.name) =
|
||||||
#[caseL, caseR])
|
#[caseL, caseR])
|
||||||
addTest $ LSpec.check "(2 parent exists)" state2.parentExpr?.isSome
|
checkTrue "(2 parent exists)" state2.parentExpr?.isSome
|
||||||
addTest $ LSpec.check "(2 root)" state2.rootExpr?.isNone
|
checkTrue "(2 root)" $ ¬ state2.isSolved
|
||||||
|
|
||||||
let state2parent ← state2.withParentContext do
|
let state2parent ← state2.withParentContext do
|
||||||
serializeExpressionSexp (← instantiateAll state2.parentExpr?.get!)
|
serializeExpressionSexp (← instantiateAll state2.parentExpr?.get!)
|
||||||
|
@ -291,7 +291,7 @@ def test_or_comm: TestM Unit := do
|
||||||
s!"((:c Or.casesOn) (:fv {fvP}) (:fv {fvQ}) {motive} (:fv {fvH}) {caseL} {caseR} {conduit})")
|
s!"((:c Or.casesOn) (:fv {fvP}) (:fv {fvQ}) {motive} (:fv {fvH}) {caseL} {caseR} {conduit})")
|
||||||
|
|
||||||
let state3_1 ← match ← state2.tacticOn (goalId := 0) (tactic := "apply Or.inr") with
|
let state3_1 ← match ← state2.tacticOn (goalId := 0) (tactic := "apply Or.inr") with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -301,27 +301,27 @@ def test_or_comm: TestM Unit := do
|
||||||
addTest $ LSpec.test "(3_1 parent)" (state3_1parent == s!"((:c Or.inr) (:fv {fvQ}) (:fv {fvP}) (:mv {state3_1goal0}))")
|
addTest $ LSpec.test "(3_1 parent)" (state3_1parent == s!"((:c Or.inr) (:fv {fvQ}) (:fv {fvP}) (:mv {state3_1goal0}))")
|
||||||
addTest $ LSpec.check "· apply Or.inr" (state3_1.goals.length = 1)
|
addTest $ LSpec.check "· apply Or.inr" (state3_1.goals.length = 1)
|
||||||
let state4_1 ← match ← state3_1.tacticOn (goalId := 0) (tactic := "assumption") with
|
let state4_1 ← match ← state3_1.tacticOn (goalId := 0) (tactic := "assumption") with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
addTest $ LSpec.check " assumption" state4_1.goals.isEmpty
|
addTest $ LSpec.check " assumption" state4_1.goals.isEmpty
|
||||||
let state4_1parent ← instantiateAll state4_1.parentExpr?.get!
|
let state4_1parent ← instantiateAll state4_1.parentExpr?.get!
|
||||||
addTest $ LSpec.test "(4_1 parent)" state4_1parent.isFVar
|
addTest $ LSpec.test "(4_1 parent)" state4_1parent.isFVar
|
||||||
addTest $ LSpec.check "(4_1 root)" state4_1.rootExpr?.isNone
|
checkTrue "(4_1 root)" $ ¬ state4_1.isSolved
|
||||||
let state3_2 ← match ← state2.tacticOn (goalId := 1) (tactic := "apply Or.inl") with
|
let state3_2 ← match ← state2.tacticOn (goalId := 1) (tactic := "apply Or.inl") with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
addTest $ LSpec.check "· apply Or.inl" (state3_2.goals.length = 1)
|
addTest $ LSpec.check "· apply Or.inl" (state3_2.goals.length = 1)
|
||||||
let state4_2 ← match ← state3_2.tacticOn (goalId := 0) (tactic := "assumption") with
|
let state4_2 ← match ← state3_2.tacticOn (goalId := 0) (tactic := "assumption") with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
addTest $ LSpec.check " assumption" state4_2.goals.isEmpty
|
addTest $ LSpec.check " assumption" state4_2.goals.isEmpty
|
||||||
addTest $ LSpec.check "(4_2 root)" state4_2.rootExpr?.isNone
|
checkTrue "(4_2 root)" $ ¬ state4_2.isSolved
|
||||||
-- Ensure the proof can continue from `state4_2`.
|
-- Ensure the proof can continue from `state4_2`.
|
||||||
let state2b ← match state4_2.continue state2 with
|
let state2b ← match state4_2.continue state2 with
|
||||||
| .error msg => do
|
| .error msg => do
|
||||||
|
@ -330,18 +330,18 @@ def test_or_comm: TestM Unit := do
|
||||||
| .ok state => pure state
|
| .ok state => pure state
|
||||||
addTest $ LSpec.test "(resume)" (state2b.goals == [state2.goals[0]!])
|
addTest $ LSpec.test "(resume)" (state2b.goals == [state2.goals[0]!])
|
||||||
let state3_1 ← match ← state2b.tacticOn (goalId := 0) (tactic := "apply Or.inr") with
|
let state3_1 ← match ← state2b.tacticOn (goalId := 0) (tactic := "apply Or.inr") with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
addTest $ LSpec.check "· apply Or.inr" (state3_1.goals.length = 1)
|
addTest $ LSpec.check "· apply Or.inr" (state3_1.goals.length = 1)
|
||||||
let state4_1 ← match ← state3_1.tacticOn (goalId := 0) (tactic := "assumption") with
|
let state4_1 ← match ← state3_1.tacticOn (goalId := 0) (tactic := "assumption") with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
addTest $ LSpec.check " assumption" state4_1.goals.isEmpty
|
addTest $ LSpec.check " assumption" state4_1.goals.isEmpty
|
||||||
addTest $ LSpec.check "(4_1 root)" state4_1.rootExpr?.isSome
|
checkTrue "(4_1 root)" $ ¬ state4_1.rootExpr?.get!.hasExprMVar
|
||||||
|
|
||||||
return ()
|
return ()
|
||||||
where
|
where
|
||||||
|
@ -375,7 +375,7 @@ def test_conv: TestM Unit := do
|
||||||
|
|
||||||
let tactic := "intro a b c1 c2 h"
|
let tactic := "intro a b c1 c2 h"
|
||||||
let state1 ← match ← state0.tacticOn (goalId := 0) (tactic := tactic) with
|
let state1 ← match ← state0.tacticOn (goalId := 0) (tactic := tactic) with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -383,7 +383,7 @@ def test_conv: TestM Unit := do
|
||||||
#[interiorGoal [] "a + b + c1 = b + a + c2"])
|
#[interiorGoal [] "a + b + c1 = b + a + c2"])
|
||||||
|
|
||||||
let state2 ← match ← state1.conv (state1.get! 0) with
|
let state2 ← match ← state1.conv (state1.get! 0) with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -392,7 +392,7 @@ def test_conv: TestM Unit := do
|
||||||
|
|
||||||
let convTactic := "rhs"
|
let convTactic := "rhs"
|
||||||
let state3R ← match ← state2.tacticOn (goalId := 0) convTactic with
|
let state3R ← match ← state2.tacticOn (goalId := 0) convTactic with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -401,7 +401,7 @@ def test_conv: TestM Unit := do
|
||||||
|
|
||||||
let convTactic := "lhs"
|
let convTactic := "lhs"
|
||||||
let state3L ← match ← state2.tacticOn (goalId := 0) convTactic with
|
let state3L ← match ← state2.tacticOn (goalId := 0) convTactic with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -410,7 +410,7 @@ def test_conv: TestM Unit := do
|
||||||
|
|
||||||
let convTactic := "congr"
|
let convTactic := "congr"
|
||||||
let state4 ← match ← state3L.tacticOn (goalId := 0) convTactic with
|
let state4 ← match ← state3L.tacticOn (goalId := 0) convTactic with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -422,7 +422,7 @@ def test_conv: TestM Unit := do
|
||||||
|
|
||||||
let convTactic := "rw [Nat.add_comm]"
|
let convTactic := "rw [Nat.add_comm]"
|
||||||
let state5_1 ← match ← state4.tacticOn (goalId := 0) convTactic with
|
let state5_1 ← match ← state4.tacticOn (goalId := 0) convTactic with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -431,7 +431,7 @@ def test_conv: TestM Unit := do
|
||||||
|
|
||||||
let convTactic := "rfl"
|
let convTactic := "rfl"
|
||||||
let state6_1 ← match ← state5_1.tacticOn (goalId := 0) convTactic with
|
let state6_1 ← match ← state5_1.tacticOn (goalId := 0) convTactic with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -446,7 +446,7 @@ def test_conv: TestM Unit := do
|
||||||
|
|
||||||
let convTactic := "rfl"
|
let convTactic := "rfl"
|
||||||
let state6 ← match ← state4_1.tacticOn (goalId := 0) convTactic with
|
let state6 ← match ← state4_1.tacticOn (goalId := 0) convTactic with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -454,14 +454,14 @@ def test_conv: TestM Unit := do
|
||||||
#[])
|
#[])
|
||||||
|
|
||||||
let state1_1 ← match ← state6.convExit with
|
let state1_1 ← match ← state6.convExit with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
let tactic := "exact h"
|
let tactic := "exact h"
|
||||||
let stateF ← match ← state1_1.tacticOn (goalId := 0) (tactic := tactic) with
|
let stateF ← match ← state1_1.tacticOn (goalId := 0) (tactic := tactic) with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -488,7 +488,7 @@ def test_calc: TestM Unit := do
|
||||||
return ()
|
return ()
|
||||||
let tactic := "intro a b c d h1 h2"
|
let tactic := "intro a b c d h1 h2"
|
||||||
let state1 ← match ← state0.tacticOn (goalId := 0) (tactic := tactic) with
|
let state1 ← match ← state0.tacticOn (goalId := 0) (tactic := tactic) with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -496,7 +496,7 @@ def test_calc: TestM Unit := do
|
||||||
#[interiorGoal [] "a + b = c + d"])
|
#[interiorGoal [] "a + b = c + d"])
|
||||||
let pred := "a + b = b + c"
|
let pred := "a + b = b + c"
|
||||||
let state2 ← match ← state1.tryCalc (state1.get! 0) (pred := pred) with
|
let state2 ← match ← state1.tryCalc (state1.get! 0) (pred := pred) with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -510,7 +510,7 @@ def test_calc: TestM Unit := do
|
||||||
|
|
||||||
let tactic := "apply h1"
|
let tactic := "apply h1"
|
||||||
let state2m ← match ← state2.tacticOn (goalId := 0) (tactic := tactic) with
|
let state2m ← match ← state2.tacticOn (goalId := 0) (tactic := tactic) with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -521,7 +521,7 @@ def test_calc: TestM Unit := do
|
||||||
return ()
|
return ()
|
||||||
let pred := "_ = c + d"
|
let pred := "_ = c + d"
|
||||||
let state4 ← match ← state3.tryCalc (state3.get! 0) (pred := pred) with
|
let state4 ← match ← state3.tryCalc (state3.get! 0) (pred := pred) with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -532,7 +532,7 @@ def test_calc: TestM Unit := do
|
||||||
addTest $ LSpec.test "(4.0 prev rhs)" (state4.calcPrevRhsOf? (state4.get! 0) |>.isNone)
|
addTest $ LSpec.test "(4.0 prev rhs)" (state4.calcPrevRhsOf? (state4.get! 0) |>.isNone)
|
||||||
let tactic := "apply h2"
|
let tactic := "apply h2"
|
||||||
let state4m ← match ← state4.tacticOn (goalId := 0) (tactic := tactic) with
|
let state4m ← match ← state4.tacticOn (goalId := 0) (tactic := tactic) with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -553,7 +553,7 @@ def test_tactic_failure_unresolved_goals : TestM Unit := do
|
||||||
|
|
||||||
let tactic := "intro p"
|
let tactic := "intro p"
|
||||||
let state1 ← match ← state0.tacticOn 0 tactic with
|
let state1 ← match ← state0.tacticOn 0 tactic with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -572,7 +572,7 @@ def test_tactic_failure_synthesize_placeholder : TestM Unit := do
|
||||||
|
|
||||||
let tactic := "intro p q r h"
|
let tactic := "intro p q r h"
|
||||||
let state1 ← match ← state0.tacticOn 0 tactic with
|
let state1 ← match ← state0.tacticOn 0 tactic with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -604,7 +604,7 @@ def test_deconstruct : TestM Unit := do
|
||||||
|
|
||||||
let tactic := "intro p q ⟨hp, hq⟩"
|
let tactic := "intro p q ⟨hp, hq⟩"
|
||||||
let state1 ← match ← state0.tacticOn 0 tactic with
|
let state1 ← match ← state0.tacticOn 0 tactic with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
fail other.toString
|
fail other.toString
|
||||||
return ()
|
return ()
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
import Test.Tactic.Assign
|
import Test.Tactic.Assign
|
||||||
import Test.Tactic.Prograde
|
import Test.Tactic.Prograde
|
||||||
|
import Test.Tactic.Special
|
||||||
|
|
|
@ -56,7 +56,7 @@ def test_define_proof : TestT Elab.TermElabM Unit := do
|
||||||
let state0 ← GoalState.create rootExpr
|
let state0 ← GoalState.create rootExpr
|
||||||
let tactic := "intro p q h"
|
let tactic := "intro p q h"
|
||||||
let state1 ← match ← state0.tacticOn (goalId := 0) (tactic := tactic) with
|
let state1 ← match ← state0.tacticOn (goalId := 0) (tactic := tactic) with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -65,7 +65,7 @@ def test_define_proof : TestT Elab.TermElabM Unit := do
|
||||||
|
|
||||||
let expr := "Or.inl (Or.inl h)"
|
let expr := "Or.inl (Or.inl h)"
|
||||||
let state2 ← match ← state1.tryAssign (state1.get! 0) (expr := expr) with
|
let state2 ← match ← state1.tryAssign (state1.get! 0) (expr := expr) with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -75,7 +75,7 @@ def test_define_proof : TestT Elab.TermElabM Unit := do
|
||||||
let evalBind := "y"
|
let evalBind := "y"
|
||||||
let evalExpr := "Or.inl h"
|
let evalExpr := "Or.inl h"
|
||||||
let state2 ← match ← state1.tryDefine (state1.get! 0) (binderName := evalBind) (expr := evalExpr) with
|
let state2 ← match ← state1.tryDefine (state1.get! 0) (binderName := evalBind) (expr := evalExpr) with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -95,7 +95,7 @@ def test_define_proof : TestT Elab.TermElabM Unit := do
|
||||||
|
|
||||||
let expr := "Or.inl y"
|
let expr := "Or.inl y"
|
||||||
let state3 ← match ← state2.tryAssign (state2.get! 0) (expr := expr) with
|
let state3 ← match ← state2.tryAssign (state2.get! 0) (expr := expr) with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -112,22 +112,22 @@ def fun_define_root_expr: ∀ (p: Prop), PProd (Nat → p) Unit → p := by
|
||||||
def test_define_root_expr : TestT Elab.TermElabM Unit := do
|
def test_define_root_expr : TestT Elab.TermElabM Unit := do
|
||||||
--let rootExpr ← parseSentence "Nat"
|
--let rootExpr ← parseSentence "Nat"
|
||||||
--let state0 ← GoalState.create rootExpr
|
--let state0 ← GoalState.create rootExpr
|
||||||
--let .success state1 ← state0.tacticOn (goalId := 0) "exact 5" | addTest $ assertUnreachable "exact 5"
|
--let .success state1 _ ← state0.tacticOn (goalId := 0) "exact 5" | addTest $ assertUnreachable "exact 5"
|
||||||
--let .some rootExpr := state1.rootExpr? | addTest $ assertUnreachable "Root expr"
|
--let .some rootExpr := state1.rootExpr? | addTest $ assertUnreachable "Root expr"
|
||||||
--addTest $ LSpec.check "root" ((toString $ ← Meta.ppExpr rootExpr) = "5")
|
--addTest $ LSpec.check "root" ((toString $ ← Meta.ppExpr rootExpr) = "5")
|
||||||
let rootExpr ← parseSentence "∀ (p: Prop), PProd (Nat → p) Unit → p"
|
let rootExpr ← parseSentence "∀ (p: Prop), PProd (Nat → p) Unit → p"
|
||||||
let state0 ← GoalState.create rootExpr
|
let state0 ← GoalState.create rootExpr
|
||||||
let tactic := "intro p x"
|
let tactic := "intro p x"
|
||||||
let .success state1 ← state0.tacticOn (goalId := 0) tactic | addTest $ assertUnreachable tactic
|
let .success state1 _ ← state0.tacticOn (goalId := 0) tactic | addTest $ assertUnreachable tactic
|
||||||
let binderName := `binder
|
let binderName := `binder
|
||||||
let value := "x.fst"
|
let value := "x.fst"
|
||||||
let expr ← state1.goals[0]!.withContext $ strToTermSyntax value
|
let expr ← state1.goals[0]!.withContext $ strToTermSyntax value
|
||||||
let tacticM := Tactic.evalDefine binderName expr
|
let tacticM := Tactic.evalDefine binderName expr
|
||||||
let .success state2 ← state1.tryTacticM (state1.get! 0) tacticM | addTest $ assertUnreachable s!"define {binderName} := {value}"
|
let .success state2 _ ← state1.tryTacticM (state1.get! 0) tacticM | addTest $ assertUnreachable s!"define {binderName} := {value}"
|
||||||
let tactic := s!"apply {binderName}"
|
let tactic := s!"apply {binderName}"
|
||||||
let .success state3 ← state2.tacticOn (goalId := 0) tactic | addTest $ assertUnreachable tactic
|
let .success state3 _ ← state2.tacticOn (goalId := 0) tactic | addTest $ assertUnreachable tactic
|
||||||
let tactic := s!"exact 5"
|
let tactic := s!"exact 5"
|
||||||
let .success state4 ← state3.tacticOn (goalId := 0) tactic | addTest $ assertUnreachable tactic
|
let .success state4 _ ← state3.tacticOn (goalId := 0) tactic | addTest $ assertUnreachable tactic
|
||||||
let .some rootExpr := state4.rootExpr? | addTest $ assertUnreachable "Root expr"
|
let .some rootExpr := state4.rootExpr? | addTest $ assertUnreachable "Root expr"
|
||||||
addTest $ LSpec.check "root" ((toString $ ← Meta.ppExpr rootExpr) = "fun p x =>\n let binder := x.fst;\n binder 5")
|
addTest $ LSpec.check "root" ((toString $ ← Meta.ppExpr rootExpr) = "fun p x =>\n let binder := x.fst;\n binder 5")
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ def test_have_proof : TestT Elab.TermElabM Unit := do
|
||||||
let state0 ← GoalState.create rootExpr
|
let state0 ← GoalState.create rootExpr
|
||||||
let tactic := "intro p q h"
|
let tactic := "intro p q h"
|
||||||
let state1 ← match ← state0.tacticOn (goalId := 0) (tactic := tactic) with
|
let state1 ← match ← state0.tacticOn (goalId := 0) (tactic := tactic) with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -149,7 +149,7 @@ def test_have_proof : TestT Elab.TermElabM Unit := do
|
||||||
|
|
||||||
let expr := "Or.inl (Or.inl h)"
|
let expr := "Or.inl (Or.inl h)"
|
||||||
let state2 ← match ← state1.tryAssign (state1.get! 0) (expr := expr) with
|
let state2 ← match ← state1.tryAssign (state1.get! 0) (expr := expr) with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -159,7 +159,7 @@ def test_have_proof : TestT Elab.TermElabM Unit := do
|
||||||
let haveBind := "y"
|
let haveBind := "y"
|
||||||
let haveType := "p ∨ q"
|
let haveType := "p ∨ q"
|
||||||
let state2 ← match ← state1.tryHave (state1.get! 0) (binderName := haveBind) (type := haveType) with
|
let state2 ← match ← state1.tryHave (state1.get! 0) (binderName := haveBind) (type := haveType) with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -171,7 +171,7 @@ def test_have_proof : TestT Elab.TermElabM Unit := do
|
||||||
|
|
||||||
let expr := "Or.inl h"
|
let expr := "Or.inl h"
|
||||||
let state3 ← match ← state2.tryAssign (state2.get! 0) (expr := expr) with
|
let state3 ← match ← state2.tryAssign (state2.get! 0) (expr := expr) with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -185,7 +185,7 @@ def test_have_proof : TestT Elab.TermElabM Unit := do
|
||||||
return ()
|
return ()
|
||||||
let expr := "Or.inl y"
|
let expr := "Or.inl y"
|
||||||
let state4 ← match ← state2b.tryAssign (state2b.get! 0) (expr := expr) with
|
let state4 ← match ← state2b.tryAssign (state2b.get! 0) (expr := expr) with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -200,7 +200,7 @@ def test_let (specialized: Bool): TestT Elab.TermElabM Unit := do
|
||||||
let state0 ← GoalState.create rootExpr
|
let state0 ← GoalState.create rootExpr
|
||||||
let tactic := "intro a p h"
|
let tactic := "intro a p h"
|
||||||
let state1 ← match ← state0.tacticOn (goalId := 0) (tactic := tactic) with
|
let state1 ← match ← state0.tacticOn (goalId := 0) (tactic := tactic) with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -216,7 +216,7 @@ def test_let (specialized: Bool): TestT Elab.TermElabM Unit := do
|
||||||
| true => state1.tryLet (state1.get! 0) (binderName := "b") (type := letType)
|
| true => state1.tryLet (state1.get! 0) (binderName := "b") (type := letType)
|
||||||
| false => state1.tryAssign (state1.get! 0) (expr := expr)
|
| false => state1.tryAssign (state1.get! 0) (expr := expr)
|
||||||
let state2 ← match result2 with
|
let state2 ← match result2 with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -241,7 +241,7 @@ def test_let (specialized: Bool): TestT Elab.TermElabM Unit := do
|
||||||
|
|
||||||
let tactic := "exact 1"
|
let tactic := "exact 1"
|
||||||
let state3 ← match ← state2.tacticOn (goalId := 0) (tactic := tactic) with
|
let state3 ← match ← state2.tacticOn (goalId := 0) (tactic := tactic) with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
@ -274,7 +274,7 @@ def test_let (specialized: Bool): TestT Elab.TermElabM Unit := do
|
||||||
|
|
||||||
let tactic := "exact Or.inl (Or.inl h)"
|
let tactic := "exact Or.inl (Or.inl h)"
|
||||||
let state4 ← match ← state3r.tacticOn (goalId := 0) (tactic := tactic) with
|
let state4 ← match ← state3r.tacticOn (goalId := 0) (tactic := tactic) with
|
||||||
| .success state => pure state
|
| .success state _ => pure state
|
||||||
| other => do
|
| other => do
|
||||||
addTest $ assertUnreachable $ other.toString
|
addTest $ assertUnreachable $ other.toString
|
||||||
return ()
|
return ()
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import LSpec
|
||||||
|
import Lean
|
||||||
|
import Test.Common
|
||||||
|
|
||||||
|
open Lean
|
||||||
|
open Pantograph
|
||||||
|
|
||||||
|
namespace Pantograph.Test.Tactic.Special
|
||||||
|
|
||||||
|
def test_exact_q : TestT Elab.TermElabM Unit := do
|
||||||
|
let rootExpr ← parseSentence "1 + 2 = 2 + 3"
|
||||||
|
let state0 ← GoalState.create rootExpr
|
||||||
|
let tactic := "exact?"
|
||||||
|
let state1? ← state0.tacticOn (goalId := 0) (tactic := tactic)
|
||||||
|
let .failure messages := state1? | fail "Must fail"
|
||||||
|
checkEq "messages" messages #["`exact?` could not close the goal. Try `apply?` to see partial suggestions."]
|
||||||
|
|
||||||
|
def suite (env: Environment): List (String × IO LSpec.TestSeq) :=
|
||||||
|
[
|
||||||
|
("exact?", test_exact_q),
|
||||||
|
] |>.map (λ (name, t) => (name, runTestTermElabM env t))
|
||||||
|
|
||||||
|
end Pantograph.Test.Tactic.Special
|
|
@ -47,11 +47,16 @@ include:
|
||||||
|
|
||||||
- If a tactic loses track of metavariables, it will not be caught until the end
|
- If a tactic loses track of metavariables, it will not be caught until the end
|
||||||
of the proof search. This is a bug in the tactic itself.
|
of the proof search. This is a bug in the tactic itself.
|
||||||
- Timeouts for executing tactics is not available. Maybe this will change in the
|
- Although a timeout feature exists in Pantograph, it relies on the coöperative
|
||||||
future.
|
multitasking from the tactic implementation. There is nothing preventing a
|
||||||
|
buggy tactic from stalling Lean if it does not check for cancellation often.
|
||||||
|
- For the same reason as above, there is no graceful way to stop a tactic which
|
||||||
|
leaks infinite memory. Users who wish to have this behaviour should run
|
||||||
|
Pantograph in a controlled environment with limited allocations. e.g.
|
||||||
|
Linux control groups.
|
||||||
- Interceptions of parsing errors generally cannot be turned into goals (e.g.
|
- Interceptions of parsing errors generally cannot be turned into goals (e.g.
|
||||||
`def mystery : Nat := :=`) due to Lean's parsing system.
|
`def mystery : Nat := :=`) due to Lean's parsing system. This question is also
|
||||||
|
not well-defined.
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,8 @@ See `Pantograph/Protocol.lean` for a description of the parameters and return va
|
||||||
- `{ "conv": <bool> }`: Enter or exit conversion tactic mode. In the case of
|
- `{ "conv": <bool> }`: Enter or exit conversion tactic mode. In the case of
|
||||||
exit, the goal id is ignored.
|
exit, the goal id is ignored.
|
||||||
- `{ "draft": <expr> }`: Draft an expression with `sorry`s, turning them into goals. Coupling is not allowed.
|
- `{ "draft": <expr> }`: Draft an expression with `sorry`s, turning them into goals. Coupling is not allowed.
|
||||||
|
If the `goals` field does not exist, the tactic execution has failed. Read
|
||||||
|
`messages` to find the reason.
|
||||||
* `goal.continue {"stateId": <id>, ["branch": <id>], ["goals": <names>]}`:
|
* `goal.continue {"stateId": <id>, ["branch": <id>], ["goals": <names>]}`:
|
||||||
Execute continuation/resumption
|
Execute continuation/resumption
|
||||||
- `{ "branch": <id> }`: Continue on branch state. The current state must have no goals.
|
- `{ "branch": <id> }`: Continue on branch state. The current state must have no goals.
|
||||||
|
|
Loading…
Reference in New Issue