go-vise

Constrained Size Output Virtual Machine
Info | Log | Files | Refs | README | LICENSE

commit 767321fa2c064b1c6e6ca1df3ea107cefda6a250
parent c0d38513d3619675cd7f614c41bbbd58f0cf368d
Author: lash <dev@holbrook.no>
Date:   Fri, 31 Mar 2023 10:59:55 +0100

WIP apply map command

Diffstat:
Mgo/resource/resource.go | 10++++++++--
Mgo/state/state.go | 19++++++++++++++++---
Mgo/state/state_test.go | 6++++++
Mgo/vm/opcodes.go | 2++
Mgo/vm/vm.go | 35++++++++++++++++++++++++++++++++++-
Mgo/vm/vm_test.go | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
6 files changed, 153 insertions(+), 8 deletions(-)

diff --git a/go/resource/resource.go b/go/resource/resource.go @@ -1,7 +1,13 @@ package resource +import ( + "context" +) + +type EntryFunc func(input []byte, ctx context.Context) (string, error) type Fetcher interface { - Get(symbol string) (string, error) - Render(symbol string, values map[string]string) (string, error) + Get(sym string) (string, error) + Render(sym string, values map[string]string) (string, error) + FuncFor(sym string) (EntryFunc, error) } diff --git a/go/state/state.go b/go/state/state.go @@ -1,7 +1,6 @@ package state import ( - "io" ) type State struct { @@ -9,7 +8,8 @@ type State struct { OutputSize uint16 CacheSize uint32 CacheUseSize uint32 - Cache io.ReadWriteSeeker + Cache []map[string]string + ExecPath []string } func NewState(bitSize uint64, outputSize uint16) State { @@ -26,7 +26,6 @@ func NewState(bitSize uint64, outputSize uint16) State { OutputSize: outputSize, CacheSize: 0, CacheUseSize: 0, - Cache: nil, } } @@ -34,3 +33,17 @@ func(st State) WithCacheSize(cacheSize uint32) State { st.CacheSize = cacheSize return st } + +func(st *State) Enter(input string) { + m := make(map[string]string) + st.Cache = append(st.Cache, m) +} + +func(st *State) Add(k string, v string) error { + st.Cache[len(st.Cache)-1][k] = v + return nil +} + +func(st *State) Get() (map[string]string, error) { + return st.Cache[len(st.Cache)-1], nil +} diff --git a/go/state/state_test.go b/go/state/state_test.go @@ -31,3 +31,9 @@ func TestNewStateCache(t *testing.T) { } } + +func TestStateCacheUse(t *testing.T) { + st := NewState(17, 0) + st.Enter("foo") + st.Add("bar", "baz") +} diff --git a/go/vm/opcodes.go b/go/vm/opcodes.go @@ -7,5 +7,7 @@ const ( CROAK LOAD RELOAD + MAP + SINK _MAX ) diff --git a/go/vm/vm.go b/go/vm/vm.go @@ -25,6 +25,10 @@ func Run(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Co RunLoad(instruction[2:], st, rs, ctx) case RELOAD: RunReload(instruction[2:], st, rs, ctx) + case MAP: + RunMap(instruction[2:], st, rs, ctx) + case SINK: + RunSink(instruction[2:], st, rs, ctx) default: err := fmt.Errorf("Unhandled state: %v", op) return st, err @@ -32,10 +36,37 @@ func Run(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Co return st, nil } -func RunCatch(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, error) { +func instructionSplit(b []byte) (string, []byte, error) { + sz := uint8(b[0]) + tailSz := uint8(len(b)) + if tailSz - 1 < sz { + return "", nil, fmt.Errorf("corrupt instruction, len %v less than symbol length: %v", tailSz, sz) + } + r := string(b[1:1+sz]) + return r, b[1+sz:], nil +} + +func RunMap(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, error) { + head, tail, err := instructionSplit(instruction) + fn, err := rs.FuncFor(head) + if err != nil { + return st, err + } + r, err := fn(tail, ctx) + if err != nil { + return st, err + } + st.Add(head, r) return st, nil } +func RunSink(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, error) { + return st, nil +} + +func RunCatch(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, error) { + return st, nil +} func RunCroak(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, error) { return st, nil @@ -48,3 +79,5 @@ func RunLoad(instruction []byte, st state.State, rs resource.Fetcher, ctx contex func RunReload(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, error) { return st, nil } + + diff --git a/go/vm/vm_test.go b/go/vm/vm_test.go @@ -4,26 +4,60 @@ import ( "context" "fmt" "testing" + "text/template" + "bytes" + "git.defalsify.org/festive/resource" "git.defalsify.org/festive/state" ) type TestResource struct { } +func getOne(input []byte, ctx context.Context) (string, error) { + return "one", nil +} + +func getTwo(input []byte, ctx context.Context) (string, error) { + return "two", nil +} + func (r *TestResource) Get(sym string) (string, error) { switch sym { case "foo": return "inky pinky blinky clyde", nil case "bar": - return "inky pinky {.one} blinky {.two} clyde", nil + return "inky pinky {{.one}} blinky {{.two}} clyde", nil } return "", fmt.Errorf("unknown symbol %s", sym) } func (r *TestResource) Render(sym string, values map[string]string) (string, error) { v, err := r.Get(sym) - return v, err + if err != nil { + return "", err + } + t, err := template.New("tester").Option("missingkey=error").Parse(v) + if err != nil { + return "", err + } + + b := bytes.NewBuffer([]byte{}) + err = t.Execute(b, values) + if err != nil { + return "", err + } + return b.String(), err +} + +func (r *TestResource) FuncFor(sym string) (resource.EntryFunc, error) { + switch sym { + case "one": + return getOne, nil + case "two": + return getTwo, nil + } + return nil, fmt.Errorf("invalid function: '%s'", sym) } func TestRun(t *testing.T) { @@ -42,3 +76,54 @@ func TestRun(t *testing.T) { } _ = r } + +func TestRunMap(t *testing.T) { + st := state.NewState(5, 255) + st.Enter("barbarbar") + rs := TestResource{} + sym := "one" + ins := append([]byte{uint8(len(sym))}, []byte(sym)...) + var err error + st, err = RunMap(ins, st, &rs, context.TODO()) + if err != nil { + t.Error(err) + } + m, err := st.Get() + if err != nil { + t.Error(err) + } + r, err := rs.Render("foo", m) + if err != nil { + t.Error(err) + } + expect := "inky pinky blinky clyde" + if r != expect { + t.Errorf("Expected %v, got %v", []byte(expect), []byte(r)) + } + + r, err = rs.Render("bar", m) + if err == nil { + t.Errorf("expected error for render of bar: %v" ,err) + } + + sym = "two" + ins = append([]byte{uint8(len(sym))}, []byte(sym)...) + st, err = RunMap(ins, st, &rs, context.TODO()) + if err != nil { + t.Error(err) + } + m, err = st.Get() + if err != nil { + t.Error(err) + } + r, err = rs.Render("bar", m) + if err != nil { + t.Error(err) + } + expect = "inky pinky one blinky two clyde" + if r != expect { + t.Errorf("Expected %v, got %v", expect, r) + } + + +}