From f6e1d2bacc24b239e78a1a4b666fb2f7940c12ee Mon Sep 17 00:00:00 2001 From: lash Date: Fri, 31 Mar 2023 17:27:10 +0100 Subject: [PATCH] Add router --- draft.txt | 3 ++ go/router/router.go | 80 ++++++++++++++++++++++++++++++++++++ go/router/router_test.go | 89 ++++++++++++++++++++++++++++++++++++++++ go/vm/vm.go | 9 ++++ 4 files changed, 181 insertions(+) create mode 100644 go/router/router.go create mode 100644 go/router/router_test.go diff --git a/draft.txt b/draft.txt index dafddf0..9669e0b 100644 --- a/draft.txt +++ b/draft.txt @@ -105,11 +105,14 @@ PREV GOTO EXIT COND <[!]state> +VAL kept in session as a router object: SELECTORHASH|SYMBOLHASH +Selectorhash 0x0 is VAL + --- diff --git a/go/router/router.go b/go/router/router.go new file mode 100644 index 0000000..9db5915 --- /dev/null +++ b/go/router/router.go @@ -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 +} diff --git a/go/router/router_test.go b/go/router/router_test.go new file mode 100644 index 0000000..6cba085 --- /dev/null +++ b/go/router/router_test.go @@ -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) + } +} diff --git a/go/vm/vm.go b/go/vm/vm.go index 1f1b2af..b1d955f 100644 --- a/go/vm/vm.go +++ b/go/vm/vm.go @@ -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 {