Skip to content

Commit

Permalink
simplify consume api to use fewer function calls, clarify whitespace …
Browse files Browse the repository at this point in the history
…skipping
  • Loading branch information
mumbleskates committed Mar 18, 2024
1 parent 8f51817 commit 9d37434
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 84 deletions.
10 changes: 10 additions & 0 deletions json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,3 +216,13 @@ func TestParseObject(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, val, map[string]any{"a": int64(1)})
}

func TestWhitespaceSkipping(t *testing.T) {
val, err := UnmarshalString(` { "a" : 1 } `)
require.NoError(t, err)
assert.Equal(t, map[string]any{"a": int64(1)}, val)

val, err = UnmarshalString(` [ true , false ] `)
require.NoError(t, err)
assert.Equal(t, []any{true, false}, val)
}
140 changes: 56 additions & 84 deletions parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -530,42 +530,6 @@ ReadingChunks:
return p.strBuf.Bytes(), nil
}

func (p *parser) consumeArrayBegin() (err error) {
return p.readByte('[')
}

func (p *parser) consumeArrayEnd() (err error) {
if err = p.skipSpaces(); err != nil {
return
}
return p.readByte(']')
}

func (p *parser) consumeObjectBegin() (err error) {
return p.readByte('{')
}

func (p *parser) consumeObjectEnd() (err error) {
if err = p.skipSpaces(); err != nil {
return
}
return p.readByte('}')
}

func (p *parser) consumeColon() (err error) {
if err = p.skipSpaces(); err != nil {
return
}
return p.readByte(':')
}

func (p *parser) consumeComma() (err error) {
if err = p.skipSpaces(); err != nil {
return
}
return p.readByte(',')
}

func (p *parser) Parse() (val any, err error) {
return p.doParse(maxDepth)
}
Expand Down Expand Up @@ -601,66 +565,70 @@ func (p *parser) doParse(remainingDepth int) (val any, err error) {
// refer to bytes in the original buffer.
val = string(str)
case arrayTy:
// Consume the opening bracket
err = p.consumeArrayBegin()
val, err = p.doParseArray(remainingDepth)
case objectTy:
val, err = p.doParseObject(remainingDepth)
case commaSym:
return nil, errUnexpectedComma
case endGroupSym:
return nil, errUnexpectedEnd
default:
panic("unreachable")
}
if err != nil {
val = nil
}
return
}

func (p *parser) doParseArray(remainingDepth int) (arr []any, err error) {
// Consume the opening bracket
err = p.readByte('[')
if err != nil {
return
}
for {
var ty valType
ty, err = p.parseType()
if err != nil {
return
}
var arr []any
for {
ty, err = p.parseType()
if ty == endGroupSym {
// Found an ending brace/bracket immediately after the start of
// the array or one of its values, cleanly ending the array
err = p.readByte(']')
if err != nil {
return
}
if ty == endGroupSym {
// Found an ending brace/bracket immediately after the start of
// the array or one of its values, cleanly ending the array
err = p.consumeArrayEnd()
if err != nil {
return
}
break
} else if len(arr) == 0 {
if ty == commaSym {
// Found a comma with no previous value
return nil, errUnexpectedComma
}
} else {
// We just read a value and the array hasn't ended. We MUST find
// a comma next.
err = p.consumeComma()
if err != nil {
return
}
break
} else if len(arr) == 0 {
if ty == commaSym {
// Found a comma with no previous value
return nil, errUnexpectedComma
}
// We now have a regular following value, not an errant comma or the
// end of the array.
var arrVal any
arrVal, err = p.doParse(remainingDepth - 1)
} else {
// We just read a value and the array hasn't ended. We MUST find
// a comma next, and we have already skipped whitespace.
err = p.readByte(',')
if err != nil {
return
}
arr = append(arr, arrVal)
}
val = arr
case objectTy:
val, err = p.doParseObject(remainingDepth)
case commaSym:
return nil, errUnexpectedComma
case endGroupSym:
return nil, errUnexpectedEnd
case unknownTy:
panic("unreachable")
}
if err != nil {
val = nil
// We now have a regular following value, not an errant comma or the
// end of the array.
var arrVal any
arrVal, err = p.doParse(remainingDepth - 1)
if err != nil {
return
}
arr = append(arr, arrVal)
}
return
}

func (p *parser) doParseObject(remainingDepth int) (obj map[string]any, err error) {
// Consume the beginning of the map
err = p.consumeObjectBegin()
// Consume the beginning of the object
err = p.readByte('{')
if err != nil {
return nil, err
}
Expand All @@ -673,7 +641,7 @@ func (p *parser) doParseObject(remainingDepth int) (obj map[string]any, err erro
if ty == endGroupSym {
// Found an ending brace/bracket immediately after the start of
// the object or one of its items, cleanly ending the object
err = p.consumeObjectEnd()
err = p.readByte('}')
if err != nil {
return
}
Expand All @@ -687,8 +655,8 @@ func (p *parser) doParseObject(remainingDepth int) (obj map[string]any, err erro
obj = make(map[string]any)
} else {
// We just parsed an item and the object hasn't ended. We MUST
// find a comma next.
err = p.consumeComma()
// find a comma next, and we have already skipped whitespace.
err = p.readByte(',')
if err != nil {
return
}
Expand All @@ -708,7 +676,11 @@ func (p *parser) doParseObject(remainingDepth int) (obj map[string]any, err erro
}
objKey := string(objKeyBytes)
// Consume the ':' separating the key and value
err = p.consumeColon()
err = p.skipSpaces()
if err != nil {
return
}
err = p.readByte(':')
if err != nil {
return
}
Expand Down

0 comments on commit 9d37434

Please sign in to comment.