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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
package json_array
import (
"bufio"
"strings"
"main/walk"
"encoding/json"
)
func assembleValue(atoms []walk.Atom) (interface{}, []walk.Atom) {
if len(atoms) == 0 {
panic("Missing JSON value in output")
}
switch atoms[0].Typ {
case walk.AtomNull:
return nil, atoms[1:]
case walk.AtomBool:
return atoms[0].Bool(), atoms[1:]
case walk.AtomNumber:
return atoms[0].Number(), atoms[1:]
case walk.AtomStringTerminal:
var builder strings.Builder
atoms = atoms[1:]
for {
if len(atoms) == 0 {
panic("Missing closing string terminal")
}
if atoms[0].Typ == walk.AtomStringTerminal {
break
}
if atoms[0].Typ != walk.AtomStringRune {
panic("Non string rune atom inside string")
}
builder.WriteRune(atoms[0].StringRune())
atoms = atoms[1:]
}
atoms = atoms[1:]
return builder.String(), atoms
case walk.AtomStringRune:
panic("String rune used outside of string terminals")
case walk.AtomTerminal:
terminal := atoms[0].Terminal()
switch terminal {
case walk.ArrayEnd, walk.MapEnd:
panic("Tried to extract value from end terminal")
case walk.ArrayBegin:
var arr []interface{}
var element interface{}
atoms = atoms[1:]
for {
if len(atoms) == 0 {
panic("Missing array end terminal")
}
if atoms[0].Typ == walk.AtomTerminal && atoms[0].Terminal() == walk.ArrayEnd {
atoms = atoms[1:]
break
}
element, atoms = assembleValue(atoms)
arr = append(arr, element)
}
return arr, atoms
case walk.MapBegin:
obj := make(map[string]interface{})
var key interface{}
var element interface{}
atoms = atoms[1:]
for {
if len(atoms) == 0 {
panic("Missing map end terminal")
}
if atoms[0].Typ == walk.AtomTerminal && atoms[0].Terminal() == walk.MapEnd {
atoms = atoms[1:]
break
}
key, atoms = assembleValue(atoms)
element, atoms = assembleValue(atoms)
keyString, keyIsString := key.(string)
if !keyIsString {
panic("Key is not string")
}
obj[keyString] = element
}
return obj, atoms
default:
panic("Invalid terminal")
}
default:
panic("Invalid atom")
}
}
func outputValue(atoms []walk.Atom, writer *bufio.Writer) {
if len(atoms) == 0 {
return
}
value, atoms := assembleValue(atoms)
if len(atoms) != 0 {
panic("Tried to output more than one JSON value")
}
bytes, err := json.MarshalIndent(value, "\t", "\t")
if err != nil {
panic("Error marshalling json into bytes")
}
_, err = writer.Write(bytes)
if err != nil {
panic("Error writing value")
}
}
type writerState int
const (
writerStateStart writerState = iota
writerStateValue
)
func NewJSONArrayWriter(writer *bufio.Writer) *JSONArrayWriter {
return &JSONArrayWriter {
writer: writer,
state: writerStateStart,
}
}
type JSONArrayWriter struct {
writer *bufio.Writer
state writerState
}
func (out *JSONArrayWriter) Write(item walk.WalkItem) error {
switch out.state {
case writerStateStart:
_, err := out.writer.WriteString("[\n\t")
if err != nil {
panic("Error outputting [ at beginning of array")
}
outputValue(item.Value, out.writer)
out.state = writerStateValue
return nil
case writerStateValue:
_, err := out.writer.WriteString(",\n\t")
if err != nil {
panic("Error outputting comma at the end of a value")
}
outputValue(item.Value, out.writer)
return nil
default:
panic("Invalid writer state")
}
}
func (out *JSONArrayWriter) AssertDone() {
if out.state == writerStateStart {
out.writer.WriteString("[")
}
out.writer.WriteString("\n]")
out.writer.Flush()
}
|