github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/examples/gno.land/p/demo/json/buffer.gno (about) 1 package json 2 3 import ( 4 "errors" 5 "io" 6 "strings" 7 8 "gno.land/p/demo/ufmt" 9 ) 10 11 type buffer struct { 12 data []byte 13 length int 14 index int 15 16 last States 17 state States 18 class Classes 19 } 20 21 // newBuffer creates a new buffer with the given data 22 func newBuffer(data []byte) *buffer { 23 return &buffer{ 24 data: data, 25 length: len(data), 26 last: GO, 27 state: GO, 28 } 29 } 30 31 // first retrieves the first non-whitespace (or other escaped) character in the buffer. 32 func (b *buffer) first() (byte, error) { 33 for ; b.index < b.length; b.index++ { 34 c := b.data[b.index] 35 36 if !(c == whiteSpace || c == carriageReturn || c == newLine || c == tab) { 37 return c, nil 38 } 39 } 40 41 return 0, io.EOF 42 } 43 44 // current returns the byte of the current index. 45 func (b *buffer) current() (byte, error) { 46 if b.index >= b.length { 47 return 0, io.EOF 48 } 49 50 return b.data[b.index], nil 51 } 52 53 // next moves to the next byte and returns it. 54 func (b *buffer) next() (byte, error) { 55 b.index++ 56 return b.current() 57 } 58 59 // step just moves to the next position. 60 func (b *buffer) step() error { 61 _, err := b.next() 62 return err 63 } 64 65 // move moves the index by the given position. 66 func (b *buffer) move(pos int) error { 67 newIndex := b.index + pos 68 69 if newIndex > b.length { 70 return io.EOF 71 } 72 73 b.index = newIndex 74 75 return nil 76 } 77 78 // slice returns the slice from the current index to the given position. 79 func (b *buffer) slice(pos int) ([]byte, error) { 80 end := b.index + pos 81 82 if end > b.length { 83 return nil, io.EOF 84 } 85 86 return b.data[b.index:end], nil 87 } 88 89 // sliceFromIndices returns a slice of the buffer's data starting from 'start' up to (but not including) 'stop'. 90 func (b *buffer) sliceFromIndices(start, stop int) []byte { 91 if start > b.length { 92 start = b.length 93 } 94 95 if stop > b.length { 96 stop = b.length 97 } 98 99 return b.data[start:stop] 100 } 101 102 // skip moves the index to skip the given byte. 103 func (b *buffer) skip(bs byte) error { 104 for b.index < b.length { 105 if b.data[b.index] == bs && !b.backslash() { 106 return nil 107 } 108 109 b.index++ 110 } 111 112 return io.EOF 113 } 114 115 // skipAny moves the index until it encounters one of the given set of bytes. 116 func (b *buffer) skipAny(endTokens map[byte]bool) error { 117 for b.index < b.length { 118 if _, exists := endTokens[b.data[b.index]]; exists { 119 return nil 120 } 121 122 b.index++ 123 } 124 125 // build error message 126 var tokens []string 127 for token := range endTokens { 128 tokens = append(tokens, string(token)) 129 } 130 131 return ufmt.Errorf( 132 "EOF reached before encountering one of the expected tokens: %s", 133 strings.Join(tokens, ", "), 134 ) 135 } 136 137 // skipAndReturnIndex moves the buffer index forward by one and returns the new index. 138 func (b *buffer) skipAndReturnIndex() (int, error) { 139 err := b.step() 140 if err != nil { 141 return 0, err 142 } 143 144 return b.index, nil 145 } 146 147 // skipUntil moves the buffer index forward until it encounters a byte contained in the endTokens set. 148 func (b *buffer) skipUntil(endTokens map[byte]bool) (int, error) { 149 for b.index < b.length { 150 currentByte, err := b.current() 151 if err != nil { 152 return b.index, err 153 } 154 155 // Check if the current byte is in the set of end tokens. 156 if _, exists := endTokens[currentByte]; exists { 157 return b.index, nil 158 } 159 160 b.index++ 161 } 162 163 return b.index, io.EOF 164 } 165 166 // significantTokens is a map where the keys are the significant characters in a JSON path. 167 // The values in the map are all true, which allows us to use the map as a set for quick lookups. 168 var significantTokens = map[byte]bool{ 169 dot: true, // access properties of an object 170 dollarSign: true, // root object 171 atSign: true, // current object 172 bracketOpen: true, // start of an array index or filter expression 173 bracketClose: true, // end of an array index or filter expression 174 } 175 176 // filterTokens stores the filter expression tokens. 177 var filterTokens = map[byte]bool{ 178 aesterisk: true, // wildcard 179 andSign: true, 180 orSign: true, 181 } 182 183 // skipToNextSignificantToken advances the buffer index to the next significant character. 184 // Significant characters are defined based on the JSON path syntax. 185 func (b *buffer) skipToNextSignificantToken() { 186 for b.index < b.length { 187 current := b.data[b.index] 188 189 if _, ok := significantTokens[current]; ok { 190 break 191 } 192 193 b.index++ 194 } 195 } 196 197 // backslash checks to see if the number of backslashes before the current index is odd. 198 // 199 // This is used to check if the current character is escaped. However, unlike the "unescape" function, 200 // "backslash" only serves to check the number of backslashes. 201 func (b *buffer) backslash() bool { 202 if b.index == 0 { 203 return false 204 } 205 206 count := 0 207 for i := b.index - 1; ; i-- { 208 if i >= b.length || b.data[i] != backSlash { 209 break 210 } 211 212 count++ 213 214 if i == 0 { 215 break 216 } 217 } 218 219 return count%2 != 0 220 } 221 222 // numIndex holds a map of valid numeric characters 223 var numIndex = map[byte]bool{ 224 '0': true, 225 '1': true, 226 '2': true, 227 '3': true, 228 '4': true, 229 '5': true, 230 '6': true, 231 '7': true, 232 '8': true, 233 '9': true, 234 '.': true, 235 'e': true, 236 'E': true, 237 } 238 239 // pathToken checks if the current token is a valid JSON path token. 240 func (b *buffer) pathToken() error { 241 var stack []byte 242 243 inToken := false 244 inNumber := false 245 first := b.index 246 247 for b.index < b.length { 248 c := b.data[b.index] 249 250 switch { 251 case c == doubleQuote || c == singleQuote: 252 inToken = true 253 if err := b.step(); err != nil { 254 return errors.New("error stepping through buffer") 255 } 256 257 if err := b.skip(c); err != nil { 258 return errors.New("unmatched quote in path") 259 } 260 261 if b.index >= b.length { 262 return errors.New("unmatched quote in path") 263 } 264 265 case c == bracketOpen || c == parenOpen: 266 inToken = true 267 stack = append(stack, c) 268 269 case c == bracketClose || c == parenClose: 270 inToken = true 271 if len(stack) == 0 || (c == bracketClose && stack[len(stack)-1] != bracketOpen) || (c == parenClose && stack[len(stack)-1] != parenOpen) { 272 return errors.New("mismatched bracket or parenthesis") 273 } 274 275 stack = stack[:len(stack)-1] 276 277 case pathStateContainsValidPathToken(c): 278 inToken = true 279 280 case c == plus || c == minus: 281 if inNumber || (b.index > 0 && numIndex[b.data[b.index-1]]) { 282 inToken = true 283 } else if !inToken && (b.index+1 < b.length && numIndex[b.data[b.index+1]]) { 284 inToken = true 285 inNumber = true 286 } else if !inToken { 287 return errors.New("unexpected operator at start of token") 288 } 289 290 default: 291 if len(stack) != 0 || inToken { 292 inToken = true 293 } else { 294 goto end 295 } 296 } 297 298 b.index++ 299 } 300 301 end: 302 if len(stack) != 0 { 303 return errors.New("unclosed bracket or parenthesis at end of path") 304 } 305 306 if first == b.index { 307 return errors.New("no token found") 308 } 309 310 if inNumber && !numIndex[b.data[b.index-1]] { 311 inNumber = false 312 } 313 314 return nil 315 } 316 317 func pathStateContainsValidPathToken(c byte) bool { 318 if _, ok := significantTokens[c]; ok { 319 return true 320 } 321 322 if _, ok := filterTokens[c]; ok { 323 return true 324 } 325 326 if _, ok := numIndex[c]; ok { 327 return true 328 } 329 330 if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' { 331 return true 332 } 333 334 return false 335 } 336 337 func (b *buffer) numeric(token bool) error { 338 if token { 339 b.last = GO 340 } 341 342 for ; b.index < b.length; b.index++ { 343 b.class = b.getClasses(doubleQuote) 344 if b.class == __ { 345 return errors.New("invalid token found while parsing path") 346 } 347 348 b.state = StateTransitionTable[b.last][b.class] 349 if b.state == __ { 350 if token { 351 break 352 } 353 354 return errors.New("invalid token found while parsing path") 355 } 356 357 if b.state < __ { 358 return nil 359 } 360 361 if b.state < MI || b.state > E3 { 362 return nil 363 } 364 365 b.last = b.state 366 } 367 368 if b.last != ZE && b.last != IN && b.last != FR && b.last != E3 { 369 return errors.New("invalid token found while parsing path") 370 } 371 372 return nil 373 } 374 375 func (b *buffer) getClasses(c byte) Classes { 376 if b.data[b.index] >= 128 { 377 return C_ETC 378 } 379 380 if c == singleQuote { 381 return QuoteAsciiClasses[b.data[b.index]] 382 } 383 384 return AsciiClasses[b.data[b.index]] 385 } 386 387 func (b *buffer) getState() States { 388 b.last = b.state 389 390 b.class = b.getClasses(doubleQuote) 391 if b.class == __ { 392 return __ 393 } 394 395 b.state = StateTransitionTable[b.last][b.class] 396 397 return b.state 398 } 399 400 // string parses a string token from the buffer. 401 func (b *buffer) string(search byte, token bool) error { 402 if token { 403 b.last = GO 404 } 405 406 for ; b.index < b.length; b.index++ { 407 b.class = b.getClasses(search) 408 409 if b.class == __ { 410 return errors.New("invalid token found while parsing path") 411 } 412 413 b.state = StateTransitionTable[b.last][b.class] 414 if b.state == __ { 415 return errors.New("invalid token found while parsing path") 416 } 417 418 if b.state < __ { 419 break 420 } 421 422 b.last = b.state 423 } 424 425 return nil 426 } 427 428 func (b *buffer) word(bs []byte) error { 429 var c byte 430 431 max := len(bs) 432 index := 0 433 434 for ; b.index < b.length; b.index++ { 435 c = b.data[b.index] 436 437 if c != bs[index] { 438 return errors.New("invalid token found while parsing path") 439 } 440 441 index++ 442 if index >= max { 443 break 444 } 445 } 446 447 if index != max { 448 return errors.New("invalid token found while parsing path") 449 } 450 451 return nil 452 } 453 454 func numberKind2f64(value interface{}) (result float64, err error) { 455 switch typed := value.(type) { 456 case float64: 457 result = typed 458 case float32: 459 result = float64(typed) 460 case int: 461 result = float64(typed) 462 case int8: 463 result = float64(typed) 464 case int16: 465 result = float64(typed) 466 case int32: 467 result = float64(typed) 468 case int64: 469 result = float64(typed) 470 case uint: 471 result = float64(typed) 472 case uint8: 473 result = float64(typed) 474 case uint16: 475 result = float64(typed) 476 case uint32: 477 result = float64(typed) 478 case uint64: 479 result = float64(typed) 480 default: 481 err = ufmt.Errorf("invalid number type: %T", value) 482 } 483 484 return 485 }