jq
jq copied to clipboard
Fix unary operator precedence, allow some expressions as object values
Currently, the precedence of the unary operator - is too low so it causes some unintuitive results. For example, as I pointed out in https://github.com/jqlang/jq/issues/2704#issuecomment-1639596368, [-1 as $x | 1,$x] results in [-1,-1] in jq 1.7 (and all previous versions), while most of us will expect to see [1,-1]. When I noticed the behavior a few years ago, I was thinking we should keep the behavior for compatibility, but the issue #2704 was created and convinced me that we should fix the behavior. Actually, We can fix by simply changing the unary operator to be parsed as a part of Term. However, this fix disallows to use the unary operator before reduce, foreach, if, and try expressions. So I would like to make these expression to Term as well, to keep supporting unary operator before them. This change allows these expression as object values; something like { x: if . then 1 else 2 end, y: try error catch ., z: reduce range(3) as $x (0; . + $x) }. Also allows us to use the unary operator in reduce expressions; reduce -.[] as $x (...). I think the changes are unlikely to break existing scripts, but let me clarify what I intentionally dropped supporting; unary operator before function declaration, and labels; [-def f: 1; f], [-label $x | 1].
Seems like an improvement and fair compromise. Also nice bonus to allow more things as object literal values, have annoyed me a few times. The parser change looks fine to me but i'm that experience with yacc.
@itchyny would it be possible to allow most binops also (except ,)? {a: .b // 1}, {a: 1 + 2} etc
@wader We'll need to duplicate yacc rules for Exp and ExpD, but yes technically that's possible.
We could also add a special rule to allow foo?//bar since ?+// and ?// are never both accepted in the same context.
If not too messy to implement i think it would be nice "quality of life" and consistency improvement. I guessing new users find it confusing that [1+2] works but not {a: 1+2}
Now I improved the parser rules to allow binary operator expressions as object values. It wasn't messy than I thought.
$ ./jq -n '{x: 1 + 2, y: false or true, z: null // 3}'
{
"x": 3,
"y": true,
"z": 3
}
I have just noticed that this patch also makes reduce ... as $x (...; ...)[] work.
Before, you had to write it as reduce ... as $x (...; ...) | .[].
Awesome!
Also the parens previously required for (foreach ...) as $x and (reduce ... ) as $x can now be dropped!
For example:
./jq -n 'reduce range(0;1) as $x([]; .+[$x]) as $x | $x'
@itchyny nice work! really like this. coming to gojq soon? :)
Had a quick look at jaq and xq's suppor for object value expressions. jaq seems to already support it (maybe @01mf02 can confirm?) but xq seem to not support it (@MiSawa maybe something to look into?). And jqjq has a wip branch to support it.
Also fixes:
$ jq -n '[1,2,3]?[0]'
jq: error: syntax error, unexpected '[', expecting end of file (Unix shell quoting issues?) at <top-level>, line 1:
[1,2,3]?[0]
jq: 1 compile error
$ gojq -n '[1,2,3]?[0]'
1
Add regression test for it?
@wader, just for reference, in jaq, the predence of negation (-) is already what @itchyny proposed, so of course, I'm all for adopting this. ;) Furthermore, jaq indeed already supports object value expressions. That means that you get:
$ jaq -n '[-1 as $x | 1,$x]'
[
1,
-1
]
$ jaq -n '{x: 1 + 2, y: false or true, z: null // 3}'
{
"x": 3,
"y": true,
"z": 3
}
$ jaq -n 'reduce range(0;1) as $x([]; .+[$x]) as $x | $x'
[
0
]
On the other hand, jaq currently cannot handle reduce range(0;1) as $x([]; .+[$x])[] and [1,2,3]?[0]; however, my new parser (WIP) can handle it.
Yes, let's include this.