lean4-htt/src/Lean/Elab/ResolveName.lean
2020-06-25 11:21:17 -07:00

134 lines
5.5 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) 2019 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.Hygiene
import Lean.Modifiers
import Lean.Elab.Alias
namespace Lean
namespace Elab
inductive OpenDecl
| simple (ns : Name) (except : List Name)
| explicit (id : Name) (declName : Name)
namespace OpenDecl
instance : Inhabited OpenDecl := ⟨simple Name.anonymous []⟩
instance : HasToString OpenDecl :=
⟨fun decl => match decl with
| explicit id decl => toString id ++ " → " ++ toString decl
| simple ns ex => toString ns ++ (if ex == [] then "" else " hiding " ++ toString ex)⟩
end OpenDecl
def rootNamespace := `_root_
def removeRoot (n : Name) : Name :=
n.replacePrefix rootNamespace Name.anonymous
/- Global name resolution -/
/- Check whether `ns ++ id` is a valid namepace name and/or there are aliases names `ns ++ id`. -/
private def resolveQualifiedName (env : Environment) (ns : Name) (id : Name) : List Name :=
let resolvedId := ns ++ id;
let resolvedIds := getAliases env resolvedId;
if env.contains resolvedId && (!id.isAtomic || !isProtected env resolvedId) then resolvedId :: resolvedIds
else
-- Check whether environment contains the private version. That is, `_private.<module_name>.ns.id`.
let resolvedIdPrv := mkPrivateName env resolvedId;
if env.contains resolvedIdPrv then resolvedIdPrv :: resolvedIds
else resolvedIds
/- Check surrounding namespaces -/
private def resolveUsingNamespace (env : Environment) (id : Name) : Name → List Name
| ns@(Name.str p _ _) =>
match resolveQualifiedName env ns id with
| [] => resolveUsingNamespace p
| resolvedIds => resolvedIds
| _ => []
/- Check exact name -/
private def resolveExact (env : Environment) (id : Name) : Option Name :=
if id.isAtomic then none
else
let resolvedId := id.replacePrefix rootNamespace Name.anonymous;
if env.contains resolvedId then some resolvedId
else
-- We also allow `_root` when accessing private declarations.
-- If we change our minds, we should just replace `resolvedId` with `id`
let resolvedIdPrv := mkPrivateName env resolvedId;
if env.contains resolvedIdPrv then some resolvedIdPrv
else none
/- Check open namespaces -/
private def resolveOpenDecls (env : Environment) (id : Name) : List OpenDecl → List Name → List Name
| [], resolvedIds => resolvedIds
| OpenDecl.simple ns exs :: openDecls, resolvedIds =>
if exs.elem id then resolveOpenDecls openDecls resolvedIds
else
let newResolvedIds := resolveQualifiedName env ns id;
resolveOpenDecls openDecls (newResolvedIds ++ resolvedIds)
| OpenDecl.explicit openedId resolvedId :: openDecls, resolvedIds =>
let resolvedIds := if id == openedId then resolvedId :: resolvedIds else resolvedIds;
resolveOpenDecls openDecls resolvedIds
private def resolveGlobalNameAux (env : Environment) (ns : Name) (openDecls : List OpenDecl)
(scpView : MacroScopesView) : Name → List String → List (Name × List String)
| id@(Name.str p s _), projs =>
-- NOTE: we assume that macro scopes always belong to the projected constant, not the projections
let id := { scpView with name := id }.review;
match resolveUsingNamespace env id ns with
| resolvedIds@(_ :: _) => resolvedIds.eraseDups.map $ fun id => (id, projs)
| [] =>
match resolveExact env id with
| some newId => [(newId, projs)]
| none =>
let resolvedIds := if env.contains id then [id] else [];
let resolvedIds := resolveOpenDecls env id openDecls resolvedIds;
let resolvedIds := getAliases env id ++ resolvedIds;
match resolvedIds with
| resolvedIds@(_ :: _) => resolvedIds.eraseDups.map $ fun id => (id, projs)
| [] => resolveGlobalNameAux p (s::projs)
| _, _ => []
def resolveGlobalName (env : Environment) (ns : Name) (openDecls : List OpenDecl) (id : Name) : List (Name × List String) :=
-- decode macro scopes from name before recursion
let extractionResult := extractMacroScopes id;
resolveGlobalNameAux env ns openDecls extractionResult extractionResult.name []
/- Namespace resolution -/
def resolveNamespaceUsingScope (env : Environment) (n : Name) : Name → Option Name
| Name.anonymous => none
| ns@(Name.str p _ _) => if isNamespace env (ns ++ n) then some (ns ++ n) else resolveNamespaceUsingScope p
| _ => unreachable!
def resolveNamespaceUsingOpenDecls (env : Environment) (n : Name) : List OpenDecl → Option Name
| [] => none
| OpenDecl.simple ns [] :: ds => if isNamespace env (ns ++ n) then some (ns ++ n) else resolveNamespaceUsingOpenDecls ds
| _ :: ds => resolveNamespaceUsingOpenDecls ds
/-
Given a name `id` try to find namespace it refers to. The resolution procedure works as follows
1- If `id` is the extact name of an existing namespace, then return `id`
2- If `id` is in the scope of `namespace` commands the namespace `s_1. ... . s_n`,
then return `s_1 . ... . s_i ++ n` if it is the name of an existing namespace. We search "backwards".
3- Finally, for each command `open N`, return `N ++ n` if it is the name of an existing namespace.
We search "backwards" again. That is, we try the most recent `open` command first.
We only consider simple `open` commands.
-/
def resolveNamespace (env : Environment) (ns : Name) (openDecls : List OpenDecl) (id : Name) : Option Name :=
if isNamespace env id then some id
else match resolveNamespaceUsingScope env id ns with
| some n => some n
| none =>
match resolveNamespaceUsingOpenDecls env id openDecls with
| some n => some n
| none => none
end Elab
end Lean