package json
import (
)
type call uint8
const (
readCall call = iota
peekCall
)
const unexpectedFmt = "unexpected token %s"
var ErrUnexpectedEOF = errors.New("%v", io.ErrUnexpectedEOF)
type Decoder struct {
lastCall call
lastToken Token
lastErr error
openStack []Kind
orig []byte
in []byte
}
func ( []byte) *Decoder {
return &Decoder{orig: , in: }
}
func ( *Decoder) () (Token, error) {
defer func() { .lastCall = peekCall }()
if .lastCall == readCall {
.lastToken, .lastErr = .Read()
}
return .lastToken, .lastErr
}
func ( *Decoder) () (Token, error) {
const = Null | Bool | Number | String
defer func() { .lastCall = readCall }()
if .lastCall == peekCall {
return .lastToken, .lastErr
}
, := .parseNext()
if != nil {
return Token{},
}
switch .kind {
case EOF:
if len(.openStack) != 0 ||
.lastToken.kind&|ObjectClose|ArrayClose == 0 {
return Token{}, ErrUnexpectedEOF
}
case Null:
if !.isValueNext() {
return Token{}, .newSyntaxError(.pos, unexpectedFmt, .RawString())
}
case Bool, Number:
if !.isValueNext() {
return Token{}, .newSyntaxError(.pos, unexpectedFmt, .RawString())
}
case String:
if .isValueNext() {
break
}
if .lastToken.kind&(ObjectOpen|comma) == 0 {
return Token{}, .newSyntaxError(.pos, unexpectedFmt, .RawString())
}
if len(.in) == 0 {
return Token{}, ErrUnexpectedEOF
}
if := .in[0]; != ':' {
return Token{}, .newSyntaxError(.currPos(), `unexpected character %s, missing ":" after field name`, string())
}
.kind = Name
.consume(1)
case ObjectOpen, ArrayOpen:
if !.isValueNext() {
return Token{}, .newSyntaxError(.pos, unexpectedFmt, .RawString())
}
.openStack = append(.openStack, .kind)
case ObjectClose:
if len(.openStack) == 0 ||
.lastToken.kind == comma ||
.openStack[len(.openStack)-1] != ObjectOpen {
return Token{}, .newSyntaxError(.pos, unexpectedFmt, .RawString())
}
.openStack = .openStack[:len(.openStack)-1]
case ArrayClose:
if len(.openStack) == 0 ||
.lastToken.kind == comma ||
.openStack[len(.openStack)-1] != ArrayOpen {
return Token{}, .newSyntaxError(.pos, unexpectedFmt, .RawString())
}
.openStack = .openStack[:len(.openStack)-1]
case comma:
if len(.openStack) == 0 ||
.lastToken.kind&(|ObjectClose|ArrayClose) == 0 {
return Token{}, .newSyntaxError(.pos, unexpectedFmt, .RawString())
}
}
.lastToken =
if .lastToken.kind == comma {
return .()
}
return , nil
}
var errRegexp = regexp.MustCompile(`^([-+._a-zA-Z0-9]{1,32}|.)`)
func ( *Decoder) () (Token, error) {
.consume(0)
:= .in
if len() == 0 {
return .consumeToken(EOF, 0), nil
}
switch [0] {
case 'n':
if := matchWithDelim("null", ); != 0 {
return .consumeToken(Null, ), nil
}
case 't':
if := matchWithDelim("true", ); != 0 {
return .consumeBoolToken(true, ), nil
}
case 'f':
if := matchWithDelim("false", ); != 0 {
return .consumeBoolToken(false, ), nil
}
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
if , := parseNumber(); {
return .consumeToken(Number, ), nil
}
case '"':
, , := .parseString()
if != nil {
return Token{},
}
return .consumeStringToken(, ), nil
case '{':
return .consumeToken(ObjectOpen, 1), nil
case '}':
return .consumeToken(ObjectClose, 1), nil
case '[':
return .consumeToken(ArrayOpen, 1), nil
case ']':
return .consumeToken(ArrayClose, 1), nil
case ',':
return .consumeToken(comma, 1), nil
}
return Token{}, .newSyntaxError(.currPos(), "invalid value %s", errRegexp.Find())
}
func ( *Decoder) ( int, string, ...interface{}) error {
:= errors.New(, ...)
, := .Position()
return errors.New("syntax error (line %d:%d): %v", , , )
}
func ( *Decoder) ( int) ( int, int) {
:= .orig[:]
= bytes.Count(, []byte("\n")) + 1
if := bytes.LastIndexByte(, '\n'); >= 0 {
= [+1:]
}
= utf8.RuneCount() + 1
return ,
}
func ( *Decoder) () int {
return len(.orig) - len(.in)
}
func ( string, []byte) int {
if !bytes.HasPrefix(, []byte()) {
return 0
}
:= len()
if < len() && isNotDelim([]) {
return 0
}
return
}
func ( byte) bool {
return ( == '-' || == '+' || == '.' || == '_' ||
('a' <= && <= 'z') ||
('A' <= && <= 'Z') ||
('0' <= && <= '9'))
}
func ( *Decoder) ( int) {
.in = .in[:]
for len(.in) > 0 {
switch .in[0] {
case ' ', '\n', '\r', '\t':
.in = .in[1:]
default:
return
}
}
}
func ( *Decoder) () bool {
if len(.openStack) == 0 {
return .lastToken.kind == 0
}
:= .openStack[len(.openStack)-1]
switch {
case ObjectOpen:
return .lastToken.kind&Name != 0
case ArrayOpen:
return .lastToken.kind&(ArrayOpen|comma) != 0
}
panic(fmt.Sprintf(
"unreachable logic in Decoder.isValueNext, lastToken.kind: %v, openStack: %v",
.lastToken.kind, ))
}
func ( *Decoder) ( Kind, int) Token {
:= Token{
kind: ,
raw: .in[:],
pos: len(.orig) - len(.in),
}
.consume()
return
}
func ( *Decoder) ( bool, int) Token {
:= Token{
kind: Bool,
raw: .in[:],
pos: len(.orig) - len(.in),
boo: ,
}
.consume()
return
}
func ( *Decoder) ( string, int) Token {
:= Token{
kind: String,
raw: .in[:],
pos: len(.orig) - len(.in),
str: ,
}
.consume()
return
}
func ( *Decoder) () *Decoder {
:= *
.openStack = append([]Kind(nil), .openStack...)
return &
}