Likely wrong precedence of the 'as' operator
Describe the bug
In the example below the numbers are squared while the output is expected to be the same as input
$ jq '.[]|.*3 as $v|.' <<<'[1, 4, 5,3]'
1
16
25
9
Similarly input is doubled but should be the same:
$ jq '.[]|.+1 as $v|.' <<<'[1, 4, 5,3]'
2
8
10
6
Environment (please complete the following information):
- OS and Version: all platforms including https://jqplay.org/
- jq version : 1.6
Not sure if correct but i think the query gets parsed as:
$ jq '.[] | (. + (1 as $tt | .))' <<<'[1, 4, 5, 3]'
2
8
10
6
which can be simplified to .[] | (. + (.)) -> .[] | (. + .) -> .[] | .+.
Wow. Thanks, indeed this is very likely. Do you know if precedence is specified somewhere in the docs? I haven't found it. I wasn't able to understand the precedence looking into https://github.com/stedolan/jq/blob/master/src/parser.y
👍 Dont think so but there is a issue related to it https://github.com/stedolan/jq/issues/2425
I'll go ahead and rename the issue as this is likely a precedence bug.
Fixing the precedence to what you expected will break existing scripts so I'm afraid it is unlikely to be fixed.
In that case, this needs to be documented. Better, provide a migration path for script authors to fix it, e.g. warning on usage. I'd assume that with the current implementation the intent to use (.*3) as $v without parenthesis is very rare and unexpected. The mentioned issue with precedence table displays as having lower precedence than arithmetic (and many other) operators and https://github.com/stedolan/jq/wiki/jq-Language-Description#operators-priority doesn't mention as at all. However, there are mentions of constructs like expr as $NAME | ... which IMO suggest that binding is a higher-level concept and therefore has lower precedence. So not recognizing .+1 as an expression in . + 1 as $v is very unexpected and every sane engineer understanding this bug probably already put the parenthesis around .+1 or in rare cases around 1 as $v.
@ksa-real If you want to play around with AST:s then fq exposes gojq:s query functions as jq functions (are _functions as they are not "public")
$ fq -n '".[]|.+1 as $v|." | _query_fromstring | ., _query_tostring'
{
"left": {
"term": {
"suffix_list": [
{
"iter": true
}
],
"type": "TermTypeIdentity"
}
},
"op": "|",
"right": {
"left": {
"term": {
"type": "TermTypeIdentity"
}
},
"op": "+",
"right": {
"term": {
"number": "1",
"suffix_list": [
{
"bind": {
"body": {
"term": {
"type": "TermTypeIdentity"
}
},
"patterns": [
{
"name": "$v"
}
]
}
}
],
"type": "TermTypeNumber"
}
}
}
}
".[] | . + 1 as $v | ."
gojq tries to be as query compitable with jq as possible, but there might be some syntax differences @itchyny?
I remember that this not precedence problem, the parser only accepts Term for binding expression.
https://github.com/stedolan/jq/blob/cff5336ec71b6fee396a95bb0e4bea365e0cd1e8/src/parser.y#L348
I remember that this not precedence problem, the parser only accepts
Termfor binding expression.https://github.com/stedolan/jq/blob/cff5336ec71b6fee396a95bb0e4bea365e0cd1e8/src/parser.y#L348
Right! The doc I referred to:
So, there's generally a cleaner way to solve most problems in jq than defining variables. Still, sometimes they do make things easier, so jq lets you define variables using
expression as $variable. All variable names start with$. Here's a slightly uglier version of the array-averaging example:length as $array_length | add / $array_length
So, doc says as expects expression on the left, not term.