<- Back to shtanton's homepage
aboutsummaryrefslogtreecommitdiff
path: root/json_array/read.go
diff options
context:
space:
mode:
authorCharlie Stanton <charlie@shtanton.xyz>2023-05-12 14:25:32 +0100
committerCharlie Stanton <charlie@shtanton.xyz>2023-05-12 14:25:32 +0100
commit3c34366bdd5d817a184d6b1c901d03a16b6faa4b (patch)
tree0a084781caf7f062b52d8c0a7481e87afb7d5caa /json_array/read.go
parent551613765c9e60e2221ac920d2756b949e68f373 (diff)
downloadstred-go-3c34366bdd5d817a184d6b1c901d03a16b6faa4b.tar
Adds the json_array IO format
Diffstat (limited to 'json_array/read.go')
-rw-r--r--json_array/read.go118
1 files changed, 118 insertions, 0 deletions
diff --git a/json_array/read.go b/json_array/read.go
new file mode 100644
index 0000000..6334197
--- /dev/null
+++ b/json_array/read.go
@@ -0,0 +1,118 @@
+package json_array
+
+import (
+ "main/walk"
+ "encoding/json"
+ "errors"
+ "bufio"
+)
+
+type state int
+const (
+ stateStart state = iota
+ stateValueStart
+ stateEnd
+ stateDead
+)
+
+func atomiseValue(value interface{}) []walk.Atom {
+ switch v := value.(type) {
+ case nil:
+ return []walk.Atom{walk.NewAtomNull()}
+ case bool:
+ return []walk.Atom{walk.NewAtomBool(v)}
+ case float64:
+ return []walk.Atom{walk.NewAtomNumber(v)}
+ case string:
+ atoms := []walk.Atom{walk.NewAtomStringTerminal()}
+ for _, r := range v {
+ atoms = append(atoms, walk.NewAtomStringRune(r))
+ }
+ atoms = append(atoms, walk.NewAtomStringTerminal())
+ return atoms
+ case []interface{}:
+ atoms := []walk.Atom{walk.NewAtomTerminal(walk.ArrayBegin)}
+ for _, element := range v {
+ atoms = append(atoms, atomiseValue(element)...)
+ }
+ atoms = append(atoms, walk.NewAtomTerminal(walk.ArrayEnd))
+ return atoms
+ case map[string]interface{}:
+ atoms := []walk.Atom{walk.NewAtomTerminal(walk.MapBegin)}
+ for key, element := range v {
+ atoms = append(atoms, atomiseValue(key)...)
+ atoms = append(atoms, atomiseValue(element)...)
+ }
+ atoms = append(atoms, walk.NewAtomTerminal(walk.MapEnd))
+ return atoms
+ default:
+ panic("Invalid JSON value type")
+ }
+}
+
+func NewJSONArrayReader(reader *bufio.Reader) *JSONArrayReader {
+ return &JSONArrayReader {
+ decoder: json.NewDecoder(reader),
+ state: stateStart,
+ index: 0,
+ }
+}
+
+type JSONArrayReader struct {
+ decoder *json.Decoder
+ state state
+ index int
+}
+
+func (in *JSONArrayReader) Read() (walk.WalkItem, error) {
+ restart:
+ switch in.state {
+ case stateStart:
+ arrayStart, err := in.decoder.Token()
+ if err != nil {
+ panic("Error reading start of JSON array")
+ }
+ delim, isDelim := arrayStart.(json.Delim)
+ if !isDelim || delim != '[' {
+ panic("JSON input is not an array!")
+ }
+ in.state = stateValueStart
+ goto restart
+ case stateValueStart:
+ if !in.decoder.More() {
+ in.state = stateEnd
+ goto restart
+ }
+ var m interface{}
+ err := in.decoder.Decode(&m)
+ if err != nil {
+ panic("Error decoding array value")
+ }
+ in.index += 1
+ return walk.WalkItem {
+ Path: []walk.Atom{walk.NewAtomNumber(float64(in.index - 1))},
+ Value: atomiseValue(m),
+ }, nil
+ case stateEnd:
+ arrayEnd, err := in.decoder.Token()
+ if err != nil {
+ panic("Error reading end of JSON array")
+ }
+ delim, isDelim := arrayEnd.(json.Delim)
+ if !isDelim || delim != ']' {
+ panic("JSON array wasn't ended")
+ }
+ in.state = stateDead
+ return walk.WalkItem{}, errors.New("eof")
+ case stateDead:
+ return walk.WalkItem{}, errors.New("eof")
+ default:
+ panic("Unreachable!!!")
+ }
+}
+
+func (in *JSONArrayReader) AssertDone() {
+ if in.state != stateDead || in.decoder.More() {
+ panic("More JSON after array value")
+ }
+}