commit 78261239b22d5b88f09cf5f74dff0e5661c2d633
parent 9f9ef86b9eb5d6ef62f6d4e224a9e960f80a5cd1
Author: lash <dev@holbrook.no>
Date: Fri, 31 Mar 2023 19:24:30 +0100
Add arg handling, invalid arg handling
Diffstat:
5 files changed, 122 insertions(+), 31 deletions(-)
diff --git a/go/router/router.go b/go/router/router.go
@@ -7,20 +7,17 @@ import (
type Router struct {
selectors []string
symbols map[string]string
- navigable bool
}
func NewRouter() Router {
return Router{
symbols: make(map[string]string),
- navigable: true,
}
}
func NewStaticRouter(symbol string) Router {
return Router{
symbols: map[string]string{"_": symbol},
- navigable: false,
}
}
@@ -32,6 +29,9 @@ func(r *Router) Add(selector string, symbol string) error {
if (l > 255) {
return fmt.Errorf("selector too long (is %v, max 255)", l)
}
+ if selector[0] == '_' {
+ return fmt.Errorf("Invalid selector prefix '_'")
+ }
l = len(symbol)
if (l > 255) {
return fmt.Errorf("symbol too long (is %v, max 255)", l)
diff --git a/go/state/state.go b/go/state/state.go
@@ -34,6 +34,14 @@ func NewState(bitSize uint64) State {
return st
}
+func(st State) Where() string {
+ if len(st.ExecPath) == 0 {
+ return ""
+ }
+ l := len(st.ExecPath)
+ return st.ExecPath[l-1]
+}
+
func(st State) WithCacheSize(cacheSize uint32) State {
st.CacheSize = cacheSize
return st
diff --git a/go/vm/opcodes.go b/go/vm/opcodes.go
@@ -1,13 +1,35 @@
package vm
+import (
+ "encoding/binary"
+)
const VERSION = 0
const (
- CATCH = iota
+ BACK = iota
+ CATCH
CROAK
LOAD
RELOAD
MAP
SINK
+ MOVE
_MAX
)
+
+func NewLine(instructionList []byte, instruction uint16, args []string, post []byte, szPost []uint8) []byte {
+ b := []byte{0x00, 0x00}
+ binary.BigEndian.PutUint16(b, instruction)
+ for _, arg := range args {
+ b = append(b, uint8(len(arg)))
+ b = append(b, []byte(arg)...)
+ }
+ if post != nil {
+ b = append(b, uint8(len(post)))
+ b = append(b, post...)
+ }
+ if szPost != nil {
+ b = append(b, szPost...)
+ }
+ return append(instructionList, b...)
+}
diff --git a/go/vm/vm.go b/go/vm/vm.go
@@ -13,18 +13,38 @@ import (
type Runner func(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, []byte, error)
+func argFromBytes(input []byte) (string, []byte, error) {
+ if len(input) == 0 {
+ return "", input, fmt.Errorf("zero length input")
+ }
+ sz := input[0]
+ out := input[1:1+sz]
+ return string(out), input[1+sz:], nil
+}
+
func Apply(input []byte, instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, []byte, error) {
var err error
- st, instruction, err = Run(instruction, st, rs, ctx)
+
+ arg, input, err := argFromBytes(input)
if err != nil {
- return st, instruction, err
+ return st, input, err
}
- rt := router.FromBytes(instruction)
-
- sym := rt.Get(string(input))
+
+ rt := router.FromBytes(input)
+ sym := rt.Get(arg)
if sym == "" {
sym = rt.Default()
- st.PutArg(string(input))
+ st.PutArg(arg)
+ }
+ if sym == "" {
+ instruction = NewLine([]byte{}, MOVE, []string{"_catch"}, nil , nil)
+ } else {
+ instruction = NewLine(instruction, MOVE, []string{sym}, nil, nil)
+ }
+
+ st, instruction, err = Run(instruction, st, rs, ctx)
+ if err != nil {
+ return st, instruction, err
}
return st, instruction, nil
}
@@ -56,6 +76,9 @@ func Run(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Co
case SINK:
st, instruction, err = RunSink(instruction[2:], st, rs, ctx)
break
+ case MOVE:
+ st, instruction, err = RunMove(instruction[2:], st, rs, ctx)
+ break
default:
err = fmt.Errorf("Unhandled state: %v", op)
}
@@ -88,8 +111,9 @@ func RunCatch(instruction []byte, st state.State, rs resource.Fetcher, ctx conte
if err != nil {
return st, instruction, err
}
+ _ = tail
st.Add(head, r, uint32(len(r)))
- return st, tail, nil
+ return st, []byte{}, nil
}
func RunCroak(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, []byte, error) {
@@ -98,8 +122,9 @@ func RunCroak(instruction []byte, st state.State, rs resource.Fetcher, ctx conte
return st, instruction, err
}
_ = head
+ _ = tail
st.Reset()
- return st, tail, nil
+ return st, []byte{}, nil
}
func RunLoad(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, []byte, error) {
@@ -135,7 +160,12 @@ func RunReload(instruction []byte, st state.State, rs resource.Fetcher, ctx cont
}
func RunMove(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, []byte, error) {
- return st, nil, nil
+ head, tail, err := instructionSplit(instruction)
+ if err != nil {
+ return st, instruction, err
+ }
+ st.Down(head)
+ return st, tail, nil
}
func refresh(key string, sym []byte, rs resource.Fetcher, ctx context.Context) (string, error) {
diff --git a/go/vm/vm_test.go b/go/vm/vm_test.go
@@ -3,13 +3,13 @@ package vm
import (
"bytes"
"context"
- "encoding/binary"
"fmt"
"log"
"testing"
"text/template"
"git.defalsify.org/festive/resource"
+ "git.defalsify.org/festive/router"
"git.defalsify.org/festive/state"
)
@@ -159,8 +159,8 @@ func TestRunMultiple(t *testing.T) {
st := state.NewState(5)
rs := TestResource{}
b := []byte{}
- b = NewTestOp(b, LOAD, []string{"one"}, nil, []uint8{0})
- b = NewTestOp(b, LOAD, []string{"two"}, nil, []uint8{42})
+ b = NewLine(b, LOAD, []string{"one"}, nil, []uint8{0})
+ b = NewLine(b, LOAD, []string{"two"}, nil, []uint8{42})
st, _, err := Run(b, st, &rs, context.TODO())
if err != nil {
t.Error(err)
@@ -171,8 +171,8 @@ func TestRunReload(t *testing.T) {
st := state.NewState(5)
rs := TestResource{}
b := []byte{}
- b = NewTestOp(b, LOAD, []string{"dyn"}, nil, []uint8{0})
- b = NewTestOp(b, MAP, []string{"dyn"}, nil, nil)
+ b = NewLine(b, LOAD, []string{"dyn"}, nil, []uint8{0})
+ b = NewLine(b, MAP, []string{"dyn"}, nil, nil)
st, _, err := Run(b, st, &rs, context.TODO())
if err != nil {
t.Error(err)
@@ -186,7 +186,7 @@ func TestRunReload(t *testing.T) {
}
dynVal = "baz"
b = []byte{}
- b = NewTestOp(b, RELOAD, []string{"dyn"}, nil, nil)
+ b = NewLine(b, RELOAD, []string{"dyn"}, nil, nil)
st, _, err = Run(b, st, &rs, context.TODO())
if err != nil {
t.Error(err)
@@ -202,19 +202,50 @@ func TestRunReload(t *testing.T) {
}
-func NewTestOp(instructionList []byte, instruction uint16, args []string, post []byte, szPost []uint8) []byte {
- b := []byte{0x00, 0x00}
- binary.BigEndian.PutUint16(b, instruction)
- for _, arg := range args {
- b = append(b, uint8(len(arg)))
- b = append(b, []byte(arg)...)
+func TestRunArg(t *testing.T) {
+ st := state.NewState(5)
+ rt := router.NewRouter()
+ rt.Add("foo", "bar")
+ rt.Add("baz", "xyzzy")
+ b := []byte{0x03}
+ b = append(b, []byte("baz")...)
+ b = append(b, rt.ToBytes()...)
+ var err error
+ st, b, err = Apply(b, []byte{}, st, nil, context.TODO())
+ if err != nil {
+ t.Error(err)
}
- if post != nil {
- b = append(b, uint8(len(post)))
- b = append(b, post...)
+ l := len(b)
+ if l != 0 {
+ t.Errorf("expected empty remainder, got length %v: %v", l, b)
}
- if szPost != nil {
- b = append(b, szPost...)
+ r := st.Where()
+ if r != "xyzzy" {
+ t.Errorf("expected where-state baz, got %v", r)
}
- return append(instructionList, b...)
}
+
+func TestRunArgInvalid(t *testing.T) {
+ st := state.NewState(5)
+ rt := router.NewRouter()
+ rt.Add("foo", "bar")
+ rt.Add("baz", "xyzzy")
+ b := []byte{0x03}
+ b = append(b, []byte("bar")...)
+ b = append(b, rt.ToBytes()...)
+ var err error
+ st, b, err = Apply(b, []byte{}, st, nil, 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)
+ }
+ r := st.Where()
+ if r != "_catch" {
+ t.Errorf("expected where-state _catch, got %v", r)
+ }
+
+}
+