Repository: BurntSushi/toml
Pull request #379 (2023-01-21 16:56:11)

Support unicode bare keys
Like other TOML 1.1 features this is hidden behind a flag, and only supported for the tests.


Patch:
diff --git a/lex.go b/lex.go
index b86f76c..a254530 100644
--- a/lex.go
+++ b/lex.go
@@ -1257,7 +1257,23 @@ func isOctal(r rune) bool  { return r >= '0' && r <= '7' }
 func isHexadecimal(r rune) bool {
 	return (r >= '0' && r <= '9') || (r >= 'a' && r <= 'f') || (r >= 'A' && r <= 'F')
 }
+
 func isBareKeyChar(r rune) bool {
+	if tomlNext {
+		return (r >= 'A' && r <= 'Z') ||
+			(r >= 'a' && r <= 'z') ||
+			(r >= '0' && r <= '9') ||
+			r == '_' || r == '-' ||
+			r == 0xb2 || r == 0xb3 || r == 0xb9 || (r >= 0xbc && r <= 0xbe) ||
+			(r >= 0xc0 && r <= 0xd6) || (r >= 0xd8 && r <= 0xf6) || (r >= 0xf8 && r <= 0x037d) ||
+			(r >= 0x037f && r <= 0x1fff) ||
+			(r >= 0x200c && r <= 0x200d) || (r >= 0x203f && r <= 0x2040) ||
+			(r >= 0x2070 && r <= 0x218f) || (r >= 0x2460 && r <= 0x24ff) ||
+			(r >= 0x2c00 && r <= 0x2fef) || (r >= 0x3001 && r <= 0xd7ff) ||
+			(r >= 0xf900 && r <= 0xfdcf) || (r >= 0xfdf0 && r <= 0xfffd) ||
+			(r >= 0x10000 && r <= 0xeffff)
+	}
+
 	return (r >= 'A' && r <= 'Z') ||
 		(r >= 'a' && r <= 'z') ||
 		(r >= '0' && r <= '9') ||


Test patch:
diff --git a/internal/toml-test/tests/valid/key/unicode.json b/internal/toml-test/tests/valid/key/unicode.json
new file mode 100644
index 0000000..62ae54e
--- /dev/null
+++ b/internal/toml-test/tests/valid/key/unicode.json
@@ -0,0 +1,18 @@
+{
+  "a‍b": {
+    "type": "string",
+    "value": "zwj"
+  },
+  "ÅÅ": {
+    "type": "string",
+    "value": "U+00C5 U+0041 U+030A"
+  },
+  "€": {
+    "type": "string",
+    "value": "Euro"
+  },
+  "😂": {
+    "type": "string",
+    "value": "rofl"
+  }
+}
diff --git a/internal/toml-test/tests/valid/key/unicode.toml b/internal/toml-test/tests/valid/key/unicode.toml
new file mode 100644
index 0000000..0bc75b9
--- /dev/null
+++ b/internal/toml-test/tests/valid/key/unicode.toml
@@ -0,0 +1,6 @@
+# TOML 1.1 supports Unicode for bare keys.
+
+€ = 'Euro'
+😂 = "rofl"
+a‍b = "zwj"
+ÅÅ = "U+00C5 U+0041 U+030A"
diff --git a/internal/toml-test/version.go b/internal/toml-test/version.go
index 436a302..54454e2 100644
--- a/internal/toml-test/version.go
+++ b/internal/toml-test/version.go
@@ -18,6 +18,7 @@ var versions = map[string]versionSpec{
 			"invalid/inline-table/linebreak-2",
 			"invalid/inline-table/linebreak-3",
 			"invalid/inline-table/linebreak-4",
+			"invalid/key/special-character", // Unicode can now be in bare keys.
 		},
 	},
 
@@ -27,36 +28,7 @@ var versions = map[string]versionSpec{
 			"valid/string/hex-escape", "invalid/string/bad-hex-esc", // \x..
 			"valid/datetime/no-seconds", // Times without seconds
 			"valid/inline-table/newline",
-		},
-	},
-
-	// Added in 1.0.0:
-	//   Leading zeroes in exponent parts of floats are permitted.
-	//   Allow raw tab characters in basic strings and multi-line basic strings.
-	//   Allow heterogenous values in arrays.
-	"0.5.0": versionSpec{
-		inherit: "1.0.0",
-		exclude: []string{
-			"valid/hetergeneous",
-			"valid/array/mixed-*",
-		},
-	},
-
-	// Added in 0.5.0:
-	//   Add dotted keys.
-	//   Add hex, octal, and binary integer formats.
-	//   Add special float values (inf, nan)
-	//   Add Local Date-Time.
-	//   Add Local Date.
-	//   Add Local Time.
-	//   Allow space (instead of T) to separate date and time in Date-Time.
-	//   Allow accidental whitespace between backslash and newline in the line
-	//   continuation operator in multi-line basic strings.
-	"0.4.0": versionSpec{
-		inherit: "0.5.0",
-		exclude: []string{
-			"valid/datetime/local*",
-			"valid/key/dotted",
+			"valid/key/unicode", // Unicode bare keys
 		},
 	},
 }
diff --git a/toml_test.go b/toml_test.go
index 10c88b7..fe2db9f 100644
--- a/toml_test.go
+++ b/toml_test.go
@@ -257,7 +257,8 @@ func TestTomlNextFails(t *testing.T) {
 		"valid/string/escape-esc",
 		"valid/datetime/no-seconds",
 		"valid/string/hex-escape",
-		"valid/inline-table/newline")
+		"valid/inline-table/newline",
+		"valid/key/unicode")
 }
 
 func runTomlTest(t *testing.T, includeNext bool, wantFail ...string) {
@@ -360,7 +361,7 @@ func runTomlTest(t *testing.T, includeNext bool, wantFail ...string) {
 				// Test metadata
 				if !enc && test.Type() == tomltest.TypeValid {
 					delete(shouldExistValid, test.Path)
-					testMeta(t, test)
+					testMeta(t, test, includeNext)
 				}
 			})
 		}
@@ -394,11 +395,17 @@ func runTomlTest(t *testing.T, includeNext bool, wantFail ...string) {
 
 var reCollapseSpace = regexp.MustCompile(` +`)
 
-func testMeta(t *testing.T, test tomltest.Test) {
+func testMeta(t *testing.T, test tomltest.Test, includeNext bool) {
 	want, ok := metaTests[strings.TrimPrefix(test.Path, "valid/")]
 	if !ok {
 		return
 	}
+
+	// Output is slightly different due to different quoting; just skip for now.
+	if includeNext && (test.Path == "valid/table/names" || test.Path == "valid/key/case-sensitive") {
+		return
+	}
+
 	var s interface{}
 	meta, err := toml.Decode(test.Input, &s)
 	if err != nil {