lean4-htt/src/Lean/Elab/Declaration.lean

278 lines
11 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/-
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Sebastian Ullrich
-/
import Lean.Util.CollectLevelParams
import Lean.Elab.DeclUtil
import Lean.Elab.DefView
import Lean.Elab.Inductive
import Lean.Elab.Structure
import Lean.Elab.MutualDef
namespace Lean.Elab.Command
open Meta
/- Auxiliary function for `expandDeclNamespace?` -/
def expandDeclIdNamespace? (declId : Syntax) : Option (Name × Syntax) :=
let (id, optUnivDeclStx) := expandDeclIdCore declId
let scpView := extractMacroScopes id
match scpView.name with
| Name.str Name.anonymous s _ => none
| Name.str pre s _ =>
let nameNew := { scpView with name := Name.mkSimple s }.review
if declId.isIdent then
some (pre, mkIdentFrom declId nameNew)
else
some (pre, declId.setArg 0 (mkIdentFrom declId nameNew))
| _ => none
/- given declarations such as `@[...] def Foo.Bla.f ...` return `some (Foo.Bla, @[...] def f ...)` -/
def expandDeclNamespace? (stx : Syntax) : Option (Name × Syntax) :=
if !stx.isOfKind `Lean.Parser.Command.declaration then none
else
let decl := stx[1]
let k := decl.getKind
if k == `Lean.Parser.Command.abbrev ||
k == `Lean.Parser.Command.def ||
k == `Lean.Parser.Command.theorem ||
k == `Lean.Parser.Command.constant ||
k == `Lean.Parser.Command.axiom ||
k == `Lean.Parser.Command.inductive ||
k == `Lean.Parser.Command.classInductive ||
k == `Lean.Parser.Command.structure then
match expandDeclIdNamespace? decl[1] with
| some (ns, declId) => some (ns, stx.setArg 1 (decl.setArg 1 declId))
| none => none
else if k == `Lean.Parser.Command.instance then
let optDeclId := decl[3]
if optDeclId.isNone then none
else match expandDeclIdNamespace? optDeclId[0] with
| some (ns, declId) => some (ns, stx.setArg 1 (decl.setArg 3 (optDeclId.setArg 0 declId)))
| none => none
else
none
def elabAxiom (modifiers : Modifiers) (stx : Syntax) : CommandElabM Unit := do
-- parser! "axiom " >> declId >> declSig
let declId := stx[1]
let (binders, typeStx) := expandDeclSig stx[2]
let scopeLevelNames ← getLevelNames
let ⟨name, declName, allUserLevelNames⟩ ← expandDeclId declId modifiers
runTermElabM declName fun vars => Term.withLevelNames allUserLevelNames $ Term.elabBinders binders.getArgs fun xs => do
Term.applyAttributesAt declName modifiers.attrs AttributeApplicationTime.beforeElaboration
let type ← Term.elabType typeStx
Term.synthesizeSyntheticMVarsNoPostponing
let type ← instantiateMVars type
let type ← mkForallFVars xs type
let (type, _) ← mkForallUsedOnly vars type
let (type, _) ← Term.levelMVarToParam type
let usedParams := collectLevelParams {} type |>.params
match sortDeclLevelParams scopeLevelNames allUserLevelNames usedParams with
| Except.error msg => throwErrorAt stx msg
| Except.ok levelParams =>
let decl := Declaration.axiomDecl {
name := declName,
lparams := levelParams,
type := type,
isUnsafe := modifiers.isUnsafe
}
Term.ensureNoUnassignedMVars decl
addDecl decl
Term.applyAttributesAt declName modifiers.attrs AttributeApplicationTime.afterTypeChecking
Term.applyAttributesAt declName modifiers.attrs AttributeApplicationTime.afterCompilation
/-
parser! "inductive " >> declId >> optDeclSig >> optional ":=" >> many ctor
parser! atomic (group ("class " >> "inductive ")) >> declId >> optDeclSig >> optional ":=" >> many ctor >> optDeriving
-/
private def inductiveSyntaxToView (modifiers : Modifiers) (decl : Syntax) : CommandElabM InductiveView := do
checkValidInductiveModifier modifiers
let (binders, type?) := expandOptDeclSig decl[2]
let declId := decl[1]
let ⟨name, declName, levelNames⟩ ← expandDeclId declId modifiers
let ctors ← decl[4].getArgs.mapM fun ctor => withRef ctor do
-- def ctor := parser! " | " >> declModifiers >> ident >> optional inferMod >> optDeclSig
let ctorModifiers ← elabModifiers ctor[1]
if ctorModifiers.isPrivate && modifiers.isPrivate then
throwError "invalid 'private' constructor in a 'private' inductive datatype"
if ctorModifiers.isProtected && modifiers.isPrivate then
throwError "invalid 'protected' constructor in a 'private' inductive datatype"
checkValidCtorModifier ctorModifiers
let ctorName := ctor.getIdAt 2
let ctorName := declName ++ ctorName
let ctorName ← withRef ctor[2] $ applyVisibility ctorModifiers.visibility ctorName
let inferMod := !ctor[3].isNone
let (binders, type?) := expandOptDeclSig ctor[4]
pure { ref := ctor, modifiers := ctorModifiers, declName := ctorName, inferMod := inferMod, binders := binders, type? := type? : CtorView }
let classes ← getOptDerivingClasses decl[5]
pure {
ref := decl
modifiers := modifiers
shortDeclName := name
declName := declName
levelNames := levelNames
binders := binders
type? := type?
ctors := ctors
derivingClasses := classes
}
private def classInductiveSyntaxToView (modifiers : Modifiers) (decl : Syntax) : CommandElabM InductiveView :=
inductiveSyntaxToView modifiers decl
def elabInductive (modifiers : Modifiers) (stx : Syntax) : CommandElabM Unit := do
let v ← inductiveSyntaxToView modifiers stx
elabInductiveViews #[v]
def elabClassInductive (modifiers : Modifiers) (stx : Syntax) : CommandElabM Unit := do
let modifiers := modifiers.addAttribute { name := `class }
let v ← classInductiveSyntaxToView modifiers stx
elabInductiveViews #[v]
@[builtinCommandElab declaration]
def elabDeclaration : CommandElab := fun stx =>
match expandDeclNamespace? stx with
| some (ns, newStx) => do
let ns := mkIdentFrom stx ns
let newStx ← `(namespace $ns:ident $newStx end $ns:ident)
withMacroExpansion stx newStx $ elabCommand newStx
| none => do
let modifiers ← elabModifiers stx[0]
let decl := stx[1]
let declKind := decl.getKind
if declKind == `Lean.Parser.Command.«axiom» then
elabAxiom modifiers decl
else if declKind == `Lean.Parser.Command.«inductive» then
elabInductive modifiers decl
else if declKind == `Lean.Parser.Command.classInductive then
elabClassInductive modifiers decl
else if declKind == `Lean.Parser.Command.«structure» then
elabStructure modifiers decl
else if isDefLike decl then
elabMutualDef #[stx]
else
throwError "unexpected declaration"
/- Return true if all elements of the mutual-block are inductive declarations. -/
private def isMutualInductive (stx : Syntax) : Bool :=
stx[1].getArgs.all fun elem =>
let decl := elem[1]
let declKind := decl.getKind
declKind == `Lean.Parser.Command.inductive
private def elabMutualInductive (elems : Array Syntax) : CommandElabM Unit := do
let views ← elems.mapM fun stx => do
let modifiers ← elabModifiers stx[0]
inductiveSyntaxToView modifiers stx[1]
elabInductiveViews views
/- Return true if all elements of the mutual-block are definitions/theorems/abbrevs. -/
private def isMutualDef (stx : Syntax) : Bool :=
stx[1].getArgs.all fun elem =>
let decl := elem[1]
isDefLike decl
private def isMutualPreambleCommand (stx : Syntax) : Bool :=
let k := stx.getKind
k == `Lean.Parser.Command.variable ||
k == `Lean.Parser.Command.variables ||
k == `Lean.Parser.Command.universe ||
k == `Lean.Parser.Command.universes ||
k == `Lean.Parser.Command.check ||
k == `Lean.Parser.Command.set_option ||
k == `Lean.Parser.Command.open
private partial def splitMutualPreamble (elems : Array Syntax) : Option (Array Syntax × Array Syntax) :=
let rec loop (i : Nat) : Option (Array Syntax × Array Syntax) :=
if h : i < elems.size then
let elem := elems.get ⟨i, h⟩
if isMutualPreambleCommand elem then
loop (i+1)
else if i == 0 then
none -- `mutual` block does not contain any preamble commands
else
some (elems[0:i], elems[i:elems.size])
else
none -- a `mutual` block containing only preamble commands is not a valid `mutual` block
loop 0
@[builtinMacro Lean.Parser.Command.mutual]
def expandMutualNamespace : Macro := fun stx => do
let mut ns? := none
let mut elemsNew := #[]
for elem in stx[1].getArgs do
match ns?, expandDeclNamespace? elem with
| _, none => elemsNew := elemsNew.push elem
| none, some (ns, elem) => ns? := some ns; elemsNew := elemsNew.push elem
| some nsCurr, some (nsNew, elem) =>
if nsCurr == nsNew then
elemsNew := elemsNew.push elem
else
Macro.throwErrorAt elem s!"conflicting namespaces in mutual declaration, using namespace '{nsNew}', but used '{nsCurr}' in previous declaration"
match ns? with
| some ns =>
let ns := mkIdentFrom stx ns
let stxNew := stx.setArg 1 (mkNullNode elemsNew)
`(namespace $ns:ident $stxNew end $ns:ident)
| none => Macro.throwUnsupported
@[builtinMacro Lean.Parser.Command.mutual]
def expandMutualElement : Macro := fun stx => do
let mut elemsNew := #[]
let mut modified := false
for elem in stx[1].getArgs do
match (← expandMacro? elem) with
| some elemNew => elemsNew := elemsNew.push elemNew; modified := true
| none => elemsNew := elemsNew.push elem
if modified then
pure $ stx.setArg 1 (mkNullNode elemsNew)
else
Macro.throwUnsupported
@[builtinMacro Lean.Parser.Command.mutual]
def expandMutualPreamble : Macro := fun stx =>
match splitMutualPreamble stx[1].getArgs with
| none => Macro.throwUnsupported
| some (preamble, rest) => do
let secCmd ← `(section)
let newMutual := stx.setArg 1 (mkNullNode rest)
let endCmd ← `(end)
pure $ mkNullNode (#[secCmd] ++ preamble ++ #[newMutual] ++ #[endCmd])
@[builtinCommandElab «mutual»]
def elabMutual : CommandElab := fun stx => do
if isMutualInductive stx then
elabMutualInductive stx[1].getArgs
else if isMutualDef stx then
elabMutualDef stx[1].getArgs
else
throwError "invalid mutual block"
/- parser! "attribute " >> "[" >> sepBy1 Term.attrInstance ", " >> "]" >> many1 ident -/
@[builtinCommandElab «attribute»] def elabAttr : CommandElab := fun stx => do
let attrs ← elabAttrs stx[2]
let idents := stx[4].getArgs
for ident in idents do withRef ident $ liftTermElabM none do
let declName ← resolveGlobalConstNoOverload ident.getId
Term.applyAttributes declName attrs
def expandInitCmd (builtin : Bool) : Macro := fun stx =>
let optHeader := stx[1]
let doSeq := stx[2]
let attrId := mkIdentFrom stx $ if builtin then `builtinInit else `init
if optHeader.isNone then
`(@[$attrId:ident]def initFn : IO Unit := do $doSeq)
else
let id := optHeader[0]
let type := optHeader[1][1]
`(def initFn : IO $type := do $doSeq
@[$attrId:ident initFn]constant $id : $type)
@[builtinMacro Lean.Parser.Command.«initialize»] def expandInitialize : Macro :=
expandInitCmd (builtin := false)
@[builtinMacro Lean.Parser.Command.«builtin_initialize»] def expandBuiltinInitialize : Macro :=
expandInitCmd (builtin := true)
end Lean.Elab.Command