The bug is in php which doesn't respect left associativity for addition in all cases.
Derokorian;11036443 wrote:The difference is in the associativity for JS and PHP. In PHP the ++/-- operator have right associativity while in js they have none. I bet this is the cause of the difference?
Precedence determines in which order operators are handled. Associativity determines in which order operands are evaluated. Operands can be atomic values, variables or expressions. But whatever an operand is does not (should not) affect associativity. It is still an operand.
a = 1;
b = 2;
a = b = 0;
// this will not evaluate as (a=b), then (b=0)
// but rather as (b=0), then (a=b)
Unary operators only have one operand, so talking about associativity is pointless:
- In which order is the operand of a unary operator evaluated?
- In the order it is.
Ambiguity can never arise since a unary operator will have an operand (its operand) on one side, while the other side is either empty or contains an operator rather than an operand.
# you will never have
a ! b
x++ y
# you may have
!b
++x
# you may have
a && !b
x++ + y
On the other hand, it's not really wrong to say that a unary operator which has its operand on the right side is right associative and a unary operator which has its operand on the left side is left associative. I'd still advice against doing so. Because while it isn't wrong, it's still confusing as hell. When you point at something which can only be in one single way and say "Behold, this is this way it is", you will always give people the impression that it could be some other way. Why else would the fact need to be stated? As far as unary operators go, you could of course decide that they should be on the opposite side instead, but its associativity would still be self evident. For example, consider a language which takes explicit type casting operators on the right hand side
i = j (int);
i = j (int) * 6.3;
And even if you'd allow the operand on both sides of the unary operator, its associativity will still be determined by what side its operand is on in any given case. The other side will still always be empty or contain an operator.
In the php docs for operator precedence and associativity, ++ and -- are listed together with other unary operators that all are written to the left of their operand (int), (float), @ etc. Thus, if you wish to talk about associativity for this group of operators, it will have to be right, but that also means that you are explicitly NOT talking about post-increment and post-decrement operators. They are in fact not listed in that table.
Precedence lets you know in which order to deal with operators
y + ++x
// Increment has higher precedence than addition, so the expression is evaluated as
y + (++x)
$i = (int) 2.3 + 3.5;
# (int) has higher precedence addition,
$i = ((int) 2.3) + 3.5
Back to a + b + c. It has left assoc, which means evaluating the leftmost operand, a, first. Looking at the second addition operator, its left operand is an expression consisting of a + b, which is why a + b is evaluated before c. Once again, compare this to how right associativity works when it comes to a = b = c, where c is evaluated before b = c is evaluated before a = b (where b now has the same value as c) is evaluated.
A quick detour to
In the case of
$x = 5;
$y = $x++ + $x
# $y is 11
They have it correct. (x++) is evaluated first, and it is the value of x (5). Then x is incremented. Next up the right hand side is evaluated which is 6 since x has now been incremented.
However, in the other case they really do get it wrong
$x = 5;
$y = $x + ++$x
# $y should be 11, but it is 12 in php
Due to the left associativity of addition, x should still be evaluated first, and it is x (5). Then (++x) should be evaluated and it should increment x before returning 6. Then addition should be performed for 5 and 6. It is, or should be, impossible for any changes to effect evaluation which (should) already have happened.
There are two cases that can be correct, depending on wether you define addition to be left or right associative. Either (if dealing with right-associative addition) the first case should be 10 and the second case should be 12, or (left-associative addition) both cases should be 11.
Back to javascript - which actually gets it right
x = 5;
y = x++ + x;
// y is 11
x = 5;
y = x + ++x;
// y is still 11 - as expected