2f03901dbdea79fbe34e68cad9974871fd0f26a0 — Martin Angers 6 months ago 9815e26
scan and test literal tokens
2 files changed, 65 insertions(+), 6 deletions(-)

M zerojson.go
M zerojson_test.go
M zerojson.go => zerojson.go +13 -6
@@ 142,14 142,17 @@ type parser struct {
 	emit func(offset int, typ byte, v []byte, err error) error
 
 	// parser state
-	cur    byte
-	tok    byte // current token, '{', '[', '"', '1', 't', 'f', 'n'
-	keyVal byte // when in an object, indicates if we're on key (':') or value (',')
-	pos    int
-	stack  stack
+	cur   byte
+	pos   int
+	stack stack
 }
 
 func (p *parser) parse() error {
+	// bootstrap execution
+	p.pos = -1
+	p.advance()
+
+loop:
 	for p.cur != eof {
 		p.skipWhitespace()
 


@@ 161,6 164,7 @@ func (p *parser) parse() error {
 		start := p.pos
 		typ = p.cur
 		p.advance()
+
 		switch typ {
 		case '{':
 			p.stack.push(typ)


@@ 169,7 173,7 @@ func (p *parser) parse() error {
 		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)
 		case 't':


@@ 178,6 182,9 @@ func (p *parser) parse() error {
 			err = p.scanLiteral(falseTrail)
 		case 'n':
 			err = p.scanLiteral(nullTrail)
+		case eof:
+			// TODO: if a token was required, error
+			break loop
 
 		default:
 			err = fmt.Errorf("invalid character: %#U", typ)

M zerojson_test.go => zerojson_test.go +52 -0
@@ 1,11 1,63 @@
 package zerojson
 
 import (
+	"bytes"
+	"fmt"
+	"io"
 	"math/rand"
+	"strings"
 	"testing"
 	"time"
+
+	"github.com/stretchr/testify/require"
 )
 
+func TestParser(t *testing.T) {
+	cases := []struct {
+		in, out string
+		err     error
+	}{
+		{"", "", nil},
+		{" \t\n ", "", nil},
+		{"null", "0: n: null", nil},
+		{"true", "0: t: true", nil},
+		{"false", "0: f: false", nil},
+		{" null", "1: n: null", nil},
+		{"\ttrue", "1: t: true", nil},
+		{"\n false", "2: f: false", nil},
+		{" null ", "1: n: null", nil},
+		{"\ttrue\t", "1: t: true", nil},
+		{"\n false\n ", "2: f: false", nil},
+		{"nulltruefalse", "0: n: null\n4: t: true\n8: f: false", nil},
+	}
+
+	for _, c := range cases {
+		t.Run(c.in, func(t *testing.T) {
+			var buf bytes.Buffer
+
+			p := parser{
+				input: []byte(c.in),
+				emit: func(offset int, typ byte, v []byte, err error) error {
+					if err == io.EOF {
+						return nil
+					}
+					fmt.Fprintf(&buf, "%d: %c: %s\n", offset, typ, string(v))
+					return err
+				},
+			}
+			err := p.parse()
+			if c.err != nil {
+				require.Error(t, err)
+				require.Equal(t, c.err, err)
+			} else {
+				require.NoError(t, err)
+			}
+
+			require.Equal(t, strings.TrimSpace(c.out), strings.TrimSpace(buf.String()))
+		})
+	}
+}
+
 func TestStack(t *testing.T) {
 	seed := time.Now().UnixNano()
 	rnd := rand.New(rand.NewSource(seed))