chore: move to new frontend
This commit is contained in:
parent
21132d6c63
commit
7111eb4d79
11 changed files with 963 additions and 1020 deletions
|
|
@ -1,3 +1,4 @@
|
|||
#lang lean4
|
||||
/-
|
||||
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
|
@ -11,187 +12,170 @@ import Lean.ResolveName
|
|||
namespace Lean
|
||||
|
||||
inductive AttributeApplicationTime
|
||||
| afterTypeChecking | afterCompilation | beforeElaboration
|
||||
| afterTypeChecking | afterCompilation | beforeElaboration
|
||||
|
||||
def AttributeApplicationTime.beq : AttributeApplicationTime → AttributeApplicationTime → Bool
|
||||
| AttributeApplicationTime.afterTypeChecking, AttributeApplicationTime.afterTypeChecking => true
|
||||
| AttributeApplicationTime.afterCompilation, AttributeApplicationTime.afterCompilation => true
|
||||
| AttributeApplicationTime.beforeElaboration, AttributeApplicationTime.beforeElaboration => true
|
||||
| _, _ => false
|
||||
| AttributeApplicationTime.afterTypeChecking, AttributeApplicationTime.afterTypeChecking => true
|
||||
| AttributeApplicationTime.afterCompilation, AttributeApplicationTime.afterCompilation => true
|
||||
| AttributeApplicationTime.beforeElaboration, AttributeApplicationTime.beforeElaboration => true
|
||||
| _, _ => false
|
||||
|
||||
instance AttributeApplicationTime.hasBeq : HasBeq AttributeApplicationTime := ⟨AttributeApplicationTime.beq⟩
|
||||
|
||||
structure Attr.Context :=
|
||||
(currNamespace : Name)
|
||||
(openDecls : List OpenDecl)
|
||||
(currNamespace : Name)
|
||||
(openDecls : List OpenDecl)
|
||||
|
||||
abbrev AttrM := ReaderT Attr.Context CoreM
|
||||
|
||||
instance attrResolveName : MonadResolveName AttrM :=
|
||||
{ getCurrNamespace := do ctx ← read; pure ctx.currNamespace,
|
||||
getOpenDecls := do ctx ← read; pure ctx.openDecls }
|
||||
instance attrResolveName : MonadResolveName AttrM := {
|
||||
getCurrNamespace := do pure (← read).currNamespace,
|
||||
getOpenDecls := do pure (← read).openDecls
|
||||
}
|
||||
|
||||
-- TODO: after we delete the old frontend, we should use `EIO` with a richer exception kind at AttributeImpl.
|
||||
-- We must perform a similar modification at `PersistentEnvExtension`
|
||||
|
||||
structure AttributeImpl :=
|
||||
(name : Name)
|
||||
(descr : String)
|
||||
(add (decl : Name) (args : Syntax) (persistent : Bool) : AttrM Unit)
|
||||
/-
|
||||
(addScoped (env : Environment) (decl : Name) (args : Syntax) : IO Environment
|
||||
:= throw (IO.userError ("attribute '" ++ toString name ++ "' does not support scopes")))
|
||||
(erase (env : Environment) (decl : Name) (persistent : Bool) : IO Environment
|
||||
:= throw (IO.userError ("attribute '" ++ toString name ++ "' does not support removal")))
|
||||
(activateScoped (env : Environment) (scope : Name) : IO Environment := pure env)
|
||||
(pushScope (env : Environment) : IO Environment := pure env)
|
||||
(popScope (env : Environment) : IO Environment := pure env)
|
||||
-/
|
||||
(applicationTime := AttributeApplicationTime.afterTypeChecking)
|
||||
(name : Name)
|
||||
(descr : String)
|
||||
(add (decl : Name) (args : Syntax) (persistent : Bool) : AttrM Unit)
|
||||
(applicationTime : AttributeApplicationTime := AttributeApplicationTime.afterTypeChecking)
|
||||
|
||||
instance AttributeImpl.inhabited : Inhabited AttributeImpl :=
|
||||
⟨{ name := arbitrary _, descr := arbitrary _, add := fun env _ _ _ => pure () }⟩
|
||||
instance : Inhabited AttributeImpl :=
|
||||
⟨{ name := arbitrary _, descr := arbitrary _, add := fun env _ _ _ => pure () }⟩
|
||||
|
||||
open Std (PersistentHashMap)
|
||||
|
||||
def mkAttributeMapRef : IO (IO.Ref (PersistentHashMap Name AttributeImpl)) :=
|
||||
IO.mkRef {}
|
||||
|
||||
@[builtinInit mkAttributeMapRef]
|
||||
constant attributeMapRef : IO.Ref (PersistentHashMap Name AttributeImpl) := arbitrary _
|
||||
builtin_initialize attributeMapRef : IO.Ref (PersistentHashMap Name AttributeImpl) ← IO.mkRef {}
|
||||
|
||||
/- Low level attribute registration function. -/
|
||||
def registerBuiltinAttribute (attr : AttributeImpl) : IO Unit := do
|
||||
m ← attributeMapRef.get;
|
||||
when (m.contains attr.name) $ throw (IO.userError ("invalid builtin attribute declaration, '" ++ toString attr.name ++ "' has already been used"));
|
||||
initializing ← IO.initializing;
|
||||
unless initializing $ throw (IO.userError ("failed to register attribute, attributes can only be registered during initialization"));
|
||||
attributeMapRef.modify (fun m => m.insert attr.name attr)
|
||||
let m ← attributeMapRef.get
|
||||
if m.contains attr.name then throw (IO.userError ("invalid builtin attribute declaration, '" ++ toString attr.name ++ "' has already been used"))
|
||||
unless (← IO.initializing) do throw (IO.userError "failed to register attribute, attributes can only be registered during initialization")
|
||||
attributeMapRef.modify fun m => m.insert attr.name attr
|
||||
|
||||
abbrev AttributeImplBuilder := List DataValue → Except String AttributeImpl
|
||||
abbrev AttributeImplBuilderTable := Std.HashMap Name AttributeImplBuilder
|
||||
|
||||
def mkAttributeImplBuilderTable : IO (IO.Ref AttributeImplBuilderTable) := IO.mkRef {}
|
||||
@[builtinInit mkAttributeImplBuilderTable] constant attributeImplBuilderTableRef : IO.Ref AttributeImplBuilderTable := arbitrary _
|
||||
builtin_initialize attributeImplBuilderTableRef : IO.Ref AttributeImplBuilderTable ← IO.mkRef {}
|
||||
|
||||
def registerAttributeImplBuilder (builderId : Name) (builder : AttributeImplBuilder) : IO Unit := do
|
||||
table ← attributeImplBuilderTableRef.get;
|
||||
when (table.contains builderId) $ throw (IO.userError ("attribute implementation builder '" ++ toString builderId ++ "' has already been declared"));
|
||||
attributeImplBuilderTableRef.modify $ fun table => table.insert builderId builder
|
||||
let table ← attributeImplBuilderTableRef.get
|
||||
if table.contains builderId then throw (IO.userError ("attribute implementation builder '" ++ toString builderId ++ "' has already been declared"))
|
||||
attributeImplBuilderTableRef.modify fun table => table.insert builderId builder
|
||||
|
||||
def mkAttributeImplOfBuilder (builderId : Name) (args : List DataValue) : IO AttributeImpl := do
|
||||
table ← attributeImplBuilderTableRef.get;
|
||||
match table.find? builderId with
|
||||
| none => throw (IO.userError ("unknown attribute implementation builder '" ++ toString builderId ++ "'"))
|
||||
| some builder => IO.ofExcept $ builder args
|
||||
let table ← attributeImplBuilderTableRef.get
|
||||
match table.find? builderId with
|
||||
| none => throw (IO.userError ("unknown attribute implementation builder '" ++ toString builderId ++ "'"))
|
||||
| some builder => IO.ofExcept $ builder args
|
||||
|
||||
inductive AttributeExtensionOLeanEntry
|
||||
| decl (declName : Name) -- `declName` has type `AttributeImpl`
|
||||
| builder (builderId : Name) (args : List DataValue)
|
||||
| decl (declName : Name) -- `declName` has type `AttributeImpl`
|
||||
| builder (builderId : Name) (args : List DataValue)
|
||||
|
||||
structure AttributeExtensionState :=
|
||||
(newEntries : List AttributeExtensionOLeanEntry := [])
|
||||
(map : PersistentHashMap Name AttributeImpl)
|
||||
(newEntries : List AttributeExtensionOLeanEntry := [])
|
||||
(map : PersistentHashMap Name AttributeImpl)
|
||||
|
||||
abbrev AttributeExtension := PersistentEnvExtension AttributeExtensionOLeanEntry (AttributeExtensionOLeanEntry × AttributeImpl) AttributeExtensionState
|
||||
|
||||
instance AttributeExtensionState.inhabited : Inhabited AttributeExtensionState := ⟨{ map := {} }⟩
|
||||
instance : Inhabited AttributeExtensionState := ⟨{ map := {} }⟩
|
||||
|
||||
private def AttributeExtension.mkInitial : IO AttributeExtensionState := do
|
||||
map ← attributeMapRef.get;
|
||||
pure { map := map }
|
||||
let map ← attributeMapRef.get
|
||||
pure { map := map }
|
||||
|
||||
unsafe def mkAttributeImplOfConstantUnsafe (env : Environment) (opts : Options) (declName : Name) : Except String AttributeImpl :=
|
||||
match env.find? declName with
|
||||
| none => throw ("unknow constant '" ++ toString declName ++ "'")
|
||||
| some info =>
|
||||
match info.type with
|
||||
| Expr.const `Lean.AttributeImpl _ _ => env.evalConst AttributeImpl opts declName
|
||||
| _ => throw ("unexpected attribute implementation type at '" ++ toString declName ++ "' (`AttributeImpl` expected")
|
||||
match env.find? declName with
|
||||
| none => throw ("unknow constant '" ++ toString declName ++ "'")
|
||||
| some info =>
|
||||
match info.type with
|
||||
| Expr.const `Lean.AttributeImpl _ _ => env.evalConst AttributeImpl opts declName
|
||||
| _ => throw ("unexpected attribute implementation type at '" ++ toString declName ++ "' (`AttributeImpl` expected")
|
||||
|
||||
@[implementedBy mkAttributeImplOfConstantUnsafe]
|
||||
constant mkAttributeImplOfConstant (env : Environment) (opts : Options) (declName : Name) : Except String AttributeImpl := arbitrary _
|
||||
constant mkAttributeImplOfConstant (env : Environment) (opts : Options) (declName : Name) : Except String AttributeImpl
|
||||
|
||||
def mkAttributeImplOfEntry (env : Environment) (opts : Options) (e : AttributeExtensionOLeanEntry) : IO AttributeImpl :=
|
||||
match e with
|
||||
| AttributeExtensionOLeanEntry.decl declName => IO.ofExcept $ mkAttributeImplOfConstant env opts declName
|
||||
| AttributeExtensionOLeanEntry.builder builderId args => mkAttributeImplOfBuilder builderId args
|
||||
match e with
|
||||
| AttributeExtensionOLeanEntry.decl declName => IO.ofExcept $ mkAttributeImplOfConstant env opts declName
|
||||
| AttributeExtensionOLeanEntry.builder builderId args => mkAttributeImplOfBuilder builderId args
|
||||
|
||||
private def AttributeExtension.addImported (es : Array (Array AttributeExtensionOLeanEntry)) : ImportM AttributeExtensionState := do
|
||||
ctx ← read;
|
||||
map ← attributeMapRef.get;
|
||||
map ← es.foldlM
|
||||
(fun map entries =>
|
||||
entries.foldlM
|
||||
(fun (map : PersistentHashMap Name AttributeImpl) entry => do
|
||||
attrImpl ← liftM $ mkAttributeImplOfEntry ctx.env ctx.opts entry;
|
||||
pure $ map.insert attrImpl.name attrImpl)
|
||||
map)
|
||||
map;
|
||||
pure { map := map }
|
||||
let ctx ← read
|
||||
let map ← attributeMapRef.get
|
||||
let map ← es.foldlM
|
||||
(fun map entries =>
|
||||
entries.foldlM
|
||||
(fun (map : PersistentHashMap Name AttributeImpl) entry => do
|
||||
let attrImpl ← liftM $ mkAttributeImplOfEntry ctx.env ctx.opts entry
|
||||
pure $ map.insert attrImpl.name attrImpl)
|
||||
map)
|
||||
map
|
||||
pure { map := map }
|
||||
|
||||
private def AttributeExtension.addEntry (s : AttributeExtensionState) (e : AttributeExtensionOLeanEntry × AttributeImpl) : AttributeExtensionState :=
|
||||
{ s with map := s.map.insert e.2.name e.2, newEntries := e.1 :: s.newEntries }
|
||||
private def addAttrEntry (s : AttributeExtensionState) (e : AttributeExtensionOLeanEntry × AttributeImpl) : AttributeExtensionState :=
|
||||
{ s with map := s.map.insert e.2.name e.2, newEntries := e.1 :: s.newEntries }
|
||||
|
||||
def mkAttributeExtension : IO AttributeExtension :=
|
||||
registerPersistentEnvExtension {
|
||||
name := `attrExt,
|
||||
mkInitial := AttributeExtension.mkInitial,
|
||||
addImportedFn := AttributeExtension.addImported,
|
||||
addEntryFn := AttributeExtension.addEntry,
|
||||
exportEntriesFn := fun s => s.newEntries.reverse.toArray,
|
||||
statsFn := fun s => format "number of local entries: " ++ format s.newEntries.length
|
||||
}
|
||||
|
||||
@[builtinInit mkAttributeExtension]
|
||||
def attributeExtension : AttributeExtension := arbitrary _
|
||||
builtin_initialize attributeExtension : AttributeExtension ←
|
||||
registerPersistentEnvExtension {
|
||||
name := `attrExt,
|
||||
mkInitial := AttributeExtension.mkInitial,
|
||||
addImportedFn := AttributeExtension.addImported,
|
||||
addEntryFn := addAttrEntry,
|
||||
exportEntriesFn := fun s => s.newEntries.reverse.toArray,
|
||||
statsFn := fun s => format "number of local entries: " ++ format s.newEntries.length
|
||||
}
|
||||
|
||||
/- Return true iff `n` is the name of a registered attribute. -/
|
||||
@[export lean_is_attribute]
|
||||
def isBuiltinAttribute (n : Name) : IO Bool := do
|
||||
m ← attributeMapRef.get; pure (m.contains n)
|
||||
let m ← attributeMapRef.get; pure (m.contains n)
|
||||
|
||||
/- Return the name of all registered attributes. -/
|
||||
def getBuiltinAttributeNames : IO (List Name) := do
|
||||
m ← attributeMapRef.get; pure $ m.foldl (fun r n _ => n::r) []
|
||||
let m ← attributeMapRef.get; pure $ m.foldl (fun r n _ => n::r) []
|
||||
|
||||
def getBuiltinAttributeImpl (attrName : Name) : IO AttributeImpl := do
|
||||
m ← attributeMapRef.get;
|
||||
match m.find? attrName with
|
||||
| some attr => pure attr
|
||||
| none => throw (IO.userError ("unknown attribute '" ++ toString attrName ++ "'"))
|
||||
let m ← attributeMapRef.get
|
||||
match m.find? attrName with
|
||||
| some attr => pure attr
|
||||
| none => throw (IO.userError ("unknown attribute '" ++ toString attrName ++ "'"))
|
||||
|
||||
@[export lean_attribute_application_time]
|
||||
def getBuiltinAttributeApplicationTime (n : Name) : IO AttributeApplicationTime := do
|
||||
attr ← getBuiltinAttributeImpl n;
|
||||
pure attr.applicationTime
|
||||
let attr ← getBuiltinAttributeImpl n
|
||||
pure attr.applicationTime
|
||||
|
||||
def isAttribute (env : Environment) (attrName : Name) : Bool :=
|
||||
(attributeExtension.getState env).map.contains attrName
|
||||
(attributeExtension.getState env).map.contains attrName
|
||||
|
||||
def getAttributeNames (env : Environment) : List Name :=
|
||||
let m := (attributeExtension.getState env).map;
|
||||
m.foldl (fun r n _ => n::r) []
|
||||
let m := (attributeExtension.getState env).map
|
||||
m.foldl (fun r n _ => n::r) []
|
||||
|
||||
def getAttributeImpl (env : Environment) (attrName : Name) : Except String AttributeImpl :=
|
||||
let m := (attributeExtension.getState env).map;
|
||||
match m.find? attrName with
|
||||
| some attr => pure attr
|
||||
| none => throw ("unknown attribute '" ++ toString attrName ++ "'")
|
||||
let m := (attributeExtension.getState env).map
|
||||
match m.find? attrName with
|
||||
| some attr => pure attr
|
||||
| none => throw ("unknown attribute '" ++ toString attrName ++ "'")
|
||||
|
||||
def registerAttributeOfDecl (env : Environment) (opts : Options) (attrDeclName : Name) : Except String Environment := do
|
||||
attrImpl ← mkAttributeImplOfConstant env opts attrDeclName;
|
||||
if isAttribute env attrImpl.name then
|
||||
throw ("invalid builtin attribute declaration, '" ++ toString attrImpl.name ++ "' has already been used")
|
||||
else
|
||||
pure $ attributeExtension.addEntry env (AttributeExtensionOLeanEntry.decl attrDeclName, attrImpl)
|
||||
let attrImpl ← mkAttributeImplOfConstant env opts attrDeclName
|
||||
if isAttribute env attrImpl.name then
|
||||
throw ("invalid builtin attribute declaration, '" ++ toString attrImpl.name ++ "' has already been used")
|
||||
else
|
||||
pure $ attributeExtension.addEntry env (AttributeExtensionOLeanEntry.decl attrDeclName, attrImpl)
|
||||
|
||||
def registerAttributeOfBuilder (env : Environment) (builderId : Name) (args : List DataValue) : IO Environment := do
|
||||
attrImpl ← mkAttributeImplOfBuilder builderId args;
|
||||
if isAttribute env attrImpl.name then
|
||||
throw (IO.userError ("invalid builtin attribute declaration, '" ++ toString attrImpl.name ++ "' has already been used"))
|
||||
else
|
||||
pure $ attributeExtension.addEntry env (AttributeExtensionOLeanEntry.builder builderId args, attrImpl)
|
||||
let attrImpl ← mkAttributeImplOfBuilder builderId args
|
||||
if isAttribute env attrImpl.name then
|
||||
throw (IO.userError ("invalid builtin attribute declaration, '" ++ toString attrImpl.name ++ "' has already been used"))
|
||||
else
|
||||
pure $ attributeExtension.addEntry env (AttributeExtensionOLeanEntry.builder builderId args, attrImpl)
|
||||
|
||||
namespace Environment
|
||||
|
||||
|
|
@ -204,9 +188,9 @@ namespace Environment
|
|||
TODO: delete after we remove old frontend. -/
|
||||
@[export lean_add_attribute]
|
||||
def addAttributeOld (env : Environment) (decl : Name) (attrName : Name) (args : Syntax := Syntax.missing) (persistent := true) : IO Environment := do
|
||||
attr ← IO.ofExcept $ getAttributeImpl env attrName;
|
||||
(_, s) ← ((attr.add decl args persistent).run { currNamespace := Name.anonymous, openDecls := [] }).toIO {} { env := env };
|
||||
pure s.env
|
||||
let attr ← IO.ofExcept $ getAttributeImpl env attrName
|
||||
let (_, s) ← ((attr.add decl args persistent).run { currNamespace := Name.anonymous, openDecls := [] }).toIO {} { env := env }
|
||||
pure s.env
|
||||
|
||||
/-
|
||||
/- Add a scoped attribute `attr` to declaration `decl` with arguments `args` and scope `decl.getPrefix`.
|
||||
|
|
@ -250,25 +234,25 @@ attrs.foldlM (fun env attr => attr.activateScoped env scope) env
|
|||
It activates scoped attributes in the new resulting namespace. -/
|
||||
@[export lean_push_scope]
|
||||
def pushScope (env : Environment) (header : Name) (isNamespace : Bool) : IO Environment := do
|
||||
let env := Lean.TODELETE.pushScopeCore env header isNamespace;
|
||||
-- attrs ← attributeArrayRef.get;
|
||||
-- attrs.foldlM (fun env attr => do env ← attr.pushScope env; if isNamespace then attr.activateScoped env ns else pure env) env
|
||||
pure env
|
||||
let env := Lean.TODELETE.pushScopeCore env header isNamespace
|
||||
-- attrs ← attributeArrayRef.get;
|
||||
-- attrs.foldlM (fun env attr => do env ← attr.pushScope env; if isNamespace then attr.activateScoped env ns else pure env) env
|
||||
pure env
|
||||
|
||||
/- We use this function to implement commands `end foo` for closing namespaces and sections. -/
|
||||
@[export lean_pop_scope]
|
||||
def popScope (env : Environment) : IO Environment := do
|
||||
let env := Lean.TODELETE.popScopeCore env;
|
||||
-- attrs ← attributeArrayRef.get;
|
||||
-- attrs.foldlM (fun env attr => attr.popScope env) env
|
||||
pure env
|
||||
let env := Lean.TODELETE.popScopeCore env
|
||||
-- attrs ← attributeArrayRef.get;
|
||||
-- attrs.foldlM (fun env attr => attr.popScope env) env
|
||||
pure env
|
||||
|
||||
end Environment
|
||||
|
||||
def addAttribute (decl : Name) (attrName : Name) (args : Syntax) (persistent : Bool := true) : AttrM Unit := do
|
||||
env ← getEnv;
|
||||
attr ← ofExcept $ getAttributeImpl env attrName;
|
||||
attr.add decl args persistent
|
||||
let env ← getEnv
|
||||
let attr ← ofExcept $ getAttributeImpl env attrName
|
||||
attr.add decl args persistent
|
||||
|
||||
|
||||
/--
|
||||
|
|
@ -280,44 +264,44 @@ attr.add decl args persistent
|
|||
They provide the predicate `tagAttr.hasTag env decl` which returns true iff declaration `decl`
|
||||
is tagged in the environment `env`. -/
|
||||
structure TagAttribute :=
|
||||
(attr : AttributeImpl)
|
||||
(ext : PersistentEnvExtension Name Name NameSet)
|
||||
(attr : AttributeImpl)
|
||||
(ext : PersistentEnvExtension Name Name NameSet)
|
||||
|
||||
def registerTagAttribute (name : Name) (descr : String) (validate : Name → AttrM Unit := fun _ => pure ()) : IO TagAttribute := do
|
||||
ext : PersistentEnvExtension Name Name NameSet ← registerPersistentEnvExtension {
|
||||
name := name,
|
||||
mkInitial := pure {},
|
||||
addImportedFn := fun _ _ => pure {},
|
||||
addEntryFn := fun (s : NameSet) n => s.insert n,
|
||||
exportEntriesFn := fun es =>
|
||||
let r : Array Name := es.fold (fun a e => a.push e) #[];
|
||||
r.qsort Name.quickLt,
|
||||
statsFn := fun s => "tag attribute" ++ Format.line ++ "number of local entries: " ++ format s.size
|
||||
};
|
||||
let attrImpl : AttributeImpl := {
|
||||
name := name,
|
||||
descr := descr,
|
||||
add := fun decl args persistent => do
|
||||
when args.hasArgs $ throwError ("invalid attribute '" ++ toString name ++ "', unexpected argument");
|
||||
unless persistent $ throwError ("invalid attribute '" ++ toString name ++ "', must be persistent");
|
||||
env ← getEnv;
|
||||
unless (env.getModuleIdxFor? decl).isNone $
|
||||
throwError ("invalid attribute '" ++ toString name ++ "', declaration is in an imported module");
|
||||
validate decl;
|
||||
env ← getEnv;
|
||||
setEnv $ ext.addEntry env decl
|
||||
};
|
||||
registerBuiltinAttribute attrImpl;
|
||||
pure { attr := attrImpl, ext := ext }
|
||||
let ext : PersistentEnvExtension Name Name NameSet ← registerPersistentEnvExtension {
|
||||
name := name,
|
||||
mkInitial := pure {},
|
||||
addImportedFn := fun _ _ => pure {},
|
||||
addEntryFn := fun (s : NameSet) n => s.insert n,
|
||||
exportEntriesFn := fun es =>
|
||||
let r : Array Name := es.fold (fun a e => a.push e) #[]
|
||||
r.qsort Name.quickLt,
|
||||
statsFn := fun s => "tag attribute" ++ Format.line ++ "number of local entries: " ++ format s.size
|
||||
}
|
||||
let attrImpl : AttributeImpl := {
|
||||
name := name,
|
||||
descr := descr,
|
||||
add := fun decl args persistent => do
|
||||
if args.hasArgs then throwError! "invalid attribute '{name}', unexpected argument"
|
||||
unless persistent do throwError! "invalid attribute '{name}', must be persistent"
|
||||
let env ← getEnv
|
||||
unless (env.getModuleIdxFor? decl).isNone do
|
||||
throwError! "invalid attribute '{name}', declaration is in an imported module"
|
||||
validate decl
|
||||
let env ← getEnv
|
||||
setEnv $ ext.addEntry env decl
|
||||
}
|
||||
registerBuiltinAttribute attrImpl
|
||||
pure { attr := attrImpl, ext := ext }
|
||||
|
||||
namespace TagAttribute
|
||||
|
||||
instance : Inhabited TagAttribute := ⟨{attr := arbitrary _, ext := arbitrary _}⟩
|
||||
|
||||
def hasTag (attr : TagAttribute) (env : Environment) (decl : Name) : Bool :=
|
||||
match env.getModuleIdxFor? decl with
|
||||
| some modIdx => (attr.ext.getModuleEntries env modIdx).binSearchContains decl Name.quickLt
|
||||
| none => (attr.ext.getState env).contains decl
|
||||
match env.getModuleIdxFor? decl with
|
||||
| some modIdx => (attr.ext.getModuleEntries env modIdx).binSearchContains decl Name.quickLt
|
||||
| none => (attr.ext.getState env).contains decl
|
||||
|
||||
end TagAttribute
|
||||
|
||||
|
|
@ -328,60 +312,60 @@ end TagAttribute
|
|||
They provide the function `pAttr.getParam env decl` which returns `some p` iff declaration `decl`
|
||||
contains the attribute `pAttr` with parameter `p`. -/
|
||||
structure ParametricAttribute (α : Type) :=
|
||||
(attr : AttributeImpl)
|
||||
(ext : PersistentEnvExtension (Name × α) (Name × α) (NameMap α))
|
||||
(attr : AttributeImpl)
|
||||
(ext : PersistentEnvExtension (Name × α) (Name × α) (NameMap α))
|
||||
|
||||
def registerParametricAttribute {α : Type} [Inhabited α] (name : Name) (descr : String)
|
||||
(getParam : Name → Syntax → AttrM α)
|
||||
(afterSet : Name → α → AttrM Unit := fun env _ _ => pure ())
|
||||
(appTime := AttributeApplicationTime.afterTypeChecking)
|
||||
: IO (ParametricAttribute α) := do
|
||||
ext : PersistentEnvExtension (Name × α) (Name × α) (NameMap α) ← registerPersistentEnvExtension {
|
||||
name := name,
|
||||
mkInitial := pure {},
|
||||
addImportedFn := fun _ _ => pure {},
|
||||
addEntryFn := fun (s : NameMap α) (p : Name × α) => s.insert p.1 p.2,
|
||||
exportEntriesFn := fun m =>
|
||||
let r : Array (Name × α) := m.fold (fun a n p => a.push (n, p)) #[];
|
||||
r.qsort (fun a b => Name.quickLt a.1 b.1),
|
||||
statsFn := fun s => "parametric attribute" ++ Format.line ++ "number of local entries: " ++ format s.size
|
||||
};
|
||||
let attrImpl : AttributeImpl := {
|
||||
name := name,
|
||||
descr := descr,
|
||||
applicationTime := appTime,
|
||||
add := fun decl args persistent => do
|
||||
unless persistent $ throwError ("invalid attribute '" ++ toString name ++ "', must be persistent");
|
||||
env ← getEnv;
|
||||
unless (env.getModuleIdxFor? decl).isNone $
|
||||
throwError ("invalid attribute '" ++ toString name ++ "', declaration is in an imported module");
|
||||
val ← getParam decl args;
|
||||
let env' := ext.addEntry env (decl, val);
|
||||
setEnv env';
|
||||
catch (afterSet decl val) (fun _ => setEnv env)
|
||||
};
|
||||
registerBuiltinAttribute attrImpl;
|
||||
pure { attr := attrImpl, ext := ext }
|
||||
let ext : PersistentEnvExtension (Name × α) (Name × α) (NameMap α) ← registerPersistentEnvExtension {
|
||||
name := name,
|
||||
mkInitial := pure {},
|
||||
addImportedFn := fun _ _ => pure {},
|
||||
addEntryFn := fun (s : NameMap α) (p : Name × α) => s.insert p.1 p.2,
|
||||
exportEntriesFn := fun m =>
|
||||
let r : Array (Name × α) := m.fold (fun a n p => a.push (n, p)) #[]
|
||||
r.qsort (fun a b => Name.quickLt a.1 b.1),
|
||||
statsFn := fun s => "parametric attribute" ++ Format.line ++ "number of local entries: " ++ format s.size
|
||||
}
|
||||
let attrImpl : AttributeImpl := {
|
||||
name := name,
|
||||
descr := descr,
|
||||
applicationTime := appTime,
|
||||
add := fun decl args persistent => do
|
||||
unless persistent do throwError! "invalid attribute '{name}', must be persistent"
|
||||
let env ← getEnv
|
||||
unless (env.getModuleIdxFor? decl).isNone do
|
||||
throwError! "invalid attribute '{name}', declaration is in an imported module"
|
||||
let val ← getParam decl args
|
||||
let env' := ext.addEntry env (decl, val)
|
||||
setEnv env'
|
||||
try afterSet decl val catch _ => setEnv env
|
||||
}
|
||||
registerBuiltinAttribute attrImpl
|
||||
pure { attr := attrImpl, ext := ext }
|
||||
|
||||
namespace ParametricAttribute
|
||||
|
||||
instance {α : Type} : Inhabited (ParametricAttribute α) := ⟨{attr := arbitrary _, ext := arbitrary _}⟩
|
||||
|
||||
def getParam {α : Type} [Inhabited α] (attr : ParametricAttribute α) (env : Environment) (decl : Name) : Option α :=
|
||||
match env.getModuleIdxFor? decl with
|
||||
| some modIdx =>
|
||||
match (attr.ext.getModuleEntries env modIdx).binSearch (decl, arbitrary _) (fun a b => Name.quickLt a.1 b.1) with
|
||||
| some (_, val) => some val
|
||||
| none => none
|
||||
| none => (attr.ext.getState env).find? decl
|
||||
match env.getModuleIdxFor? decl with
|
||||
| some modIdx =>
|
||||
match (attr.ext.getModuleEntries env modIdx).binSearch (decl, arbitrary _) (fun a b => Name.quickLt a.1 b.1) with
|
||||
| some (_, val) => some val
|
||||
| none => none
|
||||
| none => (attr.ext.getState env).find? decl
|
||||
|
||||
def setParam {α : Type} (attr : ParametricAttribute α) (env : Environment) (decl : Name) (param : α) : Except String Environment :=
|
||||
if (env.getModuleIdxFor? decl).isSome then
|
||||
Except.error ("invalid '" ++ toString attr.attr.name ++ "'.setParam, declaration is in an imported module")
|
||||
else if ((attr.ext.getState env).find? decl).isSome then
|
||||
Except.error ("invalid '" ++ toString attr.attr.name ++ "'.setParam, attribute has already been set")
|
||||
else
|
||||
Except.ok (attr.ext.addEntry env (decl, param))
|
||||
if (env.getModuleIdxFor? decl).isSome then
|
||||
Except.error ("invalid '" ++ toString attr.attr.name ++ "'.setParam, declaration is in an imported module")
|
||||
else if ((attr.ext.getState env).find? decl).isSome then
|
||||
Except.error ("invalid '" ++ toString attr.attr.name ++ "'.setParam, attribute has already been set")
|
||||
else
|
||||
Except.ok (attr.ext.addEntry env (decl, param))
|
||||
|
||||
end ParametricAttribute
|
||||
|
||||
|
|
@ -390,57 +374,57 @@ end ParametricAttribute
|
|||
associating a value `a_i` with an declaration. `α` is usually an enumeration type.
|
||||
Note that whenever we register an `EnumAttributes`, we create `n` attributes, but only one environment extension. -/
|
||||
structure EnumAttributes (α : Type) :=
|
||||
(attrs : List AttributeImpl)
|
||||
(ext : PersistentEnvExtension (Name × α) (Name × α) (NameMap α))
|
||||
(attrs : List AttributeImpl)
|
||||
(ext : PersistentEnvExtension (Name × α) (Name × α) (NameMap α))
|
||||
|
||||
def registerEnumAttributes {α : Type} [Inhabited α] (extName : Name) (attrDescrs : List (Name × String × α))
|
||||
(validate : Name → α → AttrM Unit := fun _ _ => pure ())
|
||||
(applicationTime := AttributeApplicationTime.afterTypeChecking) : IO (EnumAttributes α) := do
|
||||
ext : PersistentEnvExtension (Name × α) (Name × α) (NameMap α) ← registerPersistentEnvExtension {
|
||||
name := extName,
|
||||
mkInitial := pure {},
|
||||
addImportedFn := fun _ _ => pure {},
|
||||
addEntryFn := fun (s : NameMap α) (p : Name × α) => s.insert p.1 p.2,
|
||||
exportEntriesFn := fun m =>
|
||||
let r : Array (Name × α) := m.fold (fun a n p => a.push (n, p)) #[];
|
||||
r.qsort (fun a b => Name.quickLt a.1 b.1),
|
||||
statsFn := fun s => "enumeration attribute extension" ++ Format.line ++ "number of local entries: " ++ format s.size
|
||||
};
|
||||
let attrs := attrDescrs.map $ fun ⟨name, descr, val⟩ => {
|
||||
name := name,
|
||||
descr := descr,
|
||||
applicationTime := applicationTime,
|
||||
add := (fun decl args persistent => do
|
||||
unless persistent $ throwError ("invalid attribute '" ++ toString name ++ "', must be persistent");
|
||||
env ← getEnv;
|
||||
unless (env.getModuleIdxFor? decl).isNone $
|
||||
throwError ("invalid attribute '" ++ toString name ++ "', declaration is in an imported module");
|
||||
validate decl val;
|
||||
setEnv $ ext.addEntry env (decl, val))
|
||||
: AttributeImpl
|
||||
};
|
||||
attrs.forM registerBuiltinAttribute;
|
||||
pure { ext := ext, attrs := attrs }
|
||||
let ext : PersistentEnvExtension (Name × α) (Name × α) (NameMap α) ← registerPersistentEnvExtension {
|
||||
name := extName,
|
||||
mkInitial := pure {},
|
||||
addImportedFn := fun _ _ => pure {},
|
||||
addEntryFn := fun (s : NameMap α) (p : Name × α) => s.insert p.1 p.2,
|
||||
exportEntriesFn := fun m =>
|
||||
let r : Array (Name × α) := m.fold (fun a n p => a.push (n, p)) #[]
|
||||
r.qsort (fun a b => Name.quickLt a.1 b.1),
|
||||
statsFn := fun s => "enumeration attribute extension" ++ Format.line ++ "number of local entries: " ++ format s.size
|
||||
}
|
||||
let attrs := attrDescrs.map fun (name, descr, val) => {
|
||||
name := name,
|
||||
descr := descr,
|
||||
add := fun decl args persistent => do
|
||||
unless persistent do throwError! "invalid attribute '{name}', must be persistent"
|
||||
let env ← getEnv
|
||||
unless (env.getModuleIdxFor? decl).isNone do
|
||||
throwError! "invalid attribute '{name}', declaration is in an imported module"
|
||||
validate decl val
|
||||
setEnv $ ext.addEntry env (decl, val),
|
||||
applicationTime := applicationTime
|
||||
: AttributeImpl
|
||||
}
|
||||
attrs.forM registerBuiltinAttribute
|
||||
pure { ext := ext, attrs := attrs }
|
||||
|
||||
namespace EnumAttributes
|
||||
|
||||
instance {α : Type} : Inhabited (EnumAttributes α) := ⟨{attrs := [], ext := arbitrary _}⟩
|
||||
|
||||
def getValue {α : Type} [Inhabited α] (attr : EnumAttributes α) (env : Environment) (decl : Name) : Option α :=
|
||||
match env.getModuleIdxFor? decl with
|
||||
| some modIdx =>
|
||||
match (attr.ext.getModuleEntries env modIdx).binSearch (decl, arbitrary _) (fun a b => Name.quickLt a.1 b.1) with
|
||||
| some (_, val) => some val
|
||||
| none => none
|
||||
| none => (attr.ext.getState env).find? decl
|
||||
match env.getModuleIdxFor? decl with
|
||||
| some modIdx =>
|
||||
match (attr.ext.getModuleEntries env modIdx).binSearch (decl, arbitrary _) (fun a b => Name.quickLt a.1 b.1) with
|
||||
| some (_, val) => some val
|
||||
| none => none
|
||||
| none => (attr.ext.getState env).find? decl
|
||||
|
||||
def setValue {α : Type} (attrs : EnumAttributes α) (env : Environment) (decl : Name) (val : α) : Except String Environment :=
|
||||
if (env.getModuleIdxFor? decl).isSome then
|
||||
Except.error ("invalid '" ++ toString attrs.ext.name ++ "'.setValue, declaration is in an imported module")
|
||||
else if ((attrs.ext.getState env).find? decl).isSome then
|
||||
Except.error ("invalid '" ++ toString attrs.ext.name ++ "'.setValue, attribute has already been set")
|
||||
else
|
||||
Except.ok (attrs.ext.addEntry env (decl, val))
|
||||
if (env.getModuleIdxFor? decl).isSome then
|
||||
Except.error ("invalid '" ++ toString attrs.ext.name ++ "'.setValue, declaration is in an imported module")
|
||||
else if ((attrs.ext.getState env).find? decl).isSome then
|
||||
Except.error ("invalid '" ++ toString attrs.ext.name ++ "'.setValue, attribute has already been set")
|
||||
else
|
||||
Except.ok (attrs.ext.addEntry env (decl, val))
|
||||
|
||||
end EnumAttributes
|
||||
|
||||
|
|
@ -451,13 +435,13 @@ end EnumAttributes
|
|||
Remark: in the future, attributes should define their own parsers, and we should use `match_syntax` to
|
||||
decode the Syntax object. -/
|
||||
def attrParamSyntaxToIdentifier (s : Syntax) : Option Name :=
|
||||
match s with
|
||||
| Syntax.node k args =>
|
||||
if k == nullKind && args.size == 1 then match args.get! 0 with
|
||||
| Syntax.ident _ _ id _ => some id
|
||||
| _ => none
|
||||
else
|
||||
none
|
||||
| _ => none
|
||||
match s with
|
||||
| Syntax.node k args =>
|
||||
if k == nullKind && args.size == 1 then match args.get! 0 with
|
||||
| Syntax.ident _ _ id _ => some id
|
||||
| _ => none
|
||||
else
|
||||
none
|
||||
| _ => none
|
||||
|
||||
end Lean
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#lang lean4
|
||||
/-
|
||||
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
|
@ -15,95 +16,106 @@ namespace Lean
|
|||
namespace Core
|
||||
|
||||
structure State :=
|
||||
(env : Environment)
|
||||
(nextMacroScope : MacroScope := firstFrontendMacroScope + 1)
|
||||
(ngen : NameGenerator := {})
|
||||
(traceState : TraceState := {})
|
||||
(env : Environment)
|
||||
(nextMacroScope : MacroScope := firstFrontendMacroScope + 1)
|
||||
(ngen : NameGenerator := {})
|
||||
(traceState : TraceState := {})
|
||||
|
||||
instance State.inhabited : Inhabited State := ⟨{ env := arbitrary _ }⟩
|
||||
instance : Inhabited State := ⟨{ env := arbitrary _ }⟩
|
||||
|
||||
structure Context :=
|
||||
(options : Options := {})
|
||||
(currRecDepth : Nat := 0)
|
||||
(maxRecDepth : Nat := 1000)
|
||||
(ref : Syntax := Syntax.missing)
|
||||
(options : Options := {})
|
||||
(currRecDepth : Nat := 0)
|
||||
(maxRecDepth : Nat := 1000)
|
||||
(ref : Syntax := Syntax.missing)
|
||||
|
||||
abbrev CoreM := ReaderT Context $ StateRefT State $ EIO Exception
|
||||
|
||||
instance CoreM.inhabited {α} : Inhabited (CoreM α) :=
|
||||
⟨fun _ _ => throw $ arbitrary _⟩
|
||||
instance {α} : Inhabited (CoreM α) := ⟨fun _ _ => throw $ arbitrary _⟩
|
||||
|
||||
instance : Ref CoreM :=
|
||||
{ getRef := do ctx ← read; pure ctx.ref,
|
||||
withRef := fun α ref x => withReader (fun ctx => { ctx with ref := ref }) x }
|
||||
instance : Ref CoreM := {
|
||||
getRef := do let ctx ← read; pure ctx.ref,
|
||||
withRef := fun ref x => withReader (fun ctx => { ctx with ref := ref }) x
|
||||
}
|
||||
|
||||
instance : MonadEnv CoreM :=
|
||||
{ getEnv := do s ← get; pure s.env,
|
||||
modifyEnv := fun f => modify fun s => { s with env := f s.env } }
|
||||
instance : MonadEnv CoreM := {
|
||||
getEnv := do pure (← get).env,
|
||||
modifyEnv := fun f => modify fun s => { s with env := f s.env }
|
||||
}
|
||||
|
||||
instance : MonadOptions CoreM :=
|
||||
{ getOptions := do ctx ← read; pure ctx.options }
|
||||
instance : MonadOptions CoreM := {
|
||||
getOptions := do pure (← read).options
|
||||
}
|
||||
|
||||
instance : AddMessageContext CoreM :=
|
||||
{ addMessageContext := addMessageContextPartial }
|
||||
instance : AddMessageContext CoreM := {
|
||||
addMessageContext := addMessageContextPartial
|
||||
}
|
||||
|
||||
instance : MonadNameGenerator CoreM :=
|
||||
{ getNGen := do s ← get; pure s.ngen,
|
||||
instance : MonadNameGenerator CoreM := {
|
||||
getNGen := do pure (← get).ngen,
|
||||
setNGen := fun ngen => modify fun s => { s with ngen := ngen } }
|
||||
|
||||
instance : MonadRecDepth CoreM :=
|
||||
{ withRecDepth := fun α d x => withReader (fun ctx => { ctx with currRecDepth := d }) x,
|
||||
getRecDepth := do ctx ← read; pure ctx.currRecDepth,
|
||||
getMaxRecDepth := do ctx ← read; pure ctx.maxRecDepth }
|
||||
instance : MonadRecDepth CoreM := {
|
||||
withRecDepth := fun d x => withReader (fun ctx => { ctx with currRecDepth := d }) x,
|
||||
getRecDepth := do pure (← read).currRecDepth,
|
||||
getMaxRecDepth := do pure (← read).maxRecDepth
|
||||
}
|
||||
|
||||
@[inline] def liftIOCore {α} (x : IO α) : CoreM α := do
|
||||
ref ← getRef;
|
||||
liftM $ (adaptExcept (fun (err : IO.Error) => Exception.error ref (toString err)) x : EIO Exception α)
|
||||
let ref ← getRef
|
||||
liftM (adaptExcept (fun (err : IO.Error) => Exception.error ref (toString err)) x : EIO Exception α)
|
||||
|
||||
instance : MonadIO CoreM :=
|
||||
{ liftIO := @liftIOCore }
|
||||
instance : MonadIO CoreM := {
|
||||
liftIO := @liftIOCore
|
||||
}
|
||||
|
||||
instance : MonadTrace CoreM :=
|
||||
{ getTraceState := do s ← get; pure s.traceState,
|
||||
modifyTraceState := fun f => modify $ fun s => { s with traceState := f s.traceState } }
|
||||
instance : MonadTrace CoreM := {
|
||||
getTraceState := do pure (← get).traceState,
|
||||
modifyTraceState := fun f => modify fun s => { s with traceState := f s.traceState }
|
||||
}
|
||||
|
||||
private def mkFreshNameImp (n : Name) : CoreM Name := do
|
||||
fresh ← modifyGet fun s => (s.nextMacroScope, { s with nextMacroScope := s.nextMacroScope + 1 });
|
||||
env ← getEnv;
|
||||
pure $ addMacroScope env.mainModule n fresh
|
||||
let fresh ← modifyGet fun s => (s.nextMacroScope, { s with nextMacroScope := s.nextMacroScope + 1 });
|
||||
let env ← getEnv;
|
||||
pure $ addMacroScope env.mainModule n fresh
|
||||
|
||||
def mkFreshUserName {m} [MonadLiftT CoreM m] (n : Name) : m Name :=
|
||||
liftM $ mkFreshNameImp n
|
||||
liftM $ mkFreshNameImp n
|
||||
|
||||
@[inline] def CoreM.run {α} (x : CoreM α) (ctx : Context) (s : State) : EIO Exception (α × State) :=
|
||||
(x.run ctx).run s
|
||||
(x ctx).run s
|
||||
|
||||
@[inline] def CoreM.run' {α} (x : CoreM α) (ctx : Context) (s : State) : EIO Exception α :=
|
||||
Prod.fst <$> x.run ctx s
|
||||
Prod.fst <$> x.run ctx s
|
||||
|
||||
@[inline] def CoreM.toIO {α} (x : CoreM α) (ctx : Context) (s : State) : IO (α × State) := do
|
||||
e ← (x.run ctx s).toIO';
|
||||
match e with
|
||||
| Except.error (Exception.error _ msg) => do e ← msg.toString; throw $ IO.userError e
|
||||
| Except.error (Exception.internal id) => throw $ IO.userError $ "internal exception #" ++ toString id.idx
|
||||
| Except.ok a => pure a
|
||||
match (← (x.run ctx s).toIO') with
|
||||
| Except.error (Exception.error _ msg) => do let e ← msg.toString; throw $ IO.userError e
|
||||
| Except.error (Exception.internal id) => throw $ IO.userError $ "internal exception #" ++ toString id.idx
|
||||
| Except.ok a => pure a
|
||||
|
||||
instance hasEval {α} [MetaHasEval α] : MetaHasEval (CoreM α) :=
|
||||
⟨fun env opts x _ => do
|
||||
(a, s) ← (finally x printTraces).toIO { maxRecDepth := getMaxRecDepth opts, options := opts } { env := env};
|
||||
MetaHasEval.eval s.env opts a true /- hideUnit -/⟩
|
||||
instance {α} [MetaHasEval α] : MetaHasEval (CoreM α) := {
|
||||
eval := fun env opts x _ => do
|
||||
let x : CoreM α := do try x finally printTraces
|
||||
let (a, s) ← x.toIO { maxRecDepth := getMaxRecDepth opts, options := opts } { env := env}
|
||||
MetaHasEval.eval s.env opts a (hideUnit := true)
|
||||
}
|
||||
|
||||
end Core
|
||||
|
||||
export Core (CoreM mkFreshUserName)
|
||||
|
||||
@[inline] def catchInternalId {α} {m : Type → Type} [MonadExcept Exception m] (id : InternalExceptionId) (x : m α) (h : Exception → m α) : m α :=
|
||||
catch x fun ex => match ex with
|
||||
@[inline] def catchInternalId {α} {m : Type → Type} [Monad m] [MonadExcept Exception m] (id : InternalExceptionId) (x : m α) (h : Exception → m α) : m α := do
|
||||
try
|
||||
x
|
||||
catch ex => match ex with
|
||||
| Exception.error _ _ => throw ex
|
||||
| Exception.internal id' => if id == id' then h ex else throw ex
|
||||
|
||||
@[inline] def catchInternalIds {α} {m : Type → Type} [MonadExcept Exception m] (ids : List InternalExceptionId) (x : m α) (h : Exception → m α) : m α :=
|
||||
catch x fun ex => match ex with
|
||||
@[inline] def catchInternalIds {α} {m : Type → Type} [Monad m] [MonadExcept Exception m] (ids : List InternalExceptionId) (x : m α) (h : Exception → m α) : m α := do
|
||||
try
|
||||
x
|
||||
catch ex => match ex with
|
||||
| Exception.error _ _ => throw ex
|
||||
| Exception.internal id => if ids.contains id then h ex else throw ex
|
||||
|
||||
|
|
|
|||
|
|
@ -677,7 +677,7 @@ end Delaborator
|
|||
|
||||
/-- "Delaborate" the given term into surface-level syntax using the default and given subterm-specific options. -/
|
||||
def delab (currNamespace : Name) (openDecls : List OpenDecl) (e : Expr) (optionsPerPos : OptionsPerPos := {}) : MetaM Syntax := do
|
||||
opts ← getOptions;
|
||||
opts ← MonadOptions.getOptions;
|
||||
catchInternalId Delaborator.delabFailureId
|
||||
(Delaborator.delab.run { expr := e, defaultOptions := opts, optionsPerPos := optionsPerPos, currNamespace := currNamespace, openDecls := openDecls })
|
||||
(fun _ => unreachable!)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#lang lean4
|
||||
/-
|
||||
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
|
@ -11,102 +12,98 @@ namespace Lean
|
|||
|
||||
/- Exception type used in most Lean monads -/
|
||||
inductive Exception
|
||||
| error (ref : Syntax) (msg : MessageData)
|
||||
| internal (id : InternalExceptionId)
|
||||
| error (ref : Syntax) (msg : MessageData)
|
||||
| internal (id : InternalExceptionId)
|
||||
|
||||
def Exception.toMessageData : Exception → MessageData
|
||||
| Exception.error _ msg => msg
|
||||
| Exception.internal id => id.toString
|
||||
| Exception.error _ msg => msg
|
||||
| Exception.internal id => id.toString
|
||||
|
||||
def Exception.getRef : Exception → Syntax
|
||||
| Exception.error ref _ => ref
|
||||
| Exception.internal _ => Syntax.missing
|
||||
| Exception.error ref _ => ref
|
||||
| Exception.internal _ => Syntax.missing
|
||||
|
||||
instance Exception.inhabited : Inhabited Exception := ⟨Exception.error (arbitrary _) (arbitrary _)⟩
|
||||
instance : Inhabited Exception := ⟨Exception.error (arbitrary _) (arbitrary _)⟩
|
||||
|
||||
class Ref (m : Type → Type) :=
|
||||
(getRef : m Syntax)
|
||||
(withRef {α} : Syntax → m α → m α)
|
||||
(getRef : m Syntax)
|
||||
(withRef {α} : Syntax → m α → m α)
|
||||
|
||||
export Ref (getRef)
|
||||
|
||||
instance refTrans (m n : Type → Type) [Ref m] [MonadFunctor m m n n] [MonadLift m n] : Ref n :=
|
||||
{ getRef := liftM (getRef : m _),
|
||||
withRef := fun α ref x => monadMap (fun α => Ref.withRef ref : forall {α}, m α → m α) x }
|
||||
instance (m n : Type → Type) [Ref m] [MonadFunctor m m n n] [MonadLift m n] : Ref n := {
|
||||
getRef := liftM (getRef : m _),
|
||||
withRef := fun ref x => monadMap (m := m) (Ref.withRef ref) x
|
||||
}
|
||||
|
||||
def replaceRef (ref : Syntax) (oldRef : Syntax) : Syntax :=
|
||||
match ref.getPos with
|
||||
| some _ => ref
|
||||
| _ => oldRef
|
||||
match ref.getPos with
|
||||
| some _ => ref
|
||||
| _ => oldRef
|
||||
|
||||
@[inline] def withRef {m : Type → Type} [Monad m] [Ref m] {α} (ref : Syntax) (x : m α) : m α := do
|
||||
oldRef ← getRef;
|
||||
let ref := replaceRef ref oldRef;
|
||||
Ref.withRef ref x
|
||||
let oldRef ← getRef
|
||||
let ref := replaceRef ref oldRef
|
||||
Ref.withRef ref x
|
||||
|
||||
/- Similar to `AddMessageContext`, but for error messages.
|
||||
The default instance just uses `AddMessageContext`.
|
||||
In error messages, we may want to provide additional information (e.g., macro expansion stack),
|
||||
and refine the `(ref : Syntax)`. -/
|
||||
class AddErrorMessageContext (m : Type → Type) :=
|
||||
(add : Syntax → MessageData → m (Syntax × MessageData))
|
||||
(add : Syntax → MessageData → m (Syntax × MessageData))
|
||||
|
||||
instance addErrorMessageContextDefault (m : Type → Type) [AddMessageContext m] [Monad m] : AddErrorMessageContext m :=
|
||||
{ add := fun ref msg => do
|
||||
msg ← addMessageContext msg;
|
||||
pure (ref, msg) }
|
||||
instance addErrorMessageContextDefault (m : Type → Type) [AddMessageContext m] [Monad m] : AddErrorMessageContext m := {
|
||||
add := fun ref msg => do
|
||||
msg ← addMessageContext msg
|
||||
pure (ref, msg)
|
||||
}
|
||||
|
||||
section Methods
|
||||
|
||||
variables {m : Type → Type} [Monad m] [MonadExceptOf Exception m] [Ref m] [AddErrorMessageContext m]
|
||||
|
||||
def throwError {α} (msg : MessageData) : m α := do
|
||||
ref ← getRef;
|
||||
(ref, msg) ← AddErrorMessageContext.add ref msg;
|
||||
throw $ Exception.error ref msg
|
||||
let ref ← getRef
|
||||
let (ref, msg) ← AddErrorMessageContext.add ref msg
|
||||
throw $ Exception.error ref msg
|
||||
|
||||
def throwUnknownConstant {α} (constName : Name) : m α :=
|
||||
throwError ("unknown constant '" ++ mkConst constName ++ "'")
|
||||
throwError msg!"unknown constant '{mkConst constName}'"
|
||||
|
||||
def throwErrorAt {α} (ref : Syntax) (msg : MessageData) : m α := do
|
||||
withRef ref $ throwError msg
|
||||
withRef ref $ throwError msg
|
||||
|
||||
def ofExcept {ε α} [HasToString ε] (x : Except ε α) : m α :=
|
||||
match x with
|
||||
| Except.ok a => pure a
|
||||
| Except.error e => throwError $ toString e
|
||||
match x with
|
||||
| Except.ok a => pure a
|
||||
| Except.error e => throwError $ toString e
|
||||
|
||||
def throwKernelException {α} [MonadOptions m] (ex : KernelException) : m α := do
|
||||
opts ← getOptions;
|
||||
throwError $ ex.toMessageData opts
|
||||
throwError $ ex.toMessageData (← getOptions)
|
||||
|
||||
end Methods
|
||||
|
||||
class MonadRecDepth (m : Type → Type) :=
|
||||
(withRecDepth {α} : Nat → m α → m α)
|
||||
(getRecDepth : m Nat)
|
||||
(getMaxRecDepth : m Nat)
|
||||
(withRecDepth {α} : Nat → m α → m α)
|
||||
(getRecDepth : m Nat)
|
||||
(getMaxRecDepth : m Nat)
|
||||
|
||||
instance ReaderT.MonadRecDepth {ρ m} [Monad m] [MonadRecDepth m] : MonadRecDepth (ReaderT ρ m) :=
|
||||
{ withRecDepth := fun α d x ctx => MonadRecDepth.withRecDepth d (x ctx),
|
||||
getRecDepth := fun _ => MonadRecDepth.getRecDepth,
|
||||
getMaxRecDepth := fun _ => MonadRecDepth.getMaxRecDepth }
|
||||
instance {ρ m} [Monad m] [MonadRecDepth m] : MonadRecDepth (ReaderT ρ m) := {
|
||||
withRecDepth := fun d x ctx => MonadRecDepth.withRecDepth d (x ctx),
|
||||
getRecDepth := fun _ => MonadRecDepth.getRecDepth,
|
||||
getMaxRecDepth := fun _ => MonadRecDepth.getMaxRecDepth
|
||||
}
|
||||
|
||||
instance StateRefT.monadRecDepth {ω σ m} [Monad m] [MonadRecDepth m] : MonadRecDepth (StateRefT' ω σ m) :=
|
||||
inferInstanceAs (MonadRecDepth (ReaderT _ _))
|
||||
instance {ω σ m} [Monad m] [MonadRecDepth m] : MonadRecDepth (StateRefT' ω σ m) :=
|
||||
inferInstanceAs (MonadRecDepth (ReaderT _ _))
|
||||
|
||||
@[inline] def withIncRecDepth {α m} [Monad m] [MonadRecDepth m] [MonadExceptOf Exception m] [Ref m] [AddErrorMessageContext m]
|
||||
(x : m α) : m α := do
|
||||
curr ← MonadRecDepth.getRecDepth;
|
||||
max ← MonadRecDepth.getMaxRecDepth;
|
||||
when (curr == max) $ throwError maxRecDepthErrorMessage;
|
||||
MonadRecDepth.withRecDepth (curr+1) x
|
||||
|
||||
end Lean
|
||||
|
||||
new_frontend
|
||||
|
||||
namespace Lean
|
||||
let curr ← MonadRecDepth.getRecDepth
|
||||
let max ← MonadRecDepth.getMaxRecDepth
|
||||
if curr == max then throwError maxRecDepthErrorMessage
|
||||
MonadRecDepth.withRecDepth (curr+1) x
|
||||
|
||||
syntax "throwError! " ((interpolatedStr term) <|> term) : term
|
||||
syntax "throwErrorAt! " term:max ((interpolatedStr term) <|> term) : term
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#lang lean4
|
||||
/-
|
||||
Copyright (c) 2018 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
|
@ -14,281 +15,271 @@ import Lean.Util.PPExt
|
|||
import Lean.Util.PPGoal
|
||||
|
||||
namespace Lean
|
||||
namespace MonadOptions end MonadOptions -- hack for old frontend
|
||||
export MonadOptions (getOptions) -- hack for old frontend
|
||||
|
||||
def mkErrorStringWithPos (fileName : String) (line col : Nat) (msg : String) : String :=
|
||||
fileName ++ ":" ++ toString line ++ ":" ++ toString col ++ ": " ++ toString msg
|
||||
fileName ++ ":" ++ toString line ++ ":" ++ toString col ++ ": " ++ toString msg
|
||||
|
||||
inductive MessageSeverity
|
||||
| information | warning | error
|
||||
| information | warning | error
|
||||
|
||||
structure MessageDataContext :=
|
||||
(env : Environment) (mctx : MetavarContext) (lctx : LocalContext) (opts : Options)
|
||||
(env : Environment) (mctx : MetavarContext) (lctx : LocalContext) (opts : Options)
|
||||
|
||||
structure NamingContext :=
|
||||
(currNamespace : Name) (openDecls : List OpenDecl)
|
||||
(currNamespace : Name) (openDecls : List OpenDecl)
|
||||
|
||||
/- Structure message data. We use it for reporting errors, trace messages, etc. -/
|
||||
inductive MessageData
|
||||
| ofFormat : Format → MessageData
|
||||
| ofSyntax : Syntax → MessageData
|
||||
| ofExpr : Expr → MessageData
|
||||
| ofLevel : Level → MessageData
|
||||
| ofName : Name → MessageData
|
||||
| ofGoal : MVarId → MessageData
|
||||
/- `withContext ctx d` specifies the pretty printing context `(env, mctx, lctx, opts)` for the nested expressions in `d`. -/
|
||||
| withContext : MessageDataContext → MessageData → MessageData
|
||||
| withNamingContext : NamingContext → MessageData → MessageData
|
||||
/- Lifted `Format.nest` -/
|
||||
| nest : Nat → MessageData → MessageData
|
||||
/- Lifted `Format.group` -/
|
||||
| group : MessageData → MessageData
|
||||
/- Lifted `Format.compose` -/
|
||||
| compose : MessageData → MessageData → MessageData
|
||||
/- Tagged sections. `Name` should be viewed as a "kind", and is used by `MessageData` inspector functions.
|
||||
Example: an inspector that tries to find "definitional equality failures" may look for the tag "DefEqFailure". -/
|
||||
| tagged : Name → MessageData → MessageData
|
||||
| node : Array MessageData → MessageData
|
||||
| ofFormat : Format → MessageData
|
||||
| ofSyntax : Syntax → MessageData
|
||||
| ofExpr : Expr → MessageData
|
||||
| ofLevel : Level → MessageData
|
||||
| ofName : Name → MessageData
|
||||
| ofGoal : MVarId → MessageData
|
||||
/- `withContext ctx d` specifies the pretty printing context `(env, mctx, lctx, opts)` for the nested expressions in `d`. -/
|
||||
| withContext : MessageDataContext → MessageData → MessageData
|
||||
| withNamingContext : NamingContext → MessageData → MessageData
|
||||
/- Lifted `Format.nest` -/
|
||||
| nest : Nat → MessageData → MessageData
|
||||
/- Lifted `Format.group` -/
|
||||
| group : MessageData → MessageData
|
||||
/- Lifted `Format.compose` -/
|
||||
| compose : MessageData → MessageData → MessageData
|
||||
/- Tagged sections. `Name` should be viewed as a "kind", and is used by `MessageData` inspector functions.
|
||||
Example: an inspector that tries to find "definitional equality failures" may look for the tag "DefEqFailure". -/
|
||||
| tagged : Name → MessageData → MessageData
|
||||
| node : Array MessageData → MessageData
|
||||
|
||||
namespace MessageData
|
||||
|
||||
def nil : MessageData :=
|
||||
ofFormat Format.nil
|
||||
ofFormat Format.nil
|
||||
|
||||
def isNil : MessageData → Bool
|
||||
| ofFormat Format.nil => true
|
||||
| _ => false
|
||||
| ofFormat Format.nil => true
|
||||
| _ => false
|
||||
|
||||
def isNest : MessageData → Bool
|
||||
| nest _ _ => true
|
||||
| _ => false
|
||||
| nest _ _ => true
|
||||
| _ => false
|
||||
|
||||
instance : Inhabited MessageData := ⟨MessageData.ofFormat (arbitrary _)⟩
|
||||
|
||||
def mkPPContext (nCtx : NamingContext) (ctx : MessageDataContext) : PPContext :=
|
||||
{ env := ctx.env, mctx := ctx.mctx, lctx := ctx.lctx, opts := ctx.opts,
|
||||
currNamespace := nCtx.currNamespace, openDecls := nCtx.openDecls }
|
||||
def mkPPContext (nCtx : NamingContext) (ctx : MessageDataContext) : PPContext := {
|
||||
env := ctx.env, mctx := ctx.mctx, lctx := ctx.lctx, opts := ctx.opts,
|
||||
currNamespace := nCtx.currNamespace, openDecls := nCtx.openDecls
|
||||
}
|
||||
|
||||
partial def formatAux : NamingContext → Option MessageDataContext → MessageData → IO Format
|
||||
| _, _, ofFormat fmt => pure fmt
|
||||
| _, _, ofLevel u => pure $ fmt u
|
||||
| _, _, ofName n => pure $ fmt n
|
||||
| nCtx, some ctx, ofSyntax s => ppTerm (mkPPContext nCtx ctx) s -- HACK: might not be a term
|
||||
| _, none, ofSyntax s => pure $ s.formatStx
|
||||
| _, none, ofExpr e => pure $ format (toString e)
|
||||
| nCtx, some ctx, ofExpr e => ppExpr (mkPPContext nCtx ctx) e
|
||||
| _, none, ofGoal mvarId => pure $ "goal " ++ format (mkMVar mvarId)
|
||||
| nCtx, some ctx, ofGoal mvarId => ppGoal (mkPPContext nCtx ctx) mvarId
|
||||
| nCtx, _, withContext ctx d => formatAux nCtx ctx d
|
||||
| _, ctx, withNamingContext nCtx d => formatAux nCtx ctx d
|
||||
| nCtx, ctx, tagged cls d => do d ← formatAux nCtx ctx d; pure $ Format.sbracket (format cls) ++ " " ++ d
|
||||
| nCtx, ctx, nest n d => Format.nest n <$> formatAux nCtx ctx d
|
||||
| nCtx, ctx, compose d₁ d₂ => do d₁ ← formatAux nCtx ctx d₁; d₂ ← formatAux nCtx ctx d₂; pure $ d₁ ++ d₂
|
||||
| nCtx, ctx, group d => Format.group <$> formatAux nCtx ctx d
|
||||
| nCtx, ctx, node ds => Format.nest 2 <$> ds.foldlM (fun r d => do d ← formatAux nCtx ctx d; pure $ r ++ Format.line ++ d) Format.nil
|
||||
| _, _, ofFormat fmt => pure fmt
|
||||
| _, _, ofLevel u => pure $ fmt u
|
||||
| _, _, ofName n => pure $ fmt n
|
||||
| nCtx, some ctx, ofSyntax s => ppTerm (mkPPContext nCtx ctx) s -- HACK: might not be a term
|
||||
| _, none, ofSyntax s => pure $ s.formatStx
|
||||
| _, none, ofExpr e => pure $ format (toString e)
|
||||
| nCtx, some ctx, ofExpr e => ppExpr (mkPPContext nCtx ctx) e
|
||||
| _, none, ofGoal mvarId => pure $ "goal " ++ format (mkMVar mvarId)
|
||||
| nCtx, some ctx, ofGoal mvarId => ppGoal (mkPPContext nCtx ctx) mvarId
|
||||
| nCtx, _, withContext ctx d => formatAux nCtx ctx d
|
||||
| _, ctx, withNamingContext nCtx d => formatAux nCtx ctx d
|
||||
| nCtx, ctx, tagged cls d => do let d ← formatAux nCtx ctx d; pure $ Format.sbracket (format cls) ++ " " ++ d
|
||||
| nCtx, ctx, nest n d => Format.nest n <$> formatAux nCtx ctx d
|
||||
| nCtx, ctx, compose d₁ d₂ => do let d₁ ← formatAux nCtx ctx d₁; let d₂ ← formatAux nCtx ctx d₂; pure $ d₁ ++ d₂
|
||||
| nCtx, ctx, group d => Format.group <$> formatAux nCtx ctx d
|
||||
| nCtx, ctx, node ds => Format.nest 2 <$> ds.foldlM (fun r d => do let d ← formatAux nCtx ctx d; pure $ r ++ Format.line ++ d) Format.nil
|
||||
|
||||
protected def format (msgData : MessageData) : IO Format :=
|
||||
formatAux { currNamespace := Name.anonymous, openDecls := [] } none msgData
|
||||
formatAux { currNamespace := Name.anonymous, openDecls := [] } none msgData
|
||||
|
||||
protected def toString (msgData : MessageData) : IO String := do
|
||||
fmt ← msgData.format;
|
||||
pure $ toString fmt
|
||||
let fmt ← msgData.format
|
||||
pure $ toString fmt
|
||||
|
||||
instance : HasAppend MessageData := ⟨compose⟩
|
||||
|
||||
instance hasCoeOfFormat : HasCoe Format MessageData := ⟨ofFormat⟩
|
||||
instance hasCoeOfLevel : HasCoe Level MessageData := ⟨ofLevel⟩
|
||||
instance hasCoeOfExpr : HasCoe Expr MessageData := ⟨ofExpr⟩
|
||||
instance hasCoeOfName : HasCoe Name MessageData := ⟨ofName⟩
|
||||
instance hasCoeOfSyntax : HasCoe Syntax MessageData := ⟨ofSyntax⟩
|
||||
instance hasCoeOfOptExpr : HasCoe (Option Expr) MessageData :=
|
||||
⟨fun o => match o with | none => "none" | some e => ofExpr e⟩
|
||||
instance : Coe String MessageData := ⟨ofFormat ∘ format⟩
|
||||
instance : Coe Format MessageData := ⟨ofFormat⟩
|
||||
instance : Coe Level MessageData := ⟨ofLevel⟩
|
||||
instance : Coe Expr MessageData := ⟨ofExpr⟩
|
||||
instance : Coe Name MessageData := ⟨ofName⟩
|
||||
instance : Coe Syntax MessageData := ⟨ofSyntax⟩
|
||||
instance : Coe (Option Expr) MessageData := ⟨fun o => match o with | none => "none" | some e => ofExpr e⟩
|
||||
|
||||
instance coeOfString : Coe String MessageData := ⟨ofFormat ∘ format⟩
|
||||
instance coeOfFormat : Coe Format MessageData := ⟨ofFormat⟩
|
||||
instance coeOfLevel : Coe Level MessageData := ⟨ofLevel⟩
|
||||
instance coeOfExpr : Coe Expr MessageData := ⟨ofExpr⟩
|
||||
instance coeOfName : Coe Name MessageData := ⟨ofName⟩
|
||||
instance coeOfSyntax : Coe Syntax MessageData := ⟨ofSyntax⟩
|
||||
instance coeOfOptExpr : Coe (Option Expr) MessageData :=
|
||||
⟨fun o => match o with | none => "none" | some e => ofExpr e⟩
|
||||
-- TODO: delete
|
||||
instance : HasCoe Format MessageData := ⟨ofFormat⟩
|
||||
instance : HasCoe Level MessageData := ⟨ofLevel⟩
|
||||
instance : HasCoe Expr MessageData := ⟨ofExpr⟩
|
||||
instance : HasCoe Name MessageData := ⟨ofName⟩
|
||||
instance : HasCoe Syntax MessageData := ⟨ofSyntax⟩
|
||||
instance : HasCoe (Option Expr) MessageData := ⟨fun o => match o with | none => "none" | some e => ofExpr e⟩
|
||||
|
||||
partial def arrayExpr.toMessageData (es : Array Expr) : Nat → MessageData → MessageData
|
||||
| i, acc =>
|
||||
|
||||
partial def arrayExpr.toMessageData (es : Array Expr) (i : Nat) (acc : MessageData) : MessageData :=
|
||||
if h : i < es.size then
|
||||
let e := es.get ⟨i, h⟩;
|
||||
let acc := if i == 0 then acc ++ ofExpr e else acc ++ ", " ++ ofExpr e;
|
||||
arrayExpr.toMessageData (i+1) acc
|
||||
toMessageData es (i+1) acc
|
||||
else
|
||||
acc ++ "]"
|
||||
|
||||
instance hasCoeOfArrayExpr : HasCoe (Array Expr) MessageData := ⟨fun es => arrayExpr.toMessageData es 0 "#["⟩
|
||||
|
||||
instance coeOfArrayExpr : Coe (Array Expr) MessageData := ⟨fun es => arrayExpr.toMessageData es 0 "#["⟩
|
||||
instance : HasCoe (Array Expr) MessageData := ⟨fun es => arrayExpr.toMessageData es 0 "#["⟩
|
||||
instance : Coe (Array Expr) MessageData := ⟨fun es => arrayExpr.toMessageData es 0 "#["⟩
|
||||
|
||||
def bracket (l : String) (f : MessageData) (r : String) : MessageData := group (nest l.length $ l ++ f ++ r)
|
||||
def paren (f : MessageData) : MessageData := bracket "(" f ")"
|
||||
def sbracket (f : MessageData) : MessageData := bracket "[" f "]"
|
||||
def joinSep : List MessageData → MessageData → MessageData
|
||||
| [], sep => Format.nil
|
||||
| [a], sep => a
|
||||
| a::as, sep => a ++ sep ++ joinSep as sep
|
||||
| [], sep => Format.nil
|
||||
| [a], sep => a
|
||||
| a::as, sep => a ++ sep ++ joinSep as sep
|
||||
def ofList: List MessageData → MessageData
|
||||
| [] => "[]"
|
||||
| xs => sbracket $ joinSep xs ("," ++ Format.line)
|
||||
| [] => "[]"
|
||||
| xs => sbracket $ joinSep xs ("," ++ Format.line)
|
||||
def ofArray (msgs : Array MessageData) : MessageData :=
|
||||
ofList msgs.toList
|
||||
ofList msgs.toList
|
||||
|
||||
instance hasCoeOfList : HasCoe (List MessageData) MessageData := ⟨ofList⟩
|
||||
instance hasCoeOfListExpr : HasCoe (List Expr) MessageData := ⟨fun es => ofList $ es.map ofExpr⟩
|
||||
instance : HasCoe (List MessageData) MessageData := ⟨ofList⟩
|
||||
instance : HasCoe (List Expr) MessageData := ⟨fun es => ofList $ es.map ofExpr⟩
|
||||
|
||||
instance coeOfList : Coe (List MessageData) MessageData := ⟨ofList⟩
|
||||
instance coeOfListExpr : Coe (List Expr) MessageData := ⟨fun es => ofList $ es.map ofExpr⟩
|
||||
instance : Coe (List MessageData) MessageData := ⟨ofList⟩
|
||||
instance : Coe (List Expr) MessageData := ⟨fun es => ofList $ es.map ofExpr⟩
|
||||
|
||||
end MessageData
|
||||
|
||||
structure Message :=
|
||||
(fileName : String)
|
||||
(pos : Position)
|
||||
(endPos : Option Position := none)
|
||||
(severity : MessageSeverity := MessageSeverity.error)
|
||||
(caption : String := "")
|
||||
(data : MessageData)
|
||||
(fileName : String)
|
||||
(pos : Position)
|
||||
(endPos : Option Position := none)
|
||||
(severity : MessageSeverity := MessageSeverity.error)
|
||||
(caption : String := "")
|
||||
(data : MessageData)
|
||||
|
||||
@[export lean_mk_message]
|
||||
def mkMessageEx (fileName : String) (pos : Position) (endPos : Option Position) (severity : MessageSeverity) (caption : String) (text : String) : Message :=
|
||||
{ fileName := fileName, pos := pos, endPos := endPos, severity := severity, caption := caption, data := text }
|
||||
{ fileName := fileName, pos := pos, endPos := endPos, severity := severity, caption := caption, data := text }
|
||||
namespace Message
|
||||
|
||||
protected def toString (msg : Message) : IO String := do
|
||||
str ← msg.data.toString;
|
||||
pure $ mkErrorStringWithPos msg.fileName msg.pos.line msg.pos.column
|
||||
((match msg.severity with
|
||||
| MessageSeverity.information => ""
|
||||
| MessageSeverity.warning => "warning: "
|
||||
| MessageSeverity.error => "error: ") ++
|
||||
(if msg.caption == "" then "" else msg.caption ++ ":\n") ++ str)
|
||||
let str ← msg.data.toString
|
||||
pure $ mkErrorStringWithPos msg.fileName msg.pos.line msg.pos.column
|
||||
((match msg.severity with
|
||||
| MessageSeverity.information => ""
|
||||
| MessageSeverity.warning => "warning: "
|
||||
| MessageSeverity.error => "error: ") ++
|
||||
(if msg.caption == "" then "" else msg.caption ++ ":\n") ++ str)
|
||||
|
||||
instance : Inhabited Message :=
|
||||
⟨{ fileName := "", pos := ⟨0, 1⟩, data := arbitrary _}⟩
|
||||
instance : Inhabited Message := ⟨{ fileName := "", pos := ⟨0, 1⟩, data := arbitrary _}⟩
|
||||
|
||||
@[export lean_message_pos] def getPostEx (msg : Message) : Position := msg.pos
|
||||
@[export lean_message_severity] def getSeverityEx (msg : Message) : MessageSeverity := msg.severity
|
||||
@[export lean_message_string] unsafe def getMessageStringEx (msg : Message) : String :=
|
||||
match unsafeIO (msg.data.toString) with -- hack: this is going to be deleted
|
||||
| Except.ok msg => msg
|
||||
| Except.error e => "error formatting message: " ++ toString e
|
||||
match unsafeIO (msg.data.toString) with -- hack: this is going to be deleted
|
||||
| Except.ok msg => msg
|
||||
| Except.error e => "error formatting message: " ++ toString e
|
||||
|
||||
end Message
|
||||
|
||||
structure MessageLog :=
|
||||
(msgs : Std.PersistentArray Message := {})
|
||||
(msgs : Std.PersistentArray Message := {})
|
||||
|
||||
namespace MessageLog
|
||||
def empty : MessageLog := ⟨{}⟩
|
||||
|
||||
def isEmpty (log : MessageLog) : Bool :=
|
||||
log.msgs.isEmpty
|
||||
log.msgs.isEmpty
|
||||
|
||||
instance : Inhabited MessageLog := ⟨{}⟩
|
||||
|
||||
def add (msg : Message) (log : MessageLog) : MessageLog :=
|
||||
⟨log.msgs.push msg⟩
|
||||
⟨log.msgs.push msg⟩
|
||||
|
||||
protected def append (l₁ l₂ : MessageLog) : MessageLog :=
|
||||
⟨l₁.msgs ++ l₂.msgs⟩
|
||||
⟨l₁.msgs ++ l₂.msgs⟩
|
||||
|
||||
instance : HasAppend MessageLog :=
|
||||
⟨MessageLog.append⟩
|
||||
⟨MessageLog.append⟩
|
||||
|
||||
def hasErrors (log : MessageLog) : Bool :=
|
||||
log.msgs.any $ fun m => match m.severity with
|
||||
| MessageSeverity.error => true
|
||||
| _ => false
|
||||
log.msgs.any fun m => match m.severity with
|
||||
| MessageSeverity.error => true
|
||||
| _ => false
|
||||
|
||||
def errorsToWarnings (log : MessageLog) : MessageLog :=
|
||||
{ msgs := log.msgs.map (fun m => match m.severity with | MessageSeverity.error => { m with severity := MessageSeverity.warning } | _ => m) }
|
||||
{ msgs := log.msgs.map (fun m => match m.severity with | MessageSeverity.error => { m with severity := MessageSeverity.warning } | _ => m) }
|
||||
|
||||
def getInfoMessages (log : MessageLog) : MessageLog :=
|
||||
{ msgs := log.msgs.filter fun m => match m.severity with | MessageSeverity.information => true | _ => false }
|
||||
{ msgs := log.msgs.filter fun m => match m.severity with | MessageSeverity.information => true | _ => false }
|
||||
|
||||
def forM {m : Type → Type} [Monad m] (log : MessageLog) (f : Message → m Unit) : m Unit :=
|
||||
log.msgs.forM f
|
||||
log.msgs.forM f
|
||||
|
||||
def toList (log : MessageLog) : List Message :=
|
||||
(log.msgs.foldl (fun acc msg => msg :: acc) []).reverse
|
||||
(log.msgs.foldl (fun acc msg => msg :: acc) []).reverse
|
||||
|
||||
end MessageLog
|
||||
|
||||
def MessageData.nestD (msg : MessageData) : MessageData :=
|
||||
MessageData.nest 2 msg
|
||||
MessageData.nest 2 msg
|
||||
|
||||
def indentD (msg : MessageData) : MessageData :=
|
||||
MessageData.nestD (Format.line ++ msg)
|
||||
MessageData.nestD (Format.line ++ msg)
|
||||
|
||||
def indentExpr (e : Expr) : MessageData :=
|
||||
indentD e
|
||||
indentD e
|
||||
|
||||
namespace KernelException
|
||||
|
||||
private def mkCtx (env : Environment) (lctx : LocalContext) (opts : Options) (msg : MessageData) : MessageData :=
|
||||
MessageData.withContext { env := env, mctx := {}, lctx := lctx, opts := opts } msg
|
||||
MessageData.withContext { env := env, mctx := {}, lctx := lctx, opts := opts } msg
|
||||
|
||||
def toMessageData (e : KernelException) (opts : Options) : MessageData :=
|
||||
match e with
|
||||
| unknownConstant env constName => mkCtx env {} opts $ "(kernel) unknown constant " ++ constName
|
||||
| alreadyDeclared env constName => mkCtx env {} opts $ "(kernel) constant has already been declared " ++ constName
|
||||
| declTypeMismatch env decl givenType =>
|
||||
let process (n : Name) (expectedType : Expr) : MessageData :=
|
||||
"(kernel) declaration type mismatch " ++ n
|
||||
++ Format.line ++ "has type" ++ indentExpr givenType
|
||||
++ Format.line ++ "but it is expected to have type" ++ indentExpr expectedType;
|
||||
match decl with
|
||||
| Declaration.defnDecl { name := n, type := type, .. } => process n type
|
||||
| Declaration.thmDecl { name := n, type := type, .. } => process n type
|
||||
| _ => "(kernel) declaration type mismatch" -- TODO fix type checker, type mismatch for mutual decls does not have enough information
|
||||
| declHasMVars env constName _ => mkCtx env {} opts $ "(kernel) declaration has metavariables " ++ constName
|
||||
| declHasFVars env constName _ => mkCtx env {} opts $ "(kernel) declaration has free variables " ++ constName
|
||||
| funExpected env lctx e => mkCtx env lctx opts $ "(kernel) function expected" ++ indentExpr e
|
||||
| typeExpected env lctx e => mkCtx env lctx opts $ "(kernel) type expected" ++ indentExpr e
|
||||
| letTypeMismatch env lctx n _ _ => mkCtx env lctx opts $ "(kernel) let-declaration type mismatch " ++ n
|
||||
| exprTypeMismatch env lctx e _ => mkCtx env lctx opts $ "(kernel) type mismatch at " ++ indentExpr e
|
||||
| appTypeMismatch env lctx e fnType argType =>
|
||||
mkCtx env lctx opts $
|
||||
"application type mismatch" ++ indentExpr e
|
||||
++ Format.line ++ "argument has type" ++ indentExpr argType
|
||||
++ Format.line ++ "but function has type" ++ indentExpr fnType
|
||||
| invalidProj env lctx e => mkCtx env lctx opts $ "(kernel) invalid projection" ++ indentExpr e
|
||||
| other msg => "(kernel) " ++ msg
|
||||
match e with
|
||||
| unknownConstant env constName => mkCtx env {} opts $ "(kernel) unknown constant " ++ constName
|
||||
| alreadyDeclared env constName => mkCtx env {} opts $ "(kernel) constant has already been declared " ++ constName
|
||||
| declTypeMismatch env decl givenType =>
|
||||
let process (n : Name) (expectedType : Expr) : MessageData :=
|
||||
"(kernel) declaration type mismatch " ++ n
|
||||
++ Format.line ++ "has type" ++ indentExpr givenType
|
||||
++ Format.line ++ "but it is expected to have type" ++ indentExpr expectedType;
|
||||
match decl with
|
||||
| Declaration.defnDecl { name := n, type := type, .. } => process n type
|
||||
| Declaration.thmDecl { name := n, type := type, .. } => process n type
|
||||
| _ => "(kernel) declaration type mismatch" -- TODO fix type checker, type mismatch for mutual decls does not have enough information
|
||||
| declHasMVars env constName _ => mkCtx env {} opts $ "(kernel) declaration has metavariables " ++ constName
|
||||
| declHasFVars env constName _ => mkCtx env {} opts $ "(kernel) declaration has free variables " ++ constName
|
||||
| funExpected env lctx e => mkCtx env lctx opts $ "(kernel) function expected" ++ indentExpr e
|
||||
| typeExpected env lctx e => mkCtx env lctx opts $ "(kernel) type expected" ++ indentExpr e
|
||||
| letTypeMismatch env lctx n _ _ => mkCtx env lctx opts $ "(kernel) let-declaration type mismatch " ++ n
|
||||
| exprTypeMismatch env lctx e _ => mkCtx env lctx opts $ "(kernel) type mismatch at " ++ indentExpr e
|
||||
| appTypeMismatch env lctx e fnType argType =>
|
||||
mkCtx env lctx opts $
|
||||
"application type mismatch" ++ indentExpr e
|
||||
++ Format.line ++ "argument has type" ++ indentExpr argType
|
||||
++ Format.line ++ "but function has type" ++ indentExpr fnType
|
||||
| invalidProj env lctx e => mkCtx env lctx opts $ "(kernel) invalid projection" ++ indentExpr e
|
||||
| other msg => "(kernel) " ++ msg
|
||||
|
||||
end KernelException
|
||||
|
||||
class AddMessageContext (m : Type → Type) :=
|
||||
(addMessageContext : MessageData → m MessageData)
|
||||
(addMessageContext : MessageData → m MessageData)
|
||||
|
||||
export AddMessageContext (addMessageContext)
|
||||
|
||||
instance addMessageContextTrans (m n) [AddMessageContext m] [MonadLift m n] : AddMessageContext n :=
|
||||
{ addMessageContext := fun msg => liftM (addMessageContext msg : m _) }
|
||||
instance (m n) [AddMessageContext m] [MonadLift m n] : AddMessageContext n :=
|
||||
{ addMessageContext := fun msg => liftM (addMessageContext msg : m _) }
|
||||
|
||||
def addMessageContextPartial {m} [Monad m] [MonadEnv m] [MonadOptions m] (msgData : MessageData) : m MessageData := do
|
||||
env ← getEnv;
|
||||
opts ← getOptions;
|
||||
pure $ MessageData.withContext { env := env, mctx := {}, lctx := {}, opts := opts } msgData
|
||||
let env ← getEnv
|
||||
let opts ← getOptions
|
||||
pure $ MessageData.withContext { env := env, mctx := {}, lctx := {}, opts := opts } msgData
|
||||
|
||||
def addMessageContextFull {m} [Monad m] [MonadEnv m] [MonadMCtx m] [MonadLCtx m] [MonadOptions m] (msgData : MessageData) : m MessageData := do
|
||||
env ← getEnv;
|
||||
mctx ← getMCtx;
|
||||
lctx ← getLCtx;
|
||||
opts ← getOptions;
|
||||
pure $ MessageData.withContext { env := env, mctx := mctx, lctx := lctx, opts := opts } msgData
|
||||
|
||||
end Lean
|
||||
|
||||
new_frontend
|
||||
|
||||
namespace Lean
|
||||
let env ← getEnv
|
||||
let mctx ← getMCtx
|
||||
let lctx ← getLCtx
|
||||
let opts ← getOptions
|
||||
pure $ MessageData.withContext { env := env, mctx := mctx, lctx := lctx, opts := opts } msgData
|
||||
|
||||
class ToMessageData (α : Type) :=
|
||||
(toMessageData : α → MessageData)
|
||||
|
|
@ -296,9 +287,9 @@ class ToMessageData (α : Type) :=
|
|||
export ToMessageData (toMessageData)
|
||||
|
||||
def stringToMessageData (str : String) : MessageData :=
|
||||
let lines := str.split (· == '\n')
|
||||
let lines := lines.map (MessageData.ofFormat ∘ fmt)
|
||||
MessageData.joinSep lines (MessageData.ofFormat Format.line)
|
||||
let lines := str.split (· == '\n')
|
||||
let lines := lines.map (MessageData.ofFormat ∘ fmt)
|
||||
MessageData.joinSep lines (MessageData.ofFormat Format.line)
|
||||
|
||||
instance {α} [HasFormat α] : ToMessageData α := ⟨MessageData.ofFormat ∘ fmt⟩
|
||||
instance : ToMessageData Expr := ⟨MessageData.ofExpr⟩
|
||||
|
|
@ -316,9 +307,9 @@ instance : ToMessageData (Option Expr) := ⟨fun | none => "<not-available>" | s
|
|||
syntax:max "msg!" (interpolatedStr term) : term
|
||||
|
||||
macro_rules
|
||||
| `(msg! $interpStr) => do
|
||||
let chunks := interpStr.getArgs
|
||||
let r ← Lean.Syntax.expandInterpolatedStrChunks chunks (fun a b => `($a ++ $b)) (fun a => `(toMessageData $a))
|
||||
`(($r : MessageData))
|
||||
| `(msg! $interpStr) => do
|
||||
let chunks := interpStr.getArgs
|
||||
let r ← Lean.Syntax.expandInterpolatedStrChunks chunks (fun a b => `($a ++ $b)) (fun a => `(toMessageData $a))
|
||||
`(($r : MessageData))
|
||||
|
||||
end Lean
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#lang lean4
|
||||
/-
|
||||
Copyright (c) 2020 Sebastian Ullrich. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
|
@ -20,67 +21,65 @@ import Lean.ParserCompiler.Attribute
|
|||
import Lean.PrettyPrinter.Backtrack
|
||||
|
||||
namespace Lean
|
||||
namespace Syntax namespace MonadTraverser end MonadTraverser end Syntax -- Hack for old frontend
|
||||
namespace Parser end Parser -- Hack for old frontend
|
||||
namespace PrettyPrinter
|
||||
namespace Formatter
|
||||
|
||||
structure Context :=
|
||||
(options : Options)
|
||||
(table : Parser.TokenTable)
|
||||
(options : Options)
|
||||
(table : Parser.TokenTable)
|
||||
|
||||
structure State :=
|
||||
(stxTrav : Syntax.Traverser)
|
||||
-- Textual content of `stack` up to the first whitespace (not enclosed in an escaped ident). We assume that the textual
|
||||
-- content of `stack` is modified only by `pushText` and `pushLine`, so `leadWord` is adjusted there accordingly.
|
||||
(leadWord : String := "")
|
||||
-- Stack of generated Format objects, analogous to the Syntax stack in the parser.
|
||||
-- Note, however, that the stack is reversed because of the right-to-left traversal.
|
||||
(stack : Array Format := #[])
|
||||
(stxTrav : Syntax.Traverser)
|
||||
-- Textual content of `stack` up to the first whitespace (not enclosed in an escaped ident). We assume that the textual
|
||||
-- content of `stack` is modified only by `pushText` and `pushLine`, so `leadWord` is adjusted there accordingly.
|
||||
(leadWord : String := "")
|
||||
-- Stack of generated Format objects, analogous to the Syntax stack in the parser.
|
||||
-- Note, however, that the stack is reversed because of the right-to-left traversal.
|
||||
(stack : Array Format := #[])
|
||||
|
||||
end Formatter
|
||||
|
||||
abbrev FormatterM := ReaderT Formatter.Context $ StateRefT Formatter.State $ CoreM
|
||||
|
||||
@[inline] def FormatterM.orelse {α} (p₁ p₂ : FormatterM α) : FormatterM α := do
|
||||
s ← get;
|
||||
catchInternalId backtrackExceptionId
|
||||
p₁
|
||||
(fun _ => do set s; p₂)
|
||||
let s ← get
|
||||
catchInternalId backtrackExceptionId
|
||||
p₁
|
||||
(fun _ => do set s; p₂)
|
||||
|
||||
instance Formatter.orelse {α} : HasOrelse (FormatterM α) := ⟨FormatterM.orelse⟩
|
||||
|
||||
abbrev Formatter := FormatterM Unit
|
||||
|
||||
unsafe def mkFormatterAttribute : IO (KeyedDeclsAttribute Formatter) :=
|
||||
KeyedDeclsAttribute.init {
|
||||
builtinName := `builtinFormatter,
|
||||
name := `formatter,
|
||||
descr := "Register a formatter for a parser.
|
||||
KeyedDeclsAttribute.init {
|
||||
builtinName := `builtinFormatter,
|
||||
name := `formatter,
|
||||
descr := "Register a formatter for a parser.
|
||||
|
||||
[formatter k] registers a declaration of type `Lean.PrettyPrinter.Formatter` for the `SyntaxNodeKind` `k`.",
|
||||
valueTypeName := `Lean.PrettyPrinter.Formatter,
|
||||
evalKey := fun builtin args => do
|
||||
env ← getEnv;
|
||||
match attrParamSyntaxToIdentifier args with
|
||||
| some id =>
|
||||
-- `isValidSyntaxNodeKind` is updated only in the next stage for new `[builtin*Parser]`s, but we try to
|
||||
-- synthesize a formatter for it immediately, so we just check for a declaration in this case
|
||||
if (builtin && (env.find? id).isSome) || Parser.isValidSyntaxNodeKind env id then pure id
|
||||
else throwError ("invalid [formatter] argument, unknown syntax kind '" ++ toString id ++ "'")
|
||||
| none => throwError "invalid [formatter] argument, expected identifier"
|
||||
} `Lean.PrettyPrinter.formatterAttribute
|
||||
[formatter k] registers a declaration of type `Lean.PrettyPrinter.Formatter` for the `SyntaxNodeKind` `k`.",
|
||||
valueTypeName := `Lean.PrettyPrinter.Formatter,
|
||||
evalKey := fun builtin args => do
|
||||
let env ← getEnv
|
||||
match attrParamSyntaxToIdentifier args with
|
||||
| some id =>
|
||||
-- `isValidSyntaxNodeKind` is updated only in the next stage for new `[builtin*Parser]`s, but we try to
|
||||
-- synthesize a formatter for it immediately, so we just check for a declaration in this case
|
||||
if (builtin && (env.find? id).isSome) || Parser.isValidSyntaxNodeKind env id then pure id
|
||||
else throwError ("invalid [formatter] argument, unknown syntax kind '" ++ toString id ++ "'")
|
||||
| none => throwError "invalid [formatter] argument, expected identifier"
|
||||
} `Lean.PrettyPrinter.formatterAttribute
|
||||
@[builtinInit mkFormatterAttribute] constant formatterAttribute : KeyedDeclsAttribute Formatter := arbitrary _
|
||||
|
||||
unsafe def mkCombinatorFormatterAttribute : IO ParserCompiler.CombinatorAttribute :=
|
||||
ParserCompiler.registerCombinatorAttribute
|
||||
`combinatorFormatter
|
||||
"Register a formatter for a parser combinator.
|
||||
ParserCompiler.registerCombinatorAttribute
|
||||
`combinatorFormatter
|
||||
"Register a formatter for a parser combinator.
|
||||
|
||||
[combinatorFormatter c] registers a declaration of type `Lean.PrettyPrinter.Formatter` for the `Parser` declaration `c`.
|
||||
Note that, unlike with [formatter], this is not a node kind since combinators usually do not introduce their own node kinds.
|
||||
The tagged declaration may optionally accept parameters corresponding to (a prefix of) those of `c`, where `Parser` is replaced
|
||||
with `Formatter` in the parameter types."
|
||||
[combinatorFormatter c] registers a declaration of type `Lean.PrettyPrinter.Formatter` for the `Parser` declaration `c`.
|
||||
Note that, unlike with [formatter], this is not a node kind since combinators usually do not introduce their own node kinds.
|
||||
The tagged declaration may optionally accept parameters corresponding to (a prefix of) those of `c`, where `Parser` is replaced
|
||||
with `Formatter` in the parameter types."
|
||||
@[builtinInit mkCombinatorFormatterAttribute] constant combinatorFormatterAttribute : ParserCompiler.CombinatorAttribute := arbitrary _
|
||||
|
||||
namespace Formatter
|
||||
|
|
@ -94,352 +93,328 @@ throw $ Exception.internal backtrackExceptionId
|
|||
instance FormatterM.monadTraverser : Syntax.MonadTraverser FormatterM := ⟨{
|
||||
get := State.stxTrav <$> get,
|
||||
set := fun t => modify (fun st => { st with stxTrav := t }),
|
||||
modifyGet := fun _ f => modifyGet (fun st => let (a, t) := f st.stxTrav; (a, { st with stxTrav := t })) }⟩
|
||||
modifyGet := fun f => modifyGet (fun st => let (a, t) := f st.stxTrav; (a, { st with stxTrav := t }))
|
||||
}⟩
|
||||
|
||||
open Syntax.MonadTraverser
|
||||
|
||||
def getStack : FormatterM (Array Format) := do
|
||||
st ← get;
|
||||
pure st.stack
|
||||
let st ← get
|
||||
pure st.stack
|
||||
|
||||
def getStackSize : FormatterM Nat := do
|
||||
stack ← getStack;
|
||||
pure stack.size
|
||||
let stack ← getStack;
|
||||
pure stack.size
|
||||
|
||||
def setStack (stack : Array Format) : FormatterM Unit :=
|
||||
modify fun st => { st with stack := stack }
|
||||
modify fun st => { st with stack := stack }
|
||||
|
||||
def push (f : Format) : FormatterM Unit :=
|
||||
modify fun st => { st with stack := st.stack.push f }
|
||||
modify fun st => { st with stack := st.stack.push f }
|
||||
|
||||
def pushLine : FormatterM Unit := do
|
||||
push Format.line;
|
||||
modify fun st => { st with leadWord := "" }
|
||||
push Format.line;
|
||||
modify fun st => { st with leadWord := "" }
|
||||
|
||||
/-- Execute `x` at the right-most child of the current node, if any, then advance to the left. -/
|
||||
def visitArgs (x : FormatterM Unit) : FormatterM Unit := do
|
||||
stx ← getCur;
|
||||
when (stx.getArgs.size > 0) $
|
||||
goDown (stx.getArgs.size - 1) *> x <* goUp;
|
||||
goLeft
|
||||
let stx ← getCur
|
||||
if stx.getArgs.size > 0 then
|
||||
goDown (stx.getArgs.size - 1) *> x <* goUp
|
||||
goLeft
|
||||
|
||||
/-- Execute `x`, pass array of generated Format objects to `fn`, and push result. -/
|
||||
def fold (fn : Array Format → Format) (x : FormatterM Unit) : FormatterM Unit := do
|
||||
sp ← getStackSize;
|
||||
x;
|
||||
stack ← getStack;
|
||||
let f := fn $ stack.extract sp stack.size;
|
||||
setStack $ (stack.shrink sp).push f
|
||||
let sp ← getStackSize
|
||||
x
|
||||
let stack ← getStack
|
||||
let f := fn $ stack.extract sp stack.size
|
||||
setStack $ (stack.shrink sp).push f
|
||||
|
||||
/-- Execute `x` and concatenate generated Format objects. -/
|
||||
def concat (x : FormatterM Unit) : FormatterM Unit := do
|
||||
fold (Array.foldl (fun acc f => if acc.isNil then f else f ++ acc) Format.nil) x
|
||||
fold (Array.foldl (fun acc f => if acc.isNil then f else f ++ acc) Format.nil) x
|
||||
|
||||
def indent (x : Formatter) (indent : Option Int := none) : Formatter := do
|
||||
concat x;
|
||||
ctx ← read;
|
||||
let indent := indent.getD $ Format.getIndent ctx.options;
|
||||
modify fun st => { st with stack := st.stack.pop.push (Format.nest indent st.stack.back) }
|
||||
concat x
|
||||
let ctx ← read
|
||||
let indent := indent.getD $ Format.getIndent ctx.options
|
||||
modify fun st => { st with stack := st.stack.pop.push (Format.nest indent st.stack.back) }
|
||||
|
||||
def group (x : Formatter) : Formatter := do
|
||||
concat x;
|
||||
modify fun st => { st with stack := st.stack.pop.push (Format.fill st.stack.back) }
|
||||
concat x
|
||||
modify fun st => { st with stack := st.stack.pop.push (Format.fill st.stack.back) }
|
||||
|
||||
@[combinatorFormatter Lean.Parser.orelse] def orelse.formatter (p1 p2 : Formatter) : Formatter :=
|
||||
-- HACK: We have no (immediate) information on which side of the orelse could have produced the current node, so try
|
||||
-- them in turn. Uses the syntax traverser non-linearly!
|
||||
p1 <|> p2
|
||||
-- HACK: We have no (immediate) information on which side of the orelse could have produced the current node, so try
|
||||
-- them in turn. Uses the syntax traverser non-linearly!
|
||||
p1 <|> p2
|
||||
|
||||
-- `mkAntiquot` is quite complex, so we'd rather have its formatter synthesized below the actual parser definition.
|
||||
-- Note that there is a mutual recursion
|
||||
-- `categoryParser -> mkAntiquot -> termParser -> categoryParser`, so we need to introduce an indirection somewhere
|
||||
-- anyway.
|
||||
@[extern "lean_mk_antiquot_formatter"]
|
||||
constant mkAntiquot.formatter' (name : String) (kind : Option SyntaxNodeKind) (anonymous := true) : Formatter :=
|
||||
arbitrary _
|
||||
constant mkAntiquot.formatter' (name : String) (kind : Option SyntaxNodeKind) (anonymous := true) : Formatter
|
||||
|
||||
def formatterForKind (k : SyntaxNodeKind) : Formatter := do
|
||||
env ← getEnv;
|
||||
p::_ ← pure $ formatterAttribute.getValues env k
|
||||
| throwError $ "no known formatter for kind '" ++ k ++ "'";
|
||||
p
|
||||
let env ← getEnv
|
||||
let p::_ ← pure $ formatterAttribute.getValues env k
|
||||
| throwError! "no known formatter for kind '{k}'"
|
||||
p
|
||||
|
||||
@[combinatorFormatter Lean.Parser.withAntiquot]
|
||||
def withAntiquot.formatter (antiP p : Formatter) : Formatter :=
|
||||
-- TODO: could be optimized using `isAntiquot` (which would have to be moved), but I'd rather
|
||||
-- fix the backtracking hack outright.
|
||||
orelse.formatter antiP p
|
||||
-- TODO: could be optimized using `isAntiquot` (which would have to be moved), but I'd rather
|
||||
-- fix the backtracking hack outright.
|
||||
orelse.formatter antiP p
|
||||
|
||||
@[combinatorFormatter Lean.Parser.categoryParser]
|
||||
def categoryParser.formatter (cat : Name) : Formatter := group $ indent do
|
||||
stx ← getCur;
|
||||
if stx.getKind == `choice then
|
||||
visitArgs do {
|
||||
stx ← getCur;
|
||||
sp ← getStackSize;
|
||||
stx.getArgs.forM fun stx => formatterForKind stx.getKind;
|
||||
stack ← getStack;
|
||||
when (stack.size > sp && stack.anyRange sp stack.size fun f => f.pretty != (stack.get! sp).pretty)
|
||||
panic! "Formatter.visit: inequal choice children";
|
||||
-- discard all but one child format
|
||||
setStack $ stack.extract 0 (sp+1)
|
||||
}
|
||||
else
|
||||
withAntiquot.formatter (mkAntiquot.formatter' cat.toString none) (formatterForKind stx.getKind)
|
||||
let stx ← getCur
|
||||
if stx.getKind == `choice then
|
||||
visitArgs do
|
||||
let stx ← getCur;
|
||||
let sp ← getStackSize
|
||||
stx.getArgs.forM fun stx => formatterForKind stx.getKind
|
||||
let stack ← getStack
|
||||
if stack.size > sp && stack.anyRange sp stack.size fun f => f.pretty != (stack.get! sp).pretty then
|
||||
panic! "Formatter.visit: inequal choice children";
|
||||
-- discard all but one child format
|
||||
setStack $ stack.extract 0 (sp+1)
|
||||
else
|
||||
withAntiquot.formatter (mkAntiquot.formatter' cat.toString none) (formatterForKind stx.getKind)
|
||||
|
||||
@[combinatorFormatter Lean.Parser.categoryParserOfStack]
|
||||
def categoryParserOfStack.formatter (offset : Nat) : Formatter := do
|
||||
st ← get;
|
||||
let stx := st.stxTrav.parents.back.getArg (st.stxTrav.idxs.back - offset);
|
||||
categoryParser.formatter stx.getId
|
||||
let st ← get
|
||||
let stx := st.stxTrav.parents.back.getArg (st.stxTrav.idxs.back - offset)
|
||||
categoryParser.formatter stx.getId
|
||||
|
||||
@[combinatorFormatter Lean.Parser.error]
|
||||
def error.formatter (msg : String) : Formatter :=
|
||||
pure ()
|
||||
|
||||
def error.formatter (msg : String) : Formatter := pure ()
|
||||
@[combinatorFormatter Lean.Parser.try]
|
||||
def try.formatter (p : Formatter) : Formatter :=
|
||||
p
|
||||
|
||||
def try.formatter (p : Formatter) : Formatter := p
|
||||
@[combinatorFormatter Lean.Parser.lookahead]
|
||||
def lookahead.formatter (p : Formatter) : Formatter :=
|
||||
pure ()
|
||||
def lookahead.formatter (p : Formatter) : Formatter := pure ()
|
||||
|
||||
@[combinatorFormatter Lean.Parser.notFollowedBy]
|
||||
def notFollowedBy.formatter (p : Formatter) : Formatter :=
|
||||
pure ()
|
||||
def notFollowedBy.formatter (p : Formatter) : Formatter := pure ()
|
||||
|
||||
@[combinatorFormatter Lean.Parser.andthen]
|
||||
def andthen.formatter (p1 p2 : Formatter) : Formatter :=
|
||||
p2 *> p1
|
||||
def andthen.formatter (p1 p2 : Formatter) : Formatter := p2 *> p1
|
||||
|
||||
def checkKind (k : SyntaxNodeKind) : FormatterM Unit := do
|
||||
stx ← getCur;
|
||||
when (k != stx.getKind) $ do {
|
||||
trace! `PrettyPrinter.format.backtrack ("unexpected node kind '" ++ toString stx.getKind ++ "', expected '" ++ toString k ++ "'");
|
||||
throwBacktrack
|
||||
}
|
||||
let stx ← getCur
|
||||
if k != stx.getKind then
|
||||
trace[PrettyPrinter.format.backtrack]! "unexpected node kind '{stx.getKind}', expected '{k}'"
|
||||
throwBacktrack
|
||||
|
||||
@[combinatorFormatter Lean.Parser.node]
|
||||
def node.formatter (k : SyntaxNodeKind) (p : Formatter) : Formatter := do
|
||||
checkKind k;
|
||||
visitArgs p
|
||||
checkKind k;
|
||||
visitArgs p
|
||||
|
||||
@[combinatorFormatter Lean.Parser.trailingNode]
|
||||
def trailingNode.formatter (k : SyntaxNodeKind) (_ : Nat) (p : Formatter) : Formatter := do
|
||||
checkKind k;
|
||||
visitArgs do
|
||||
p;
|
||||
-- leading term, not actually produced by `p`
|
||||
categoryParser.formatter `foo
|
||||
checkKind k
|
||||
visitArgs do
|
||||
p;
|
||||
-- leading term, not actually produced by `p`
|
||||
categoryParser.formatter `foo
|
||||
|
||||
def parseToken (s : String) : FormatterM ParserState := do
|
||||
ctx ← read;
|
||||
env ← getEnv;
|
||||
pure $ Parser.tokenFn { input := s, fileName := "", fileMap := FileMap.ofString "", prec := 0, env := env, tokens := ctx.table } (Parser.mkParserState s)
|
||||
let ctx ← read
|
||||
let env ← getEnv
|
||||
pure $ Parser.tokenFn { input := s, fileName := "", fileMap := FileMap.ofString "", prec := 0, env := env, tokens := ctx.table } (Parser.mkParserState s)
|
||||
|
||||
def pushTokenCore (tk : String) : FormatterM Unit :=
|
||||
if tk.toSubstring.dropRightWhile (fun s => s == ' ') == tk.toSubstring then
|
||||
push tk
|
||||
else do
|
||||
pushLine;
|
||||
push tk.trimRight
|
||||
def pushTokenCore (tk : String) : FormatterM Unit := do
|
||||
if tk.toSubstring.dropRightWhile (fun s => s == ' ') == tk.toSubstring then
|
||||
push tk
|
||||
else
|
||||
pushLine
|
||||
push tk.trimRight
|
||||
|
||||
def pushToken (tk : String) : FormatterM Unit := do
|
||||
st ← get;
|
||||
-- If there is no space between `tk` and the next word, compare parsing `tk` with and without the next word
|
||||
if st.leadWord != "" && tk.trimRight == tk then do
|
||||
t1 ← parseToken tk.trimLeft;
|
||||
t2 ← parseToken $ tk.trimLeft ++ st.leadWord;
|
||||
if t1.pos == t2.pos then do
|
||||
-- same result => use `tk` as is, extend `leadWord` if not prefixed by whitespace
|
||||
pushTokenCore tk;
|
||||
modify fun st => { st with leadWord := if tk.trimLeft == tk then tk ++ st.leadWord else "" }
|
||||
else do
|
||||
-- different result => add space
|
||||
pushTokenCore $ tk ++ " ";
|
||||
let st ← get
|
||||
-- If there is no space between `tk` and the next word, compare parsing `tk` with and without the next word
|
||||
if st.leadWord != "" && tk.trimRight == tk then
|
||||
let t1 ← parseToken tk.trimLeft
|
||||
let t2 ← parseToken $ tk.trimLeft ++ st.leadWord
|
||||
if t1.pos == t2.pos then
|
||||
-- same result => use `tk` as is, extend `leadWord` if not prefixed by whitespace
|
||||
pushTokenCore tk
|
||||
modify fun st => { st with leadWord := if tk.trimLeft == tk then tk ++ st.leadWord else "" }
|
||||
else
|
||||
-- different result => add space
|
||||
pushTokenCore $ tk ++ " "
|
||||
modify fun st => { st with leadWord := if tk.trimLeft == tk then tk else "" }
|
||||
else
|
||||
-- already separated => use `tk` as is
|
||||
pushTokenCore tk
|
||||
modify fun st => { st with leadWord := if tk.trimLeft == tk then tk else "" }
|
||||
else do {
|
||||
-- already separated => use `tk` as is
|
||||
pushTokenCore tk;
|
||||
modify fun st => { st with leadWord := if tk.trimLeft == tk then tk else "" }
|
||||
}
|
||||
|
||||
@[combinatorFormatter symbol]
|
||||
@[combinatorFormatter Lean.Parser.symbol]
|
||||
def symbol.formatter (sym : String) : Formatter := do
|
||||
stx ← getCur;
|
||||
if stx.isToken sym then do
|
||||
pushToken sym;
|
||||
goLeft
|
||||
else do
|
||||
trace! `PrettyPrinter.format.backtrack ("unexpected syntax '" ++ stx ++ "', expected symbol '" ++ sym ++ "'");
|
||||
throwBacktrack
|
||||
let stx ← getCur
|
||||
if stx.isToken sym then do
|
||||
pushToken sym;
|
||||
goLeft
|
||||
else do
|
||||
trace[PrettyPrinter.format.backtrack]! "unexpected syntax '{stx}', expected symbol '{sym}'"
|
||||
throwBacktrack
|
||||
|
||||
@[combinatorFormatter nonReservedSymbol] def nonReservedSymbol.formatter := symbol.formatter
|
||||
@[combinatorFormatter Lean.Parser.nonReservedSymbol] def nonReservedSymbol.formatter := symbol.formatter
|
||||
|
||||
@[combinatorFormatter unicodeSymbol]
|
||||
@[combinatorFormatter Lean.Parser.unicodeSymbol]
|
||||
def unicodeSymbol.formatter (sym asciiSym : String) : Formatter := do
|
||||
stx ← getCur;
|
||||
Syntax.atom _ val ← pure stx
|
||||
| throwError $ "not an atom: " ++ toString stx;
|
||||
if val == sym.trim then
|
||||
pushToken sym
|
||||
else
|
||||
pushToken asciiSym;
|
||||
goLeft
|
||||
let stx ← getCur
|
||||
let Syntax.atom _ val ← pure stx
|
||||
| throwError $ "not an atom: " ++ toString stx
|
||||
if val == sym.trim then
|
||||
pushToken sym
|
||||
else
|
||||
pushToken asciiSym;
|
||||
goLeft
|
||||
|
||||
@[combinatorFormatter identNoAntiquot]
|
||||
@[combinatorFormatter Lean.Parser.identNoAntiquot]
|
||||
def identNoAntiquot.formatter : Formatter := do
|
||||
checkKind identKind;
|
||||
stx ← getCur;
|
||||
let id := stx.getId;
|
||||
let id := id.simpMacroScopes;
|
||||
let s := id.toString;
|
||||
if id.isAnonymous then
|
||||
pushToken "[anonymous]"
|
||||
else if isInaccessibleUserName id || id.components.any Name.isNum ||
|
||||
-- loose bvar
|
||||
"#".isPrefixOf s then
|
||||
-- not parsable anyway, output as-is
|
||||
pushToken s
|
||||
else do {
|
||||
-- try to parse `s` as-is; if it fails, escape
|
||||
pst ← parseToken s;
|
||||
if pst.stxStack == #[stx] then
|
||||
checkKind identKind;
|
||||
let stx ← getCur
|
||||
let id := stx.getId
|
||||
let id := id.simpMacroScopes
|
||||
let s := id.toString;
|
||||
if id.isAnonymous then
|
||||
pushToken "[anonymous]"
|
||||
else if isInaccessibleUserName id || id.components.any Name.isNum ||
|
||||
-- loose bvar
|
||||
"#".isPrefixOf s then
|
||||
-- not parsable anyway, output as-is
|
||||
pushToken s
|
||||
else
|
||||
let n := stx.getId;
|
||||
-- TODO: do something better than escaping all parts
|
||||
let n := (n.components.map fun c => "«" ++ toString c ++ "»").foldl mkNameStr Name.anonymous;
|
||||
pushToken n.toString
|
||||
};
|
||||
goLeft
|
||||
-- try to parse `s` as-is; if it fails, escape
|
||||
let pst ← parseToken s
|
||||
if pst.stxStack == #[stx] then
|
||||
pushToken s
|
||||
else
|
||||
let n := stx.getId
|
||||
-- TODO: do something better than escaping all parts
|
||||
let n := (n.components.map fun c => "«" ++ toString c ++ "»").foldl mkNameStr Name.anonymous
|
||||
pushToken n.toString
|
||||
goLeft
|
||||
|
||||
@[combinatorFormatter rawIdent] def rawIdent.formatter : Formatter := do
|
||||
checkKind identKind;
|
||||
stx ← getCur;
|
||||
pushToken stx.getId.toString;
|
||||
goLeft
|
||||
@[combinatorFormatter Lean.Parser.rawIdent] def rawIdent.formatter : Formatter := do
|
||||
checkKind identKind
|
||||
let stx ← getCur
|
||||
pushToken stx.getId.toString;
|
||||
goLeft
|
||||
|
||||
@[combinatorFormatter Lean.Parser.identEq] def identEq.formatter := rawIdent.formatter
|
||||
@[combinatorFormatter Lean.Parser.identEq] def identEq.formatter (id : Name) := rawIdent.formatter
|
||||
|
||||
def visitAtom (k : SyntaxNodeKind) : Formatter := do
|
||||
stx ← getCur;
|
||||
when (k != Name.anonymous) $
|
||||
checkKind k;
|
||||
Syntax.atom _ val ← pure $ stx.ifNode (fun n => n.getArg 0) (fun _ => stx)
|
||||
| throwError $ "not an atom: " ++ toString stx;
|
||||
pushToken val;
|
||||
goLeft
|
||||
let stx ← getCur
|
||||
if k != Name.anonymous then
|
||||
checkKind k
|
||||
let Syntax.atom _ val ← pure $ stx.ifNode (fun n => n.getArg 0) (fun _ => stx)
|
||||
| throwError $ "not an atom: " ++ toString stx
|
||||
pushToken val
|
||||
goLeft
|
||||
|
||||
@[combinatorFormatter charLitNoAntiquot] def charLitNoAntiquot.formatter := visitAtom charLitKind
|
||||
@[combinatorFormatter strLitNoAntiquot] def strLitNoAntiquot.formatter := visitAtom strLitKind
|
||||
@[combinatorFormatter nameLitNoAntiquot] def nameLitNoAntiquot.formatter := visitAtom nameLitKind
|
||||
@[combinatorFormatter numLitNoAntiquot] def numLitNoAntiquot.formatter := visitAtom numLitKind
|
||||
@[combinatorFormatter fieldIdx] def fieldIdx.formatter := visitAtom fieldIdxKind
|
||||
@[combinatorFormatter Lean.Parser.charLitNoAntiquot] def charLitNoAntiquot.formatter := visitAtom charLitKind
|
||||
@[combinatorFormatter Lean.Parser.strLitNoAntiquot] def strLitNoAntiquot.formatter := visitAtom strLitKind
|
||||
@[combinatorFormatter Lean.Parser.nameLitNoAntiquot] def nameLitNoAntiquot.formatter := visitAtom nameLitKind
|
||||
@[combinatorFormatter Lean.Parser.numLitNoAntiquot] def numLitNoAntiquot.formatter := visitAtom numLitKind
|
||||
@[combinatorFormatter Lean.Parser.fieldIdx] def fieldIdx.formatter := visitAtom fieldIdxKind
|
||||
|
||||
@[combinatorFormatter many]
|
||||
@[combinatorFormatter Lean.Parser.many]
|
||||
def many.formatter (p : Formatter) : Formatter := do
|
||||
stx ← getCur;
|
||||
visitArgs $ stx.getArgs.size.forM fun _ => p
|
||||
let stx ← getCur
|
||||
visitArgs $ stx.getArgs.size.forM fun _ => p
|
||||
|
||||
@[combinatorFormatter many1] def many1.formatter (p : Formatter) : Formatter :=
|
||||
many.formatter p
|
||||
@[combinatorFormatter Lean.Parser.many1] def many1.formatter (p : Formatter) : Formatter := many.formatter p
|
||||
|
||||
@[combinatorFormatter Parser.optional]
|
||||
def optional.formatter (p : Formatter) : Formatter := do
|
||||
visitArgs p
|
||||
@[combinatorFormatter Lean.Parser.optional]
|
||||
def optional.formatter (p : Formatter) : Formatter := visitArgs p
|
||||
|
||||
@[combinatorFormatter Parser.many1Unbox]
|
||||
@[combinatorFormatter Lean.Parser.many1Unbox]
|
||||
def many1Unbox.formatter (p : Formatter) : Formatter := do
|
||||
stx ← getCur;
|
||||
if stx.getKind == nullKind then do
|
||||
many.formatter p
|
||||
else
|
||||
p
|
||||
let stx ← getCur
|
||||
if stx.getKind == nullKind then do
|
||||
many.formatter p
|
||||
else
|
||||
p
|
||||
|
||||
@[combinatorFormatter sepBy]
|
||||
@[combinatorFormatter Lean.Parser.sepBy]
|
||||
def sepBy.formatter (p pSep : Formatter) : Formatter := do
|
||||
stx ← getCur;
|
||||
visitArgs $ (List.range stx.getArgs.size).reverse.forM $ fun i => if i % 2 == 0 then p else pSep
|
||||
let stx ← getCur
|
||||
visitArgs $ (List.range stx.getArgs.size).reverse.forM $ fun i => if i % 2 == 0 then p else pSep
|
||||
|
||||
@[combinatorFormatter sepBy1] def sepBy1.formatter := sepBy.formatter
|
||||
|
||||
@[combinatorFormatter Lean.Parser.withPosition] def withPosition.formatter (p : Formatter) : Formatter := do
|
||||
p
|
||||
@[combinatorFormatter Lean.Parser.withoutPosition] def withoutPosition.formatter (p : Formatter) : Formatter := do
|
||||
p
|
||||
|
||||
@[combinatorFormatter Lean.Parser.withForbidden] def withForbidden.formatter (tk : Token) (p : Formatter) : Formatter := do
|
||||
p
|
||||
@[combinatorFormatter Lean.Parser.withoutForbidden] def withoutForbidden.formatter (p : Formatter) : Formatter := do
|
||||
p
|
||||
@[combinatorFormatter Lean.Parser.sepBy1] def sepBy1.formatter := sepBy.formatter
|
||||
|
||||
@[combinatorFormatter Lean.Parser.withPosition] def withPosition.formatter (p : Formatter) : Formatter := p
|
||||
@[combinatorFormatter Lean.Parser.withoutPosition] def withoutPosition.formatter (p : Formatter) : Formatter := p
|
||||
@[combinatorFormatter Lean.Parser.withForbidden] def withForbidden.formatter (tk : Token) (p : Formatter) : Formatter := p
|
||||
@[combinatorFormatter Lean.Parser.withoutForbidden] def withoutForbidden.formatter (p : Formatter) : Formatter := p
|
||||
@[combinatorFormatter Lean.Parser.setExpected]
|
||||
def setExpected.formatter (expected : List String) (p : Formatter) : Formatter :=
|
||||
p
|
||||
def setExpected.formatter (expected : List String) (p : Formatter) : Formatter := p
|
||||
|
||||
@[combinatorFormatter Lean.Parser.toggleInsideQuot]
|
||||
def toggleInsideQuot.formatter (p : Formatter) : Formatter :=
|
||||
p
|
||||
def toggleInsideQuot.formatter (p : Formatter) : Formatter := p
|
||||
|
||||
@[combinatorFormatter checkWsBefore] def checkWsBefore.formatter : Formatter := do
|
||||
st ← get;
|
||||
when (st.leadWord != "") $
|
||||
pushLine
|
||||
@[combinatorFormatter Lean.Parser.checkWsBefore] def checkWsBefore.formatter : Formatter := do
|
||||
let st ← get
|
||||
if st.leadWord != "" then
|
||||
pushLine
|
||||
|
||||
@[combinatorFormatter checkPrec] def checkPrec.formatter : Formatter := pure ()
|
||||
@[combinatorFormatter checkStackTop] def checkStackTop.formatter : Formatter := pure ()
|
||||
@[combinatorFormatter checkNoWsBefore] def checkNoWsBefore.formatter : Formatter := pure ()
|
||||
@[combinatorFormatter checkTailWs] def checkTailWs.formatter : Formatter := pure ()
|
||||
@[combinatorFormatter checkColGe] def checkColGe.formatter : Formatter := pure ()
|
||||
@[combinatorFormatter checkColGt] def checkColGt.formatter : Formatter := pure ()
|
||||
@[combinatorFormatter checkLineEq] def checkLineEq.formatter : Formatter := pure ()
|
||||
@[combinatorFormatter Lean.Parser.checkPrec] def checkPrec.formatter : Formatter := pure ()
|
||||
@[combinatorFormatter Lean.Parser.checkStackTop] def checkStackTop.formatter : Formatter := pure ()
|
||||
@[combinatorFormatter Lean.Parser.checkNoWsBefore] def checkNoWsBefore.formatter : Formatter := pure ()
|
||||
@[combinatorFormatter Lean.Parser.checkTailWs] def checkTailWs.formatter : Formatter := pure ()
|
||||
@[combinatorFormatter Lean.Parser.checkColGe] def checkColGe.formatter : Formatter := pure ()
|
||||
@[combinatorFormatter Lean.Parser.checkColGt] def checkColGt.formatter : Formatter := pure ()
|
||||
@[combinatorFormatter Lean.Parser.checkLineEq] def checkLineEq.formatter : Formatter := pure ()
|
||||
|
||||
@[combinatorFormatter eoi] def eoi.formatter : Formatter := pure ()
|
||||
@[combinatorFormatter notFollowedByCategoryToken] def notFollowedByCategoryToken.formatter : Formatter := pure ()
|
||||
@[combinatorFormatter checkNoImmediateColon] def checkNoImmediateColon.formatter : Formatter := pure ()
|
||||
@[combinatorFormatter Lean.Parser.eoi] def eoi.formatter : Formatter := pure ()
|
||||
@[combinatorFormatter Lean.Parser.notFollowedByCategoryToken] def notFollowedByCategoryToken.formatter : Formatter := pure ()
|
||||
@[combinatorFormatter Lean.Parser.checkNoImmediateColon] def checkNoImmediateColon.formatter : Formatter := pure ()
|
||||
@[combinatorFormatter Lean.Parser.checkInsideQuot] def checkInsideQuot.formatter : Formatter := pure ()
|
||||
@[combinatorFormatter Lean.Parser.checkOutsideQuot] def checkOutsideQuot.formatter : Formatter := pure ()
|
||||
@[combinatorFormatter Lean.Parser.skip] def skip.formatter : Formatter := pure ()
|
||||
|
||||
@[combinatorFormatter pushNone] def pushNone.formatter : Formatter := goLeft
|
||||
@[combinatorFormatter Lean.Parser.pushNone] def pushNone.formatter : Formatter := goLeft
|
||||
|
||||
-- TODO: delete with old frontend
|
||||
@[combinatorFormatter quotedSymbol] def quotedSymbol.formatter : Formatter := do
|
||||
checkKind quotedSymbolKind;
|
||||
visitArgs do
|
||||
push "`"; goLeft;
|
||||
visitAtom Name.anonymous;
|
||||
push "`"; goLeft
|
||||
@[combinatorFormatter Lean.Parser.quotedSymbol] def quotedSymbol.formatter : Formatter := do
|
||||
checkKind quotedSymbolKind
|
||||
visitArgs do
|
||||
push "`"; goLeft
|
||||
visitAtom Name.anonymous
|
||||
push "`"; goLeft
|
||||
|
||||
@[combinatorFormatter Lean.Parser.interpolatedStr]
|
||||
def interpolatedStr.formatter (p : Formatter) : Formatter :=
|
||||
throwError "NIY"
|
||||
def interpolatedStr.formatter (p : Formatter) : Formatter := throwError "NIY"
|
||||
|
||||
@[combinatorFormatter unquotedSymbol] def unquotedSymbol.formatter := visitAtom Name.anonymous
|
||||
@[combinatorFormatter Lean.Parser.unquotedSymbol] def unquotedSymbol.formatter := visitAtom Name.anonymous
|
||||
|
||||
@[combinatorFormatter ite, macroInline] def ite {α : Type} (c : Prop) [h : Decidable c] (t e : Formatter) : Formatter :=
|
||||
if c then t else e
|
||||
if c then t else e
|
||||
|
||||
end Formatter
|
||||
open Formatter
|
||||
|
||||
def format (formatter : Formatter) (stx : Syntax) : CoreM Format := do
|
||||
options ← getOptions;
|
||||
table ← Parser.builtinTokenTable.get;
|
||||
let options ← getOptions
|
||||
let table ← Parser.builtinTokenTable.get
|
||||
catchInternalId backtrackExceptionId
|
||||
(do
|
||||
(_, st) ← (concat formatter { table := table, options := options }).run { stxTrav := Syntax.Traverser.fromSyntax stx };
|
||||
let (_, st) ← (concat formatter { table := table, options := options }).run { stxTrav := Syntax.Traverser.fromSyntax stx };
|
||||
pure $ Format.fill $ st.stack.get! 0)
|
||||
(fun _ => throwError "format: uncaught backtrack exception")
|
||||
|
||||
def formatTerm := format $ categoryParser.formatter `term
|
||||
def formatCommand := format $ categoryParser.formatter `command
|
||||
|
||||
@[builtinInit] private def regTraceClasses : IO Unit := do
|
||||
registerTraceClass `PrettyPrinter.format;
|
||||
pure ()
|
||||
builtin_initialize registerTraceClass `PrettyPrinter.format;
|
||||
|
||||
end PrettyPrinter
|
||||
end Lean
|
||||
|
|
|
|||
|
|
@ -35,14 +35,14 @@ unsafe def interpretParserDescr : ParserDescr → AttrM Parenthesizer
|
|||
| ParserDescr.sepBy1 d₁ d₂ => sepBy1.parenthesizer <$> interpretParserDescr d₁ <*> interpretParserDescr d₂
|
||||
| ParserDescr.node k prec d => leadingNode.parenthesizer k prec <$> interpretParserDescr d
|
||||
| ParserDescr.trailingNode k prec d => trailingNode.parenthesizer k prec <$> interpretParserDescr d
|
||||
| ParserDescr.symbol tk => pure $ symbol.parenthesizer
|
||||
| ParserDescr.symbol tk => pure $ symbol.parenthesizer tk
|
||||
| ParserDescr.numLit => pure $ withAntiquot.parenthesizer (mkAntiquot.parenthesizer' "numLit" `numLit) numLitNoAntiquot.parenthesizer
|
||||
| ParserDescr.strLit => pure $ withAntiquot.parenthesizer (mkAntiquot.parenthesizer' "strLit" `strLit) strLitNoAntiquot.parenthesizer
|
||||
| ParserDescr.charLit => pure $ withAntiquot.parenthesizer (mkAntiquot.parenthesizer' "charLit" `charLit) charLitNoAntiquot.parenthesizer
|
||||
| ParserDescr.nameLit => pure $ withAntiquot.parenthesizer (mkAntiquot.parenthesizer' "nameLit" `nameLit) nameLitNoAntiquot.parenthesizer
|
||||
| ParserDescr.ident => pure $ withAntiquot.parenthesizer (mkAntiquot.parenthesizer' "ident" `ident) identNoAntiquot.parenthesizer
|
||||
| ParserDescr.interpolatedStr d => interpolatedStr.parenthesizer <$> interpretParserDescr d
|
||||
| ParserDescr.nonReservedSymbol tk includeIdent => pure $ nonReservedSymbol.parenthesizer
|
||||
| ParserDescr.nonReservedSymbol tk includeIdent => pure $ nonReservedSymbol.parenthesizer tk includeIdent
|
||||
| ParserDescr.noWs => pure $ checkNoWsBefore.parenthesizer
|
||||
| ParserDescr.parser constName => interpretParser (ctx interpretParserDescr) constName
|
||||
| ParserDescr.cat catName prec => pure $ categoryParser.parenthesizer catName prec
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#lang lean4
|
||||
/-
|
||||
Copyright (c) 2020 Sebastian Ullrich. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
|
@ -78,27 +79,24 @@ import Lean.ParserCompiler.Attribute
|
|||
import Lean.PrettyPrinter.Backtrack
|
||||
|
||||
namespace Lean
|
||||
namespace Syntax namespace MonadTraverser end MonadTraverser end Syntax -- Hack for old frontend
|
||||
namespace Parser end Parser -- Hack for old frontend
|
||||
|
||||
namespace PrettyPrinter
|
||||
namespace Parenthesizer
|
||||
|
||||
structure Context :=
|
||||
-- We need to store this `categoryParser` argument to deal with the implicit Pratt parser call in `trailingNode.parenthesizer`.
|
||||
(cat : Name := Name.anonymous)
|
||||
-- We need to store this `categoryParser` argument to deal with the implicit Pratt parser call in `trailingNode.parenthesizer`.
|
||||
(cat : Name := Name.anonymous)
|
||||
|
||||
structure State :=
|
||||
(stxTrav : Syntax.Traverser)
|
||||
--- precedence and category of the current left-most trailing parser, if any; see module doc for details
|
||||
(contPrec : Option Nat := none)
|
||||
(contCat := Name.anonymous)
|
||||
-- current minimum precedence in this Pratt parser call, if any; see module doc for details
|
||||
(minPrec : Option Nat := none)
|
||||
-- precedence of the trailing Pratt parser call if any; see module doc for details
|
||||
(trailPrec : Option Nat := none)
|
||||
-- true iff we have already visited a token on this parser level; used for detecting trailing parsers
|
||||
(visitedToken : Bool := false)
|
||||
structure State :=
|
||||
(stxTrav : Syntax.Traverser)
|
||||
--- precedence and category of the current left-most trailing parser, if any; see module doc for details
|
||||
(contPrec : Option Nat := none)
|
||||
(contCat : Name := Name.anonymous)
|
||||
-- current minimum precedence in this Pratt parser call, if any; see module doc for details
|
||||
(minPrec : Option Nat := none)
|
||||
-- precedence of the trailing Pratt parser call if any; see module doc for details
|
||||
(trailPrec : Option Nat := none)
|
||||
-- true iff we have already visited a token on this parser level; used for detecting trailing parsers
|
||||
(visitedToken : Bool := false)
|
||||
|
||||
end Parenthesizer
|
||||
|
||||
|
|
@ -106,66 +104,66 @@ abbrev ParenthesizerM := ReaderT Parenthesizer.Context $ StateRefT Parenthesizer
|
|||
abbrev Parenthesizer := ParenthesizerM Unit
|
||||
|
||||
@[inline] def ParenthesizerM.orelse {α} (p₁ p₂ : ParenthesizerM α) : ParenthesizerM α := do
|
||||
s ← get;
|
||||
catchInternalId backtrackExceptionId
|
||||
p₁
|
||||
(fun _ => do set s; p₂)
|
||||
let s ← get
|
||||
catchInternalId backtrackExceptionId
|
||||
p₁
|
||||
(fun _ => do set s; p₂)
|
||||
|
||||
instance Parenthesizer.orelse {α} : HasOrelse (ParenthesizerM α) := ⟨ParenthesizerM.orelse⟩
|
||||
|
||||
unsafe def mkParenthesizerAttribute : IO (KeyedDeclsAttribute Parenthesizer) :=
|
||||
KeyedDeclsAttribute.init {
|
||||
builtinName := `builtinParenthesizer,
|
||||
name := `parenthesizer,
|
||||
descr := "Register a parenthesizer for a parser.
|
||||
KeyedDeclsAttribute.init {
|
||||
builtinName := `builtinParenthesizer,
|
||||
name := `parenthesizer,
|
||||
descr := "Register a parenthesizer for a parser.
|
||||
|
||||
[parenthesizer k] registers a declaration of type `Lean.PrettyPrinter.Parenthesizer` for the `SyntaxNodeKind` `k`.",
|
||||
valueTypeName := `Lean.PrettyPrinter.Parenthesizer,
|
||||
evalKey := fun builtin args => do
|
||||
env ← getEnv;
|
||||
match attrParamSyntaxToIdentifier args with
|
||||
| some id =>
|
||||
-- `isValidSyntaxNodeKind` is updated only in the next stage for new `[builtin*Parser]`s, but we try to
|
||||
-- synthesize a parenthesizer for it immediately, so we just check for a declaration in this case
|
||||
if (builtin && (env.find? id).isSome) || Parser.isValidSyntaxNodeKind env id then pure id
|
||||
else throwError ("invalid [parenthesizer] argument, unknown syntax kind '" ++ toString id ++ "'")
|
||||
| none => throwError "invalid [parenthesizer] argument, expected identifier"
|
||||
} `Lean.PrettyPrinter.parenthesizerAttribute
|
||||
@[builtinInit mkParenthesizerAttribute] constant parenthesizerAttribute : KeyedDeclsAttribute Parenthesizer := arbitrary _
|
||||
[parenthesizer k] registers a declaration of type `Lean.PrettyPrinter.Parenthesizer` for the `SyntaxNodeKind` `k`.",
|
||||
valueTypeName := `Lean.PrettyPrinter.Parenthesizer,
|
||||
evalKey := fun builtin args => do
|
||||
let env ← getEnv
|
||||
match attrParamSyntaxToIdentifier args with
|
||||
| some id =>
|
||||
-- `isValidSyntaxNodeKind` is updated only in the next stage for new `[builtin*Parser]`s, but we try to
|
||||
-- synthesize a parenthesizer for it immediately, so we just check for a declaration in this case
|
||||
if (builtin && (env.find? id).isSome) || Parser.isValidSyntaxNodeKind env id then pure id
|
||||
else throwError ("invalid [parenthesizer] argument, unknown syntax kind '" ++ toString id ++ "'")
|
||||
| none => throwError "invalid [parenthesizer] argument, expected identifier"
|
||||
} `Lean.PrettyPrinter.parenthesizerAttribute
|
||||
@[builtinInit mkParenthesizerAttribute] constant parenthesizerAttribute : KeyedDeclsAttribute Parenthesizer
|
||||
|
||||
abbrev CategoryParenthesizer := forall (prec : Nat), Parenthesizer
|
||||
|
||||
unsafe def mkCategoryParenthesizerAttribute : IO (KeyedDeclsAttribute CategoryParenthesizer) :=
|
||||
KeyedDeclsAttribute.init {
|
||||
builtinName := `builtinCategoryParenthesizer,
|
||||
name := `categoryParenthesizer,
|
||||
descr := "Register a parenthesizer for a syntax category.
|
||||
KeyedDeclsAttribute.init {
|
||||
builtinName := `builtinCategoryParenthesizer,
|
||||
name := `categoryParenthesizer,
|
||||
descr := "Register a parenthesizer for a syntax category.
|
||||
|
||||
[parenthesizer cat] registers a declaration of type `Lean.PrettyPrinter.CategoryParenthesizer` for the category `cat`,
|
||||
which is used when parenthesizing calls of `categoryParser cat prec`. Implementations should call `maybeParenthesize`
|
||||
with the precedence and `cat`. If no category parenthesizer is registered, the category will never be parenthesized,
|
||||
but still be traversed for parenthesizing nested categories.",
|
||||
valueTypeName := `Lean.PrettyPrinter.CategoryParenthesizer,
|
||||
evalKey := fun _ args => do
|
||||
env ← getEnv;
|
||||
match attrParamSyntaxToIdentifier args with
|
||||
| some id =>
|
||||
if Parser.isParserCategory env id then pure id
|
||||
else throwError ("invalid [parenthesizer] argument, unknown parser category '" ++ toString id ++ "'")
|
||||
| none => throwError "invalid [parenthesizer] argument, expected identifier"
|
||||
} `Lean.PrettyPrinter.categoryParenthesizerAttribute
|
||||
@[builtinInit mkCategoryParenthesizerAttribute] constant categoryParenthesizerAttribute : KeyedDeclsAttribute CategoryParenthesizer := arbitrary _
|
||||
[parenthesizer cat] registers a declaration of type `Lean.PrettyPrinter.CategoryParenthesizer` for the category `cat`,
|
||||
which is used when parenthesizing calls of `categoryParser cat prec`. Implementations should call `maybeParenthesize`
|
||||
with the precedence and `cat`. If no category parenthesizer is registered, the category will never be parenthesized,
|
||||
but still be traversed for parenthesizing nested categories.",
|
||||
valueTypeName := `Lean.PrettyPrinter.CategoryParenthesizer,
|
||||
evalKey := fun _ args => do
|
||||
let env ← getEnv
|
||||
match attrParamSyntaxToIdentifier args with
|
||||
| some id =>
|
||||
if Parser.isParserCategory env id then pure id
|
||||
else throwError ("invalid [parenthesizer] argument, unknown parser category '" ++ toString id ++ "'")
|
||||
| none => throwError "invalid [parenthesizer] argument, expected identifier"
|
||||
} `Lean.PrettyPrinter.categoryParenthesizerAttribute
|
||||
@[builtinInit mkCategoryParenthesizerAttribute] constant categoryParenthesizerAttribute : KeyedDeclsAttribute CategoryParenthesizer
|
||||
|
||||
unsafe def mkCombinatorParenthesizerAttribute : IO ParserCompiler.CombinatorAttribute :=
|
||||
ParserCompiler.registerCombinatorAttribute
|
||||
`combinatorParenthesizer
|
||||
"Register a parenthesizer for a parser combinator.
|
||||
ParserCompiler.registerCombinatorAttribute
|
||||
`combinatorParenthesizer
|
||||
"Register a parenthesizer for a parser combinator.
|
||||
|
||||
[combinatorParenthesizer c] registers a declaration of type `Lean.PrettyPrinter.Parenthesizer` for the `Parser` declaration `c`.
|
||||
Note that, unlike with [parenthesizer], this is not a node kind since combinators usually do not introduce their own node kinds.
|
||||
The tagged declaration may optionally accept parameters corresponding to (a prefix of) those of `c`, where `Parser` is replaced
|
||||
with `Parenthesizer` in the parameter types."
|
||||
@[builtinInit mkCombinatorParenthesizerAttribute] constant combinatorParenthesizerAttribute : ParserCompiler.CombinatorAttribute := arbitrary _
|
||||
[combinatorParenthesizer c] registers a declaration of type `Lean.PrettyPrinter.Parenthesizer` for the `Parser` declaration `c`.
|
||||
Note that, unlike with [parenthesizer], this is not a node kind since combinators usually do not introduce their own node kinds.
|
||||
The tagged declaration may optionally accept parameters corresponding to (a prefix of) those of `c`, where `Parser` is replaced
|
||||
with `Parenthesizer` in the parameter types."
|
||||
@[builtinInit mkCombinatorParenthesizerAttribute] constant combinatorParenthesizerAttribute : ParserCompiler.CombinatorAttribute
|
||||
|
||||
namespace Parenthesizer
|
||||
|
||||
|
|
@ -178,221 +176,218 @@ throw $ Exception.internal backtrackExceptionId
|
|||
instance ParenthesizerM.monadTraverser : Syntax.MonadTraverser ParenthesizerM := ⟨{
|
||||
get := State.stxTrav <$> get,
|
||||
set := fun t => modify (fun st => { st with stxTrav := t }),
|
||||
modifyGet := fun _ f => modifyGet (fun st => let (a, t) := f st.stxTrav; (a, { st with stxTrav := t })) }⟩
|
||||
modifyGet := fun f => modifyGet (fun st => let (a, t) := f st.stxTrav; (a, { st with stxTrav := t }))
|
||||
}⟩
|
||||
|
||||
open Syntax.MonadTraverser
|
||||
|
||||
def addPrecCheck (prec : Nat) : ParenthesizerM Unit :=
|
||||
modify $ fun st => { st with contPrec := Nat.min (st.contPrec.getD prec) prec, minPrec := Nat.min (st.minPrec.getD prec) prec }
|
||||
modify fun st => { st with contPrec := Nat.min (st.contPrec.getD prec) prec, minPrec := Nat.min (st.minPrec.getD prec) prec }
|
||||
|
||||
/-- Execute `x` at the right-most child of the current node, if any, then advance to the left. -/
|
||||
def visitArgs (x : ParenthesizerM Unit) : ParenthesizerM Unit := do
|
||||
stx ← getCur;
|
||||
when (stx.getArgs.size > 0) $
|
||||
goDown (stx.getArgs.size - 1) *> x <* goUp;
|
||||
goLeft
|
||||
let stx ← getCur
|
||||
if stx.getArgs.size > 0 then
|
||||
goDown (stx.getArgs.size - 1) *> x <* goUp
|
||||
goLeft
|
||||
|
||||
-- Macro scopes in the parenthesizer output are ultimately ignored by the pretty printer,
|
||||
-- so give a trivial implementation.
|
||||
instance monadQuotation : MonadQuotation ParenthesizerM := {
|
||||
getCurrMacroScope := pure $ arbitrary _,
|
||||
getMainModule := pure $ arbitrary _,
|
||||
withFreshMacroScope := fun α x => x,
|
||||
withFreshMacroScope := fun x => x,
|
||||
}
|
||||
|
||||
/-- Run `x` and parenthesize the result using `mkParen` if necessary. -/
|
||||
def maybeParenthesize (cat : Name) (mkParen : Syntax → Syntax) (prec : Nat) (x : ParenthesizerM Unit) : ParenthesizerM Unit := do
|
||||
stx ← getCur;
|
||||
idx ← getIdx;
|
||||
st ← get;
|
||||
-- reset precs for the recursive call
|
||||
set { stxTrav := st.stxTrav : State };
|
||||
trace! `PrettyPrinter.parenthesize ("parenthesizing (cont := " ++ toString (st.contPrec, st.contCat) ++ ")" ++ MessageData.nest 2 (line ++ stx));
|
||||
x;
|
||||
{ minPrec := some minPrec, trailPrec := trailPrec, .. } ← get
|
||||
| panic! "maybeParenthesize: visited a syntax tree without precedences?!";
|
||||
trace! `PrettyPrinter.parenthesize ("...precedences are " ++ fmt prec ++ " >? " ++ fmt minPrec ++ ", " ++ fmt (trailPrec, cat) ++ " <=? " ++ fmt (st.contPrec, st.contCat));
|
||||
-- Should we parenthesize?
|
||||
when (prec > minPrec || match trailPrec, st.contPrec with some trailPrec, some contPrec => cat == st.contCat && trailPrec <= contPrec | _, _ => false) $ do {
|
||||
-- The recursive `visit` call, by the invariant, has moved to the preceding node. In order to parenthesize
|
||||
-- the original node, we must first move to the right, except if we already were at the left-most child in the first
|
||||
-- place.
|
||||
when (idx > 0) goRight;
|
||||
stx ← getCur;
|
||||
match stx.getHeadInfo, stx.getTailInfo with
|
||||
| some hi, some ti =>
|
||||
-- Move leading/trailing whitespace of `stx` outside of parentheses
|
||||
let stx := (stx.setHeadInfo { hi with leading := "".toSubstring }).setTailInfo { ti with trailing := "".toSubstring };
|
||||
let stx := mkParen stx;
|
||||
let stx := (stx.setHeadInfo { hi with trailing := "".toSubstring }).setTailInfo { ti with leading := "".toSubstring };
|
||||
setCur stx
|
||||
| _, _ => setCur (mkParen stx);
|
||||
stx ← getCur; trace! `PrettyPrinter.parenthesize ("parenthesized: " ++ stx.formatStx none);
|
||||
goLeft;
|
||||
-- after parenthesization, there is no more trailing parser
|
||||
modify (fun st => { st with contPrec := Parser.maxPrec, contCat := cat, trailPrec := none })
|
||||
};
|
||||
{ trailPrec := trailPrec, .. } ← get;
|
||||
-- If we already had a token at this level, keep the trailing parser. Otherwise, use the minimum of
|
||||
-- `prec` and `trailPrec`.
|
||||
let trailPrec := if st.visitedToken then st.trailPrec else match trailPrec with
|
||||
| some trailPrec => some (Nat.min trailPrec prec)
|
||||
| _ => some prec;
|
||||
modify (fun stP => { stP with minPrec := st.minPrec, trailPrec := trailPrec })
|
||||
let stx ← getCur
|
||||
let idx ← getIdx
|
||||
let st ← get
|
||||
-- reset precs for the recursive call
|
||||
set { stxTrav := st.stxTrav : State }
|
||||
trace[PrettyPrinter.parenthesize]! "parenthesizing (cont := {(st.contPrec, st.contCat)}){MessageData.nest 2 (line ++ stx)}"
|
||||
x
|
||||
let { minPrec := some minPrec, trailPrec := trailPrec, .. } ← get
|
||||
| panic! "maybeParenthesize: visited a syntax tree without precedences?!"
|
||||
trace[PrettyPrinter.parenthesize]! "...precedences are {prec} >? {minPrec}, {(trailPrec, cat)} <=? {(st.contPrec, st.contCat)}"
|
||||
-- Should we parenthesize?
|
||||
if (prec > minPrec || match trailPrec, st.contPrec with some trailPrec, some contPrec => cat == st.contCat && trailPrec <= contPrec | _, _ => false) then
|
||||
-- The recursive `visit` call, by the invariant, has moved to the preceding node. In order to parenthesize
|
||||
-- the original node, we must first move to the right, except if we already were at the left-most child in the first
|
||||
-- place.
|
||||
if idx > 0 then goRight
|
||||
let stx ← getCur
|
||||
match stx.getHeadInfo, stx.getTailInfo with
|
||||
| some hi, some ti =>
|
||||
-- Move leading/trailing whitespace of `stx` outside of parentheses
|
||||
let stx := (stx.setHeadInfo { hi with leading := "".toSubstring }).setTailInfo { ti with trailing := "".toSubstring }
|
||||
let stx := mkParen stx
|
||||
let stx := (stx.setHeadInfo { hi with trailing := "".toSubstring }).setTailInfo { ti with leading := "".toSubstring }
|
||||
setCur stx
|
||||
| _, _ => setCur (mkParen stx)
|
||||
let stx ← getCur; trace! `PrettyPrinter.parenthesize ("parenthesized: " ++ stx.formatStx none)
|
||||
goLeft
|
||||
-- after parenthesization, there is no more trailing parser
|
||||
modify (fun st => { st with contPrec := Parser.maxPrec, contCat := cat, trailPrec := none })
|
||||
let { trailPrec := trailPrec, .. } ← get
|
||||
-- If we already had a token at this level, keep the trailing parser. Otherwise, use the minimum of
|
||||
-- `prec` and `trailPrec`.
|
||||
let trailPrec := if st.visitedToken then st.trailPrec else match trailPrec with
|
||||
| some trailPrec => some (Nat.min trailPrec prec)
|
||||
| _ => some prec
|
||||
modify (fun stP => { stP with minPrec := st.minPrec, trailPrec := trailPrec })
|
||||
|
||||
/-- Adjust state and advance. -/
|
||||
def visitToken : Parenthesizer := do
|
||||
modify (fun st => { st with contPrec := none, contCat := Name.anonymous, visitedToken := true });
|
||||
goLeft
|
||||
modify fun st => { st with contPrec := none, contCat := Name.anonymous, visitedToken := true }
|
||||
goLeft
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.orelse] def orelse.parenthesizer (p1 p2 : Parenthesizer) : Parenthesizer := do
|
||||
st ← get;
|
||||
-- HACK: We have no (immediate) information on which side of the orelse could have produced the current node, so try
|
||||
-- them in turn. Uses the syntax traverser non-linearly!
|
||||
p1 <|> p2
|
||||
let st ← get
|
||||
-- HACK: We have no (immediate) information on which side of the orelse could have produced the current node, so try
|
||||
-- them in turn. Uses the syntax traverser non-linearly!
|
||||
p1 <|> p2
|
||||
|
||||
-- `mkAntiquot` is quite complex, so we'd rather have its parenthesizer synthesized below the actual parser definition.
|
||||
-- Note that there is a mutual recursion
|
||||
-- `categoryParser -> mkAntiquot -> termParser -> categoryParser`, so we need to introduce an indirection somewhere
|
||||
-- anyway.
|
||||
@[extern 8 "lean_mk_antiquot_parenthesizer"]
|
||||
constant mkAntiquot.parenthesizer' (name : String) (kind : Option SyntaxNodeKind) (anonymous := true) : Parenthesizer :=
|
||||
arbitrary _
|
||||
constant mkAntiquot.parenthesizer' (name : String) (kind : Option SyntaxNodeKind) (anonymous := true) : Parenthesizer
|
||||
|
||||
@[inline] def liftCoreM {α} (x : CoreM α) : ParenthesizerM α :=
|
||||
liftM x
|
||||
liftM x
|
||||
|
||||
def throwError {α} (msg : MessageData) : ParenthesizerM α :=
|
||||
liftCoreM $ throwError msg
|
||||
liftCoreM $ Lean.throwError msg
|
||||
|
||||
def parenthesizerForKind (k : SyntaxNodeKind) : Parenthesizer := do
|
||||
env ← getEnv;
|
||||
p::_ ← pure $ parenthesizerAttribute.getValues env k
|
||||
| throwError $ "no known parenthesizer for kind '" ++ toString k ++ "'";
|
||||
p
|
||||
let env ← getEnv
|
||||
let p::_ ← pure $ parenthesizerAttribute.getValues env k
|
||||
| throwError! "no known parenthesizer for kind '{k}'"
|
||||
p
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.withAntiquot]
|
||||
def withAntiquot.parenthesizer (antiP p : Parenthesizer) : Parenthesizer :=
|
||||
-- TODO: could be optimized using `isAntiquot` (which would have to be moved), but I'd rather
|
||||
-- fix the backtracking hack outright.
|
||||
orelse.parenthesizer antiP p
|
||||
-- TODO: could be optimized using `isAntiquot` (which would have to be moved), but I'd rather
|
||||
-- fix the backtracking hack outright.
|
||||
orelse.parenthesizer antiP p
|
||||
|
||||
def parenthesizeCategoryCore (cat : Name) (prec : Nat) : Parenthesizer :=
|
||||
withReader (fun ctx => { ctx with cat := cat }) do
|
||||
stx ← getCur;
|
||||
if stx.getKind == `choice then
|
||||
visitArgs $ stx.getArgs.size.forM $ fun _ => do
|
||||
stx ← getCur;
|
||||
parenthesizerForKind stx.getKind
|
||||
else
|
||||
withAntiquot.parenthesizer (mkAntiquot.parenthesizer' cat.toString none) (parenthesizerForKind stx.getKind);
|
||||
modify fun st => { st with contCat := cat }
|
||||
withReader (fun ctx => { ctx with cat := cat }) do
|
||||
let stx ← getCur
|
||||
if stx.getKind == `choice then
|
||||
visitArgs $ stx.getArgs.size.forM $ fun _ => do
|
||||
stx ← getCur
|
||||
parenthesizerForKind stx.getKind
|
||||
else
|
||||
withAntiquot.parenthesizer (mkAntiquot.parenthesizer' cat.toString none) (parenthesizerForKind stx.getKind)
|
||||
modify fun st => { st with contCat := cat }
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.categoryParser]
|
||||
def categoryParser.parenthesizer (cat : Name) (prec : Nat) : Parenthesizer := do
|
||||
env ← getEnv;
|
||||
match categoryParenthesizerAttribute.getValues env cat with
|
||||
| p::_ => p prec
|
||||
-- Fall back to the generic parenthesizer.
|
||||
-- In this case this node will never be parenthesized since we don't know which parentheses to use.
|
||||
| _ => parenthesizeCategoryCore cat prec
|
||||
let env ← getEnv
|
||||
match categoryParenthesizerAttribute.getValues env cat with
|
||||
| p::_ => p prec
|
||||
-- Fall back to the generic parenthesizer.
|
||||
-- In this case this node will never be parenthesized since we don't know which parentheses to use.
|
||||
| _ => parenthesizeCategoryCore cat prec
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.categoryParserOfStack]
|
||||
def categoryParserOfStack.parenthesizer (offset : Nat) (prec : Nat) : Parenthesizer := do
|
||||
st ← get;
|
||||
let stx := st.stxTrav.parents.back.getArg (st.stxTrav.idxs.back - offset);
|
||||
categoryParser.parenthesizer stx.getId prec
|
||||
let st ← get
|
||||
let stx := st.stxTrav.parents.back.getArg (st.stxTrav.idxs.back - offset)
|
||||
categoryParser.parenthesizer stx.getId prec
|
||||
|
||||
@[builtinCategoryParenthesizer term]
|
||||
def term.parenthesizer : CategoryParenthesizer | prec => do
|
||||
stx ← getCur;
|
||||
-- this can happen at `termParser <|> many1 commandParser` in `Term.stxQuot`
|
||||
if stx.getKind == nullKind then
|
||||
throwBacktrack
|
||||
else do
|
||||
maybeParenthesize `term (fun stx => Unhygienic.run `(($stx))) prec $
|
||||
parenthesizeCategoryCore `term prec
|
||||
let stx ← getCur
|
||||
-- this can happen at `termParser <|> many1 commandParser` in `Term.stxQuot`
|
||||
if stx.getKind == nullKind then
|
||||
throwBacktrack
|
||||
else do
|
||||
maybeParenthesize `term (fun stx => Unhygienic.run `(($stx))) prec $
|
||||
parenthesizeCategoryCore `term prec
|
||||
|
||||
@[builtinCategoryParenthesizer tactic]
|
||||
def tactic.parenthesizer : CategoryParenthesizer | prec => do
|
||||
maybeParenthesize `tactic (fun stx => Unhygienic.run `(tactic|($stx))) prec $
|
||||
parenthesizeCategoryCore `tactic prec
|
||||
maybeParenthesize `tactic (fun stx => Unhygienic.run `(tactic|($stx))) prec $
|
||||
parenthesizeCategoryCore `tactic prec
|
||||
|
||||
@[builtinCategoryParenthesizer level]
|
||||
def level.parenthesizer : CategoryParenthesizer | prec => do
|
||||
maybeParenthesize `level (fun stx => Unhygienic.run `(level|($stx))) prec $
|
||||
parenthesizeCategoryCore `level prec
|
||||
maybeParenthesize `level (fun stx => Unhygienic.run `(level|($stx))) prec $
|
||||
parenthesizeCategoryCore `level prec
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.error]
|
||||
def error.parenthesizer (msg : String) : Parenthesizer :=
|
||||
pure ()
|
||||
pure ()
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.try]
|
||||
def try.parenthesizer (p : Parenthesizer) : Parenthesizer :=
|
||||
p
|
||||
p
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.lookahead]
|
||||
def lookahead.parenthesizer (p : Parenthesizer) : Parenthesizer :=
|
||||
pure ()
|
||||
pure ()
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.notFollowedBy]
|
||||
def notFollowedBy.parenthesizer (p : Parenthesizer) : Parenthesizer :=
|
||||
pure ()
|
||||
pure ()
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.andthen]
|
||||
def andthen.parenthesizer (p1 p2 : Parenthesizer) : Parenthesizer :=
|
||||
p2 *> p1
|
||||
p2 *> p1
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.node]
|
||||
def node.parenthesizer (k : SyntaxNodeKind) (p : Parenthesizer) : Parenthesizer := do
|
||||
stx ← getCur;
|
||||
when (k != stx.getKind) $ do {
|
||||
trace! `PrettyPrinter.parenthesize.backtrack ("unexpected node kind '" ++ toString stx.getKind ++ "', expected '" ++ toString k ++ "'");
|
||||
-- HACK; see `orelse.parenthesizer`
|
||||
throwBacktrack
|
||||
};
|
||||
visitArgs p
|
||||
let stx ← getCur
|
||||
if k != stx.getKind then
|
||||
trace[PrettyPrinter.parenthesize.backtrack]! "unexpected node kind '{stx.getKind}', expected '{k}'"
|
||||
-- HACK; see `orelse.parenthesizer`
|
||||
throwBacktrack
|
||||
visitArgs p
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.checkPrec]
|
||||
def checkPrec.parenthesizer (prec : Nat) : Parenthesizer :=
|
||||
addPrecCheck prec
|
||||
addPrecCheck prec
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.leadingNode]
|
||||
def leadingNode.parenthesizer (k : SyntaxNodeKind) (prec : Nat) (p : Parenthesizer) : Parenthesizer := do
|
||||
node.parenthesizer k p;
|
||||
addPrecCheck prec;
|
||||
-- Limit `cont` precedence to `maxPrec-1`.
|
||||
-- This is because `maxPrec-1` is the precedence of function application, which is the only way to turn a leading parser
|
||||
-- into a trailing one.
|
||||
modify fun st => { st with contPrec := Nat.min (Parser.maxPrec-1) prec }
|
||||
node.parenthesizer k p
|
||||
addPrecCheck prec
|
||||
-- Limit `cont` precedence to `maxPrec-1`.
|
||||
-- This is because `maxPrec-1` is the precedence of function application, which is the only way to turn a leading parser
|
||||
-- into a trailing one.
|
||||
modify fun st => { st with contPrec := Nat.min (Parser.maxPrec-1) prec }
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.trailingNode]
|
||||
def trailingNode.parenthesizer (k : SyntaxNodeKind) (prec : Nat) (p : Parenthesizer) : Parenthesizer := do
|
||||
stx ← getCur;
|
||||
when (k != stx.getKind) $ do {
|
||||
trace! `PrettyPrinter.parenthesize.backtrack ("unexpected node kind '" ++ toString stx.getKind ++ "', expected '" ++ toString k ++ "'");
|
||||
-- HACK; see `orelse.parenthesizer`
|
||||
throwBacktrack
|
||||
};
|
||||
visitArgs $ do {
|
||||
p;
|
||||
addPrecCheck prec;
|
||||
ctx ← read;
|
||||
modify fun st => { st with contCat := ctx.cat };
|
||||
-- After visiting the nodes actually produced by the parser passed to `trailingNode`, we are positioned on the
|
||||
-- left-most child, which is the term injected by `trailingNode` in place of the recursion. Left recursion is not an
|
||||
-- issue for the parenthesizer, so we can think of this child being produced by `termParser 0`, or whichever Pratt
|
||||
-- parser is calling us.
|
||||
categoryParser.parenthesizer ctx.cat 0
|
||||
}
|
||||
let stx ← getCur
|
||||
if k != stx.getKind then
|
||||
trace[PrettyPrinter.parenthesize.backtrack]! "unexpected node kind '{stx.getKind}', expected '{k}'"
|
||||
-- HACK; see `orelse.parenthesizer`
|
||||
throwBacktrack
|
||||
visitArgs do
|
||||
p
|
||||
addPrecCheck prec
|
||||
let ctx ← read
|
||||
modify fun st => { st with contCat := ctx.cat }
|
||||
-- After visiting the nodes actually produced by the parser passed to `trailingNode`, we are positioned on the
|
||||
-- left-most child, which is the term injected by `trailingNode` in place of the recursion. Left recursion is not an
|
||||
-- issue for the parenthesizer, so we can think of this child being produced by `termParser 0`, or whichever Pratt
|
||||
-- parser is calling us.
|
||||
categoryParser.parenthesizer ctx.cat 0
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.symbol] def symbol.parenthesizer := visitToken
|
||||
@[combinatorParenthesizer Lean.Parser.unicodeSymbol] def unicodeSymbol.parenthesizer := visitToken
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.symbol] def symbol.parenthesizer (sym : String) := visitToken
|
||||
@[combinatorParenthesizer Lean.Parser.unicodeSymbol] def unicodeSymbol.parenthesizer (sym asciiSym : String) := visitToken
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.identNoAntiquot] def identNoAntiquot.parenthesizer := visitToken
|
||||
@[combinatorParenthesizer Lean.Parser.rawIdent] def rawIdent.parenthesizer := visitToken
|
||||
@[combinatorParenthesizer Lean.Parser.identEq] def identEq.parenthesizer := visitToken
|
||||
@[combinatorParenthesizer Lean.Parser.nonReservedSymbol] def nonReservedSymbol.parenthesizer := visitToken
|
||||
@[combinatorParenthesizer Lean.Parser.identEq] def identEq.parenthesizer (id : Name) := visitToken
|
||||
@[combinatorParenthesizer Lean.Parser.nonReservedSymbol] def nonReservedSymbol.parenthesizer (sym : String) (includeIdent : Bool) := visitToken
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.charLitNoAntiquot] def charLitNoAntiquot.parenthesizer := visitToken
|
||||
@[combinatorParenthesizer Lean.Parser.strLitNoAntiquot] def strLitNoAntiquot.parenthesizer := visitToken
|
||||
|
|
@ -402,49 +397,41 @@ visitArgs $ do {
|
|||
|
||||
@[combinatorParenthesizer Lean.Parser.many]
|
||||
def many.parenthesizer (p : Parenthesizer) : Parenthesizer := do
|
||||
stx ← getCur;
|
||||
visitArgs $ stx.getArgs.size.forM fun _ => p
|
||||
let stx ← getCur
|
||||
visitArgs $ stx.getArgs.size.forM fun _ => p
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.many1]
|
||||
def many1.parenthesizer (p : Parenthesizer) : Parenthesizer := do
|
||||
many.parenthesizer p
|
||||
many.parenthesizer p
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.many1Unbox]
|
||||
def many1Unbox.parenthesizer (p : Parenthesizer) : Parenthesizer := do
|
||||
stx ← getCur;
|
||||
if stx.getKind == nullKind then
|
||||
many.parenthesizer p
|
||||
else
|
||||
p
|
||||
let stx ← getCur
|
||||
if stx.getKind == nullKind then
|
||||
many.parenthesizer p
|
||||
else
|
||||
p
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.optional]
|
||||
def optional.parenthesizer (p : Parenthesizer) : Parenthesizer := do
|
||||
visitArgs p
|
||||
visitArgs p
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.sepBy]
|
||||
def sepBy.parenthesizer (p pSep : Parenthesizer) : Parenthesizer := do
|
||||
stx ← getCur;
|
||||
visitArgs $ (List.range stx.getArgs.size).reverse.forM $ fun i => if i % 2 == 0 then p else pSep
|
||||
let stx ← getCur
|
||||
visitArgs $ (List.range stx.getArgs.size).reverse.forM $ fun i => if i % 2 == 0 then p else pSep
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.sepBy1] def sepBy1.parenthesizer := sepBy.parenthesizer
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.withPosition] def withPosition.parenthesizer (p : Parenthesizer) : Parenthesizer := do
|
||||
p
|
||||
@[combinatorParenthesizer Lean.Parser.withoutPosition] def withoutPosition.parenthesizer (p : Parenthesizer) : Parenthesizer := do
|
||||
p
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.withForbidden] def withForbidden.parenthesizer (tk : Parser.Token) (p : Parenthesizer) : Parenthesizer := do
|
||||
p
|
||||
@[combinatorParenthesizer Lean.Parser.withoutForbidden] def withoutForbidden.parenthesizer (p : Parenthesizer) : Parenthesizer := do
|
||||
p
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.withPosition] def withPosition.parenthesizer (p : Parenthesizer) : Parenthesizer := p
|
||||
@[combinatorParenthesizer Lean.Parser.withoutPosition] def withoutPosition.parenthesizer (p : Parenthesizer) : Parenthesizer := p
|
||||
@[combinatorParenthesizer Lean.Parser.withForbidden] def withForbidden.parenthesizer (tk : Parser.Token) (p : Parenthesizer) : Parenthesizer := p
|
||||
@[combinatorParenthesizer Lean.Parser.withoutForbidden] def withoutForbidden.parenthesizer (p : Parenthesizer) : Parenthesizer := p
|
||||
@[combinatorParenthesizer Lean.Parser.setExpected]
|
||||
def setExpected.parenthesizer (expected : List String) (p : Parenthesizer) : Parenthesizer :=
|
||||
p
|
||||
def setExpected.parenthesizer (expected : List String) (p : Parenthesizer) : Parenthesizer := p
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.toggleInsideQuot]
|
||||
def toggleInsideQuot.parenthesizer (p : Parenthesizer) : Parenthesizer :=
|
||||
p
|
||||
def toggleInsideQuot.parenthesizer (p : Parenthesizer) : Parenthesizer := p
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.checkStackTop] def checkStackTop.parenthesizer : Parenthesizer := pure ()
|
||||
@[combinatorParenthesizer Lean.Parser.checkWsBefore] def checkWsBefore.parenthesizer : Parenthesizer := pure ()
|
||||
|
|
@ -466,29 +453,26 @@ p
|
|||
@[combinatorParenthesizer Lean.Parser.unquotedSymbol] def unquotedSymbol.parenthesizer := visitToken
|
||||
|
||||
@[combinatorParenthesizer Lean.Parser.interpolatedStr]
|
||||
def interpolatedStr.parenthesizer (p : Parenthesizer) : Parenthesizer :=
|
||||
throwError "NIY"
|
||||
def interpolatedStr.parenthesizer (p : Parenthesizer) : Parenthesizer := throwError "NIY"
|
||||
|
||||
@[combinatorParenthesizer ite, macroInline] def ite {α : Type} (c : Prop) [h : Decidable c] (t e : Parenthesizer) : Parenthesizer :=
|
||||
if c then t else e
|
||||
if c then t else e
|
||||
|
||||
end Parenthesizer
|
||||
open Parenthesizer
|
||||
|
||||
/-- Add necessary parentheses in `stx` parsed by `parser`. -/
|
||||
def parenthesize (parenthesizer : Parenthesizer) (stx : Syntax) : CoreM Syntax :=
|
||||
catchInternalId backtrackExceptionId
|
||||
(do
|
||||
(_, st) ← (parenthesizer {}).run { stxTrav := Syntax.Traverser.fromSyntax stx };
|
||||
pure st.stxTrav.cur)
|
||||
(fun _ => throwError "parenthesize: uncaught backtrack exception")
|
||||
catchInternalId backtrackExceptionId
|
||||
(do
|
||||
let (_, st) ← (parenthesizer {}).run { stxTrav := Syntax.Traverser.fromSyntax stx }
|
||||
pure st.stxTrav.cur)
|
||||
(fun _ => throwError "parenthesize: uncaught backtrack exception")
|
||||
|
||||
def parenthesizeTerm := parenthesize $ categoryParser.parenthesizer `term 0
|
||||
def parenthesizeCommand := parenthesize $ categoryParser.parenthesizer `command 0
|
||||
|
||||
@[builtinInit] private def regTraceClasses : IO Unit := do
|
||||
registerTraceClass `PrettyPrinter.parenthesize;
|
||||
pure ()
|
||||
builtin_initialize registerTraceClass `PrettyPrinter.parenthesize
|
||||
|
||||
end PrettyPrinter
|
||||
end Lean
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
#lang lean4
|
||||
import Lean
|
||||
open Lean
|
||||
open Lean.Parser
|
||||
|
||||
def testParser (input : String) : IO Unit :=
|
||||
do
|
||||
env ← mkEmptyEnvironment;
|
||||
stx ← IO.ofExcept $ runParserCategory env `term input "<input>";
|
||||
let env ← mkEmptyEnvironment;
|
||||
let stx ← IO.ofExcept $ runParserCategory env `term input "<input>";
|
||||
IO.println stx
|
||||
|
||||
def test (is : List String) : IO Unit :=
|
||||
|
|
@ -15,7 +16,7 @@ is.forM $ fun input => do
|
|||
|
||||
def testParserFailure (input : String) : IO Unit :=
|
||||
do
|
||||
env ← mkEmptyEnvironment;
|
||||
let env ← mkEmptyEnvironment;
|
||||
match runParserCategory env `term input "<input>" with
|
||||
| Except.ok stx => throw (IO.userError ("unexpected success\n" ++ toString stx))
|
||||
| Except.error msg => IO.println ("failed as expected, error: " ++ msg)
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
#lang lean4
|
||||
import Lean
|
||||
|
||||
namespace Lean
|
||||
|
||||
def ex1 : CoreM Nat := do
|
||||
env ← getEnv;
|
||||
let env ← getEnv
|
||||
pure $ privateExt.getState env
|
||||
|
||||
#eval ex1
|
||||
|
||||
def ex2 : CoreM Nat := do
|
||||
env ← getEnv;
|
||||
let env ← getEnv
|
||||
pure $ { privateExt with idx := 3 }.getState env -- Error
|
||||
|
||||
-- #eval ex2
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
2
|
||||
envExtensionSealed.lean:13:7: error: invalid structure notation source, not a structure
|
||||
Lean.privateExt
|
||||
which has type
|
||||
Lean.EnvExtensionInterface.ext Lean.EnvExtensionInterfaceImp Nat
|
||||
1
|
||||
envExtensionSealed.lean:14:7: error: invalid {...} notation, source type is not of the form (C ...)
|
||||
EnvExtensionInterfaceImp.1 Nat
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue