go-vise

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

input.go (3595B)


      1 package vm
      2 
      3 import (
      4 	"bytes"
      5 	"context"
      6 	"fmt"
      7 	"regexp"
      8 
      9 	"git.defalsify.org/vise.git/cache"
     10 	"git.defalsify.org/vise.git/state"
     11 )
     12 
     13 var (
     14 	inputRegexStr = "^[a-zA-Z0-9].*$"
     15 	inputRegex = regexp.MustCompile(inputRegexStr)
     16 	ctrlRegexStr = "^[><_^.]$"
     17 	ctrlRegex = regexp.MustCompile(ctrlRegexStr)
     18 	symRegexStr = "^[a-zA-Z0-9][a-zA-Z0-9_]+$"
     19 	symRegex = regexp.MustCompile(symRegexStr)
     20 
     21 )
     22 
     23 // InvalidInputError indicates client input that was unhandled by the bytecode (INCMP fallthrough)
     24 type InvalidInputError struct {
     25 	input string
     26 }
     27 
     28 // NewInvalidInputError creates a new InvalidInputError
     29 func NewInvalidInputError(input string) error {
     30 	return InvalidInputError{input}
     31 }
     32 
     33 // Error implements error interface.
     34 func(e InvalidInputError) Error() string {
     35 	return fmt.Sprintf("invalid input: '%s'", e.input)
     36 }
     37 
     38 // CheckInput validates the given byte string as client input.
     39 func ValidInput(input []byte) error {
     40 	if !inputRegex.Match(input) {
     41 		return fmt.Errorf("Input '%s' does not match input format /%s/", input, inputRegexStr)
     42 	}
     43 	return nil
     44 }
     45 
     46 // control characters for relative navigation.
     47 func validControl(input []byte) error {
     48 	if !ctrlRegex.Match(input) {
     49 		return fmt.Errorf("Input '%s' does not match 'control' format /%s/", input, ctrlRegexStr)
     50 	}
     51 	return nil
     52 }
     53 
     54 // CheckSym validates the given byte string as a node symbol.
     55 func ValidSym(input []byte) error {
     56 	if bytes.Equal(input, []byte("_catch")) {
     57 		return nil
     58 	}
     59 	if !symRegex.Match(input) {
     60 		return fmt.Errorf("Input '%s' does not match 'sym' format /%s/", input, symRegexStr)
     61 	}
     62 	return nil
     63 }
     64 
     65 // false if target is not valid
     66 func valid(target []byte) bool {
     67 	var ok bool
     68 	if len(target) == 0 {
     69 		return false
     70 	}
     71 
     72 	err := ValidSym(target)
     73 	if err == nil {
     74 		ok = true
     75 	}
     76 
     77 	if !ok {
     78 		err = validControl(target)
     79 		if err == nil {
     80 			ok = true
     81 		}
     82 	}
     83 	return ok 
     84 }
     85 
     86 // CheckTarget tests whether the navigation state transition is available in the current state.
     87 //
     88 // Fails if target is formally invalid, or if navigation is unavailable.
     89 func CheckTarget(target []byte, st *state.State) (bool, error) {
     90 	ok := valid(target)
     91 	if !ok {
     92 		return false, fmt.Errorf("invalid target: %x", target)
     93 	}
     94 
     95 	switch target[0] {
     96 	case '_':
     97 		topOk, err := st.Top()
     98 		if err != nil {
     99 			return false, err
    100 		}
    101 		return topOk, nil
    102 	case '<':
    103 		_, prevOk := st.Sides()
    104 		return prevOk, nil
    105 	case '>':
    106 		nextOk, _ := st.Sides()
    107 		return nextOk, nil
    108 	}
    109 	return true, nil
    110 }
    111 
    112 // route parsed target symbol to navigation state change method,
    113 func applyTarget(target []byte, st *state.State, ca cache.Memory, ctx context.Context) (string, uint16, error) {
    114 	var err error
    115 	sym, idx := st.Where()
    116 
    117 	ok := valid(target)
    118 	if !ok {
    119 		return sym, idx, fmt.Errorf("invalid input: %s", target)
    120 	}
    121 
    122 	switch string(target) {
    123 	case "_":
    124 		sym, err = st.Up()
    125 		if err != nil {
    126 			return sym, idx, err
    127 		}
    128 		err = ca.Pop()
    129 		if err != nil {
    130 			return sym, idx, err
    131 		}
    132 
    133 	case ">":
    134 		idx, err = st.Next()
    135 		if err != nil {
    136 			return sym, idx, err
    137 		}
    138 	case "<":
    139 		idx, err = st.Previous()
    140 		if err != nil {
    141 			return sym, idx, err
    142 		}
    143 	case "^":
    144 		notTop := true
    145 		for notTop {
    146 			notTop, err := st.Top()
    147 			if notTop {
    148 				break
    149 			}
    150 			sym, err = st.Up()
    151 			if err != nil {
    152 				return sym, idx, err
    153 			}
    154 			err = ca.Pop()
    155 			if err != nil {
    156 				return sym, idx, err
    157 			}
    158 		}
    159 	case ".":
    160 		st.Same()
    161 		location, idx := st.Where()
    162 		return location, idx, nil
    163 	default:
    164 		sym = string(target)
    165 		err := st.Down(sym)
    166 		if err != nil {
    167 			return sym, idx, err
    168 		}
    169 		err = ca.Push()
    170 		if err != nil {
    171 			return sym, idx, err
    172 		}
    173 		idx = 0
    174 	}
    175 	return sym, idx, nil
    176 }