From 81dcb87b2158f625ca10a20df5a93a42bbcaf26b Mon Sep 17 00:00:00 2001 From: Charlie Stanton Date: Sun, 24 Mar 2024 19:18:58 +0000 Subject: Implements helper function navigateTo in json/write.go --- json/write.go | 303 +++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 259 insertions(+), 44 deletions(-) (limited to 'json/write.go') diff --git a/json/write.go b/json/write.go index 9e349be..97b3f4e 100644 --- a/json/write.go +++ b/json/write.go @@ -4,25 +4,26 @@ import ( "bufio" "fmt" "main/walk" + // "text/scanner" ) -func isNumber(value walk.Value) bool { - _, isFloat := value.(walk.NumberScalar) - return isFloat +func isInt(segment walk.PathSegment) bool { + _, isInt := segment.(int) + return isInt } -func isString(value walk.Value) bool { - _, isString := value.(walk.StringStructure) +func isString(segment walk.PathSegment) bool { + _, isString := segment.(string) return isString } -func segmentEqual(left walk.Value, right walk.Value) bool { +func segmentEqual(left walk.PathSegment, right walk.PathSegment) bool { switch left := left.(type) { - case walk.NumberScalar: - _, isNumber := right.(walk.NumberScalar) - return isNumber - case walk.StringStructure: - right, isString := right.(walk.StringStructure) + case int: + _, isInt := right.(int) + return isInt + case string: + right, isString := right.(string) return isString && left == right default: panic("Invalid path segment type") @@ -32,7 +33,7 @@ func segmentEqual(left walk.Value, right walk.Value) bool { type JSONWriterState int const ( JSONWriterStateBeforeValue JSONWriterState = iota - JSONWriterStateAfterValue JSONWriterState = iota + JSONWriterStateAfterValue JSONWriterStateInArray JSONWriterStateInMap ) @@ -46,29 +47,243 @@ func NewJSONWriter(writer *bufio.Writer) *JSONWriter { } type JSONWriter struct { - path []walk.Value + path []walk.PathSegment writer *bufio.Writer state JSONWriterState } -func (writer *JSONWriter) Write(item walk.WalkItem) error { - path := item.Path - for _, value := range item.Value { - err := writer.write(path, value) - if err != nil { - return err +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.Value, value walk.Value) error { +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 @@ -92,10 +307,10 @@ func (writer *JSONWriter) write(targetPath []walk.Value, value walk.Value) error if diversionPoint < len(targetPath) { segment := targetPath[diversionPoint] switch segment.(type) { - case walk.NumberScalar: + case int: writer.writer.WriteString("[\n") goto inArray - case walk.StringStructure: + case string: writer.writer.WriteString("{\n") goto inMap default: @@ -104,11 +319,11 @@ func (writer *JSONWriter) write(targetPath []walk.Value, value walk.Value) error } switch value := value.(type) { - case walk.NullScalar: + case walk.NullValue: writer.writer.WriteString("null") writer.state = JSONWriterStateAfterValue return nil - case walk.BoolScalar: + case walk.BoolValue: if value { writer.writer.WriteString("true") } else { @@ -116,20 +331,20 @@ func (writer *JSONWriter) write(targetPath []walk.Value, value walk.Value) error } writer.state = JSONWriterStateAfterValue return nil - case walk.NumberScalar: + case walk.NumberValue: writer.writer.WriteString(fmt.Sprintf("%v", value)) writer.state = JSONWriterStateAfterValue return nil - case walk.StringStructure: + case walk.StringValue: writer.writer.WriteString(fmt.Sprintf("%q", value)) writer.state = JSONWriterStateAfterValue return nil - case walk.ArrayStructure: + case walk.ArrayValue: // TODO: write the contents of the structures writer.writer.WriteString("[\n") writer.state = JSONWriterStateInArray return nil - case walk.MapStructure: + case walk.MapValue: writer.writer.WriteString("{\n") writer.state = JSONWriterStateInMap return nil @@ -143,15 +358,15 @@ func (writer *JSONWriter) write(targetPath []walk.Value, value walk.Value) error if diversionPoint == len(writer.path) - 1 && diversionPoint < len(targetPath) { segment := writer.path[diversionPoint] switch segment.(type) { - case walk.NumberScalar: - _, isNumber := targetPath[diversionPoint].(walk.NumberScalar) + case int: + _, isNumber := targetPath[diversionPoint].(walk.NumberValue) if isNumber { writer.writer.WriteString(",\n") writer.path = writer.path[:diversionPoint] goto inArray } - case walk.StringStructure: - _, isString := targetPath[diversionPoint].(walk.StringStructure) + case string: + _, isString := targetPath[diversionPoint].(walk.StringValue) if isString { writer.writer.WriteString(",\n") writer.path = writer.path[:diversionPoint] @@ -164,10 +379,10 @@ func (writer *JSONWriter) write(targetPath []walk.Value, value walk.Value) error writer.writer.WriteString("\n") switch writer.path[len(writer.path) - 1].(type) { - case walk.NumberScalar: + case int: writer.path = writer.path[:len(writer.path) - 1] goto inArray - case walk.StringStructure: + case string: writer.path = writer.path[:len(writer.path) - 1] goto inMap default: @@ -186,9 +401,9 @@ func (writer *JSONWriter) write(targetPath []walk.Value, value walk.Value) error diversionPoint-- writer.path = writer.path[:diversionPoint] switch segment.(type) { - case walk.NumberScalar: + case int: goto inArray - case walk.StringStructure: + case string: goto inMap default: panic("Invalid segment type") @@ -204,9 +419,9 @@ func (writer *JSONWriter) write(targetPath []walk.Value, value walk.Value) error diversionPoint-- writer.path = writer.path[:diversionPoint] switch segment.(type) { - case walk.NumberScalar: + case int: goto inArray - case walk.StringStructure: + case string: goto inMap default: panic("Invalid segment type") @@ -222,12 +437,12 @@ func (writer *JSONWriter) write(targetPath []walk.Value, value walk.Value) error if diversionPoint < len(targetPath) { switch s := targetPath[diversionPoint].(type) { - case walk.NumberScalar: + case int: writer.path = append(writer.path, s) diversionPoint++ writer.indent(len(writer.path)) goto beforeValue - case walk.StringStructure: + case string: writer.indent(len(writer.path)) writer.writer.WriteString("]") goto afterValue @@ -250,11 +465,11 @@ func (writer *JSONWriter) write(targetPath []walk.Value, value walk.Value) error if diversionPoint < len(targetPath) { switch s := targetPath[diversionPoint].(type) { - case walk.NumberScalar: + case int: writer.indent(len(writer.path)) writer.writer.WriteString("}") goto afterValue - case walk.StringStructure: + case string: writer.path = append(writer.path, s) diversionPoint++ writer.indent(len(writer.path)) @@ -278,11 +493,11 @@ func (writer *JSONWriter) AssertDone() { } for i := len(writer.path) - 1; i >= 0; i -= 1 { switch writer.path[i].(type) { - case walk.NumberScalar: + case int: writer.writer.WriteString("\n") writer.indent(i) writer.writer.WriteString("]") - case walk.StringStructure: + case string: writer.writer.WriteString("\n") writer.indent(i) writer.writer.WriteString("}") -- cgit v1.2.3