feat(library/init/lean/elaborator): improve namespace, scope, and end commands

This commit is contained in:
Leonardo de Moura 2019-07-22 08:14:35 -07:00
parent eb47746647
commit 2387f3c2a2
3 changed files with 51 additions and 14 deletions

View file

@ -19,6 +19,7 @@ structure ElabScope :=
(cmd : String)
(header : Name)
(options : Options := {})
(ns : Name := Name.anonymous) -- current namespace
structure ElabState :=
(env : Environment)
@ -315,6 +316,12 @@ namespace Elab
instance {α} : Inhabited (Elab α) :=
⟨fun _ => default _⟩
def getNamespace : Elab Name :=
do s ← get;
match s.scopes with
| [] => pure Name.anonymous
| (sc::_) => pure sc.ns
/- Remark: in an ideal world where performance doesn't matter, we would define `Elab` as
```
ExceptT ElabException (StateT ElabException IO)

View file

@ -9,31 +9,52 @@ import init.lean.elaborator.basic
namespace Lean
namespace Elab
private def addScopes (cmd : String) : Name → Name → List ElabScope → List ElabScope
| Name.anonymous ns scopes := scopes
| (Name.mkString p h) ns scopes :=
let ns := Name.mkString ns h;
{ cmd := cmd, header := h, ns := ns.mkString h } :: addScopes p ns scopes
| _ _ _ := [] -- unreachable
@[builtinCommandElab «namespace»] def elabNamespace : CommandElab :=
fun n => do
ns ← getNamespace;
header ← (n.getArg 1).getIdentVal;
modify $ fun s =>
let scope : ElabScope := { cmd := "namespace", header := header };
{ scopes := scope :: s.scopes, .. s }
modify $ fun s => { scopes := addScopes "namespace" header ns s.scopes, .. s }
@[builtinCommandElab «section»] def elabSection : CommandElab :=
fun n => do
ns ← getNamespace;
header ← (n.getArg 1).getOptionalIdent;
let header := header.getOrElse Name.anonymous;
modify $ fun s =>
let scope : ElabScope := { cmd := "section", header := header };
{ scopes := scope :: s.scopes, .. s }
match header with
| some header => { scopes := addScopes "section" header ns s.scopes, .. s }
| none => { scopes := { cmd := "section", header := Name.anonymous, ns := ns } :: s.scopes, .. s }
private def getNumEndScopes : Option Name → Nat
| none := 1
| (some n) := n.getNumParts
private def checkAnonymousScope : List ElabScope → Bool
| ({ header := Name.anonymous, .. } :: _) := true
| _ := false
private def checkEndHeader : Name → List ElabScope → Bool
| Name.anonymous _ := true
| (Name.mkString p s) ({ header := h, .. } :: scopes) := h.eqStr s && checkEndHeader p scopes
| _ _ := false
@[builtinCommandElab «end»] def elabEnd : CommandElab :=
fun n => do
s ← get;
match s.scopes with
| [] => throw "invalid 'end', there is no open scope"
| (scope::scopes) => do
modify $ fun s => { scopes := scopes, .. s };
header ← (n.getArg 1).getOptionalIdent;
let header := header.getOrElse Name.anonymous;
unless (scope.header == header) $ throw "invalid 'end', name mismatch"
s ← get;
header ← (n.getArg 1).getOptionalIdent;
let num := getNumEndScopes header;
let scopes := s.scopes;
modify $ fun s => { scopes := s.scopes.drop num, .. s };
when (num > scopes.length) $ throw "invalid 'end', insufficient scopes";
match header with
| none => unless (checkAnonymousScope scopes) $ throw "invalid 'end', name is missing"
| some header => unless (checkEndHeader header scopes) $ throw "invalid 'end', name mismatch"
end Elab
end Lean

View file

@ -43,6 +43,11 @@ def getPrefix : Name → Name
| (mkString p s) := p
| (mkNumeral p s) := p
def getNumParts : Name → Nat
| anonymous := 0
| (mkString p _) := getNumParts p + 1
| (mkNumeral p _) := getNumParts p + 1
def updatePrefix : Name → Name → Name
| anonymous newP := anonymous
| (mkString p s) newP := mkString newP s
@ -81,6 +86,10 @@ protected def decEq : ∀ (a b : @& Name), Decidable (a = b)
instance : DecidableEq Name :=
{decEq := Name.decEq}
def eqStr : Name → String → Bool
| (mkString Name.anonymous s) s' := s == s'
| _ _ := false
protected def append : Name → Name → Name
| n anonymous := n
| n (mkString p s) :=