feat: improve ToString/Quote Name

This commit is contained in:
Sebastian Ullrich 2021-05-16 17:48:49 +02:00
parent 3dafe26c72
commit 23f0c1051c
8 changed files with 72 additions and 58 deletions

View file

@ -69,15 +69,45 @@ def isIdEndEscape (c : Char) : Bool := c = idEndEscape
namespace Name
def toStringWithSep (sep : String) : Name → String
| anonymous => "[anonymous]"
| str anonymous s _ => s
| num anonymous v _ => toString v
| str n s _ => toStringWithSep sep n ++ sep ++ s
| num n v _ => toStringWithSep sep n ++ sep ++ Nat.repr v
def getRoot : Name → Name
| anonymous => anonymous
| n@(str anonymous _ _) => n
| n@(num anonymous _ _) => n
| str n _ _ => getRoot n
| num n _ _ => getRoot n
protected def toString : Name → String :=
toStringWithSep "."
@[export lean_is_inaccessible_user_name]
def isInaccessibleUserName : Name → Bool
| Name.str _ s _ => s.contains '✝' || s == "_inaccessible"
| Name.num p idx _ => isInaccessibleUserName p
| _ => false
def escapePart (s : String) : Option String :=
if s.length > 0 && isIdFirst s[0] && (s.toSubstring.drop 1).all isIdRest then s
else if s.any isIdEndEscape then none
else some <| idBeginEscape.toString ++ s ++ idEndEscape.toString
-- NOTE: does not roundtrip even with `escape = true` if name is anonymous or contains numeric part or `idEndEscape`
variable (sep : String) (escape : Bool)
def toStringWithSep : Name → String
| anonymous => "[anonymous]"
| str anonymous s _ => maybeEscape s
| num anonymous v _ => toString v
| str n s _ => toStringWithSep n ++ sep ++ maybeEscape s
| num n v _ => toStringWithSep n ++ sep ++ Nat.repr v
where
maybeEscape s := if escape then escapePart s |>.getD s else s
protected def toString (n : Name) (escape := true) : String :=
-- never escape "prettified" inaccessible names or macro scopes or pseudo-syntax introduced by the delaborator
toStringWithSep "." (escape && !n.isInaccessibleUserName && !n.hasMacroScopes && !maybePseudoSyntax) n
where
maybePseudoSyntax :=
if let Name.str _ s _ := n.getRoot then
-- could be pseudo-syntax for loose bvar or universe mvar, output as is
"#".isPrefixOf s || "?".isPrefixOf s
else
false
instance : ToString Name where
toString n := n.toString
@ -369,6 +399,9 @@ def mkNumLit (val : String) (info := SourceInfo.none) : Syntax :=
def mkScientificLit (val : String) (info := SourceInfo.none) : Syntax :=
mkLit scientificLitKind val info
def mkNameLit (val : String) (info := SourceInfo.none) : Syntax :=
mkLit nameLitKind val info
/- Recall that we don't have special Syntax constructors for storing numeric and string atoms.
The idea is to have an extensible approach where embedded DSLs may have new kind of atoms and/or
different ways of representing them. So, our atoms contain just the parsed string.
@ -665,12 +698,23 @@ instance : Quote String := ⟨Syntax.mkStrLit⟩
instance : Quote Nat := ⟨fun n => Syntax.mkNumLit <| toString n⟩
instance : Quote Substring := ⟨fun s => Syntax.mkCApp `String.toSubstring #[quote s.toString]⟩
private def quoteName : Name → Syntax
| Name.anonymous => mkCIdent ``Name.anonymous
| Name.str n s _ => Syntax.mkCApp ``Name.mkStr #[quoteName n, quote s]
| Name.num n i _ => Syntax.mkCApp ``Name.mkNum #[quoteName n, quote i]
-- in contrast to `Name.toString`, we can, and want to be, precise here
private def getEscapedNameParts? (acc : List String) : Name → OptionM (List String)
| Name.anonymous => acc
| Name.str n s _ => do
let s ← Name.escapePart s
getEscapedNameParts? (s::acc) n
| Name.num n i _ => none
instance : Quote Name := ⟨quoteName⟩
private def quoteNameMk : Name → Syntax
| Name.anonymous => mkCIdent ``Name.anonymous
| Name.str n s _ => Syntax.mkCApp ``Name.mkStr #[quoteNameMk n, quote s]
| Name.num n i _ => Syntax.mkCApp ``Name.mkNum #[quoteNameMk n, quote i]
instance : Quote Name where
quote n := match getEscapedNameParts? [] n with
| some ss => mkNode `Lean.Parser.Term.quotedName #[Syntax.mkNameLit ("`" ++ ".".intercalate ss)]
| none => quoteNameMk n
instance {α β : Type} [Quote α] [Quote β] : Quote (α × β) where
quote

View file

@ -20,13 +20,6 @@ def getPrefix : Name → Name
| str p s _ => p
| num p s _ => p
def getRoot : Name → Name
| anonymous => anonymous
| n@(str anonymous _ _) => n
| n@(num anonymous _ _) => n
| str n _ _ => getRoot n
| num n _ _ => getRoot n
def getString! : Name → String
| str _ s _ => s
| _ => unreachable!

View file

@ -62,12 +62,6 @@ private def mkInaccessibleUserName (unicode : Bool) : Name → Name
Name.mkNum (mkInaccessibleUserName unicode p) idx
| n => n
@[export lean_is_inaccessible_user_name]
def isInaccessibleUserName : Name → Bool
| Name.str _ s _ => s.contains '✝' || s == "_inaccessible"
| Name.num p idx _ => isInaccessibleUserName p
| _ => false
def sanitizeNamesDefault := true
def getSanitizeNames (o : Options) : Bool:= o.get `pp.sanitizeNames sanitizeNamesDefault
builtin_initialize

View file

@ -3,7 +3,6 @@ Copyright (c) 2020 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
-/
import Lean.Hygiene
import Lean.Meta.InferType
namespace Lean.Meta
@ -132,7 +131,7 @@ def collect (goalTarget : Expr) : MetaM (NameSet × NameSet) := do
else
let lctx ← getLCtx
let hiddenInaccessible := lctx.foldl (init := {}) fun hiddenInaccessible localDecl => do
if isInaccessibleUserName localDecl.userName then
if localDecl.userName.isInaccessibleUserName then
hiddenInaccessible.insert localDecl.fvarId
else
hiddenInaccessible

View file

@ -1054,9 +1054,9 @@ private def nameLitAux (startPos : Nat) : ParserFn := fun c s =>
else
let stx := s.stxStack.back
match stx with
| Syntax.ident _ rawStr _ _ =>
| Syntax.ident info rawStr _ _ =>
let s := s.popSyntax
s.pushSyntax (Syntax.node nameLitKind #[mkAtomFrom stx rawStr.toString])
s.pushSyntax (Syntax.mkNameLit rawStr.toString info)
| _ => s.mkError "invalid Name literal"
private def tokenFnAux : ParserFn := fun c s =>

View file

@ -336,27 +336,11 @@ def unicodeSymbolNoAntiquot.formatter (sym asciiSym : String) : Formatter := do
@[combinatorFormatter Lean.Parser.identNoAntiquot]
def identNoAntiquot.formatter : Formatter := do
checkKind identKind;
checkKind identKind
let Syntax.ident info _ id _ ← getCur
| throwError m!"not an ident: {← getCur}"
let id := id.simpMacroScopes
let s := id.toString;
if id.isAnonymous then
pushToken info "[anonymous]"
else if isInaccessibleUserName id || id.components.any Name.isNum ||
-- loose bvar
"#".isPrefixOf s then
-- not parsable anyway, output as-is
pushToken info s
else
-- try to parse `s` as-is; if it fails, escape
let pst ← parseToken s
if pst.pos == s.bsize then
pushToken info s
else
-- TODO: do something better than escaping all parts
let id := (id.components.map fun c => "«" ++ toString c ++ "»").foldl Name.mkStr Name.anonymous
pushToken info id.toString
pushToken info id.toString
goLeft
@[combinatorFormatter Lean.Parser.rawIdentNoAntiquot] def rawIdentNoAntiquot.formatter : Formatter := do

View file

@ -1,8 +1,8 @@
301.lean:1:9-1:17: error: missing cases:
(Nat.succ _)
301.lean:1:21-1:24: error: type mismatch
(
«»
(«»
fun (x : Nat) =>
match x with
| 0 => 0)

View file

@ -3,9 +3,9 @@ StxQuot.lean:8:12: error: expected command, identifier or term
"<missing>"
"<missing>"
"<missing>"
"(term_+_ <missing> \"+\" (numLit \"1\"))"
"(term_+_ <missing> \"+\" (numLit \"1\"))"
"(term_+_ (numLit \"1\") \"+\" (numLit \"1\"))"
"(«term_+_» <missing> \"+\" (numLit \"1\"))"
"(«term_+_» <missing> \"+\" (numLit \"1\"))"
"(«term_+_» (numLit \"1\") \"+\" (numLit \"1\"))"
StxQuot.lean:18:15: error: expected term
"(Term.fun \"fun\" (Term.basicFun [`a._@.UnhygienicMain._hyg.1] \"=>\" `a._@.UnhygienicMain._hyg.1))"
"(Command.declaration\n (Command.declModifiers [] [] [] [] [] [])\n (Command.def\n \"def\"\n (Command.declId `foo._@.UnhygienicMain._hyg.1 [])\n (Command.optDeclSig [] [])\n (Command.declValSimple \":=\" (numLit \"1\") [])))"
@ -13,11 +13,11 @@ StxQuot.lean:18:15: error: expected term
"`Nat.one._@.UnhygienicMain._hyg.1"
"`Nat.one._@.UnhygienicMain._hyg.1"
"(Term.app `f._@.UnhygienicMain._hyg.1 [`Nat.one._@.UnhygienicMain._hyg.1 `Nat.one._@.UnhygienicMain._hyg.1])"
"(term_$__\n `f._@.UnhygienicMain._hyg.1\n \"$\"\n (Term.app `f._@.UnhygienicMain._hyg.1 [`Nat.one._@.UnhygienicMain._hyg.1 (numLit \"1\")]))"
"(«term_$__»\n `f._@.UnhygienicMain._hyg.1\n \"$\"\n (Term.app `f._@.UnhygienicMain._hyg.1 [`Nat.one._@.UnhygienicMain._hyg.1 (numLit \"1\")]))"
"(Term.app `f._@.UnhygienicMain._hyg.1 [`Nat.one._@.UnhygienicMain._hyg.1])"
"(Term.proj `Nat.one._@.UnhygienicMain._hyg.1 \".\" `b._@.UnhygienicMain._hyg.1)"
"(term_+_ (numLit \"2\") \"+\" (numLit \"1\"))"
"(term_+_ (term_+_ (numLit \"1\") \"+\" (numLit \"2\")) \"+\" (numLit \"1\"))"
"(«term_+_» (numLit \"2\") \"+\" (numLit \"1\"))"
"(«term_+_» («term_+_» (numLit \"1\") \"+\" (numLit \"2\")) \"+\" (numLit \"1\"))"
"(Command.declaration\n (Command.declModifiers [] [] [] [] [] [])\n (Command.def\n \"def\"\n (Command.declId `foo._@.UnhygienicMain._hyg.1 [])\n (Command.optDeclSig [] [])\n (Command.declValSimple \":=\" (numLit \"1\") [])))"
"[(Command.declaration\n (Command.declModifiers [] [] [] [] [] [])\n (Command.def\n \"def\"\n (Command.declId `bar._@.UnhygienicMain._hyg.1 [])\n (Command.optDeclSig [] [])\n (Command.declValSimple \":=\" (numLit \"2\") [])))\n (Command.declaration\n (Command.declModifiers [] [] [] [] [] [])\n (Command.def\n \"def\"\n (Command.declId `foo._@.UnhygienicMain._hyg.1 [])\n (Command.optDeclSig [] [])\n (Command.declValSimple \":=\" (numLit \"1\") [])))]"
"0"
@ -36,8 +36,8 @@ StxQuot.lean:18:15: error: expected term
"(Term.structInst\n \"{\"\n []\n [(group\n (Term.structInstField (Term.structInstLVal `a._@.UnhygienicMain._hyg.1 []) \":=\" `a._@.UnhygienicMain._hyg.1)\n [])]\n (Term.optEllipsis [])\n []\n \"}\")"
"(Command.section \"section\" [])"
"(Command.section \"section\" [`foo._@.UnhygienicMain._hyg.1])"
"(Term.match\n \"match\"\n []\n [(Term.matchDiscr [] `a._@.UnhygienicMain._hyg.1)]\n []\n \"with\"\n (Term.matchAlts\n [(Term.matchAlt \"|\" [`a._@.UnhygienicMain._hyg.1] \"=>\" `b._@.UnhygienicMain._hyg.1)\n (Term.matchAlt\n \"|\"\n [(term_+_ `a._@.UnhygienicMain._hyg.1 \"+\" (numLit \"1\"))]\n \"=>\"\n (term_+_ `b._@.UnhygienicMain._hyg.1 \"+\" (numLit \"1\")))]))"
"(Term.match\n \"match\"\n []\n [(Term.matchDiscr [] `a._@.UnhygienicMain._hyg.1)]\n []\n \"with\"\n (Term.matchAlts\n [(Term.matchAlt \"|\" [`a._@.UnhygienicMain._hyg.1] \"=>\" `b._@.UnhygienicMain._hyg.1)\n (Term.matchAlt\n \"|\"\n [(term_+_ `a._@.UnhygienicMain._hyg.1 \"+\" (numLit \"1\"))]\n \"=>\"\n (term_+_ `b._@.UnhygienicMain._hyg.1 \"+\" (numLit \"1\")))]))"
"(Term.match\n \"match\"\n []\n [(Term.matchDiscr [] `a._@.UnhygienicMain._hyg.1)]\n []\n \"with\"\n (Term.matchAlts\n [(Term.matchAlt \"|\" [`a._@.UnhygienicMain._hyg.1] \"=>\" `b._@.UnhygienicMain._hyg.1)\n (Term.matchAlt\n \"|\"\n [(«term_+_» `a._@.UnhygienicMain._hyg.1 \"+\" (numLit \"1\"))]\n \"=>\"\n («term_+_» `b._@.UnhygienicMain._hyg.1 \"+\" (numLit \"1\")))]))"
"(Term.match\n \"match\"\n []\n [(Term.matchDiscr [] `a._@.UnhygienicMain._hyg.1)]\n []\n \"with\"\n (Term.matchAlts\n [(Term.matchAlt \"|\" [`a._@.UnhygienicMain._hyg.1] \"=>\" `b._@.UnhygienicMain._hyg.1)\n (Term.matchAlt\n \"|\"\n [(«term_+_» `a._@.UnhygienicMain._hyg.1 \"+\" (numLit \"1\"))]\n \"=>\"\n («term_+_» `b._@.UnhygienicMain._hyg.1 \"+\" (numLit \"1\")))]))"
"#[`a._@.UnhygienicMain._hyg.1, `b._@.UnhygienicMain._hyg.1]"
"1"
"(Term.sufficesDecl [] `x._@.UnhygienicMain._hyg.1 (Term.fromTerm \"from\" `x._@.UnhygienicMain._hyg.1))"