github.com/goshafaq/sonic@v0.0.0-20231026082336-871835fb94c6/ast/visitor.go (about) 1 /* 2 * Copyright 2021 ByteDance Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package ast 18 19 import ( 20 "encoding/json" 21 22 "github.com/goshafaq/sonic/internal/native/types" 23 ) 24 25 // Visitor handles the callbacks during preorder traversal of a JSON AST. 26 // 27 // According to the JSON RFC8259, a JSON AST can be defined by 28 // the following rules without seperator / whitespace tokens. 29 // 30 // JSON-AST = value 31 // value = false / null / true / object / array / number / string 32 // object = begin-object [ member *( member ) ] end-object 33 // member = string value 34 // array = begin-array [ value *( value ) ] end-array 35 type Visitor interface { 36 37 // OnNull handles a JSON null value. 38 OnNull() error 39 40 // OnBool handles a JSON true / false value. 41 OnBool(v bool) error 42 43 // OnString handles a JSON string value. 44 OnString(v string) error 45 46 // OnInt64 handles a JSON number value with int64 type. 47 OnInt64(v int64, n json.Number) error 48 49 // OnFloat64 handles a JSON number value with float64 type. 50 OnFloat64(v float64, n json.Number) error 51 52 // OnObjectBegin handles the beginning of a JSON object value with a 53 // suggested capacity that can be used to make your custom object container. 54 // 55 // After this point the visitor will receive a sequence of callbacks like 56 // [string, value, string, value, ......, ObjectEnd]. 57 // 58 // Note: 59 // 1. This is a recursive definition which means the value can 60 // also be a JSON object / array described by a sequence of callbacks. 61 // 2. The suggested capacity will be 0 if current object is empty. 62 // 3. Currently sonic use a fixed capacity for non-empty object (keep in 63 // sync with ast.Node) which might not be very suitable. This may be 64 // improved in future version. 65 OnObjectBegin(capacity int) error 66 67 // OnObjectKey handles a JSON object key string in member. 68 OnObjectKey(key string) error 69 70 // OnObjectEnd handles the ending of a JSON object value. 71 OnObjectEnd() error 72 73 // OnArrayBegin handles the beginning of a JSON array value with a 74 // suggested capacity that can be used to make your custom array container. 75 // 76 // After this point the visitor will receive a sequence of callbacks like 77 // [value, value, value, ......, ArrayEnd]. 78 // 79 // Note: 80 // 1. This is a recursive definition which means the value can 81 // also be a JSON object / array described by a sequence of callbacks. 82 // 2. The suggested capacity will be 0 if current array is empty. 83 // 3. Currently sonic use a fixed capacity for non-empty array (keep in 84 // sync with ast.Node) which might not be very suitable. This may be 85 // improved in future version. 86 OnArrayBegin(capacity int) error 87 88 // OnArrayEnd handles the ending of a JSON array value. 89 OnArrayEnd() error 90 } 91 92 // VisitorOptions contains all Visitor's options. The default value is an 93 // empty VisitorOptions{}. 94 type VisitorOptions struct { 95 // OnlyNumber indicates parser to directly return number value without 96 // conversion, then the first argument of OnInt64 / OnFloat64 will always 97 // be zero. 98 OnlyNumber bool 99 } 100 101 var defaultVisitorOptions = &VisitorOptions{} 102 103 // Preorder decodes the whole JSON string and callbacks each AST node to visitor 104 // during preorder traversal. Any visitor method with an error returned will 105 // break the traversal and the given error will be directly returned. The opts 106 // argument can be reused after every call. 107 func Preorder(str string, visitor Visitor, opts *VisitorOptions) error { 108 if opts == nil { 109 opts = defaultVisitorOptions 110 } 111 // process VisitorOptions first to guarantee that all options will be 112 // constant during decoding and make options more readable. 113 var ( 114 optDecodeNumber = !opts.OnlyNumber 115 ) 116 117 tv := &traverser{ 118 parser: Parser{ 119 s: str, 120 noLazy: true, 121 skipValue: false, 122 }, 123 visitor: visitor, 124 } 125 126 if optDecodeNumber { 127 tv.parser.decodeNumber(true) 128 } 129 130 err := tv.decodeValue() 131 132 if optDecodeNumber { 133 tv.parser.decodeNumber(false) 134 } 135 return err 136 } 137 138 type traverser struct { 139 parser Parser 140 visitor Visitor 141 } 142 143 // NOTE: keep in sync with (*Parser).Parse method. 144 func (self *traverser) decodeValue() error { 145 switch val := self.parser.decodeValue(); val.Vt { 146 case types.V_EOF: 147 return types.ERR_EOF 148 case types.V_NULL: 149 return self.visitor.OnNull() 150 case types.V_TRUE: 151 return self.visitor.OnBool(true) 152 case types.V_FALSE: 153 return self.visitor.OnBool(false) 154 case types.V_STRING: 155 return self.decodeString(val.Iv, val.Ep) 156 case types.V_DOUBLE: 157 return self.visitor.OnFloat64(val.Dv, 158 json.Number(self.parser.s[val.Ep:self.parser.p])) 159 case types.V_INTEGER: 160 return self.visitor.OnInt64(val.Iv, 161 json.Number(self.parser.s[val.Ep:self.parser.p])) 162 case types.V_ARRAY: 163 return self.decodeArray() 164 case types.V_OBJECT: 165 return self.decodeObject() 166 default: 167 return types.ParsingError(-val.Vt) 168 } 169 } 170 171 // NOTE: keep in sync with (*Parser).decodeArray method. 172 func (self *traverser) decodeArray() error { 173 sp := self.parser.p 174 ns := len(self.parser.s) 175 176 /* check for EOF */ 177 self.parser.p = self.parser.lspace(sp) 178 if self.parser.p >= ns { 179 return types.ERR_EOF 180 } 181 182 /* check for empty array */ 183 if self.parser.s[self.parser.p] == ']' { 184 self.parser.p++ 185 if err := self.visitor.OnArrayBegin(0); err != nil { 186 return err 187 } 188 return self.visitor.OnArrayEnd() 189 } 190 191 /* allocate array space and parse every element */ 192 if err := self.visitor.OnArrayBegin(_DEFAULT_NODE_CAP); err != nil { 193 return err 194 } 195 for { 196 /* decode the value */ 197 if err := self.decodeValue(); err != nil { 198 return err 199 } 200 self.parser.p = self.parser.lspace(self.parser.p) 201 202 /* check for EOF */ 203 if self.parser.p >= ns { 204 return types.ERR_EOF 205 } 206 207 /* check for the next character */ 208 switch self.parser.s[self.parser.p] { 209 case ',': 210 self.parser.p++ 211 case ']': 212 self.parser.p++ 213 return self.visitor.OnArrayEnd() 214 default: 215 return types.ERR_INVALID_CHAR 216 } 217 } 218 } 219 220 // NOTE: keep in sync with (*Parser).decodeObject method. 221 func (self *traverser) decodeObject() error { 222 sp := self.parser.p 223 ns := len(self.parser.s) 224 225 /* check for EOF */ 226 self.parser.p = self.parser.lspace(sp) 227 if self.parser.p >= ns { 228 return types.ERR_EOF 229 } 230 231 /* check for empty object */ 232 if self.parser.s[self.parser.p] == '}' { 233 self.parser.p++ 234 if err := self.visitor.OnObjectBegin(0); err != nil { 235 return err 236 } 237 return self.visitor.OnObjectEnd() 238 } 239 240 /* allocate object space and decode each pair */ 241 if err := self.visitor.OnObjectBegin(_DEFAULT_NODE_CAP); err != nil { 242 return err 243 } 244 for { 245 var njs types.JsonState 246 var err types.ParsingError 247 248 /* decode the key */ 249 if njs = self.parser.decodeValue(); njs.Vt != types.V_STRING { 250 return types.ERR_INVALID_CHAR 251 } 252 253 /* extract the key */ 254 idx := self.parser.p - 1 255 key := self.parser.s[njs.Iv:idx] 256 257 /* check for escape sequence */ 258 if njs.Ep != -1 { 259 if key, err = unquote(key); err != 0 { 260 return err 261 } 262 } 263 264 if err := self.visitor.OnObjectKey(key); err != nil { 265 return err 266 } 267 268 /* expect a ':' delimiter */ 269 if err = self.parser.delim(); err != 0 { 270 return err 271 } 272 273 /* decode the value */ 274 if err := self.decodeValue(); err != nil { 275 return err 276 } 277 278 self.parser.p = self.parser.lspace(self.parser.p) 279 280 /* check for EOF */ 281 if self.parser.p >= ns { 282 return types.ERR_EOF 283 } 284 285 /* check for the next character */ 286 switch self.parser.s[self.parser.p] { 287 case ',': 288 self.parser.p++ 289 case '}': 290 self.parser.p++ 291 return self.visitor.OnObjectEnd() 292 default: 293 return types.ERR_INVALID_CHAR 294 } 295 } 296 } 297 298 // NOTE: keep in sync with (*Parser).decodeString method. 299 func (self *traverser) decodeString(iv int64, ep int) error { 300 p := self.parser.p - 1 301 s := self.parser.s[iv:p] 302 303 /* fast path: no escape sequence */ 304 if ep == -1 { 305 return self.visitor.OnString(s) 306 } 307 308 /* unquote the string */ 309 out, err := unquote(s) 310 if err != 0 { 311 return err 312 } 313 return self.visitor.OnString(out) 314 }