Skip to content

Commit 18a752f

Browse files
committed
ArbitraryIndex
1 parent 1ce70d1 commit 18a752f

File tree

2 files changed

+221
-0
lines changed

2 files changed

+221
-0
lines changed

Diff for: arbitrary_index.go

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package jq
2+
3+
import (
4+
"fmt"
5+
6+
"nikand.dev/go/cbor"
7+
)
8+
9+
type (
10+
ArbitraryIndex struct {
11+
Base Filter
12+
Index Filter
13+
NoErr bool
14+
15+
lastl Off
16+
lnext, rnext bool
17+
18+
lastp NodePath
19+
}
20+
)
21+
22+
func NewIndex(base, index Filter) *ArbitraryIndex {
23+
return &ArbitraryIndex{Base: base, Index: index}
24+
}
25+
26+
func NewIndexNoErr(base, index Filter) *ArbitraryIndex {
27+
return &ArbitraryIndex{Base: base, Index: index, NoErr: true}
28+
}
29+
30+
func (f *ArbitraryIndex) ApplyToGetPath(b *Buffer, off Off, base NodePath, next bool) (res Off, path NodePath, more bool, err error) {
31+
return f.applyTo(b, off, base, next, true, f.NoErr)
32+
}
33+
34+
func (f *ArbitraryIndex) ApplyTo(b *Buffer, off Off, next bool) (res Off, more bool, err error) {
35+
res, _, more, err = f.applyTo(b, off, nil, next, false, f.NoErr)
36+
return
37+
}
38+
39+
func (f *ArbitraryIndex) applyTo(b *Buffer, off Off, base NodePath, next bool, addpath addpath, null bool) (res Off, path NodePath, more bool, err error) {
40+
if !next {
41+
f.lastl = None
42+
f.lnext = false
43+
f.rnext = false
44+
f.lastp = f.lastp[:0]
45+
} else if !f.lnext && !f.rnext {
46+
return None, base, false, nil
47+
}
48+
49+
path = base
50+
51+
for !next || f.lnext || f.rnext {
52+
next = true
53+
54+
if !f.rnext {
55+
if addpath {
56+
f.lastl, path, f.lnext, err = ApplyGetPath(f.Base, b, off, path[:len(base)], f.lnext)
57+
} else {
58+
f.lastl, f.lnext, err = f.Base.ApplyTo(b, off, f.lnext)
59+
}
60+
if err != nil {
61+
return None, path, false, err
62+
}
63+
64+
if f.lastl == None {
65+
continue
66+
}
67+
68+
f.lastp = append(f.lastp[:0], path[len(base):]...)
69+
} else if addpath {
70+
path = append(path[:len(base)], f.lastp...)
71+
}
72+
73+
res, f.rnext, err = f.Index.ApplyTo(b, off, f.rnext)
74+
if err != nil {
75+
return None, path, false, err
76+
}
77+
78+
if res == None {
79+
continue
80+
}
81+
82+
break
83+
}
84+
85+
if f.lastl == None || res == None {
86+
return None, path[:len(base)], false, nil
87+
}
88+
89+
br := b.Reader()
90+
tag := br.Tag(res)
91+
92+
switch tag {
93+
case cbor.Int, cbor.Neg:
94+
idx := br.Int(res)
95+
96+
res, path, err = indexApplyTo(idx, b, f.lastl, path, false, addpath, null)
97+
case cbor.Bytes, cbor.String:
98+
key := br.Bytes(res)
99+
100+
res, path, err = keyApplyTo(string(key), b, f.lastl, path, false, addpath, null, res)
101+
default:
102+
return None, base, false, NewTypeError(tag, cbor.Int, cbor.Neg, cbor.Bytes, cbor.String)
103+
}
104+
if err != nil {
105+
return None, path[:len(base)], false, err
106+
}
107+
108+
more = f.lnext || f.rnext
109+
110+
return res, path, more, nil
111+
}
112+
113+
func (f ArbitraryIndex) String() string {
114+
return fmt.Sprintf(".(%v)[%v]%s", f.Base, f.Index, csel(f.NoErr, "?", ""))
115+
}

Diff for: arbitrary_index_test.go

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package jq
2+
3+
import "testing"
4+
5+
func TestArbitraryIndex(tb *testing.T) {
6+
b := NewBuffer()
7+
r0 := b.appendVal(obj{
8+
"arr",
9+
arr{"a", "b", "c", "d"},
10+
"idx",
11+
arr{0, 1, 3, -1, -3, -4, -100, 100},
12+
})
13+
14+
testIter(tb, NewIndex(NewQuery("arr"), NewQuery("idx", Iter{})), b, r0, []any{
15+
"a", "b", "d", "d", "b", "a", Null, Null,
16+
})
17+
}
18+
19+
func TestArbitraryKey(tb *testing.T) {
20+
b := NewBuffer()
21+
r1 := b.appendVal(obj{
22+
"a",
23+
arr{
24+
obj{"a", 1, "b", 2},
25+
obj{"a", 3, "b", 4},
26+
},
27+
"b",
28+
arr{"a", "b"},
29+
})
30+
31+
testIter(tb, NewIndex(NewQuery("a", Iter{}), NewQuery("b", Iter{})), b, r1, []any{
32+
1, 2, 3, 4,
33+
})
34+
}
35+
36+
func TestArbitraryIndexPath(tb *testing.T) {
37+
b := NewBuffer()
38+
39+
arrkey := b.appendVal("arr")
40+
a0 := b.appendVal("a")
41+
b0 := b.appendVal("b")
42+
c0 := b.appendVal("c")
43+
d0 := b.appendVal("d")
44+
arr0 := b.appendVal(arr{a0, b0, c0, d0})
45+
46+
r0 := b.appendVal(obj{
47+
arrkey,
48+
arr0,
49+
"idx",
50+
arr{0, 1, 3, -1, -3, -4, -100, 100},
51+
})
52+
53+
testIterPath(tb, NewIndex(NewQuery("arr"), NewQuery("idx", Iter{})), b, r0, []any{
54+
"a", "b", "d", "d", "b", "a", Null, Null,
55+
}, []NodePath{
56+
{psk(r0, 0, arrkey), ps(arr0, 0)},
57+
{psk(r0, 0, arrkey), ps(arr0, 1)},
58+
{psk(r0, 0, arrkey), ps(arr0, 3)},
59+
{psk(r0, 0, arrkey), ps(arr0, 3)},
60+
{psk(r0, 0, arrkey), ps(arr0, 1)},
61+
{psk(r0, 0, arrkey), ps(arr0, 0)},
62+
{psk(r0, 0, arrkey), ps(arr0, -100)},
63+
{psk(r0, 0, arrkey), ps(arr0, 100)},
64+
})
65+
66+
if tb.Failed() {
67+
tb.Logf("dump %v\n%s", r0, b.Dump())
68+
}
69+
}
70+
71+
func TestArbitraryKeyPath(tb *testing.T) {
72+
b := NewBuffer()
73+
74+
akey := b.appendVal("a")
75+
bkey := b.appendVal("b")
76+
ckey := b.appendVal("c")
77+
78+
obj0 := b.appendVal(obj{akey, 1, bkey, 2})
79+
obj1 := b.appendVal(obj{akey, 3, bkey, 4})
80+
81+
arr0 := b.appendVal(arr{obj0, obj1})
82+
83+
r0 := b.appendVal(obj{
84+
bkey,
85+
arr{akey, bkey, ckey},
86+
akey,
87+
arr0,
88+
})
89+
90+
testIterPath(tb, NewIndex(NewQuery("a", Iter{}), NewQuery("b", Iter{})), b, r0, []any{
91+
1, 2, Null,
92+
3, 4, Null,
93+
}, []NodePath{
94+
{psk(r0, 1, akey), ps(arr0, 0), psk(obj0, 0, akey)},
95+
{psk(r0, 1, akey), ps(arr0, 0), psk(obj0, 1, bkey)},
96+
{psk(r0, 1, akey), ps(arr0, 0), psk(obj0, -1, ckey)},
97+
98+
{psk(r0, 1, akey), ps(arr0, 1), psk(obj1, 0, akey)},
99+
{psk(r0, 1, akey), ps(arr0, 1), psk(obj1, 1, bkey)},
100+
{psk(r0, 1, akey), ps(arr0, 1), psk(obj1, -1, ckey)},
101+
})
102+
103+
if tb.Failed() {
104+
tb.Logf("dump %v\n%s", r0, b.Dump())
105+
}
106+
}

0 commit comments

Comments
 (0)