Skip to content

Commit

Permalink
Merge branch 'main' into feat/go121
Browse files Browse the repository at this point in the history
  • Loading branch information
liuq19 authored Aug 9, 2023
2 parents 0bdd095 + cd1ed4d commit bdd5049
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 54 deletions.
69 changes: 53 additions & 16 deletions ast/iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,26 +82,54 @@ type ObjectIterator struct {
Iterator
}

func (self *ListIterator) next() *Node {
next_start:
if !self.HasNext() {
return nil
} else {
n := self.p.nodeAt(self.i)
self.i++
if !n.Exists() {
goto next_start
}
return n
}
}

// Next scans through children of underlying V_ARRAY,
// copies each child to v, and returns .HasNext().
func (self *ListIterator) Next(v *Node) bool {
if !self.HasNext() {
n := self.next()
if n == nil {
return false
}
*v = *n
return true
}

func (self *ObjectIterator) next() *Pair {
next_start:
if !self.HasNext() {
return nil
} else {
*v, self.i = *self.p.nodeAt(self.i), self.i + 1
return true
n := self.p.pairAt(self.i)
self.i++
if !n.Value.Exists() {
goto next_start
}
return n
}
}

// Next scans through children of underlying V_OBJECT,
// copies each child to v, and returns .HasNext().
func (self *ObjectIterator) Next(p *Pair) bool {
if !self.HasNext() {
n := self.next()
if n == nil {
return false
} else {
*p, self.i = *self.p.pairAt(self.i), self.i + 1
return true
}
*p = *n
return true
}

// Sequence represents scanning path of single-layer nodes.
Expand Down Expand Up @@ -129,30 +157,39 @@ type Scanner func(path Sequence, node *Node) bool
//
// Especailly, if the node is not V_ARRAY or V_OBJECT,
// the node itself will be returned and Sequence.Index == -1.
//
// NOTICE: A unsetted node WON'T trigger sc, but its index still counts into Path.Index
func (self *Node) ForEach(sc Scanner) error {
switch self.itype() {
case types.V_ARRAY:
ns, err := self.unsafeArray()
iter, err := self.Values()
if err != nil {
return err
}
for i:=0; i<ns.Len(); i++ {
if !sc(Sequence{i, nil}, ns.At(i)) {
return err
v := iter.next()
for v != nil {
if !sc(Sequence{iter.i-1, nil}, v) {
return nil
}
v = iter.next()
}
case types.V_OBJECT:
ns, err := self.unsafeMap()
iter, err := self.Properties()
if err != nil {
return err
}
for i:=0; i<ns.Len(); i++ {
if !sc(Sequence{i, &ns.At(i).Key}, &ns.At(i).Value) {
return err
v := iter.next()
for v != nil {
if !sc(Sequence{iter.i-1, &v.Key}, &v.Value) {
return nil
}
v = iter.next()
}
default:
if self.Check() != nil {
return self
}
sc(Sequence{-1, nil}, self)
}
return self.Check()
return nil
}
74 changes: 70 additions & 4 deletions ast/iterator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@
package ast

import (
`fmt`
`strconv`
`testing`
"fmt"
"strconv"
"testing"

`github.com/stretchr/testify/assert`
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func getTestIteratorSample(loop int) (string, int) {
Expand Down Expand Up @@ -207,6 +208,71 @@ func TestIterator(t *testing.T) {
}
}

func TestExist(t *testing.T) {
n := NewRaw(`null`)
if !n.Exists() {
t.Fatal()
}
nn := n.Get("xx")
if nn.Exists() {
t.Fatal()
}

root := NewRaw(`{"a":1, "b":[1,2], "c":{"1":1, "2":2}}`)
if !root.Exists() {
t.Fatal()
}
exi, err := root.Unset("a")
if !exi || err != nil {
t.Fatal(exi, err)
}

root.ForEach(func(path Sequence, node *Node) bool {
if path.Key != nil && *path.Key == "a" {
t.Fatal()
}
if path.Index == 1 {
if *path.Key != "b" {
t.Fatal()
}
exi, err := node.UnsetByIndex(1)
if !exi || err != nil {
t.Fatal(exi, err)
}
node.ForEach(func(path Sequence, node *Node) bool {
if path.Index == 1 {
t.Fatal()
}
return true
})
}

if path.Index == 2 {
if *path.Key != "c" {
t.Fatal()
}
exi, err := node.UnsetByIndex(1)
if !exi || err != nil {
t.Fatal(exi, err)
}
node.ForEach(func(path Sequence, node *Node) bool {
if path.Index == 1 {
t.Fatal()
}
return true
})
}
return true
})

out, err := root.Raw()
if err != nil {
t.Fatal(err)
}
require.Equal(t, `{"b":[1],"c":{"1":1}}`, out)

}

func BenchmarkArrays(b *testing.B) {
for i:=0;i<b.N;i++{
root,err := NewSearcher(_TwitterJson).GetByPath("statuses",1,"entities","hashtags")
Expand Down
13 changes: 8 additions & 5 deletions ast/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ func (self *Node) UnmarshalJSON(data []byte) (err error) {

// Type returns json type represented by the node
// It will be one of belows:
// V_NONE = 0 (empty node)
// V_NONE = 0 (empty node, key not exists)
// V_ERROR = 1 (error node)
// V_NULL = 2 (json value `null`)
// V_NULL = 2 (json value `null`, key exists)
// V_TRUE = 3 (json value `true`)
// V_FALSE = 4 (json value `false`)
// V_ARRAY = 5 (json value array)
Expand All @@ -102,7 +102,7 @@ func (self Node) itype() types.ValueType {

// Exists returns false only if the self is nil or empty node V_NONE
func (self *Node) Exists() bool {
return self != nil && self.t != _V_NONE
return self.Valid() && self.t != _V_NONE
}

// Valid reports if self is NOT V_ERROR or nil
Expand All @@ -114,7 +114,7 @@ func (self *Node) Valid() bool {
}

// Check checks if the node itself is valid, and return:
// - ErrNotFound If the node is nil
// - ErrNotExist If the node is nil
// - Its underlying error If the node is V_ERROR
func (self *Node) Check() error {
if self == nil {
Expand Down Expand Up @@ -572,7 +572,7 @@ func (self *Node) SetAny(key string, val interface{}) (bool, error) {
return self.Set(key, NewAny(val))
}

// Unset remove the node of given key under object parent, and reports if the key has existed.
// Unset RESET the node of given key under object parent, and reports if the key has existed.
// WARN: After conducting `UnsetXX()`, the node's length WON'T change
func (self *Node) Unset(key string) (bool, error) {
self.must(types.V_OBJECT, "an object")
Expand Down Expand Up @@ -619,6 +619,9 @@ func (self *Node) UnsetByIndex(index int) (bool, error) {
if it == types.V_ARRAY {
p = self.Index(index)
}else if it == types.V_OBJECT {
if err := self.checkRaw(); err != nil {
return false, err
}
pr := self.skipIndexPair(index)
if pr == nil {
return false, ErrNotExist
Expand Down
3 changes: 3 additions & 0 deletions ast/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ const (
)

var (
// ErrNotExist means both key and value doesn't exist
ErrNotExist error = newError(_ERR_NOT_FOUND, "value not exists")

// ErrUnsupportType means API on the node is unsupported
ErrUnsupportType error = newError(_ERR_UNSUPPORT_TYPE, "unsupported type")
)

Expand Down
66 changes: 38 additions & 28 deletions internal/decoder/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,45 +44,55 @@ func (self SyntaxError) Description() string {
}

func (self SyntaxError) description() string {
i := 16
p := self.Pos - i
q := self.Pos + i

/* check for empty source */
if self.Src == "" {
return fmt.Sprintf("no sources available: %#v", self)
}

p, x, q, y := calcBounds(len(self.Src), self.Pos)

/* compose the error description */
return fmt.Sprintf(
"at index %d: %s\n\n\t%s\n\t%s^%s\n",
self.Pos,
self.Message(),
self.Src[p:q],
strings.Repeat(".", x),
strings.Repeat(".", y),
)
}

func calcBounds(size int, pos int) (lbound int, lwidth int, rbound int, rwidth int) {
if pos >= size || pos < 0 {
return 0, 0, size, 0
}

i := 16
lbound = pos - i
rbound = pos + i

/* prevent slicing before the beginning */
if p < 0 {
p, q, i = 0, q - p, i + p
if lbound < 0 {
lbound, rbound, i = 0, rbound - lbound, i + lbound
}

/* prevent slicing beyond the end */
if n := len(self.Src); q > n {
n = q - n
q = len(self.Src)
if n := size; rbound > n {
n = rbound - n
rbound = size

/* move the left bound if possible */
if p > n {
if lbound > n {
i += n
p -= n
lbound -= n
}
}

/* left and right length */
x := clamp_zero(i)
y := clamp_zero(q - p - i - 1)
lwidth = clamp_zero(i)
rwidth = clamp_zero(rbound - lbound - i - 1)

/* compose the error description */
return fmt.Sprintf(
"at index %d: %s\n\n\t%s\n\t%s^%s\n",
self.Pos,
self.Message(),
self.Src[p:q],
strings.Repeat(".", x),
strings.Repeat(".", y),
)
return
}

func (self SyntaxError) Message() string {
Expand All @@ -107,16 +117,19 @@ var stackOverflow = &json.UnsupportedValueError {
Value : reflect.ValueOf("..."),
}

//go:nosplit
func error_wrap(src string, pos int, code types.ParsingError) error {
return SyntaxError {
return *error_wrap_heap(src, pos, code)
}

//go:noinline
func error_wrap_heap(src string, pos int, code types.ParsingError) *SyntaxError {
return &SyntaxError {
Pos : pos,
Src : src,
Code : code,
}
}

//go:nosplit
func error_type(vt *rt.GoType) error {
return &json.UnmarshalTypeError{Type: vt.Pack()}
}
Expand Down Expand Up @@ -158,7 +171,6 @@ func (self MismatchTypeError) Description() string {
return fmt.Sprintf("Mismatch type %s with value %s %s", self.Type.String(), swithchJSONType(self.Src, self.Pos), se.description())
}

//go:nosplit
func error_mismatch(src string, pos int, vt *rt.GoType) error {
return &MismatchTypeError {
Pos : pos,
Expand All @@ -167,12 +179,10 @@ func error_mismatch(src string, pos int, vt *rt.GoType) error {
}
}

//go:nosplit
func error_field(name string) error {
return errors.New("json: unknown field " + strconv.Quote(name))
}

//go:nosplit
func error_value(value string, vtype reflect.Type) error {
return &json.UnmarshalTypeError {
Type : vtype,
Expand Down
2 changes: 1 addition & 1 deletion internal/decoder/errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,4 @@ func TestErrors_ShortDescription(t *testing.T) {

func TestErrors_EmptyDescription(t *testing.T) {
println(make_err("", 0).Description())
}
}
Loading

0 comments on commit bdd5049

Please sign in to comment.