<- Back to shtanton's homepage
aboutsummaryrefslogtreecommitdiff
path: root/json/write.go
diff options
context:
space:
mode:
Diffstat (limited to 'json/write.go')
-rw-r--r--json/write.go202
1 files changed, 202 insertions, 0 deletions
diff --git a/json/write.go b/json/write.go
new file mode 100644
index 0000000..d024a56
--- /dev/null
+++ b/json/write.go
@@ -0,0 +1,202 @@
+package json
+
+import (
+ "bufio"
+ "fmt"
+ "main/walk"
+)
+
+func isNumber(value walk.Value) bool {
+ _, isFloat := value.(walk.NumberScalar)
+ return isFloat
+}
+
+func isString(value walk.Value) bool {
+ _, isString := value.(walk.StringStructure)
+ return isString
+}
+
+func segmentEqual(left walk.Value, right walk.Value) bool {
+ switch left := left.(type) {
+ case walk.NumberScalar:
+ _, isNumber := right.(walk.NumberScalar)
+ return isNumber
+ case walk.StringStructure:
+ right, isString := right.(walk.StringStructure)
+ return isString && left == right
+ default:
+ panic("Invalid path segment type")
+ }
+}
+
+type JSONWriterState int
+const (
+ JSONWriterStateBeforeValue JSONWriterState = iota
+ JSONWriterStateAfterValue JSONWriterState = iota
+ JSONWriterStateInArray
+ JSONWriterStateInMap
+)
+
+func NewJSONWriter(writer *bufio.Writer) *JSONWriter {
+ return &JSONWriter {
+ path: nil,
+ writer: writer,
+ state: JSONWriterStateBeforeValue,
+ }
+}
+
+type JSONWriter struct {
+ path []walk.Value
+ 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
+ }
+ }
+ return nil
+}
+
+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 {
+ diversionPoint := len(writer.path)
+ 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 JSONWriterStateInMap:
+ goto inMap
+ case JSONWriterStateInArray:
+ goto inArray
+ default:
+ panic("Invalid JSONWriterState")
+ }
+
+ beforeValue: {
+ switch value := value.(type) {
+ case walk.NullScalar:
+ writer.writer.WriteString("null")
+ writer.state = JSONWriterStateAfterValue
+ return nil
+ case walk.BoolScalar:
+ if value {
+ writer.writer.WriteString("true")
+ } else {
+ writer.writer.WriteString("false")
+ }
+ writer.state = JSONWriterStateAfterValue
+ return nil
+ case walk.NumberScalar:
+ writer.writer.WriteString(fmt.Sprintf("%v", value))
+ writer.state = JSONWriterStateAfterValue
+ return nil
+ case walk.StringStructure:
+ writer.writer.WriteString(fmt.Sprintf("%q", value))
+ writer.state = JSONWriterStateAfterValue
+ return nil
+ case walk.ArrayStructure:
+ // TODO: write the contents of the structures
+ writer.writer.WriteString("[\n")
+ writer.state = JSONWriterStateInArray
+ return nil
+ case walk.MapStructure:
+ writer.writer.WriteString("{\n")
+ writer.state = JSONWriterStateInMap
+ return nil
+ default:
+ panic("Invalid value type")
+ }
+ }
+
+ afterValue: {
+ if len(writer.path) == 0 {
+ writer.writer.WriteRune('\n')
+ goto beforeValue
+ }
+ switch writer.path[len(writer.path) - 1].(type) {
+ case walk.NumberScalar:
+ // TODO: second part of this condition might be redundant
+ if len(writer.path) - 1 <= diversionPoint && len(targetPath) >= len(writer.path) && isNumber(targetPath[len(writer.path) - 1]) {
+ writer.writer.WriteString(",\n")
+ writer.path = writer.path[:len(writer.path) - 1]
+ goto inArray
+ } else {
+ writer.writer.WriteString("\n")
+ writer.indent(len(writer.path) - 1)
+ writer.writer.WriteString("]")
+ writer.path = writer.path[:len(writer.path) - 1]
+ goto afterValue
+ }
+ case walk.StringStructure:
+ if len(writer.path) -1 <= diversionPoint && len(targetPath) >= len(writer.path) && isString(targetPath[len(writer.path) - 1]) {
+ writer.writer.WriteString(",\n")
+ writer.path = writer.path[:len(writer.path) - 1]
+ goto inMap
+ } else {
+ writer.writer.WriteString("\n")
+ writer.indent(len(writer.path) - 1)
+ writer.writer.WriteString("}")
+ writer.path = writer.path[:len(writer.path) - 1]
+ goto afterValue
+ }
+ default:
+ panic("Invalid path segment type")
+ }
+ }
+
+ inMap: {
+ if len(writer.path) <= diversionPoint && len(targetPath) > len(writer.path) && isString(targetPath[len(writer.path)]) {
+ writer.indent(len(writer.path) + 1)
+ writer.writer.WriteString(fmt.Sprintf("%q: ", targetPath[len(writer.path)].(walk.StringStructure)))
+ writer.path = append(writer.path, targetPath[len(writer.path)].(walk.StringStructure))
+ goto beforeValue
+ } else {
+ writer.writer.WriteString("\n}")
+ goto afterValue
+ }
+ }
+
+ inArray: {
+ if len(writer.path) <= diversionPoint && len(targetPath) > len(writer.path) && isNumber(targetPath[len(writer.path)]) {
+ writer.indent(len(writer.path) + 1)
+ writer.path = append(writer.path, walk.NumberScalar(0))
+ goto beforeValue
+ } else {
+ writer.writer.WriteString("\n]")
+ goto afterValue
+ }
+ }
+}
+
+func (writer *JSONWriter) AssertDone() {
+ for i := len(writer.path) - 1; i >= 0; i -= 1 {
+ switch writer.path[i].(type) {
+ case walk.NumberScalar:
+ writer.writer.WriteString("\n")
+ writer.indent(i)
+ writer.writer.WriteString("]")
+ case walk.StringStructure:
+ writer.writer.WriteString("\n")
+ writer.indent(i)
+ writer.writer.WriteString("}")
+ default:
+ panic("Invalid path segment type")
+ }
+ }
+ writer.writer.Flush()
+}