1// Parsing keys handling both bare and quoted keys.
2
3package toml
4
5import (
6	"bytes"
7	"errors"
8	"fmt"
9	"unicode"
10)
11
12// Convert the bare key group string to an array.
13// The input supports double quotation to allow "." inside the key name,
14// but escape sequences are not supported. Lexers must unescape them beforehand.
15func parseKey(key string) ([]string, error) {
16	groups := []string{}
17	var buffer bytes.Buffer
18	inQuotes := false
19	wasInQuotes := false
20	ignoreSpace := true
21	expectDot := false
22
23	for _, char := range key {
24		if ignoreSpace {
25			if char == ' ' {
26				continue
27			}
28			ignoreSpace = false
29		}
30		switch char {
31		case '"':
32			if inQuotes {
33				groups = append(groups, buffer.String())
34				buffer.Reset()
35				wasInQuotes = true
36			}
37			inQuotes = !inQuotes
38			expectDot = false
39		case '.':
40			if inQuotes {
41				buffer.WriteRune(char)
42			} else {
43				if !wasInQuotes {
44					if buffer.Len() == 0 {
45						return nil, errors.New("empty table key")
46					}
47					groups = append(groups, buffer.String())
48					buffer.Reset()
49				}
50				ignoreSpace = true
51				expectDot = false
52				wasInQuotes = false
53			}
54		case ' ':
55			if inQuotes {
56				buffer.WriteRune(char)
57			} else {
58				expectDot = true
59			}
60		default:
61			if !inQuotes && !isValidBareChar(char) {
62				return nil, fmt.Errorf("invalid bare character: %c", char)
63			}
64			if !inQuotes && expectDot {
65				return nil, errors.New("what?")
66			}
67			buffer.WriteRune(char)
68			expectDot = false
69		}
70	}
71	if inQuotes {
72		return nil, errors.New("mismatched quotes")
73	}
74	if buffer.Len() > 0 {
75		groups = append(groups, buffer.String())
76	}
77	if len(groups) == 0 {
78		return nil, errors.New("empty key")
79	}
80	return groups, nil
81}
82
83func isValidBareChar(r rune) bool {
84	return isAlphanumeric(r) || r == '-' || unicode.IsNumber(r)
85}
86