package json_array import ( "bufio" "strings" "main/walk" "encoding/json" ) func assembleValue(atoms []walk.AtomOLD) (interface{}, []walk.AtomOLD) { 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(values []interface{}, writer *bufio.Writer) { for _, value := range values { 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() }