jq icon indicating copy to clipboard operation
jq copied to clipboard

Likely wrong precedence of the 'as' operator

Open ksa-real opened this issue 3 years ago • 9 comments

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

ksa-real avatar Jun 23 '22 13:06 ksa-real

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 .[] | (. + (.)) -> .[] | (. + .) -> .[] | .+.

wader avatar Jun 23 '22 13:06 wader

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

ksa-real avatar Jun 23 '22 13:06 ksa-real

👍 Dont think so but there is a issue related to it https://github.com/stedolan/jq/issues/2425

wader avatar Jun 23 '22 14:06 wader

I'll go ahead and rename the issue as this is likely a precedence bug.

ksa-real avatar Jun 24 '22 05:06 ksa-real

Fixing the precedence to what you expected will break existing scripts so I'm afraid it is unlikely to be fixed.

itchyny avatar Jun 24 '22 07:06 itchyny

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 avatar Jun 24 '22 07:06 ksa-real

@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?

wader avatar Jun 24 '22 16:06 wader

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

itchyny avatar Jun 25 '22 00:06 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

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.

ksa-real avatar Jun 25 '22 08:06 ksa-real