package json_array import ( "main/walk" "encoding/json" "errors" "bufio" ) type state int const ( stateStart state = iota stateValueStart stateEnd stateDead ) func atomiseValue(value interface{}) []walk.Atom { switch v := value.(type) { case nil: return []walk.Atom{walk.NewAtomNull()} case bool: return []walk.Atom{walk.NewAtomBool(v)} case float64: return []walk.Atom{walk.NewAtomNumber(v)} case string: atoms := []walk.Atom{walk.NewAtomStringTerminal()} for _, r := range v { atoms = append(atoms, walk.NewAtomStringRune(r)) } atoms = append(atoms, walk.NewAtomStringTerminal()) return atoms case []interface{}: atoms := []walk.Atom{walk.NewAtomTerminal(walk.ArrayBegin)} for _, element := range v { atoms = append(atoms, atomiseValue(element)...) } atoms = append(atoms, walk.NewAtomTerminal(walk.ArrayEnd)) return atoms case map[string]interface{}: atoms := []walk.Atom{walk.NewAtomTerminal(walk.MapBegin)} for key, element := range v { atoms = append(atoms, atomiseValue(key)...) atoms = append(atoms, atomiseValue(element)...) } atoms = append(atoms, walk.NewAtomTerminal(walk.MapEnd)) return atoms default: panic("Invalid JSON value type") } } func NewJSONArrayReader(reader *bufio.Reader) *JSONArrayReader { return &JSONArrayReader { decoder: json.NewDecoder(reader), state: stateStart, index: 0, } } type JSONArrayReader struct { decoder *json.Decoder state state index int } func (in *JSONArrayReader) Read() (walk.WalkItem, error) { restart: switch in.state { case stateStart: arrayStart, err := in.decoder.Token() if err != nil { panic("Error reading start of JSON array") } delim, isDelim := arrayStart.(json.Delim) if !isDelim || delim != '[' { panic("JSON input is not an array!") } in.state = stateValueStart goto restart case stateValueStart: if !in.decoder.More() { in.state = stateEnd goto restart } var m interface{} err := in.decoder.Decode(&m) if err != nil { panic("Error decoding array value") } in.index += 1 return walk.WalkItem { Path: []walk.Atom{walk.NewAtomNumber(float64(in.index - 1))}, Value: atomiseValue(m), }, nil case stateEnd: arrayEnd, err := in.decoder.Token() if err != nil { panic("Error reading end of JSON array") } delim, isDelim := arrayEnd.(json.Delim) if !isDelim || delim != ']' { panic("JSON array wasn't ended") } in.state = stateDead return walk.WalkItem{}, errors.New("eof") case stateDead: return walk.WalkItem{}, errors.New("eof") default: panic("Unreachable!!!") } } func (in *JSONArrayReader) AssertDone() { if in.state != stateDead || in.decoder.More() { panic("More JSON after array value") } }