| // Parsing keys handling both bare and quoted keys. |
| |
| package toml |
| |
| import ( |
| "bytes" |
| "errors" |
| "fmt" |
| "unicode" |
| ) |
| |
| func parseKey(key string) ([]string, error) { |
| groups := []string{} |
| var buffer bytes.Buffer |
| inQuotes := false |
| wasInQuotes := false |
| escapeNext := false |
| ignoreSpace := true |
| expectDot := false |
| |
| for _, char := range key { |
| if ignoreSpace { |
| if char == ' ' { |
| continue |
| } |
| ignoreSpace = false |
| } |
| if escapeNext { |
| buffer.WriteRune(char) |
| escapeNext = false |
| continue |
| } |
| switch char { |
| case '\\': |
| escapeNext = true |
| continue |
| case '"': |
| if inQuotes { |
| groups = append(groups, buffer.String()) |
| buffer.Reset() |
| wasInQuotes = true |
| } |
| inQuotes = !inQuotes |
| expectDot = false |
| case '.': |
| if inQuotes { |
| buffer.WriteRune(char) |
| } else { |
| if !wasInQuotes { |
| if buffer.Len() == 0 { |
| return nil, errors.New("empty table key") |
| } |
| groups = append(groups, buffer.String()) |
| buffer.Reset() |
| } |
| ignoreSpace = true |
| expectDot = false |
| wasInQuotes = false |
| } |
| case ' ': |
| if inQuotes { |
| buffer.WriteRune(char) |
| } else { |
| expectDot = true |
| } |
| default: |
| if !inQuotes && !isValidBareChar(char) { |
| return nil, fmt.Errorf("invalid bare character: %c", char) |
| } |
| if !inQuotes && expectDot { |
| return nil, errors.New("what?") |
| } |
| buffer.WriteRune(char) |
| expectDot = false |
| } |
| } |
| if inQuotes { |
| return nil, errors.New("mismatched quotes") |
| } |
| if escapeNext { |
| return nil, errors.New("unfinished escape sequence") |
| } |
| if buffer.Len() > 0 { |
| groups = append(groups, buffer.String()) |
| } |
| if len(groups) == 0 { |
| return nil, errors.New("empty key") |
| } |
| return groups, nil |
| } |
| |
| func isValidBareChar(r rune) bool { |
| return isAlphanumeric(r) || r == '-' || unicode.IsNumber(r) |
| } |