From 3c34366bdd5d817a184d6b1c901d03a16b6faa4b Mon Sep 17 00:00:00 2001 From: Charlie Stanton Date: Fri, 12 May 2023 14:25:32 +0100 Subject: Adds the json_array IO format --- json_array/read.go | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 json_array/read.go (limited to 'json_array/read.go') 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") + } +} -- cgit v1.2.3