Add stateful render method to vm

This commit is contained in:
lash 2023-04-09 15:35:26 +01:00
parent 45de1f5c7a
commit 95bee7f8e0
Signed by untrusted user who does not match committer: lash
GPG Key ID: 21D2E7BB88C2A746
7 changed files with 87 additions and 118 deletions

View File

@ -7,7 +7,6 @@ import (
"log" "log"
"git.defalsify.org/festive/cache" "git.defalsify.org/festive/cache"
"git.defalsify.org/festive/render"
"git.defalsify.org/festive/resource" "git.defalsify.org/festive/resource"
"git.defalsify.org/festive/state" "git.defalsify.org/festive/state"
"git.defalsify.org/festive/vm" "git.defalsify.org/festive/vm"
@ -23,7 +22,7 @@ type Engine struct {
st *state.State st *state.State
rs resource.Resource rs resource.Resource
ca cache.Memory ca cache.Memory
pg render.Renderer vm *vm.Vm
} }
// NewEngine creates a new Engine // NewEngine creates a new Engine
@ -32,6 +31,7 @@ func NewEngine(st *state.State, rs resource.Resource, ca cache.Memory) Engine {
st: st, st: st,
rs: rs, rs: rs,
ca: ca, ca: ca,
vm: vm.NewVm(st, rs, ca, nil),
} }
return engine return engine
} }
@ -40,15 +40,12 @@ func NewEngine(st *state.State, rs resource.Resource, ca cache.Memory) Engine {
// //
// It loads and executes code for the start node. // It loads and executes code for the start node.
func(en *Engine) Init(sym string, ctx context.Context) error { func(en *Engine) Init(sym string, ctx context.Context) error {
mn := render.NewMenu()
en.pg = render.NewPage(en.ca, en.rs).WithMenu(mn)
vmi := vm.NewVm(en.st, en.rs, en.ca, mn, nil)
err := en.st.SetInput([]byte{}) err := en.st.SetInput([]byte{})
if err != nil { if err != nil {
return err return err
} }
b := vm.NewLine(nil, vm.MOVE, []string{sym}, nil, nil) b := vm.NewLine(nil, vm.MOVE, []string{sym}, nil, nil)
b, err = vmi.Run(b, ctx) b, err = en.vm.Run(b, ctx)
if err != nil { if err != nil {
return err return err
} }
@ -75,9 +72,6 @@ func (en *Engine) Exec(input []byte, ctx context.Context) (bool, error) {
if err != nil { if err != nil {
return false, err return false, err
} }
mn := render.NewMenu()
en.pg = render.NewPage(en.ca, en.rs).WithMenu(mn)
vmi := vm.NewVm(en.st, en.rs, en.ca, mn, en.pg)
log.Printf("new execution with input '%s' (0x%x)", input, input) log.Printf("new execution with input '%s' (0x%x)", input, input)
code, err := en.st.GetCode() code, err := en.st.GetCode()
@ -87,7 +81,7 @@ func (en *Engine) Exec(input []byte, ctx context.Context) (bool, error) {
if len(code) == 0 { if len(code) == 0 {
return false, fmt.Errorf("no code to execute") return false, fmt.Errorf("no code to execute")
} }
code, err = vmi.Run(code, ctx) code, err = en.vm.Run(code, ctx)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -120,27 +114,11 @@ func (en *Engine) Exec(input []byte, ctx context.Context) (bool, error) {
// - the supplied writer fails to process the writes. // - the supplied writer fails to process the writes.
func(en *Engine) WriteResult(w io.Writer) error { func(en *Engine) WriteResult(w io.Writer) error {
location, idx := en.st.Where() location, idx := en.st.Where()
v, err := en.ca.Get() r, err := en.vm.Render()
if err != nil {
return err
}
// r, err := en.rs.RenderTemplate(location, v, idx, nil)
// if err != nil {
// return err
// }
// m, err := en.rs.RenderMenu(idx)
// if err != nil {
// return err
// }
// if len(m) > 0 {
// r += "\n" + m
// }
r, err := en.pg.Render(location, v, idx)
if err != nil { if err != nil {
return err return err
} }
c, err := io.WriteString(w, r) c, err := io.WriteString(w, r)
log.Printf("%v bytes written as result for %v", c, location) log.Printf("%v bytes written as result for %v idx %v", c, location, idx)
en.pg = nil
return err return err
} }

View File

@ -257,7 +257,9 @@ func(pg *Page) render(sym string, values map[string]string, idx uint16) (string,
return "", err return "", err
} }
log.Printf("rendered %v bytes for menu", len(s)) log.Printf("rendered %v bytes for menu", len(s))
r += "\n" + s if len(s) > 0 {
r += "\n" + s
}
if pg.sizer != nil { if pg.sizer != nil {
_, ok = pg.sizer.Check(r) _, ok = pg.sizer.Check(r)
if !ok { if !ok {
@ -267,8 +269,13 @@ func(pg *Page) render(sym string, values map[string]string, idx uint16) (string,
return r, nil return r, nil
} }
func(pg *Page) Render(sym string, values map[string]string, idx uint16) (string, error) { func(pg *Page) Render(sym string, idx uint16) (string, error) {
var err error var err error
values, err := pg.cache.Get()
if err != nil {
return "", err
}
values, err = pg.prepare(sym, values, idx) values, err = pg.prepare(sym, values, idx)
if err != nil { if err != nil {

View File

@ -1,7 +1 @@
package render package render
type Renderer interface {
Map(key string) error
Render(sym string, values map[string]string, idx uint16) (string, error)
Reset()
}

View File

@ -29,11 +29,6 @@ func(szr *Sizer) WithMenuSize(menuSize uint16) *Sizer {
} }
func(szr *Sizer) Set(key string, size uint16) error { func(szr *Sizer) Set(key string, size uint16) error {
var ok bool
_, ok = szr.memberSizes[key]
if ok {
return fmt.Errorf("already have key %s", key)
}
szr.memberSizes[key] = size szr.memberSizes[key] = size
if size == 0 { if size == 0 {
szr.sink = key szr.sink = key

View File

@ -98,20 +98,13 @@ func TestSizeLimit(t *testing.T) {
mn.Put("1", "foo the foo") mn.Put("1", "foo the foo")
mn.Put("2", "go to bar") mn.Put("2", "go to bar")
vals, err := ca.Get() var err error
_, err = pg.Render("small", 0)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
_, err = pg.Render("small", vals, 0) _, err = pg.Render("toobig", 0)
if err != nil {
t.Fatal(err)
}
mn.Put("1", "foo the foo")
mn.Put("2", "go to bar")
_, err = pg.Render("toobig", vals, 0)
if err == nil { if err == nil {
t.Fatalf("expected size exceeded") t.Fatalf("expected size exceeded")
} }
@ -138,15 +131,10 @@ func TestSizePages(t *testing.T) {
pg.Map("baz") pg.Map("baz")
pg.Map("xyzzy") pg.Map("xyzzy")
vals, err := ca.Get()
if err != nil {
t.Fatal(err)
}
mn.Put("1", "foo the foo") mn.Put("1", "foo the foo")
mn.Put("2", "go to bar") mn.Put("2", "go to bar")
r, err := pg.Render("pages", vals, 0) r, err := pg.Render("pages", 0)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -163,7 +151,7 @@ lala poo
if r != expect { if r != expect {
t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expect, r) t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expect, r)
} }
r, err = pg.Render("pages", vals, 1) r, err = pg.Render("pages", 1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -14,19 +14,29 @@ import (
type Vm struct { type Vm struct {
st *state.State st *state.State
rs resource.Resource rs resource.Resource
pg render.Renderer pg *render.Page
ca cache.Memory ca cache.Memory
mn *render.Menu mn *render.Menu
sizer *render.Sizer
} }
func NewVm(st *state.State, rs resource.Resource, ca cache.Memory, mn *render.Menu, pg render.Renderer) *Vm { func NewVm(st *state.State, rs resource.Resource, ca cache.Memory, sizer *render.Sizer) *Vm {
return &Vm{ vmi := &Vm{
st: st, st: st,
rs: rs, rs: rs,
pg: pg,
ca: ca, ca: ca,
mn: mn, sizer: sizer,
}
vmi.Reset()
return vmi
}
func(vmi *Vm) Reset() {
vmi.mn = render.NewMenu()
vmi.pg = render.NewPage(vmi.ca, vmi.rs).WithMenu(vmi.mn)
if vmi.sizer != nil {
vmi.pg = vmi.pg.WithSizer(vmi.sizer)
} }
} }
@ -316,6 +326,16 @@ func(vm *Vm) RunMPrev(b []byte, ctx context.Context) ([]byte, error) {
return b, nil return b, nil
} }
func(vm *Vm) Render() (string, error) {
sym, idx := vm.st.Where()
r, err := vm.pg.Render(sym, idx)
if err != nil {
return "", err
}
vm.Reset()
return r, nil
}
// retrieve data for key // retrieve data for key
func refresh(key string, rs resource.Resource, ctx context.Context) (string, error) { func refresh(key string, rs resource.Resource, ctx context.Context) (string, error) {
fn, err := rs.FuncFor(key) fn, err := rs.FuncFor(key)
@ -327,3 +347,4 @@ func refresh(key string, rs resource.Resource, ctx context.Context) (string, err
} }
return fn(ctx) return fn(ctx)
} }

View File

@ -46,6 +46,8 @@ func (r TestResource) GetTemplate(sym string) (string, error) {
return "inky pinky {{.baz}} blinky clyde", nil return "inky pinky {{.baz}} blinky clyde", nil
case "three": case "three":
return "{{.one}} inky pinky {{.three}} blinky clyde {{.two}}", nil return "{{.one}} inky pinky {{.three}} blinky clyde {{.two}}", nil
case "root":
return "root", nil
case "_catch": case "_catch":
return "aiee", nil return "aiee", nil
} }
@ -85,7 +87,7 @@ func TestRun(t *testing.T) {
st := state.NewState(5) st := state.NewState(5)
rs := TestResource{} rs := TestResource{}
ca := cache.NewCache() ca := cache.NewCache()
vm := NewVm(&st, &rs, ca, nil, nil) vm := NewVm(&st, &rs, ca, nil)
b := NewLine(nil, MOVE, []string{"foo"}, nil, nil) b := NewLine(nil, MOVE, []string{"foo"}, nil, nil)
b = NewLine(b, HALT, nil, nil, nil) b = NewLine(b, HALT, nil, nil, nil)
@ -105,59 +107,46 @@ func TestRunLoadRender(t *testing.T) {
st := state.NewState(5) st := state.NewState(5)
rs := TestResource{} rs := TestResource{}
ca := cache.NewCache() ca := cache.NewCache()
pg := render.NewPage(ca, rs) vm := NewVm(&st, &rs, ca, nil)
vm := NewVm(&st, &rs, ca, nil, pg)
st.Down("barbarbar") st.Down("bar")
var err error var err error
b := NewLine(nil, LOAD, []string{"one"}, []byte{0x0a}, nil) b := NewLine(nil, LOAD, []string{"one"}, []byte{0x0a}, nil)
b = NewLine(b, LOAD, []string{"two"}, []byte{0x0a}, nil)
b = NewLine(b, HALT, nil, nil, nil) b = NewLine(b, HALT, nil, nil, nil)
b, err = vm.Run(b, context.TODO()) b, err = vm.Run(b, context.TODO())
if err != nil { if err != nil {
t.Error(err) t.Fatal(err)
} }
m, err := ca.Get() r, err := vm.Render()
if err != nil { if err != nil {
t.Error(err) t.Fatal(err)
} }
r, err := pg.RenderTemplate("foo", m, 0) expect := "inky pinky one blinky two clyde"
if err != nil {
t.Error(err)
}
expect := "inky pinky blinky clyde"
if r != expect { if r != expect {
t.Errorf("Expected %v, got %v", []byte(expect), []byte(r)) t.Fatalf("Expected\n\t%s\ngot\n\t%s\n", expect, r)
}
r, err = pg.RenderTemplate("bar", m, 0)
if err == nil {
t.Errorf("expected error for render of bar: %v" ,err)
} }
b = NewLine(nil, LOAD, []string{"two"}, []byte{0x0a}, nil) b = NewLine(nil, LOAD, []string{"two"}, []byte{0x0a}, nil)
b = NewLine(b, HALT, nil, nil, nil) b = NewLine(b, HALT, nil, nil, nil)
b, err = vm.Run(b, context.TODO()) b, err = vm.Run(b, context.TODO())
if err != nil { if err != nil {
t.Error(err) t.Fatal(err)
} }
b = NewLine(nil, MAP, []string{"one"}, nil, nil) b = NewLine(nil, MAP, []string{"one"}, nil, nil)
b = NewLine(b, HALT, nil, nil, nil) b = NewLine(b, HALT, nil, nil, nil)
_, err = vm.Run(b, context.TODO()) _, err = vm.Run(b, context.TODO())
if err != nil { if err != nil {
t.Error(err) t.Fatal(err)
} }
m, err = ca.Get() r, err = vm.Render()
if err != nil { if err != nil {
t.Error(err) t.Fatal(err)
}
r, err = pg.RenderTemplate("bar", m, 0)
if err != nil {
t.Error(err)
} }
expect = "inky pinky one blinky two clyde" expect = "inky pinky one blinky two clyde"
if r != expect { if r != expect {
t.Errorf("Expected %v, got %v", expect, r) t.Fatalf("Expected %v, got %v", expect, r)
} }
} }
@ -165,7 +154,7 @@ func TestRunMultiple(t *testing.T) {
st := state.NewState(5) st := state.NewState(5)
rs := TestResource{} rs := TestResource{}
ca := cache.NewCache() ca := cache.NewCache()
vm := NewVm(&st, &rs, ca, nil, nil) vm := NewVm(&st, &rs, ca, nil)
b := NewLine(nil, MOVE, []string{"test"}, nil, nil) b := NewLine(nil, MOVE, []string{"test"}, nil, nil)
b = NewLine(b, LOAD, []string{"one"}, []byte{0x00}, nil) b = NewLine(b, LOAD, []string{"one"}, []byte{0x00}, nil)
@ -184,8 +173,8 @@ func TestRunReload(t *testing.T) {
st := state.NewState(5) st := state.NewState(5)
rs := TestResource{} rs := TestResource{}
ca := cache.NewCache() ca := cache.NewCache()
pg := render.NewPage(ca, rs) szr := render.NewSizer(128)
vm := NewVm(&st, &rs, ca, nil, pg) vm := NewVm(&st, &rs, ca, szr)
b := NewLine(nil, MOVE, []string{"root"}, nil, nil) b := NewLine(nil, MOVE, []string{"root"}, nil, nil)
b = NewLine(b, LOAD, []string{"dyn"}, nil, []uint8{0}) b = NewLine(b, LOAD, []string{"dyn"}, nil, []uint8{0})
@ -195,12 +184,13 @@ func TestRunReload(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
r, err := pg.Val("dyn") r, err := vm.Render()
// r, err := pg.Val("dyn")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if r != "three" { if r != "root" {
t.Fatalf("expected result 'three', got %v", r) t.Fatalf("expected result 'root', got %v", r)
} }
dynVal = "baz" dynVal = "baz"
b = NewLine(nil, RELOAD, []string{"dyn"}, nil, nil) b = NewLine(nil, RELOAD, []string{"dyn"}, nil, nil)
@ -209,21 +199,21 @@ func TestRunReload(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
r, err = pg.Val("dyn") // r, err = pg.Val("dyn")
if err != nil { // if err != nil {
t.Fatal(err) // t.Fatal(err)
} // }
log.Printf("dun now %s", r) // log.Printf("dun now %s", r)
if r != "baz" { // if r != "baz" {
t.Fatalf("expected result 'baz', got %v", r) // t.Fatalf("expected result 'baz', got %v", r)
} // }
} }
func TestHalt(t *testing.T) { func TestHalt(t *testing.T) {
st := state.NewState(5) st := state.NewState(5)
rs := TestResource{} rs := TestResource{}
ca := cache.NewCache() ca := cache.NewCache()
vm := NewVm(&st, &rs, ca, nil, nil) vm := NewVm(&st, &rs, ca, nil)
b := NewLine(nil, MOVE, []string{"root"}, nil, nil) b := NewLine(nil, MOVE, []string{"root"}, nil, nil)
b = NewLine(b, LOAD, []string{"one"}, nil, []uint8{0}) b = NewLine(b, LOAD, []string{"one"}, nil, []uint8{0})
@ -247,7 +237,7 @@ func TestRunArg(t *testing.T) {
st := state.NewState(5) st := state.NewState(5)
rs := TestResource{} rs := TestResource{}
ca := cache.NewCache() ca := cache.NewCache()
vm := NewVm(&st, &rs, ca, nil, nil) vm := NewVm(&st, &rs, ca, nil)
input := []byte("bar") input := []byte("bar")
_ = st.SetInput(input) _ = st.SetInput(input)
@ -272,8 +262,7 @@ func TestRunInputHandler(t *testing.T) {
st := state.NewState(5) st := state.NewState(5)
rs := TestResource{} rs := TestResource{}
ca := cache.NewCache() ca := cache.NewCache()
pg := render.NewPage(ca, rs) vm := NewVm(&st, &rs, ca, nil)
vm := NewVm(&st, &rs, ca, nil, pg)
_ = st.SetInput([]byte("baz")) _ = st.SetInput([]byte("baz"))
@ -300,8 +289,7 @@ func TestRunArgInvalid(t *testing.T) {
st := state.NewState(5) st := state.NewState(5)
rs := TestResource{} rs := TestResource{}
ca := cache.NewCache() ca := cache.NewCache()
mn := render.NewMenu() vm := NewVm(&st, &rs, ca, nil)
vm := NewVm(&st, &rs, ca, mn, nil)
_ = st.SetInput([]byte("foo")) _ = st.SetInput([]byte("foo"))
@ -324,8 +312,7 @@ func TestRunMenu(t *testing.T) {
st := state.NewState(5) st := state.NewState(5)
rs := TestResource{} rs := TestResource{}
ca := cache.NewCache() ca := cache.NewCache()
mn := render.NewMenu() vm := NewVm(&st, &rs, ca, nil)
vm := NewVm(&st, &rs, ca, mn, nil)
var err error var err error
@ -343,11 +330,11 @@ func TestRunMenu(t *testing.T) {
t.Errorf("expected empty remainder, got length %v: %v", l, b) t.Errorf("expected empty remainder, got length %v: %v", l, b)
} }
r, err := mn.Render(0) r, err := vm.Render()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
expect := "0:one\n1:two" expect := "inky pinky blinky clyde\n0:one\n1:two"
if r != expect { if r != expect {
t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expect, r) t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expect, r)
} }
@ -358,8 +345,7 @@ func TestRunMenuBrowse(t *testing.T) {
st := state.NewState(5) st := state.NewState(5)
rs := TestResource{} rs := TestResource{}
ca := cache.NewCache() ca := cache.NewCache()
mn := render.NewMenu() vm := NewVm(&st, &rs, ca, nil)
vm := NewVm(&st, &rs, ca, mn, nil)
var err error var err error
@ -377,11 +363,11 @@ func TestRunMenuBrowse(t *testing.T) {
t.Errorf("expected empty remainder, got length %v: %v", l, b) t.Errorf("expected empty remainder, got length %v: %v", l, b)
} }
r, err := mn.Render(0) r, err := vm.Render()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
expect := "0:one\n1:two" expect := "inky pinky blinky clyde\n0:one\n1:two"
if r != expect { if r != expect {
t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expect, r) t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expect, r)
} }