commit 4181fe057697d421382cf956f78b150f86f4843b
parent 3febf0a6e27578b7449cadff48e505ec49b7acdd
Author: lash <dev@holbrook.no>
Date: Sat, 1 Apr 2023 21:25:20 +0100
Add HALT opcode
Diffstat:
4 files changed, 142 insertions(+), 110 deletions(-)
diff --git a/README.md b/README.md
@@ -16,6 +16,7 @@ The VM defines the following opcode symbols:
* `RELOAD <symbol>` - Execute a code symbol already loaded by `LOAD` and cache the data, constrained to the previously given `size` for the same symbol.
* `MAP <symbol>` - Expose a code symbol previously loaded by `LOAD` to the rendering client. Roughly corresponds to the `global` directive in Python.
* `MOVE <symbol>` - Create a new execution frame, invalidating all previous `MAP` calls. More detailed: After a `MOVE` call, a `BACK` call will return to the same execution frame, with the same symbols available, but all `MAP` calls will have to be repeated.
+* 'HALT' - Stop execution. The remaining bytecode (typicaly, the routing code for the node) is returned to the invoking function.
### External code
diff --git a/go/vm/opcodes.go b/go/vm/opcodes.go
@@ -6,14 +6,15 @@ import (
const VERSION = 0
const (
- BACK = iota
- CATCH
- CROAK
- LOAD
- RELOAD
- MAP
- MOVE
- _MAX
+ BACK = 0
+ CATCH = 1
+ CROAK = 2
+ LOAD = 3
+ RELOAD = 4
+ MAP = 5
+ MOVE = 6
+ HALT = 7
+ _MAX = 7
)
func NewLine(instructionList []byte, instruction uint16, args []string, post []byte, szPost []uint8) []byte {
diff --git a/go/vm/vm.go b/go/vm/vm.go
@@ -96,6 +96,9 @@ func Run(instruction []byte, st *state.State, rs resource.Resource, ctx context.
instruction, err = RunMove(instruction[2:], st, rs, ctx)
case BACK:
instruction, err = RunBack(instruction[2:], st, rs, ctx)
+ case HALT:
+ log.Printf("found HALT, stopping")
+ return instruction[2:], err
default:
err = fmt.Errorf("Unhandled state: %v", op)
}
diff --git a/go/vm/vm_test.go b/go/vm/vm_test.go
@@ -50,6 +50,8 @@ func (r *TestResource) GetTemplate(sym string) (string, error) {
return "inky pinky {{.baz}} blinky clyde", nil
case "three":
return "{{.one}} inky pinky {{.three}} blinky clyde {{.two}}", nil
+ case "_catch":
+ return "aiee", nil
}
panic(fmt.Sprintf("unknown symbol %s", sym))
return "", fmt.Errorf("unknown symbol %s", sym)
@@ -253,108 +255,112 @@ func TestRunArgInvalid(t *testing.T) {
}
}
-//func TestRunArgInstructions(t *testing.T) {
-// st := state.NewState(5)
-// rs := TestResource{}
-//
-// rt := router.NewRouter()
-// rt.Add("foo", "bar")
-// b := []byte{0x03}
-// b = append(b, []byte("foo")...)
-// b = append(b, rt.ToBytes()...)
-//
-// bi := NewLine([]byte{}, LOAD, []string{"one"}, nil, []uint8{0})
-// bi = NewLine(bi, LOAD, []string{"two"}, nil, []uint8{3})
-// bi = NewLine(bi, MAP, []string{"one"}, nil, nil)
-// bi = NewLine(bi, MAP, []string{"two"}, nil, nil)
-// var err error
-// b, err = Apply(b, bi, &st, &rs, context.TODO())
-// if err != nil {
-// t.Error(err)
-// }
-// l := len(b)
-// if l != 0 {
-// t.Errorf("expected empty remainder, got length %v: %v", l, b)
-// }
-// loc := st.Where()
-// if loc != "bar" {
-// t.Errorf("expected where-state bar, got %v", loc)
-// }
-// m, err := st.Get()
-// if err != nil {
-// t.Fatal(err)
-// }
-// _, err = rs.RenderTemplate(loc, m)
-// if err == nil {
-// t.Fatalf("expected error to generate template")
-// }
-// _, err = Run(bi, &st, &rs, context.TODO())
-// if err != nil {
-// t.Error(err)
-// }
-// m, err = st.Get()
-// if err != nil {
-// t.Fatal(err)
-// }
-// _, err = rs.RenderTemplate(loc, m)
-// if err != nil {
-// t.Fatal(err)
-// }
-//}
-//
-//func TestRunMoveAndBack(t *testing.T) {
-// st := state.NewState(5)
-// rs := TestResource{}
-// rt := router.NewRouter()
-// rt.Add("foo", "bar")
-// b := []byte{0x03}
-// b = append(b, []byte("foo")...)
-// //b = append(b, rt.ToBytes()...)
-// bi := NewLine([]byte{}, LOAD, []string{"one"}, nil, []uint8{0})
-//
-// var err error
-// b, err = Apply(b, bi, &st, &rs, context.TODO())
-// if err != nil {
-// t.Error(err)
-// }
-// l := len(b)
-// if l != 0 {
-// t.Errorf("expected empty remainder, got length %v: %v", l, b)
-// }
-//
-// rt = router.NewRouter()
-// rt.Add("foo", "baz")
-// b = []byte{0x03}
-// b = append(b, []byte("foo")...)
-// b = append(b, rt.ToBytes()...)
-// bi = NewLine([]byte{}, LOAD, []string{"two"}, nil, []uint8{0})
-// b, err = Apply(b, bi, &st, &rs, context.TODO())
-// if err != nil {
-// t.Error(err)
-// }
-// l = len(b)
-// if l != 0 {
-// t.Errorf("expected empty remainder, got length %v: %v", l, b)
-// }
-//
-// rt = router.NewRouter()
-// rt.Add("foo", "_")
-// b = []byte{0x03}
-// b = append(b, []byte("foo")...)
-// //b = append(b, rt.ToBytes()...)
-// b, err = Apply(b, rt.ToBytes(), &st, &rs, context.TODO())
-// if err != nil {
-// t.Error(err)
-// }
-// l = len(b)
-// if l != 0 {
-// t.Errorf("expected empty remainder, got length %v: %v", l, b)
-// }
-// loc := st.Where()
-// if loc != "bar" {
-// t.Errorf("expected where-string 'bar', got %v", loc)
-// }
-//}
+func TestRunArgInstructions(t *testing.T) {
+ t.Skip("pending fix for separating router code from executing code")
+ st := state.NewState(5)
+ rs := TestResource{}
+
+ rt := router.NewRouter()
+ rt.Add("foo", "bar")
+ b := []byte{0x03}
+ b = append(b, []byte("foo")...)
+
+ bi := NewLine(rt.ToBytes(), LOAD, []string{"one"}, nil, []uint8{0})
+ bi = NewLine(bi, LOAD, []string{"two"}, nil, []uint8{3})
+ bi = NewLine(bi, MAP, []string{"one"}, nil, nil)
+ bi = NewLine(bi, MAP, []string{"two"}, nil, nil)
+ var err error
+ b, err = Apply(b, bi, &st, &rs, context.TODO())
+ if err != nil {
+ t.Error(err)
+ }
+ l := len(b)
+ if l != 0 {
+ t.Errorf("expected empty remainder, got length %v: %v", l, b)
+ }
+ loc := st.Where()
+ if loc != "bar" {
+ t.Errorf("expected where-state bar, got %v", loc)
+ }
+ m, err := st.Get()
+ if err != nil {
+ t.Fatal(err)
+ }
+ r, err := rs.RenderTemplate(loc, m)
+ if err != nil {
+ t.Fatal(err) //f("expected error to generate template")
+ }
+ if r != "aiee" {
+ t.Fatalf("expected result 'aiee', got '%v'", r)
+ }
+ _, err = Run(bi, &st, &rs, context.TODO())
+ if err != nil {
+ t.Error(err)
+ }
+ m, err = st.Get()
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = rs.RenderTemplate(loc, m)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestRunMoveAndBack(t *testing.T) {
+ t.Skip("pending fix for separating router code from executing code")
+ st := state.NewState(5)
+ rs := TestResource{}
+ rt := router.NewRouter()
+ rt.Add("foo", "bar")
+ b := []byte{0x03}
+ b = append(b, []byte("foo")...)
+ //b = append(b, rt.ToBytes()...)
+ bi := NewLine([]byte{}, LOAD, []string{"one"}, nil, []uint8{0})
+
+ var err error
+ b, err = Apply(b, bi, &st, &rs, context.TODO())
+ if err != nil {
+ t.Error(err)
+ }
+ l := len(b)
+ if l != 0 {
+ t.Errorf("expected empty remainder, got length %v: %v", l, b)
+ }
+
+ rt = router.NewRouter()
+ rt.Add("foo", "baz")
+ b = []byte{0x03}
+ b = append(b, []byte("foo")...)
+ b = append(b, rt.ToBytes()...)
+ bi = NewLine([]byte{}, LOAD, []string{"two"}, nil, []uint8{0})
+ b, err = Apply(b, bi, &st, &rs, context.TODO())
+ if err != nil {
+ t.Error(err)
+ }
+ l = len(b)
+ if l != 0 {
+ t.Errorf("expected empty remainder, got length %v: %v", l, b)
+ }
+
+ rt = router.NewRouter()
+ rt.Add("foo", "_")
+ b = []byte{0x03}
+ b = append(b, []byte("foo")...)
+ //b = append(b, rt.ToBytes()...)
+ b, err = Apply(b, rt.ToBytes(), &st, &rs, context.TODO())
+ if err != nil {
+ t.Error(err)
+ }
+ l = len(b)
+ if l != 0 {
+ t.Errorf("expected empty remainder, got length %v: %v", l, b)
+ }
+ loc := st.Where()
+ if loc != "bar" {
+ t.Errorf("expected where-string 'bar', got %v", loc)
+ }
+}
func TestCatchAndBack(t *testing.T) {
st := state.NewState(5)
@@ -396,3 +402,24 @@ func TestCatchAndBack(t *testing.T) {
t.Error(err)
}
}
+
+
+func TestHalt(t *testing.T) {
+ st := state.NewState(5)
+ rs := TestResource{}
+ b := NewLine([]byte{}, LOAD, []string{"one"}, nil, []uint8{0})
+ b = NewLine(b, HALT, nil, nil, nil)
+ b = NewLine(b, MOVE, []string{"foo"}, nil, nil)
+ var err error
+ b, err = Run(b, &st, &rs, context.TODO())
+ if err != nil {
+ t.Error(err)
+ }
+ r := st.Where()
+ if r == "foo" {
+ t.Fatalf("Expected where-symbol not to be 'foo'")
+ }
+ if !bytes.Equal(b[:2], []byte{0x00, MOVE}) {
+ t.Fatalf("Expected MOVE instruction, found '%v'", b)
+ }
+}