Fix Unquoted String scanning

This commit is contained in:
Tnze
2021-05-25 16:13:55 +08:00
parent 5320722ed0
commit 69601cec28
3 changed files with 87 additions and 87 deletions

31
nbt/bigTest_test.snbt Normal file

File diff suppressed because one or more lines are too long

View File

@ -4,37 +4,6 @@ import (
"errors" "errors"
) )
type token int
const (
ILLEGAL token = iota
IDENT // name
INT // 12345
FLT // 12345.67
BYTE // b or B
SHORT // s or S
LONG // l or L
FLOAT // f or F
DOUBLE // d or D
STRING // "abc" 'def'
LPAREN // (
LBRACK // [
LBRACE // {
COMMA // ,
PERIOD // .
RPAREN // )
RBRACK // ]
RBRACE // }
SEMICOLON // ;
COLON // :
)
const ( const (
scanContinue = iota // uninteresting byte scanContinue = iota // uninteresting byte
scanBeginCompound // begin TAG_Compound (after left-brace ) scanBeginCompound // begin TAG_Compound (after left-brace )
@ -130,7 +99,7 @@ func (s *scanner) stateBeginValue(c byte) int {
if isNumber(c) { if isNumber(c) {
return s.stateNum1(c) return s.stateNum1(c)
} }
if isNumOrLetter(c) { if isAllowedInUnquotedString(c) {
return s.stateBeginString(c) return s.stateBeginString(c)
} }
} }
@ -155,45 +124,42 @@ func (s *scanner) stateBeginString(c byte) int {
} }
switch c { switch c {
case '\'': case '\'':
s.step = s.stateInSqString s.step = s.stateInSingleQuotedString
return scanContinue return scanContinue
case '"': case '"':
s.step = s.stateInDqString s.step = s.stateInDoubleQuotedString
return scanContinue return scanContinue
default: default:
if isNumOrLetter(c) { if isAllowedInUnquotedString(c) {
s.step = s.stateInPureString s.step = s.stateInUnquotedString
return scanContinue return scanContinue
} }
} }
return s.error(c, "looking for beginning of string") return s.error(c, "looking for beginning of string")
} }
func (s *scanner) stateInSqString(c byte) int { func (s *scanner) stateInSingleQuotedString(c byte) int {
if c == '\\' { if c == '\\' {
s.step = s.stateInSqStringEsc s.step = s.stateInSingleQuotedStringEsc
return scanContinue return scanContinue
} }
if c == '\'' { if c == '\'' {
s.step = s.stateEndValue s.step = s.stateEndValue
return scanContinue return scanContinue
} }
if isNumOrLetter(c) { return scanContinue
return scanContinue
}
return s.stateEndValue(c)
} }
func (s *scanner) stateInSqStringEsc(c byte) int { func (s *scanner) stateInSingleQuotedStringEsc(c byte) int {
switch c { switch c {
case 'b', 'f', 'n', 'r', 't', '\\', '/', '\'': case 'b', 'f', 'n', 'r', 't', '\\', '/', '\'':
s.step = s.stateInSqString s.step = s.stateInSingleQuotedString
return scanContinue return scanContinue
} }
return s.error(c, "in string escape code") return s.error(c, "in string escape code")
} }
func (s *scanner) stateInDqString(c byte) int { func (s *scanner) stateInDoubleQuotedString(c byte) int {
if c == '\\' { if c == '\\' {
s.step = s.stateInDqStringEsc s.step = s.stateInDqStringEsc
return scanContinue return scanContinue
@ -202,23 +168,20 @@ func (s *scanner) stateInDqString(c byte) int {
s.step = s.stateEndValue s.step = s.stateEndValue
return scanContinue return scanContinue
} }
if isNumOrLetter(c) { return scanContinue
return scanContinue
}
return s.stateEndValue(c)
} }
func (s *scanner) stateInDqStringEsc(c byte) int { func (s *scanner) stateInDqStringEsc(c byte) int {
switch c { switch c {
case 'b', 'f', 'n', 'r', 't', '\\', '/', '"': case 'b', 'f', 'n', 'r', 't', '\\', '/', '"':
s.step = s.stateInDqString s.step = s.stateInDoubleQuotedString
return scanContinue return scanContinue
} }
return s.error(c, "in string escape code") return s.error(c, "in string escape code")
} }
func (s *scanner) stateInPureString(c byte) int { func (s *scanner) stateInUnquotedString(c byte) int {
if isNumOrLetter(c) { if isAllowedInUnquotedString(c) {
return scanContinue return scanContinue
} }
return s.stateEndValue(c) return s.stateEndValue(c)
@ -244,15 +207,19 @@ func (s *scanner) stateListOrArrayT(c byte) int {
s.step = s.stateBeginValue s.step = s.stateBeginValue
return scanListType return scanListType
} }
return s.stateInPureString(c) return s.stateInUnquotedString(c)
} }
func (s *scanner) stateNeg(c byte) int { func (s *scanner) stateNeg(c byte) int {
if !isNumber(c) { if isNumber(c) {
s.error(c, "not a number after '-'") s.step = s.stateNum1
return scanContinue
} }
s.step = s.stateNum1 if isAllowedInUnquotedString(c) {
return scanContinue s.step = s.stateInUnquotedString
return scanContinue
}
return s.error(c, "not a number after '-'")
} }
func (s *scanner) stateNum1(c byte) int { func (s *scanner) stateNum1(c byte) int {
@ -264,6 +231,10 @@ func (s *scanner) stateNum1(c byte) int {
s.step = s.stateNumDot s.step = s.stateNumDot
return scanContinue return scanContinue
} }
if isAllowedInUnquotedString(c) {
s.step = s.stateInUnquotedString
return scanContinue
}
return s.stateEndNumValue(c) return s.stateEndNumValue(c)
} }
@ -274,6 +245,10 @@ func (s *scanner) stateNumDot(c byte) int {
s.step = s.stateNumDot0 s.step = s.stateNumDot0
return scanContinue return scanContinue
} }
if isAllowedInUnquotedString(c) {
s.step = s.stateInUnquotedString
return scanContinue
}
return s.error(c, "after decimal point in numeric literal") return s.error(c, "after decimal point in numeric literal")
} }
@ -284,6 +259,10 @@ func (s *scanner) stateNumDot0(c byte) int {
s.step = s.stateNumDot0 s.step = s.stateNumDot0
return scanContinue return scanContinue
} }
if isAllowedInUnquotedString(c) {
s.step = s.stateInUnquotedString
return scanContinue
}
return s.stateEndNumDotValue(c) return s.stateEndNumDotValue(c)
} }
@ -382,15 +361,13 @@ func isSpace(c byte) bool {
} }
func isNumber(c byte) bool { func isNumber(c byte) bool {
if c >= '0' && c <= '9' { return c >= '0' && c <= '9'
return true
}
return false
} }
func isNumOrLetter(c byte) bool { func isAllowedInUnquotedString(c byte) bool {
if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || isNumber(c) { return c == '_' || c == '-' ||
return true c == '.' || c == '+' ||
} c >= '0' && c <= '9' ||
return false c >= 'A' && c <= 'Z' ||
c >= 'a' && c <= 'z'
} }

View File

@ -1,6 +1,9 @@
package nbt package nbt
import "testing" import (
_ "embed"
"testing"
)
func TestSNBT_number(t *testing.T) { func TestSNBT_number(t *testing.T) {
goods := []string{ goods := []string{
@ -9,10 +12,6 @@ func TestSNBT_number(t *testing.T) {
"255B", "1234s", "6666L", "255B", "1234s", "6666L",
"314F", "3.14f", "3.14159265358979323846264D", "314F", "3.14f", "3.14159265358979323846264D",
} }
bads := []string{
".0", "1234.5678.90",
"25-5B", "1234.s",
}
var s scanner var s scanner
scan := func(str string) bool { scan := func(str string) bool {
s.reset() s.reset()
@ -29,36 +28,29 @@ func TestSNBT_number(t *testing.T) {
t.Errorf("scan valid data %q error: %v", str, s.err) t.Errorf("scan valid data %q error: %v", str, s.err)
} }
} }
for _, str := range bads {
if scan(str) {
t.Errorf("scan invalid data %q success", str)
}
}
} }
//go:embed bigTest_test.snbt
var bigTest string
func TestSNBT_compound(t *testing.T) { func TestSNBT_compound(t *testing.T) {
goods := []string{ goods := []string{
`{}`, `{name:3.14f}`, `{ "name" : 12345 }`, `{}`, `{name:3.14f}`, `{ "name" : 12345 }`,
`{ abc: { }}`, `{ "ab\"c": {}, def: 12345}`, `{ abc: { }}`, `{ "a b\"c": {}, def: 12345}`,
bigTest,
} }
var s scanner var s scanner
scan := func(str string) bool { for _, str := range goods {
s.reset() s.reset()
for _, c := range []byte(str) { for i, c := range []byte(str) {
res := s.step(c) res := s.step(c)
if res == scanError { if res == scanError {
return false t.Errorf("scan valid data %q error: %v at [%d]", str[:i], s.err, i)
break
} }
} }
return true
}
for _, str := range goods {
if scan(str) == false {
t.Errorf("scan valid data %q error: %v", str, s.err)
}
} }
} }
func TestSNBT_list(t *testing.T) { func TestSNBT_list(t *testing.T) {
goods := []string{ goods := []string{
`[]`, `[a, 'b', "c", d]`, // List of string `[]`, `[a, 'b', "c", d]`, // List of string