`not` unlike symbolic unary operators lose precedence when capturing dot calls on unary numerics operations

60 views
Skip to first unread message

Luke Imhoff

unread,
May 13, 2017, 10:18:39 PM5/13/17
to elixir-lang-core
I noticed this when splitting `not` from the other unary operators in IntelliJ Elixir's lexer grammar (because I can only do spacing rules on lexer tokens, so I need NOT_OPERATOR separate from UNARY_OPERATOR for https://github.com/KronicDeth/intellij-elixir/issues/98 so I can have no space between the symbolic operators, but require the space after `not` as it will blend into the argument otherwise).  I'm not sure this is really a problem or not.  It's just a weird edge-case that I produced since I test unary numeric and unary non-numeric operators.

`not` is part of `unary_op_eol` (https://github.com/elixir-lang/elixir/blob/39dd31c7d96f302986579bb6938d23c9db101661/lib/elixir/src/elixir_parser.yrl#L73), which also contains `+`, `-`, `!`, `^`, `~~~`.

Let's start with unary numeric at the top-level

iex> Code.string_to_quoted("not 1")     
{:ok, {:not, [line: 1], [1]}}
iex> Code.string_to_quoted("! 1")  
{:ok, {:!, [line: 1], [1]}}

Next, let's do something weird and make the `1` act something you can do dot calls on.  This is gibberish semantically in current Elixir, but something the syntax allows.

iex> Code.string_to_quoted("not 1.(2)")
{:ok, {{:., [line: 1], [{:not, [line: 1], [1]}]}, [line: 1], [2]}}
iex> Code.string_to_quoted("! 1.(2)")  
{:ok, {{:., [line: 1], [{:!, [line: 1], [1]}]}, [line: 1], [2]}}

So, the precedence of the AST is the same: (1) `not`/`!` on `1`; (2) dot call with `2` on the result of (1).

Finally, let's try to capture the weird syntax

iex> Code.string_to_quoted("&not 1.(2)")
{:ok, {:&, [line: 1], [{:not, [line: 1], [{{:., [line: 1], [1]}, [line: 1], [2]}]}]}}
iex> Code.string_to_quoted("&! 1.(2)")  
{:ok, {:&, [line: 1], [{{:., [line: 1], [{:!, [line: 1], [1]}]}, [line: 1], [2]}]}}

So the precedence of the symbolic `!` and word `not` is no longer the same.
For `not`: (1) `1.(2)` is called; (2) `not` the  result of (1)`; (3) capture
For `!`: `!1` is called; (2) `.(2)` is called on the result of (1); (3) capture

What has happened is that `not` has lost it's "keywordness" and is moving the position of a normal function name (like `foo` below)

iex> Code.string_to_quoted("&foo 1.(2)")
{:ok, {:&, [line: 1], [{:foo, [line: 1], [{{:., [line: 1], [1]}, [line: 1], [2]}]}]}}

So, this example is gibberish.  Does it apply to non-numeric unary operations?

iex> Code.string_to_quoted("&! a.(2)")  
{:ok, {:&, [line: 1], [{:!, [line: 1], [{{:., [line: 1], [{:a, [line: 1], nil}]}, [line: 1], [2]}]}]}}
iex> Code.string_to_quoted("&not a.(2)")
{:ok, {:&, [line: 1], [{:not, [line: 1], [{{:., [line: 1], [{:a, [line: 1], nil}]}, [line: 1], [2]}]}]}}
iex(56)> Code.string_to_quoted("&foo a.(2)")
{:ok, {:&, [line: 1], [{:foo, [line: 1], [{{:., [line: 1], [{:a, [line: 1], nil}]}, [line: 1], [2]}]}]}}

So, it's all consistent there: (1) `a.(2)` is evaluated; (2) the operator/function call; and (3) it's all captured.

So, is the inconsistency in unary numeric `not` a bug in the native grammar or something to be expected?  If the unary operators should bind more tightly to numerics than non-numerics (as is my understanding of the current grammar) then `{:ok, {:&, [line: 1], [{{:., [line: 1], [{:!, [line: 1], [1]}]}, [line: 1], [2]}]}}` is correct and `not` should be `{:ok, {:&, [line: 1], [{{:., [line: 1], [{:not, [line: 1], [1]}]}, [line: 1], [2]}]}}`.

The unary numeric operations in current language semantics seem to be there for positional captures, like `&1`, so this is mostly me bringing it up because I have to change IntelliJ Elixir's grammar to take this behaviour for unary numeric `not` into account separately from symbolic unary numerics.

José Valim

unread,
May 14, 2017, 3:41:53 AM5/14/17
to elixir-l...@googlegroups.com
Thank you Luke for the detailed bug report.

I think we should fix this by making "!" behave like "not" and remove this special case from the grammar. The only unary operator that will bind tightly to numbers then will be &, which is already behaves differently from the others as it has very low precedence.

Note that @ will still bind tightly to numbers but that's rather because it has very high precedence and not a special case.

I will push a commit to master later today.


José Valim
Skype: jv.ptec
Founder and Director of R&D

--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-core+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/f5f75451-93dc-4163-bd9a-7055dab69d62%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Luke Imhoff

unread,
May 14, 2017, 12:52:49 PM5/14/17
to elixir-lang-core, jose....@plataformatec.com.br
I don't know if this effects your planned change to the grammar, but as a side-effect of `not` being treated as a normal function instead of a keyword, `not` when captured allows any arity (you may have already realized this, I did not).

For uncaptured, `not` with a different arity than 1 is a syntax error


iex> Code.string_to_quoted("not 1")    
{:ok, {:not, [line: 1], [1]}}
iex> Code.string_to_quoted("not 1, 2")
{:error, {1, "syntax error before: ", "','"}}

While captured it doesn't care

iex> Code.string_to_quoted("&not 1")  
{:ok, {:&, [line: 1], [{:not, [line: 1], [1]}]}}
iex> Code.string_to_quoted("&not 1, 2")
{:ok, {:&, [line: 1], [{:not, [line: 1], [1, 2]}]}}

This may count as a separated bug just a related depending on how it's fixed.

To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.

José Valim

unread,
May 14, 2017, 1:32:36 PM5/14/17
to Luke Imhoff, elixir-lang-core
That's a bug in how the capture of keyword operators work. It has also been fixed in master. Thank you for the report!



José Valim
Skype: jv.ptec
Founder and Director of R&D

Reply all
Reply to author
Forward
0 new messages