Add arg handling, invalid arg handling

This commit is contained in:
lash 2023-03-31 19:24:30 +01:00
parent 9f9ef86b9e
commit 78261239b2
Signed by untrusted user who does not match committer: lash
GPG Key ID: 21D2E7BB88C2A746
5 changed files with 123 additions and 32 deletions

View File

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

View File

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

View File

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

View File

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

View File

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