committed by
Thomas Pelletier
parent
878c11e70e
commit
a410399d2c
+88
-21
@@ -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())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user