From da80ef06a91e3869cc3c1d4dbd07259c408ff490 Mon Sep 17 00:00:00 2001
From: Nadrieril
Date: Thu, 9 Apr 2020 23:09:47 +0100
Subject: Move Label escaping out of its Display impl

---
 dhall/src/syntax/text/printer.rs                   | 54 +++++++++++++---------
 .../success/regression/ToMapQuotedFieldsA.dhall    |  1 +
 .../success/regression/ToMapQuotedFieldsB.dhall    |  1 +
 serde_dhall/tests/de.rs                            | 16 ++++++-
 4 files changed, 49 insertions(+), 23 deletions(-)
 create mode 100644 dhall/tests/normalization/success/regression/ToMapQuotedFieldsA.dhall
 create mode 100644 dhall/tests/normalization/success/regression/ToMapQuotedFieldsB.dhall

diff --git a/dhall/src/syntax/text/printer.rs b/dhall/src/syntax/text/printer.rs
index 9e90660..ccba385 100644
--- a/dhall/src/syntax/text/printer.rs
+++ b/dhall/src/syntax/text/printer.rs
@@ -146,6 +146,21 @@ where
     f.write_str(close)
 }
 
+fn fmt_label(label: &Label, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+    // TODO: distinguish between reserved and nonreserved locations for quoting builtins
+    let s = String::from(label);
+    let is_reserved = match s.as_str() {
+        "let" | "in" | "if" | "then" | "else" | "Type" | "Kind" | "Sort"
+        | "True" | "False" | "Some" => true,
+        _ => Builtin::parse(&s).is_some(),
+    };
+    if !is_reserved && s.chars().all(|c| c.is_ascii_alphanumeric()) {
+        write!(f, "{}", s)
+    } else {
+        write!(f, "`{}`", s)
+    }
+}
+
 /// Generic instance that delegates to subexpressions
 impl<SE: Display + Clone> Display for ExprKind<SE> {
     fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
@@ -153,16 +168,21 @@ impl<SE: Display + Clone> Display for ExprKind<SE> {
         match self {
             Var(a) => a.fmt(f)?,
             Lam(a, b, c) => {
-                write!(f, "λ({} : {}) → {}", a, b, c)?;
+                write!(f, "λ(")?;
+                fmt_label(a, f)?;
+                write!(f, " : {}) → {}", b, c)?;
             }
             Pi(a, b, c) if &String::from(a) == "_" => {
                 write!(f, "{} → {}", b, c)?;
             }
             Pi(a, b, c) => {
-                write!(f, "∀({} : {}) → {}", a, b, c)?;
+                write!(f, "∀(")?;
+                fmt_label(a, f)?;
+                write!(f, " : {}) → {}", b, c)?;
             }
             Let(a, b, c, d) => {
-                write!(f, "let {}", a)?;
+                write!(f, "let ")?;
+                fmt_label(a, f)?;
                 if let Some(b) = b {
                     write!(f, " : {}", b)?;
                 }
@@ -183,14 +203,16 @@ impl<SE: Display + Clone> Display for ExprKind<SE> {
             }
             RecordLit(a) if a.is_empty() => f.write_str("{=}")?,
             RecordLit(a) => fmt_list("{ ", ", ", " }", a, f, |(k, v), f| {
-                write!(f, "{} = {}", k, v)
+                fmt_label(k, f)?;
+                write!(f, " = {}", v)
             })?,
             RecordType(a) if a.is_empty() => f.write_str("{}")?,
             RecordType(a) => fmt_list("{ ", ", ", " }", a, f, |(k, t), f| {
-                write!(f, "{} : {}", k, t)
+                fmt_label(k, f)?;
+                write!(f, " : {}", t)
             })?,
             UnionType(a) => fmt_list("< ", " | ", " >", a, f, |(k, v), f| {
-                write!(f, "{}", k)?;
+                fmt_label(k, f)?;
                 if let Some(v) = v {
                     write!(f, ": {}", v)?;
                 }
@@ -238,11 +260,12 @@ impl<SE: Display + Clone> Display for OpKind<SE> {
                 }
             }
             Field(a, b) => {
-                write!(f, "{}.{}", a, b)?;
+                write!(f, "{}.", a)?;
+                fmt_label(b, f)?;
             }
             Projection(e, ls) => {
                 write!(f, "{}.", e)?;
-                fmt_list("{ ", ", ", " }", ls, f, Display::fmt)?;
+                fmt_list("{ ", ", ", " }", ls, f, fmt_label)?;
             }
             ProjectionByExpr(a, b) => {
                 write!(f, "{}.({})", a, b)?;
@@ -379,18 +402,7 @@ impl Display for NaiveDouble {
 
 impl Display for Label {
     fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
-        // TODO: distinguish between reserved and nonreserved locations for quoting builtins
-        let s = String::from(self);
-        let is_reserved = match s.as_str() {
-            "let" | "in" | "if" | "then" | "else" | "Type" | "Kind"
-            | "Sort" | "True" | "False" | "Some" => true,
-            _ => Builtin::parse(&s).is_some(),
-        };
-        if !is_reserved && s.chars().all(|c| c.is_ascii_alphanumeric()) {
-            write!(f, "{}", s)
-        } else {
-            write!(f, "`{}`", s)
-        }
+        write!(f, "{}", String::from(self))
     }
 }
 
@@ -495,7 +507,7 @@ impl Display for Scheme {
 impl Display for V {
     fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
         let V(x, n) = self;
-        x.fmt(f)?;
+        fmt_label(x, f)?;
         if *n != 0 {
             write!(f, "@{}", n)?;
         }
diff --git a/dhall/tests/normalization/success/regression/ToMapQuotedFieldsA.dhall b/dhall/tests/normalization/success/regression/ToMapQuotedFieldsA.dhall
new file mode 100644
index 0000000..2b355fa
--- /dev/null
+++ b/dhall/tests/normalization/success/regression/ToMapQuotedFieldsA.dhall
@@ -0,0 +1 @@
+toMap { `if` = 0, `foo%bar` = 1 }
diff --git a/dhall/tests/normalization/success/regression/ToMapQuotedFieldsB.dhall b/dhall/tests/normalization/success/regression/ToMapQuotedFieldsB.dhall
new file mode 100644
index 0000000..d71242b
--- /dev/null
+++ b/dhall/tests/normalization/success/regression/ToMapQuotedFieldsB.dhall
@@ -0,0 +1 @@
+[ { mapKey = "foo%bar", mapValue = 1 }, { mapKey = "if", mapValue = 0 } ]
diff --git a/serde_dhall/tests/de.rs b/serde_dhall/tests/de.rs
index a5c42fd..41b4080 100644
--- a/serde_dhall/tests/de.rs
+++ b/serde_dhall/tests/de.rs
@@ -60,6 +60,9 @@ fn test_de_typed() {
 
 #[test]
 fn test_de_untyped() {
+    use std::collections::BTreeMap;
+    use std::collections::HashMap;
+
     fn parse<T: FromDhall>(s: &str) -> T {
         from_str(s).parse().unwrap()
     }
@@ -70,7 +73,6 @@ fn test_de_untyped() {
         (1, "foo".to_owned(), 42)
     );
 
-    use std::collections::HashMap;
     let mut expected_map = HashMap::new();
     expected_map.insert("x".to_string(), 1);
     expected_map.insert("y".to_string(), 2);
@@ -79,7 +81,17 @@ fn test_de_untyped() {
         expected_map
     );
 
-    use std::collections::BTreeMap;
+    let mut expected_map = HashMap::new();
+    expected_map.insert("if".to_string(), 1);
+    expected_map.insert("FOO_BAR".to_string(), 2);
+    expected_map.insert("baz-kux".to_string(), 3);
+    assert_eq!(
+        parse::<HashMap<String, usize>>(
+            "{ `if` = 1, FOO_BAR = 2, baz-kux = 3 }"
+        ),
+        expected_map
+    );
+
     let mut expected_map = BTreeMap::new();
     expected_map.insert("x".to_string(), 1);
     expected_map.insert("y".to_string(), 2);
-- 
cgit v1.2.3