Add router

This commit is contained in:
lash 2023-03-31 17:27:10 +01:00
parent aefebf278a
commit f6e1d2bacc
Signed by untrusted user who does not match committer: lash
GPG Key ID: 21D2E7BB88C2A746
4 changed files with 181 additions and 0 deletions

View File

@ -105,11 +105,14 @@ PREV <selector> <display>
GOTO <selector> <display> <symbol>
EXIT <selector> <display>
COND <selector> <display> <symbol> <[!]state>
VAL <symbol>
kept in session as a router object:
SELECTORHASH|SYMBOLHASH
Selectorhash 0x0 is VAL
---

80
go/router/router.go Normal file
View File

@ -0,0 +1,80 @@
package router
import (
"fmt"
)
type Router struct {
selectors []string
symbols map[string]string
}
func NewRouter() Router {
return Router{
symbols: make(map[string]string),
}
}
func(r *Router) Add(selector string, symbol string) error {
if r.symbols[selector] != "" {
return fmt.Errorf("selector %v already set to symbol %v", selector, symbol)
}
l := len(selector)
if (l > 255) {
return fmt.Errorf("selector too long (is %v, max 255)", l)
}
l = len(symbol)
if (l > 255) {
return fmt.Errorf("symbol too long (is %v, max 255)", l)
}
r.selectors = append(r.selectors, selector)
r.symbols[selector] = symbol
return nil
}
func(r *Router) Next() []byte {
if len(r.selectors) == 0 {
return []byte{}
}
k := r.selectors[0]
r.selectors = r.selectors[1:]
v := r.symbols[k]
if len(r.selectors) == 0 {
r.symbols = nil
} else {
delete(r.symbols, k)
}
lk := len(k)
lv := len(v)
b := []byte{uint8(lk)}
b = append(b, k...)
b = append(b, uint8(lv))
b = append(b, v...)
return b
}
func(r *Router) ToBytes() []byte {
b := []byte{}
for true {
v := r.Next()
if len(v) == 0 {
break
}
b = append(b, v...)
}
return b
}
func FromBytes(b []byte) Router {
rb := NewRouter()
for len(b) > 0 {
l := b[0]
k := b[1:1+l]
b = b[1+l:]
l = b[0]
v := b[1:1+l]
b = b[1+l:]
rb.Add(string(k), string(v))
}
return rb
}

89
go/router/router_test.go Normal file
View File

@ -0,0 +1,89 @@
package router
import (
"bytes"
"testing"
)
func TestRouter(t *testing.T) {
r := NewRouter()
err := r.Add("foo", "bar")
if err != nil {
t.Error(err)
}
err = r.Add("baz", "barbarbar")
if err != nil {
t.Error(err)
}
err = r.Add("foo", "xyzzy")
if err == nil {
t.Errorf("expected error for duplicate key foo")
}
}
func TestRouterOut(t *testing.T) {
rt := NewRouter()
err := rt.Add("foo", "inky")
if err != nil {
t.Error(err)
}
err = rt.Add("barbar", "pinky")
if err != nil {
t.Error(err)
}
err = rt.Add("bazbazbaz", "blinky")
if err != nil {
t.Error(err)
}
rb := []byte{}
r := rt.Next()
expect := append([]byte{0x3}, []byte("foo")...)
expect = append(expect, 4)
expect = append(expect, []byte("inky")...)
if !bytes.Equal(r, expect) {
t.Errorf("expected %v, got %v", expect, r)
}
rb = append(rb, r...)
r = rt.Next()
expect = append([]byte{0x6}, []byte("barbar")...)
expect = append(expect, 5)
expect = append(expect, []byte("pinky")...)
if !bytes.Equal(r, expect) {
t.Errorf("expected %v, got %v", expect, r)
}
rb = append(rb, r...)
r = rt.Next()
expect = append([]byte{0x9}, []byte("bazbazbaz")...)
expect = append(expect, 6)
expect = append(expect, []byte("blinky")...)
if !bytes.Equal(r, expect) {
t.Errorf("expected %v, got %v", expect, r)
}
rb = append(rb, r...)
}
func TestSerialize(t *testing.T) {
rt := NewRouter()
err := rt.Add("foo", "inky")
if err != nil {
t.Error(err)
}
err = rt.Add("barbar", "pinky")
if err != nil {
t.Error(err)
}
err = rt.Add("bazbazbaz", "blinky")
if err != nil {
t.Error(err)
}
// Serialize and deserialize.
ra := rt.ToBytes()
rt = FromBytes(ra)
rb := rt.ToBytes()
if !bytes.Equal(ra, rb) {
t.Errorf("expected %v, got %v", ra, rb)
}
}

View File

@ -12,6 +12,15 @@ import (
type Runner func(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, []byte, error)
func Apply(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, []byte, error) {
var err error
st, instruction, err = Run(instruction, st, rs, ctx)
if err != nil {
return st, instruction, err
}
return st, instruction, nil
}
func Run(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, []byte, error) {
var err error
for len(instruction) > 0 {