Scan numbers
This commit is contained in:
@ -33,14 +33,60 @@ const (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
scanContinue = iota
|
scanContinue = iota
|
||||||
|
scanSkipSpace
|
||||||
|
scanEndValue
|
||||||
|
scanEnd
|
||||||
scanError
|
scanError
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const maxNestingDepth = 10000
|
||||||
|
|
||||||
type scanner struct {
|
type scanner struct {
|
||||||
step func(c byte) int
|
step func(c byte) int
|
||||||
parseState []int
|
parseState []int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reset prepares the scanner for use.
|
||||||
|
// It must be called before calling s.step.
|
||||||
|
func (s *scanner) reset() {
|
||||||
|
s.step = s.stateBeginValue
|
||||||
|
s.parseState = s.parseState[0:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// pushParseState pushes a new parse state p onto the parse stack.
|
||||||
|
// an error state is returned if maxNestingDepth was exceeded, otherwise successState is returned.
|
||||||
|
func (s *scanner) pushParseState(c byte, newParseState int, successState int) int {
|
||||||
|
s.parseState = append(s.parseState, newParseState)
|
||||||
|
if len(s.parseState) <= maxNestingDepth {
|
||||||
|
return successState
|
||||||
|
}
|
||||||
|
return scanError
|
||||||
|
}
|
||||||
|
|
||||||
|
// popParseState pops a parse state (already obtained) off the stack
|
||||||
|
// and updates s.step accordingly.
|
||||||
|
func (s *scanner) popParseState() {
|
||||||
|
n := len(s.parseState) - 1
|
||||||
|
s.parseState = s.parseState[0:n]
|
||||||
|
if n == 0 {
|
||||||
|
s.step = s.stateEndTop
|
||||||
|
//s.endTop = true
|
||||||
|
} else {
|
||||||
|
s.step = s.stateEndValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateEndTop is the state after finishing the top-level value,
|
||||||
|
// such as after reading `{}` or `[1,2,3]`.
|
||||||
|
// Only space characters should be seen now.
|
||||||
|
func (s *scanner) stateEndTop(c byte) int {
|
||||||
|
if !isSpace(c) {
|
||||||
|
// Complain about non-space byte on next call.
|
||||||
|
//s.error(c, "after top-level value")
|
||||||
|
}
|
||||||
|
return scanEnd
|
||||||
|
}
|
||||||
|
|
||||||
func (s *scanner) stateBeginValue(c byte) int {
|
func (s *scanner) stateBeginValue(c byte) int {
|
||||||
switch c {
|
switch c {
|
||||||
case '{': // beginning of TAG_Compound
|
case '{': // beginning of TAG_Compound
|
||||||
@ -49,14 +95,103 @@ func (s *scanner) stateBeginValue(c byte) int {
|
|||||||
s.step = s.stateList
|
s.step = s.stateList
|
||||||
case '"', '\'': // beginning of TAG_String
|
case '"', '\'': // beginning of TAG_String
|
||||||
|
|
||||||
|
default:
|
||||||
|
if c >= '0' && c <= '9' {
|
||||||
|
s.step = s.stateNum1
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return scanError
|
return scanError
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *scanner) stateCompound(c byte) int {
|
func (s *scanner) stateNum1(c byte) int {
|
||||||
|
if c >= '0' || c <= '9' {
|
||||||
|
s.step = s.stateNum1
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
if c == '.' {
|
||||||
|
s.step = s.stateNumDot
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
return s.stateEndValue(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateDot is the state after reading the integer and decimal point in a number,
|
||||||
|
// such as after reading `1.`.
|
||||||
|
func (s *scanner) stateNumDot(c byte) int {
|
||||||
|
if c >= '0' || c <= '9' {
|
||||||
|
s.step = s.stateNumDot0
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
return scanError
|
return scanError
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *scanner) stateList(c byte) int {
|
// stateNumDot0 is the state after reading the integer, decimal point, and subsequent
|
||||||
|
// digits of a number, such as after reading `3.14`.
|
||||||
|
func (s *scanner) stateNumDot0(c byte) int {
|
||||||
|
if c >= '0' || c <= '9' {
|
||||||
|
s.step = s.stateNumDot0
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
return s.stateEndNumDotValue(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *scanner) stateCompound(c byte) int {
|
||||||
|
return s.error(c, "not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *scanner) stateList(c byte) int {
|
||||||
|
return s.error(c, "not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *scanner) stateEndNumValue(c byte) int {
|
||||||
|
if isSpace(c) {
|
||||||
|
s.step = s.stateEndValue
|
||||||
|
return scanSkipSpace
|
||||||
|
}
|
||||||
|
switch c {
|
||||||
|
case 'b', 'B': // TAG_Byte
|
||||||
|
s.step = s.stateEndValue
|
||||||
|
return scanSkipSpace
|
||||||
|
case 's', 'S': // TAG_Short
|
||||||
|
s.step = s.stateEndValue
|
||||||
|
return scanSkipSpace
|
||||||
|
case 'l', 'L': // TAG_Long
|
||||||
|
s.step = s.stateEndValue
|
||||||
|
return scanSkipSpace
|
||||||
|
case 'f', 'F', 'd', 'D':
|
||||||
|
return s.stateEndNumDotValue(c)
|
||||||
|
}
|
||||||
|
return s.stateEndValue(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *scanner) stateEndNumDotValue(c byte) int {
|
||||||
|
switch c {
|
||||||
|
case 'f', 'F': // TAG_Float
|
||||||
|
s.step = s.stateEndValue
|
||||||
|
return scanSkipSpace
|
||||||
|
case 'd', 'D': // TAG_Double
|
||||||
|
s.step = s.stateEndValue
|
||||||
|
return scanSkipSpace
|
||||||
|
}
|
||||||
|
return s.stateEndValue(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *scanner) stateEndValue(c byte) int {
|
||||||
|
return s.error(c, "not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *scanner) error(c byte, context string) int {
|
||||||
|
s.step = s.stateError
|
||||||
return scanError
|
return scanError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// stateError is the state after reaching a syntax error,
|
||||||
|
// such as after reading `[1}` or `5.1.2`.
|
||||||
|
func (s *scanner) stateError(c byte) int {
|
||||||
|
return scanError
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSpace(c byte) bool {
|
||||||
|
return c <= ' ' && (c == ' ' || c == '\t' || c == '\r' || c == '\n')
|
||||||
|
}
|
||||||
|
23
nbt/snbt_scanner_test.go
Normal file
23
nbt/snbt_scanner_test.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package nbt
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestSNBT(t *testing.T) {
|
||||||
|
var s scanner
|
||||||
|
for _, str := range []string{
|
||||||
|
"0", "1234567890", "3.1415926",
|
||||||
|
"255B", "1234s", "6666L",
|
||||||
|
"314F", "3.14f", "3.14159265358979323846264D",
|
||||||
|
} {
|
||||||
|
s.reset()
|
||||||
|
var scanCodes []int
|
||||||
|
for _, c := range []byte(str) {
|
||||||
|
res := s.step(c)
|
||||||
|
if res == scanError {
|
||||||
|
t.Errorf("scan error")
|
||||||
|
}
|
||||||
|
scanCodes = append(scanCodes, res)
|
||||||
|
}
|
||||||
|
t.Logf("scancodes: %v", scanCodes)
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user