<- Back to shtanton's homepage
aboutsummaryrefslogtreecommitdiff
path: root/json_array/write.go
diff options
context:
space:
mode:
Diffstat (limited to 'json_array/write.go')
-rw-r--r--json_array/write.go156
1 files changed, 156 insertions, 0 deletions
diff --git a/json_array/write.go b/json_array/write.go
new file mode 100644
index 0000000..4d202c4
--- /dev/null
+++ b/json_array/write.go
@@ -0,0 +1,156 @@
+package json_array
+
+import (
+ "bufio"
+ "strings"
+ "main/walk"
+ "encoding/json"
+)
+
+func assembleValue(atoms []walk.Atom) (interface{}, []walk.Atom) {
+ 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(atoms []walk.Atom, writer *bufio.Writer) {
+ if len(atoms) == 0 {
+ return
+ }
+ value, atoms := assembleValue(atoms)
+ if len(atoms) != 0 {
+ panic("Tried to output more than one JSON value")
+ }
+ 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()
+}