/- 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..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