|
/- |
|
Copyright (c) 2023 Lean FRO, LLC. All rights reserved. |
|
Released under Apache 2.0 license as described in the file LICENSE. |
|
Authors: Scott Morrison |
|
-/ |
|
import Lean.Replay |
|
import Lean.Elab.Command |
|
import REPL.Util.Pickle |
|
|
|
open Lean Elab |
|
|
|
namespace Lean.Elab.Command |
|
|
|
@[inline] def CommandElabM.run (x : CommandElabM α) (ctx : Context) (s : State) : EIO Exception (α × State) := |
|
(x ctx).run s |
|
|
|
@[inline] def CommandElabM.run' (x : CommandElabM α) (ctx : Context) (s : State) : EIO Exception α := |
|
Prod.fst <$> x.run ctx s |
|
|
|
@[inline] def CommandElabM.toIO (x : CommandElabM α) (ctx : Context) (s : State) : IO (α × State) := do |
|
match (← (x.run ctx s).toIO') with |
|
| Except.error (Exception.error _ msg) => throw <| IO.userError (← msg.toString) |
|
| Except.error (Exception.internal id _) => throw <| IO.userError <| "internal exception #" ++ toString id.idx |
|
| Except.ok a => return a |
|
|
|
end Lean.Elab.Command |
|
|
|
namespace REPL |
|
|
|
/ |
|
Bundled structure for the `State` and `Context` objects |
|
for the `CommandElabM` monad. |
|
-/ |
|
structure CommandSnapshot where |
|
cmdState : Command.State |
|
cmdContext : Command.Context |
|
|
|
namespace CommandSnapshot |
|
|
|
open Lean.Elab.Command |
|
|
|
/ |
|
structure CompactableCommandSnapshot where |
|
|
|
scopes : List Scope := [{ header := "" }] |
|
nextMacroScope : Nat := firstFrontendMacroScope + 1 |
|
maxRecDepth : Nat |
|
nextInstIdx : Nat := 1 |
|
ngen : NameGenerator := {} |
|
|
|
|
|
|
|
|
|
open System (FilePath) |
|
|
|
/ |
|
Run a `CommandElabM` monadic function in the current `ProofSnapshot`, |
|
updating the `Command.State`. |
|
-/ |
|
def runCommandElabM (p : CommandSnapshot) (t : CommandElabM α) : IO (α × CommandSnapshot) := do |
|
let (a, cmdState) ← (CommandElabM.toIO · p.cmdContext p.cmdState) do t |
|
return (a, { p with cmdState }) |
|
|
|
|
|
/ |
|
Pickle a `CommandSnapshot`, discarding closures and non-essential caches. |
|
|
|
When pickling the `Environment`, we do so relative to its imports. |
|
-/ |
|
def pickle (p : CommandSnapshot) (path : FilePath) : IO Unit := do |
|
let env := p.cmdState.env |
|
let p' := { p with cmdState := { p.cmdState with env := ← mkEmptyEnvironment }} |
|
_root_.pickle path |
|
(env.header.imports, |
|
env.constants.map₂, |
|
({ p'.cmdState with } : CompactableCommandSnapshot), |
|
p'.cmdContext) |
|
|
|
/ |
|
Unpickle a `CommandSnapshot`. |
|
-/ |
|
def unpickle (path : FilePath) : IO (CommandSnapshot × CompactedRegion) := unsafe do |
|
let ((imports, map₂, cmdState, cmdContext), region) ← |
|
_root_.unpickle (Array Import × PHashMap Name ConstantInfo × CompactableCommandSnapshot × |
|
Command.Context) path |
|
let env ← (← importModules imports {} 0).replay (HashMap.ofList map₂.toList) |
|
let p' : CommandSnapshot := |
|
{ cmdState := { cmdState with env } |
|
cmdContext } |
|
let (_, p'') ← p'.runCommandElabM do |
|
for o in ← getOpenDecls do |
|
if let .simple ns _ := o then do |
|
activateScoped ns |
|
return (p'', region) |
|
|
|
end CommandSnapshot |
|
|
|
/ |
|
Bundled structure for the `State` and `Context` objects |
|
for the `CoreM`, `MetaM`, `TermElabM`, and `TacticM` monads. |
|
-/ |
|
structure ProofSnapshot where |
|
coreState : Core.State |
|
coreContext : Core.Context |
|
metaState : Meta.State |
|
metaContext : Meta.Context |
|
termState : Term.State |
|
termContext : Term.Context |
|
tacticState : Tactic.State |
|
tacticContext : Tactic.Context |
|
|
|
namespace ProofSnapshot |
|
|
|
open Lean Elab Tactic |
|
|
|
/ |
|
def newMessages (new : ProofSnapshot) (old? : Option ProofSnapshot := none) : List Lean.Message := |
|
match old? with |
|
| none => new.coreState.messages.msgs.toList |
|
| some old => new.coreState.messages.msgs.toList.drop (old.coreState.messages.msgs.size) |
|
|
|
/ |
|
def newInfoTrees (new : ProofSnapshot) (old? : Option ProofSnapshot := none) : List InfoTree := |
|
let infoState := new.coreState.infoState |
|
let trees := match old? with |
|
| none => infoState.trees.toList |
|
| some old => infoState.trees.toList.drop (old.coreState.infoState.trees.size) |
|
trees.map fun t => t.substitute infoState.assignment |
|
|
|
/ |
|
def runCoreM (p : ProofSnapshot) (t : CoreM α) : IO (α × ProofSnapshot) := do |
|
let (a, coreState) ← (Lean.Core.CoreM.toIO · p.coreContext p.coreState) do t |
|
return (a, { p with coreState }) |
|
|
|
/ |
|
def runMetaM (p : ProofSnapshot) (t : MetaM α) : IO (α × ProofSnapshot) := do |
|
let ((a, metaState), p') ← |
|
p.runCoreM (Lean.Meta.MetaM.run (ctx := p.metaContext) (s := p.metaState) do t) |
|
return (a, { p' with metaState }) |
|
|
|
/ |
|
def runTermElabM (p : ProofSnapshot) (t : TermElabM α) : IO (α × ProofSnapshot) := do |
|
let ((a, termState), p') ← p.runMetaM (Lean.Elab.Term.TermElabM.run (s := p.termState) |
|
(do let r ← t; Term.synthesizeSyntheticMVarsNoPostponing; pure r)) |
|
return (a, { p' with termState }) |
|
|
|
/ |
|
def runTacticM (p : ProofSnapshot) (t : TacticM α) : IO (α × ProofSnapshot) := do |
|
let ((a, tacticState), p') ← p.runTermElabM (t p.tacticContext |>.run p.tacticState) |
|
return (a, { p' with tacticState }) |
|
|
|
/ |
|
Run a `TacticM` monadic function in the current `ProofSnapshot`, updating the `Tactic.State`, |
|
and discarding the return value. |
|
-/ |
|
def runTacticM' (p : ProofSnapshot) (t : TacticM α) : IO ProofSnapshot := |
|
Prod.snd <$> p.runTacticM t |
|
|
|
/ |
|
def newTraces (new : ProofSnapshot) (old? : Option ProofSnapshot := none) : IO (List String) := |
|
match old? with |
|
| none => (·.1) <$> new.runCoreM (do |
|
(← getTraces).toList.mapM fun t => do pure (← t.msg.toString).trim) |
|
| some old => do |
|
let oldCount ← (·.1) <$> old.runCoreM (return (← getTraces).size) |
|
(·.1) <$> new.runCoreM (do |
|
((← getTraces).toList.drop oldCount).mapM fun t => do pure (← t.msg.toString).trim) |
|
|
|
/ |
|
Evaluate a `Syntax` into a `TacticM` tactic, and run it in the current `ProofSnapshot`. |
|
-/ |
|
def runSyntax (p : ProofSnapshot) (t : Syntax) : IO ProofSnapshot := |
|
Prod.snd <$> p.runTacticM (evalTactic t) |
|
|
|
/ |
|
Parse a string into a `Syntax`, evaluate it as a `TacticM` tactic, |
|
and run it in the current `ProofSnapshot`. |
|
-/ |
|
def runString (p : ProofSnapshot) (t : String) : IO ProofSnapshot := |
|
match Parser.runParserCategory p.coreState.env `tactic t with |
|
| .error e => throw (IO.userError e) |
|
| .ok stx => p.runSyntax stx |
|
|
|
/ |
|
def ppGoals (p : ProofSnapshot) : IO (List Format) := |
|
Prod.fst <$> p.runMetaM do p.tacticState.goals.mapM (Meta.ppGoal ·) |
|
/ |
|
Construct a `ProofSnapshot` from a `ContextInfo` and optional `LocalContext`, and a list of goals. |
|
|
|
For convenience, we also allow a list of `Expr`s, and these are appended to the goals |
|
as fresh metavariables with the given types. |
|
-/ |
|
def create (ctx : ContextInfo) (lctx? : Option LocalContext) (env? : Option Environment) |
|
(goals : List MVarId) (types : List Expr := []) : IO ProofSnapshot := do |
|
ctx.runMetaM (lctx?.getD {}) do |
|
let goals := goals ++ (← types.mapM fun t => Expr.mvarId! <$> Meta.mkFreshExprMVar (some t)) |
|
goals.head!.withContext do |
|
let s ← getThe Core.State |
|
let s := match env? with |
|
| none => s |
|
| some env => { s with env } |
|
pure <| |
|
{ coreState := s |
|
coreContext := ← readThe Core.Context |
|
metaState := ← getThe Meta.State |
|
metaContext := ← readThe Meta.Context |
|
termState := {} |
|
termContext := {} |
|
tacticState := { goals } |
|
tacticContext := { elaborator := .anonymous } } |
|
|
|
open Lean.Core in |
|
/ |
|
structure CompactableCoreState where |
|
|
|
nextMacroScope : MacroScope := firstFrontendMacroScope + 1 |
|
ngen : NameGenerator := {} |
|
|
|
|
|
|
|
|
|
|
|
open Lean.Meta in |
|
/ |
|
structure CompactableMetaContext where |
|
config : Config := {} |
|
lctx : LocalContext := {} |
|
localInstances : LocalInstances := #[] |
|
defEqCtx? : Option DefEqContext := none |
|
synthPendingDepth : Nat := 0 |
|
|
|
|
|
/ |
|
structure CompactableTermContext where |
|
declName? : Option Name := none |
|
auxDeclToFullName : FVarIdMap Name := {} |
|
macroStack : MacroStack := [] |
|
mayPostpone : Bool := true |
|
errToSorry : Bool := true |
|
autoBoundImplicit : Bool := false |
|
autoBoundImplicits : PArray Expr := {} |
|
|
|
sectionVars : NameMap Name := {} |
|
sectionFVars : NameMap Expr := {} |
|
implicitLambda : Bool := true |
|
isNoncomputableSection : Bool := false |
|
ignoreTCFailures : Bool := false |
|
inPattern : Bool := false |
|
|
|
saveRecAppSyntax : Bool := true |
|
holesAsSyntheticOpaque : Bool := false |
|
|
|
open System (FilePath) |
|
|
|
/ |
|
Pickle a `ProofSnapshot`, discarding closures and non-essential caches. |
|
|
|
When pickling the `Environment`, we do so relative to its imports. |
|
-/ |
|
def pickle (p : ProofSnapshot) (path : FilePath) : IO Unit := do |
|
let env := p.coreState.env |
|
let p' := { p with coreState := { p.coreState with env := ← mkEmptyEnvironment }} |
|
_root_.pickle path |
|
(env.header.imports, |
|
env.constants.map₂, |
|
({ p'.coreState with } : CompactableCoreState), |
|
p'.coreContext, |
|
p'.metaState, |
|
({ p'.metaContext with } : CompactableMetaContext), |
|
p'.termState, |
|
({ p'.termContext with } : CompactableTermContext), |
|
p'.tacticState, |
|
p'.tacticContext) |
|
|
|
/ |
|
Unpickle a `ProofSnapshot`. |
|
-/ |
|
def unpickle (path : FilePath) (cmd? : Option CommandSnapshot) : |
|
IO (ProofSnapshot × CompactedRegion) := unsafe do |
|
let ((imports, map₂, coreState, coreContext, metaState, metaContext, termState, termContext, |
|
tacticState, tacticContext), region) ← |
|
_root_.unpickle (Array Import × PHashMap Name ConstantInfo × CompactableCoreState × |
|
Core.Context × Meta.State × CompactableMetaContext × Term.State × CompactableTermContext × |
|
Tactic.State × Tactic.Context) path |
|
let env ← match cmd? with |
|
| none => |
|
enableInitializersExecution |
|
(← importModules imports {} 0).replay (HashMap.ofList map₂.toList) |
|
| some cmd => |
|
cmd.cmdState.env.replay (HashMap.ofList map₂.toList) |
|
let p' : ProofSnapshot := |
|
{ coreState := { coreState with env } |
|
coreContext |
|
metaState |
|
metaContext := { metaContext with } |
|
termState |
|
termContext := { termContext with } |
|
tacticState |
|
tacticContext } |
|
let (_, p'') ← p'.runCoreM do |
|
for o in ← getOpenDecls do |
|
if let .simple ns _ := o then |
|
activateScoped ns |
|
return (p'', region) |
|
|
|
end ProofSnapshot |
|
|