~mna/zerojson

7e6746b3f84b4e0a2ffd54916f5de69074f8f081 — Martin Angers 1 year, 2 months ago 90c97f1
scan numbers, test and benchmark
3 files changed, 112 insertions(+), 2 deletions(-)

M zerojson.go
M zerojson_bench_test.go
M zerojson_test.go
M zerojson.go => zerojson.go +56 -2
@@ 31,6 31,9 @@ const (
	// ErrIncompleteHexEsc is the error representing an invalid \uXXXX hexadecimal
	// escape sequence in a string value.
	ErrIncompleteHexEsc errorString = "incomplete hexadecimal escape sequence"

	// ErrIncompleteNumber is the error representing an invalid number value.
	ErrIncompleteNumber errorString = "incomplete number"
)

// JSON supports 7 different values:


@@ 185,9 188,9 @@ loop:
		case '"':
			err = p.scanString()
		case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
			//first := typ
			first := typ
			typ = '1'
			//err = p.scanNumber(first)
			err = p.scanNumber(first)
		case 't':
			err = p.scanToken(trueTrail)
		case 'f':


@@ 210,6 213,57 @@ loop:
	return p.emit(p.pos, p.cur, nil, io.EOF)
}

func (p *parser) scanNumber(first byte) error {
	// if first == '-', the next char must be 0-9
	if first == '-' {
		if '0' <= p.cur && p.cur <= '9' {
			first = p.cur
			p.advance()
		} else {
			return ErrIncompleteNumber
		}
	}

	// if first digit is not '0', scan any subsequent digits
	if first != '0' {
		for '0' <= p.cur && p.cur <= '9' {
			p.advance()
		}
	}

	// scan any fractional digit
	if p.cur == '.' {
		var digitSeen bool
		p.advance()
		for '0' <= p.cur && p.cur <= '9' {
			digitSeen = true
			p.advance()
		}
		if !digitSeen {
			return ErrIncompleteNumber
		}
	}

	// scan any exponent
	if p.cur == 'e' || p.cur == 'E' {
		p.advance()
		if p.cur == '+' || p.cur == '-' {
			p.advance()
		}

		var digitSeen bool
		for '0' <= p.cur && p.cur <= '9' {
			digitSeen = true
			p.advance()
		}
		if !digitSeen {
			return ErrIncompleteNumber
		}
	}

	return nil
}

func (p *parser) scanString() error {
	for p.cur != '"' {
		switch {

M zerojson_bench_test.go => zerojson_bench_test.go +4 -0
@@ 22,6 22,10 @@ func BenchmarkString(b *testing.B) {
	benchmarkInput(b, `"a\bcde\fghijklm\nopq\rs\t\uaB12vwxyz\"\\\/"`)
}

func BenchmarkNumber(b *testing.B) {
	benchmarkInput(b, `123.456e+7890`)
}

func benchmarkInput(b *testing.B, input string) {
	p := parser{
		input: []byte(fmt.Sprintf(" %s ", input)),

M zerojson_test.go => zerojson_test.go +52 -0
@@ 43,6 43,58 @@ func TestParser(t *testing.T) {
		{`"\a"`, "0: \": \"", ErrUnclosedString},
		{`"\uabc"`, "0: \": \"\\uabc", ErrIncompleteHexEsc},
		{"\"\x00\"", "0: \": \"", ErrUnclosedString},

		{"0", "0: 1: 0", nil},
		{"1", "0: 1: 1", nil},
		{"2", "0: 1: 2", nil},
		{"3", "0: 1: 3", nil},
		{"4", "0: 1: 4", nil},
		{"5", "0: 1: 5", nil},
		{"6", "0: 1: 6", nil},
		{"7", "0: 1: 7", nil},
		{"8", "0: 1: 8", nil},
		{"9", "0: 1: 9", nil},
		{"00", "0: 1: 0\n1: 1: 0\n", nil}, // TODO: would it be better to error?
		{"-0", "0: 1: -0", nil},
		{"-1", "0: 1: -1", nil},
		{"-2", "0: 1: -2", nil},
		{"-3", "0: 1: -3", nil},
		{"-4", "0: 1: -4", nil},
		{"-5", "0: 1: -5", nil},
		{"-6", "0: 1: -6", nil},
		{"-7", "0: 1: -7", nil},
		{"-8", "0: 1: -8", nil},
		{"-9", "0: 1: -9", nil},
		{"-01", "0: 1: -0\n2: 1: 1", nil}, // TODO: same
		{"1234567890", "0: 1: 1234567890", nil},
		{"-1234567890", "0: 1: -1234567890", nil},
		{"1.0123456789", "0: 1: 1.0123456789", nil},
		{"1.", "0: 1: 1.", ErrIncompleteNumber},
		{"-", "0: 1: -", ErrIncompleteNumber},
		{"0.123456789", "0: 1: 0.123456789", nil},
		{"-0.123456789", "0: 1: -0.123456789", nil},

		{"1.2e3", "0: 1: 1.2e3", nil},
		{"1.2E3", "0: 1: 1.2E3", nil},
		{"1.2e+3", "0: 1: 1.2e+3", nil},
		{"1.2E+3", "0: 1: 1.2E+3", nil},
		{"1.2e-3", "0: 1: 1.2e-3", nil},
		{"1.2E-3", "0: 1: 1.2E-3", nil},
		{"-1.2e+3", "0: 1: -1.2e+3", nil},
		{"-1.2E+3", "0: 1: -1.2E+3", nil},
		{"-1.2e-3", "0: 1: -1.2e-3", nil},
		{"-1.2E-3", "0: 1: -1.2E-3", nil},

		{"1e3", "0: 1: 1e3", nil},
		{"1E3", "0: 1: 1E3", nil},
		{"1e+3", "0: 1: 1e+3", nil},
		{"1E+3", "0: 1: 1E+3", nil},
		{"1e-3", "0: 1: 1e-3", nil},
		{"1E-3", "0: 1: 1E-3", nil},
		{"-1e+3", "0: 1: -1e+3", nil},
		{"-1E+3", "0: 1: -1E+3", nil},
		{"-1e-3", "0: 1: -1e-3", nil},
		{"-1E-3", "0: 1: -1E-3", nil},
	}

	for _, c := range cases {