]> granicus.if.org Git - jq/commitdiff
Fold operation (code/docs/test)
authorStephen Dolan <mu@netsoc.tcd.ie>
Fri, 28 Dec 2012 15:04:16 +0000 (15:04 +0000)
committerStephen Dolan <mu@netsoc.tcd.ie>
Fri, 28 Dec 2012 15:05:34 +0000 (15:05 +0000)
compile.c
compile.h
docs/content/3.manual/manual.yml
lexer.l
parser.y
testdata

index 139b64bc2e34ce50e91064fcbe4c512a0a59d724..178b046344370195d6a06db41c9fe20e05141b9d 100644 (file)
--- a/compile.c
+++ b/compile.c
@@ -306,6 +306,17 @@ block gen_collect(block expr) {
                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 result_var = block_bind(gen_op_var_unbound(STOREV, "result"),
                                 gen_noop(), OP_HAS_VARIABLE);
index e74afce49638ecfbb096dca44ea8327c98d3f040..ce1b308968d2a9a85887536e305d8f9edfbe6078 100644 (file)
--- a/compile.h
+++ b/compile.h
@@ -33,6 +33,7 @@ block gen_call(const char* name, block body);
 block gen_subexp(block a);
 block gen_both(block a, block b);
 block gen_collect(block expr);
+block gen_fold(const char* varname, block init, block body);
 block gen_assign(block expr);
 block gen_definedor(block a, block b);
 block gen_condbranch(block iftrue, block iffalse);
index fd91ef13495cb736f1535fd141de69fb9f0b8602..1de1a387b6cfc28ad5f4d91e973def1363286f55 100644 (file)
@@ -841,7 +841,7 @@ sections:
             input: '{}'
             output: [42]
           
-  - title: Variables and Functions
+  - title: Advanced features
     body: |
       Variables are an absolute necessity in most programming languages, but
       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
       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:
       - title: Variables
         body: |
@@ -962,6 +966,39 @@ sections:
           - program: 'def addvalue(f): f as $x | map(. + $x); addvalue(.[0])'
             input: '[[1,2],[10,20]]'
             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
diff --git a/lexer.l b/lexer.l
index 12851de889fe69cbbcf514a661ff80efb21705f7..fd0e120e36a2d36618c8ebd5f49e8772a42cdade 100644 (file)
--- a/lexer.l
+++ b/lexer.l
@@ -49,6 +49,7 @@ struct lexer_param;
 "and" { return AND; }
 "or" { return OR; }
 "end" { return END; }
+"fold" { return FOLD; }
 "//" { return DEFINEDOR; }
 "|=" { return SETPIPE; }
 "+=" { return SETPLUS; }
index 397982d62690ca67fd3630aa9b1712d5f9b1582a..5cc2640f24b9496795d8fba2a4cb25a03d34e43a 100644 (file)
--- a/parser.y
+++ b/parser.y
@@ -58,6 +58,7 @@ struct lexer_param;
 %token THEN "then"
 %token ELSE "else"
 %token ELSE_IF "elif"
+%token FOLD "fold"
 %token END "end"
 %token AND "and"
 %token OR "or"
@@ -201,6 +202,11 @@ Term "as" '$' IDENT '|' Exp {
   jv_free($4);
 } |
 
+"fold" Term "as" '$' IDENT '(' Exp ')' {
+  $$ = gen_fold(jv_string_value($5), $2, $7);
+  jv_free($5);
+} |
+
 "if" Exp "then" Exp ElseBody {
   $$ = gen_cond($2, $4, $5);
 } |
index c3352af459f76ffbb64192593c2e85f39ed53d32..bb8e677a9553f8d53af81b9664e00137c51a57bf 100644 (file)
--- a/testdata
+++ b/testdata
@@ -331,6 +331,10 @@ def fac: if . == 1 then 1 else . * (. - 1 | fac) end; [.[] | fac]
 # []
 # 1001
 
+fold 0 as $s (.[] | $s + .)
+[1,2,4]
+7
+
 #
 # Assignment
 #