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