elixir-lang / tree-sitter-elixir

Elixir grammar for tree-sitter
https://elixir-lang.org/tree-sitter-elixir
Apache License 2.0
245 stars 24 forks source link

binary_operator `<>` is not consistent with other operators #45

Closed wkirschbaum closed 1 year ago

wkirschbaum commented 1 year ago

in effort to build indentation using tree-sitter i ran into the following issue

with the following code outputs the desired structure

    1 +
      2 +
      3 +
      4

with output ( value in [] )

(binary_operator
    left: 
     (binary_operator
      left: (binary_operator left: (integer [1] ) operator: + right: (integer [2] ))
      operator: + right: (integer [3]))
    operator: + right: (integer [4]))

but with the following structure

    "1" <>
      "2" <>
        "3" <>
          "4"

with output ( value in [] )

(binary_operator
    left: (string quoted_end: " (quoted_content ["1"]) ")
    operator: <>
    right: 
     (binary_operator
      left: (string quoted_end: " (quoted_content ["2"]) ")
      operator: <>
      right: 
       (binary_operator
        left: (string quoted_end: " (quoted_conten ["3"]t) ")
        operator: <>
        right: (string quoted_end: " (quoted_content ["4"]) "))))

The left and right operators are swapped when using <> which make it hard to consistently parse and indent the two examples the same way.

the-mikedavis commented 1 year ago

This is expected: some operators are left-associative while others are right-associative. You can see which is which in the binary_operator rule in the source - prec.left is left-associative and prec.right is right-associative: https://github.com/elixir-lang/tree-sitter-elixir/blob/b20eaa75565243c50be5e35e253d8beb58f45d56/grammar.js#L454-L503

These mirror the associativities declared in the Elixir parser: https://github.com/elixir-lang/elixir/blob/1f452aafb0534d8453378060edcc4871f4f3fd75/lib/elixir/src/elixir_parser.yrl#L70-L88

I'm not sure how the queries work in the Emacs tree-sitter indentation mechanism but it should be possible to tell nested left- and right-associative structures apart using queries. You could match on each case for the nesting to tell them apart:

; for example: 1 * 2 + 3 * 4 
(binary_operator
  left: (binary_operator)
  right: (binary_operator))

; nested left-associative
(binary_operator
  left: (binary_operator))

; nested right-associative
(binary_operator
  right: (binary_operator))

; not nested
(binary_operator)
wkirschbaum commented 1 year ago

@the-mikedavis thanks for the response. I did not click that the associativity is different, but makes sense. You are right, it is possible to query the difference.