There is no reason for having `MonadIO` anymore. The `MonadLift` type
class is well behaved in the new frontend, the `MonadFinally` solves
the problem at monad stacks such as `ExcepT e IO`.
This commit also changes the type of the IO printing functions.
For example, the type of `IO.println` was
```
def IO.println {m} [MonadIO m] {α} [ToString α] (s : α) : m Unit
```
and now it is just
```
def IO.println {α} [ToString α] (s : α) : IO Unit
```
We rely on the new frontend auto-lifting feature.
That is, if there is an instance `[MonadLiftT IO m]`, then
a term of type `IO a` is automatically coerced to `m a`
We also want a simpler `IO.println` for writing tests.
For example,
```
```
doesn't work because there isn't sufficient information for inferring
the parameter `m` in the previous `IO.println`.
The shortest workaround looked very weird
```
```
I considered adding `IO` as a default value for `m` when we have
`MonadIO m`, as we use `Nat` as the default for `ofNat a`, but it felt
like uncessary complexity.
@Kha The commit seems to work well. The auto-lifting featuring has
been working great for me. There is still room for improvement.
For example, given `MonadLiftT m n`, it doesn't automatically lift
`a -> m b` into `a -> n b`. So, code such as
`foo >>= IO.println`
had to be rewritten as
`foo >>= fun x => IO.println x`
I will add this feature later.
If you have time, please try to play with this feature and figure out
if it is stable enough for making it the default.
That is, if it roboust enough, we can stop using the following idiom
for writing functions that can be lifted automatically.
```
def instantiateLevelMVarsImp (u : Level) : MetaM Level :=
...
def instantiateLevelMVars {m} [MonadLiftT MetaM m] (u : Level) : m Level :=
liftMetaM $ instantiateLevelMVarsImp u
```
I think we only need this idiom when using `MonadControlT` which is
not as common as `MonadLiftT`.
147 lines
6.9 KiB
Text
147 lines
6.9 KiB
Text
/-
|
||
Copyright (c) 2020 Sebastian Ullrich. All rights reserved.
|
||
Released under Apache 2.0 license as described in the file LICENSE.
|
||
Authors: Sebastian Ullrich
|
||
-/
|
||
|
||
/-!
|
||
Gadgets for compiling parser declarations into other programs, such as pretty printers.
|
||
-/
|
||
|
||
import Lean.Util.ReplaceExpr
|
||
import Lean.Meta.Basic
|
||
import Lean.Meta.WHNF
|
||
import Lean.ParserCompiler.Attribute
|
||
import Lean.Parser.Extension
|
||
|
||
namespace Lean
|
||
namespace ParserCompiler
|
||
|
||
structure Context (α : Type) :=
|
||
(varName : Name)
|
||
(categoryAttr : KeyedDeclsAttribute α)
|
||
(combinatorAttr : CombinatorAttribute)
|
||
|
||
def Context.tyName {α} (ctx : Context α) : Name := ctx.categoryAttr.defn.valueTypeName
|
||
|
||
-- replace all references of `Parser` with `tyName` as a first approximation
|
||
def preprocessParserBody {α} (ctx : Context α) (e : Expr) : Expr :=
|
||
e.replace fun e => if e.isConstOf `Lean.Parser.Parser then mkConst ctx.tyName else none
|
||
|
||
section
|
||
open Meta
|
||
|
||
variables {α} (ctx : Context α) (force : Bool := false) in
|
||
/--
|
||
Translate an expression of type `Parser` into one of type `tyName`, tagging intermediary constants with
|
||
`ctx.combinatorAttr`. If `force` is `false`, refuse to do so for imported constants. -/
|
||
partial def compileParserExpr (e : Expr) : MetaM Expr := do
|
||
let e ← whnfCore e
|
||
match e with
|
||
| e@(Expr.lam _ _ _ _) => lambdaLetTelescope e fun xs b => compileParserExpr b >>= mkLambdaFVars xs
|
||
| e@(Expr.fvar _ _) => pure e
|
||
| _ => do
|
||
let fn := e.getAppFn
|
||
let Expr.const c _ _ ← pure fn
|
||
| throwError! "call of unknown parser at '{e}'"
|
||
let args := e.getAppArgs
|
||
-- call the translated `p` with (a prefix of) the arguments of `e`, recursing for arguments
|
||
-- of type `ty` (i.e. formerly `Parser`)
|
||
let mkCall (p : Name) := do
|
||
let ty ← inferType (mkConst p)
|
||
forallTelescope ty fun params _ => do
|
||
let mut p := mkConst p
|
||
let args := e.getAppArgs
|
||
for i in [:Nat.min params.size args.size] do
|
||
let param := params[i]
|
||
let arg := args[i]
|
||
let paramTy ← inferType param
|
||
let resultTy ← forallTelescope paramTy fun _ b => pure b
|
||
let arg ← if resultTy.isConstOf ctx.tyName then compileParserExpr arg else pure arg
|
||
p := mkApp p arg
|
||
pure p
|
||
let env ← getEnv
|
||
match ctx.combinatorAttr.getDeclFor? env c with
|
||
| some p => mkCall p
|
||
| none =>
|
||
let c' := c ++ ctx.varName
|
||
let cinfo ← getConstInfo c
|
||
let resultTy ← forallTelescope cinfo.type fun _ b => pure b
|
||
if resultTy.isConstOf `Lean.Parser.TrailingParser || resultTy.isConstOf `Lean.Parser.Parser then do
|
||
-- synthesize a new `[combinatorAttr c]`
|
||
let some value ← pure cinfo.value?
|
||
| throwError! "don't know how to generate {ctx.varName} for non-definition '{e}'"
|
||
unless (env.getModuleIdxFor? c).isNone || force do
|
||
throwError! "refusing to generate code for imported parser declaration '{c}'; use `@[runParserAttributeHooks]` on its definition instead."
|
||
let value ← compileParserExpr $ preprocessParserBody ctx value
|
||
let ty ← forallTelescope cinfo.type fun params _ =>
|
||
params.foldrM (init := mkConst ctx.tyName) fun param ty => do
|
||
let paramTy ← inferType param;
|
||
let paramTy ← forallTelescope paramTy fun _ b => pure $
|
||
if b.isConstOf `Lean.Parser.Parser then mkConst ctx.tyName
|
||
else b
|
||
pure $ mkForall `_ BinderInfo.default paramTy ty
|
||
let decl := Declaration.defnDecl {
|
||
name := c', lparams := [],
|
||
type := ty, value := value, hints := ReducibilityHints.opaque, isUnsafe := false }
|
||
let env ← getEnv
|
||
let env ← match env.addAndCompile {} decl with
|
||
| Except.ok env => pure env
|
||
| Except.error kex => do throwError (← (kex.toMessageData {}).toString)
|
||
setEnv $ ctx.combinatorAttr.setDeclFor env c c'
|
||
mkCall c'
|
||
else
|
||
-- if this is a generic function, e.g. `AndThen.andthen`, it's easier to just unfold it until we are
|
||
-- back to parser combinators
|
||
let some e' ← unfoldDefinition? e
|
||
| throwError! "don't know how to generate {ctx.varName} for non-parser combinator '{e}'"
|
||
compileParserExpr e'
|
||
end
|
||
|
||
open Core
|
||
|
||
/-- Compile the given declaration into a `[(builtin)categoryAttr declName]` -/
|
||
def compileCategoryParser {α} (ctx : Context α) (declName : Name) (builtin : Bool) : AttrM Unit := do
|
||
-- This will also tag the declaration as a `[combinatorParenthesizer declName]` in case the parser is used by other parsers.
|
||
-- Note that simply having `[(builtin)Parenthesizer]` imply `[combinatorParenthesizer]` is not ideal since builtin
|
||
-- attributes are active only in the next stage, while `[combinatorParenthesizer]` is active immediately (since we never
|
||
-- call them at compile time but only reference them).
|
||
let (Expr.const c' _ _) ← (compileParserExpr ctx (mkConst declName) (force := false)).run'
|
||
| unreachable!
|
||
-- We assume that for tagged parsers, the kind is equal to the declaration name. This is automatically true for parsers
|
||
-- using `parser!` or `syntax`.
|
||
let kind := declName
|
||
addAttribute c' (if builtin then ctx.categoryAttr.defn.builtinName else ctx.categoryAttr.defn.name) (mkNullNode #[mkIdent kind])
|
||
|
||
variables {α} (ctx : Context α) in
|
||
def compileEmbeddedParsers : ParserDescr → MetaM Unit
|
||
| ParserDescr.const _ => pure ()
|
||
| ParserDescr.unary _ d => compileEmbeddedParsers d
|
||
| ParserDescr.binary _ d₁ d₂ => compileEmbeddedParsers d₁ *> compileEmbeddedParsers d₂
|
||
| ParserDescr.parser constName => discard $ compileParserExpr ctx (mkConst constName) (force := false)
|
||
| ParserDescr.node _ _ d => compileEmbeddedParsers d
|
||
| ParserDescr.nodeWithAntiquot _ _ d => compileEmbeddedParsers d
|
||
| ParserDescr.trailingNode _ _ d => compileEmbeddedParsers d
|
||
| ParserDescr.symbol _ => pure ()
|
||
| ParserDescr.nonReservedSymbol _ _ => pure ()
|
||
| ParserDescr.cat _ _ => pure ()
|
||
|
||
/-- Precondition: `α` must match `ctx.tyName`. -/
|
||
unsafe def registerParserCompiler {α} (ctx : Context α) : IO Unit := do
|
||
Parser.registerParserAttributeHook {
|
||
postAdd := fun catName constName builtin => do
|
||
let info ← getConstInfo constName
|
||
if info.type.isConstOf `Lean.ParserDescr || info.type.isConstOf `Lean.TrailingParserDescr then
|
||
let d ← evalConstCheck ParserDescr `Lean.ParserDescr constName <|>
|
||
evalConstCheck TrailingParserDescr `Lean.TrailingParserDescr constName
|
||
compileEmbeddedParsers ctx d $.run'
|
||
else
|
||
if catName.isAnonymous then
|
||
-- `[runBuiltinParserAttributeHooks]` => force compilation even if imported, do not apply `ctx.categoryAttr`.
|
||
discard (compileParserExpr ctx (mkConst constName) (force := true)).run'
|
||
else
|
||
compileCategoryParser ctx constName builtin
|
||
}
|
||
|
||
end ParserCompiler
|
||
end Lean
|