208 lines
5.4 KiB
Go
208 lines
5.4 KiB
Go
|
package json
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"io"
|
||
|
|
||
|
"github.com/polydawn/refmt/shared"
|
||
|
. "github.com/polydawn/refmt/tok"
|
||
|
)
|
||
|
|
||
|
type Decoder struct {
|
||
|
r shared.SlickReader
|
||
|
|
||
|
stack []decoderStep // When empty, and step returns done, all done.
|
||
|
step decoderStep // Shortcut to end of stack.
|
||
|
some bool // Set to true after first value in any context; use to decide if a comma must precede the next value.
|
||
|
}
|
||
|
|
||
|
func NewDecoder(r io.Reader) (d *Decoder) {
|
||
|
d = &Decoder{
|
||
|
r: shared.NewReader(r),
|
||
|
stack: make([]decoderStep, 0, 10),
|
||
|
}
|
||
|
d.step = d.step_acceptValue
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (d *Decoder) Reset() {
|
||
|
d.stack = d.stack[0:0]
|
||
|
d.step = d.step_acceptValue
|
||
|
d.some = false
|
||
|
}
|
||
|
|
||
|
type decoderStep func(tokenSlot *Token) (done bool, err error)
|
||
|
|
||
|
func (d *Decoder) Step(tokenSlot *Token) (done bool, err error) {
|
||
|
done, err = d.step(tokenSlot)
|
||
|
// If the step errored: out, entirely.
|
||
|
if err != nil {
|
||
|
return true, err
|
||
|
}
|
||
|
// If the step wasn't done, return same status.
|
||
|
if !done {
|
||
|
return false, nil
|
||
|
}
|
||
|
// If it WAS done, and stack empty, we're entirely done.
|
||
|
nSteps := len(d.stack) - 1
|
||
|
if nSteps <= 0 {
|
||
|
return true, nil // that's all folks
|
||
|
}
|
||
|
// Pop the stack. Reset "some" to true.
|
||
|
d.step = d.stack[nSteps]
|
||
|
d.stack = d.stack[0:nSteps]
|
||
|
d.some = true
|
||
|
return false, nil
|
||
|
}
|
||
|
|
||
|
func (d *Decoder) pushPhase(newPhase decoderStep) {
|
||
|
d.stack = append(d.stack, d.step)
|
||
|
d.step = newPhase
|
||
|
d.some = false
|
||
|
}
|
||
|
|
||
|
func readn1skippingWhitespace(r shared.SlickReader) (majorByte byte, err error) {
|
||
|
for {
|
||
|
majorByte, err = r.Readn1()
|
||
|
switch majorByte {
|
||
|
case ' ', '\t', '\r', '\n': // continue
|
||
|
default:
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// The original step, where any value is accepted, and no terminators for composites are valid.
|
||
|
// ONLY used in the original step; all other steps handle leaf nodes internally.
|
||
|
func (d *Decoder) step_acceptValue(tokenSlot *Token) (done bool, err error) {
|
||
|
majorByte, err := readn1skippingWhitespace(d.r)
|
||
|
if err != nil {
|
||
|
return true, err
|
||
|
}
|
||
|
return d.stepHelper_acceptValue(majorByte, tokenSlot)
|
||
|
}
|
||
|
|
||
|
// Step in midst of decoding an array.
|
||
|
func (d *Decoder) step_acceptArrValueOrBreak(tokenSlot *Token) (done bool, err error) {
|
||
|
majorByte, err := readn1skippingWhitespace(d.r)
|
||
|
if err != nil {
|
||
|
return true, err
|
||
|
}
|
||
|
if d.some {
|
||
|
switch majorByte {
|
||
|
case ']':
|
||
|
tokenSlot.Type = TArrClose
|
||
|
return true, nil
|
||
|
case ',':
|
||
|
majorByte, err = readn1skippingWhitespace(d.r)
|
||
|
if err != nil {
|
||
|
return true, err
|
||
|
}
|
||
|
// and now fall through to the next switch
|
||
|
}
|
||
|
}
|
||
|
switch majorByte {
|
||
|
case ']':
|
||
|
tokenSlot.Type = TArrClose
|
||
|
return true, nil
|
||
|
default:
|
||
|
_, err := d.stepHelper_acceptValue(majorByte, tokenSlot)
|
||
|
d.some = true
|
||
|
return false, err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Step in midst of decoding a map, key expected up next, or end.
|
||
|
func (d *Decoder) step_acceptMapKeyOrBreak(tokenSlot *Token) (done bool, err error) {
|
||
|
majorByte, err := readn1skippingWhitespace(d.r)
|
||
|
if err != nil {
|
||
|
return true, err
|
||
|
}
|
||
|
if d.some {
|
||
|
switch majorByte {
|
||
|
case '}':
|
||
|
tokenSlot.Type = TMapClose
|
||
|
return true, nil
|
||
|
case ',':
|
||
|
majorByte, err = readn1skippingWhitespace(d.r)
|
||
|
if err != nil {
|
||
|
return true, err
|
||
|
}
|
||
|
// and now fall through to the next switch
|
||
|
}
|
||
|
}
|
||
|
switch majorByte {
|
||
|
case '}':
|
||
|
tokenSlot.Type = TMapClose
|
||
|
return true, nil
|
||
|
default:
|
||
|
// Consume a string for key.
|
||
|
_, err := d.stepHelper_acceptValue(majorByte, tokenSlot) // FIXME surely not *any* value? not composites, at least?
|
||
|
// Now scan up to consume the colon as well, which is required next.
|
||
|
majorByte, err = readn1skippingWhitespace(d.r)
|
||
|
if err != nil {
|
||
|
return true, err
|
||
|
}
|
||
|
if majorByte != ':' {
|
||
|
return true, fmt.Errorf("expected colon after map key; got 0x%x", majorByte)
|
||
|
}
|
||
|
// Next up: expect a value.
|
||
|
d.step = d.step_acceptMapValue
|
||
|
d.some = true
|
||
|
return false, err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Step in midst of decoding a map, value expected up next.
|
||
|
func (d *Decoder) step_acceptMapValue(tokenSlot *Token) (done bool, err error) {
|
||
|
majorByte, err := readn1skippingWhitespace(d.r)
|
||
|
if err != nil {
|
||
|
return true, err
|
||
|
}
|
||
|
d.step = d.step_acceptMapKeyOrBreak
|
||
|
_, err = d.stepHelper_acceptValue(majorByte, tokenSlot)
|
||
|
return false, err
|
||
|
}
|
||
|
|
||
|
func (d *Decoder) stepHelper_acceptValue(majorByte byte, tokenSlot *Token) (done bool, err error) {
|
||
|
switch majorByte {
|
||
|
case '{':
|
||
|
tokenSlot.Type = TMapOpen
|
||
|
tokenSlot.Length = -1
|
||
|
d.pushPhase(d.step_acceptMapKeyOrBreak)
|
||
|
return false, nil
|
||
|
case '[':
|
||
|
tokenSlot.Type = TArrOpen
|
||
|
tokenSlot.Length = -1
|
||
|
d.pushPhase(d.step_acceptArrValueOrBreak)
|
||
|
return false, nil
|
||
|
case 'n':
|
||
|
d.r.Readnzc(3) // FIXME must check these equal "ull"!
|
||
|
tokenSlot.Type = TNull
|
||
|
return true, nil
|
||
|
case '"':
|
||
|
tokenSlot.Type = TString
|
||
|
tokenSlot.Str, err = d.decodeString()
|
||
|
return true, err
|
||
|
case 'f':
|
||
|
d.r.Readnzc(4) // FIXME must check these equal "alse"!
|
||
|
tokenSlot.Type = TBool
|
||
|
tokenSlot.Bool = false
|
||
|
return true, nil
|
||
|
case 't':
|
||
|
d.r.Readnzc(3) // FIXME must check these equal "rue"!
|
||
|
tokenSlot.Type = TBool
|
||
|
tokenSlot.Bool = true
|
||
|
return true, nil
|
||
|
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||
|
// Some kind of numeric... but in json, we *can't tell* if it's float or int.
|
||
|
// JSON in general doesn't differentiate. But we usually try to anyway.
|
||
|
// (If this results in us yielding an int, and an obj.Unmarshaller is filling a float,
|
||
|
// it's the Unmarshaller responsibility to decide to cast that.)
|
||
|
tokenSlot.Type, tokenSlot.Int, tokenSlot.Float64, err = d.decodeNumber(majorByte)
|
||
|
return true, err
|
||
|
default:
|
||
|
return true, fmt.Errorf("Invalid byte while expecting start of value: 0x%x", majorByte)
|
||
|
}
|
||
|
}
|