<- Back to shtanton's homepage
aboutsummaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorCharlie Stanton <charlie@shtanton.xyz>2023-02-19 09:27:55 +0000
committerCharlie Stanton <charlie@shtanton.xyz>2023-02-19 09:27:55 +0000
commit3bd45dc49a35b82dcc4ae93796c3e152d799bc0b (patch)
tree3a681ac5dbd777d2b6b116429cfbd934815661ce /main
parenta5a4db8283fda88c5bd42272de0258e5d134c5bd (diff)
downloadstred-go-3bd45dc49a35b82dcc4ae93796c3e152d799bc0b.tar
Move JSON serialising, deserialising and walking code into a separate package
Diffstat (limited to 'main')
-rw-r--r--main/command.go40
-rw-r--r--main/filter.go42
-rw-r--r--main/json.go293
-rw-r--r--main/main.go37
-rw-r--r--main/parse.go33
-rw-r--r--main/pathfilter.go16
6 files changed, 80 insertions, 381 deletions
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
}