<- Back to shtanton's homepage
aboutsummaryrefslogtreecommitdiff
path: root/json_array/read.go
blob: 633419743f780b53a2e543c108558c1584d3e4fd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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")
	}
}