Add HALT opcode
This commit is contained in:
parent
3febf0a6e2
commit
4181fe0576
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
227
go/vm/vm_test.go
227
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")...)
|
||||
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})
|
||||
// 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")...)
|
||||
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()...)
|
||||
// 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)
|
||||
// }
|
||||
//}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user