From 3bd45dc49a35b82dcc4ae93796c3e152d799bc0b Mon Sep 17 00:00:00 2001 From: Charlie Stanton Date: Sun, 19 Feb 2023 09:27:55 +0000 Subject: Move JSON serialising, deserialising and walking code into a separate package --- main/command.go | 40 ++++--- main/filter.go | 42 +++---- main/json.go | 293 ------------------------------------------------- main/main.go | 37 ++----- main/parse.go | 33 +++--- main/pathfilter.go | 16 ++- walk/walk.go | 316 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 396 insertions(+), 381 deletions(-) create mode 100644 walk/walk.go diff --git a/main/command.go b/main/command.go index 91cb5e4..c61b0cd 100644 --- a/main/command.go +++ b/main/command.go @@ -1,5 +1,9 @@ package main +import ( + "main/walk" +) + type PrintValueCommand struct {} func (cmd PrintValueCommand) exec(state *ProgramState) { for _, item := range state.space { @@ -9,19 +13,19 @@ func (cmd PrintValueCommand) exec(state *ProgramState) { type ToggleTerminalCommand struct {} func (cmd ToggleTerminalCommand) exec(state *ProgramState) { - toggled := map[TerminalValue]TerminalValue { - ArrayBegin: MapBegin, - ArrayEnd: MapEnd, - MapBegin: ArrayBegin, - MapEnd: ArrayEnd, + toggled := map[walk.TerminalValue]walk.TerminalValue { + walk.ArrayBegin: walk.MapBegin, + walk.ArrayEnd: walk.MapEnd, + walk.MapBegin: walk.ArrayBegin, + walk.MapEnd: walk.ArrayEnd, } for i := range state.space { - terminal, isTerminal := state.space[i].value.(TerminalValue) + terminal, isTerminal := state.space[i].Value.(walk.TerminalValue) if !isTerminal { continue } - state.space[i].value = toggled[terminal] + state.space[i].Value = toggled[terminal] } } @@ -48,26 +52,26 @@ func (cmd SequenceCommand) exec(state *ProgramState) { } type AppendLiteralCommand struct { - values []WalkValue + values []walk.WalkValue } func (cmd AppendLiteralCommand) exec(state *ProgramState) { for _, value := range cmd.values { - state.space = append(state.space, WalkItem { - path: nil, - value: value, + state.space = append(state.space, walk.WalkItem { + Path: nil, + Value: value, }) } } type PrependLiteralCommand struct { - values []WalkValue + values []walk.WalkValue } func (cmd PrependLiteralCommand) exec(state *ProgramState) { - var newItems []WalkItem + var newItems []walk.WalkItem for _, value := range cmd.values { - newItems = append(newItems, WalkItem { - path: nil, - value: value, + newItems = append(newItems, walk.WalkItem { + Path: nil, + Value: value, }) } state.space = append(newItems, state.space...) @@ -76,7 +80,7 @@ func (cmd PrependLiteralCommand) exec(state *ProgramState) { type NextCommand struct {} func (cmd NextCommand) exec(state *ProgramState) { nextItem := <- state.in - state.space = []WalkItem{nextItem} + state.space = []walk.WalkItem{nextItem} } type AppendNextCommand struct {} @@ -86,7 +90,7 @@ func (cmd AppendNextCommand) exec(state *ProgramState) { } type PrintLiteralsCommand struct { - items []WalkItem + items []walk.WalkItem } func (cmd PrintLiteralsCommand) exec(state *ProgramState) { for _, item := range cmd.items { diff --git a/main/filter.go b/main/filter.go index f69d01a..d80ae8f 100644 --- a/main/filter.go +++ b/main/filter.go @@ -1,12 +1,16 @@ package main +import ( + "main/walk" +) + type PathFilter struct { initial PathFilterState } -func (filter PathFilter) exec(space WalkItem) bool { +func (filter PathFilter) exec(space walk.WalkItem) bool { pathFilterState := make(map[PathFilterState]struct{}) pathFilterState[filter.initial] = struct{}{} - for _, segment := range space.path { + for _, segment := range space.Path { nextPathFilterState := make(map[PathFilterState]struct{}) for curState := range pathFilterState { for nextState := range curState.eat(segment) { @@ -24,48 +28,48 @@ func (filter PathFilter) exec(space WalkItem) bool { } type MapTerminalFilter struct {} -func (filter MapTerminalFilter) exec(space WalkItem) bool { - terminal, isTerminal := space.value.(TerminalValue) +func (filter MapTerminalFilter) exec(space walk.WalkItem) bool { + terminal, isTerminal := space.Value.(walk.TerminalValue) if !isTerminal { return false } - return terminal == MapBegin || terminal == MapEnd + return terminal == walk.MapBegin || terminal == walk.MapEnd } type BeginTerminalFilter struct {} -func (filter BeginTerminalFilter) exec(space WalkItem) bool { - terminal, isTerminal := space.value.(TerminalValue) +func (filter BeginTerminalFilter) exec(space walk.WalkItem) bool { + terminal, isTerminal := space.Value.(walk.TerminalValue) if !isTerminal { return false } - return terminal == ArrayBegin || terminal == MapBegin + return terminal == walk.ArrayBegin || terminal == walk.MapBegin } type EndTerminalFilter struct {} -func (filter EndTerminalFilter) exec(space WalkItem) bool { - terminal, isTerminal := space.value.(TerminalValue) +func (filter EndTerminalFilter) exec(space walk.WalkItem) bool { + terminal, isTerminal := space.Value.(walk.TerminalValue) if !isTerminal { return false } - return terminal == ArrayEnd || terminal == MapEnd + return terminal == walk.ArrayEnd || terminal == walk.MapEnd } type TerminalFilter struct {} -func (filter TerminalFilter) exec(space WalkItem) bool { - _, isTerminal := space.value.(TerminalValue) +func (filter TerminalFilter) exec(space walk.WalkItem) bool { + _, isTerminal := space.Value.(walk.TerminalValue) return isTerminal } type RootFilter struct {} -func (filter RootFilter) exec(space WalkItem) bool { - return len(space.path) == 0 +func (filter RootFilter) exec(space walk.WalkItem) bool { + return len(space.Path) == 0 } type AndFilter struct { left Filter right Filter } -func (filter AndFilter) exec(space WalkItem) bool { +func (filter AndFilter) exec(space walk.WalkItem) bool { return filter.left.exec(space) && filter.right.exec(space) } @@ -73,17 +77,17 @@ type OrFilter struct { left Filter right Filter } -func (filter OrFilter) exec(space WalkItem) bool { +func (filter OrFilter) exec(space walk.WalkItem) bool { return filter.left.exec(space) || filter.right.exec(space) } type NotFilter struct { content Filter } -func (filter NotFilter) exec(space WalkItem) bool { +func (filter NotFilter) exec(space walk.WalkItem) bool { return !filter.content.exec(space) } type Filter interface { - exec(WalkItem) bool + exec(walk.WalkItem) bool } \ No newline at end of file diff --git a/main/json.go b/main/json.go index 77c3733..06ab7d0 100644 --- a/main/json.go +++ b/main/json.go @@ -1,294 +1 @@ package main - -import ( - "io" - "encoding/json" - "fmt" -) - -type WalkItemStream struct { - channel chan WalkItem - rewinds []WalkItem -} - -func (stream *WalkItemStream) next() (WalkItem, bool) { - if len(stream.rewinds) == 0 { - item, hasItem := <- stream.channel - return item, hasItem - } - item := stream.rewinds[len(stream.rewinds)-1] - stream.rewinds = stream.rewinds[0:len(stream.rewinds)-1] - return item, true -} - -func (stream *WalkItemStream) rewind(item WalkItem) { - stream.rewinds = append(stream.rewinds, item) -} - -func (stream *WalkItemStream) peek() (WalkItem, bool) { - item, hasItem := stream.next() - if !hasItem { - return item, false - } - stream.rewind(item) - return item, true -} - -func tokenToValue(token json.Token) WalkValue { - switch token.(type) { - case nil: - return ValueNull {} - case bool: - return ValueBool(token.(bool)) - case float64: - return ValueNumber(token.(float64)) - case string: - return ValueString(token.(string)) - default: - panic("Can't convert JSON token to value") - } -} - -func readValue(dec *json.Decoder, path Path, out chan WalkItem) bool { - if !dec.More() { - return true - } - t, err := dec.Token() - if err == io.EOF { - return true - } else if err != nil { - panic("Invalid JSON") - } - switch t.(type) { - case nil, string, float64, bool: - v := tokenToValue(t) - out <- WalkItem {v, path} - return false - case json.Delim: - switch rune(t.(json.Delim)) { - case '[': - out <- WalkItem {ArrayBegin, path} - index := 0 - for dec.More() { - empty := readValue(dec, append(path, index), out) - if empty { - break - } - index += 1 - } - t, err := dec.Token() - if err != nil { - panic("Invalid JSON") - } - delim, isDelim := t.(json.Delim) - if !isDelim || delim != ']' { - panic("Expected ] in JSON") - } - out <- WalkItem{ArrayEnd, path} - return false - case '{': - out <- WalkItem {MapBegin, path} - for dec.More() { - t, _ := dec.Token() - key, keyIsString := t.(string) - if !keyIsString { - panic("Invalid JSON") - } - empty := readValue(dec, append(path, key), out) - if empty { - panic("Invalid JSON") - } - } - t, err := dec.Token() - if err != nil { - panic("Invalid JSON") - } - delim, isDelim := t.(json.Delim) - if !isDelim || delim != '}' { - panic("Expected } in JSON") - } - out <- WalkItem {MapEnd, path} - return false - default: - panic("Error parsing JSON") - } - default: - panic("Invalid JSON token") - } -} - -func startWalk(dec *json.Decoder, out chan WalkItem) { - isEmpty := readValue(dec, nil, out) - if isEmpty { - panic("Missing JSON input") - } - close(out) -} - -func Json(r io.Reader) chan WalkItem { - dec := json.NewDecoder(r) - out := make(chan WalkItem) - go startWalk(dec, out) - return out -} - -func printIndent(indent int) { - for i := 0; i < indent; i += 1 { - fmt.Print("\t") - } -} - -func jsonOutArray(in *WalkItemStream, indent int) { - fmt.Println("[") - token, hasToken := in.next() - if !hasToken { - panic("Missing ] in output JSON") - } - terminal, isTerminal := token.value.(TerminalValue) - if isTerminal && terminal == ArrayEnd { - fmt.Print("\n") - printIndent(indent) - fmt.Print("]") - return - } - in.rewind(token) - for { - valueToken := jsonOutValue(in, indent + 1, true) - if valueToken != nil { - panic("Missing value in output JSON array") - } - token, hasToken := in.next() - if !hasToken { - panic("Missing ] in output JSON") - } - terminal, isTerminal := token.value.(TerminalValue) - if isTerminal && terminal == ArrayEnd { - fmt.Print("\n") - printIndent(indent) - fmt.Print("]") - return - } - in.rewind(token) - fmt.Println(",") - } -} - -func jsonOutMap(in *WalkItemStream, indent int) { - fmt.Println("{") - token, hasToken := in.next() - if !hasToken { - panic("Missing } in output JSON") - } - terminal, isTerminal := token.value.(TerminalValue) - if isTerminal && terminal == MapEnd { - fmt.Print("\n") - printIndent(indent) - fmt.Print("}") - return - } - in.rewind(token) - for { - keyToken, hasKeyToken := in.peek() - if !hasKeyToken { - panic("Missing map element") - } - printIndent(indent + 1) - if len(keyToken.path) == 0 { - panic("Map element missing key") - } - key := keyToken.path[len(keyToken.path)-1] - switch key.(type) { - case int: - fmt.Print(key.(int)) - case string: - fmt.Printf("%q", key.(string)) - default: - panic("Invalid path segment") - } - fmt.Print(": ") - valueToken := jsonOutValue(in, indent + 1, false) - if valueToken != nil { - panic("Missing value int output JSON map") - } - token, hasToken := in.next() - if !hasToken { - panic("Missing } in output JSON") - } - terminal, isTerminal := token.value.(TerminalValue) - if isTerminal && terminal == MapEnd { - fmt.Print("\n") - printIndent(indent) - fmt.Print("}") - return - } - in.rewind(token) - fmt.Println(",") - } -} - -func jsonOutValue(in *WalkItemStream, indent int, doIndent bool) WalkValue { - token, hasToken := in.next() - if !hasToken { - panic("Missing JSON token in output") - } - switch token.value.(type) { - case ValueNull: - if doIndent { - printIndent(indent) - } - fmt.Printf("null") - return nil - case ValueBool: - if doIndent { - printIndent(indent) - } - if token.value.(ValueBool) { - fmt.Print("true") - } else { - fmt.Print("false") - } - return nil - case ValueNumber: - if doIndent { - printIndent(indent) - } - fmt.Printf("%v", token.value) - return nil - case ValueString: - if doIndent { - printIndent(indent) - } - fmt.Printf("%q", token.value) - return nil - case TerminalValue: - switch token.value.(TerminalValue) { - case ArrayBegin: - if doIndent { - printIndent(indent) - } - jsonOutArray(in, indent) - return nil - case MapBegin: - if doIndent { - printIndent(indent) - } - jsonOutMap(in, indent) - return nil - default: - return token - } - default: - panic("Invalid WalkValue") - } -} - -func JsonOut(in chan WalkItem) { - stream := WalkItemStream { - channel: in, - rewinds: nil, - } - if jsonOutValue(&stream, 0, true) != nil { - panic("Invalid output JSON") - } - fmt.Print("\n") -} diff --git a/main/main.go b/main/main.go index 46b83e7..d657ea2 100644 --- a/main/main.go +++ b/main/main.go @@ -5,36 +5,15 @@ import ( "bufio" "fmt" "main/subex" + "main/walk" ) -type PathSegment interface {} -type Path []PathSegment - -type TerminalValue int -const ( - ArrayBegin TerminalValue = iota - ArrayEnd - MapBegin - MapEnd -) -type ValueNull struct {} -type ValueBool bool -type ValueNumber float64 -type ValueString string - -type WalkValue interface {} - -type WalkItem struct { - value WalkValue - path Path -} - type Program []Command type ProgramState struct { - space []WalkItem - in chan WalkItem - out chan WalkItem + space []walk.WalkItem + in chan walk.WalkItem + out chan walk.WalkItem program []Command } @@ -78,17 +57,17 @@ func mainISH() { program := Parse(tokens) stdin := bufio.NewReader(os.Stdin) - dataStream := Json(stdin) + dataStream := walk.Json(stdin) state := ProgramState { in: dataStream, - out: make(chan WalkItem), + out: make(chan walk.WalkItem), program: program, } go func () { for walkItem := range dataStream { - state.space = []WalkItem{walkItem} + state.space = []walk.WalkItem{walkItem} for _, cmd := range state.program { cmd.exec(&state) } @@ -101,5 +80,5 @@ func mainISH() { close(state.out) }() - JsonOut(state.out) + walk.JsonOut(state.out) } \ No newline at end of file diff --git a/main/parse.go b/main/parse.go index 5466a02..73c7913 100644 --- a/main/parse.go +++ b/main/parse.go @@ -4,6 +4,7 @@ import ( "strings" "strconv" "fmt" + "main/walk" ) type parser struct { @@ -130,9 +131,9 @@ func (p *parser) parseFilter(minPower int) Filter { return lhs } -func (p *parser) parseLiterals() (items []WalkItem) { - var path Path - var value WalkValue +func (p *parser) parseLiterals() (items []walk.WalkItem) { + var path walk.Path + var value walk.WalkValue loop: for { token := p.next() switch token.typ { @@ -141,17 +142,17 @@ func (p *parser) parseLiterals() (items []WalkItem) { break loop case TokenComma: case TokenNullLiteral: - value = ValueNull{} + value = walk.ValueNull{} case TokenTrueLiteral: - value = ValueBool(true) + value = walk.ValueBool(true) case TokenFalseLiteral: - value = ValueBool(false) + value = walk.ValueBool(false) case TokenNumberLiteral: numberLiteral, err := strconv.ParseFloat(token.val, 64) if err != nil { panic("Error parsing number literal to float64") } - value = ValueNumber(numberLiteral) + value = walk.ValueNumber(numberLiteral) case TokenDoubleQuote: stringToken := p.next() if stringToken.typ != TokenStringLiteral { @@ -167,29 +168,29 @@ func (p *parser) parseLiterals() (items []WalkItem) { if path != nil { panic("Expected value after path:") } - path = Path{stringLiteral} + path = walk.Path{stringLiteral} } else { p.rewind(colon) - value = ValueString(stringLiteral) + value = walk.ValueString(stringLiteral) } case TokenTerminalLiteral: switch token.val { case "{": - value = MapBegin + value = walk.MapBegin case "}": - value = MapEnd + value = walk.MapEnd case "[": - value = ArrayBegin + value = walk.ArrayBegin case "]": - value = ArrayEnd + value = walk.ArrayEnd default: panic("Invalid terminal token") } } if value != nil { - items = append(items, WalkItem { - path: path, - value: value, + items = append(items, walk.WalkItem { + Path: path, + Value: value, }) path = nil value = nil diff --git a/main/pathfilter.go b/main/pathfilter.go index 7e21efe..1af3b6d 100644 --- a/main/pathfilter.go +++ b/main/pathfilter.go @@ -1,9 +1,13 @@ package main +import ( + "main/walk" +) + type AnySegmentPathFilter struct { next PathFilterState } -func (filter AnySegmentPathFilter) eat(segment PathSegment) map[PathFilterState]struct{} { +func (filter AnySegmentPathFilter) eat(segment walk.PathSegment) map[PathFilterState]struct{} { res := make(map[PathFilterState]struct{}) res[filter.next] = struct{}{} return res @@ -15,7 +19,7 @@ func (filter AnySegmentPathFilter) accept() bool { type OrPathFilter struct { filters [2]PathFilterState } -func (filter OrPathFilter) eat(segment PathSegment) map[PathFilterState]struct{} { +func (filter OrPathFilter) eat(segment walk.PathSegment) map[PathFilterState]struct{} { res := make(map[PathFilterState]struct{}) for _, f := range filter.filters { for r := range f.eat(segment) { @@ -34,7 +38,7 @@ func (filter OrPathFilter) accept() bool { } type NonePathFilter struct {} -func (filter NonePathFilter) eat(segment PathSegment) map[PathFilterState]struct{} { +func (filter NonePathFilter) eat(segment walk.PathSegment) map[PathFilterState]struct{} { return make(map[PathFilterState]struct{}) } func (filter NonePathFilter) accept() bool { @@ -45,7 +49,7 @@ type StringSegmentPathFilter struct { index string next PathFilterState } -func (filter StringSegmentPathFilter) eat(segment PathSegment) map[PathFilterState]struct{} { +func (filter StringSegmentPathFilter) eat(segment walk.PathSegment) map[PathFilterState]struct{} { s, isString := segment.(string) res := make(map[PathFilterState]struct{}) if isString && s == filter.index { @@ -61,7 +65,7 @@ type IntegerSegmentPathFilter struct { index int next PathFilterState } -func (filter IntegerSegmentPathFilter) eat(segment PathSegment) map[PathFilterState]struct{} { +func (filter IntegerSegmentPathFilter) eat(segment walk.PathSegment) map[PathFilterState]struct{} { i, isInteger := segment.(int) res := make(map[PathFilterState]struct{}) if isInteger && i == filter.index { @@ -74,6 +78,6 @@ func (filter IntegerSegmentPathFilter) accept() bool { } type PathFilterState interface { - eat(PathSegment) map[PathFilterState]struct{} + eat(walk.PathSegment) map[PathFilterState]struct{} accept() bool } diff --git a/walk/walk.go b/walk/walk.go new file mode 100644 index 0000000..19180b4 --- /dev/null +++ b/walk/walk.go @@ -0,0 +1,316 @@ +package walk + +import ( + "io" + "encoding/json" + "fmt" +) + +type PathSegment interface {} +type Path []PathSegment + +type TerminalValue int +const ( + ArrayBegin TerminalValue = iota + ArrayEnd + MapBegin + MapEnd +) +type ValueNull struct {} +type ValueBool bool +type ValueNumber float64 +type ValueString string + +type WalkValue interface {} + +type WalkItem struct { + Value WalkValue + Path Path +} + +type WalkItemStream struct { + channel chan WalkItem + rewinds []WalkItem +} + +func (stream *WalkItemStream) next() (WalkItem, bool) { + if len(stream.rewinds) == 0 { + item, hasItem := <- stream.channel + return item, hasItem + } + item := stream.rewinds[len(stream.rewinds)-1] + stream.rewinds = stream.rewinds[0:len(stream.rewinds)-1] + return item, true +} + +func (stream *WalkItemStream) rewind(item WalkItem) { + stream.rewinds = append(stream.rewinds, item) +} + +func (stream *WalkItemStream) peek() (WalkItem, bool) { + item, hasItem := stream.next() + if !hasItem { + return item, false + } + stream.rewind(item) + return item, true +} + +func tokenToValue(token json.Token) WalkValue { + switch token.(type) { + case nil: + return ValueNull {} + case bool: + return ValueBool(token.(bool)) + case float64: + return ValueNumber(token.(float64)) + case string: + return ValueString(token.(string)) + default: + panic("Can't convert JSON token to value") + } +} + +func readValue(dec *json.Decoder, path Path, out chan WalkItem) bool { + if !dec.More() { + return true + } + t, err := dec.Token() + if err == io.EOF { + return true + } else if err != nil { + panic("Invalid JSON") + } + switch t.(type) { + case nil, string, float64, bool: + v := tokenToValue(t) + out <- WalkItem {v, path} + return false + case json.Delim: + switch rune(t.(json.Delim)) { + case '[': + out <- WalkItem {ArrayBegin, path} + index := 0 + for dec.More() { + empty := readValue(dec, append(path, index), out) + if empty { + break + } + index += 1 + } + t, err := dec.Token() + if err != nil { + panic("Invalid JSON") + } + delim, isDelim := t.(json.Delim) + if !isDelim || delim != ']' { + panic("Expected ] in JSON") + } + out <- WalkItem{ArrayEnd, path} + return false + case '{': + out <- WalkItem {MapBegin, path} + for dec.More() { + t, _ := dec.Token() + key, keyIsString := t.(string) + if !keyIsString { + panic("Invalid JSON") + } + empty := readValue(dec, append(path, key), out) + if empty { + panic("Invalid JSON") + } + } + t, err := dec.Token() + if err != nil { + panic("Invalid JSON") + } + delim, isDelim := t.(json.Delim) + if !isDelim || delim != '}' { + panic("Expected } in JSON") + } + out <- WalkItem {MapEnd, path} + return false + default: + panic("Error parsing JSON") + } + default: + panic("Invalid JSON token") + } +} + +func startWalk(dec *json.Decoder, out chan WalkItem) { + isEmpty := readValue(dec, nil, out) + if isEmpty { + panic("Missing JSON input") + } + close(out) +} + +func Json(r io.Reader) chan WalkItem { + dec := json.NewDecoder(r) + out := make(chan WalkItem) + go startWalk(dec, out) + return out +} + +func printIndent(indent int) { + for i := 0; i < indent; i += 1 { + fmt.Print("\t") + } +} + +func jsonOutArray(in *WalkItemStream, indent int) { + fmt.Println("[") + token, hasToken := in.next() + if !hasToken { + panic("Missing ] in output JSON") + } + terminal, isTerminal := token.Value.(TerminalValue) + if isTerminal && terminal == ArrayEnd { + fmt.Print("\n") + printIndent(indent) + fmt.Print("]") + return + } + in.rewind(token) + for { + valueToken := jsonOutValue(in, indent + 1, true) + if valueToken != nil { + panic("Missing value in output JSON array") + } + token, hasToken := in.next() + if !hasToken { + panic("Missing ] in output JSON") + } + terminal, isTerminal := token.Value.(TerminalValue) + if isTerminal && terminal == ArrayEnd { + fmt.Print("\n") + printIndent(indent) + fmt.Print("]") + return + } + in.rewind(token) + fmt.Println(",") + } +} + +func jsonOutMap(in *WalkItemStream, indent int) { + fmt.Println("{") + token, hasToken := in.next() + if !hasToken { + panic("Missing } in output JSON") + } + terminal, isTerminal := token.Value.(TerminalValue) + if isTerminal && terminal == MapEnd { + fmt.Print("\n") + printIndent(indent) + fmt.Print("}") + return + } + in.rewind(token) + for { + keyToken, hasKeyToken := in.peek() + if !hasKeyToken { + panic("Missing map element") + } + printIndent(indent + 1) + if len(keyToken.Path) == 0 { + panic("Map element missing key") + } + key := keyToken.Path[len(keyToken.Path)-1] + switch key.(type) { + case int: + fmt.Print(key.(int)) + case string: + fmt.Printf("%q", key.(string)) + default: + panic("Invalid path segment") + } + fmt.Print(": ") + valueToken := jsonOutValue(in, indent + 1, false) + if valueToken != nil { + panic("Missing value int output JSON map") + } + token, hasToken := in.next() + if !hasToken { + panic("Missing } in output JSON") + } + terminal, isTerminal := token.Value.(TerminalValue) + if isTerminal && terminal == MapEnd { + fmt.Print("\n") + printIndent(indent) + fmt.Print("}") + return + } + in.rewind(token) + fmt.Println(",") + } +} + +func jsonOutValue(in *WalkItemStream, indent int, doIndent bool) WalkValue { + token, hasToken := in.next() + if !hasToken { + panic("Missing JSON token in output") + } + switch token.Value.(type) { + case ValueNull: + if doIndent { + printIndent(indent) + } + fmt.Printf("null") + return nil + case ValueBool: + if doIndent { + printIndent(indent) + } + if token.Value.(ValueBool) { + fmt.Print("true") + } else { + fmt.Print("false") + } + return nil + case ValueNumber: + if doIndent { + printIndent(indent) + } + fmt.Printf("%v", token.Value) + return nil + case ValueString: + if doIndent { + printIndent(indent) + } + fmt.Printf("%q", token.Value) + return nil + case TerminalValue: + switch token.Value.(TerminalValue) { + case ArrayBegin: + if doIndent { + printIndent(indent) + } + jsonOutArray(in, indent) + return nil + case MapBegin: + if doIndent { + printIndent(indent) + } + jsonOutMap(in, indent) + return nil + default: + return token + } + default: + panic("Invalid WalkValue") + } +} + +func JsonOut(in chan WalkItem) { + stream := WalkItemStream { + channel: in, + rewinds: nil, + } + if jsonOutValue(&stream, 0, true) != nil { + panic("Invalid output JSON") + } + fmt.Print("\n") +} -- cgit v1.2.3