Skip to content

Commit 5a863bf

Browse files
committedJul 7, 2014
Add foreach and limit
1 parent d0ca11d commit 5a863bf

File tree

7 files changed

+88
-0
lines changed

7 files changed

+88
-0
lines changed
 

‎builtin.c

+1
Original file line numberDiff line numberDiff line change
@@ -963,6 +963,7 @@ static const char* const jq_builtins[] = {
963963
" def _while: "
964964
" if cond then ., (update | _while) else empty end; "
965965
" _while;",
966+
"def limit(n; exp): if n < 0 then exp else foreach exp as $item ([n, null]; if .[0] < 1 then empty else [.[0] -1, $item] end; .[1]) end;",
966967
};
967968
#undef LIBM_DD
968969

‎compile.c

+35
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,41 @@ block gen_reduce(const char* varname, block source, block init, block body) {
374374
gen_op_bound(LOADVN, res_var));
375375
}
376376

377+
block gen_foreach(const char* varname, block source, block init, block update, block extract) {
378+
block output = gen_op_targetlater(JUMP);
379+
block state_var = gen_op_var_fresh(STOREV, "foreach");
380+
block loop = BLOCK(gen_op_simple(DUP),
381+
// get a value from the source expression:
382+
source,
383+
// bind the $varname to that value for all the code in
384+
// this block_bind() to see:
385+
block_bind(gen_op_unbound(STOREV, varname),
386+
// load the loop state variable
387+
BLOCK(gen_op_bound(LOADVN, state_var),
388+
// generate updated state
389+
update,
390+
// save the updated state for value extraction
391+
gen_op_simple(DUP),
392+
// save new state
393+
gen_op_bound(STOREV, state_var),
394+
// extract an output...
395+
extract,
396+
// ...and output it
397+
output),
398+
OP_HAS_VARIABLE));
399+
block foreach = BLOCK(gen_op_simple(DUP),
400+
init,
401+
state_var,
402+
gen_op_target(FORK, loop),
403+
loop,
404+
// At this point `foreach`'s input will be on
405+
// top of the stack, and we don't want to output
406+
// it, so we backtrack.
407+
gen_op_simple(BACKTRACK));
408+
inst_set_target(output, foreach);
409+
return foreach;
410+
}
411+
377412
block gen_definedor(block a, block b) {
378413
// var found := false
379414
block found_var = gen_op_var_fresh(STOREV, "found");

‎compile.h

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ block gen_subexp(block a);
3232
block gen_both(block a, block b);
3333
block gen_collect(block expr);
3434
block gen_reduce(const char* varname, block source, block init, block body);
35+
block gen_foreach(const char* varname, block source, block init, block update, block extract);
3536
block gen_definedor(block a, block b);
3637
block gen_condbranch(block iftrue, block iffalse);
3738
block gen_and(block a, block b);

‎docs/content/3.manual/manual.yml

+36
Original file line numberDiff line numberDiff line change
@@ -1777,6 +1777,42 @@ sections:
17771777
input: '[10,2,5,3]'
17781778
output: ['20']
17791779

1780+
- title: `limit(n; exp)`
1781+
body: |
1782+
1783+
The `limit` function extracts up to `n` outputs from `exp`.
1784+
1785+
examples:
1786+
- program: '[limit(3;.[])]'
1787+
input: '[0,1,2,3,4,5,6,7,8,9]'
1788+
output: ['[0,1,2]']
1789+
1790+
- title: `foreach`
1791+
body: |
1792+
1793+
The `foreach` syntax is similar to `reduce`, but intended to
1794+
allow the construction of `limit` and reducers that produce
1795+
intermediate results (see example).
1796+
1797+
The form is `foreach EXP as $var (INIT; UPDATE; EXTRACT)`.
1798+
Like `reduce`, `INIT` is evaluated once to produce a state
1799+
value, then each output of `EXP` is bound to `$var`, `UPDATE`
1800+
is evaluated for each output of `EXP` with the current state
1801+
and with `$var` visible. Each value output by `UPDATE`
1802+
replaces the previous state. Finally, `EXTRACT` is evaluated
1803+
for each new state to extract an output of `foreach`.
1804+
1805+
This is mostly useful only for constructing `reduce`- and
1806+
`limit`-like functions.
1807+
1808+
examples:
1809+
- program: '[foreach .[] as $item
1810+
([[],[]];
1811+
if $item == null then [[],.[0]] else [(.[0] + [$item]),[]] end;
1812+
if $item == null then .[1] else empty end)]'
1813+
input: '[1,2,3,4,null,"a","b",null]'
1814+
output: ['[[1,2,3,4],["a","b"]]']
1815+
17801816
- title: Recursion
17811817
body: |
17821818

‎lexer.l

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ struct lexer_param;
5151
"or" { return OR; }
5252
"end" { return END; }
5353
"reduce" { return REDUCE; }
54+
"foreach" { return FOREACH; }
5455
"//" { return DEFINEDOR; }
5556
"try" { return TRY; }
5657
"catch" { return CATCH; }

‎parser.y

+6
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ struct lexer_param;
6363
%token ELSE "else"
6464
%token ELSE_IF "elif"
6565
%token REDUCE "reduce"
66+
%token FOREACH "foreach"
6667
%token END "end"
6768
%token AND "and"
6869
%token OR "or"
@@ -234,6 +235,11 @@ Term "as" '$' IDENT '|' Exp {
234235
jv_free($5);
235236
} |
236237

238+
"foreach" Term "as" '$' IDENT '(' Exp ';' Exp ';' Exp ')' {
239+
$$ = gen_foreach(jv_string_value($5), $2, $7, $9, $11);
240+
jv_free($5);
241+
} |
242+
237243
"if" Exp "then" Exp ElseBody {
238244
$$ = gen_cond($2, $4, $5);
239245
} |

‎tests/all.test

+8
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,14 @@ null
226226
1
227227
[1,2,4,8,16,32,64]
228228

229+
[foreach .[] as $item ([3, null]; if .[0] < 1 then empty else [.[0] -1, $item] end; .[1])]
230+
[11,22,33,44,55,66,77,88,99]
231+
[11,22,33]
232+
233+
[limit(3; .[])]
234+
[11,22,33,44,55,66,77,88,99]
235+
[11,22,33]
236+
229237
#
230238
# Slices
231239
#

0 commit comments

Comments
 (0)
Please sign in to comment.