summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/pelletier/go-toml/keysparsing.go
blob: e923bc4f9b740ba2fe20de86054e322d61e2687d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// Parsing keys handling both bare and quoted keys.

package toml

import (
	"errors"
	"fmt"
	"unicode"
)

// Convert the bare key group string to an array.
// The input supports double quotation and single quotation,
// but escape sequences are not supported. Lexers must unescape them beforehand.
func parseKey(key string) ([]string, error) {
	runes := []rune(key)
	var groups []string

	if len(key) == 0 {
		return nil, errors.New("empty key")
	}

	idx := 0
	for idx < len(runes) {
		for ; idx < len(runes) && isSpace(runes[idx]); idx++ {
			// skip leading whitespace
		}
		if idx >= len(runes) {
			break
		}
		r := runes[idx]
		if isValidBareChar(r) {
			// parse bare key
			startIdx := idx
			endIdx := -1
			idx++
			for idx < len(runes) {
				r = runes[idx]
				if isValidBareChar(r) {
					idx++
				} else if r == '.' {
					endIdx = idx
					break
				} else if isSpace(r) {
					endIdx = idx
					for ; idx < len(runes) && isSpace(runes[idx]); idx++ {
						// skip trailing whitespace
					}
					if idx < len(runes) && runes[idx] != '.' {
						return nil, fmt.Errorf("invalid key character after whitespace: %c", runes[idx])
					}
					break
				} else {
					return nil, fmt.Errorf("invalid bare key character: %c", r)
				}
			}
			if endIdx == -1 {
				endIdx = idx
			}
			groups = append(groups, string(runes[startIdx:endIdx]))
		} else if r == '\'' {
			// parse single quoted key
			idx++
			startIdx := idx
			for {
				if idx >= len(runes) {
					return nil, fmt.Errorf("unclosed single-quoted key")
				}
				r = runes[idx]
				if r == '\'' {
					groups = append(groups, string(runes[startIdx:idx]))
					idx++
					break
				}
				idx++
			}
		} else if r == '"' {
			// parse double quoted key
			idx++
			startIdx := idx
			for {
				if idx >= len(runes) {
					return nil, fmt.Errorf("unclosed double-quoted key")
				}
				r = runes[idx]
				if r == '"' {
					groups = append(groups, string(runes[startIdx:idx]))
					idx++
					break
				}
				idx++
			}
		} else if r == '.' {
			idx++
			if idx >= len(runes) {
				return nil, fmt.Errorf("unexpected end of key")
			}
			r = runes[idx]
			if !isValidBareChar(r) && r != '\'' && r != '"' && r != ' ' {
				return nil, fmt.Errorf("expecting key part after dot")
			}
		} else {
			return nil, fmt.Errorf("invalid key character: %c", r)
		}
	}
	if len(groups) == 0 {
		return nil, fmt.Errorf("empty key")
	}
	return groups, nil
}

func isValidBareChar(r rune) bool {
	return isAlphanumeric(r) || r == '-' || unicode.IsNumber(r)
}