Summary
This is a source transformation that modify Python’s syntax to allow the use of increment (
x++,++x) and decrement (x--,--x) operators.
Unary increment and decrement operators
This implements unary pre/post increments and decrements operators,
(++x, --x, x++, x--). These operators are well-liked
by some, but can be a huge source of confusion. In case you are not
familiar with them:
Pre-increment (
++x) and pre-decrement (--x) operators modify their operand (x) by 1 and return the value after having done so. One question one might have is what exactly is meant by ‘after’?. For example, in some implementation of C, the expressiony = x- --xis considered to be indefined: do we decrementxafter doing the assignment or before? Also note that, in Python, we have that++x == x == --xis syntactically valid.Post-increment (
x++) and post-decrement (x--) operators also modify their operand by 1 but return the value before doing so. Note that, in Python, these expressions,x++andx--anre not syntactically valid.
The idea to implement these operators via an import hook was inspired by this implementation from Matthias Wippich which uses codecs and token-based transformations. Both Wippich’s implementation, and that of Github user dankeyy (using regular expression and codecs-based) make cleverly use of the walrus operator in the following way:
++x -> (x, x := x + 1)[1]
--x -> (x, x := x - 1)[1]
x++ -> (x, x := x + 1)[0]
x-- -> (x, x := x - 1)[0]
Note that the following are valid Python expressions:
x++y, x+-y, +x+++y, x+ +y, etc.
While greatly inspired by the existing implementations, we have found that they fail to give the expected result in some situations. Our implementation takes care to ensure that these valid expressions remain so with their current meaning; this is done by only making changes when a human reader would be able to unambiguously identify if an increment or decrement operator is used.
So, taking x++ as an example, we will impose the restriction that, in
*x++*, * cannot be another unary sign, + or - nor can it be
a valid Python identifier. In *x++* and similar, if * represent
an identifier, then spaces between
* and the increment/decrement are not significant. Since we can’t have
two consecutive identifiers in Python, no change will take place.
However, in x++, there can be no space between any of these symbols.
Here’s a sample session illustrating the transformations that do or do not occur:
> ideas -a inc_dec
Ideas Console version 0.3.3. [Python version: 3.11.9]
ideas> from ideas import transform
ideas> transform("x++")
(x, x := x + 1)[0]
ideas> transform("x--")
(x, x := x + 1)[0]
ideas> transform("--x")
(x, x := x - 1)[1]
ideas> transform("++x")
(x, x := x + 1)[1]
ideas> transform("x+++")
x+++
ideas> transform("---x")
---x
ideas> transform("x++y")
x++y
ideas> transform("x++ y") # think of 'x++' as a new value 'X', and thus 'X y'
x++ y # which would be invalid; so we keep the valid syntax
ideas> transform("x++ +y")
(x, x := x + 1)[0] +y
ideas> transform("'x++'") # inside string
'x++'
ideas> transform("y- ++x")
y- (x, x := x + 1)[1]
ideas> transform("y-++x") # valid Python syntax
y-++x