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 }