7

I can't understand precedence of Ruby operators in a following example:

x = 1 && y = 2

Since && has higher precedence than =, my understanding is that similarly to + and * operators:

1 + 2 * 3 + 4

which is resolved as

1 + (2 * 3) + 4

it should be equal to:

x = (1 && y) = 2

However, all Ruby sources (including internal syntax parser Ripper) parse this as

x = (1 && (y = 2))

Why?


EDIT [08.01.2016]

Let's focus on a subexpression: 1 && y = 2

According to precedence rules, we should try to parse it as:

(1 && y) = 2

which doesn't make sense because = requires a specific LHS (variable, constant, [] array item etc). But since (1 && y) is a correct expression, how should the parser deal with it?

I've tried consulting with Ruby's parse.y, but it's so spaghetti-like that I can't understand specific rules for assignment.

Michael
  • 215
  • 1
  • 6

2 Answers2

2

Simple. Ruby only interprets it in a way that makes sense. = is assignment. In your expectation:

x = (1 && y) = 2

it does not make sense to assign something to 1 && y. You can only assign something to a variable or a constant.

And note that the purpose of the precedence rule is to disambiguate an otherwise ambiguous expression. If one way to interpret it does not make sense, then there is no need for disambiguation, and hence the precedence rule would not be in effect.

sawa
  • 165,429
  • 45
  • 277
  • 381
  • 4
    Parser uses rules, it doesn't have an intelligence to differentiate what makes sense and what doesn't, so your answer is uninformative. – Rustam Gasanov Jan 07 '16 at 17:04
  • @RustamA.Gasanov Checking whether the left hand side of assignment is a variable or a constant does not require intelligence. It can be done without executing the code. – sawa Jan 07 '16 at 17:06
  • I am actually trying to write a Ruby parser in order to better learn Ruby internals. And your answer doesn't really help. `x = 1 && y` (equal to `x = (1 && y)`) is a fine expression on its own which parser is happy to accept according to general grammar rules. Therefore I'm looking for a specific mechanism in Ruby grammar to parse `y = 2` first in this case. – Michael Jan 07 '16 at 17:09
  • @Michael It makes sense to assign `1 && y` to some variable. It does not make sense to assign something to `1 && y`. – sawa Jan 07 '16 at 17:11
  • 1
    Ok, I think that you should mention valid(for assignment) l-values, they are listed here http://ruby-doc.org/docs/ruby-doc-bundle/Manual/man-1.4/syntax.html#assign . The issue is really in `1 && y` which evaluates to 1 and is invalid for assignment. – Rustam Gasanov Jan 07 '16 at 17:19
  • @RustamA.Gasanov `1 && y` does not need to be evaluated in order for `(1 && y) = 2` to be illicit. It can be, and is, decided at the parsing level that that (i.e., the fact that it consists of three tokens) does not make sense. But your link seems to be helpful. I am not against you posting that as your own answer. – sawa Jan 07 '16 at 17:30
  • 1
    @sawa I understood, your point makes sense, it's really don't need to be evaluated, because any result it could possibly give won't be a valid l-value. upvoted – Rustam Gasanov Jan 07 '16 at 17:31
  • 1
    Does it have anything to do with associativity? - As per [precedence table](http://stackoverflow.com/a/21060235/794242), the `=` has right-to-left associativity and `&&` has left-to-right – Wand Maker Jan 07 '16 at 17:44
  • @WandMaker, that is what I think is happening here too. You should write it as an answer. – ndnenkov Jan 07 '16 at 20:01
  • @ndn - I don't have sufficient points to support that - its a wild guess. Feel free to build on it if you find some merit in it and post it as answer yourself. – Wand Maker Jan 07 '16 at 20:07
  • @sawa OK, I've tried to add this "makes sense" part to my parser. And it succeeds in rejecting `1 && y` as a left side of the assignment. However, I don't know how to recover from this in later stage. My `and-expression` parsing is looking only for expression with higher precedence (bitwise, math etc), assuming that lower precedence is handled already. – Michael Jan 08 '16 at 13:26
0

My understanding is that in the case of

x = 1 && y = 2

The logical AND is parsed first. The AND then is forced to evaluate its left side and its right side. In the evaluation the left side the first assignment occurs, and then in the evaluation of the right side the second does the same. It is for this reason that in the case of:

x = false && y = 2

"y" will never be assigned. The AND is hit, forcing the x assignment to evaluate, but never bothering to run the y assignment, as it is unnecessary for the AND to fulfill its purpose.

In other words, the parser is, again, simply to my understanding, smart enough to recognize that in running the AND the equation is split into a left and right side (equations), which are in turn evaluated according to the order of operation.

ConnorCMcKee
  • 1,625
  • 1
  • 11
  • 23
  • `and` operator behaves in the way you describe. `&&` is interpreted as `x = (1 && (y = 2))`. Yes, if `1` was to be replaced by `false`/`nil` then `y=` assignment would not be resolved at all, but not because of `x` value, but because of the left side of inner expression (`1 && (y=2)`). – Michael Jan 08 '16 at 10:42