Basic keys parsing
diff --git a/keysparsing.go b/keysparsing.go new file mode 100644 index 0000000..6bdb0b8 --- /dev/null +++ b/keysparsing.go
@@ -0,0 +1,55 @@ +// Parsing keys handling both bare and quoted keys. + +package toml + +import ( + "bytes" + "fmt" +) + +func parseKey(key string) ([]string, error) { + groups := []string{} + var buffer bytes.Buffer + inQuotes := false + escapeNext := false + for _, char := range key { + if escapeNext { + buffer.WriteRune(char) + escapeNext = false + continue + } + switch char { + case '\\': + escapeNext = true + continue + case '"': + inQuotes = !inQuotes + case '.': + if inQuotes { + buffer.WriteRune(char) + } else { + groups = append(groups, buffer.String()) + buffer.Reset() + } + default: + if !inQuotes && !isValidBareChar(char) { + return nil, fmt.Errorf("invalid bare character: %c", char) + } + buffer.WriteRune(char) + } + } + if inQuotes { + return nil, fmt.Errorf("mismatched quotes") + } + if escapeNext { + return nil, fmt.Errorf("unfinished escape sequence") + } + if buffer.Len() > 0 { + groups = append(groups, buffer.String()) + } + return groups, nil +} + +func isValidBareChar(r rune) bool { + return isAlphanumeric(r) || r == '-' +}
diff --git a/keysparsing_test.go b/keysparsing_test.go new file mode 100644 index 0000000..2d5379f --- /dev/null +++ b/keysparsing_test.go
@@ -0,0 +1,44 @@ +package toml + +import ( + "fmt" + "testing" +) + +func testResult(t *testing.T, key string, expected []string) { + parsed, err := parseKey(key) + if err != nil { + t.Fatal("Unexpected error:", err) + } + if len(expected) != len(parsed) { + t.Fatal("Expected length", len(expected), "but", len(parsed), "parsed") + } + for index, expectedKey := range expected { + if expectedKey != parsed[index] { + t.Fatal("Expected", expectedKey, "at index", index, "but found", parsed[index]) + } + } +} + +func testError(t *testing.T, key string, expectedError string) { + _, err := parseKey(key) + if fmt.Sprintf("%s", err) != expectedError { + t.Fatalf("Expected error \"%s\", but got \"%s\".", expectedError, err) + } +} + +func TestBareKeyBasic(t *testing.T) { + testResult(t, "test", []string{"test"}) +} + +func TestBareKeyDotted(t *testing.T) { + testResult(t, "this.is.a.key", []string{"this", "is", "a", "key"}) +} + +func TestDottedKeyBasic(t *testing.T) { + testResult(t, "\"a.dotted.key\"", []string{"a.dotted.key"}) +} + +func TestBaseKeyPound(t *testing.T) { + testError(t, "hello#world", "invalid bare character: #") +}
diff --git a/parser.go b/parser.go index e83647d..e4d1083 100644 --- a/parser.go +++ b/parser.go
@@ -98,7 +98,10 @@ } // get or create group array element at the indicated part in the path - keys := strings.Split(key.val, ".") + keys, err := parseKey(key.val) + if err != nil { + p.raiseError(key, "invalid group array key: %s", err) + } p.tree.createSubTree(keys[:len(keys)-1], startToken.Position) // create parent entries destTree := p.tree.GetPath(keys) var array []*TomlTree @@ -153,7 +156,10 @@ } p.seenGroupKeys = append(p.seenGroupKeys, key.val) - keys := strings.Split(key.val, ".") + keys, err := parseKey(key.val) + if err != nil { + p.raiseError(key, "invalid group array key: %s", err) + } if err := p.tree.createSubTree(keys, startToken.Position); err != nil { p.raiseError(key, "%s", err) }
diff --git a/parser_test.go b/parser_test.go index dc051ea..d33ddde 100644 --- a/parser_test.go +++ b/parser_test.go
@@ -461,3 +461,10 @@ "foo.bar.b": Position{3, 1}, }) } + +func TestInvalidGroupArray(t *testing.T) { + _, err := Load("[key#group]\nanswer = 42") + if err == nil { + t.Error("Should error") + } +}