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