Support single quoted keys (#193)

Fixes #61
This commit is contained in:
Kazuyoshi Kato
2017-10-19 07:56:30 -07:00
committed by Thomas Pelletier
parent 878c11e70e
commit a410399d2c
2 changed files with 103 additions and 22 deletions
+88 -21
View File
@@ -6,15 +6,37 @@ import (
"bytes"
"errors"
"fmt"
"strconv"
"unicode"
)
var escapeSequenceMap = map[rune]rune{
'b': '\b',
't': '\t',
'n': '\n',
'f': '\f',
'r': '\r',
'"': '"',
'\\': '\\',
}
type parseKeyState int
const (
BARE parseKeyState = iota
BASIC
LITERAL
ESC
UNICODE_4
UNICODE_8
)
func parseKey(key string) ([]string, error) {
groups := []string{}
var buffer bytes.Buffer
inQuotes := false
var hex bytes.Buffer
state := BARE
wasInQuotes := false
escapeNext := false
ignoreSpace := true
expectDot := false
@@ -25,25 +47,67 @@ func parseKey(key string) ([]string, error) {
}
ignoreSpace = false
}
if escapeNext {
buffer.WriteRune(char)
escapeNext = false
if state == ESC {
if char == 'u' {
state = UNICODE_4
hex.Reset()
} else if char == 'U' {
state = UNICODE_8
hex.Reset()
} else if newChar, ok := escapeSequenceMap[char]; ok {
buffer.WriteRune(newChar)
state = BASIC
} else {
return nil, fmt.Errorf(`invalid escape sequence \%c`, char)
}
continue
}
if state == UNICODE_4 || state == UNICODE_8 {
if isHexDigit(char) {
hex.WriteRune(char)
}
if (state == UNICODE_4 && hex.Len() == 4) || (state == UNICODE_8 && hex.Len() == 8) {
if value, err := strconv.ParseInt(hex.String(), 16, 32); err == nil {
buffer.WriteRune(rune(value))
} else {
return nil, err
}
state = BASIC
}
continue
}
switch char {
case '\\':
escapeNext = true
continue
case '"':
if inQuotes {
if state == BASIC {
state = ESC
} else if state == LITERAL {
buffer.WriteRune(char)
}
case '\'':
if state == BARE {
state = LITERAL
} else if state == LITERAL {
groups = append(groups, buffer.String())
buffer.Reset()
wasInQuotes = true
state = BARE
}
expectDot = false
case '"':
if state == BARE {
state = BASIC
} else if state == BASIC {
groups = append(groups, buffer.String())
buffer.Reset()
state = BARE
wasInQuotes = true
}
inQuotes = !inQuotes
expectDot = false
case '.':
if inQuotes {
if state != BARE {
buffer.WriteRune(char)
} else {
if !wasInQuotes {
@@ -58,28 +122,31 @@ func parseKey(key string) ([]string, error) {
wasInQuotes = false
}
case ' ':
if inQuotes {
if state == BASIC {
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?")
if state == BARE {
if !isValidBareChar(char) {
return nil, fmt.Errorf("invalid bare character: %c", char)
} else if expectDot {
return nil, errors.New("what?")
}
}
buffer.WriteRune(char)
expectDot = false
}
}
if inQuotes {
// state must be BARE at the end
if state == ESC {
return nil, errors.New("unfinished escape sequence")
} else if state != BARE {
return nil, errors.New("mismatched quotes")
}
if escapeNext {
return nil, errors.New("unfinished escape sequence")
}
if buffer.Len() > 0 {
groups = append(groups, buffer.String())
}