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.
* `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

View File

@ -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 {

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)
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)
}

View File

@ -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)
}
}