<- Back to shtanton's homepage
aboutsummaryrefslogtreecommitdiff
path: root/json_tokens/write.go
diff options
context:
space:
mode:
authorCharlie Stanton <charlie@shtanton.xyz>2023-05-12 11:37:44 +0100
committerCharlie Stanton <charlie@shtanton.xyz>2023-05-12 11:37:44 +0100
commit551613765c9e60e2221ac920d2756b949e68f373 (patch)
treeac579a9e0d6c015edca694880f259c8dac4d7a04 /json_tokens/write.go
parente98ebbad387def55d8347adb5bf45034d542cce0 (diff)
downloadstred-go-551613765c9e60e2221ac920d2756b949e68f373.tar
Move reading and writing of tokens into a separate package to prepare for other input and output formats
Diffstat (limited to 'json_tokens/write.go')
-rw-r--r--json_tokens/write.go151
1 files changed, 151 insertions, 0 deletions
diff --git a/json_tokens/write.go b/json_tokens/write.go
new file mode 100644
index 0000000..813f2f3
--- /dev/null
+++ b/json_tokens/write.go
@@ -0,0 +1,151 @@
+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.Atom) {
+ 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.Atom) {
+ 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,
+ }
+}