| // Parsing keys handling both bare and quoted keys. |
| |
| package toml |
| |
| import ( |
| "errors" |
| "fmt" |
| ) |
| |
| // 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 == '-' || isDigit(r) |
| } |