From 5320722ed04d3e67a4702ef98bf60f622436714e Mon Sep 17 00:00:00 2001 From: Tnze Date: Tue, 25 May 2021 14:25:08 +0800 Subject: [PATCH] Scan List and Array --- nbt/snbt_decode.go | 65 ++++++++++++++++++++++++++++++++-------- nbt/snbt_scanner_test.go | 27 ++++++++++++++++- 2 files changed, 78 insertions(+), 14 deletions(-) diff --git a/nbt/snbt_decode.go b/nbt/snbt_decode.go index 8b49fd8..1a7fb8e 100644 --- a/nbt/snbt_decode.go +++ b/nbt/snbt_decode.go @@ -37,7 +37,10 @@ const ( const ( scanContinue = iota // uninteresting byte - scanBeginCompound // begin compound (after left-brace ) + scanBeginCompound // begin TAG_Compound (after left-brace ) + scanBeginList // begin TAG_List (after left-brack) + scanListValue // just finished read list value + scanListType // just finished read list type (after "B;" or "L;") scanCompoundTagName // just finished read tag name (before colon) scanCompoundValue // just finished read value (before comma or right-brace ) scanSkipSpace // space byte; can skip; known to be last "continue" result @@ -86,7 +89,7 @@ func (s *scanner) pushParseState(c byte, newParseState int, successState int) in // and updates s.step accordingly. func (s *scanner) popParseState() { n := len(s.parseState) - 1 - s.parseState = s.parseState[0:n] + s.parseState = s.parseState[:n] if n == 0 { s.step = s.stateEndTop //s.endTop = true @@ -108,6 +111,7 @@ func (s *scanner) stateEndTop(c byte) int { func (s *scanner) stateBeginValue(c byte) int { if isSpace(c) { + s.step = s.stateBeginValue return scanSkipSpace } switch c { @@ -115,17 +119,19 @@ func (s *scanner) stateBeginValue(c byte) int { s.step = s.stateCompoundOrEmpty return s.pushParseState(c, parseCompoundName, scanBeginCompound) case '[': // beginning of TAG_List - s.step = s.stateList + s.step = s.stateListOrArray + return s.pushParseState(c, parseListValue, scanBeginList) case '"', '\'': // beginning of TAG_String - s.step = s.stateBeginString - return scanContinue + return s.stateBeginString(c) case '-': // beginning of negative number s.step = s.stateNeg return scanContinue default: if isNumber(c) { - s.step = s.stateNum1 - return scanContinue + return s.stateNum1(c) + } + if isNumOrLetter(c) { + return s.stateBeginString(c) } } return s.error(c, "looking for beginning of value") @@ -144,6 +150,9 @@ func (s *scanner) stateCompoundOrEmpty(c byte) int { } func (s *scanner) stateBeginString(c byte) int { + if isSpace(c) { + return scanSkipSpace + } switch c { case '\'': s.step = s.stateInSqString @@ -157,7 +166,7 @@ func (s *scanner) stateBeginString(c byte) int { return scanContinue } } - return s.error(c, "looking for beginning of tag name string") + return s.error(c, "looking for beginning of string") } func (s *scanner) stateInSqString(c byte) int { @@ -215,8 +224,27 @@ func (s *scanner) stateInPureString(c byte) int { return s.stateEndValue(c) } -func (s *scanner) stateList(c byte) int { - return s.error(c, "not implemented") +func (s *scanner) stateListOrArray(c byte) int { + if isSpace(c) { + return scanSkipSpace + } + switch c { + case 'B', 'I', 'L': + s.step = s.stateListOrArrayT + return scanContinue + case ']': + return s.stateEndValue(c) + default: + return s.stateBeginValue(c) + } +} + +func (s *scanner) stateListOrArrayT(c byte) int { + if c == ';' { + s.step = s.stateBeginValue + return scanListType + } + return s.stateInPureString(c) } func (s *scanner) stateNeg(c byte) int { @@ -301,7 +329,6 @@ func (s *scanner) stateEndValue(c byte) int { return s.stateEndTop(c) } if isSpace(c) { - s.step = s.stateEndValue return scanSkipSpace } @@ -313,18 +340,30 @@ func (s *scanner) stateEndValue(c byte) int { s.step = s.stateBeginValue return scanCompoundTagName } - return s.error(c, "after object key") + return s.error(c, "after compound tag name") case parseCompoundValue: switch c { case ',': + s.parseState[n-1] = parseCompoundName s.step = s.stateBeginString return scanCompoundValue case '}': s.popParseState() return scanEndValue } + return s.error(c, "after compound value") + case parseListValue: + switch c { + case ',': + s.step = s.stateBeginValue + return scanListValue + case ']': + s.popParseState() + return scanEndValue + } + return s.error(c, "after list element") } - return s.error(c, "not implemented") + return s.error(c, "") } func (s *scanner) error(c byte, context string) int { s.step = s.stateError diff --git a/nbt/snbt_scanner_test.go b/nbt/snbt_scanner_test.go index 2f4e177..44d1532 100644 --- a/nbt/snbt_scanner_test.go +++ b/nbt/snbt_scanner_test.go @@ -39,7 +39,32 @@ func TestSNBT_number(t *testing.T) { func TestSNBT_compound(t *testing.T) { goods := []string{ `{}`, `{name:3.14f}`, `{ "name" : 12345 }`, - `{ abc: {}}`, + `{ abc: { }}`, `{ "ab\"c": {}, def: 12345}`, + } + var s scanner + scan := func(str string) bool { + s.reset() + for _, c := range []byte(str) { + res := s.step(c) + if res == scanError { + return false + } + } + 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) { + goods := []string{ + `[]`, `[a, 'b', "c", d]`, // List of string + `[{}, {}, {"a\"b":520}]`, // List of Compound + `[B,C,D]`, `[L, "abc"]`, // List of string (like array) + `[B; 01B, 02B, 3B, 10B, 127B]`, // Array } var s scanner scan := func(str string) bool {