Add HALT opcode

This commit is contained in:
lash 2023-04-01 21:25:20 +01:00
parent 3febf0a6e2
commit 4181fe0576
Signed by untrusted user who does not match committer: lash
GPG Key ID: 21D2E7BB88C2A746
4 changed files with 142 additions and 110 deletions

View File

@ -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. * `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. * `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. * `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 ### External code

View File

@ -6,14 +6,15 @@ import (
const VERSION = 0 const VERSION = 0
const ( const (
BACK = iota BACK = 0
CATCH CATCH = 1
CROAK CROAK = 2
LOAD LOAD = 3
RELOAD RELOAD = 4
MAP MAP = 5
MOVE MOVE = 6
_MAX HALT = 7
_MAX = 7
) )
func NewLine(instructionList []byte, instruction uint16, args []string, post []byte, szPost []uint8) []byte { func NewLine(instructionList []byte, instruction uint16, args []string, post []byte, szPost []uint8) []byte {

View File

@ -96,6 +96,9 @@ func Run(instruction []byte, st *state.State, rs resource.Resource, ctx context.
instruction, err = RunMove(instruction[2:], st, rs, ctx) instruction, err = RunMove(instruction[2:], st, rs, ctx)
case BACK: case BACK:
instruction, err = RunBack(instruction[2:], st, rs, ctx) instruction, err = RunBack(instruction[2:], st, rs, ctx)
case HALT:
log.Printf("found HALT, stopping")
return instruction[2:], err
default: default:
err = fmt.Errorf("Unhandled state: %v", op) err = fmt.Errorf("Unhandled state: %v", op)
} }

View File

@ -50,6 +50,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 "_catch":
return "aiee", nil
} }
panic(fmt.Sprintf("unknown symbol %s", sym)) panic(fmt.Sprintf("unknown symbol %s", sym))
return "", fmt.Errorf("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) { func TestRunArgInstructions(t *testing.T) {
// st := state.NewState(5) t.Skip("pending fix for separating router code from executing code")
// rs := TestResource{} st := state.NewState(5)
// rs := TestResource{}
// rt := router.NewRouter()
// rt.Add("foo", "bar") rt := router.NewRouter()
// b := []byte{0x03} rt.Add("foo", "bar")
// b = append(b, []byte("foo")...) b := []byte{0x03}
// b = append(b, rt.ToBytes()...) b = append(b, []byte("foo")...)
//
// bi := NewLine([]byte{}, LOAD, []string{"one"}, nil, []uint8{0}) bi := NewLine(rt.ToBytes(), LOAD, []string{"one"}, nil, []uint8{0})
// bi = NewLine(bi, LOAD, []string{"two"}, nil, []uint8{3}) bi = NewLine(bi, LOAD, []string{"two"}, nil, []uint8{3})
// bi = NewLine(bi, MAP, []string{"one"}, nil, nil) bi = NewLine(bi, MAP, []string{"one"}, nil, nil)
// bi = NewLine(bi, MAP, []string{"two"}, nil, nil) bi = NewLine(bi, MAP, []string{"two"}, nil, nil)
// var err error var err error
// b, err = Apply(b, bi, &st, &rs, context.TODO()) b, err = Apply(b, bi, &st, &rs, context.TODO())
// if err != nil { if err != nil {
// t.Error(err) t.Error(err)
// } }
// l := len(b) l := len(b)
// if l != 0 { if l != 0 {
// t.Errorf("expected empty remainder, got length %v: %v", l, b) t.Errorf("expected empty remainder, got length %v: %v", l, b)
// } }
// loc := st.Where() loc := st.Where()
// if loc != "bar" { if loc != "bar" {
// t.Errorf("expected where-state bar, got %v", loc) t.Errorf("expected where-state bar, got %v", loc)
// } }
// m, err := st.Get() m, err := st.Get()
// if err != nil { if err != nil {
// t.Fatal(err) t.Fatal(err)
// } }
// _, err = rs.RenderTemplate(loc, m) r, err := rs.RenderTemplate(loc, m)
// if err == nil { if err != nil {
// t.Fatalf("expected error to generate template") t.Fatal(err) //f("expected error to generate template")
// } }
// _, err = Run(bi, &st, &rs, context.TODO()) if r != "aiee" {
// if err != nil { t.Fatalf("expected result 'aiee', got '%v'", r)
// t.Error(err) }
// } _, err = Run(bi, &st, &rs, context.TODO())
// m, err = st.Get() if err != nil {
// if err != nil { t.Error(err)
// t.Fatal(err) }
// } m, err = st.Get()
// _, err = rs.RenderTemplate(loc, m) if err != nil {
// if err != nil { t.Fatal(err)
// 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() func TestRunMoveAndBack(t *testing.T) {
// rt.Add("foo", "bar") t.Skip("pending fix for separating router code from executing code")
// b := []byte{0x03} st := state.NewState(5)
// b = append(b, []byte("foo")...) rs := TestResource{}
// //b = append(b, rt.ToBytes()...) rt := router.NewRouter()
// bi := NewLine([]byte{}, LOAD, []string{"one"}, nil, []uint8{0}) rt.Add("foo", "bar")
// b := []byte{0x03}
// var err error b = append(b, []byte("foo")...)
// b, err = Apply(b, bi, &st, &rs, context.TODO()) //b = append(b, rt.ToBytes()...)
// if err != nil { bi := NewLine([]byte{}, LOAD, []string{"one"}, nil, []uint8{0})
// t.Error(err)
// } var err error
// l := len(b) b, err = Apply(b, bi, &st, &rs, context.TODO())
// if l != 0 { if err != nil {
// t.Errorf("expected empty remainder, got length %v: %v", l, b) t.Error(err)
// } }
// l := len(b)
// rt = router.NewRouter() if l != 0 {
// rt.Add("foo", "baz") t.Errorf("expected empty remainder, got length %v: %v", l, b)
// b = []byte{0x03} }
// b = append(b, []byte("foo")...)
// b = append(b, rt.ToBytes()...) rt = router.NewRouter()
// bi = NewLine([]byte{}, LOAD, []string{"two"}, nil, []uint8{0}) rt.Add("foo", "baz")
// b, err = Apply(b, bi, &st, &rs, context.TODO()) b = []byte{0x03}
// if err != nil { b = append(b, []byte("foo")...)
// t.Error(err) b = append(b, rt.ToBytes()...)
// } bi = NewLine([]byte{}, LOAD, []string{"two"}, nil, []uint8{0})
// l = len(b) b, err = Apply(b, bi, &st, &rs, context.TODO())
// if l != 0 { if err != nil {
// t.Errorf("expected empty remainder, got length %v: %v", l, b) t.Error(err)
// } }
// l = len(b)
// rt = router.NewRouter() if l != 0 {
// rt.Add("foo", "_") t.Errorf("expected empty remainder, got length %v: %v", l, b)
// b = []byte{0x03} }
// b = append(b, []byte("foo")...)
// //b = append(b, rt.ToBytes()...) rt = router.NewRouter()
// b, err = Apply(b, rt.ToBytes(), &st, &rs, context.TODO()) rt.Add("foo", "_")
// if err != nil { b = []byte{0x03}
// t.Error(err) b = append(b, []byte("foo")...)
// } //b = append(b, rt.ToBytes()...)
// l = len(b) b, err = Apply(b, rt.ToBytes(), &st, &rs, context.TODO())
// if l != 0 { if err != nil {
// t.Errorf("expected empty remainder, got length %v: %v", l, b) t.Error(err)
// } }
// loc := st.Where() l = len(b)
// if loc != "bar" { if l != 0 {
// t.Errorf("expected where-string 'bar', got %v", loc) 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) { func TestCatchAndBack(t *testing.T) {
st := state.NewState(5) st := state.NewState(5)
@ -396,3 +402,24 @@ func TestCatchAndBack(t *testing.T) {
t.Error(err) 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)
}
}