package json import ( "bufio" "fmt" "main/walk" // "text/scanner" ) func isInt(segment walk.PathSegment) bool { _, isInt := segment.(int) return isInt } func isString(segment walk.PathSegment) bool { _, isString := segment.(string) return isString } func segmentEqual(left walk.PathSegment, right walk.PathSegment) bool { switch left := left.(type) { case int: _, isInt := right.(int) return isInt case string: right, isString := right.(string) return isString && left == right default: panic("Invalid path segment type") } } type JSONWriterState int const ( JSONWriterStateBeforeValue JSONWriterState = iota JSONWriterStateAfterValue JSONWriterStateInArray JSONWriterStateInMap ) func NewJSONWriter(writer *bufio.Writer) *JSONWriter { return &JSONWriter { path: nil, writer: writer, state: JSONWriterStateBeforeValue, } } type JSONWriter struct { path []walk.PathSegment writer *bufio.Writer state JSONWriterState } func (writer *JSONWriter) navigateTo(keepLen int, path []walk.PathSegment, state JSONWriterState) { for { if keepLen > len(writer.path) { panic("keepLen > len(writer.path)") } else if len(writer.path) == keepLen { if len(path) == 0 { switch writer.state { case JSONWriterStateBeforeValue: switch state { case JSONWriterStateBeforeValue: return case JSONWriterStateAfterValue: panic("Cannot go from BeforeValue to AfterValue in navigateTo") case JSONWriterStateInArray: writer.writer.WriteRune('[') writer.state = JSONWriterStateInArray case JSONWriterStateInMap: writer.writer.WriteRune('{') writer.state = JSONWriterStateInMap } case JSONWriterStateAfterValue: if state == JSONWriterStateAfterValue { return } else { if keepLen == 0 { writer.writer.WriteRune('\n') writer.state = JSONWriterStateBeforeValue } else { writer.writer.WriteRune(',') path = writer.path[len(writer.path) - 1:] writer.path = writer.path[:len(writer.path) - 1] keepLen -= 1 switch path[0].(type) { case string: writer.state = JSONWriterStateInMap case int: writer.state = JSONWriterStateInArray } } } case JSONWriterStateInArray: if state == JSONWriterStateInArray { return } else { writer.writer.WriteRune(']') writer.state = JSONWriterStateAfterValue } case JSONWriterStateInMap: if state == JSONWriterStateInMap { return } else { writer.writer.WriteRune('}') writer.state = JSONWriterStateAfterValue } } } else { // len(path) > 0 switch writer.state { case JSONWriterStateBeforeValue: switch path[0].(type) { case string: writer.writer.WriteRune('{') writer.state = JSONWriterStateInMap case int: writer.writer.WriteRune('[') writer.state = JSONWriterStateInArray } case JSONWriterStateAfterValue: if keepLen == 0 { writer.writer.WriteRune('\n') writer.state = JSONWriterStateBeforeValue } else { writer.writer.WriteRune(',') path = append(writer.path[len(writer.path) - 1:], path...) writer.path = writer.path[:len(writer.path) - 1] keepLen -= 1 switch path[0].(type) { case string: writer.state = JSONWriterStateInMap case int: writer.state = JSONWriterStateInArray } } case JSONWriterStateInArray: switch path[0].(type) { case string: writer.writer.WriteRune(']') writer.state = JSONWriterStateAfterValue case int: writer.path = append(writer.path, path[0]) path = path[1:] keepLen += 1 writer.state = JSONWriterStateBeforeValue } case JSONWriterStateInMap: switch p := path[0].(type) { case string: fmt.Fprintf(writer.writer, "%q:", p) writer.path = append(writer.path, p) path = path[1:] keepLen += 1 writer.state = JSONWriterStateBeforeValue case int: writer.writer.WriteRune('}') writer.state = JSONWriterStateAfterValue } } } } else { switch writer.state { case JSONWriterStateBeforeValue: panic("Cannot close structures from BeforeValue in navigateTo") case JSONWriterStateAfterValue: if len(writer.path) == keepLen + 1 { if len(path) == 0 { switch writer.path[len(writer.path) - 1].(type) { case string: if state == JSONWriterStateInMap { writer.writer.WriteRune(',') writer.path = writer.path[:len(writer.path) - 1] writer.state = JSONWriterStateInMap } else { writer.writer.WriteRune('}') writer.path = writer.path[:len(writer.path) - 1] } case int: if state == JSONWriterStateInArray { writer.writer.WriteRune(',') writer.path = writer.path[:len(writer.path) - 1] writer.state = JSONWriterStateInArray } else { writer.writer.WriteRune(']') writer.path = writer.path[:len(writer.path) - 1] } } } else { switch writer.path[len(writer.path) - 1].(type) { case string: switch path[0].(type) { case string: writer.writer.WriteRune(',') writer.path = writer.path[:len(writer.path) - 1] writer.state = JSONWriterStateInMap case int: writer.writer.WriteRune('}') writer.path = writer.path[:len(writer.path) - 1] } case int: switch path[0].(type) { case string: writer.writer.WriteRune(']') writer.path = writer.path[:len(writer.path) - 1] case int: writer.writer.WriteRune(',') writer.path = writer.path[:len(writer.path) - 1] writer.state = JSONWriterStateInArray } } } } else { switch writer.path[len(writer.path) - 1].(type) { case string: writer.writer.WriteRune('}') writer.path = writer.path[:len(writer.path) - 1] case int: writer.writer.WriteRune(']') writer.path = writer.path[:len(writer.path) - 1] } } case JSONWriterStateInArray: writer.writer.WriteRune(']') writer.state = JSONWriterStateAfterValue case JSONWriterStateInMap: writer.writer.WriteRune('}') writer.state = JSONWriterStateAfterValue } } } } func (writer *JSONWriter) Write(value walk.Value) error { return nil } func (writer *JSONWriter) pathWrite(value walk.Value, path []walk.PathSegment) error { switch value := value.(type) { case walk.NullValue: return writer.write(path, value) case walk.BoolValue: return writer.write(path, value) case walk.NumberValue: return writer.write(path, value) case walk.StringValue: return writer.write(path, value) case walk.ArrayValue: if len(value) == 0 { return writer.write(path, value) } else { for _, element := range value { err := writer.pathWrite(element.Value, append(path, element.Index)) if err != nil { return err } } return nil } case walk.MapValue: if len(value) == 0 { return writer.write(path, value) } else { for _, element := range value { err := writer.pathWrite(element.Value, append(path, element.Key)) if err != nil { return err } } return nil } case walk.RuneValue: panic("Cannot write rune value") default: panic("Unrecognised value type") } } func (writer *JSONWriter) indent(level int) { for i := 0; i < level; i += 1 { writer.writer.WriteRune('\t') } } func (writer *JSONWriter) write(targetPath []walk.PathSegment, value walk.Value) error { diversionPoint := 0 for diversionPoint < len(writer.path) && diversionPoint < len(targetPath) && segmentEqual(writer.path[diversionPoint], targetPath[diversionPoint]) { diversionPoint += 1 } switch writer.state { case JSONWriterStateBeforeValue: goto beforeValue case JSONWriterStateAfterValue: goto afterValue case JSONWriterStateInArray: goto inArray case JSONWriterStateInMap: goto inMap } beforeValue: { if diversionPoint < len(writer.path) { panic("Writing a value before doing a necessary leave") } if diversionPoint < len(targetPath) { segment := targetPath[diversionPoint] switch segment.(type) { case int: writer.writer.WriteString("[\n") goto inArray case string: writer.writer.WriteString("{\n") goto inMap default: panic("Invalid path segment") } } switch value := value.(type) { case walk.NullValue: writer.writer.WriteString("null") writer.state = JSONWriterStateAfterValue return nil case walk.BoolValue: if value { writer.writer.WriteString("true") } else { writer.writer.WriteString("false") } writer.state = JSONWriterStateAfterValue return nil case walk.NumberValue: writer.writer.WriteString(fmt.Sprintf("%v", value)) writer.state = JSONWriterStateAfterValue return nil case walk.StringValue: writer.writer.WriteString(fmt.Sprintf("%q", value)) writer.state = JSONWriterStateAfterValue return nil case walk.ArrayValue: // TODO: write the contents of the structures writer.writer.WriteString("[\n") writer.state = JSONWriterStateInArray return nil case walk.MapValue: writer.writer.WriteString("{\n") writer.state = JSONWriterStateInMap return nil default: panic("Invalid value type") } } afterValue: { if diversionPoint < len(writer.path) { if diversionPoint == len(writer.path) - 1 && diversionPoint < len(targetPath) { segment := writer.path[diversionPoint] switch segment.(type) { case int: _, isNumber := targetPath[diversionPoint].(walk.NumberValue) if isNumber { writer.writer.WriteString(",\n") writer.path = writer.path[:diversionPoint] goto inArray } case string: _, isString := targetPath[diversionPoint].(walk.StringValue) if isString { writer.writer.WriteString(",\n") writer.path = writer.path[:diversionPoint] goto inMap } default: panic("Invalid segment type") } } writer.writer.WriteString("\n") switch writer.path[len(writer.path) - 1].(type) { case int: writer.path = writer.path[:len(writer.path) - 1] goto inArray case string: writer.path = writer.path[:len(writer.path) - 1] goto inMap default: panic("Invalid segment type") } } // TODO: handle len(writer.path) == 0 if diversionPoint < len(targetPath) { if len(writer.path) == 0 { writer.writer.WriteString("\n") goto beforeValue } segment := writer.path[diversionPoint - 1] writer.writer.WriteString(",") diversionPoint-- writer.path = writer.path[:diversionPoint] switch segment.(type) { case int: goto inArray case string: goto inMap default: panic("Invalid segment type") } } if len(writer.path) == 0 { writer.writer.WriteString("\n") goto beforeValue } segment := writer.path[diversionPoint - 1] writer.writer.WriteString(",\n") diversionPoint-- writer.path = writer.path[:diversionPoint] switch segment.(type) { case int: goto inArray case string: goto inMap default: panic("Invalid segment type") } } inArray: { if diversionPoint < len(writer.path) { writer.indent(len(writer.path)) writer.writer.WriteString("]") goto afterValue } if diversionPoint < len(targetPath) { switch s := targetPath[diversionPoint].(type) { case int: writer.path = append(writer.path, s) diversionPoint++ writer.indent(len(writer.path)) goto beforeValue case string: writer.indent(len(writer.path)) writer.writer.WriteString("]") goto afterValue default: panic("Invalid segment type") } } writer.indent(len(writer.path)) writer.writer.WriteString("]") goto afterValue } inMap: { if diversionPoint < len(writer.path) { writer.indent(len(writer.path)) writer.writer.WriteString("}") goto afterValue } if diversionPoint < len(targetPath) { switch s := targetPath[diversionPoint].(type) { case int: writer.indent(len(writer.path)) writer.writer.WriteString("}") goto afterValue case string: writer.path = append(writer.path, s) diversionPoint++ writer.indent(len(writer.path)) writer.writer.WriteString(fmt.Sprintf("%q: ", s)) goto beforeValue } } writer.indent(len(writer.path)) writer.writer.WriteString("}") goto afterValue } } func (writer *JSONWriter) AssertDone() { switch writer.state { case JSONWriterStateInArray: writer.writer.WriteString("]") case JSONWriterStateInMap: writer.writer.WriteString("}") } for i := len(writer.path) - 1; i >= 0; i -= 1 { switch writer.path[i].(type) { case int: writer.writer.WriteString("\n") writer.indent(i) writer.writer.WriteString("]") case string: writer.writer.WriteString("\n") writer.indent(i) writer.writer.WriteString("}") default: panic("Invalid path segment type") } } writer.writer.Flush() }