commit 8c287b909bb4d6ed2254717b51fbb1ca24177b04
parent 31e011d53139cf02df8e33562025ca5d7cbed659
Author: lash <dev@holbrook.no>
Date: Sun, 2 Apr 2023 12:12:06 +0100
WIP Factor out instruction parsing
Diffstat:
5 files changed, 390 insertions(+), 425 deletions(-)
diff --git a/go/vm/opcodes.go b/go/vm/opcodes.go
@@ -1,10 +1,9 @@
package vm
-import (
- "encoding/binary"
-)
const VERSION = 0
+type Opcode uint16
+
// VM Opcodes
const (
BACK = 0
@@ -18,21 +17,3 @@ const (
INCMP = 8
_MAX = 8
)
-
-// NewLine creates a new instruction line for the VM.
-func NewLine(instructionList []byte, instruction uint16, strargs []string, byteargs []byte, numargs []uint8) []byte {
- b := []byte{0x00, 0x00}
- binary.BigEndian.PutUint16(b, instruction)
- for _, arg := range strargs {
- b = append(b, uint8(len(arg)))
- b = append(b, []byte(arg)...)
- }
- if byteargs != nil {
- b = append(b, uint8(len(byteargs)))
- b = append(b, byteargs...)
- }
- if numargs != nil {
- b = append(b, numargs...)
- }
- return append(instructionList, b...)
-}
diff --git a/go/vm/runner.go b/go/vm/runner.go
@@ -0,0 +1,205 @@
+package vm
+
+import (
+ "encoding/binary"
+ "context"
+ "fmt"
+ "log"
+
+ "git.defalsify.org/festive/resource"
+ "git.defalsify.org/festive/state"
+)
+
+//type Runner func(instruction []byte, st state.State, rs resource.Resource, ctx context.Context) (state.State, []byte, error)
+
+// Run extracts individual op codes and arguments and executes them.
+//
+// Each step may update the state.
+//
+// On error, the remaining instructions will be returned. State will not be rolled back.
+func Run(instruction []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
+ var err error
+ for len(instruction) > 0 {
+ log.Printf("instruction is now 0x%x", instruction)
+ op := binary.BigEndian.Uint16(instruction[:2])
+ if op > _MAX {
+ return instruction, fmt.Errorf("opcode value %v out of range (%v)", op, _MAX)
+ }
+ switch op {
+ case CATCH:
+ instruction, err = RunCatch(instruction[2:], st, rs, ctx)
+ case CROAK:
+ instruction, err = RunCroak(instruction[2:], st, rs, ctx)
+ case LOAD:
+ instruction, err = RunLoad(instruction[2:], st, rs, ctx)
+ case RELOAD:
+ instruction, err = RunReload(instruction[2:], st, rs, ctx)
+ case MAP:
+ instruction, err = RunMap(instruction[2:], st, rs, ctx)
+ case MOVE:
+ instruction, err = RunMove(instruction[2:], st, rs, ctx)
+ case BACK:
+ instruction, err = RunBack(instruction[2:], st, rs, ctx)
+ case INCMP:
+ instruction, err = RunIncmp(instruction[2:], st, rs, ctx)
+ case HALT:
+ return RunHalt(instruction[2:], st, rs, ctx)
+ default:
+ err = fmt.Errorf("Unhandled state: %v", op)
+ }
+ if err != nil {
+ return instruction, err
+ }
+ }
+ return instruction, nil
+}
+
+// RunMap executes the MAP opcode
+func RunMap(instruction []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
+ head, tail, err := instructionSplit(instruction)
+ if err != nil {
+ return instruction, err
+ }
+ err = st.Map(head)
+ return tail, err
+}
+
+// RunMap executes the CATCH opcode
+func RunCatch(instruction []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
+ head, tail, err := instructionSplit(instruction)
+ if err != nil {
+ return instruction, err
+ }
+ bitFieldSize := tail[0]
+ bitField := tail[1:1+bitFieldSize]
+ tail = tail[1+bitFieldSize:]
+ matchMode := tail[0] // matchmode 1 is match NOT set bit
+ tail = tail[1:]
+ match := false
+ if matchMode > 0 {
+ if !st.GetIndex(bitField) {
+ match = true
+ }
+ } else if st.GetIndex(bitField) {
+ match = true
+ }
+
+ if match {
+ log.Printf("catch at flag %v, moving to %v", bitField, head)
+ st.Down(head)
+ tail = []byte{}
+ }
+ return tail, nil
+}
+
+// RunMap executes the CROAK opcode
+func RunCroak(instruction []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
+ head, tail, err := instructionSplit(instruction)
+ if err != nil {
+ return instruction, err
+ }
+ _ = head
+ _ = tail
+ st.Reset()
+ return []byte{}, nil
+}
+
+// RunLoad executes the LOAD opcode
+func RunLoad(instruction []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
+ head, tail, err := instructionSplit(instruction)
+ if err != nil {
+ return instruction, err
+ }
+ if !st.Check(head) {
+ return instruction, fmt.Errorf("key %v already loaded", head)
+ }
+ sz := uint16(tail[0])
+ tail = tail[1:]
+
+ r, err := refresh(head, rs, ctx)
+ if err != nil {
+ return tail, err
+ }
+ err = st.Add(head, r, sz)
+ return tail, err
+}
+
+// RunLoad executes the RELOAD opcode
+func RunReload(instruction []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
+ head, tail, err := instructionSplit(instruction)
+ if err != nil {
+ return instruction, err
+ }
+ r, err := refresh(head, rs, ctx)
+ if err != nil {
+ return tail, err
+ }
+ st.Update(head, r)
+ return tail, nil
+}
+
+// RunLoad executes the MOVE opcode
+func RunMove(instruction []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
+ head, tail, err := instructionSplit(instruction)
+ if err != nil {
+ return instruction, err
+ }
+ st.Down(head)
+ return tail, nil
+}
+
+// RunLoad executes the BACK opcode
+func RunBack(instruction []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
+ st.Up()
+ return instruction, nil
+}
+
+// RunIncmp executes the INCMP opcode
+func RunIncmp(instruction []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
+ head, tail, err := instructionSplit(instruction)
+ if err != nil {
+ return instruction, err
+ }
+ sym, tail, err := instructionSplit(tail)
+ if err != nil {
+ return instruction, err
+ }
+ v, err := st.GetFlag(state.FLAG_INMATCH)
+ if err != nil {
+ return tail, err
+ }
+ if v {
+ return tail, nil
+ }
+ input, err := st.GetInput()
+ if err != nil {
+ return tail, err
+ }
+ log.Printf("checking input %v %v", input, head)
+ if head == string(input) {
+ log.Printf("input match for '%s'", input)
+ _, err = st.SetFlag(state.FLAG_INMATCH)
+ st.Down(sym)
+ }
+ return tail, err
+}
+
+// RunHalt executes the HALT opcode
+func RunHalt(instruction []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
+ log.Printf("found HALT, stopping")
+ _, err := st.ResetFlag(state.FLAG_INMATCH)
+ return instruction, err
+}
+
+
+// retrieve data for key
+func refresh(key string, rs resource.Resource, ctx context.Context) (string, error) {
+ fn, err := rs.FuncFor(key)
+ if err != nil {
+ return "", err
+ }
+ if fn == nil {
+ return "", fmt.Errorf("no retrieve function for external symbol %v", key)
+ }
+ return fn(ctx)
+}
diff --git a/go/vm/vm_test.go b/go/vm/runner_test.go
diff --git a/go/vm/vm.go b/go/vm/vm.go
@@ -2,215 +2,147 @@ package vm
import (
"encoding/binary"
- "context"
"fmt"
- "log"
-
- "git.defalsify.org/festive/resource"
- "git.defalsify.org/festive/state"
)
-//type Runner func(instruction []byte, st state.State, rs resource.Resource, 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
-}
-
-// Run extracts individual op codes and arguments and executes them.
-//
-// Each step may update the state.
-//
-// On error, the remaining instructions will be returned. State will not be rolled back.
-func Run(instruction []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
- var err error
- for len(instruction) > 0 {
- log.Printf("instruction is now 0x%x", instruction)
- op := binary.BigEndian.Uint16(instruction[:2])
- if op > _MAX {
- return instruction, fmt.Errorf("opcode value %v out of range (%v)", op, _MAX)
- }
- switch op {
- case CATCH:
- instruction, err = RunCatch(instruction[2:], st, rs, ctx)
- case CROAK:
- instruction, err = RunCroak(instruction[2:], st, rs, ctx)
- case LOAD:
- instruction, err = RunLoad(instruction[2:], st, rs, ctx)
- case RELOAD:
- instruction, err = RunReload(instruction[2:], st, rs, ctx)
- case MAP:
- instruction, err = RunMap(instruction[2:], st, rs, ctx)
- case MOVE:
- instruction, err = RunMove(instruction[2:], st, rs, ctx)
- case BACK:
- instruction, err = RunBack(instruction[2:], st, rs, ctx)
- case INCMP:
- instruction, err = RunIncmp(instruction[2:], st, rs, ctx)
- case HALT:
- return RunHalt(instruction[2:], st, rs, ctx)
- default:
- err = fmt.Errorf("Unhandled state: %v", op)
- }
- if err != nil {
- return instruction, err
- }
- }
- return instruction, nil
+
+func ParseLoad(b []byte) (string, uint32, []byte, error) {
+ return parseSymLen(b, LOAD)
}
-// RunMap executes the MAP opcode
-func RunMap(instruction []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
- head, tail, err := instructionSplit(instruction)
- if err != nil {
- return instruction, err
- }
- err = st.Map(head)
- return tail, err
+func ParseReload(b []byte) (string, []byte, error) {
+ return parseSym(b, RELOAD)
}
-// RunMap executes the CATCH opcode
-func RunCatch(instruction []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
- head, tail, err := instructionSplit(instruction)
- if err != nil {
- return instruction, err
- }
- bitFieldSize := tail[0]
- bitField := tail[1:1+bitFieldSize]
- tail = tail[1+bitFieldSize:]
- matchMode := tail[0] // matchmode 1 is match NOT set bit
- tail = tail[1:]
- match := false
- if matchMode > 0 {
- if !st.GetIndex(bitField) {
- match = true
- }
- } else if st.GetIndex(bitField) {
- match = true
- }
+func ParseMap(b []byte) (string, []byte, error) {
+ return parseSym(b, MAP)
+}
- if match {
- log.Printf("catch at flag %v, moving to %v", bitField, head)
- st.Down(head)
- tail = []byte{}
- }
- return tail, nil
+func ParseMove(b []byte) (string, []byte, error) {
+ return parseSym(b, MOVE)
}
-// RunMap executes the CROAK opcode
-func RunCroak(instruction []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
- head, tail, err := instructionSplit(instruction)
- if err != nil {
- return instruction, err
- }
- _ = head
- _ = tail
- st.Reset()
- return []byte{}, nil
+func ParseHalt(b []byte) ([]byte, error) {
+ return parseNoArg(b, HALT)
+}
+
+func ParseCatch(b []byte) (string, uint8, []byte, error) {
+ return parseSymSig(b, CATCH)
+}
+
+func ParseCroak(b []byte) (string, uint8, []byte, error) {
+ return parseSymSig(b, CROAK)
}
-// RunLoad executes the LOAD opcode
-func RunLoad(instruction []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
- head, tail, err := instructionSplit(instruction)
+func ParseInCmp(b []byte) (string, string, []byte, error) {
+ return parseTwoSym(b, INCMP)
+}
+
+func parseNoArg(b []byte, op Opcode) ([]byte, error) {
+ return opCheck(b, op)
+}
+
+func parseSym(b []byte, op Opcode) (string, []byte, error) {
+ b, err := opCheck(b, op)
if err != nil {
- return instruction, err
- }
- if !st.Check(head) {
- return instruction, fmt.Errorf("key %v already loaded", head)
+ return "", b, err
}
- sz := uint16(tail[0])
- tail = tail[1:]
-
- r, err := refresh(head, rs, ctx)
+ sym, tail, err := instructionSplit(b)
if err != nil {
- return tail, err
+ return "", b, err
}
- err = st.Add(head, r, sz)
- return tail, err
+ return sym, tail, nil
}
-// RunLoad executes the RELOAD opcode
-func RunReload(instruction []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
- head, tail, err := instructionSplit(instruction)
+func parseTwoSym(b []byte, op Opcode) (string, string, []byte, error) {
+ b, err := opCheck(b, op)
if err != nil {
- return instruction, err
+ return "", "", b, err
}
- r, err := refresh(head, rs, ctx)
+ symOne, tail, err := instructionSplit(b)
if err != nil {
- return tail, err
+ return "", "", b, err
}
- st.Update(head, r)
- return tail, nil
-}
-
-// RunLoad executes the MOVE opcode
-func RunMove(instruction []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
- head, tail, err := instructionSplit(instruction)
+ symTwo, tail, err := instructionSplit(tail)
if err != nil {
- return instruction, err
+ return "", "", tail, err
}
- st.Down(head)
- return tail, nil
+ return symOne, symTwo, tail, nil
}
-// RunLoad executes the BACK opcode
-func RunBack(instruction []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
- st.Up()
- return instruction, nil
-}
-
-// RunIncmp executes the INCMP opcode
-func RunIncmp(instruction []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
- head, tail, err := instructionSplit(instruction)
+func parseSymLen(b []byte, op Opcode) (string, uint32, []byte, error) {
+ b, err := opCheck(b, op)
if err != nil {
- return instruction, err
+ return "", 0, b, err
}
- sym, tail, err := instructionSplit(tail)
+ sym, tail, err := instructionSplit(b)
if err != nil {
- return instruction, err
+ return "", 0, b, err
}
- v, err := st.GetFlag(state.FLAG_INMATCH)
+ sz, tail, err := intSplit(tail)
if err != nil {
- return tail, err
+ return "", 0, b, err
}
- if v {
- return tail, nil
+ return sym, sz, tail, nil
+}
+
+func parseSymSig(b []byte, op Opcode) (string, uint8, []byte, error) {
+ b, err := opCheck(b, op)
+ if err != nil {
+ return "", 0, b, err
}
- input, err := st.GetInput()
+ sym, tail, err := instructionSplit(b)
if err != nil {
- return tail, err
+ return "", 0, b, err
}
- log.Printf("checking input %v %v", input, head)
- if head == string(input) {
- log.Printf("input match for '%s'", input)
- _, err = st.SetFlag(state.FLAG_INMATCH)
- st.Down(sym)
+ if len(tail) == 0 {
+ return "", 0, b, fmt.Errorf("instruction too short")
}
- return tail, err
+ n := tail[0]
+ tail = tail[1:]
+ return sym, n, tail, nil
}
-// RunHalt executes the HALT opcode
-func RunHalt(instruction []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
- log.Printf("found HALT, stopping")
- _, err := st.ResetFlag(state.FLAG_INMATCH)
- return instruction, err
+// NewLine creates a new instruction line for the VM.
+func NewLine(instructionList []byte, instruction uint16, strargs []string, byteargs []byte, numargs []uint8) []byte {
+ if instructionList == nil {
+ instructionList = []byte{}
+ }
+ b := []byte{0x00, 0x00}
+ binary.BigEndian.PutUint16(b, instruction)
+ for _, arg := range strargs {
+ b = append(b, uint8(len(arg)))
+ b = append(b, []byte(arg)...)
+ }
+ if byteargs != nil {
+ b = append(b, uint8(len(byteargs)))
+ b = append(b, byteargs...)
+ }
+ if numargs != nil {
+ b = append(b, numargs...)
+ }
+ return append(instructionList, b...)
}
-
-// retrieve data for key
-func refresh(key string, rs resource.Resource, ctx context.Context) (string, error) {
- fn, err := rs.FuncFor(key)
- if err != nil {
- return "", err
- }
- if fn == nil {
- return "", fmt.Errorf("no retrieve function for external symbol %v", key)
+func intSplit(b []byte) (uint32, []byte, error) {
+ l := uint8(b[0])
+ sz := uint32(l)
+ b = b[1:]
+ if l > 0 {
+ r := []byte{0, 0, 0, 0}
+ c := 0
+ ll := 4 - l
+ var i uint8
+ for i = 0; i < 4; i++ {
+ if i >= ll {
+ r[i] = b[c]
+ c += 1
+ }
+ }
+ sz = binary.BigEndian.Uint32(r)
+ b = b[l:]
}
- return fn(ctx)
+ return sz, b, nil
}
// split instruction into symbol and arguments
@@ -229,3 +161,26 @@ func instructionSplit(b []byte) (string, []byte, error) {
r := string(b[1:1+sz])
return r, b[1+sz:], nil
}
+
+func opCheck(b []byte, opIn Opcode) ([]byte, error) {
+ op, b, err := opSplit(b)
+ if err != nil {
+ return b, err
+ }
+ if op != opIn {
+ return b, fmt.Errorf("not a %v instruction", op)
+ }
+ return b, nil
+}
+
+func opSplit(b []byte) (Opcode, []byte, error) {
+ l := len(b)
+ if l < 2 {
+ return 0, b, fmt.Errorf("input size %v too short for opcode", l)
+ }
+ op := binary.BigEndian.Uint16(b)
+ if op > _MAX {
+ return 0, b, fmt.Errorf("invalid opcode %v", op)
+ }
+ return Opcode(op), b[2:], nil
+}
diff --git a/go/vm/vm_test.go b/go/vm/vm_test.go
@@ -1,284 +1,108 @@
package vm
import (
- "bytes"
- "context"
- "fmt"
- "log"
"testing"
-
- "git.defalsify.org/festive/resource"
- "git.defalsify.org/festive/state"
)
-var dynVal = "three"
-
-type TestResource struct {
- state *state.State
-}
-
-func getOne(ctx context.Context) (string, error) {
- return "one", nil
-}
-
-func getTwo(ctx context.Context) (string, error) {
- return "two", nil
-}
-
-func getDyn(ctx context.Context) (string, error) {
- return dynVal, nil
-}
-
-type TestStatefulResolver struct {
- state *state.State
-}
-
-func (r *TestResource) GetTemplate(sym string) (string, error) {
- switch sym {
- case "foo":
- return "inky pinky blinky clyde", nil
- case "bar":
- return "inky pinky {{.one}} blinky {{.two}} clyde", nil
- case "baz":
- 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)
-}
-
-func (r *TestResource) RenderTemplate(sym string, values map[string]string) (string, error) {
- return resource.DefaultRenderTemplate(r, sym, values)
-}
-
-func (r *TestResource) FuncFor(sym string) (resource.EntryFunc, error) {
- switch sym {
- case "one":
- return getOne, nil
- case "two":
- return getTwo, nil
- case "dyn":
- return getDyn, nil
- case "arg":
- return r.getInput, nil
- }
- return nil, fmt.Errorf("invalid function: '%s'", sym)
-}
-
-func(r *TestResource) getInput(ctx context.Context) (string, error) {
- v, err := r.state.GetInput()
- return string(v), err
-}
-
-func(r *TestResource) GetCode(sym string) ([]byte, error) {
- return []byte{}, nil
-}
-
-func TestRun(t *testing.T) {
- st := state.NewState(5)
- rs := TestResource{}
- b := []byte{0x00, MOVE, 0x03}
- b = append(b, []byte("foo")...)
- _, err := Run(b, &st, &rs, context.TODO())
+func TestParseNoArg(t *testing.T) {
+ b := NewLine(nil, HALT, nil, nil, nil)
+ b, err := ParseHalt(b)
if err != nil {
- t.Errorf("error on valid opcode: %v", err)
- }
-
- b = []byte{0x01, 0x02}
- _, err = Run(b, &st, &rs, context.TODO())
- if err == nil {
- t.Errorf("no error on invalid opcode")
+ t.Fatal(err)
}
}
-func TestRunLoadRender(t *testing.T) {
- st := state.NewState(5)
- st.Down("barbarbar")
- rs := TestResource{}
- sym := "one"
- ins := append([]byte{uint8(len(sym))}, []byte(sym)...)
- ins = append(ins, 0x0a)
- var err error
- _, err = RunLoad(ins, &st, &rs, context.TODO())
+func TestParseSym(t *testing.T) {
+ b := NewLine(nil, MAP, []string{"baz"}, nil, nil)
+ sym, b, err := ParseMap(b)
if err != nil {
- t.Error(err)
+ t.Fatal(err)
}
- m, err := st.Get()
- if err != nil {
- t.Error(err)
- }
- r, err := rs.RenderTemplate("foo", m)
- if err != nil {
- t.Error(err)
- }
- expect := "inky pinky blinky clyde"
- if r != expect {
- t.Errorf("Expected %v, got %v", []byte(expect), []byte(r))
- }
-
- r, err = rs.RenderTemplate("bar", m)
- if err == nil {
- t.Errorf("expected error for render of bar: %v" ,err)
+ if sym != "baz" {
+ t.Fatalf("expected sym baz, got %v", sym)
}
- sym = "two"
- ins = append([]byte{uint8(len(sym))}, []byte(sym)...)
- ins = append(ins, 0)
- _, err = RunLoad(ins, &st, &rs, context.TODO())
- if err != nil {
- t.Error(err)
- }
- m, err = st.Get()
- if err != nil {
- t.Error(err)
- }
- r, err = rs.RenderTemplate("bar", m)
+ b = NewLine(nil, RELOAD, []string{"xyzzy"}, nil, nil)
+ sym, b, err = ParseReload(b)
if err != nil {
- t.Error(err)
+ t.Fatal(err)
}
- expect = "inky pinky one blinky two clyde"
- if r != expect {
- t.Errorf("Expected %v, got %v", expect, r)
+ if sym != "xyzzy" {
+ t.Fatalf("expected sym xyzzy, got %v", sym)
}
-}
-func TestRunMultiple(t *testing.T) {
- st := state.NewState(5)
- rs := TestResource{}
- b := []byte{}
- b = NewLine(b, LOAD, []string{"one"}, nil, []uint8{0})
- b = NewLine(b, LOAD, []string{"two"}, nil, []uint8{42})
- _, err := Run(b, &st, &rs, context.TODO())
+ b = NewLine(nil, MOVE, []string{"plugh"}, nil, nil)
+ sym, b, err = ParseMove(b)
if err != nil {
- t.Error(err)
+ t.Fatal(err)
+ }
+ if sym != "plugh" {
+ t.Fatalf("expected sym plugh, got %v", sym)
}
}
-func TestRunReload(t *testing.T) {
- st := state.NewState(5)
- rs := TestResource{}
- b := []byte{}
- b = NewLine(b, LOAD, []string{"dyn"}, nil, []uint8{0})
- b = NewLine(b, MAP, []string{"dyn"}, nil, nil)
- _, err := Run(b, &st, &rs, context.TODO())
- if err != nil {
- t.Error(err)
- }
- r, err := st.Val("dyn")
+func TestParseTwoSym(t *testing.T) {
+ b := NewLine(nil, INCMP, []string{"foo", "bar"}, nil, nil)
+ one, two, b, err := ParseInCmp(b)
if err != nil {
- t.Error(err)
+ t.Fatal(err)
}
- if r != "three" {
- t.Errorf("expected result 'three', got %v", r)
+ if one != "foo" {
+ t.Fatalf("expected symone foo, got %v", one)
}
- dynVal = "baz"
- b = []byte{}
- b = NewLine(b, RELOAD, []string{"dyn"}, nil, nil)
- _, err = Run(b, &st, &rs, context.TODO())
- if err != nil {
- t.Error(err)
- }
- r, err = st.Val("dyn")
- if err != nil {
- t.Error(err)
+ if two != "bar" {
+ t.Fatalf("expected symtwo bar, got %v", two)
}
- log.Printf("dun now %s", r)
- if r != "baz" {
- t.Errorf("expected result 'baz', got %v", r)
- }
-
}
-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())
+func TestParseSymSig(t *testing.T) {
+ b := NewLine(nil, CATCH, []string{"baz"}, nil, []uint8{0x0d})
+ sym, n, b, err := ParseCatch(b)
if err != nil {
- t.Error(err)
+ t.Fatal(err)
}
- r := st.Where()
- if r == "foo" {
- t.Fatalf("Expected where-symbol not to be 'foo'")
+ if sym != "baz" {
+ t.Fatalf("expected sym baz, got %v", sym)
}
- if !bytes.Equal(b[:2], []byte{0x00, MOVE}) {
- t.Fatalf("Expected MOVE instruction, found '%v'", b)
+ if n != 13 {
+ t.Fatalf("expected n 13, got %v", n)
}
}
-func TestRunArg(t *testing.T) {
- st := state.NewState(5)
- rs := TestResource{}
-
- input := []byte("bar")
- _ = st.SetInput(input)
-
- bi := NewLine([]byte{}, INCMP, []string{"bar", "baz"}, nil, nil)
- b, err := Run(bi, &st, &rs, context.TODO())
+func TestParseSymAndLen(t *testing.T) {
+ b := NewLine(nil, LOAD, []string{"foo"}, []byte{0x2a}, nil)
+ sym, n, b, err := ParseLoad(b)
if err != nil {
- t.Error(err)
+ t.Fatal(err)
}
- l := len(b)
- if l != 0 {
- t.Errorf("expected empty remainder, got length %v: %v", l, b)
+ if sym != "foo" {
+ t.Fatalf("expected sym foo, got %v", sym)
}
- r := st.Where()
- if r != "baz" {
- t.Errorf("expected where-state baz, got %v", r)
+ if n != 42 {
+ t.Fatalf("expected n 42, got %v", n)
}
-}
-
-func TestRunInputHandler(t *testing.T) {
- st := state.NewState(5)
- rs := TestResource{}
-
- _ = st.SetInput([]byte("baz"))
-
- bi := NewLine([]byte{}, INCMP, []string{"bar", "aiee"}, nil, nil)
- bi = NewLine(bi, INCMP, []string{"baz", "foo"}, nil, nil)
- bi = NewLine(bi, 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
- _, err = Run(bi, &st, &rs, context.TODO())
+ b = NewLine(nil, LOAD, []string{"bar"}, []byte{0x02, 0x9a}, nil)
+ sym, n, b, err = ParseLoad(b)
if err != nil {
- t.Fatal(err)
+ t.Fatal(err)
}
- r := st.Where()
- if r != "foo" {
- t.Fatalf("expected where-sym 'foo', got '%v'", r)
+ if sym != "bar" {
+ t.Fatalf("expected sym foo, got %v", sym)
+ }
+ if n != 666 {
+ t.Fatalf("expected n 666, got %v", n)
}
-}
-
-func TestRunArgInvalid(t *testing.T) {
- st := state.NewState(5)
- rs := TestResource{}
-
- _ = st.SetInput([]byte("foo"))
-
- var err error
-
- b := NewLine([]byte{}, INCMP, []string{"bar", "baz"}, nil, nil)
- b = NewLine(b, CATCH, []string{"_catch"}, []byte{state.FLAG_INMATCH}, []uint8{1})
- b, err = Run(b, &st, &rs, context.TODO())
+ b = NewLine(nil, LOAD, []string{"baz"}, []byte{0x0}, nil)
+ sym, n, b, err = ParseLoad(b)
if err != nil {
- t.Error(err)
+ t.Fatal(err)
}
- l := len(b)
- if l != 0 {
- t.Errorf("expected empty remainder, got length %v: %v", l, b)
+ if sym != "baz" {
+ t.Fatalf("expected sym foo, got %v", sym)
}
- r := st.Where()
- if r != "_catch" {
- t.Errorf("expected where-state _catch, got %v", r)
+ if n != 0 {
+ t.Fatalf("expected n 666, got %v", n)
}
}