1 // Parsing keys handling both bare and quoted keys. |
1 // Parsing keys handling both bare and quoted keys. |
2 |
2 |
3 package toml |
3 package toml |
4 |
4 |
5 import ( |
5 import ( |
6 "bytes" |
|
7 "errors" |
6 "errors" |
8 "fmt" |
7 "fmt" |
9 "unicode" |
8 "unicode" |
10 ) |
9 ) |
11 |
10 |
12 // Convert the bare key group string to an array. |
11 // Convert the bare key group string to an array. |
13 // The input supports double quotation to allow "." inside the key name, |
12 // The input supports double quotation and single quotation, |
14 // but escape sequences are not supported. Lexers must unescape them beforehand. |
13 // but escape sequences are not supported. Lexers must unescape them beforehand. |
15 func parseKey(key string) ([]string, error) { |
14 func parseKey(key string) ([]string, error) { |
16 groups := []string{} |
15 runes := []rune(key) |
17 var buffer bytes.Buffer |
16 var groups []string |
18 inQuotes := false |
|
19 wasInQuotes := false |
|
20 ignoreSpace := true |
|
21 expectDot := false |
|
22 |
17 |
23 for _, char := range key { |
18 if len(key) == 0 { |
24 if ignoreSpace { |
19 return nil, errors.New("empty key") |
25 if char == ' ' { |
20 } |
26 continue |
21 |
|
22 idx := 0 |
|
23 for idx < len(runes) { |
|
24 for ; idx < len(runes) && isSpace(runes[idx]); idx++ { |
|
25 // skip leading whitespace |
|
26 } |
|
27 if idx >= len(runes) { |
|
28 break |
|
29 } |
|
30 r := runes[idx] |
|
31 if isValidBareChar(r) { |
|
32 // parse bare key |
|
33 startIdx := idx |
|
34 endIdx := -1 |
|
35 idx++ |
|
36 for idx < len(runes) { |
|
37 r = runes[idx] |
|
38 if isValidBareChar(r) { |
|
39 idx++ |
|
40 } else if r == '.' { |
|
41 endIdx = idx |
|
42 break |
|
43 } else if isSpace(r) { |
|
44 endIdx = idx |
|
45 for ; idx < len(runes) && isSpace(runes[idx]); idx++ { |
|
46 // skip trailing whitespace |
|
47 } |
|
48 if idx < len(runes) && runes[idx] != '.' { |
|
49 return nil, fmt.Errorf("invalid key character after whitespace: %c", runes[idx]) |
|
50 } |
|
51 break |
|
52 } else { |
|
53 return nil, fmt.Errorf("invalid bare key character: %c", r) |
|
54 } |
27 } |
55 } |
28 ignoreSpace = false |
56 if endIdx == -1 { |
29 } |
57 endIdx = idx |
30 switch char { |
|
31 case '"': |
|
32 if inQuotes { |
|
33 groups = append(groups, buffer.String()) |
|
34 buffer.Reset() |
|
35 wasInQuotes = true |
|
36 } |
58 } |
37 inQuotes = !inQuotes |
59 groups = append(groups, string(runes[startIdx:endIdx])) |
38 expectDot = false |
60 } else if r == '\'' { |
39 case '.': |
61 // parse single quoted key |
40 if inQuotes { |
62 idx++ |
41 buffer.WriteRune(char) |
63 startIdx := idx |
42 } else { |
64 for { |
43 if !wasInQuotes { |
65 if idx >= len(runes) { |
44 if buffer.Len() == 0 { |
66 return nil, fmt.Errorf("unclosed single-quoted key") |
45 return nil, errors.New("empty table key") |
|
46 } |
|
47 groups = append(groups, buffer.String()) |
|
48 buffer.Reset() |
|
49 } |
67 } |
50 ignoreSpace = true |
68 r = runes[idx] |
51 expectDot = false |
69 if r == '\'' { |
52 wasInQuotes = false |
70 groups = append(groups, string(runes[startIdx:idx])) |
|
71 idx++ |
|
72 break |
|
73 } |
|
74 idx++ |
53 } |
75 } |
54 case ' ': |
76 } else if r == '"' { |
55 if inQuotes { |
77 // parse double quoted key |
56 buffer.WriteRune(char) |
78 idx++ |
57 } else { |
79 startIdx := idx |
58 expectDot = true |
80 for { |
|
81 if idx >= len(runes) { |
|
82 return nil, fmt.Errorf("unclosed double-quoted key") |
|
83 } |
|
84 r = runes[idx] |
|
85 if r == '"' { |
|
86 groups = append(groups, string(runes[startIdx:idx])) |
|
87 idx++ |
|
88 break |
|
89 } |
|
90 idx++ |
59 } |
91 } |
60 default: |
92 } else if r == '.' { |
61 if !inQuotes && !isValidBareChar(char) { |
93 idx++ |
62 return nil, fmt.Errorf("invalid bare character: %c", char) |
94 if idx >= len(runes) { |
|
95 return nil, fmt.Errorf("unexpected end of key") |
63 } |
96 } |
64 if !inQuotes && expectDot { |
97 r = runes[idx] |
65 return nil, errors.New("what?") |
98 if !isValidBareChar(r) && r != '\'' && r != '"' && r != ' ' { |
|
99 return nil, fmt.Errorf("expecting key part after dot") |
66 } |
100 } |
67 buffer.WriteRune(char) |
101 } else { |
68 expectDot = false |
102 return nil, fmt.Errorf("invalid key character: %c", r) |
69 } |
103 } |
70 } |
104 } |
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 { |
105 if len(groups) == 0 { |
78 return nil, errors.New("empty key") |
106 return nil, fmt.Errorf("empty key") |
79 } |
107 } |
80 return groups, nil |
108 return groups, nil |
81 } |
109 } |
82 |
110 |
83 func isValidBareChar(r rune) bool { |
111 func isValidBareChar(r rune) bool { |