Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

builtin.jq: whilst #2776

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 21 additions & 6 deletions docs/content/manual/manual.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1819,22 +1819,37 @@ sections:
input: '"useful but not for é"'
output: ['"USEFUL BUT NOT FOR é"']

- title: "`while(cond; update)`"
- title: "`while(cond; update)`, `whilst(cond; update)`"
body: |

The `while(cond; update)` function allows you to repeatedly
These two functions allow you to repeatedly
apply an update to `.` until `cond` is false.

Note that `while(cond; update)` is internally defined as a
recursive jq function. Recursive calls within `while` will
not consume additional memory if `update` produces at most one
output for each input. See advanced topics below.
`while(cond; update)` begins by emitting its input
unconditionally, and then applies `update` to that value; if
the updated value exists and is truthy (i.e. neither `false`
nor `null`), then that value is applied to `while(cond;
update)` recursively.

`whilst(cond; update)` is similar but begins by applying
`update` to the input without emitting it; if the updated
value exists and is truthy, it is output, and then applied
to `whilst(cond; update)` recursively.

Note that both `while` and `whilst` are defined as recursive
jq functions. Recursive calls within these functions will
not consume additional memory if `update` produces at most
one output for each input. See advanced topics below.

examples:
- program: '[while(.<100; .*2)]'
input: '1'
output: ['[1,2,4,8,16,32,64]']

- program: '[whilst(.<100; .*2)]'
input: '1'
output: ['[2,4,8,16,32,64,128]']

- title: "`repeat(exp)`"
body: |

Expand Down
17 changes: 14 additions & 3 deletions jq.1.prebuilt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 11 additions & 6 deletions src/builtin.jq
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,18 @@ def gsub($re; s): sub($re; s; "g");
########################################################################
# generic iterator/generator
def while(cond; update):
def _while:
if cond then ., (update | _while) else empty end;
_while;
def _while:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to define a code style for jq. I've become very happy with 4-space indentation except for 2 spaces for |, like this:

    .
  | stuff

I think until we decide on a style for jq code, maybe it'd be best to not restyle existing jq code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was just trying to ensure while/whilst/until were consistently formatted, admittedly in the 2-space style -- two spaces saves space-time, no? I thought that was important for builtin.jq ...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing style of surrounding code to be more consistent is something I like to do -though it can be irritating because it clutters diffs and complicates use of git blame-, but in this case I'm not sure what style we ought to adopt, and my own jq-coding style has change over time, so for src/builtin.jq I would prefer we don't gratuitously re-style until we decide on a style. Conversely, I'm not asking you to adopt a style for jq code for now either -- if eventually we have to restyle all of src/builtin.jq, so be it.

(For C code in jq the style is fairly consistent already, and very much in @stedolan's C style.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nicowilliams - In case you haven't noticed, @itchyny's builtin.jq in gojq uses the two-space style :-)

https://github.com/itchyny/gojq/blob/main/builtin.jq

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pkoppstein no, I've not noticed. It's on my list to eventually go take a look at fq, jqjq, gojq, rq, and others, but my time is limited. What they do for style is interesting, but what they do for semantics is much more interesting as we want jq-alikes to interoperate. As for style, like I said, my own jq style has evolved and I think we should discuss that separately and not gratuitously restyle src/builtin.jq for the time being even though I otherwise would prefer that we do restyle as we go.

if cond then ., (update | _while) else empty end;
_while;
# like while/2 but emit the final term rather than the first one
def whilst(cond; update):
def _whilst: if cond then update | (., _whilst) else empty end;
_whilst;
def until(cond; next):
def _until:
if cond then . else (next|_until) end;
_until;
def _until:
if cond then . else (next|_until) end;
_until;

def limit($n; exp):
if $n > 0 then label $out | foreach exp as $item ($n; .-1; $item, if . <= 0 then break $out else empty end)
elif $n == 0 then empty
Expand Down
4 changes: 4 additions & 0 deletions tests/jq.test
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,10 @@ null
1
[1,2,4,8,16,32,64]

[whilst(.<100; .*2)]
1
[2,4,8,16,32,64,128]

[(label $here | .[] | if .>1 then break $here else . end), "hi!"]
[0,1,2]
[0,1,"hi!"]
Expand Down
4 changes: 4 additions & 0 deletions tests/man.test

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.