9815e26416ae791fd399d7f857f99c0e9cb22a3f — Martin Angers 6 months ago 77434e3
peek, scan literal
2 files changed, 51 insertions(+), 29 deletions(-)

M zerojson.go
M zerojson_test.go
M zerojson.go => zerojson.go +33 -28
@@ 1,8 1,21 @@
 package zerojson
 
 import (
-	"bytes"
 	"fmt"
+	"io"
+)
+
+// errorString allows a string constant to be an error.
+type errorString string
+
+func (e errorString) Error() string {
+	return string(e)
+}
+
+const (
+	// ErrIncompleteLiteral is the error representing an incomplete true, false or
+	// null literal.
+	ErrIncompleteLiteral errorString = "incomplete literal"
 )
 
 // JSON supports 7 different values:


@@ 74,7 87,7 @@ func (s *stack) push(v byte) {
 }
 
 func (s *stack) peek() byte {
-	if s.cur > 0 {
+	if s.cur > 0 || s.depth == 0 {
 		return s.cur
 	}
 


@@ 109,7 122,7 @@ type parser struct {
 	// input is the data to parse
 	input []byte
 	// emit is called for each parsed tokens with the type of token, the
-	// value and an error if the token is bad token. If the function returns
+	// value and an error if the token is an invalid token. If the function returns
 	// an error, the parsing stops, otherwise it continues with the next
 	// token.
 	//


@@ 121,11 134,11 @@ type parser struct {
 	// 't' : true (v is the full literal)
 	// 'f' : false (v is the full literal)
 	// 'n' : null (v is the full literal)
-	// '!' : bad token (v is the full bad token, e.g. an unclosed string
-	//       will have a v from opening '"' to eof)
+	// '\x00' : unknown or eof, only provided with a non-nil err
 	//
 	// The value v is a slice into the original input, it should not be
-	// modified.
+	// modified. If err is not nil, typ represents the type that it should
+	// have been had it been valid.
 	emit func(offset int, typ byte, v []byte, err error) error
 
 	// parser state


@@ 147,22 160,18 @@ func (p *parser) parse() error {
 
 		start := p.pos
 		typ = p.cur
-		switch p.cur {
+		p.advance()
+		switch typ {
 		case '{':
-			p.stack.push(p.cur)
-			p.advance()
-
+			p.stack.push(typ)
 		case '[':
-			p.stack.push(p.cur)
-			p.advance()
-
+			p.stack.push(typ)
 		case '"':
-			err = p.scanString()
-
+			//err = p.scanString()
 		case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+			first := typ
 			typ = '1'
-			err = p.scanNumber()
-
+			//err = p.scanNumber(first)
 		case 't':
 			err = p.scanLiteral(trueTrail)
 		case 'f':


@@ 171,27 180,23 @@ func (p *parser) parse() error {
 			err = p.scanLiteral(nullTrail)
 
 		default:
-			err = fmt.Errorf("invalid character: %#U", p.cur)
-			p.advance()
+			err = fmt.Errorf("invalid character: %#U", typ)
 		}
 
-		if err != nil {
-			typ = '!'
-		}
 		if e := p.emit(start, typ, p.input[start:p.pos], err); e != nil {
 			return e
 		}
 	}
-	return nil
+	return p.emit(p.pos, p.cur, nil, io.EOF)
 }
 
 func (p *parser) scanLiteral(trail string) error {
-	if !bytes.HasPrefix(p.input[p.pos:], []byte(trail)) {
-		return ("nvalid character")
+	for _, b := range []byte(trail) {
+		if p.cur != b {
+			return ErrIncompleteLiteral
+		}
+		p.advance()
 	}
-
-	p.pos += len(trail)
-	p.advance()
 	return nil
 }
 

M zerojson_test.go => zerojson_test.go +18 -1
@@ 30,16 30,33 @@ func TestStack(t *testing.T) {
 	for count := 0; count < 2; count++ {
 		for _, v := range vals {
 			s.push(v)
+			b := s.peek()
+			if b != v {
+				t.Fatalf("peek: want %#U, got %#U", v, b)
+			}
 		}
 
 		for i := len(vals) - 1; i >= 0; i-- {
 			v := s.pop()
 			if v != vals[i] {
-				t.Fatalf("at %d: want %x, got %x", i, vals[i], v)
+				t.Fatalf("pop at %d: want %#U, got %#U", i, vals[i], v)
+			}
+
+			if i > 0 {
+				want := vals[i-1]
+				for j := 0; j < 2; j++ {
+					got := s.peek()
+					if want != got {
+						t.Fatalf("peek at %d: want %#U, got %#U", i, want, got)
+					}
+				}
 			}
 		}
 		if s.depth != 0 {
 			t.Fatalf("depth should be 0, is %d", s.depth)
 		}
+		if got := s.peek(); got != 0 {
+			t.Fatalf("peek when empty should be 0, got %#U", got)
+		}
 	}
 }