Use INCMP for lateral move, new input vm mod file
This commit is contained in:
parent
a8a6adbe6c
commit
6221e1dce2
@ -71,9 +71,9 @@ func (mp *MenuProcessor) ToLines() []byte {
|
|||||||
case MENU_UP:
|
case MENU_UP:
|
||||||
postLines = vm.NewLine(postLines, vm.INCMP, []string{v.choice, "_"}, nil, nil)
|
postLines = vm.NewLine(postLines, vm.INCMP, []string{v.choice, "_"}, nil, nil)
|
||||||
case MENU_NEXT:
|
case MENU_NEXT:
|
||||||
_ = postLines
|
postLines = vm.NewLine(postLines, vm.INCMP, []string{v.choice, ">"}, nil, nil)
|
||||||
case MENU_PREVIOUS:
|
case MENU_PREVIOUS:
|
||||||
_ = postLines
|
postLines = vm.NewLine(postLines, vm.INCMP, []string{v.choice, "<"}, nil, nil)
|
||||||
default:
|
default:
|
||||||
postLines = vm.NewLine(postLines, vm.INCMP, []string{v.choice, v.target}, nil, nil)
|
postLines = vm.NewLine(postLines, vm.INCMP, []string{v.choice, v.target}, nil, nil)
|
||||||
}
|
}
|
||||||
@ -82,5 +82,3 @@ func (mp *MenuProcessor) ToLines() []byte {
|
|||||||
preLines = vm.NewLine(preLines, vm.HALT, nil, nil, nil)
|
preLines = vm.NewLine(preLines, vm.HALT, nil, nil, nil)
|
||||||
return append(preLines, postLines...)
|
return append(preLines, postLines...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,6 +40,8 @@ MOUT 2 "blinky clyde"
|
|||||||
MOUT 99 "tinky-winky"
|
MOUT 99 "tinky-winky"
|
||||||
HALT
|
HALT
|
||||||
INCMP 0 foo
|
INCMP 0 foo
|
||||||
|
INCMP 1 >
|
||||||
|
INCMP 2 <
|
||||||
INCMP 99 _
|
INCMP 99 _
|
||||||
`
|
`
|
||||||
if r != expect {
|
if r != expect {
|
||||||
|
@ -5,18 +5,12 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"regexp"
|
|
||||||
|
|
||||||
"git.defalsify.org/festive/resource"
|
"git.defalsify.org/festive/resource"
|
||||||
"git.defalsify.org/festive/state"
|
"git.defalsify.org/festive/state"
|
||||||
"git.defalsify.org/festive/vm"
|
"git.defalsify.org/festive/vm"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
inputRegexStr = "^[a-zA-Z0-9].*$"
|
|
||||||
inputRegex = regexp.MustCompile(inputRegexStr)
|
|
||||||
)
|
|
||||||
|
|
||||||
//type Config struct {
|
//type Config struct {
|
||||||
// FlagCount uint32
|
// FlagCount uint32
|
||||||
// CacheSize uint32
|
// CacheSize uint32
|
||||||
@ -51,14 +45,6 @@ func(en *Engine) Init(sym string, ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// return descriptive error if client input is invalid
|
|
||||||
func checkInput(input []byte) error {
|
|
||||||
if !inputRegex.Match(input) {
|
|
||||||
return fmt.Errorf("Input '%s' does not match format /%s/", input, inputRegexStr)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exec processes user input against the current state of the virtual machine environment.
|
// Exec processes user input against the current state of the virtual machine environment.
|
||||||
//
|
//
|
||||||
// If successfully executed, output of the last execution is available using the WriteResult call.
|
// If successfully executed, output of the last execution is available using the WriteResult call.
|
||||||
@ -70,7 +56,7 @@ func checkInput(input []byte) error {
|
|||||||
// - no current bytecode is available
|
// - no current bytecode is available
|
||||||
// - input processing against bytcode failed
|
// - input processing against bytcode failed
|
||||||
func (en *Engine) Exec(input []byte, ctx context.Context) (bool, error) {
|
func (en *Engine) Exec(input []byte, ctx context.Context) (bool, error) {
|
||||||
err := checkInput(input)
|
err := vm.CheckInput(input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return true, err
|
return true, err
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,7 @@ func TestSizeLimit(t *testing.T) {
|
|||||||
rs := TestSizeResource{
|
rs := TestSizeResource{
|
||||||
mrs,
|
mrs,
|
||||||
}
|
}
|
||||||
|
st.Down("test")
|
||||||
st.Add("foo", "inky", 4)
|
st.Add("foo", "inky", 4)
|
||||||
st.Add("bar", "pinky", 10)
|
st.Add("bar", "pinky", 10)
|
||||||
st.Add("baz", "blinky", 0)
|
st.Add("baz", "blinky", 0)
|
||||||
@ -107,6 +108,7 @@ func TestSizePages(t *testing.T) {
|
|||||||
rs := TestSizeResource{
|
rs := TestSizeResource{
|
||||||
mrs,
|
mrs,
|
||||||
}
|
}
|
||||||
|
st.Down("test")
|
||||||
st.Add("foo", "inky", 4)
|
st.Add("foo", "inky", 4)
|
||||||
st.Add("bar", "pinky", 10)
|
st.Add("bar", "pinky", 10)
|
||||||
st.Add("baz", "blinky", 20)
|
st.Add("baz", "blinky", 20)
|
||||||
|
@ -72,7 +72,7 @@ func NewState(bitSize uint32) State {
|
|||||||
} else {
|
} else {
|
||||||
st.Flags = []byte{}
|
st.Flags = []byte{}
|
||||||
}
|
}
|
||||||
st.Down("")
|
//st.Down("")
|
||||||
return st
|
return st
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,7 +223,6 @@ func(st *State) Down(input string) {
|
|||||||
st.resetCurrent()
|
st.resetCurrent()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Up removes the latest symbol to the command stack, and make the previous symbol current.
|
// Up removes the latest symbol to the command stack, and make the previous symbol current.
|
||||||
//
|
//
|
||||||
// Frees all symbols and associated values loaded at the previous stack level. Cache capacity is increased by the corresponding amount.
|
// Frees all symbols and associated values loaded at the previous stack level. Cache capacity is increased by the corresponding amount.
|
||||||
@ -231,10 +230,10 @@ func(st *State) Down(input string) {
|
|||||||
// Clears mapping and sink.
|
// Clears mapping and sink.
|
||||||
//
|
//
|
||||||
// Fails if called at top frame.
|
// Fails if called at top frame.
|
||||||
func(st *State) Up() error {
|
func(st *State) Up() (string, error) {
|
||||||
l := len(st.Cache)
|
l := len(st.Cache)
|
||||||
if l == 0 {
|
if l == 0 {
|
||||||
return fmt.Errorf("exit called beyond top frame")
|
return "", fmt.Errorf("exit called beyond top frame")
|
||||||
}
|
}
|
||||||
l -= 1
|
l -= 1
|
||||||
m := st.Cache[l]
|
m := st.Cache[l]
|
||||||
@ -245,8 +244,12 @@ func(st *State) Up() error {
|
|||||||
}
|
}
|
||||||
st.Cache = st.Cache[:l]
|
st.Cache = st.Cache[:l]
|
||||||
st.execPath = st.execPath[:l]
|
st.execPath = st.execPath[:l]
|
||||||
|
sym := ""
|
||||||
|
if len(st.execPath) > 0 {
|
||||||
|
sym = st.execPath[len(st.execPath)-1]
|
||||||
|
}
|
||||||
st.resetCurrent()
|
st.resetCurrent()
|
||||||
return nil
|
return sym, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add adds a cache value under a cache symbol key.
|
// Add adds a cache value under a cache symbol key.
|
||||||
|
@ -168,6 +168,7 @@ func TestStateCacheUse(t *testing.T) {
|
|||||||
func TestStateDownUp(t *testing.T) {
|
func TestStateDownUp(t *testing.T) {
|
||||||
st := NewState(17)
|
st := NewState(17)
|
||||||
st.Down("one")
|
st.Down("one")
|
||||||
|
st.Down("two")
|
||||||
err := st.Add("foo", "bar", 0)
|
err := st.Add("foo", "bar", 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
@ -179,15 +180,21 @@ func TestStateDownUp(t *testing.T) {
|
|||||||
if st.CacheUseSize != 8 {
|
if st.CacheUseSize != 8 {
|
||||||
t.Errorf("expected cache use size 8 got %v", st.CacheUseSize)
|
t.Errorf("expected cache use size 8 got %v", st.CacheUseSize)
|
||||||
}
|
}
|
||||||
err = st.Up()
|
s, err := st.Up()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
err = st.Up()
|
if s != "one" {
|
||||||
|
t.Errorf("expected sym 'one', got '%s'", s)
|
||||||
|
}
|
||||||
|
s, err = st.Up()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
err = st.Up()
|
if s != "" {
|
||||||
|
t.Errorf("expected sym '', got '%s'", s)
|
||||||
|
}
|
||||||
|
s, err = st.Up()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("expected out of top frame error")
|
t.Errorf("expected out of top frame error")
|
||||||
}
|
}
|
||||||
|
@ -129,22 +129,6 @@ func ParseAll(b []byte, w io.Writer) (int, error) {
|
|||||||
rs = fmt.Sprintf("%s %s \"%s\"\n", s, r, v)
|
rs = fmt.Sprintf("%s %s \"%s\"\n", s, r, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case MNEXT:
|
|
||||||
r, v, bb, err := ParseMNext(b)
|
|
||||||
b = bb
|
|
||||||
if err == nil {
|
|
||||||
if w != nil {
|
|
||||||
rs = fmt.Sprintf("%s %s \"%s\"\n", s, r, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case MPREV:
|
|
||||||
r, v, bb, err := ParseMPrev(b)
|
|
||||||
b = bb
|
|
||||||
if err == nil {
|
|
||||||
if w != nil {
|
|
||||||
rs = fmt.Sprintf("%s %s \"%s\"\n", s, r, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rn, err
|
return rn, err
|
||||||
|
@ -91,26 +91,6 @@ func TestToString(t *testing.T) {
|
|||||||
t.Fatalf("expected:\n\t%v\ngot:\n\t%v", expect, r)
|
t.Fatalf("expected:\n\t%v\ngot:\n\t%v", expect, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
b = NewLine(nil, MNEXT, []string{"11", "nextmenu"}, nil, nil)
|
|
||||||
r, err = ToString(b)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
expect = "MNEXT 11 \"nextmenu\"\n"
|
|
||||||
if r != expect {
|
|
||||||
t.Fatalf("expected:\n\t%v\ngot:\n\t%v", expect, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
b = NewLine(nil, MPREV, []string{"222", "previous menu item"}, nil, nil)
|
|
||||||
r, err = ToString(b)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
expect = "MPREV 222 \"previous menu item\"\n"
|
|
||||||
if r != expect {
|
|
||||||
t.Fatalf("expected:\n\t%v\ngot:\n\t%v", expect, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
b = NewLine(nil, MOUT, []string{"1", "foo"}, nil, nil)
|
b = NewLine(nil, MOUT, []string{"1", "foo"}, nil, nil)
|
||||||
r, err = ToString(b)
|
r, err = ToString(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
52
go/vm/input.go
Normal file
52
go/vm/input.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"git.defalsify.org/festive/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
inputRegexStr = "^[a-zA-Z0-9].*$"
|
||||||
|
inputRegex = regexp.MustCompile(inputRegexStr)
|
||||||
|
ctrlInputRegexStr = "^[<>_]$"
|
||||||
|
ctrlInputRegex = regexp.MustCompile(inputRegexStr)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func CheckInput(input []byte) error {
|
||||||
|
if !inputRegex.Match(input) {
|
||||||
|
return fmt.Errorf("Input '%s' does not match format /%s/", input, inputRegexStr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func applyControlInput(input []byte, st *state.State, ctx context.Context) (string, error) {
|
||||||
|
var err error
|
||||||
|
sym, idx := st.Where()
|
||||||
|
switch input[0] {
|
||||||
|
case '_':
|
||||||
|
sym, err = st.Up()
|
||||||
|
if err != nil {
|
||||||
|
return sym, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ = idx
|
||||||
|
return sym, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApplyInput(inputString string, st *state.State, ctx context.Context) (string, error) {
|
||||||
|
input := []byte(inputString)
|
||||||
|
if ctrlInputRegex.Match(input) {
|
||||||
|
return applyControlInput(input, st, ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := CheckInput(input)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
st.Down(inputString)
|
||||||
|
return inputString, nil
|
||||||
|
}
|
@ -17,9 +17,7 @@ const (
|
|||||||
INCMP = 8
|
INCMP = 8
|
||||||
MSIZE = 9
|
MSIZE = 9
|
||||||
MOUT = 10
|
MOUT = 10
|
||||||
MNEXT = 11
|
_MAX = 10
|
||||||
MPREV = 12
|
|
||||||
_MAX = 12
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -35,8 +33,6 @@ var (
|
|||||||
INCMP: "INCMP",
|
INCMP: "INCMP",
|
||||||
MSIZE: "MSIZE",
|
MSIZE: "MSIZE",
|
||||||
MOUT: "MOUT",
|
MOUT: "MOUT",
|
||||||
MNEXT: "MNEXT",
|
|
||||||
MPREV: "MPREV",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OpcodeIndex = map[string]Opcode {
|
OpcodeIndex = map[string]Opcode {
|
||||||
@ -51,8 +47,6 @@ var (
|
|||||||
"INCMP": INCMP,
|
"INCMP": INCMP,
|
||||||
"MSIZE": MSIZE,
|
"MSIZE": MSIZE,
|
||||||
"MOUT": MOUT,
|
"MOUT": MOUT,
|
||||||
"MNEXT": MNEXT,
|
|
||||||
"MPREV": MPREV,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
)
|
)
|
||||||
|
@ -44,10 +44,6 @@ func Run(b []byte, st *state.State, rs resource.Resource, ctx context.Context) (
|
|||||||
b, err = RunMSize(b, st, rs, ctx)
|
b, err = RunMSize(b, st, rs, ctx)
|
||||||
case MOUT:
|
case MOUT:
|
||||||
b, err = RunMOut(b, st, rs, ctx)
|
b, err = RunMOut(b, st, rs, ctx)
|
||||||
case MNEXT:
|
|
||||||
b, err = RunMNext(b, st, rs, ctx)
|
|
||||||
case MPREV:
|
|
||||||
b, err = RunMPrev(b, st, rs, ctx)
|
|
||||||
case HALT:
|
case HALT:
|
||||||
b, err = RunHalt(b, st, rs, ctx)
|
b, err = RunHalt(b, st, rs, ctx)
|
||||||
return b, err
|
return b, err
|
||||||
@ -245,26 +241,6 @@ func RunMSize(b []byte, st *state.State, rs resource.Resource, ctx context.Conte
|
|||||||
return b, err
|
return b, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunMNext executes the MNEXT opcode
|
|
||||||
func RunMNext(b []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
|
|
||||||
selector, display, b, err := ParseMNext(b)
|
|
||||||
if err != nil {
|
|
||||||
return b, err
|
|
||||||
}
|
|
||||||
err = rs.SetMenuBrowse(selector, display, false)
|
|
||||||
return b, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunMPrev executes the MPREV opcode
|
|
||||||
func RunMPrev(b []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
|
|
||||||
selector, display, b, err := ParseMPrev(b)
|
|
||||||
if err != nil {
|
|
||||||
return b, err
|
|
||||||
}
|
|
||||||
err = rs.SetMenuBrowse(selector, display, false)
|
|
||||||
return b, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunMOut executes the MOUT opcode
|
// RunMOut executes the MOUT opcode
|
||||||
func RunMOut(b []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
|
func RunMOut(b []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
|
||||||
choice, title, b, err := ParseMOut(b)
|
choice, title, b, err := ParseMOut(b)
|
||||||
|
@ -160,7 +160,8 @@ func TestRunLoadRender(t *testing.T) {
|
|||||||
func TestRunMultiple(t *testing.T) {
|
func TestRunMultiple(t *testing.T) {
|
||||||
st := state.NewState(5)
|
st := state.NewState(5)
|
||||||
rs := TestResource{}
|
rs := TestResource{}
|
||||||
b := NewLine(nil, LOAD, []string{"one"}, []byte{0x00}, nil)
|
b := NewLine(nil, MOVE, []string{"test"}, nil, nil)
|
||||||
|
b = NewLine(b, LOAD, []string{"one"}, []byte{0x00}, nil)
|
||||||
b = NewLine(b, LOAD, []string{"two"}, []byte{42}, nil)
|
b = NewLine(b, LOAD, []string{"two"}, []byte{42}, nil)
|
||||||
b = NewLine(b, HALT, nil, nil, nil)
|
b = NewLine(b, HALT, nil, nil, nil)
|
||||||
b, err := Run(b, &st, &rs, context.TODO())
|
b, err := Run(b, &st, &rs, context.TODO())
|
||||||
@ -211,7 +212,8 @@ func TestRunReload(t *testing.T) {
|
|||||||
func TestHalt(t *testing.T) {
|
func TestHalt(t *testing.T) {
|
||||||
st := state.NewState(5)
|
st := state.NewState(5)
|
||||||
rs := TestResource{}
|
rs := TestResource{}
|
||||||
b := NewLine([]byte{}, LOAD, []string{"one"}, nil, []uint8{0})
|
b := NewLine(nil, MOVE, []string{"root"}, nil, nil)
|
||||||
|
b = NewLine(b, LOAD, []string{"one"}, nil, []uint8{0})
|
||||||
b = NewLine(b, HALT, nil, nil, nil)
|
b = NewLine(b, HALT, nil, nil, nil)
|
||||||
b = NewLine(b, MOVE, []string{"foo"}, nil, nil)
|
b = NewLine(b, MOVE, []string{"foo"}, nil, nil)
|
||||||
var err error
|
var err error
|
||||||
@ -336,8 +338,6 @@ func TestRunMenuBrowse(t *testing.T) {
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
b := NewLine(nil, MOVE, []string{"foo"}, nil, nil)
|
b := NewLine(nil, MOVE, []string{"foo"}, nil, nil)
|
||||||
b = NewLine(b, MNEXT, []string{"11", "two"}, nil, nil)
|
|
||||||
b = NewLine(b, MPREV, []string{"22", "two"}, nil, nil)
|
|
||||||
b = NewLine(b, MOUT, []string{"0", "one"}, nil, nil)
|
b = NewLine(b, MOUT, []string{"0", "one"}, nil, nil)
|
||||||
b = NewLine(b, MOUT, []string{"1", "two"}, nil, nil)
|
b = NewLine(b, MOUT, []string{"1", "two"}, nil, nil)
|
||||||
b = NewLine(b, HALT, nil, nil, nil)
|
b = NewLine(b, HALT, nil, nil, nil)
|
||||||
|
Loading…
Reference in New Issue
Block a user