3 ms·
How this works, for the interested: Python internally compiles to a bytecode for a stack-based virtual machine. If you import the module `dis` and execute `di
by pavpanchekha 15y ago
How this works, for the interested:
Python internally compiles to a bytecode for a stack-based virtual machine. If you import the module `dis` and execute `dis.dis` on a function, you can see this bytecode.
Like most bytecode VMs, there are jump instructions for compiling for and while loops to. For example,
def foo(n):
a, b = 0, 1
for i in range(n):
a, b = b, a + b
return a
compiles to
2 0 LOAD_CONST 3 ((0, 1))
3 UNPACK_SEQUENCE 2
6 STORE_FAST 1 (a)
9 STORE_FAST 2 (b)
3 12 SETUP_LOOP 37 (to 52)
15 LOAD_GLOBAL 0 (range)
18 LOAD_FAST 0 (n)
21 CALL_FUNCTION 1
24 GET_ITER
>> 25 FOR_ITER 23 (to 51)
28 STORE_FAST 3 (i)
4 31 LOAD_FAST 2 (b)
34 LOAD_FAST 1 (a)
37 LOAD_FAST 2 (b)
40 BINARY_ADD
41 ROT_TWO
42 STORE_FAST 1 (a)
45 STORE_FAST 2 (b)
48 JUMP_ABSOLUTE 25
>> 51 POP_BLOCK
5 >> 52 LOAD_FAST 1 (a)
55 RETURN_VALUE
Note the `JUMP_ABSOLUTE` bytecode at position 48 (which jumps to the `FOR_ITER` at position 25). What we want to do is insert more of these instructions by manually modifying bytecode (something Python allows us to do).
The rest is a simple job of identifying the syntax (`goto .x` is really `goto.x`, an attribute access, which is simple to identify as the `LOAD_ATTR` instruction) and inserting the proper jumps. So in fact this is a pure-python modification, which works solely by changing how Python compiles to bytecode.