diff options
| author | Charlie Stanton <charlie@shtanton.xyz> | 2023-02-19 09:27:55 +0000 | 
|---|---|---|
| committer | Charlie Stanton <charlie@shtanton.xyz> | 2023-02-19 09:27:55 +0000 | 
| commit | 3bd45dc49a35b82dcc4ae93796c3e152d799bc0b (patch) | |
| tree | 3a681ac5dbd777d2b6b116429cfbd934815661ce /main | |
| parent | a5a4db8283fda88c5bd42272de0258e5d134c5bd (diff) | |
| download | stred-go-3bd45dc49a35b82dcc4ae93796c3e152d799bc0b.tar | |
Move JSON serialising, deserialising and walking code into a separate package
Diffstat (limited to 'main')
| -rw-r--r-- | main/command.go | 40 | ||||
| -rw-r--r-- | main/filter.go | 42 | ||||
| -rw-r--r-- | main/json.go | 293 | ||||
| -rw-r--r-- | main/main.go | 37 | ||||
| -rw-r--r-- | main/parse.go | 33 | ||||
| -rw-r--r-- | main/pathfilter.go | 16 | 
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  } | 
