mirror of
https://github.com/stedolan/jq.git
synced 2024-05-11 05:55:39 +00:00
Fold operation (code/docs/test)
This commit is contained in:
11
compile.c
11
compile.c
@@ -306,6 +306,17 @@ block gen_collect(block expr) {
|
|||||||
gen_op_var_bound(LOADV, array_var));
|
gen_op_var_bound(LOADV, array_var));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
block gen_fold(const char* varname, block init, block fold) {
|
||||||
|
block loop = BLOCK(fold, gen_op_var_unbound(STOREV, varname), gen_op_simple(BACKTRACK));
|
||||||
|
return BLOCK(gen_op_simple(DUP),
|
||||||
|
init,
|
||||||
|
block_bind(gen_op_var_unbound(STOREV, varname),
|
||||||
|
BLOCK(gen_op_target(FORK, loop),
|
||||||
|
loop,
|
||||||
|
gen_op_var_unbound(LOADV, varname)),
|
||||||
|
OP_HAS_VARIABLE));
|
||||||
|
}
|
||||||
|
|
||||||
block gen_assign(block expr) {
|
block gen_assign(block expr) {
|
||||||
block result_var = block_bind(gen_op_var_unbound(STOREV, "result"),
|
block result_var = block_bind(gen_op_var_unbound(STOREV, "result"),
|
||||||
gen_noop(), OP_HAS_VARIABLE);
|
gen_noop(), OP_HAS_VARIABLE);
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ block gen_call(const char* name, block body);
|
|||||||
block gen_subexp(block a);
|
block gen_subexp(block a);
|
||||||
block gen_both(block a, block b);
|
block gen_both(block a, block b);
|
||||||
block gen_collect(block expr);
|
block gen_collect(block expr);
|
||||||
|
block gen_fold(const char* varname, block init, block body);
|
||||||
block gen_assign(block expr);
|
block gen_assign(block expr);
|
||||||
block gen_definedor(block a, block b);
|
block gen_definedor(block a, block b);
|
||||||
block gen_condbranch(block iftrue, block iffalse);
|
block gen_condbranch(block iftrue, block iffalse);
|
||||||
|
|||||||
@@ -841,7 +841,7 @@ sections:
|
|||||||
input: '{}'
|
input: '{}'
|
||||||
output: [42]
|
output: [42]
|
||||||
|
|
||||||
- title: Variables and Functions
|
- title: Advanced features
|
||||||
body: |
|
body: |
|
||||||
Variables are an absolute necessity in most programming languages, but
|
Variables are an absolute necessity in most programming languages, but
|
||||||
they're relegated to an "advanced feature" in jq.
|
they're relegated to an "advanced feature" in jq.
|
||||||
@@ -858,6 +858,10 @@ sections:
|
|||||||
(many jq functions such as `map` and `find` are in fact written
|
(many jq functions such as `map` and `find` are in fact written
|
||||||
in jq).
|
in jq).
|
||||||
|
|
||||||
|
Finally, jq has a `fold` operation, which is very powerful but a
|
||||||
|
bit tricky. Again, it's mostly used internally, to define some
|
||||||
|
useful bits of jq's standard library.
|
||||||
|
|
||||||
entries:
|
entries:
|
||||||
- title: Variables
|
- title: Variables
|
||||||
body: |
|
body: |
|
||||||
@@ -962,6 +966,39 @@ sections:
|
|||||||
- program: 'def addvalue(f): f as $x | map(. + $x); addvalue(.[0])'
|
- program: 'def addvalue(f): f as $x | map(. + $x); addvalue(.[0])'
|
||||||
input: '[[1,2],[10,20]]'
|
input: '[[1,2],[10,20]]'
|
||||||
output: ['[[1,2,1,2], [10,20,1,2]]']
|
output: ['[[1,2,1,2], [10,20,1,2]]']
|
||||||
|
|
||||||
|
- title: Fold
|
||||||
|
body: |
|
||||||
|
|
||||||
|
The `fold` syntax in jq allows you to combine all of the
|
||||||
|
results of an expression by accumulating them into a single
|
||||||
|
answer. As an example, we'll pass `[3,2,1]` to this expression:
|
||||||
|
|
||||||
|
fold 0 as $sum (.[] | $sum + .)
|
||||||
|
|
||||||
|
The variable `$sum` is first given the value `0`. The body
|
||||||
|
of the fold (i.e. `.[] | $sum + .`) is evaluated. `.[]`
|
||||||
|
produces three results, `3`, `2`, and `1`. For the first
|
||||||
|
one, `$sum + .` gives `3`.
|
||||||
|
|
||||||
|
Having produced this answer, jq backtracks to find the next
|
||||||
|
result as per usual. However, this time, `$sum` is set to
|
||||||
|
the previous value of the body, so `$sum + .` gives
|
||||||
|
`5`. After the final backtracking, `$sum + .` gives
|
||||||
|
`6`. This final value is used as the value of the entire
|
||||||
|
`fold` expression, so the above filter returns `6`.
|
||||||
|
|
||||||
|
More formally, in order to evaluate `fold INIT as $VAR
|
||||||
|
(BODY)`, jq first sets `$VAR` to the value of `INIT`. It
|
||||||
|
then runs through `BODY`. Each time `BODY` produces a value,
|
||||||
|
`$VAR` is set to that value and jq backtracks to find the
|
||||||
|
next one. When `BODY` stops producing values, the final
|
||||||
|
value of `$VAR` is the result of the entire expression.
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- program: 'fold 0 as $sum (.[] | $sum + .)'
|
||||||
|
input: '[10,2,5,3]'
|
||||||
|
output: ['20']
|
||||||
|
|
||||||
|
|
||||||
- title: Assignment
|
- title: Assignment
|
||||||
|
|||||||
1
lexer.l
1
lexer.l
@@ -49,6 +49,7 @@ struct lexer_param;
|
|||||||
"and" { return AND; }
|
"and" { return AND; }
|
||||||
"or" { return OR; }
|
"or" { return OR; }
|
||||||
"end" { return END; }
|
"end" { return END; }
|
||||||
|
"fold" { return FOLD; }
|
||||||
"//" { return DEFINEDOR; }
|
"//" { return DEFINEDOR; }
|
||||||
"|=" { return SETPIPE; }
|
"|=" { return SETPIPE; }
|
||||||
"+=" { return SETPLUS; }
|
"+=" { return SETPLUS; }
|
||||||
|
|||||||
6
parser.y
6
parser.y
@@ -58,6 +58,7 @@ struct lexer_param;
|
|||||||
%token THEN "then"
|
%token THEN "then"
|
||||||
%token ELSE "else"
|
%token ELSE "else"
|
||||||
%token ELSE_IF "elif"
|
%token ELSE_IF "elif"
|
||||||
|
%token FOLD "fold"
|
||||||
%token END "end"
|
%token END "end"
|
||||||
%token AND "and"
|
%token AND "and"
|
||||||
%token OR "or"
|
%token OR "or"
|
||||||
@@ -201,6 +202,11 @@ Term "as" '$' IDENT '|' Exp {
|
|||||||
jv_free($4);
|
jv_free($4);
|
||||||
} |
|
} |
|
||||||
|
|
||||||
|
"fold" Term "as" '$' IDENT '(' Exp ')' {
|
||||||
|
$$ = gen_fold(jv_string_value($5), $2, $7);
|
||||||
|
jv_free($5);
|
||||||
|
} |
|
||||||
|
|
||||||
"if" Exp "then" Exp ElseBody {
|
"if" Exp "then" Exp ElseBody {
|
||||||
$$ = gen_cond($2, $4, $5);
|
$$ = gen_cond($2, $4, $5);
|
||||||
} |
|
} |
|
||||||
|
|||||||
Reference in New Issue
Block a user