diff options
author | Charlie Stanton <charlie@shtanton.xyz> | 2023-05-12 14:25:32 +0100 |
---|---|---|
committer | Charlie Stanton <charlie@shtanton.xyz> | 2023-05-12 14:25:32 +0100 |
commit | 3c34366bdd5d817a184d6b1c901d03a16b6faa4b (patch) | |
tree | 0a084781caf7f062b52d8c0a7481e87afb7d5caa /json_array/read.go | |
parent | 551613765c9e60e2221ac920d2756b949e68f373 (diff) | |
download | stred-go-3c34366bdd5d817a184d6b1c901d03a16b6faa4b.tar |
Adds the json_array IO format
Diffstat (limited to 'json_array/read.go')
-rw-r--r-- | json_array/read.go | 118 |
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") + } +} |