Skip to content

Commit 7d92d9a

Browse files
committed
wip: Add custom iterator support
TODO: NewSliceIter helper? less confusion about reference or not Not a good idea to separate function/iterator? could wrap all functions as one value iterators but might impact performance? Keep track of path? currently iterator give 0 as path remove repl or add to cli?
1 parent d2f80a2 commit 7d92d9a

File tree

8 files changed

+378
-22
lines changed

8 files changed

+378
-22
lines changed

cmd/repl/main.go

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package main
2+
3+
import (
4+
"bufio"
5+
"fmt"
6+
"io"
7+
"os"
8+
9+
"github.com/itchyny/gojq"
10+
)
11+
12+
func read(c interface{}, a []interface{}) interface{} {
13+
prompt, ok := a[0].(string)
14+
if !ok {
15+
return fmt.Errorf("%v: src is not a string", a[0])
16+
}
17+
fmt.Fprint(os.Stdout, prompt)
18+
s := bufio.NewScanner(os.Stdin)
19+
if ok = s.Scan(); !ok {
20+
fmt.Fprintln(os.Stdout)
21+
return io.EOF
22+
}
23+
24+
return s.Text()
25+
}
26+
27+
func eval(c interface{}, a []interface{}) interface{} {
28+
src, ok := a[0].(string)
29+
if !ok {
30+
return fmt.Errorf("%v: src is not a string", a[0])
31+
}
32+
iter, err := replRun(c, src)
33+
if err != nil {
34+
return err
35+
}
36+
37+
return iter
38+
}
39+
40+
func print(c interface{}, a []interface{}) interface{} {
41+
if _, err := fmt.Fprintln(os.Stdout, c); err != nil {
42+
return err
43+
}
44+
45+
return gojq.EmptyIter{}
46+
}
47+
48+
func itertest(c interface{}, a []interface{}) interface{} {
49+
return &gojq.SliceIter{Slice: []interface{}{1, 2, 3}}
50+
}
51+
52+
func itererr(c interface{}, a []interface{}) interface{} {
53+
return &gojq.SliceIter{Slice: []interface{}{1, fmt.Errorf("itervaluerr")}}
54+
}
55+
56+
type preludeLoader struct{}
57+
58+
func (preludeLoader) LoadInitModules() ([]*gojq.Query, error) {
59+
replSrc := `
60+
def repl:
61+
def _wrap: if (. | type) != "array" then [.] end;
62+
def _repl:
63+
try read("> ") as $e |
64+
(try (.[] | eval($e)) catch . | print),
65+
_repl;
66+
_wrap | _repl;
67+
`
68+
gq, err := gojq.Parse(replSrc)
69+
if err != nil {
70+
return nil, err
71+
}
72+
73+
return []*gojq.Query{gq}, nil
74+
}
75+
76+
func replRun(c interface{}, src string) (gojq.Iter, error) {
77+
gq, err := gojq.Parse(src)
78+
if err != nil {
79+
return nil, err
80+
}
81+
gc, err := gojq.Compile(gq,
82+
gojq.WithModuleLoader(preludeLoader{}),
83+
gojq.WithFunction("read", 1, 1, read),
84+
gojq.WithIterator("eval", 1, 1, eval),
85+
gojq.WithIterator("print", 0, 0, print),
86+
87+
gojq.WithIterator("itertest", 0, 0, itertest),
88+
gojq.WithIterator("itererr", 0, 0, itererr),
89+
)
90+
if err != nil {
91+
return nil, err
92+
}
93+
94+
return gc.Run(c), nil
95+
}
96+
97+
func main() {
98+
expr := "repl"
99+
if len(os.Args) > 1 {
100+
expr = os.Args[1]
101+
}
102+
iter, err := replRun(nil, expr)
103+
if err != nil {
104+
panic(err)
105+
}
106+
for {
107+
v, ok := iter.Next()
108+
if !ok {
109+
break
110+
} else if err, ok := v.(error); ok {
111+
fmt.Fprintf(os.Stderr, "err: %v\n", err)
112+
break
113+
} else if d, ok := v.([2]interface{}); ok {
114+
fmt.Fprintf(os.Stdout, "%s: %v\n", d[0], d[1])
115+
}
116+
}
117+
}

compiler.go

+14-3
Original file line numberDiff line numberDiff line change
@@ -897,6 +897,7 @@ func (c *compiler) compileFunc(e *Func) error {
897897
e.Args,
898898
nil,
899899
false,
900+
false,
900901
)
901902
case "input":
902903
if c.inputIter == nil {
@@ -907,13 +908,15 @@ func (c *compiler) compileFunc(e *Func) error {
907908
e.Args,
908909
nil,
909910
false,
911+
false,
910912
)
911913
case "modulemeta":
912914
return c.compileCallInternal(
913915
[3]interface{}{c.funcModulemeta, 0, e.Name},
914916
e.Args,
915917
nil,
916918
false,
919+
false,
917920
)
918921
default:
919922
return c.compileCall(e.Name, e.Args)
@@ -925,6 +928,7 @@ func (c *compiler) compileFunc(e *Func) error {
925928
e.Args,
926929
nil,
927930
false,
931+
fn.iterator,
928932
)
929933
}
930934
return &funcNotFoundError{e}
@@ -1294,12 +1298,13 @@ func (c *compiler) compileCall(name string, args []*Query) error {
12941298
args,
12951299
nil,
12961300
name == "_index" || name == "_slice",
1301+
false,
12971302
)
12981303
}
12991304

13001305
func (c *compiler) compileCallPc(fn *funcinfo, args []*Query) error {
13011306
if len(args) == 0 {
1302-
return c.compileCallInternal(fn.pc, args, nil, false)
1307+
return c.compileCallInternal(fn.pc, args, nil, false, false)
13031308
}
13041309
xs, vars := make([]*Query, len(args)), make(map[int]bool, len(fn.args))
13051310
for i, j := range fn.argsorder {
@@ -1308,12 +1313,15 @@ func (c *compiler) compileCallPc(fn *funcinfo, args []*Query) error {
13081313
vars[i] = true
13091314
}
13101315
}
1311-
return c.compileCallInternal(fn.pc, xs, vars, false)
1316+
return c.compileCallInternal(fn.pc, xs, vars, false, false)
13121317
}
13131318

1314-
func (c *compiler) compileCallInternal(fn interface{}, args []*Query, vars map[int]bool, indexing bool) error {
1319+
func (c *compiler) compileCallInternal(fn interface{}, args []*Query, vars map[int]bool, indexing bool, each bool) error {
13151320
if len(args) == 0 {
13161321
c.append(&code{op: opcall, v: fn})
1322+
if each {
1323+
c.append(&code{op: opeach})
1324+
}
13171325
return nil
13181326
}
13191327
idx := c.newVariable()
@@ -1367,6 +1375,9 @@ func (c *compiler) compileCallInternal(fn interface{}, args []*Query, vars map[i
13671375
}
13681376
c.append(&code{op: opload, v: idx})
13691377
c.append(&code{op: opcall, v: fn})
1378+
if each {
1379+
c.append(&code{op: opeach})
1380+
}
13701381
return nil
13711382
}
13721383

execute.go

+34-6
Original file line numberDiff line numberDiff line change
@@ -217,10 +217,18 @@ loop:
217217
break loop
218218
}
219219
backtrack = false
220+
221+
var xc [2]interface{}
222+
var xv interface{}
220223
var xs [][2]interface{}
224+
221225
switch v := env.pop().(type) {
222226
case [][2]interface{}:
223-
xs = v
227+
xc = v[0]
228+
if len(v) > 1 {
229+
xv = v[1:]
230+
}
231+
// xs = v
224232
case []interface{}:
225233
if !env.paths.empty() && (env.expdepth == 0 && !reflect.DeepEqual(v, env.paths.top().([2]interface{})[1])) {
226234
err = &invalidPathIterError{v}
@@ -229,10 +237,14 @@ loop:
229237
if len(v) == 0 {
230238
break loop
231239
}
232-
xs = make([][2]interface{}, len(v))
240+
xs := make([][2]interface{}, len(v))
233241
for i, v := range v {
234242
xs[i] = [2]interface{}{i, v}
235243
}
244+
xc = xs[0]
245+
if len(xs) > 1 {
246+
xv = xs[1:]
247+
}
236248
case map[string]interface{}:
237249
if !env.paths.empty() && (env.expdepth == 0 && !reflect.DeepEqual(v, env.paths.top().([2]interface{})[1])) {
238250
err = &invalidPathIterError{v}
@@ -250,19 +262,35 @@ loop:
250262
sort.Slice(xs, func(i, j int) bool {
251263
return xs[i][0].(string) < xs[j][0].(string)
252264
})
265+
xc = xs[0]
266+
if len(xs) > 1 {
267+
xv = xs[1:]
268+
}
269+
case Iter:
270+
iv, ok := v.Next()
271+
if !ok {
272+
break loop
273+
}
274+
if e, ok := iv.(error); ok {
275+
err = e
276+
break loop
277+
} else {
278+
xc = [2]interface{}{0, iv}
279+
xv = v
280+
}
253281
default:
254282
err = &iteratorError{v}
255283
break loop
256284
}
257-
if len(xs) > 1 {
258-
env.push(xs[1:])
285+
if xv != nil {
286+
env.push(xv)
259287
env.pushfork(code.op, pc)
260288
env.pop()
261289
}
262-
env.push(xs[0][1])
290+
env.push(xc[1])
263291
if !env.paths.empty() {
264292
if env.expdepth == 0 {
265-
env.paths.push(xs[0])
293+
env.paths.push(xc)
266294
}
267295
}
268296
case opexpbegin:

func.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const (
3030
type function struct {
3131
argcount int
3232
callback func(interface{}, []interface{}) interface{}
33+
iterator bool
3334
}
3435

3536
func (fn function) accept(cnt int) bool {
@@ -60,7 +61,7 @@ func init() {
6061
"contains": argFunc1(funcContains),
6162
"explode": argFunc0(funcExplode),
6263
"implode": argFunc0(funcImplode),
63-
"split": {argcount1 | argcount2, funcSplit},
64+
"split": {argcount1 | argcount2, funcSplit, false},
6465
"tojson": argFunc0(funcToJSON),
6566
"fromjson": argFunc0(funcFromJSON),
6667
"format": argFunc1(funcFormat),
@@ -172,9 +173,9 @@ func init() {
172173
"strptime": argFunc1(funcStrptime),
173174
"now": argFunc0(funcNow),
174175
"_match": argFunc3(funcMatch),
175-
"error": {argcount0 | argcount1, funcError},
176+
"error": {argcount0 | argcount1, funcError, false},
176177
"halt": argFunc0(funcHalt),
177-
"halt_error": {argcount0 | argcount1, funcHaltError},
178+
"halt_error": {argcount0 | argcount1, funcHaltError, false},
178179
"_type_error": argFunc1(internalfuncTypeError),
179180
}
180181
}
@@ -184,6 +185,7 @@ func argFunc0(fn func(interface{}) interface{}) function {
184185
argcount0, func(v interface{}, _ []interface{}) interface{} {
185186
return fn(v)
186187
},
188+
false,
187189
}
188190
}
189191

@@ -192,6 +194,7 @@ func argFunc1(fn func(interface{}, interface{}) interface{}) function {
192194
argcount1, func(v interface{}, args []interface{}) interface{} {
193195
return fn(v, args[0])
194196
},
197+
false,
195198
}
196199
}
197200

@@ -200,6 +203,7 @@ func argFunc2(fn func(interface{}, interface{}, interface{}) interface{}) functi
200203
argcount2, func(v interface{}, args []interface{}) interface{} {
201204
return fn(v, args[0], args[1])
202205
},
206+
false,
203207
}
204208
}
205209

@@ -208,6 +212,7 @@ func argFunc3(fn func(interface{}, interface{}, interface{}, interface{}) interf
208212
argcount3, func(v interface{}, args []interface{}) interface{} {
209213
return fn(v, args[0], args[1], args[2])
210214
},
215+
false,
211216
}
212217
}
213218

iter.go

+25
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,28 @@ func (c *unitIter) Next() (interface{}, bool) {
2121
}
2222
return nil, false
2323
}
24+
25+
// SliceIter is a Iter that iterate a interface{} slice from first to last value
26+
type SliceIter struct{ Slice []interface{} }
27+
28+
// Next value in slice or no value if at end
29+
func (i *SliceIter) Next() (interface{}, bool) {
30+
if len(i.Slice) == 0 {
31+
return nil, false
32+
}
33+
e := i.Slice[0]
34+
i.Slice = i.Slice[1:]
35+
return e, true
36+
}
37+
38+
// EmptyIter is a Iter that return no value, similar to "empty" keyword.
39+
type EmptyIter struct{}
40+
41+
// Next returns no value
42+
func (EmptyIter) Next() (interface{}, bool) { return nil, false }
43+
44+
// IterFn is a Iter that calls a provided next function
45+
type IterFn func() (interface{}, bool)
46+
47+
// Next value in slice or no value if at end
48+
func (i IterFn) Next() (interface{}, bool) { return i() }

0 commit comments

Comments
 (0)