From 551613765c9e60e2221ac920d2756b949e68f373 Mon Sep 17 00:00:00 2001 From: Charlie Stanton Date: Fri, 12 May 2023 11:37:44 +0100 Subject: Move reading and writing of tokens into a separate package to prepare for other input and output formats --- json_tokens/write.go | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 json_tokens/write.go (limited to 'json_tokens/write.go') 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, + } +} -- cgit v1.2.3