diff --git a/engine/engine_test.go b/engine/engine_test.go index 4bc06fb..8c03690 100644 --- a/engine/engine_test.go +++ b/engine/engine_test.go @@ -32,16 +32,23 @@ func NewFsWrapper(path string, st *state.State) FsWrapper { } } -func(fs FsWrapper) one(sym string, input []byte, ctx context.Context) (string, error) { - return "one", nil +func(fs FsWrapper) one(sym string, input []byte, ctx context.Context) (resource.Result, error) { + return resource.Result{ + Content: "one", + }, nil } -func(fs FsWrapper) inky(sym string, input []byte, ctx context.Context) (string, error) { - return "tinkywinky", nil +func(fs FsWrapper) inky(sym string, input []byte, ctx context.Context) (resource.Result, error) { + return resource.Result{ + Content: "tinkywinky", + }, nil } -func(fs FsWrapper) pinky(sym string, input []byte, ctx context.Context) (string, error) { - return fmt.Sprintf("xyzzy: %x", input), nil +func(fs FsWrapper) pinky(sym string, input []byte, ctx context.Context) (resource.Result, error) { + r := fmt.Sprintf("xyzzy: %x", input) + return resource.Result{ + Content: r, + }, nil } func(fs FsWrapper) FuncFor(sym string) (resource.EntryFunc, error) { diff --git a/render/size_test.go b/render/size_test.go index e965775..db11bed 100644 --- a/render/size_test.go +++ b/render/size_test.go @@ -41,20 +41,28 @@ func funcFor(sym string) (resource.EntryFunc, error) { return nil, fmt.Errorf("unknown func: %s", sym) } -func getFoo(sym string, input []byte, ctx context.Context) (string, error) { - return "inky", nil +func getFoo(sym string, input []byte, ctx context.Context) (resource.Result, error) { + return resource.Result{ + Content: "inky", + }, nil } -func getBar(sym string, input []byte, ctx context.Context) (string, error) { - return "pinky", nil +func getBar(sym string, input []byte, ctx context.Context) (resource.Result, error) { + return resource.Result{ + Content: "pinky", + }, nil } -func getBaz(sym string, input []byte, ctx context.Context) (string, error) { - return "blinky", nil +func getBaz(sym string, input []byte, ctx context.Context) (resource.Result, error) { + return resource.Result{ + Content: "blinky", + }, nil } -func getXyzzy(sym string, input []byte, ctx context.Context) (string, error) { - return "inky pinky\nblinky clyde sue\ntinkywinky dipsy\nlala poo\none two three four five six seven\neight nine ten\neleven twelve", nil +func getXyzzy(sym string, input []byte, ctx context.Context) (resource.Result, error) { + return resource.Result{ + Content: "inky pinky\nblinky clyde sue\ntinkywinky dipsy\nlala poo\none two three four five six seven\neight nine ten\neleven twelve", + }, nil } func TestSizeCheck(t *testing.T) { diff --git a/resource/fs.go b/resource/fs.go index 1b6bda6..82ab96d 100644 --- a/resource/fs.go +++ b/resource/fs.go @@ -55,18 +55,20 @@ func(fs FsResource) String() string { return fmt.Sprintf("fs resource at path: %s", fs.Path) } -func(fs FsResource) getFunc(sym string, input []byte, ctx context.Context) (string, error) { +func(fs FsResource) getFunc(sym string, input []byte, ctx context.Context) (Result, error) { return fs.getFuncNoCtx(sym, input) } -func(fs FsResource) getFuncNoCtx(sym string, input []byte) (string, error) { +func(fs FsResource) getFuncNoCtx(sym string, input []byte) (Result, error) { fb := sym + ".txt" fp := path.Join(fs.Path, fb) log.Printf("getfunc search dir %s %s for %s", fs.Path, fp, sym) r, err := ioutil.ReadFile(fp) if err != nil { - return "", fmt.Errorf("failed getting data for sym '%s': %v", sym, err) + return Result{}, fmt.Errorf("failed getting data for sym '%s': %v", sym, err) } s := string(r) - return strings.TrimSpace(s), nil + return Result{ + Content: strings.TrimSpace(s), + }, nil } diff --git a/resource/resource.go b/resource/resource.go index c406c84..ee1bfed 100644 --- a/resource/resource.go +++ b/resource/resource.go @@ -4,8 +4,14 @@ import ( "context" ) +type Result struct { + Content string + FlagSet []uint32 + FlagReset []uint32 +} + // EntryFunc is a function signature for retrieving value for a key -type EntryFunc func(sym string, input []byte, ctx context.Context) (string, error) +type EntryFunc func(sym string, input []byte, ctx context.Context) (Result, error) type CodeFunc func(sym string) ([]byte, error) type TemplateFunc func(sym string) (string, error) type FuncForFunc func(sym string) (EntryFunc, error) diff --git a/resource/resource_test.go b/resource/resource_test.go index 66f5a21..7914759 100644 --- a/resource/resource_test.go +++ b/resource/resource_test.go @@ -37,18 +37,27 @@ func funcFor(sym string) (EntryFunc, error) { return nil, fmt.Errorf("unknown func: %s", sym) } -func get(sym string, input []byte, ctx context.Context) (string, error) { +func get(sym string, input []byte, ctx context.Context) (Result, error) { switch sym { case "foo": - return "inky", nil + return Result{ + Content: "inky", + }, nil case "bar": - return "pinky", nil + return Result{ + Content: "pinky", + }, nil case "baz": - return "blinky", nil + return Result{ + Content: "blinky", + }, nil } - return "", fmt.Errorf("unknown sym: %s", sym) + return Result{}, fmt.Errorf("unknown sym: %s", sym) } -func getXyzzy(sym string, input []byte, ctx context.Context) (string, error) { - return "inky pinky\nblinky clyde sue\ntinkywinky dipsy\nlala poo\none two three four five six seven\neight nine ten\neleven twelve", nil +func getXyzzy(sym string, input []byte, ctx context.Context) (Result, error) { + r := "inky pinky\nblinky clyde sue\ntinkywinky dipsy\nlala poo\none two three four five six seven\neight nine ten\neleven twelve" + return Result{ + Content: r, + }, nil } diff --git a/state/flag.go b/state/flag.go index b829415..295358c 100644 --- a/state/flag.go +++ b/state/flag.go @@ -5,4 +5,16 @@ const ( FLAG_INMATCH = 2 FLAG_TERMINATE = 3 FLAG_DIRTY = 4 + FLAG_LOADFAIL = 5 + //FLAG_WRITEABLE = FLAG_LOADFAIL ) + +func IsWriteableFlag(flag uint32) bool { + if flag > 7 { + return true + } + //if flag & FLAG_WRITEABLE > 0 { + // return true + //} + return false +} diff --git a/state/state.go b/state/state.go index 08abe72..39a64c6 100644 --- a/state/state.go +++ b/state/state.go @@ -112,6 +112,11 @@ func(st *State) ResetFlag(bitIndex uint32) (bool, error) { return true, nil } +// ResetBaseFlags restes all builtin flags not writeable by client. +func(st *State) ResetBaseFlags() { + st.Flags[0] = 0 +} + // GetFlag returns the state of the flag at the given bit field index. // // Fails if bit field index is out of range. diff --git a/vm/runner.go b/vm/runner.go index 1fe00f4..92463ee 100644 --- a/vm/runner.go +++ b/vm/runner.go @@ -60,6 +60,7 @@ func(vm *Vm) Run(b []byte, ctx context.Context) ([]byte, error) { log.Printf("terminate set! bailing!") return []byte{}, nil } + vm.st.ResetBaseFlags() _, err = vm.st.SetFlag(state.FLAG_DIRTY) if err != nil { panic(err) @@ -179,7 +180,8 @@ func(vm *Vm) RunCatch(b []byte, ctx context.Context) ([]byte, error) { log.Printf("catch at flag %v, moving to %v", sig, sym) //bitField, d) vm.st.Down(sym) vm.Reset() - b = []byte{} + bh := NewLine(nil, HALT, nil, nil, nil) + b = append(bh, b...) } return b, nil } @@ -430,6 +432,33 @@ func(vm *Vm) refresh(key string, rs resource.Resource, ctx context.Context) (str return "", fmt.Errorf("no retrieve function for external symbol %v", key) } input, _ := vm.st.GetInput() - return fn(key, input, ctx) -} + r, err := fn(key, input, ctx) + if err != nil { + var perr error + _, perr = vm.st.SetFlag(state.FLAG_LOADFAIL) + if perr != nil { + panic(err) + } + return "", err + } + for _, flag := range r.FlagSet { + if !state.IsWriteableFlag(flag) { + continue + } + _, err = vm.st.SetFlag(flag) + if err != nil { + panic(err) + } + } + for _, flag := range r.FlagReset { + if !state.IsWriteableFlag(flag) { + continue + } + _, err = vm.st.ResetFlag(flag) + if err != nil { + panic(err) + } + } + return r.Content, err +} diff --git a/vm/runner_test.go b/vm/runner_test.go index 4f78f8d..7da7ef4 100644 --- a/vm/runner_test.go +++ b/vm/runner_test.go @@ -20,16 +20,45 @@ type TestResource struct { state *state.State } -func getOne(sym string, input []byte, ctx context.Context) (string, error) { - return "one", nil +func getOne(sym string, input []byte, ctx context.Context) (resource.Result, error) { + return resource.Result{ + Content: "one", + }, nil } -func getTwo(sym string, input []byte, ctx context.Context) (string, error) { - return "two", nil +func getTwo(sym string, input []byte, ctx context.Context) (resource.Result, error) { + return resource.Result{ + Content: "two", + }, nil } -func getDyn(sym string, input []byte, ctx context.Context) (string, error) { - return dynVal, nil +func getDyn(sym string, input []byte, ctx context.Context) (resource.Result, error) { + return resource.Result{ + Content: dynVal, + }, nil +} + +func getEcho(sym string, input []byte, ctx context.Context) (resource.Result, error) { + r := fmt.Sprintf("echo: %s", input) + return resource.Result{ + Content: r, + }, nil +} + +func setFlag(sym string, input []byte, ctx context.Context) (resource.Result, error) { + s := fmt.Sprintf("ping") + r := resource.Result{ + Content: s, + } + if len(input) > 0 { + r.FlagSet = append(r.FlagSet, uint32(input[0])) + } + if len(input) > 1 { + r.FlagReset = append(r.FlagReset, uint32(input[1])) + } + log.Printf("setflag %v", r) + return r, nil + } type TestStatefulResolver struct { @@ -50,6 +79,8 @@ func (r TestResource) GetTemplate(sym string) (string, error) { return "root", nil case "_catch": return "aiee", nil + case "flagCatch": + return "flagiee", nil } panic(fmt.Sprintf("unknown symbol %s", sym)) return "", fmt.Errorf("unknown symbol %s", sym) @@ -65,21 +96,32 @@ func (r TestResource) FuncFor(sym string) (resource.EntryFunc, error) { return getDyn, nil case "arg": return r.getInput, nil + case "echo": + return getEcho, nil + case "setFlagOne": + return setFlag, nil } return nil, fmt.Errorf("invalid function: '%s'", sym) } -func(r TestResource) getInput(sym string, input []byte, ctx context.Context) (string, error) { +func(r TestResource) getInput(sym string, input []byte, ctx context.Context) (resource.Result, error) { v, err := r.state.GetInput() - return string(v), err + return resource.Result{ + Content: string(v), + }, err } func(r TestResource) GetCode(sym string) ([]byte, error) { var b []byte - if sym == "_catch" { + switch sym { + case "_catch": + b = NewLine(b, MOUT, []string{"0", "repent"}, nil, nil) + b = NewLine(b, HALT, nil, nil, nil) + case "flagCatch": b = NewLine(b, MOUT, []string{"0", "repent"}, nil, nil) b = NewLine(b, HALT, nil, nil, nil) } + return b, nil } @@ -409,3 +451,73 @@ func TestRunReturn(t *testing.T) { t.Fatalf("expected location 'root', got '%s'", location) } } + + +func TestRunLoadInput(t *testing.T) { + st := state.NewState(5) + rs := TestResource{} + ca := cache.NewCache() + vm := NewVm(&st, &rs, ca, nil) + + var err error + + st.Down("root") + st.SetInput([]byte("foobar")) + + b := NewLine(nil, LOAD, []string{"echo"}, []byte{0x00}, nil) + b = NewLine(b, HALT, nil, nil, nil) + + ctx := context.TODO() + + b, err = vm.Run(b, ctx) + if err != nil { + t.Fatal(err) + } + + r, err := ca.Get("echo") + if err != nil { + t.Fatal(err) + } + if r != "echo: foobar" { + t.Fatalf("expected 'echo: foobar', got %s", r) + } +} + +func TestInputBranch(t *testing.T) { + st := state.NewState(5) + rs := TestResource{} + ca := cache.NewCache() + vm := NewVm(&st, &rs, ca, nil) + + var err error + + st.Down("root") + + b := NewLine(nil, LOAD, []string{"setFlagOne"}, []byte{0x00}, nil) + b = NewLine(b, CATCH, []string{"flagCatch"}, []byte{8}, []uint8{0}) + b = NewLine(b, RELOAD, []string{"setFlagOne"}, nil, nil) + b = NewLine(b, CATCH, []string{"flagCatch"}, []byte{8}, []uint8{0}) + b = NewLine(b, CATCH, []string{"one"}, []byte{9}, []uint8{0}) + + ctx := context.TODO() + + st.SetInput([]byte{0x08}) + b, err = vm.Run(b, ctx) + if err != nil { + t.Fatal(err) + } + location, _ := st.Where() + if location != "flagCatch" { + t.Fatalf("expected 'flagCatch', got %s", location) + } + + st.SetInput([]byte{0x09, 0x08}) + b, err = vm.Run(b, ctx) + if err != nil { + t.Fatal(err) + } + location, _ = st.Where() + if location != "one" { + t.Fatalf("expected 'one', got %s", location) + } +}