visitor.go raw

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