keith-packard / snek

Snek programming language for tiny systems
GNU General Public License v3.0
294 stars 30 forks source link

Error if you do print(1 == 2 != 0), but no error for print((1 == 2) != 0) or print(1 == (2 != 0)) #50

Closed mobluse closed 2 years ago

mobluse commented 2 years ago

There is an error if you do print(1 == 2 != 0). This error doesn't occur in other Pythons.

 pi  ~  snek
Welcome to Snek version 1.7
> print(1 == 2 != 0)
<stdin>:1 invalid type: 1
> print((1 == 2) != 0)
0
> print(1 == (2 != 0))
1
> 
 pi  ~  /media/pi/Elements/micropython/ports/unix/micropython
MicroPython v1.17-134-gcb99ca986 on 2021-11-01; linux version
Use Ctrl-D to exit, Ctrl-E for paste mode
>>> print(1 == 2 != 0)
False
>>> 
 pi  ~  python3
Python 3.7.3 (default, Jan 22 2021, 20:04:44) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> print(1 == 2 != 0)
False
>>> 
 pi  ~  python2
Python 2.7.16 (default, Oct 10 2019, 22:02:15) 
[GCC 8.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> print(1 == 2 != 0)
False
>>> 
keith-packard commented 2 years ago

Thanks much for the very clear test cases! Super helpful in diagnosing the problem. Python is fairly unique in these 'chained' comparison operations; the snek code for doing this was added recently and turned out to have a selection of issues which your tests uncovered.

I only recently learned that 1 == (2 != 0) is very different from 1 == 2 != 0. The first case compares 1 against the value of the (2 != 0) operation. 2 != 0 is True, which compares equal to 1:

>>> 2 != 0
True
>>> 1 == True
True
>>> 1 == (2 != 0)
True

The second case is what I mean by 'chained comparison' operators. In this case, there are effectively two comparisons linked by a conjunction (and) operator, and Python says that the second comparison is only done if the first is True (just like other and operations). So, you can kinda rewrite the second expression as 1 == 2 and 2 != 0:

>>> 1 == 2
False
>>> 2 != 0
True
>>> 1 == 2 and 2 != 0
False
>>> 1 == 2 != 0
False

The trick with these chained comparisons is that the second operand must not be evaluated twice, so you have to be a bit careful in the implementation.