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  }