refactor: CommandElabM and FrontendM in IO
This commit is contained in:
parent
3264210d88
commit
bc7455e04e
5 changed files with 112 additions and 48 deletions
|
|
@ -14,10 +14,6 @@ namespace Lean
|
|||
namespace Elab
|
||||
namespace Command
|
||||
|
||||
structure Context :=
|
||||
(fileName : String)
|
||||
(fileMap : FileMap)
|
||||
|
||||
structure Scope :=
|
||||
(kind : String)
|
||||
(header : String)
|
||||
|
|
@ -32,7 +28,6 @@ instance Scope.inhabited : Inhabited Scope := ⟨{ kind := "", header := "" }⟩
|
|||
structure State :=
|
||||
(env : Environment)
|
||||
(messages : MessageLog := {})
|
||||
(cmdPos : String.Pos := 0)
|
||||
(scopes : List Scope := [{ kind := "root", header := "" }])
|
||||
|
||||
instance State.inhabited : Inhabited State := ⟨{ env := arbitrary _ }⟩
|
||||
|
|
@ -40,11 +35,44 @@ instance State.inhabited : Inhabited State := ⟨{ env := arbitrary _ }⟩
|
|||
def mkState (env : Environment) (messages : MessageLog := {}) (opts : Options := {}) : State :=
|
||||
{ env := env, messages := messages, scopes := [{ kind := "root", header := "", opts := opts }] }
|
||||
|
||||
abbrev CommandElabM := ReaderT Context (EStateM Exception State)
|
||||
structure Context :=
|
||||
(fileName : String)
|
||||
(fileMap : FileMap)
|
||||
(stateRef : IO.Ref State)
|
||||
(cmdPos : String.Pos := 0)
|
||||
|
||||
abbrev CommandElabCoreM (ε) := ReaderT Context (EIO ε)
|
||||
abbrev CommandElabM := CommandElabCoreM Exception
|
||||
abbrev CommandElab := SyntaxNode → CommandElabM Unit
|
||||
|
||||
def mkMessageAux (ctx : Context) (ref : Syntax) (msgData : MessageData) (severity : MessageSeverity) : Message :=
|
||||
mkMessageCore ctx.fileName ctx.fileMap msgData severity (ref.getPos.getD ctx.cmdPos)
|
||||
|
||||
private def ioErrorToMessage (ctx : Context) (ref : Syntax) (err : IO.Error) : Message :=
|
||||
mkMessageAux ctx ref ("IO error, " ++ toString err) MessageSeverity.error
|
||||
|
||||
@[inline] def liftIOCore {α} (ctx : Context) (ref : Syntax) (x : IO α) : EIO Exception α :=
|
||||
EIO.adaptExcept (ioErrorToMessage ctx ref) x
|
||||
|
||||
@[inline] def liftIO {α} (ref : Syntax) (x : IO α) : CommandElabM α :=
|
||||
fun ctx => liftIOCore ctx ref x
|
||||
|
||||
private def getState : CommandElabM State :=
|
||||
fun ctx => liftIOCore ctx Syntax.missing $ ctx.stateRef.get
|
||||
|
||||
private def setState (s : State) : CommandElabM Unit :=
|
||||
fun ctx => liftIOCore ctx Syntax.missing $ ctx.stateRef.set s
|
||||
|
||||
@[inline] private def modifyGetState {α} (f : State → α × State) : CommandElabM α := do
|
||||
s ← getState; let (a, s) := f s; setState s; pure a
|
||||
|
||||
instance CommandElabCoreM.monadState : MonadState State CommandElabM :=
|
||||
{ get := getState,
|
||||
set := setState,
|
||||
modifyGet := @modifyGetState }
|
||||
|
||||
instance CommandElabM.monadLog : MonadLog CommandElabM :=
|
||||
{ getCmdPos := do s ← get; pure s.cmdPos,
|
||||
{ getCmdPos := do ctx ← read; pure ctx.cmdPos,
|
||||
getFileMap := do ctx ← read; pure ctx.fileMap,
|
||||
getFileName := do ctx ← read; pure ctx.fileName,
|
||||
logMessage := fun msg => modify $ fun s => { messages := s.messages.add msg, .. s } }
|
||||
|
|
@ -107,7 +135,7 @@ let scope := s.scopes.head!;
|
|||
{ config := { opts := scope.opts, foApprox := true, ctxApprox := true, quasiPatternApprox := true, isDefEqStuckEx := true },
|
||||
fileName := ctx.fileName,
|
||||
fileMap := ctx.fileMap,
|
||||
cmdPos := s.cmdPos,
|
||||
cmdPos := ctx.cmdPos,
|
||||
currNamespace := scope.currNamespace,
|
||||
univNames := scope.univNames,
|
||||
openDecls := scope.openDecls }
|
||||
|
|
@ -125,8 +153,22 @@ match result with
|
|||
| EStateM.Result.error (Term.Exception.error ex) newS => EStateM.Result.error ex { env := newS.env, messages := newS.messages, .. s }
|
||||
| EStateM.Result.error Term.Exception.postpone newS => unreachable!
|
||||
|
||||
@[inline] def runTermElabM {α} (x : TermElabM α) : CommandElabM α :=
|
||||
fun ctx s => toCommandResult ctx s $ (Term.elabBinders (getVarDecls s) (fun _ => x)) (mkTermContext ctx s) (mkTermState s)
|
||||
instance CommandElabM.inhabited {α} : Inhabited (CommandElabM α) :=
|
||||
⟨throw $ arbitrary _⟩
|
||||
|
||||
@[inline] def runTermElabM {α} (x : TermElabM α) : CommandElabM α := do
|
||||
ctx ← read;
|
||||
s ← get;
|
||||
match ((Term.elabBinders (getVarDecls s) (fun _ => x)) (mkTermContext ctx s)).run (mkTermState s) with
|
||||
| EStateM.Result.ok a newS => do modify $ fun s => { env := newS.env, messages := newS.messages, .. s }; pure a
|
||||
| EStateM.Result.error (Term.Exception.error ex) newS => do modify $ fun s => { env := newS.env, messages := newS.messages, .. s }; throw ex
|
||||
| EStateM.Result.error Term.Exception.postpone newS => unreachable!
|
||||
|
||||
@[inline] def withLogging (x : CommandElabM Unit) : CommandElabM Unit :=
|
||||
catch x (fun ex => do logMessage ex; pure ())
|
||||
|
||||
@[inline] def catchExceptions (x : CommandElabM Unit) : CommandElabCoreM Empty Unit :=
|
||||
fun ctx => EIO.catchExceptions (withLogging x ctx) (fun _ => pure ())
|
||||
|
||||
def dbgTrace {α} [HasToString α] (a : α) : CommandElabM Unit :=
|
||||
_root_.dbgTrace (toString a) $ fun _ => pure ()
|
||||
|
|
|
|||
|
|
@ -11,40 +11,52 @@ namespace Lean
|
|||
namespace Elab
|
||||
namespace Frontend
|
||||
|
||||
structure State :=
|
||||
(commandState : Command.State)
|
||||
(parserState : Parser.ModuleParserState)
|
||||
structure Context :=
|
||||
(commandStateRef : IO.Ref Command.State)
|
||||
(parserStateRef : IO.Ref Parser.ModuleParserState)
|
||||
(cmdPosRef : IO.Ref String.Pos)
|
||||
(parserCtx : Parser.ParserContextCore)
|
||||
|
||||
abbrev FrontendM := ReaderT Parser.ParserContextCore (StateM State)
|
||||
abbrev FrontendM := ReaderT Context (EIO Empty)
|
||||
|
||||
private def getCmdContext : FrontendM Command.Context := do
|
||||
c ← read;
|
||||
pure { fileName := c.fileName, fileMap := c.fileMap }
|
||||
@[inline] def liftIOCore! {α} [Inhabited α] (x : IO α) : EIO Empty α :=
|
||||
EIO.catchExceptions x (fun _ => unreachable!)
|
||||
|
||||
@[inline] def runCommandElabM (x : Command.CommandElabM Unit) : FrontendM Unit := do
|
||||
fun ctx s =>
|
||||
let parserState := s.parserState;
|
||||
match x { fileName := ctx.fileName, fileMap := ctx.fileMap } s.commandState with
|
||||
| EStateM.Result.ok _ newCmdState => ((), { commandState := newCmdState, parserState := parserState })
|
||||
| EStateM.Result.error ex newCmdState =>
|
||||
let newCmdState := { messages := newCmdState.messages.add ex, .. newCmdState };
|
||||
((), { commandState := newCmdState, parserState := parserState })
|
||||
@[inline] def runCommandElabM (x : Command.CommandElabM Unit) : FrontendM Unit :=
|
||||
fun ctx => do
|
||||
cmdPos ← liftIOCore! $ ctx.cmdPosRef.get;
|
||||
let cmdCtx : Command.Context := { cmdPos := cmdPos, stateRef := ctx.commandStateRef, fileName := ctx.parserCtx.fileName, fileMap := ctx.parserCtx.fileMap };
|
||||
EIO.catchExceptions (x cmdCtx) (fun _ => pure ())
|
||||
|
||||
def elabCommandAtFrontend (stx : Syntax) : FrontendM Unit :=
|
||||
runCommandElabM (Command.elabCommand stx)
|
||||
runCommandElabM (Command.withLogging $ Command.elabCommand stx)
|
||||
|
||||
def updateCmdPos : FrontendM Unit :=
|
||||
modify $ fun s => { commandState := { cmdPos := s.parserState.pos, .. s.commandState }, .. s }
|
||||
fun ctx => do
|
||||
parserState ← liftIOCore! $ ctx.parserStateRef.get;
|
||||
liftIOCore! $ ctx.cmdPosRef.set parserState.pos
|
||||
|
||||
def getParserState : FrontendM Parser.ModuleParserState :=
|
||||
fun ctx => liftIOCore! $ ctx.parserStateRef.get
|
||||
|
||||
def getCommandState : FrontendM Command.State :=
|
||||
fun ctx => liftIOCore! $ ctx.commandStateRef.get
|
||||
|
||||
def setParserState (ps : Parser.ModuleParserState) : FrontendM Unit :=
|
||||
fun ctx => liftIOCore! $ ctx.parserStateRef.set ps
|
||||
|
||||
def setMessages (msgs : MessageLog) : FrontendM Unit :=
|
||||
fun ctx => liftIOCore! $ ctx.commandStateRef.modify $ fun s => { messages := msgs, .. s }
|
||||
|
||||
def processCommand : FrontendM Bool := do
|
||||
updateCmdPos;
|
||||
s ← get;
|
||||
let cs := s.commandState;
|
||||
let ps := s.parserState;
|
||||
cs ← getCommandState;
|
||||
ps ← getParserState;
|
||||
c ← read;
|
||||
match Parser.parseCommand cs.env c ps cs.messages with
|
||||
match Parser.parseCommand cs.env c.parserCtx ps cs.messages with
|
||||
| (cmd, ps, messages) => do
|
||||
set { commandState := { messages := messages, .. cs }, parserState := ps };
|
||||
setParserState ps;
|
||||
setMessages messages;
|
||||
if Parser.isEOI cmd || Parser.isExitCommand cmd then do
|
||||
pure true -- Done
|
||||
else do
|
||||
|
|
@ -64,23 +76,33 @@ end Frontend
|
|||
|
||||
open Frontend
|
||||
|
||||
def process (input : String) (env : Environment) (opts : Options) (fileName : Option String := none) : Environment × MessageLog :=
|
||||
let fileName := fileName.getD "<input>";
|
||||
let ctx := Parser.mkParserContextCore env input fileName;
|
||||
let cmdState := Command.mkState env {} opts;
|
||||
match (processCommands ctx).run { commandState := cmdState, parserState := {} } with
|
||||
| (_, { commandState := { env := env, messages := messages, .. }, .. }) => (env, messages)
|
||||
def IO.processCommands (parserCtx : Parser.ParserContextCore) (parserStateRef : IO.Ref Parser.ModuleParserState) (cmdStateRef : IO.Ref Command.State) : IO Unit := do
|
||||
ps ← parserStateRef.get;
|
||||
cmdPosRef ← IO.mkRef ps.pos;
|
||||
EIO.adaptExcept (fun (ex : Empty) => unreachable!) $ -- TODO: compiler support for Empty.rec is missing
|
||||
processCommands { commandStateRef := cmdStateRef, parserStateRef := parserStateRef, cmdPosRef := cmdPosRef, parserCtx := parserCtx }
|
||||
|
||||
def process (input : String) (env : Environment) (opts : Options) (fileName : Option String := none) : IO (Environment × MessageLog) := do
|
||||
let fileName := fileName.getD "<input>";
|
||||
let parserCtx := Parser.mkParserContextCore env input fileName;
|
||||
parserStateRef ← IO.mkRef { Parser.ModuleParserState . };
|
||||
cmdStateRef ← IO.mkRef $ Command.mkState env {} opts;
|
||||
IO.processCommands parserCtx parserStateRef cmdStateRef;
|
||||
cmdState ← cmdStateRef.get;
|
||||
pure (cmdState.env, cmdState.messages)
|
||||
|
||||
def testFrontend (input : String) (opts : Options := {}) (fileName : Option String := none) : IO (Environment × MessageLog) := do
|
||||
env ← mkEmptyEnvironment;
|
||||
let fileName := fileName.getD "<input>";
|
||||
let ctx := Parser.mkParserContextCore env input fileName;
|
||||
match Parser.parseHeader env ctx with
|
||||
let parserCtx := Parser.mkParserContextCore env input fileName;
|
||||
match Parser.parseHeader env parserCtx with
|
||||
| (header, parserState, messages) => do
|
||||
(env, messages) ← processHeader header messages ctx;
|
||||
let cmdState := Command.mkState env messages opts;
|
||||
match (processCommands ctx).run { commandState := cmdState, parserState := parserState } with
|
||||
| (_, { commandState := { env := env, messages := messages, .. }, .. }) => pure (env, messages)
|
||||
(env, messages) ← processHeader header messages parserCtx;
|
||||
parserStateRef ← IO.mkRef parserState;
|
||||
cmdStateRef ← IO.mkRef $ Command.mkState env messages opts;
|
||||
IO.processCommands parserCtx parserStateRef cmdStateRef;
|
||||
cmdState ← cmdStateRef.get;
|
||||
pure (cmdState.env, cmdState.messages)
|
||||
|
||||
end Elab
|
||||
end Lean
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@ def EIO (ε : Type) : Type → Type := EStateM ε IO.RealWorld
|
|||
@[inline] def EIO.adaptExcept {α ε ε'} (f : ε → ε') (x : EIO ε α) : EIO ε' α :=
|
||||
EStateM.adaptExcept f x
|
||||
|
||||
@[inline] def EIO.catchExceptions {ε} (x : EIO ε Unit) (h : ε → EIO Empty Unit) : EIO Empty Unit :=
|
||||
@[inline] def EIO.catchExceptions {α ε} (x : EIO ε α) (h : ε → EIO Empty α) : EIO Empty α :=
|
||||
fun s => match x s with
|
||||
| EStateM.Result.ok _ s => EStateM.Result.ok () s
|
||||
| EStateM.Result.ok a s => EStateM.Result.ok a s
|
||||
| EStateM.Result.error ex s => h ex s
|
||||
|
||||
instance (ε : Type) : Monad (EIO ε) := inferInstanceAs (Monad (EStateM ε IO.RealWorld))
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ open Lean.Elab
|
|||
def run (input : String) (failIff : Bool := true) : MetaIO Unit :=
|
||||
do env ← MetaIO.getEnv;
|
||||
opts ← MetaIO.getOptions;
|
||||
let (env, messages) := process input env opts;
|
||||
(env, messages) ← liftM $ process input env opts;
|
||||
messages.forM $ fun msg => IO.println msg;
|
||||
when (failIff && messages.hasErrors) $ throw (IO.userError "errors have been found");
|
||||
when (!failIff && !messages.hasErrors) $ throw (IO.userError "there are no errors");
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ open Lean.Elab
|
|||
def run (input : String) (failIff : Bool := true) : MetaIO Unit :=
|
||||
do env ← MetaIO.getEnv;
|
||||
opts ← MetaIO.getOptions;
|
||||
let (env, messages) := process input env opts;
|
||||
(env, messages) ← liftM $ process input env opts;
|
||||
messages.toList.forM $ fun msg => IO.println msg;
|
||||
when (failIff && messages.hasErrors) $ throw (IO.userError "errors have been found");
|
||||
when (!failIff && !messages.hasErrors) $ throw (IO.userError "there are no errors");
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue