Add arg handling, invalid arg handling
This commit is contained in:
parent
9f9ef86b9e
commit
78261239b2
@ -7,20 +7,17 @@ import (
|
|||||||
type Router struct {
|
type Router struct {
|
||||||
selectors []string
|
selectors []string
|
||||||
symbols map[string]string
|
symbols map[string]string
|
||||||
navigable bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRouter() Router {
|
func NewRouter() Router {
|
||||||
return Router{
|
return Router{
|
||||||
symbols: make(map[string]string),
|
symbols: make(map[string]string),
|
||||||
navigable: true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStaticRouter(symbol string) Router {
|
func NewStaticRouter(symbol string) Router {
|
||||||
return Router{
|
return Router{
|
||||||
symbols: map[string]string{"_": symbol},
|
symbols: map[string]string{"_": symbol},
|
||||||
navigable: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,6 +29,9 @@ func(r *Router) Add(selector string, symbol string) error {
|
|||||||
if (l > 255) {
|
if (l > 255) {
|
||||||
return fmt.Errorf("selector too long (is %v, max 255)", l)
|
return fmt.Errorf("selector too long (is %v, max 255)", l)
|
||||||
}
|
}
|
||||||
|
if selector[0] == '_' {
|
||||||
|
return fmt.Errorf("Invalid selector prefix '_'")
|
||||||
|
}
|
||||||
l = len(symbol)
|
l = len(symbol)
|
||||||
if (l > 255) {
|
if (l > 255) {
|
||||||
return fmt.Errorf("symbol too long (is %v, max 255)", l)
|
return fmt.Errorf("symbol too long (is %v, max 255)", l)
|
||||||
|
@ -34,6 +34,14 @@ func NewState(bitSize uint64) State {
|
|||||||
return st
|
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 {
|
func(st State) WithCacheSize(cacheSize uint32) State {
|
||||||
st.CacheSize = cacheSize
|
st.CacheSize = cacheSize
|
||||||
return st
|
return st
|
||||||
|
@ -1,13 +1,35 @@
|
|||||||
package vm
|
package vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
)
|
||||||
const VERSION = 0
|
const VERSION = 0
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CATCH = iota
|
BACK = iota
|
||||||
|
CATCH
|
||||||
CROAK
|
CROAK
|
||||||
LOAD
|
LOAD
|
||||||
RELOAD
|
RELOAD
|
||||||
MAP
|
MAP
|
||||||
SINK
|
SINK
|
||||||
|
MOVE
|
||||||
_MAX
|
_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...)
|
||||||
|
}
|
||||||
|
50
go/vm/vm.go
50
go/vm/vm.go
@ -13,19 +13,39 @@ import (
|
|||||||
|
|
||||||
type Runner func(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, []byte, error)
|
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) {
|
func Apply(input []byte, instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, []byte, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
arg, input, err := argFromBytes(input)
|
||||||
|
if err != nil {
|
||||||
|
return st, input, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rt := router.FromBytes(input)
|
||||||
|
sym := rt.Get(arg)
|
||||||
|
if sym == "" {
|
||||||
|
sym = rt.Default()
|
||||||
|
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)
|
st, instruction, err = Run(instruction, st, rs, ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return st, instruction, err
|
return st, instruction, err
|
||||||
}
|
}
|
||||||
rt := router.FromBytes(instruction)
|
|
||||||
|
|
||||||
sym := rt.Get(string(input))
|
|
||||||
if sym == "" {
|
|
||||||
sym = rt.Default()
|
|
||||||
st.PutArg(string(input))
|
|
||||||
}
|
|
||||||
return st, instruction, nil
|
return st, instruction, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,6 +76,9 @@ func Run(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Co
|
|||||||
case SINK:
|
case SINK:
|
||||||
st, instruction, err = RunSink(instruction[2:], st, rs, ctx)
|
st, instruction, err = RunSink(instruction[2:], st, rs, ctx)
|
||||||
break
|
break
|
||||||
|
case MOVE:
|
||||||
|
st, instruction, err = RunMove(instruction[2:], st, rs, ctx)
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("Unhandled state: %v", op)
|
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 {
|
if err != nil {
|
||||||
return st, instruction, err
|
return st, instruction, err
|
||||||
}
|
}
|
||||||
|
_ = tail
|
||||||
st.Add(head, r, uint32(len(r)))
|
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) {
|
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
|
return st, instruction, err
|
||||||
}
|
}
|
||||||
_ = head
|
_ = head
|
||||||
|
_ = tail
|
||||||
st.Reset()
|
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) {
|
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) {
|
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) {
|
func refresh(key string, sym []byte, rs resource.Fetcher, ctx context.Context) (string, error) {
|
||||||
|
@ -3,13 +3,13 @@ package vm
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"testing"
|
"testing"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"git.defalsify.org/festive/resource"
|
"git.defalsify.org/festive/resource"
|
||||||
|
"git.defalsify.org/festive/router"
|
||||||
"git.defalsify.org/festive/state"
|
"git.defalsify.org/festive/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -159,8 +159,8 @@ func TestRunMultiple(t *testing.T) {
|
|||||||
st := state.NewState(5)
|
st := state.NewState(5)
|
||||||
rs := TestResource{}
|
rs := TestResource{}
|
||||||
b := []byte{}
|
b := []byte{}
|
||||||
b = NewTestOp(b, LOAD, []string{"one"}, nil, []uint8{0})
|
b = NewLine(b, LOAD, []string{"one"}, nil, []uint8{0})
|
||||||
b = NewTestOp(b, LOAD, []string{"two"}, nil, []uint8{42})
|
b = NewLine(b, LOAD, []string{"two"}, nil, []uint8{42})
|
||||||
st, _, err := Run(b, st, &rs, context.TODO())
|
st, _, err := Run(b, st, &rs, context.TODO())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
@ -171,8 +171,8 @@ func TestRunReload(t *testing.T) {
|
|||||||
st := state.NewState(5)
|
st := state.NewState(5)
|
||||||
rs := TestResource{}
|
rs := TestResource{}
|
||||||
b := []byte{}
|
b := []byte{}
|
||||||
b = NewTestOp(b, LOAD, []string{"dyn"}, nil, []uint8{0})
|
b = NewLine(b, LOAD, []string{"dyn"}, nil, []uint8{0})
|
||||||
b = NewTestOp(b, MAP, []string{"dyn"}, nil, nil)
|
b = NewLine(b, MAP, []string{"dyn"}, nil, nil)
|
||||||
st, _, err := Run(b, st, &rs, context.TODO())
|
st, _, err := Run(b, st, &rs, context.TODO())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
@ -186,7 +186,7 @@ func TestRunReload(t *testing.T) {
|
|||||||
}
|
}
|
||||||
dynVal = "baz"
|
dynVal = "baz"
|
||||||
b = []byte{}
|
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())
|
st, _, err = Run(b, st, &rs, context.TODO())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
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 {
|
func TestRunArg(t *testing.T) {
|
||||||
b := []byte{0x00, 0x00}
|
st := state.NewState(5)
|
||||||
binary.BigEndian.PutUint16(b, instruction)
|
rt := router.NewRouter()
|
||||||
for _, arg := range args {
|
rt.Add("foo", "bar")
|
||||||
b = append(b, uint8(len(arg)))
|
rt.Add("baz", "xyzzy")
|
||||||
b = append(b, []byte(arg)...)
|
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 {
|
l := len(b)
|
||||||
b = append(b, uint8(len(post)))
|
if l != 0 {
|
||||||
b = append(b, post...)
|
t.Errorf("expected empty remainder, got length %v: %v", l, b)
|
||||||
}
|
}
|
||||||
if szPost != nil {
|
r := st.Where()
|
||||||
b = append(b, szPost...)
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user