Reinstatate MNEXT, MPREV
This commit is contained in:
parent
8b1f91e859
commit
9e4205e6e8
@ -14,7 +14,6 @@ const (
|
|||||||
MENU_UP = _MENU_OFFSET + 1
|
MENU_UP = _MENU_OFFSET + 1
|
||||||
MENU_NEXT = _MENU_OFFSET + 2
|
MENU_NEXT = _MENU_OFFSET + 2
|
||||||
MENU_PREVIOUS = _MENU_OFFSET + 3
|
MENU_PREVIOUS = _MENU_OFFSET + 3
|
||||||
//MENU_BROWSE = _MENU_OFFSET + 4
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -23,7 +22,6 @@ var (
|
|||||||
"UP": MENU_UP,
|
"UP": MENU_UP,
|
||||||
"NEXT": MENU_NEXT,
|
"NEXT": MENU_NEXT,
|
||||||
"PREVIOUS": MENU_PREVIOUS,
|
"PREVIOUS": MENU_PREVIOUS,
|
||||||
//"BROWSE": MENU_BROWSE,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -66,15 +64,18 @@ func (mp *MenuProcessor) ToLines() []byte {
|
|||||||
postLines := []byte{}
|
postLines := []byte{}
|
||||||
|
|
||||||
for _, v := range mp.items {
|
for _, v := range mp.items {
|
||||||
preLines = vm.NewLine(preLines, vm.MOUT, []string{v.choice, v.display}, nil, nil)
|
|
||||||
switch v.code {
|
switch v.code {
|
||||||
case MENU_UP:
|
case MENU_UP:
|
||||||
|
preLines = vm.NewLine(preLines, vm.MOUT, []string{v.choice, v.display}, nil, nil)
|
||||||
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:
|
||||||
|
preLines = vm.NewLine(preLines, vm.MNEXT, []string{v.choice, v.display}, nil, nil)
|
||||||
postLines = vm.NewLine(postLines, vm.INCMP, []string{v.choice, ">"}, nil, nil)
|
postLines = vm.NewLine(postLines, vm.INCMP, []string{v.choice, ">"}, nil, nil)
|
||||||
case MENU_PREVIOUS:
|
case MENU_PREVIOUS:
|
||||||
|
preLines = vm.NewLine(preLines, vm.MPREV, []string{v.choice, v.display}, nil, nil)
|
||||||
postLines = vm.NewLine(postLines, vm.INCMP, []string{v.choice, "<"}, nil, nil)
|
postLines = vm.NewLine(postLines, vm.INCMP, []string{v.choice, "<"}, nil, nil)
|
||||||
default:
|
default:
|
||||||
|
preLines = vm.NewLine(preLines, vm.MOUT, []string{v.choice, v.display}, nil, nil)
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,8 +35,8 @@ func TestMenuInterpreter(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
expect := `MOUT 0 "inky"
|
expect := `MOUT 0 "inky"
|
||||||
MOUT 1 "pinky"
|
MNEXT 1 "pinky"
|
||||||
MOUT 2 "blinky clyde"
|
MPREV 2 "blinky clyde"
|
||||||
MOUT 99 "tinky-winky"
|
MOUT 99 "tinky-winky"
|
||||||
HALT
|
HALT
|
||||||
INCMP 0 foo
|
INCMP 0 foo
|
||||||
|
@ -56,7 +56,7 @@ func(en *Engine) Init(sym string, ctx context.Context) 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 := vm.CheckInput(input)
|
err := vm.ValidInput(input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return true, err
|
return true, err
|
||||||
}
|
}
|
||||||
|
@ -214,6 +214,9 @@ func(st State) Where() (string, uint16) {
|
|||||||
|
|
||||||
// Next moves to the next sink page index.
|
// Next moves to the next sink page index.
|
||||||
func(st State) Next() (uint16, error) {
|
func(st State) Next() (uint16, error) {
|
||||||
|
if len(st.execPath) == 0 {
|
||||||
|
return 0, fmt.Errorf("state root node not yet defined")
|
||||||
|
}
|
||||||
st.sizeIdx += 1
|
st.sizeIdx += 1
|
||||||
return st.sizeIdx, nil
|
return st.sizeIdx, nil
|
||||||
}
|
}
|
||||||
@ -222,6 +225,9 @@ func(st State) Next() (uint16, error) {
|
|||||||
//
|
//
|
||||||
// Fails if try to move beyond index 0.
|
// Fails if try to move beyond index 0.
|
||||||
func(st *State) Previous() (uint16, error) {
|
func(st *State) Previous() (uint16, error) {
|
||||||
|
if len(st.execPath) == 0 {
|
||||||
|
return 0, fmt.Errorf("state root node not yet defined")
|
||||||
|
}
|
||||||
if st.sizeIdx == 0 {
|
if st.sizeIdx == 0 {
|
||||||
return 0, fmt.Errorf("already at first index")
|
return 0, fmt.Errorf("already at first index")
|
||||||
}
|
}
|
||||||
@ -233,6 +239,9 @@ func(st *State) Previous() (uint16, error) {
|
|||||||
//
|
//
|
||||||
// Two values are returned, for the "next" and "previous" options in that order. A false value means the option is not available in the current state.
|
// Two values are returned, for the "next" and "previous" options in that order. A false value means the option is not available in the current state.
|
||||||
func(st *State) Sides() (bool, bool) {
|
func(st *State) Sides() (bool, bool) {
|
||||||
|
if len(st.execPath) == 0 {
|
||||||
|
return false, false
|
||||||
|
}
|
||||||
next := true
|
next := true
|
||||||
if st.sizeIdx == 0 {
|
if st.sizeIdx == 0 {
|
||||||
return next, false
|
return next, false
|
||||||
@ -240,6 +249,16 @@ func(st *State) Sides() (bool, bool) {
|
|||||||
return next, true
|
return next, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Top returns true if currently at topmode node.
|
||||||
|
//
|
||||||
|
// Fails if first Down() was never called.
|
||||||
|
func(st *State) Top() (bool, error) {
|
||||||
|
if len(st.execPath) == 0 {
|
||||||
|
return false, fmt.Errorf("state root node not yet defined")
|
||||||
|
}
|
||||||
|
return len(st.execPath) == 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Down adds the given symbol to the command stack.
|
// Down adds the given symbol to the command stack.
|
||||||
//
|
//
|
||||||
// Clears mapping and sink.
|
// Clears mapping and sink.
|
||||||
@ -259,10 +278,10 @@ func(st *State) Down(input string) {
|
|||||||
//
|
//
|
||||||
// Fails if called at top frame.
|
// Fails if called at top frame.
|
||||||
func(st *State) Up() (string, error) {
|
func(st *State) Up() (string, error) {
|
||||||
l := len(st.Cache)
|
if len(st.execPath) == 0 {
|
||||||
if l == 0 {
|
|
||||||
return "", fmt.Errorf("exit called beyond top frame")
|
return "", fmt.Errorf("exit called beyond top frame")
|
||||||
}
|
}
|
||||||
|
l := len(st.Cache)
|
||||||
l -= 1
|
l -= 1
|
||||||
m := st.Cache[l]
|
m := st.Cache[l]
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
|
@ -129,6 +129,22 @@ 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,6 +91,26 @@ 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 {
|
||||||
|
@ -19,7 +19,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// CheckInput validates the given byte string as client input.
|
// CheckInput validates the given byte string as client input.
|
||||||
func CheckInput(input []byte) error {
|
func ValidInput(input []byte) error {
|
||||||
if !inputRegex.Match(input) {
|
if !inputRegex.Match(input) {
|
||||||
return fmt.Errorf("Input '%s' does not match input format /%s/", input, inputRegexStr)
|
return fmt.Errorf("Input '%s' does not match input format /%s/", input, inputRegexStr)
|
||||||
}
|
}
|
||||||
@ -27,7 +27,7 @@ func CheckInput(input []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// control characters for relative navigation.
|
// control characters for relative navigation.
|
||||||
func checkControl(input []byte) error {
|
func validControl(input []byte) error {
|
||||||
if !ctrlRegex.Match(input) {
|
if !ctrlRegex.Match(input) {
|
||||||
return fmt.Errorf("Input '%s' does not match 'control' format /%s/", input, ctrlRegexStr)
|
return fmt.Errorf("Input '%s' does not match 'control' format /%s/", input, ctrlRegexStr)
|
||||||
}
|
}
|
||||||
@ -35,36 +35,68 @@ func checkControl(input []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CheckSym validates the given byte string as a node symbol.
|
// CheckSym validates the given byte string as a node symbol.
|
||||||
func CheckSym(input []byte) error {
|
func ValidSym(input []byte) error {
|
||||||
if !symRegex.Match(input) {
|
if !symRegex.Match(input) {
|
||||||
return fmt.Errorf("Input '%s' does not match 'sym' format /%s/", input, symRegexStr)
|
return fmt.Errorf("Input '%s' does not match 'sym' format /%s/", input, symRegexStr)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// false if target is not valid
|
||||||
|
func valid(target []byte) bool {
|
||||||
|
var ok bool
|
||||||
|
if len(target) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
err := ValidSym(target)
|
||||||
|
if err == nil {
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
err = validControl(target)
|
||||||
|
if err == nil {
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckTarget tests whether the navigation state transition is available in the current state.
|
||||||
|
//
|
||||||
|
// Fails if target is formally invalid, or if navigation is unavailable.
|
||||||
|
func CheckTarget(target []byte, st *state.State) (bool, error) {
|
||||||
|
ok := valid(target)
|
||||||
|
if !ok {
|
||||||
|
return false, fmt.Errorf("invalid target: %x", target)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch target[0] {
|
||||||
|
case '_':
|
||||||
|
topOk, err := st.Top()
|
||||||
|
if err!= nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return topOk, nil
|
||||||
|
case '<':
|
||||||
|
_, prevOk := st.Sides()
|
||||||
|
return prevOk, nil
|
||||||
|
case '>':
|
||||||
|
nextOk, _ := st.Sides()
|
||||||
|
return nextOk, nil
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
// route parsed target symbol to navigation state change method,
|
// route parsed target symbol to navigation state change method,
|
||||||
func applyTarget(target []byte, st *state.State, ctx context.Context) (string, uint16, error) {
|
func applyTarget(target []byte, st *state.State, ctx context.Context) (string, uint16, error) {
|
||||||
var err error
|
var err error
|
||||||
var valid bool
|
|
||||||
sym, idx := st.Where()
|
sym, idx := st.Where()
|
||||||
|
|
||||||
err = CheckInput(target)
|
ok := valid(target)
|
||||||
if err == nil {
|
if !ok {
|
||||||
valid = true
|
return sym, idx, fmt.Errorf("invalid input: %x", target)
|
||||||
}
|
|
||||||
|
|
||||||
if !valid {
|
|
||||||
err = CheckSym(target)
|
|
||||||
if err == nil {
|
|
||||||
valid = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !valid {
|
|
||||||
err = checkControl(target)
|
|
||||||
if err == nil {
|
|
||||||
valid = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch target[0] {
|
switch target[0] {
|
||||||
|
@ -17,7 +17,9 @@ const (
|
|||||||
INCMP = 8
|
INCMP = 8
|
||||||
MSIZE = 9
|
MSIZE = 9
|
||||||
MOUT = 10
|
MOUT = 10
|
||||||
_MAX = 10
|
MNEXT = 11
|
||||||
|
MPREV = 12
|
||||||
|
_MAX = 12
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -33,6 +35,8 @@ 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 {
|
||||||
@ -47,6 +51,8 @@ var (
|
|||||||
"INCMP": INCMP,
|
"INCMP": INCMP,
|
||||||
"MSIZE": MSIZE,
|
"MSIZE": MSIZE,
|
||||||
"MOUT": MOUT,
|
"MOUT": MOUT,
|
||||||
|
"MNEXT": MNEXT,
|
||||||
|
"MPREV": MPREV,
|
||||||
}
|
}
|
||||||
|
|
||||||
)
|
)
|
||||||
|
@ -44,6 +44,10 @@ 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
|
||||||
@ -253,6 +257,26 @@ func RunMOut(b []byte, st *state.State, rs resource.Resource, ctx context.Contex
|
|||||||
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
|
||||||
|
}
|
||||||
|
|
||||||
// retrieve data for key
|
// retrieve data for key
|
||||||
func refresh(key string, rs resource.Resource, ctx context.Context) (string, error) {
|
func refresh(key string, rs resource.Resource, ctx context.Context) (string, error) {
|
||||||
fn, err := rs.FuncFor(key)
|
fn, err := rs.FuncFor(key)
|
||||||
|
Loading…
Reference in New Issue
Block a user