github.com/mithrandie/csvq@v1.18.1/lib/json/path_scanner.go (about)

     1  package json
     2  
     3  import (
     4  	"bytes"
     5  )
     6  
     7  const (
     8  	PathSeparator = '.'
     9  	PathEscape    = '\\'
    10  )
    11  
    12  const EOF = -1
    13  
    14  type PathScanner struct {
    15  	src    []rune
    16  	srcPos int
    17  	offset int
    18  
    19  	column int
    20  }
    21  
    22  func (s *PathScanner) Init(src string) *PathScanner {
    23  	s.src = []rune(src)
    24  	s.srcPos = 0
    25  	s.offset = 0
    26  	s.column = 0
    27  
    28  	return s
    29  }
    30  
    31  func (s *PathScanner) peek() rune {
    32  	if len(s.src) <= s.srcPos {
    33  		return EOF
    34  	}
    35  	return s.src[s.srcPos]
    36  }
    37  
    38  func (s *PathScanner) next() rune {
    39  	ch := s.peek()
    40  	if ch == EOF {
    41  		return ch
    42  	}
    43  
    44  	s.srcPos++
    45  	s.offset++
    46  	s.column++
    47  	return ch
    48  }
    49  
    50  func (s *PathScanner) runes() []rune {
    51  	return s.src[(s.srcPos - s.offset):s.srcPos]
    52  }
    53  
    54  func (s *PathScanner) literal() string {
    55  	return string(s.runes())
    56  }
    57  
    58  func (s *PathScanner) Scan() PathToken {
    59  	s.offset = 0
    60  	ch := s.next()
    61  
    62  	token := ch
    63  	literal := string(ch)
    64  	column := s.column
    65  
    66  	switch ch {
    67  	case EOF, PathSeparator:
    68  		break
    69  	default:
    70  		s.scanObjectMember()
    71  		literal = s.unescapeObjectMember(s.literal())
    72  		token = OBJECT_PATH
    73  	}
    74  
    75  	return PathToken{Token: int(token), Literal: literal, Column: column}
    76  }
    77  
    78  func (s *PathScanner) scanObjectMember() {
    79  ScanObjectMember:
    80  	for {
    81  		switch s.peek() {
    82  		case EOF, PathSeparator:
    83  			break ScanObjectMember
    84  		case PathEscape:
    85  			s.next()
    86  		}
    87  
    88  		s.next()
    89  	}
    90  }
    91  
    92  func (s *PathScanner) unescapeObjectMember(src string) string {
    93  	runes := []rune(src)
    94  	var buf bytes.Buffer
    95  
    96  	escaped := false
    97  	for _, r := range runes {
    98  		if escaped {
    99  			switch r {
   100  			case PathSeparator, PathEscape:
   101  				buf.WriteRune(r)
   102  			default:
   103  				buf.WriteRune(PathEscape)
   104  				buf.WriteRune(r)
   105  			}
   106  			escaped = false
   107  			continue
   108  		}
   109  
   110  		if r == PathEscape {
   111  			escaped = true
   112  			continue
   113  		}
   114  
   115  		buf.WriteRune(r)
   116  	}
   117  	if escaped {
   118  		buf.WriteRune(PathEscape)
   119  	}
   120  
   121  	return buf.String()
   122  }