diff options
-rw-r--r-- | json_array/read.go | 118 | ||||
-rw-r--r-- | json_array/write.go | 151 | ||||
-rw-r--r-- | json_tokens/read.go | 490 | ||||
-rw-r--r-- | json_tokens/write.go | 151 |
4 files changed, 0 insertions, 910 deletions
diff --git a/json_array/read.go b/json_array/read.go deleted file mode 100644 index 786bc2c..0000000 --- a/json_array/read.go +++ /dev/null @@ -1,118 +0,0 @@ -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.AtomOLD { - switch v := value.(type) { - case nil: - return []walk.AtomOLD{walk.NewAtomNull()} - case bool: - return []walk.AtomOLD{walk.NewAtomBool(v)} - case float64: - return []walk.AtomOLD{walk.NewAtomNumber(v)} - case string: - atoms := []walk.AtomOLD{walk.NewAtomStringTerminal()} - for _, r := range v { - atoms = append(atoms, walk.NewAtomStringRune(r)) - } - atoms = append(atoms, walk.NewAtomStringTerminal()) - return atoms - case []interface{}: - atoms := []walk.AtomOLD{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.AtomOLD{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: []interface{}{float64(in.index - 1)}, - Value: []interface{}{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") - } -} diff --git a/json_array/write.go b/json_array/write.go deleted file mode 100644 index aaa2851..0000000 --- a/json_array/write.go +++ /dev/null @@ -1,151 +0,0 @@ -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() -} diff --git a/json_tokens/read.go b/json_tokens/read.go deleted file mode 100644 index b0acf71..0000000 --- a/json_tokens/read.go +++ /dev/null @@ -1,490 +0,0 @@ -package json_tokens - -import ( - "main/walk" - "bufio" - "strings" - "strconv" - "fmt" -) - -type ReadAction int -const ( - ActionReadValue ReadAction = iota - ActionAppendPath - ActionPopPath - ActionIncrementPath - ActionAppendPathNull -) - -type JSONInStructure int -const ( - JSONInMap JSONInStructure = iota - JSONInArray -) - -type JSONInState int -const ( - JSONInValueEnd JSONInState = iota - JSONInValue - JSONInValueStart - JSONInString - JSONInKey -) - -type JSONIn struct { - path []walk.AtomOLD - reader *bufio.Reader - structure []JSONInStructure - state JSONInState - readBuffer []walk.AtomOLD - readIndex int - readBufferCapacity int - actionBuffer []ReadAction - actionIndex int -} - -func NewJSONIn(reader *bufio.Reader) *JSONIn { - return &JSONIn { - path: make([]walk.AtomOLD, 0, 256), - reader: reader, - structure: []JSONInStructure{}, - state: JSONInValueStart, - readBuffer: make([]walk.AtomOLD, 0, 256), - readIndex: 0, - readBufferCapacity: 256, - actionBuffer: make([]ReadAction, 0, 256), - actionIndex: 0, - } -} - -func isWhitespace(r rune) bool { - for _, ws := range " \t\r\n" { - if r == ws { - return true - } - } - return false -} - -func isNumberRune(r rune) bool { - return '0' <= r && r <= '9' || r == '.' -} - -func (in *JSONIn) popPath() { - if len(in.path) == 0 { - panic("Tried to pop from empty path") - } - finalAtom := in.path[len(in.path) - 1] - if finalAtom.Typ != walk.AtomStringTerminal { - in.path = in.path[:len(in.path) - 1] - return - } - i := len(in.path) - 2 - for { - if i < 0 { - panic("Missing string begin in path") - } - if in.path[i].Typ == walk.AtomStringTerminal { - break - } - i-- - } - in.path = in.path[:i] -} - -func (in *JSONIn) nextNonWsRune() (rune, error) { - for { - r, _, err := in.reader.ReadRune() - if err != nil { - return 0, err - } - if !isWhitespace(r) { - return r, nil - } - } -} - -func (in *JSONIn) requireString(criteria string) { - for _, r := range criteria { - in.require(r) - } -} - -func (in *JSONIn) require(criterion rune) { - r, _, err := in.reader.ReadRune() - if err != nil { - panic("Error while reading required rune: " + err.Error()) - } - if r != criterion { - panic("Required rune not read") - } -} - -// Returns the first full value of a list of atoms and also a boolean to indicate if there isn't a value at the beginning -func firstValue(atoms []walk.AtomOLD) ([]walk.AtomOLD, bool) { - if len(atoms) == 0 { - return nil, true - } - if atoms[0].Typ != walk.AtomStringTerminal { - return atoms[0:1], false - } - i := 1 - for { - if i == len(atoms) { - return nil, true - } - if atoms[i].Typ == walk.AtomStringTerminal { - return atoms[0:i+1], false - } - i++ - } -} - -func (in *JSONIn) readValue() []walk.AtomOLD { - try: - value, incomplete := firstValue(in.readBuffer[in.readIndex:]) - if incomplete { - if in.readIndex == 0 { - newReadBuffer := make([]walk.AtomOLD, len(in.readBuffer), in.readBufferCapacity * 2) - in.readBufferCapacity *= 2 - copy(newReadBuffer, in.readBuffer) - in.readBuffer = newReadBuffer - in.fillReadBuffer() - goto try - } - copy(in.readBuffer, in.readBuffer[in.readIndex:]) - in.readBuffer = in.readBuffer[:len(in.readBuffer) - in.readIndex] - in.readIndex = 0 - copy(in.actionBuffer, in.actionBuffer[in.actionIndex:]) - in.actionBuffer = in.actionBuffer[:len(in.actionBuffer) - in.actionIndex] - in.actionIndex = 0 - in.fillReadBuffer() - goto try - } - in.readIndex += len(value) - return value -} - -func (in *JSONIn) Read() (walk.WalkItem, error) { - for { - if in.actionIndex == len(in.actionBuffer) { - in.actionIndex = 0 - in.readIndex = 0 - in.actionBuffer = in.actionBuffer[:0] - in.readBuffer = in.readBuffer[:0] - err := in.fillReadBuffer() - if len(in.actionBuffer) == 0 { - return walk.WalkItem{}, err - } - } - action := in.actionBuffer[in.actionIndex] - in.actionIndex++ - switch action { - case ActionReadValue: - value := in.readValue() - return walk.WalkItem { - Value: value, - Path: in.path, - }, nil - case ActionAppendPath: - value := in.readValue() - in.path = append(in.path, value...) - case ActionAppendPathNull: - in.path = append(in.path, walk.NewAtomNull()) - case ActionPopPath: - in.popPath() - case ActionIncrementPath: - prevIndex := in.path[len(in.path) - 1] - if prevIndex.Typ == walk.AtomNull { - prevIndex = walk.NewAtomNumber(0) - } else if prevIndex.Typ == walk.AtomNumber { - prevIndex = walk.NewAtomNumber(prevIndex.Number() + 1) - } else { - panic("Invalid index in array input. Type: " + fmt.Sprintf("%v", prevIndex.Typ)) - } - in.path[len(in.path) - 1] = prevIndex - default: - panic("Invalid ReadAction") - } - } -} - -func (in *JSONIn) AssertDone() { - if len(in.structure) != 0 || in.state != JSONInValueEnd || in.readIndex < len(in.readBuffer) { - panic("Input ended on incomplete JSON root") - } -} - -func (in *JSONIn) pushReadBuffer(atom walk.AtomOLD) bool { - in.readBuffer = append(in.readBuffer, atom) - return len(in.readBuffer) == in.readBufferCapacity -} - -func (in *JSONIn) pushActionBuffer(action ReadAction) { - in.actionBuffer = append(in.actionBuffer, action) -} - -// Appends to the readBuffer until it has reached capacity -// Also appends to the actionBuffer as needed -func (in *JSONIn) fillReadBuffer() error { - switch in.state { - case JSONInValueStart: - goto valueStart - case JSONInValue: - goto value - case JSONInValueEnd: - goto valueEnd - case JSONInString: - goto string - case JSONInKey: - goto key - default: - panic("Invalid JSONInState") - } - valueStart: { - if len(in.structure) == 0 { - goto value - } - innermost := in.structure[len(in.structure) - 1] - switch innermost { - case JSONInMap: - goto mapValue - case JSONInArray: - goto arrayValue - default: - panic("Invalid JSONInStructure") - } - } - value: { - r, err := in.nextNonWsRune() - if err != nil { - panic("Missing value in JSON") - } - switch r { - case 'n': - in.requireString("ull") - in.pushActionBuffer(ActionReadValue) - if in.pushReadBuffer(walk.NewAtomNull()) { - in.state = JSONInValueEnd - return nil - } - goto valueEnd - case 'f': - in.requireString("alse") - in.pushActionBuffer(ActionReadValue) - if in.pushReadBuffer(walk.NewAtomBool(false)) { - in.state = JSONInValueEnd - return nil - } - goto valueEnd - case 't': - in.requireString("rue") - in.pushActionBuffer(ActionReadValue) - if in.pushReadBuffer(walk.NewAtomBool(true)) { - in.state = JSONInValueEnd - return nil - } - goto valueEnd - case '"': - in.pushActionBuffer(ActionReadValue) - if in.pushReadBuffer(walk.NewAtomStringTerminal()) { - in.state = JSONInString - return nil - } - goto string - case '{': - in.structure = append(in.structure, JSONInMap) - in.pushActionBuffer(ActionReadValue) - in.pushActionBuffer(ActionAppendPathNull) - if in.pushReadBuffer(walk.NewAtomTerminal(walk.MapBegin)) { - in.state = JSONInValueStart - return nil - } - goto mapValue - case '[': - in.structure = append(in.structure, JSONInArray) - in.pushActionBuffer(ActionReadValue) - in.pushActionBuffer(ActionAppendPathNull) - if in.pushReadBuffer(walk.NewAtomTerminal(walk.ArrayBegin)) { - in.state = JSONInValueStart - return nil - } - goto arrayValue - } - if isNumberRune(r) { - var builder strings.Builder - builder.WriteRune(r) - for { - r, _, err = in.reader.ReadRune() - if err != nil { - break - } - if !isNumberRune(r) { - in.reader.UnreadRune() - break - } - builder.WriteRune(r) - } - number, parseError := strconv.ParseFloat(builder.String(), 64) - if parseError != nil { - panic("Invalid number") - } - in.pushActionBuffer(ActionReadValue) - if in.pushReadBuffer(walk.NewAtomNumber(number)) { - in.state = JSONInValueEnd - return nil - } - goto valueEnd - } - panic("Invalid JSON value starting with: " + string(r)) - } - string: { - r, _, err := in.reader.ReadRune() - if err != nil { - panic("Missing closing terminal in string input: " + err.Error()) - } - if r == '"' { - if in.pushReadBuffer(walk.NewAtomStringTerminal()) { - in.state = JSONInValueEnd - return nil - } - goto valueEnd - } - if r == '\\' { - r, _, err = in.reader.ReadRune() - if err != nil { - panic("Missing rune after \\") - } - if in.pushReadBuffer(walk.NewAtomStringRune(r)) { - in.state = JSONInString - return nil - } - goto string - } - if in.pushReadBuffer(walk.NewAtomStringRune(r)) { - in.state = JSONInString - return nil - } - goto string - } - key: { - var full bool - for { - r, _, err := in.reader.ReadRune() - if err != nil { - panic("Missing closing terminal in string input: " + err.Error()) - } - if r == '"' { - full = in.pushReadBuffer(walk.NewAtomStringTerminal()) - break - } - if r == '\\' { - r, _, err = in.reader.ReadRune() - if err != nil { - panic("Missing rune after \\") - } - if in.pushReadBuffer(walk.NewAtomStringRune(r)) { - in.state = JSONInKey - return nil - } - continue - } - if in.pushReadBuffer(walk.NewAtomStringRune(r)) { - in.state = JSONInKey - return nil - } - continue - } - r, err := in.nextNonWsRune() - if err != nil { - panic("Expected : got: " + err.Error()) - } - if r != ':' { - panic("Expected : after key") - } - if full { - in.state = JSONInValue - return nil - } - goto value - } - valueEnd: { - r, err := in.nextNonWsRune() - if err != nil { - in.state = JSONInValueEnd - return err - } - if len(in.structure) == 0 { - panic("More input after root JSON object ends") - } - innermost := in.structure[len(in.structure) - 1] - if innermost == JSONInMap && r == '}' { - in.structure = in.structure[:len(in.structure) - 1] - in.pushActionBuffer(ActionPopPath) - in.pushActionBuffer(ActionReadValue) - if in.pushReadBuffer(walk.NewAtomTerminal(walk.MapEnd)) { - in.state = JSONInValueEnd - return nil - } - goto valueEnd - } else if innermost == JSONInArray && r == ']' { - in.structure = in.structure[:len(in.structure) - 1] - in.pushActionBuffer(ActionPopPath) - in.pushActionBuffer(ActionReadValue) - if in.pushReadBuffer(walk.NewAtomTerminal(walk.ArrayEnd)) { - in.state = JSONInValueEnd - return nil - } - goto valueEnd - } - if r != ',' { - panic("Expected , after JSON value, found: \"" + string(r) + "\"") - } - goto valueStart - } - mapValue: { - in.pushActionBuffer(ActionPopPath) - r, err := in.nextNonWsRune() - if err != nil { - panic("Missing value inside object") - } - if r == '}' { - in.structure = in.structure[:len(in.structure) - 1] - in.pushActionBuffer(ActionReadValue) - if in.pushReadBuffer(walk.NewAtomTerminal(walk.MapEnd)) { - in.state = JSONInValueEnd - return nil - } - goto valueEnd - } - if r != '"' { - panic("Expected key found something else") - } - in.pushActionBuffer(ActionAppendPath) - if in.pushReadBuffer(walk.NewAtomStringTerminal()) { - in.state = JSONInKey - return nil - } - goto key - } - arrayValue: { - r, err := in.nextNonWsRune() - if err != nil { - panic("Missing value inside array") - } - if r == ']' { - in.structure = in.structure[:len(in.structure) - 1] - in.pushActionBuffer(ActionPopPath) - in.pushActionBuffer(ActionReadValue) - if in.pushReadBuffer(walk.NewAtomTerminal(walk.ArrayEnd)) { - in.state = JSONInValueEnd - return nil - } - goto valueEnd - } - in.reader.UnreadRune() - in.pushActionBuffer(ActionIncrementPath) - goto value - } -} diff --git a/json_tokens/write.go b/json_tokens/write.go deleted file mode 100644 index 78ed186..0000000 --- a/json_tokens/write.go +++ /dev/null @@ -1,151 +0,0 @@ -package json_tokens - -import ( - "fmt" - "strings" - "bufio" - "main/walk" -) - -func stringPathSegment(segment walk.PathSegment) string { - return fmt.Sprintf("%v", segment) -} - -type JSONOutStructure int -const ( - JSONOutRoot JSONOutStructure = iota - JSONOutMap - JSONOutArray - JSONOutString - JSONOutValueEnd -) - -type JSONOut struct { - structure []JSONOutStructure - writer *bufio.Writer -} - -func (out *JSONOut) indent(adjust int) { - fmt.Fprint(out.writer, strings.Repeat("\t", len(out.structure) - 1 + adjust)) -} - -func (out *JSONOut) atomOut(key string, atom walk.AtomOLD) { - state := out.structure[len(out.structure) - 1] - switch state { - case JSONOutRoot, JSONOutMap, JSONOutArray: - switch atom.Typ { - case walk.AtomNull, walk.AtomBool, walk.AtomNumber: - out.indent(0) - if state == JSONOutMap { - fmt.Fprintf(out.writer, "%q: ", key) - } - fmt.Fprint(out.writer, atom.String()) - out.structure = append(out.structure, JSONOutValueEnd) - case walk.AtomStringTerminal: - out.indent(0) - if state == JSONOutMap { - fmt.Fprintf(out.writer, "%q: ", key) - } - fmt.Fprint(out.writer, "\"") - out.structure = append(out.structure, JSONOutString) - case walk.AtomTerminal: - switch atom.Terminal() { - case walk.MapBegin: - out.indent(0) - if state == JSONOutMap { - fmt.Fprintf(out.writer, "%q: ", key) - } - fmt.Fprint(out.writer, "{\n") - out.structure = append(out.structure, JSONOutMap) - case walk.ArrayBegin: - out.indent(0) - if state == JSONOutMap { - fmt.Fprintf(out.writer, "%q: ", key) - } - fmt.Fprint(out.writer, "[\n") - out.structure = append(out.structure, JSONOutArray) - case walk.MapEnd: - out.indent(-1) - if state != JSONOutMap { - panic("Map ended while not inside a map") - } - fmt.Fprint(out.writer, "}") - out.structure[len(out.structure) - 1] = JSONOutValueEnd - case walk.ArrayEnd: - out.indent(-1) - if state != JSONOutArray { - panic("Array ended while not inside a array") - } - fmt.Fprint(out.writer, "]") - out.structure[len(out.structure) - 1] = JSONOutValueEnd - default: - panic("Invalid TerminalValue") - } - default: - panic("Invalid AtomType in root value") - } - case JSONOutValueEnd: - out.structure = out.structure[:len(out.structure) - 1] - underState := out.structure[len(out.structure) - 1] - if underState == JSONOutMap && atom.Typ == walk.AtomTerminal && atom.Terminal() == walk.MapEnd { - fmt.Fprint(out.writer, "\n") - out.indent(-1) - fmt.Fprint(out.writer, "}") - out.structure[len(out.structure) - 1] = JSONOutValueEnd - } else if underState == JSONOutArray && atom.Typ == walk.AtomTerminal && atom.Terminal() == walk.ArrayEnd { - fmt.Fprint(out.writer, "\n") - out.indent(-1) - fmt.Fprint(out.writer, "]") - out.structure[len(out.structure) - 1] = JSONOutValueEnd - } else if underState == JSONOutRoot { - panic("Tried to output JSON after root value has concluded") - } else { - fmt.Fprint(out.writer, ",\n") - out.atomOut(key, atom) - } - case JSONOutString: - if atom.Typ == walk.AtomStringTerminal { - fmt.Fprint(out.writer, "\"") - out.structure[len(out.structure) - 1] = JSONOutValueEnd - } else { - fmt.Fprint(out.writer, atom.String()) - } - default: - panic("Invalid JSONOutState") - } -} - -func (out *JSONOut) Print(path walk.Path, values []walk.AtomOLD) { - var segment walk.PathSegment - if len(path) > 0 { - segment = path[len(path) - 1] - } - segmentString := stringPathSegment(segment) - for _, atom := range values { - out.atomOut(segmentString, atom) - } -} - -func (out *JSONOut) Write(item walk.WalkItem) error { - pathValues, err := walk.Compound(item.Path) - if err != nil { - return err - } - path := walk.PathFromWalkValues(pathValues) - out.Print(path, item.Value) - return nil -} - -func (out *JSONOut) AssertDone() { - out.writer.Flush() - if len(out.structure) != 2 || out.structure[0] != JSONOutRoot || out.structure[1] != JSONOutValueEnd { - panic("Program ended with incomplete JSON output") - } -} - -func NewJSONOut(writer *bufio.Writer) *JSONOut { - return &JSONOut { - structure: []JSONOutStructure{JSONOutRoot}, - writer: writer, - } -} |