go-vise

Constrained Size Output Virtual Machine
Info | Log | Files | Refs | README | LICENSE

commit 8b1f91e8599c08b83911adcc278d0d19a8f42fba
parent 6221e1dce2fb16c9518e452ab9d3ad027c8047eb
Author: lash <dev@holbrook.no>
Date:   Sat,  8 Apr 2023 08:54:55 +0100

Factor out target sym navigation handling

Diffstat:
Mgo/asm/asm.go | 1-
Mgo/state/state.go | 28++++++++++++++++++++++++++++
Mgo/vm/input.go | 84++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Mgo/vm/runner.go | 4+++-
4 files changed, 93 insertions(+), 24 deletions(-)

diff --git a/go/asm/asm.go b/go/asm/asm.go @@ -212,7 +212,6 @@ func parseOne(op vm.Opcode, instruction *Instruction, w io.Writer) (int, error) n, err = writeSym(b, *a.Sym) n_buf += n return flush(b, w) - } func (a Arg) String() string { diff --git a/go/state/state.go b/go/state/state.go @@ -212,6 +212,34 @@ func(st State) Where() (string, uint16) { return st.execPath[l-1], st.sizeIdx } +// Next moves to the next sink page index. +func(st State) Next() (uint16, error) { + st.sizeIdx += 1 + return st.sizeIdx, nil +} + +// Previous moves to the next sink page index. +// +// Fails if try to move beyond index 0. +func(st *State) Previous() (uint16, error) { + if st.sizeIdx == 0 { + return 0, fmt.Errorf("already at first index") + } + st.sizeIdx -= 1 + return st.sizeIdx, nil +} + +// Sides informs the caller which index page options will currently succeed. +// +// 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) { + next := true + if st.sizeIdx == 0 { + return next, false + } + return next, true +} + // Down adds the given symbol to the command stack. // // Clears mapping and sink. diff --git a/go/vm/input.go b/go/vm/input.go @@ -11,42 +11,82 @@ import ( var ( inputRegexStr = "^[a-zA-Z0-9].*$" inputRegex = regexp.MustCompile(inputRegexStr) - ctrlInputRegexStr = "^[<>_]$" - ctrlInputRegex = regexp.MustCompile(inputRegexStr) -) + ctrlRegexStr = "^[<>_]$" + ctrlRegex = regexp.MustCompile(inputRegexStr) + symRegexStr = "^[a-zA-Z0-9][a-zA-Z0-9_]+$" + symRegex = regexp.MustCompile(inputRegexStr) +) +// CheckInput validates the given byte string as client input. func CheckInput(input []byte) error { if !inputRegex.Match(input) { - return fmt.Errorf("Input '%s' does not match format /%s/", input, inputRegexStr) + return fmt.Errorf("Input '%s' does not match input format /%s/", input, inputRegexStr) } return nil } -func applyControlInput(input []byte, st *state.State, ctx context.Context) (string, error) { +// control characters for relative navigation. +func checkControl(input []byte) error { + if !ctrlRegex.Match(input) { + return fmt.Errorf("Input '%s' does not match 'control' format /%s/", input, ctrlRegexStr) + } + return nil +} + +// CheckSym validates the given byte string as a node symbol. +func CheckSym(input []byte) error { + if !symRegex.Match(input) { + return fmt.Errorf("Input '%s' does not match 'sym' format /%s/", input, symRegexStr) + } + return nil +} + +// route parsed target symbol to navigation state change method, +func applyTarget(target []byte, st *state.State, ctx context.Context) (string, uint16, error) { var err error + var valid bool sym, idx := st.Where() - switch input[0] { - case '_': - sym, err = st.Up() - if err != nil { - return sym, err + + err = CheckInput(target) + if err == nil { + valid = true + } + + if !valid { + err = CheckSym(target) + if err == nil { + valid = true } } - _ = 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) + if !valid { + err = checkControl(target) + if err == nil { + valid = true + } } - err := CheckInput(input) - if err != nil { - return "", err + switch target[0] { + case '_': + sym, err = st.Up() + if err != nil { + return sym, idx, err + } + case '>': + idx, err = st.Next() + if err != nil { + return sym, idx, err + } + case '<': + idx, err = st.Previous() + if err != nil { + return sym, idx, err + } + default: + sym = string(target) + st.Down(sym) + idx = 0 } - st.Down(inputString) - return inputString, nil + return sym, idx, nil } diff --git a/go/vm/runner.go b/go/vm/runner.go @@ -212,7 +212,9 @@ func RunInCmp(b []byte, st *state.State, rs resource.Resource, ctx context.Conte log.Printf("input match for '%s'", input) _, err = st.SetFlag(state.FLAG_INMATCH) - st.Down(target) + + sym, _, err = applyTarget([]byte(target), st, ctx) + code, err := rs.GetCode(target) if err != nil { return b, err