go-vise

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

state.go (9992B)


      1 package state
      2 
      3 import (
      4 	"fmt"
      5 	"strings"
      6 
      7 	"git.defalsify.org/vise.git/lang"
      8 )
      9 
     10 type IndexError struct {
     11 }
     12 
     13 func(err *IndexError) Error() string {
     14 	return fmt.Sprintf("already at first index")
     15 }
     16 
     17 // State holds the command stack, error condition of a unique execution session.
     18 //
     19 // It also holds cached values for all results of executed symbols.
     20 //
     21 // Cached values are linked to the command stack level it which they were loaded. When they go out of scope they are freed.
     22 //
     23 // Values must be mapped to a level in order to be available for retrieval and count towards size
     24 //
     25 // It can hold a single argument, which is freed once it is read
     26 //
     27 // Symbols are loaded with individual size limitations. The limitations apply if a load symbol is updated. Symbols may be added with a 0-value for limits, called a "sink." If mapped, the sink will consume all net remaining size allowance unused by other symbols. Only one sink may be mapped per level.
     28 //
     29 // Symbol keys do not count towards cache size limitations.
     30 //
     31 // 8 first flags are reserved.
     32 type State struct {
     33 	Code []byte // Pending bytecode to execute
     34 	ExecPath []string // Command symbols stack
     35 	BitSize uint32 // Size of (32-bit capacity) bit flag byte array
     36 	SizeIdx uint16 // Lateral page browse index in current frame
     37 	Flags []byte // Error state
     38 	Moves uint32 // Number of times navigation has been performed
     39 	Language *lang.Language // Language selector for rendering
     40 	input []byte // Last input
     41 	debug bool // Make string representation more human friendly
     42 }
     43 
     44 // number of bytes necessary to represent a bitfield of the given size.
     45 func toByteSize(BitSize uint32) uint8 {
     46 	if BitSize == 0 {
     47 		return 0
     48 	}
     49 	n := BitSize % 8
     50 	if n > 0 {
     51 		BitSize += (8 - n)
     52 	}
     53 	return uint8(BitSize / 8)
     54 }
     55 
     56 //// Retrieve the state of a state flag
     57 //func getFlag(bitIndex uint32, bitField []byte) bool {
     58 //	byteIndex := bitIndex / 8
     59 //	localBitIndex := bitIndex % 8
     60 //	b := bitField[byteIndex]
     61 //	return (b & (1 << localBitIndex)) > 0
     62 //}
     63 
     64 // NewState creates a new State object with BitSize number of error condition states in ADDITION to the 8 builtin flags.
     65 func NewState(BitSize uint32) State {
     66 	st := State{
     67 		BitSize: BitSize + 8,
     68 	}
     69 	byteSize := toByteSize(BitSize + 8)
     70 	if byteSize > 0 {
     71 		st.Flags = make([]byte, byteSize) 
     72 	} else {
     73 		st.Flags = []byte{}
     74 	}
     75 	return st
     76 }
     77 
     78 func(st State) WithDebug() State {
     79 	st.debug = true
     80 	return st
     81 }
     82 
     83 // SetFlag sets the flag at the given bit field index
     84 //
     85 // Returns true if bit state was changed.
     86 //
     87 // Fails if bitindex is out of range.
     88 func(st *State) SetFlag(bitIndex uint32) bool {
     89 	if bitIndex + 1 > st.BitSize {
     90 		panic(fmt.Sprintf("bit index %v is out of range of bitfield size %v", bitIndex, st.BitSize))
     91 	}
     92 	r := getFlag(bitIndex, st.Flags)
     93 	if r {
     94 		return false
     95 	}
     96 	byteIndex := bitIndex / 8
     97 	localBitIndex := bitIndex % 8
     98 	b := st.Flags[byteIndex] 
     99 	st.Flags[byteIndex] = b | (1 << localBitIndex)
    100 	return true
    101 }
    102 
    103 
    104 // ResetFlag resets the flag at the given bit field index.
    105 //
    106 // Returns true if bit state was changed.
    107 //
    108 // Fails if bitindex is out of range.
    109 func(st *State) ResetFlag(bitIndex uint32) bool {
    110 	if bitIndex + 1 > st.BitSize {
    111 		panic(fmt.Sprintf("bit index %v is out of range of bitfield size %v", bitIndex, st.BitSize))
    112 	}
    113 	r := getFlag(bitIndex, st.Flags)
    114 	if !r {
    115 		return false
    116 	}
    117 	byteIndex := bitIndex / 8
    118 	localBitIndex := bitIndex % 8
    119 	b := st.Flags[byteIndex] 
    120 	st.Flags[byteIndex] = b & (^(1 << localBitIndex))
    121 	return true
    122 }
    123 
    124 // GetFlag returns the state of the flag at the given bit field index.
    125 //
    126 // Fails if bit field index is out of range.
    127 func(st *State) GetFlag(bitIndex uint32) bool {
    128 	if bitIndex + 1 > st.BitSize {
    129 		panic(fmt.Sprintf("bit index %v is out of range of bitfield size %v", bitIndex, st.BitSize))
    130 	}
    131 	return getFlag(bitIndex, st.Flags)
    132 }
    133 
    134 // FlagBitSize reports the amount of bits available in the bit field index.
    135 func(st *State) FlagBitSize() uint32 {
    136 	return st.BitSize
    137 }
    138 
    139 // FlagBitSize reports the amount of bits available in the bit field index.
    140 func(st *State) FlagByteSize() uint8 {
    141 	return uint8(len(st.Flags))
    142 }
    143 
    144 // MatchFlag matches the current state of the given flag.
    145 //
    146 // The flag is specified given its bit index in the bit field.
    147 //
    148 // If invertMatch is set, a positive result will be returned if the flag is not set.
    149 func(st *State) MatchFlag(sig uint32, matchSet bool) bool {
    150 	r := st.GetFlag(sig)
    151 	return matchSet == r
    152 }
    153 
    154 // GetIndex scans a byte slice in same order as in storage, and returns the index of the first set bit.
    155 //
    156 // If the given byte slice is too small for the bit field bitsize, the check will terminate at end-of-data without error.
    157 func(st *State) GetIndex(flags []byte) bool {
    158 	var globalIndex uint32
    159 	if st.BitSize == 0 {
    160 		return false
    161 	}
    162 	if len(flags) == 0 {
    163 		return false
    164 	}
    165 	var byteIndex uint8
    166 	var localIndex uint8
    167 	l := uint8(len(flags))
    168 	var i uint32
    169 	for i = 0; i < st.BitSize; i++ {
    170 		testVal := flags[byteIndex] & (1 << localIndex)
    171 		if (testVal & st.Flags[byteIndex]) > 0 {
    172 			return true
    173 		}
    174 		globalIndex += 1
    175 		if globalIndex % 8 == 0 {
    176 			byteIndex += 1
    177 			localIndex = 0
    178 			if byteIndex > (l - 1) {
    179 				return false				
    180 			}
    181 		} else {
    182 			localIndex += 1
    183 		}
    184 	}
    185 	return false
    186 }
    187 
    188 // Where returns the current active rendering symbol.
    189 func(st *State) Where() (string, uint16) {
    190 	if len(st.ExecPath) == 0 {
    191 		return "", 0
    192 	}
    193 	l := len(st.ExecPath)
    194 	return st.ExecPath[l-1], st.SizeIdx
    195 }
    196 
    197 // Next moves to the next sink page index.
    198 func(st *State) Next() (uint16, error) {
    199 	if len(st.ExecPath) == 0 {
    200 		return 0, fmt.Errorf("state root node not yet defined")
    201 	}
    202 	st.SizeIdx += 1
    203 	s, idx := st.Where()
    204 	Logg.Debugf("next page", "location", s, "index", idx)
    205 	st.Moves += 1
    206 	return st.SizeIdx, nil
    207 }
    208 
    209 func(st *State) Same() {
    210 	st.Moves += 1
    211 }
    212 
    213 // Previous moves to the next sink page index.
    214 //
    215 // Fails if try to move beyond index 0.
    216 func(st *State) Previous() (uint16, error) {
    217 	if len(st.ExecPath) == 0 {
    218 		return 0, fmt.Errorf("state root node not yet defined")
    219 	}
    220 	if st.SizeIdx == 0 {
    221 		return 0, &IndexError{} // ("already at first index")
    222 	}
    223 	st.SizeIdx -= 1
    224 	s, idx := st.Where()
    225 	Logg.Debugf("previous page", "location", s, "index", idx)
    226 	st.Moves += 1
    227 	return st.SizeIdx, nil
    228 }
    229 
    230 // Sides informs the caller which index page options will currently succeed.
    231 //
    232 // 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.
    233 func(st *State) Sides() (bool, bool) {
    234 	if len(st.ExecPath) == 0 {
    235 		return false, false
    236 	}
    237 	next := true
    238 	Logg.Tracef("sides", "index", st.SizeIdx)
    239 	if st.SizeIdx == 0 {
    240 		return next, false	
    241 	}
    242 	return next, true
    243 }
    244 
    245 // Top returns true if currently at topmode node.
    246 //
    247 // Fails if first Down() was never called.
    248 func(st *State) Top() (bool, error) {
    249 	if len(st.ExecPath) == 0 {
    250 		return false, fmt.Errorf("state root node not yet defined")
    251 	}
    252 	return len(st.ExecPath) == 1, nil
    253 }
    254 
    255 // Down adds the given symbol to the command stack.
    256 //
    257 // Clears mapping and sink.
    258 func(st *State) Down(input string) error {
    259 	st.ExecPath = append(st.ExecPath, input)
    260 	st.SizeIdx = 0
    261 	st.Moves += 1
    262 	return nil
    263 }
    264 
    265 // Up removes the latest symbol to the command stack, and make the previous symbol current.
    266 //
    267 // Frees all symbols and associated values loaded at the previous stack level. Cache capacity is increased by the corresponding amount.
    268 //
    269 // Clears mapping and sink.
    270 //
    271 // Fails if called at top frame.
    272 func(st *State) Up() (string, error) {
    273 	l := len(st.ExecPath)
    274 	if l == 0 {
    275 		return "", fmt.Errorf("exit called beyond top frame")
    276 	}
    277 	Logg.Tracef("execpath before", "path", st.ExecPath)
    278 	st.ExecPath = st.ExecPath[:l-1]
    279 	sym := ""
    280 	if len(st.ExecPath) > 0 {
    281 		sym = st.ExecPath[len(st.ExecPath)-1]
    282 	}
    283 	st.SizeIdx = 0
    284 	Logg.Tracef("execpath after", "path", st.ExecPath)
    285 	st.Moves += 1
    286 	return sym, nil
    287 }
    288 
    289 // Depth returns the current call stack depth.
    290 func(st *State) Depth() uint8 {
    291 	return uint8(len(st.ExecPath)-1)
    292 }
    293 
    294 // Appendcode adds the given bytecode to the end of the existing code.
    295 func(st *State) AppendCode(b []byte) error {
    296 	st.Code = append(st.Code, b...)
    297 	Logg.Debugf("code changed (append)", "code", b)
    298 	return nil
    299 }
    300 
    301 // SetCode replaces the current bytecode with the given bytecode.
    302 func(st *State) SetCode(b []byte) {
    303 	Logg.Debugf("code changed (set)", "code", b)
    304 	st.Code = b
    305 }
    306 
    307 // Get the remaning cached bytecode
    308 func(st *State) GetCode() ([]byte, error) {
    309 	b := st.Code
    310 	st.Code = []byte{}
    311 	return b, nil
    312 }
    313 
    314 // GetInput gets the most recent client input.
    315 func(st *State) GetInput() ([]byte, error) {
    316 	if st.input == nil {
    317 		return nil, fmt.Errorf("no input has been set")
    318 	}
    319 	return st.input, nil
    320 }
    321 
    322 // SetInput is used to record the latest client input.
    323 func(st *State) SetInput(input []byte) error {
    324 	l := len(input)
    325 	if l > 255 {
    326 		return fmt.Errorf("input size %v too large (limit %v)", l, 255)
    327 	}
    328 	st.input = input
    329 	return nil
    330 }
    331 
    332 // Reset re-initializes the state to run from top node with accumulated client state.
    333 func(st *State) Restart() error {
    334 	st.resetBaseFlags()
    335 	st.Moves = 0
    336 	st.SizeIdx = 0
    337 	st.input = []byte{}
    338 	return nil
    339 }
    340 
    341 // SetLanguage validates and sets language according to the given ISO639 language code.
    342 func(st *State) SetLanguage(code string) error {
    343 	if code == "" {
    344 		st.Language = nil
    345 	}
    346 	l, err := lang.LanguageFromCode(code)
    347 	if err != nil {
    348 		return err
    349 	}
    350 	st.Language = &l
    351 	Logg.Infof("language set", "language", l)
    352 	return nil
    353 }
    354 
    355 // String implements String interface
    356 func(st State) String() string {
    357 	var flags string
    358 	if st.debug {
    359 		flags = FlagDebugger.AsString(st.Flags, st.BitSize - 8)
    360 	} else {
    361 		flags = fmt.Sprintf("0x%x", st.Flags)
    362 	}
    363 	var lang string
    364 	if st.Language == nil {
    365 		lang = "(default)"
    366 	} else {
    367 		lang = fmt.Sprintf("%s", *st.Language)
    368 	}
    369 	return fmt.Sprintf("moves: %v idx: %v flags: %s path: %s lang: %s", st.Moves, st.SizeIdx, flags, strings.Join(st.ExecPath, "/"), lang)
    370 }
    371 
    372 // initializes all flags not in control of client.
    373 func(st *State) resetBaseFlags() {
    374 	st.Flags[0] = 0
    375 }
    376