Commit 9b8d4e09 authored by Brad Fitzpatrick's avatar Brad Fitzpatrick

json: keep track of error offset in SyntaxError

R=rsc
CC=golang-dev
https://golang.org/cl/4430043
parent e8065656
...@@ -71,7 +71,7 @@ var unmarshalTests = []unmarshalTest{ ...@@ -71,7 +71,7 @@ var unmarshalTests = []unmarshalTest{
{`{"X":"a", "y":"b", "Z":"c"}`, new(badTag), badTag{"a", "b", "c"}, nil}, {`{"X":"a", "y":"b", "Z":"c"}`, new(badTag), badTag{"a", "b", "c"}, nil},
// syntax errors // syntax errors
{`{"X": "foo", "Y"}`, nil, nil, SyntaxError("invalid character '}' after object key")}, {`{"X": "foo", "Y"}`, nil, nil, &SyntaxError{"invalid character '}' after object key", 17}},
// composite tests // composite tests
{allValueIndent, new(All), allValue, nil}, {allValueIndent, new(All), allValue, nil},
...@@ -125,12 +125,12 @@ func TestMarshalBadUTF8(t *testing.T) { ...@@ -125,12 +125,12 @@ func TestMarshalBadUTF8(t *testing.T) {
} }
func TestUnmarshal(t *testing.T) { func TestUnmarshal(t *testing.T) {
var scan scanner
for i, tt := range unmarshalTests { for i, tt := range unmarshalTests {
var scan scanner
in := []byte(tt.in) in := []byte(tt.in)
if err := checkValid(in, &scan); err != nil { if err := checkValid(in, &scan); err != nil {
if !reflect.DeepEqual(err, tt.err) { if !reflect.DeepEqual(err, tt.err) {
t.Errorf("#%d: checkValid: %v", i, err) t.Errorf("#%d: checkValid: %#v", i, err)
continue continue
} }
} }
......
...@@ -23,6 +23,7 @@ import ( ...@@ -23,6 +23,7 @@ import (
func checkValid(data []byte, scan *scanner) os.Error { func checkValid(data []byte, scan *scanner) os.Error {
scan.reset() scan.reset()
for _, c := range data { for _, c := range data {
scan.bytes++
if scan.step(scan, int(c)) == scanError { if scan.step(scan, int(c)) == scanError {
return scan.err return scan.err
} }
...@@ -56,10 +57,12 @@ func nextValue(data []byte, scan *scanner) (value, rest []byte, err os.Error) { ...@@ -56,10 +57,12 @@ func nextValue(data []byte, scan *scanner) (value, rest []byte, err os.Error) {
} }
// A SyntaxError is a description of a JSON syntax error. // A SyntaxError is a description of a JSON syntax error.
type SyntaxError string type SyntaxError struct {
msg string // description of error
func (e SyntaxError) String() string { return string(e) } Offset int64 // error occurred after reading Offset bytes
}
func (e *SyntaxError) String() string { return e.msg }
// A scanner is a JSON scanning state machine. // A scanner is a JSON scanning state machine.
// Callers call scan.reset() and then pass bytes in one at a time // Callers call scan.reset() and then pass bytes in one at a time
...@@ -89,6 +92,9 @@ type scanner struct { ...@@ -89,6 +92,9 @@ type scanner struct {
// 1-byte redo (see undo method) // 1-byte redo (see undo method)
redoCode int redoCode int
redoState func(*scanner, int) int redoState func(*scanner, int) int
// total bytes consumed, updated by decoder.Decode
bytes int64
} }
// These values are returned by the state transition functions // These values are returned by the state transition functions
...@@ -148,7 +154,7 @@ func (s *scanner) eof() int { ...@@ -148,7 +154,7 @@ func (s *scanner) eof() int {
return scanEnd return scanEnd
} }
if s.err == nil { if s.err == nil {
s.err = SyntaxError("unexpected end of JSON input") s.err = &SyntaxError{"unexpected end of JSON input", s.bytes}
} }
return scanError return scanError
} }
...@@ -581,7 +587,7 @@ func stateError(s *scanner, c int) int { ...@@ -581,7 +587,7 @@ func stateError(s *scanner, c int) int {
// error records an error and switches to the error state. // error records an error and switches to the error state.
func (s *scanner) error(c int, context string) int { func (s *scanner) error(c int, context string) int {
s.step = stateError s.step = stateError
s.err = SyntaxError("invalid character " + quoteChar(c) + " " + context) s.err = &SyntaxError{"invalid character " + quoteChar(c) + " " + context, s.bytes}
return scanError return scanError
} }
......
...@@ -23,8 +23,8 @@ func NewDecoder(r io.Reader) *Decoder { ...@@ -23,8 +23,8 @@ func NewDecoder(r io.Reader) *Decoder {
return &Decoder{r: r} return &Decoder{r: r}
} }
// Decode reads the next JSON-encoded value from the // Decode reads the next JSON-encoded value from its
// connection and stores it in the value pointed to by v. // input and stores it in the value pointed to by v.
// //
// See the documentation for Unmarshal for details about // See the documentation for Unmarshal for details about
// the conversion of JSON into a Go value. // the conversion of JSON into a Go value.
...@@ -62,6 +62,7 @@ Input: ...@@ -62,6 +62,7 @@ Input:
for { for {
// Look in the buffer for a new value. // Look in the buffer for a new value.
for i, c := range dec.buf[scanp:] { for i, c := range dec.buf[scanp:] {
dec.scan.bytes++
v := dec.scan.step(&dec.scan, int(c)) v := dec.scan.step(&dec.scan, int(c))
if v == scanEnd { if v == scanEnd {
scanp += i scanp += i
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment