diff options
Diffstat (limited to 'json_array/write.go')
-rw-r--r-- | json_array/write.go | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/json_array/write.go b/json_array/write.go new file mode 100644 index 0000000..4d202c4 --- /dev/null +++ b/json_array/write.go @@ -0,0 +1,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() +} |