1// Parsing keys handling both bare and quoted keys. 2 3package toml 4 5import ( 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. 13func 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 110func isValidBareChar(r rune) bool { 111 return isAlphanumeric(r) || r == '-' || isDigit(r) 112} 113