Lexikální a syntaktická analýza zdrojových kódů jazyka Python (4.část)

25. 8. 2022
Doba čtení: 44 minut

Sdílet

 Autor: Depositphotos
V dalším článku o lexikální a syntaktické analýze zdrojových kódů napsaných v Pythonu vlastně již překročíme oblast analýzy zdrojového kódu a přesuneme se k popisu bajtkódu, který je výsledkem překladu abstraktního syntaktického stromu (AST).

Obsah

1. Lexikální a syntaktická analýza zdrojových kódů programovacího jazyka Python (4.část)

2. Překlad modulu do bajtkódu, obsah vygenerovaného binárního souboru

3. Překlad aritmetických výrazů do bajtkódu

4. Zásobníkový efekt (stack effect)

5. Naznačení operací prováděných virtuálním strojem Pythonu zásobníkovým efektem

6. Porovnání s bajtkódem virtuálního stroje jazyka Lua

7. Překlad výrazů s Booleovskými operátory

8. Překlad výrazů s relačními operátory

9. Překlad řídicí struktury typu if-then

10. Překlad řídicí struktury typu if-then-else

11. Jednoduchá programová smyčka typu while

12. Programová smyčka typu while se složitějším tělem

13. Dvě vnořené smyčky typu while – výpis řady prvočísel

14. Jednoduchá programová smyčka typu do-while/repeat-until

15. Příloha A: základní instrukce pro manipulaci se zásobníkem

16. Příloha B: aritmetické a logické operace

17. Příloha C: podmíněné a nepodmíněné skoky, větvení kódu

18. Příloha D: Tabulka všech operačních kódů instrukcí Python VM

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Lexikální a syntaktická analýza zdrojových kódů programovacího jazyka Python (4.část)

Na konci předchozího článku jsme se zmínili o tom, jakým způsobem se překládá kód reprezentovaný v abstraktním syntaktickém stromu (AST) do bajtkódu. Jedná se o poměrně mechanickou operaci, jejímž výsledkem je sekvence instrukcí bajtkódu vytvořená pro každou funkci, resp. metodu. Sekvence těchto instrukcí je buď přímo interpretována (standardní CPython), nebo ji lze přeložit do nativního kódu (AOT, JIT, což bude téma samostatného článku).

Překlad do bajtkódu si můžeme vyzkoušet přímo v REPLu jazyka Python:

import dis
dis.dis("1+2*3")
 
  1           0 LOAD_CONST               0 (7)
              2 RETURN_VALUE

Z předchozího výsledku je patrné, že došlo k optimalizaci výrazu za jeho výsledek – hodnotu 7.

Nyní si vyzkoušejme překlad funkce se dvěma parametry do bajtkódu:

def add(x,y):
...     return x+y
...
dis.dis(add)
 
  2           0 LOAD_FAST                0 (x)
              2 LOAD_FAST                1 (y)
              4 BINARY_ADD
              6 RETURN_VALUE

V tomto případě nemá Python k dispozici žádné informace o typech, což ostatně platí i pro bajtkód, kde operace BINARY_ADD pouze reprezentuje operátor +, který lze pochopitelně přetížit (a už v samotném Pythonu je několikrát přetížen).

V bajtkódu jsou uloženy i další metainformace o přeložené funkci. Ty lze získat následovně:

>>> print(dis.code_info(add))
 
Name:              add
Filename:          <stdin>
Argument count:    2
Positional-only arguments: 0
Kw-only arguments: 0
Number of locals:  2
Stack size:        2
Flags:             OPTIMIZED, NEWLOCALS, NOFREE
Constants:
   0: None
Variable names:
   0: x
   1: y

2. Překlad modulu do bajtkódu, obsah vygenerovaného binárního souboru

Díky existenci modulu py_compile je možné zajistit překlad jakéhokoli modulu do bajtkódu. K tomuto účelu slouží následující volání (posledním argumentem je pochopitelně překládaný zdrojový kód):

$ python3 -m py_compile dis_0.py

Po překladu do bajtkódu získáme binární soubor nazvaný dis0.{python_version}.pyc, jehož obsah si můžeme prohlédnout libovolným hexa prohlížečem. Standardním hexa prohlížečem na Linuxu je nástroj od (nenechte se zmást jeho jménem, které klame – jedná se i o hexa prohlížeč):

$ od -t x1 dis_0.cpython-38.pyc
 
0000000 55 0d 0d 0a 00 00 00 00 ac 9b 00 63 3c 00 00 00
0000020 e3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000040 00 02 00 00 00 40 00 00 00 73 0c 00 00 00 64 00
0000060 64 01 84 00 5a 00 64 02 53 00 29 03 63 02 00 00
0000100 00 00 00 00 00 00 00 00 00 03 00 00 00 02 00 00
0000120 00 43 00 00 00 73 0c 00 00 00 7c 00 7c 01 17 00
0000140 7d 02 7c 02 53 00 29 01 4e a9 00 29 03 da 01 78
0000160 da 01 79 da 06 72 65 73 75 6c 74 72 01 00 00 00
0000200 72 01 00 00 00 fa 08 64 69 73 5f 30 2e 70 79 da
0000220 0b 61 64 64 5f 6e 75 6d 62 65 72 73 01 00 00 00
0000240 73 04 00 00 00 00 01 08 01 72 06 00 00 00 4e 29
0000260 01 72 06 00 00 00 72 01 00 00 00 72 01 00 00 00
0000300 72 01 00 00 00 72 05 00 00 00 da 08 3c 6d 6f 64
0000320 75 6c 65 3e 01 00 00 00 f3 00 00 00 00
0000335

Na začátku tohoto binárního souboru se nachází dvojice bajtů, jejichž hodnoty se mění s každou verzí Pythonu. Za těmito dvěma bajty je dvojice 0×0d 0×0a kvůli detekci manipulace se souborem jako s textem (předpokládá se, že se jeden z těchto bajtů buď ztratí nebo naopak zduplikuje). Následuje binárně uložený objekt, který je výsledkem překladu a který kromě tabulky symbolů (viz předchozí článek) obsahuje i sekvenci operačních kódů instrukcí následovaných operačním kódem STOP s hodnotou 0.

3. Překlad aritmetických výrazů do bajtkódu

Nyní se již pojďme věnovat poněkud praktičtějšímu tématu – konkrétně způsobu překladu různých typických programových konstrukcí do bajtkódu virtuálního stroje jazyka Python. V této kapitole si ukážeme, jak se konkrétně překládají základní aritmetické výrazy, a to zejména z toho důvodu, že právě na překladu aritmetických výrazů bude patrný největší rozdíl mezi virtuálními stroji založenými na zásobníku (Python VM) a operacích s operandy uloženými na tomto zásobníku na jedné straně a s virtuálními stroji založenými na sadě registrů na straně druhé (Lua VM).

Budeme analyzovat bajtkód tohoto programu napsaného v Pythonu:

#
# Modul s nekolika jednoduchymi funkcemi
# pro otestovani zakladnich vlastnosti bajtkodu jazyka Python
# prekladu aritmetickych vyrazu.
#
 
def vyraz1(x, y):
    result = x + y
    return result
 
 
def vyraz2(x, y):
    result = x - y
    return result
 
 
def vyraz3(x, y):
    result = x * y
    return result
 
 
def vyraz4(x, y):
    result = x / y
    return result
 
 
def vyraz5(x, y):
    result = x % y
    return result
 
 
def vyraz6(x, y, z):
    result = x + y * z
    return result
 
 
def vyraz7(x, y, z, w):
    result = x + y * z + w
    return result
 
 
def vyraz8(x, y, z, w):
    result = 2 * (x + y) * (z + w) * ((x + z) / (y + w))
    return result
 
 
#
# Vse je nutne otestovat.
#
def main():
    print(vyraz1(4, 3))
    print(vyraz2(4, 3))
    print(vyraz3(4, 3))
    print(vyraz4(4, 3))
    print(vyraz5(4, 3))
    print(vyraz6(4, 3, 2))
    print(vyraz7(4, 3, 2, 1))
    print(vyraz8(4, 3, 2, 1))
 
def disassemble():
    from dis import dis
 
    print("\nvyraz1:")
    dis(vyraz1)
 
    print("\nvyraz2:")
    dis(vyraz2)
 
    print("\nvyraz3:")
    dis(vyraz3)
 
    print("\nvyraz4:")
    dis(vyraz4)
 
    print("\nvyraz5:")
    dis(vyraz5)
 
    print("\nvyraz6:")
    dis(vyraz6)
 
    print("\nvyraz7:")
    dis(vyraz7)
 
    print("\nvyraz8:")
    dis(vyraz8)
 
main()
 
disassemble()

V Pythonu je použit, jak zajisté po přečtení předchozí části tohoto miniseriálu očekáváte, zásobníkový kód. Pro aritmetické operace se tedy nejdříve načtou oba operandy na zásobník instrukcí LOAD_FAST, provede se příslušná binární operace BINARY_? a následně se hodnota uloží zpět do lokální proměnné instrukcí STORE_FAST:

vyraz1:
  8           0 LOAD_FAST                0 (x)           ; uložení prvního parametru na zásobník operandů
              2 LOAD_FAST                1 (y)           ; uložení druhého parametru na zásobník operandů
              4 BINARY_ADD                               ; provedení zvolené binární operace
              6 STORE_FAST               2 (result)      ; přečtení vypočteného výsledku s jeho uložením do lokální proměnné
 
  9           8 LOAD_FAST                2 (result)      ; načtení hodnoty lokální proměnné
             10 RETURN_VALUE                             ; která bude současně návratovou hodnotou funkce
 
vyraz2:
 13           0 LOAD_FAST                0 (x)           ; uložení prvního parametru na zásobník operandů
              2 LOAD_FAST                1 (y)           ; uložení druhého parametru na zásobník operandů
              4 BINARY_SUBTRACT                          ; provedení zvolené binární operace
              6 STORE_FAST               2 (result)      ; přečtení vypočteného výsledku s jeho uložením do lokální proměnné
 
 14           8 LOAD_FAST                2 (result)      ; načtení hodnoty lokální proměnné
             10 RETURN_VALUE                             ; která bude současně návratovou hodnotou funkce
 
vyraz3:
 18           0 LOAD_FAST                0 (x)           ; uložení prvního parametru na zásobník operandů
              2 LOAD_FAST                1 (y)           ; uložení druhého parametru na zásobník operandů
              4 BINARY_MULTIPLY                          ; provedení zvolené binární operace
              6 STORE_FAST               2 (result)      ; přečtení vypočteného výsledku s jeho uložením do lokální proměnné
 
 19           8 LOAD_FAST                2 (result)      ; načtení hodnoty lokální proměnné
             10 RETURN_VALUE                             ; která bude současně návratovou hodnotou funkce
 
vyraz4:
 23           0 LOAD_FAST                0 (x)           ; uložení prvního parametru na zásobník operandů
              2 LOAD_FAST                1 (y)           ; uložení druhého parametru na zásobník operandů
              4 BINARY_TRUE_DIVIDE                       ; provedení zvolené binární operace
              6 STORE_FAST               2 (result)      ; přečtení vypočteného výsledku s jeho uložením do lokální proměnné
 
 24           8 LOAD_FAST                2 (result)      ; načtení hodnoty lokální proměnné
             10 RETURN_VALUE                             ; která bude současně návratovou hodnotou funkce
 
vyraz5:
 28           0 LOAD_FAST                0 (x)           ; uložení prvního parametru na zásobník operandů
              2 LOAD_FAST                1 (y)           ; uložení druhého parametru na zásobník operandů
              4 BINARY_MODULO                            ; provedení zvolené binární operace
              6 STORE_FAST               2 (result)      ; přečtení vypočteného výsledku s jeho uložením do lokální proměnné
 
 29           8 LOAD_FAST                2 (result)      ; načtení hodnoty lokální proměnné
             10 RETURN_VALUE                             ; která bude současně návratovou hodnotou funkce
 
vyraz6:
 33           0 LOAD_FAST                0 (x)           ; uložení prvního parametru na zásobník operandů
              2 LOAD_FAST                1 (y)           ; uložení druhého parametru na zásobník operandů
              4 LOAD_FAST                2 (z)           ; uložení třetího parametru na zásobník operandů
              6 BINARY_MULTIPLY                          ; provedení zvolené binární operace, výsledek se uloží na zásobník
              8 BINARY_ADD                               ; provedení zvolené binární operace
             10 STORE_FAST               3 (result)      ; přečtení vypočteného výsledku s jeho uložením do lokální proměnné
 
 34          12 LOAD_FAST                3 (result)      ; načtení hodnoty lokální proměnné
             14 RETURN_VALUE                             ; která bude současně návratovou hodnotou funkce
 
vyraz7:
 38           0 LOAD_FAST                0 (x)           ; uložení prvního parametru na zásobník operandů
              2 LOAD_FAST                1 (y)           ; uložení druhého parametru na zásobník operandů
              4 LOAD_FAST                2 (z)           ; uložení třetího parametru na zásobník operandů
              6 BINARY_MULTIPLY                          ; provedení zvolené binární operace, výsledek se uloží na zásobník
              8 BINARY_ADD                               ; provedení zvolené binární operace
             10 LOAD_FAST                3 (w)           ; uložení čtvrtého parametru na zásobník operandů
             12 BINARY_ADD                               ; provedení zvolené binární operace
             14 STORE_FAST               4 (result)      ; přečtení vypočteného výsledku s jeho uložením do lokální proměnné
 
 39          16 LOAD_FAST                4 (result)      ; načtení hodnoty lokální proměnné
             18 RETURN_VALUE                             ; která bude současně návratovou hodnotou funkce
 
vyraz8:
 43           0 LOAD_CONST               1 (2)           ; uložení konstanty na zásobník operandů
              2 LOAD_FAST                0 (x)           ; uložení prvního parametru na zásobník operandů
              4 LOAD_FAST                1 (y)           ; uložení druhého parametru na zásobník operandů
              6 BINARY_ADD                               ; provedení zvolené binární operace
              8 BINARY_MULTIPLY                          ; provedení zvolené binární operace, výsledek se uloží na zásobník
             10 LOAD_FAST                2 (z)           ; uložení třetího parametru na zásobník operandů
             12 LOAD_FAST                3 (w)           ; uložení čtvrtého parametru na zásobník operandů
             14 BINARY_ADD                               ; provedení zvolené binární operace
             16 BINARY_MULTIPLY                          ; provedení zvolené binární operace, výsledek se uloží na zásobník
             18 LOAD_FAST                0 (x)           ; uložení prvního parametru na zásobník operandů
             20 LOAD_FAST                2 (z)           ; uložení třetího parametru na zásobník operandů
             22 BINARY_ADD                               ; provedení zvolené binární operace
             24 LOAD_FAST                1 (y)           ; uložení druhého parametru na zásobník operandů
             26 LOAD_FAST                3 (w)           ; uložení čtvrtého parametru na zásobník operandů
             28 BINARY_ADD
             30 BINARY_TRUE_DIVIDE
             32 BINARY_MULTIPLY                          ; provedení zvolené binární operace, výsledek se uloží na zásobník
             34 STORE_FAST               4 (result)      ; přečtení vypočteného výsledku s jeho uložením do lokální proměnné
 
 44          36 LOAD_FAST                4 (result)      ; načtení hodnoty lokální proměnné
             38 RETURN_VALUE                             ; která bude současně návratovou hodnotou funkce

4. Zásobníkový efekt (stack effect)

Už před několika desítkami let programátoři píšící aplikace v programovacím jazyku Forth používali pro popis vlivu slov (příkazů) na položky uložené na zásobníku takzvaný zásobníkový efekt (stack effect). Pro další čtení je užitečné si čtení zásobníkového efektu osvojit.

Nejedná se v podstatě o nic složitého: uvnitř kulatých závorek, které ve Forthu ale i Factoru značí začátek a konec poznámky se zásobníkovým efektem, je symbolicky zapsán stav části zásobníku před provedením operace a po dvojici znaků „–“ stav zásobníku po provedení dané operace. Vzhledem k tomu, že na zásobníku může být uloženo teoreticky libovolné množství hodnot a daná operace většinou ovlivňuje pouze hodnoty umístěné blízko jeho vrcholu, je zásobníkový efekt zapsán pouze pro ty pozice na zásobníku, které jsou operací nějakým způsobem dotčeny, tj. operace tyto hodnoty přečte, zruší či modifikuje. Položky umístěné níže nemá cenu zapisovat, jen by zápis zbytečně komplikovaly. Položka umístěná nejvíce vlevo je ve skutečnosti uložena na spodnějších místech zásobníku, než položky napravo od ní.

Následuje jednoduchý příklad zápisu zásobníkového efektu pro operaci součtu dvou čísel. Nejprve je zapsán název operace, v tomto případě nazvané plus. Poté je zapsána otevírací kulatá závorka značící začátek poznámky, za níž následuje stav nejvyšších položek zásobníku před provedením operace (tedy těch položek, které jsou operací dotčeny – ale můžeme jich zapsat i více). Po oddělovači představovaném dvojicí znaků „–“ je uveden stav nejvyšších položek po provedení operace, za nímž následuje uzavírací kulatá závorka značící konec poznámky (ve skutečnosti jsou i kulaté závorky běžnými slovy jazyka a proto musí být od okolních slov odděleny alespoň jednou mezerou).

Vzhledem k tomu, že operace sčítání načte dvě hodnoty ze zásobníku a zpět vloží jejich součet, jsou použity odlišné symboly x, y a z, přičemž samozřejmě platí, že z=x+y:

# "hlavička" operátoru plus
plus ( x y -- z )

Pokud by nějaká operace například prohazovala hodnoty uložené na zásobníku, ale jinak by je žádným způsobem neměnila, byly by použity stejné symboly, což je případ operátoru swap či rot (což mohou být i instrukce bajtkódu):

# "hlavička" operátorů swap a rot
swap ( x y -- y x )
rot  ( x y z -- y z x )

5. Naznačení operací prováděných virtuálním strojem Pythonu zásobníkovým efektem

Výše popsaný koncept zásobníkového efektu můžeme použít i při popisu operací prováděných instrukcemi bajtkódu. Jednoduchý příklad z předchozích kapitol:

vyraz1:                                                  ; ( -- )
  8           0 LOAD_FAST                0 (x)           ; ( -- x )
              2 LOAD_FAST                1 (y)           ; ( x -- x y )
              4 BINARY_ADD                               ; ( x y -- z )
              6 STORE_FAST               2 (result)      ; ( z -- )
 
  9           8 LOAD_FAST                2 (result)      ; ( -- z )
             10 RETURN_VALUE                             ; ( -- )

A pro nejsložitější funkci z předchozích kapitol:

vyraz8:                                                  ; (  -- )
 43           0 LOAD_CONST               1 (2)           ; (  -- 2 )
              2 LOAD_FAST                0 (x)           ; ( 2 -- 2 x )
              4 LOAD_FAST                1 (y)           ; ( 2 x -- 2 x y )
              6 BINARY_ADD                               ; ( 2 x y -- 2 x+y )
              8 BINARY_MULTIPLY                          ; ( 2 x+y -- 2*(x+y) )
             10 LOAD_FAST                2 (z)           ; ( 2*(x+y) -- 2*(x+y) z )
             12 LOAD_FAST                3 (w)           ; ( 2*(x+y) z -- 2*(x+y) z w )
             14 BINARY_ADD                               ; ( 2*(x+y) z w -- 2*(x+y) z+w )
             16 BINARY_MULTIPLY                          ; ( 2*(x+y) z+w -- 2*(x+y)*(z+w) )
             18 LOAD_FAST                0 (x)           ; ( 2*(x+y)*(z+w) -- 2*(x+y)*(z+w) x )
             20 LOAD_FAST                2 (z)           ; ( 2*(x+y)*(z+w) x -- 2*(x+y)*(z+w) x z )
             22 BINARY_ADD                               ; ( 2*(x+y)*(z+w) x z -- 2*(x+y)*(z+w) x+z )
             24 LOAD_FAST                1 (y)           ; ( 2*(x+y)*(z+w) x+z -- 2*(x+y)*(z+w) x+z y )
             26 LOAD_FAST                3 (w)           ; ( 2*(x+y)*(z+w) x+z y -- 2*(x+y)*(z+w) x+z y w )
             28 BINARY_ADD                               ; ( 2*(x+y)*(z+w) x+z y w -- 2*(x+y)*(z+w) x+z y+w )
             30 BINARY_TRUE_DIVIDE                       ; ( 2*(x+y)*(z+w) x+z y+w -- 2*(x+y)*(z+w) (x+z)/(y+w) )
             32 BINARY_MULTIPLY                          ; ( 2*(x+y)*(z+w) (x+z)/(y+w) -- 2*(x+y)*(z+w)*((x+z)/(y+w)) )
             34 STORE_FAST               4 (result)      ; ( 2*(x+y)*(z+w)*((x+z)/(y+w)) -- )
 
 44          36 LOAD_FAST                4 (result)
             38 RETURN_VALUE
Poznámka: maximální hloubka zásobníku je zde pouhé čtyři prvky!

6. Porovnání s bajtkódem virtuálního stroje jazyka Lua

Pro zajímavost se podívejme, jak bude podobný zdrojový kód přeložen v jazyku Lua, jehož bajtkód není založen na zásobníkovém stroji, ale na stroji registrovém:

--
-- Modul s nekolika jednoduchymi funkcemi
-- pro otestovani zakladnich vlastnosti bajtkodu jazyka Lua
-- prekladu aritmetickych vyrazu.
--
 
function vyraz1(x, y)
    local result = x + y
    return result
end
 
function vyraz2(x, y)
    local result = x - y
    return result
end
 
function vyraz3(x, y)
    local result = x * y
    return result
end
 
function vyraz4(x, y)
    local result = x / y
    return result
end
 
function vyraz5(x, y)
    local result = x % y
    return result
end
 
function vyraz6(x, y, z)
    local result = x + y * z
    return result
end
 
function vyraz7(x, y, z, w)
    local result = x + y * z + w
    return result
end
 
function vyraz8(x, y, z, w)
    local result = 2 * (x + y) * (z + w) * ((x + z) / (y + w))
    return result
end
 
--
-- Vse je nutne otestovat.
--
function main()
    print(vyraz1(4, 3))
    print(vyraz2(4, 3))
    print(vyraz3(4, 3))
    print(vyraz4(4, 3))
    print(vyraz5(4, 3))
    print(vyraz6(4, 3, 2))
    print(vyraz7(4, 3, 2, 1))
    print(vyraz8(4, 3, 2, 1))
end
 
main()

Ve virtuálním stroji jazyka Lua je použit kód využívající „registry“ virtuálního stroje a proto je počet instrukcí menší, než u kódu zásobníkového. Za tuto efektivitu platíme omezeným počtem registrů (což v praxi nemusí vadit):

function <Test2.lua:7,10> (3 instructions at 0x9e07c88)
2 params, 3 slots, 0 upvalues, 3 locals, 0 constants, 0 functions
        1       [8]     ADD             2 0 1  ; provést aritmetickou operaci s parametrem 0 a 1
        2       [9]     RETURN          2 2    ; návrat z funkce a vrácení hodnoty lokální proměnné 2
        3       [10]    RETURN          0 1    ; automaticky vkládaná instrukce návratu z funkce
 
function <Test2.lua:12,15> (3 instructions at 0x9e07f20)
2 params, 3 slots, 0 upvalues, 3 locals, 0 constants, 0 functions
        1       [13]    SUB             2 0 1  ; provést aritmetickou operaci s parametrem 0 a 1
        2       [14]    RETURN          2 2    ; návrat z funkce a vrácení hodnoty lokální proměnné 2
        3       [15]    RETURN          0 1    ; automaticky vkládaná instrukce návratu z funkce
 
function <Test2.lua:17,20> (3 instructions at 0x9e080f0)
2 params, 3 slots, 0 upvalues, 3 locals, 0 constants, 0 functions
        1       [18]    MUL             2 0 1  ; provést aritmetickou operaci s parametrem 0 a 1
        2       [19]    RETURN          2 2    ; návrat z funkce a vrácení hodnoty lokální proměnné 2
        3       [20]    RETURN          0 1    ; automaticky vkládaná instrukce návratu z funkce
 
function <Test2.lua:22,25> (3 instructions at 0x9e07ec8)
2 params, 3 slots, 0 upvalues, 3 locals, 0 constants, 0 functions
        1       [23]    DIV             2 0 1  ; provést aritmetickou operaci s parametrem 0 a 1
        2       [24]    RETURN          2 2    ; návrat z funkce a vrácení hodnoty lokální proměnné 2
        3       [25]    RETURN          0 1    ; automaticky vkládaná instrukce návratu z funkce
 
function <Test2.lua:27,30> (3 instructions at 0x9e084f8)
2 params, 3 slots, 0 upvalues, 3 locals, 0 constants, 0 functions
        1       [28]    MOD             2 0 1  ; provést aritmetickou operaci s parametrem 0 a 1
        2       [29]    RETURN          2 2    ; návrat z funkce a vrácení hodnoty lokální proměnné 2
        3       [30]    RETURN          0 1    ; automaticky vkládaná instrukce návratu z funkce
 
function <Test2.lua:32,35> (4 instructions at 0x9e07e18)
3 params, 4 slots, 0 upvalues, 4 locals, 0 constants, 0 functions
        1       [33]    MUL             3 1 2  ; provést aritmetickou operaci s parametrem 1 a 2
        2       [33]    ADD             3 0 3  ; provést aritmetickou operaci s parametrem 0 a lokální proměnnou 3
        3       [34]    RETURN          3 2    ; návrat z funkce a vrácení hodnoty lokální proměnné 3
        4       [35]    RETURN          0 1    ; automaticky vkládaná instrukce návratu z funkce
 
function <Test2.lua:37,40> (5 instructions at 0x9e08458)
4 params, 5 slots, 0 upvalues, 5 locals, 0 constants, 0 functions
        1       [38]    MUL             4 1 2  ; provést aritmetickou operaci s parametrem 1 a 2
        2       [38]    ADD             4 0 4  ; provést další aritmetickou operaci s parametrem 0 a lokální proměnnou 4
        3       [38]    ADD             4 4 3  ; provést další aritmetickou operaci s parametrem 3 a lokální proměnnou 4
        4       [39]    RETURN          4 2    ; návrat z funkce a vrácení hodnoty lokální proměnné 4
        5       [40]    RETURN          0 1    ; automaticky vkládaná instrukce návratu z funkce
 
function <Test2.lua:42,45> (10 instructions at 0x9e08720)
4 params, 7 slots, 0 upvalues, 5 locals, 1 constant, 0 functions
        1       [43]    ADD             4 0 1  ; provést aritmetickou operaci s parametrem 0 a 1
        2       [43]    MUL             4 -1 4  ; 2 - ; zde se adresuje konstanta -2 !!!
        3       [43]    ADD             5 2 3  ; provést další aritmetickou operaci, výsledek do lok. proměnné 5
        4       [43]    MUL             4 4 5
        5       [43]    ADD             5 0 2
        6       [43]    ADD             6 1 3
        7       [43]    DIV             5 5 6
        8       [43]    MUL             4 4 5
        9       [44]    RETURN          4 2    ; návrat z funkce a vrácení hodnoty lokální proměnné 4
        10      [45]    RETURN          0 1    ; automaticky vkládaná instrukce návratu z funkce

7. Překlad výrazů s Booleovskými operátory

Booleovské (logické) výrazy se v bajtkódu programovacího jazyka Python většinou překládají s využitím podmíněného skoku. Před samotnou instrukcí skoku se vyhodnotí podmínka, v našem případě první část logického výrazu, a na základě této podmínky se zjistí hodnota druhé části logického výrazu (pokud se samozřejmě jedná o operace se dvěma operandy). Podívejme se nyní na způsob překladu výrazů obsahujících negaci, logický součet, logický součin, operaci nonekvivalence a různé kombinace těchto operací:

#
# Modul s nekolika jednoduchymi funkcemi
# pro otestovani zakladnich vlastnosti bajtkodu jazyka Python
# prekladu Booleovskych vyrazu.
#
 
def vyraz1(x):
    result = not x
    return result
 
def vyraz2(x, y):
    result = x and y
    return result
 
def vyraz3(x, y):
    result = x or y
    return result
 
def vyraz4(x, y):
    result = x ^ y
    return result
 
def vyraz5(x, y, z):
    result = x or y and z
    return result
 
def vyraz6(x, y, z, w):
    result = (x or y) and (z or w)
    return result
 
 
#
# Vse je nutne otestovat.
#
def main():
    print(vyraz1(True))
    print(vyraz2(True, False))
    print(vyraz3(True, False))
    print(vyraz4(True, False))
    print(vyraz5(True, False, True))
    print(vyraz6(True, False, True, False))
 
def disassemble():
    from dis import dis
 
    print("\nvyraz1:")
    dis(vyraz1)
 
    print("\nvyraz2:")
    dis(vyraz2)
 
    print("\nvyraz3:")
    dis(vyraz3)
 
    print("\nvyraz4:")
    dis(vyraz4)
 
    print("\nvyraz5:")
    dis(vyraz5)
 
    print("\nvyraz6:")
    dis(vyraz6)
 
main()
 
disassemble()

Povšimněte si, že se kromě první funkce (negace) skutečně používají podmíněné skoky, tj. instrukce JUMP_IF_?, s nimiž se ostatně setkáme i v navazujícím textu:

vyraz1:
  8           0 LOAD_FAST                0 (x)
              2 UNARY_NOT
              4 STORE_FAST               1 (result)
 
  9           6 LOAD_FAST                1 (result)
              8 RETURN_VALUE
 
vyraz2:
 12           0 LOAD_FAST                0 (x)
              2 JUMP_IF_FALSE_OR_POP     6
              4 LOAD_FAST                1 (y)
        >>    6 STORE_FAST               2 (result)
 
 13           8 LOAD_FAST                2 (result)
             10 RETURN_VALUE
 
vyraz3:
 16           0 LOAD_FAST                0 (x)
              2 JUMP_IF_TRUE_OR_POP      6
              4 LOAD_FAST                1 (y)
        >>    6 STORE_FAST               2 (result)
 
 17           8 LOAD_FAST                2 (result)
             10 RETURN_VALUE
 
vyraz4:
 20           0 LOAD_FAST                0 (x)
              2 LOAD_FAST                1 (y)
              4 BINARY_XOR
              6 STORE_FAST               2 (result)
 
 21           8 LOAD_FAST                2 (result)
             10 RETURN_VALUE
 
vyraz5:
 24           0 LOAD_FAST                0 (x)
              2 JUMP_IF_TRUE_OR_POP     10
              4 LOAD_FAST                1 (y)
              6 JUMP_IF_FALSE_OR_POP    10
              8 LOAD_FAST                2 (z)
        >>   10 STORE_FAST               3 (result)

 25          12 LOAD_FAST                3 (result)
             14 RETURN_VALUE
 
vyraz6:
 28           0 LOAD_FAST                0 (x)
              2 POP_JUMP_IF_TRUE         8
              4 LOAD_FAST                1 (y)
              6 JUMP_IF_FALSE_OR_POP    14
        >>    8 LOAD_FAST                2 (z)
             10 JUMP_IF_TRUE_OR_POP     14
             12 LOAD_FAST                3 (w)
        >>   14 STORE_FAST               4 (result)
 
 29          16 LOAD_FAST                4 (result)
             18 RETURN_VALUE

8. Překlad výrazů s relačními operátory

Výrazy s relačními operátory se překládají do značné míry stejným způsobem, jako aritmetické výrazy (jen se používají odlišné binární instrukce), o čemž se ostatně přesvědčíme po pohledu na bajtkód vygenerovaný pro následující skript:

#
# Modul s nekolika jednoduchymi funkcemi
# pro otestovani zakladnich vlastnosti bajtkodu jazyka Python
# prekladu relacnich vyrazu.
#
 
def vyraz1(x, y):
    result = x <= y
    return result
 
def vyraz2(x, y):
    result = x < y
    return result
 
def vyraz3(x, y):
    result = x == y
    return result
 
def vyraz4(x, y):
    result = x != y
    return result
 
def vyraz5(x, y):
    result = x >= y
    return result
 
def vyraz6(x, y):
    result = x > y
    return result
 
 
#
# Vse je nutne otestovat.
#
def main():
    print(vyraz1(1, 2))
    print(vyraz2(1, 2))
    print(vyraz3(1, 2))
    print(vyraz4(1, 2))
    print(vyraz5(1, 2))
    print(vyraz6(1, 2))
 
def disassemble():
    from dis import dis
 
    print("\nvyraz1:")
    dis(vyraz1)
 
    print("\nvyraz2:")
    dis(vyraz2)
 
    print("\nvyraz3:")
    dis(vyraz3)
 
    print("\nvyraz4:")
    dis(vyraz4)
 
    print("\nvyraz5:")
    dis(vyraz5)
 
    print("\nvyraz6:")
    dis(vyraz6)
 
main()
 
disassemble()

Z bajtkódu je patrné, že se používá „univerzální“ instrukce COMPARE_OP, jejímž celočíselným parametrem je číslo (index) prováděné operace:

vyraz1:
  8           0 LOAD_FAST                0 (x)
              2 LOAD_FAST                1 (y)
              4 COMPARE_OP               1 (<=)
              6 STORE_FAST               2 (result)
 
  9           8 LOAD_FAST                2 (result)
             10 RETURN_VALUE
 
vyraz2:
 12           0 LOAD_FAST                0 (x)
              2 LOAD_FAST                1 (y)
              4 COMPARE_OP               0 (<)
              6 STORE_FAST               2 (result)
 
 13           8 LOAD_FAST                2 (result)
             10 RETURN_VALUE
 
vyraz3:
 16           0 LOAD_FAST                0 (x)
              2 LOAD_FAST                1 (y)
              4 COMPARE_OP               2 (==)
              6 STORE_FAST               2 (result)
 
 17           8 LOAD_FAST                2 (result)
             10 RETURN_VALUE
 
vyraz4:
 20           0 LOAD_FAST                0 (x)
              2 LOAD_FAST                1 (y)
              4 COMPARE_OP               3 (!=)
              6 STORE_FAST               2 (result)
 
 21           8 LOAD_FAST                2 (result)
             10 RETURN_VALUE
 
vyraz5:
 24           0 LOAD_FAST                0 (x)
              2 LOAD_FAST                1 (y)
              4 COMPARE_OP               5 (>=)
              6 STORE_FAST               2 (result)
 
 25           8 LOAD_FAST                2 (result)
             10 RETURN_VALUE
 
vyraz6:
 28           0 LOAD_FAST                0 (x)
              2 LOAD_FAST                1 (y)
              4 COMPARE_OP               4 (>)
              6 STORE_FAST               2 (result)
 
 29           8 LOAD_FAST                2 (result)
             10 RETURN_VALUE

9. Překlad řídicí struktury typu if-then

Nyní se již konečně dostáváme k mnohem zajímavějšímu tématu – konkrétně k podmíněným skokům, kterými jsou realizovány strukturované příkazy (rozeskoky) typu if-then a samozřejmě i úplné rozhodovací konstrukce typu if-then-else. Jedná se o tak časté programové konstrukce, že pro ně byly v bajtkódu vyhrazeny speciální operace. Nejprve se podívejme na zdrojový kód dvou funkcí, jejichž bajtkód budeme zkoumat:

def prikaz1(x):
    if x:
        return 10
    return 20
 
def prikaz2(x, y):
    if x:
        if y:
            return 10
    return 20
 
#
# Vse je nutne otestovat.
#
def main():
    print(prikaz1(True))
    print(prikaz1(False))
    print(prikaz2(True, True))
    print(prikaz2(True, False))
    print(prikaz2(False, True))
    print(prikaz2(False, False))
 
def disassemble():
    from dis import dis
 
    print("\nprikaz1:")
    dis(prikaz1)
 
    print("\nprikaz2:")
    dis(prikaz2)
 
main()
 
disassemble()

Nejzajímavější částí bajtkódu jsou v tomto případě instrukce POP_JUMP_IF_FALSE (resp. POP_JUMP_IF_TRUE), které načtou hodnotu a pokud je rovna False (tedy vlastně 0), provede se podmíněný skok na konec rozvětvení:

prikaz1:
  8           0 LOAD_FAST                0 (x)
              2 POP_JUMP_IF_FALSE        8
 
  9           4 LOAD_CONST               1 (10)
              6 RETURN_VALUE
 
 10     >>    8 LOAD_CONST               2 (20)
             10 RETURN_VALUE
 
prikaz2:
 13           0 LOAD_FAST                0 (x)
              2 POP_JUMP_IF_FALSE       12
 
 14           4 LOAD_FAST                1 (y)
              6 POP_JUMP_IF_FALSE       12
 
 15           8 LOAD_CONST               1 (10)
             10 RETURN_VALUE
 
 16     >>   12 LOAD_CONST               2 (20)
             14 RETURN_VALUE

10. Překlad řídicí struktury typu if-then-else

Úplná programová konstrukce if-then-else vznikne z neúplné konstrukce typu if-then doplněním programové větve else, což je samozřejmě podporováno i bajtkódem virtuálního stroje Pythonu – musí se jednat o úplné rozvětvení a tedy typicky o kombinaci podmíněného skoku se skokem nepodmíněným:

def prikaz1(x):
    if x:
        return 10
    else:
        return 20
 
def prikaz2(x, y):
    if x:
        if y:
            return 10
        else:
            return 20
    else:
        return 30
 
#
# Vse je nutne otestovat.
#
def main():
    print(prikaz1(True))
    print(prikaz1(False))
    print(prikaz2(True, True))
    print(prikaz2(True, False))
    print(prikaz2(False, True))
    print(prikaz2(False, False))
 
def disassemble():
    from dis import dis
 
    print("\nprikaz1:")
    dis(prikaz1)
 
    print("\nprikaz2:")
    dis(prikaz2)
 
main()
 
disassemble()

V bajtkódu nyní uvidíme dvě možnosti. První variantou je plný rozeskok, přičemž každá větev končí instrukcí RETURN_VALUE:

prikaz1:
  8           0 LOAD_FAST                0 (x)
              2 POP_JUMP_IF_FALSE        8
 
  9           4 LOAD_CONST               1 (10)
              6 RETURN_VALUE
 
 11     >>    8 LOAD_CONST               2 (20)
             10 RETURN_VALUE
             12 LOAD_CONST               0 (None)
             14 RETURN_VALUE

Druhá varianta spočívá ve využití již výše zmíněné kombinace podmíněného a nepodmíněného skoku, tedy zde konkrétně instrukcí POP_JUMP_IF_FALSE a JUMP_FORWARD:

prikaz2:
 14           0 LOAD_FAST                0 (x)
              2 POP_JUMP_IF_FALSE       18
 
 15           4 LOAD_FAST                1 (y)
              6 POP_JUMP_IF_FALSE       12
 
 16           8 LOAD_CONST               1 (10)
             10 RETURN_VALUE
 
 18     >>   12 LOAD_CONST               2 (20)
             14 RETURN_VALUE
             16 JUMP_FORWARD             4 (to 22)
 
 20     >>   18 LOAD_CONST               3 (30)
             20 RETURN_VALUE
        >>   22 LOAD_CONST               0 (None)
             24 RETURN_VALUE

11. Jednoduchá programová smyčka typu while

Základním a ve skutečnosti i univerzálním typem smyčky je ve strukturovaném programování smyčka typu while. U tohoto typu smyčky se podmínka pro ukončení smyčky kontroluje vždy na začátku každé iterace, což mj. znamená, že se tělo smyčky nemusí provést ani jednou, a to v případě, kdy podmínka není splněna již před provedením první iterace, tj. před vstupem do smyčky. Z tohoto důvodu se konstrukce while používá například v situacích, kdy se mají zpracovávat vstupní data (čtená ze souboru, z databázové tabulky atd.), u nichž není zřejmé, kolik údajů se bude načítat a zda se vůbec nějaký údaj na vstupu bude nacházet. Zajímavé je, že ostatní typy smyček lze většinou přepsat právě na programovou smyčku typu while, i když – jak uvidíme na příkladu Pythonu – se nemusí vždy jednat o elegantní řešení. Pojďme si tedy na velmi jednoduchých příkladech ukázat způsob překladu této programové smyčky do bajtkódu virtuálního stroje Pythonu. Nejprve si ukážeme překlad smyčky, v níž se pouze snižuje hodnota počitadla kontrolovaného v podmínce pro další iteraci.

def loop(x):
    while x > 0:
        x = x - 1
    return x
 
 
#
# Vse je nutne otestovat.
#
def main():
    print(loop(10))
 
#
# Vypsani bajtkodu testovane funkce
#
def disassemble():
    from dis import dis
 
    print("\nloop:")
    dis(loop)
 
main()
 
disassemble()

Bajtkód vytvořený pro virtuální stroj Pythonu je kupodivu poměrně dlouhý (což prakticky znamená i pomalejší vyhodnocení v porovnání s Luou). Můžeme zde vidět jak nepodmíněný skok JUMP_ABSOLUTE, tak i podmíněný skok JUMP_IF_FALSE, pro nějž se podmínka vyhodnocuje instrukcí COMPARE_OP (již známe). Povšimněte si taktéž nutnosti použití instrukce POP_TOP sloužící pro „uklizení“ obsahu zásobníku operandů:

loop:
  8     >>    0 LOAD_FAST                0 (x)
              2 LOAD_CONST               1 (0)
              4 COMPARE_OP               4 (>)
              6 POP_JUMP_IF_FALSE       18
 
  9           8 LOAD_FAST                0 (x)
             10 LOAD_CONST               2 (1)
             12 BINARY_SUBTRACT
             14 STORE_FAST               0 (x)
             16 JUMP_ABSOLUTE            0
 
 10     >>   18 LOAD_FAST                0 (x)
             20 RETURN_VALUE

12. Programová smyčka typu while se složitějším tělem

Pro zajímavost se ještě podívejme na poněkud složitější demonstrační příklady, v nichž je opět použita programová smyčka typu while. Tyto příklady slouží k výpočtu exponenciální funkce 2x, popř. y2x (což je v Pythonu vlastně zbytečné protože pro tuto operaci máme k dispozici operátor **). Vzhledem k tomu, že hodnoty x a y nejsou dopředu známé, nemůže překladač provádět prakticky žádné optimalizace smyčky (to mohl teoreticky udělat v předchozím příkladu, což se však, jak již víme, ve skutečnosti nestalo, alespoň nikoli na úrovni bajtkódu).

def loop(x, y):
    while x > 0:
        x = x - 1
        y = y * 2
    return y
 
 
#
# Vse je nutne otestovat.
#
def main():
    print(loop(10, 1))
 
def disassemble():
    from dis import dis
 
    print("\nloop:")
    dis(loop)
 
main()
 
disassemble()

Opět můžeme vidět, že se test na ukončení smyčky provádí v úvodní části kódu (porovnání+podmíněný skok), zatímco na konci smyčky je použit nepodmíněný skok na začátek:

loop:
  8     >>    0 LOAD_FAST                0 (x)
              2 LOAD_CONST               1 (0)
              4 COMPARE_OP               4 (>)
              6 POP_JUMP_IF_FALSE       26
 
  9           8 LOAD_FAST                0 (x)
             10 LOAD_CONST               2 (1)
             12 BINARY_SUBTRACT
             14 STORE_FAST               0 (x)
 
 10          16 LOAD_FAST                1 (y)
             18 LOAD_CONST               3 (2)
             20 BINARY_MULTIPLY
             22 STORE_FAST               1 (y)
             24 JUMP_ABSOLUTE            0
 
 11     >>   26 LOAD_FAST                1 (y)
             28 RETURN_VALUE
Poznámka: překlad do bajtkódu je v případě Pythonu poměrně přímočará mechanická záležitost; žádné složitější optimalizace zde nejsou patrné.

13. Dvě vnořené smyčky typu while – výpis řady prvočísel

Nastává čas ukázat si způsob překladu algoritmu, v němž se používají dvě vnořené programové smyčky typu while. Zde je již situace zajímavější, protože delší zdrojový kód dává překladači alespoň teoretickou možnost provedení různých optimalizací. Jako příklad jsem vybral výpočet řady prvočísel od 2 do zadané maximální hodnoty, přičemž se pro výpočet používá ta nejjednodušší a současně i nejpomalejší :-) metoda založená na postupném zjišťování, zda je zadaná hodnota n dělitelná nějakým celým číslem v rozsahu 2..n-1 (teoreticky je možné smyčku ukončit dříve, ovšem nechtěl jsem zbytečně do demonstračního algoritmu přidávat volání funkcí).

V Pythonu je možné tento algoritmus zapsat následujícím způsobem:

def primeNumbers(min, max):
    i = min
    while i <= max:        # projit vsechny hodnoty od min do max
        j = 2
        while j < i:       # (lze optimalizovat a zkratit smycku!)
            if i % j == 0: # je mozne celociselne delit?
                break      # - pak ovsem nejde o prvocislo
            j = j + 1      # vyzkouset dalsiho kandidata na celociselne deleni
        if j == i:         # pokud jsme dosli az k cislu i
            print(i)       # nedoslo nikdy k celociselnemu deleni
        i = i + 1          # dalsi hodnota v posloupnosti
 
#
# Vse je nutne otestovat.
#
def main():
    primeNumbers(2, 1000)
 
def disassemble():
    from dis import dis
 
    print("\nprimeNumbers:")
    dis(primeNumbers)
 
main()
 
disassemble()

Překlad do bajtkódu je v tomto případě dosti těžkopádný a jedná se o jeden z důvodů, proč je interpret Pythonu tak pomalý v porovnání s nejbližší (technologickou) konkurencí:

primeNumbers:
  9           0 LOAD_FAST                0 (min)      # i = min
              2 STORE_FAST               2 (i)
 
 10     >>    4 LOAD_FAST                2 (i)
              6 LOAD_FAST                1 (max)
              8 COMPARE_OP               1 (<=)
             10 POP_JUMP_IF_FALSE       74            # while(i <= max):
 
 11          12 LOAD_CONST               1 (2)
             14 STORE_FAST               3 (j)
 
 12     >>   16 LOAD_FAST                3 (j)        # j = 2
             18 LOAD_FAST                2 (i)
             20 COMPARE_OP               0 (<)
             22 POP_JUMP_IF_FALSE       48
 
 13          24 LOAD_FAST                2 (i)        # if i % j == 0:
             26 LOAD_FAST                3 (j)
             28 BINARY_MODULO
             30 LOAD_CONST               2 (0)
             32 COMPARE_OP               2 (==)
             34 POP_JUMP_IF_FALSE       38    # break
 
 14          36 JUMP_ABSOLUTE           48
 
 15     >>   38 LOAD_FAST                3 (j)        # j = j + 1
             40 LOAD_CONST               3 (1)
             42 BINARY_ADD
             44 STORE_FAST               3 (j)
             46 JUMP_ABSOLUTE           16            # konec vnitřní smyčky
 
 16     >>   48 LOAD_FAST                3 (j)        # if (j == i):
             50 LOAD_FAST                2 (i)
             52 COMPARE_OP               2 (==)
             54 POP_JUMP_IF_FALSE       64
 
 17          56 LOAD_GLOBAL              0 (print)
             58 LOAD_FAST                2 (i)        # print i
             60 CALL_FUNCTION            1
             62 POP_TOP
 
 18     >>   64 LOAD_FAST                2 (i)        # i = i + 1
             66 LOAD_CONST               3 (1)
             68 BINARY_ADD
             70 STORE_FAST               2 (i)
             72 JUMP_ABSOLUTE            4            # konec vnější smyčky
        >>   74 LOAD_CONST               0 (None)
             76 RETURN_VALUE

14. Jednoduchá programová smyčka typu do-while/repeat-until

Kromě programové smyčky typu while se ve strukturovaném programování velmi často setkáme i se smyčkou typu do-while, popř. s ekvivalentní smyčkou repeat-until. Zatímco se ve smyčce while provádí test na ukončení smyčky před každou iterací, je v případě do-whilerepeat-until test prováděn na konci těla smyčky a tedy již po dokončení jedné iterace, což mimochodem znamená, že tělo smyčky bude vždy provedeno alespoň jedenkrát. Rozdíl mezi smyčkami do-while (céčková větev programovacích jazyků) a repeat-until (Pascal, Modula-2, Lua) spočívá pouze v tom, zda je splnění zapsané podmínky považováno za vstup do další iterace či naopak za důvod pro ukončení celé programové smyčky. Zatímco programovací jazyk Java podporuje smyčku do-while, najdeme v jazyku Lua smyčku repeat-until a v Pythonu je nutné tuto smyčku emulovat s využitím smyčky typu while doplněnou o podmínku na konci, popř. doplněnou o další lokální proměnnou:

def loop(x):
    while True:
        x = x - 1
        if x <= 0: break
    return x
 
#
# Vse je nutne otestovat.
#
def main():
    print(loop(10))
  
#
# Vypsani bajtkodu testovane funkce
#
def disassemble():
    from dis import dis
  
    print("\nloop:")
    dis(loop)
  
main()
  
disassemble()

V případě Pythonu je smyčka do-while ve skutečnosti pouze emulována, čemuž odpovídá i (relativně přímočarý) překlad do bajtkódu:

loop:
 10     >>    0 LOAD_FAST                0 (x)
              2 LOAD_CONST               1 (1)
              4 BINARY_SUBTRACT                   # x=x-1
              6 STORE_FAST               0 (x)
 
 11           8 LOAD_FAST                0 (x)
             10 LOAD_CONST               2 (0)
             12 COMPARE_OP               1 (<=)   # vyhodnocení podmínky
             14 POP_JUMP_IF_FALSE        0        # podmíněný skok na začátek smyčky
             16 JUMP_ABSOLUTE           20        # výskok ze smyčky
             18 JUMP_ABSOLUTE            0        # nepodmíněný skok na začátek smyčky
 
 12     >>   20 LOAD_FAST                0 (x)    # návratová hodnota
             22 RETURN_VALUE
Poznámka: povšimněte si, že skok na adrese 18 je vlastně zbytečný, protože k této instrukci se řízení programu nikdy nedostane.

15. Příloha A: základní instrukce pro manipulaci se zásobníkem

Před provedením aritmetických, logických i bitových instrukcí jakož i před zavoláním jiné funkce, popř. metody je mnohdy nutné data vložit do vhodných registrů a/nebo na správné místo v zásobníku operandů. K tomu slouží instrukce popsané v této příloze.

Python VM je zásobníkový virtuální stroj, proto jeho instrukční sada obsahuje několik instrukcí, které se nápadně podobají slovům používaným v programovacím jazyku Forth (ono se ostatně není čemu divit, protože se jedná skutečně o základní „zásobníkové“ instrukce). Důležité je, že tyto instrukce již nepotřebují být rozděleny podle toho s jakými daty pracují, protože každému prvku uloženém na zásobníku operandů je stejně přiřazen datový tag, který je těmito instrukcemi využíván:

# Instrukce Operand 1 Operand 2 Operace
1 POP_TOP implicitní implicitní odstraní jednu položku z vrcholu zásobníku operandů
2 ROT_TWO implicitní implicitní rotace (swap) dvou položek
3 ROT_THREE implicitní implicitní rotace (roll) tří položek
4 ROT_FOUR implicitní implicitní rotace čtyř položek
5 DUP_TOP implicitní implicitní duplikace položky na zásobníku operandů

16. Příloha B: aritmetické a logické operace

Začněme popisem aritmetických instrukcí, které jsou velmi jednoduché a snadno pochopitelné.

Aritmetické instrukce pracují s hodnotami umístěnými na zásobníku operandů, podobně jako je tomu například i v JVM. Všechny instrukce navíc mohou pracovat i pro různé typy objektů (to odpovídá přetížení příslušných operandů):

# Instrukce Operand 1 Operand 2 Operace
1 BINARY_ADD číslo/objekt číslo/objekt součet
2 BINARY_SUBTRACT číslo/objekt číslo/objekt rozdíl
3 BINARY_MULTIPLY číslo/objekt číslo/objekt součin
4 BINARY_DIVIDE číslo/objekt číslo/objekt podíl
5 BINARY_MODULO číslo/objekt číslo/objekt podíl modulo
6 BINARY_POWER číslo/objekt číslo/objekt umocnění
7 UNARY_NEGATIVE číslo/objekt × změna znaménka

Druhou skupinou instrukcí jsou instrukce, pomocí nichž se implementují téměř všechny základní bitové a logické operace.

# Instrukce Operand 1 Operand 2 Operace
1 BINARY_AND číslo/objekt číslo/objekt bitový/logický součin
2 BINARY_OR číslo/objekt číslo/objekt bitový/logický součet
3 BINARY_XOR číslo/objekt číslo/objekt bitová/logická nonekvivalence
7 UNARY_NOT číslo/objekt × negace

V Python VM nalezneme i následující instrukce pro posuny (pro celočíselné operandy se jedná o aritmetický posun doleva a doprava):

# Instrukce Operand 1 Operand 2 Operace
1 BINARY_LSHIFT číslo/objekt číslo/objekt posun doleva
2 BINARY_RSHIFT číslo/objekt číslo/objekt posun doprava

17. Příloha C: podmíněné a nepodmíněné skoky, větvení kódu

Ve virtuálním stroji programovacího jazyka Python nalezneme celkem šest základních instrukcí skoku – konkrétně dvě instrukce určené pro provedení nepodmíněných skoků a tři instrukce pro provedení skoků podmíněných:

bitcoin školení listopad 24

# Instrukce Operand 1 Operand 2 Prováděná operace
1 JUMP_ABSOLUTE index instrukce, na kterou skok vede × skok na zadaný index (lokální adresu)
2 JUMP_FORWARD index instrukce, na kterou skok vede × skok na relativní index (je zadána delta oproti současné adrese)
         
3 POP_JUMP_IF_TRUE index instrukce, na kterou skok vede × podmíněný skok na základě obsahu TOS; obsah TOS je odstraněn
4 POP_JUMP_IF_FALSE index instrukce, na kterou skok vede × podmíněný skok na základě obsahu TOS (opačná podmínka); obsah TOS je odstraněn
5 JUMP_IF_TRUE_OR_POP index instrukce, na kterou skok vede × pokud TOS==true, provede se skok, v opačném případě se TOS odstraní
6 JUMP_IF_FALSE_OR_POP index instrukce, na kterou skok vede × opačné chování, než je tomu v předchozí instrukci

18. Příloha D: Tabulka všech operačních kódů instrukcí Python VM

Virtuální stroj programovacího jazyka Python využívá v bajtkódu instrukce, jejichž operační kód má šířku osmi bitů (jeden bajt). To znamená, že je teoreticky možné využít 256 různých instrukcí. Ve skutečnosti jsou však některé operační kódy neobsazeny, což je ostatně patrné i při pohledu na tabulku se seznamem všech (v současnosti používaných) instrukcí Python VM.

Poznámka: tabulka je platná pro Python 3.10. Dřívější varianty operačních kódů se u přibližně 10% instrukcí liší!
Operační kód Instrukce Význam
0×00 STOP_CODE konec bajtkódu, není využito interpretrem
     
0×01 POP_TOP odstraní položku ze zásobníku operandů
0×02 ROT_TWO rotace (swap) dvou položek
0×03 ROT_THREE rotace (roll) tří položek
0×04 DUP_TOP duplikace položky na zásobníku operandů
0×05 ROT_FOUR rotace čtyř položek
     
     
0×06 neobsazeno (neobsazeno)
0×07 neobsazeno (neobsazeno)
0×08 neobsazeno (neobsazeno)
     
0×09 NOP žádná operace (výplň)
     
0×0a UNARY_POSITIVE unární operátor +
0×0b UNARY_NEGATIVE změna znaménka
0×0c UNARY_NOT negace
0×0d UNARY_CONVERT implementace konstrukce backtick (`)
     
0×0e neobsazeno (neobsazeno)
     
0×0f UNARY_INVERT bitová negace
     
0×10 BINARY_MATRIX_MULTIPLY (nově obsazeno maticovým operátorem násobení)
0×11 INBINARY_MATRIX_MULTIPLY (nově obsazeno maticovým operátorem násobení)
0×12 neobsazeno (neobsazeno)
     
0×13 BINARY_POWER umocnění
0×14 BINARY_MULTIPLY součin
0×15 BINARY_DIVIDE podíl
0×16 BINARY_MODULO podíl modulo
0×17 BINARY_ADD součet
0×18 BINARY_SUBTRACT rozdíl
     
0×19 BINARY_SUBSCR přístup k prvkům složeného datového typu (řetězec, seznam, n-tice)
     
0×1a BINARY_FLOOR_DIVIDE podíl (//)
0×1b BINARY_TRUE_DIVIDE podíl
0×1c INPLACE_FLOOR_DIVIDE podíl (odlišný přístup k prvkům na zásobníku)
0×1d INPLACE_TRUE_DIVIDE podíl (odlišný přístup k prvkům na zásobníku)
     
0×1e GET_LEN získání délky sekvence (řetězec, seznam, n-tice)
0×1f MATCH_MAPPING pattern matching
0×20 MATCH_SEQUENCE pattern matching
0×21 MATCH_KEYS pattern matching
     
0×22 COPY_DICT_WITHOUT_KEYS (nově obsazeno)
0×23 neobsazeno (neobsazeno)
0×24 neobsazeno (neobsazeno)
0×25 neobsazeno (neobsazeno)
0×26 neobsazeno (neobsazeno)
0×27 neobsazeno (neobsazeno)
     
0×28 STORE_SLICE+0 přístup k prvkům složeného datového typu (typicky seznamu)
0×29 STORE_SLICE+1 přístup k prvkům složeného datového typu (typicky seznamu)
0×2a STORE_SLICE+2 přístup k prvkům složeného datového typu (typicky seznamu)
0×2b STORE_SLICE+3 přístup k prvkům složeného datového typu (typicky seznamu)
     
0×2c WITH_EXCEPT_START (nově obsazeno)
0×2d GET_AITER (nově obsazeno, await/async)
0×2e GET_ANEXT (nově obsazeno, await/async)
0×2f BEFORE_ASYNC_WITH (nově obsazeno, await/async)
0×30 END_ASYNC_FOR (nově obsazeno, await/async)
0×31 neobsazeno (neobsazeno)
     
0×32 DELETE_SLICE+0 přístup k prvkům složeného datového typu (typicky seznamu)
0×33 DELETE_SLICE+1 přístup k prvkům složeného datového typu (typicky seznamu)
0×34 DELETE_SLICE+2 přístup k prvkům složeného datového typu (typicky seznamu)
0×35 DELETE_SLICE+3 přístup k prvkům složeného datového typu (typicky seznamu)
     
0×36 STORE_MAP uloží do mapy další dvojici klíč:hodnota
     
0×37 INPLACE_ADD varianta instrukce BINARY_ADD (odlišný přístup k prvkům na zásobníku)
0×38 INPLACE_SUBTRACT varianta instrukce BINARY_SUBTRACT (odlišný přístup k prvkům na zásobníku)
0×39 INPLACE_MULTIPLY varianta instrukce BINARY_MULTIPLY (odlišný přístup k prvkům na zásobníku)
0×3a INPLACE_DIVIDE varianta instrukce BINARY_DIVIDE (odlišný přístup k prvkům na zásobníku)
0×3b INPLACE_MODULO varianta instrukce BINARY_MODULO (odlišný přístup k prvkům na zásobníku)
     
0×3c STORE_SUBSCR přístup k prvkům n-tice, seznamu či řetězce
0×3d DELETE_SUBSCR vymazání prvku složeného datového typu
     
0×3e BINARY_LSHIFT posun doleva
0×3f BINARY_RSHIFT posun doprava
0×40 BINARY_AND bitový/logický součin
0×41 BINARY_XOR bitová/logická nonekvivalence
0×42 BINARY_OR bitový/logický součet
     
0×43 INPLACE_POWER operace **
     
0×44 GET_ITER získání iterátoru pro prvek uložený na TOS
     
0×45 GET_YIELD_FROM_ITER získání reference na pozastavený kód v korutině
     
0×46 PRINT_EXPR tisk výrazu
0×47 LOAD_BUILD_CLASS (nově obsazeno, předtím použito pro varianty operace PRINT)
0×48 YIELD_FROM (nově obsazeno, předtím použito pro varianty operace PRINT)
0×49 GET_AWAITABLE (nově obsazeno, předtím použito pro varianty operace PRINT)
0×4a LOAD_ASSERTION_ERROR (nově obsazeno, předtím použito pro varianty operace PRINT)
     
0×4b INPLACE_LSHIFT posun doleva
0×4c INPLACE_RSHIFT posun doprava
0×4d INPLACE_AND bitový/logický součin
0×4e INPLACE_XOR bitová/logická nonekvivalence
0×4f INPLACE_OR bitový/logický součet
     
0×50 BREAK_LOOP přerušení programové smyčky
     
0×51 WITH_CLEANUP vymazání zásobníku při ukončení bloku with
0×52 LOAD_LOCALS použito při definici tříd
0×53 RETURN_VALUE návrat z funkce a vrácení hodnoty z TOS
     
0×54 IMPORT_STAR nahraje všechny (nelokální) symboly do jmenného prostoru
     
0×55 EXEC_STMT volá exec
0×56 YIELD_VALUE použito v generátoru pro vrácení (další) hodnoty
0×57 POP_BLOCK odstraní blok ze zásobníku (konec smyčky atd.)
0×58 END_FINALLY ukončí klauzuli finally
     
0×59 BUILD_CLASS vytvoří novou třídu
0×5a STORE_NAME práce se jmény uloženými v co_names
0×5b DELETE_NAME práce se jmény uloženými v co_names
     
0×5c UNPACK_SEQUENCE rozdělení sekvence na jednotlivé prvky a jejich uložení na zásobník
0×5d FOR_ITER použito při implementaci programové smyčky
0×5e LIST_APPEND/UNPACK_EX volá list.append()
0×5f STORE_ATTR práce se jmény uloženými v co_names
0×60 DELETE_ATTR práce se jmény uloženými v co_names
0×61 STORE_GLOBAL práce s globálními jmény
0×62 DELETE_GLOBAL práce s globálními jmény
     
0×63 ROTN instrukce ROT opakovaná 1× až 5×
     
0×64 LOAD_CONST práce s hodnotami uloženými v co_const
0×65 LOAD_NAME práce s hodnotami uloženými v co_names
     
0×66 BUILD_TUPLE vytvoření nové n-tice
0×67 BUILD_LIST vytvoření nového seznamu
0×68 BUILD_SET vytvoření nové množiny
0×69 BUILD_MAP vytvoření nové mapy
0×6a LOAD_ATTR přečtení atributu objektu
     
0×6b COMPARE_OP provedení zvolené Booleovské operace
     
0×6c IMPORT_NAME import modulu
0×6d IMPORT_FROM import modulu
     
0×6e JUMP_FORWARD skok na relativní index (je zadána delta oproti současné adrese)
0×6f JUMP_IF_FALSE_OR_POP opačné chování, než je tomu v předchozí instrukci
0×70 JUMP_IF_TRUE_OR_POP pokud TOS==true, provede se skok, v opačném případě se TOS odstraní
0×71 JUMP_ABSOLUTE skok na zadaný index (lokální adresu)
0×72 POP_JUMP_IF_FALSE podmíněný skok na základě obsahu TOS; obsah TOS je odstraněn
0×73 POP_JUMP_IF_TRUE podmíněný skok na základě obsahu TOS (opačná podmínka); obsah TOS je odstraněn
     
0×74 LOAD_GLOBAL uložení zvoleného globálního jména na TOS
     
0×75 IS_OP (nově obsazeno)
0×76 CONTAINS_OP (nově obsazeno)
     
0×77 CONTINUE_LOOP implementace příkazu continue
0×78 SETUP_LOOP příprava programové smyčky (vytvoření bloku atd.)
0×79 SETUP_EXCEPT vytvoření kopie bloku try na zásobníku
0×7a SETUP_FINALLY vytvoření kopie bloku try na zásobníku
     
0×7b neobsazeno (neobsazeno)
     
0×7c LOAD_FAST uložení reference lokální proměnné na zásobník
0×7d STORE_FAST uložení hodnoty do lokální proměnné z TOS
0×7e DELETE_FAST vymazání lokální proměnné
     
0×7f neobsazeno (neobsazeno)
0×80 neobsazeno (neobsazeno)
0×81 GEN_START (nově obsazeno)
     
0×82 RAISE_VARARGS vyhození výjimky
0×83 CALL_FUNCTION volání funkce
0×84 MAKE_FUNCTION uložení reference na novou funkci na TOS
0×85 BUILD_SLICE implementace funkce slice()
0×86 MAKE_CLOSURE vytvoření uzávěru
0×87 LOAD_CLOSURE uloží referenci na uzávěr
0×88 LOAD_DEREF práce s uzávěry a/nebo objekty
0×89 STORE_DEREF práce s uzávěry a/nebo objekty
0×8a DELETE_DEREF práce s uzávěry a/nebo objekty
     
0×8b neobsazeno (neobsazeno)
     
0×8c CALL_FUNCTION_VAR zavolání funkce s proměnným počtem argumentů
0×8d CALL_FUNCTION_KW zavolání funkce s pojmenovanými argumenty
0×8e CALL_FUNCTION_VAR_KW zavolání funkce – kombinace obou předchozích variant
     
0×8f SETUP_WITH začátek bloku with
     
0×90 EXTENDED_ARG podpora pro argumenty funkcí, jichž je více než 65535
     
0×91 LIST_APPEND (nově obsazeno, přidání elementu do seznamu)
     
0×92 SET_ADD rozšíření bajtkódu: práce s množinou
0×93 MAP_ADD rozšíření bajtkódu: práce s mapou
     
0×94 LOAD_CLASSDEREF (nově obsazeno)
     
0×95 neobsazeno (neobsazeno)
0×96 neobsazeno (neobsazeno)
0×97 neobsazeno (neobsazeno)
     
0×98 MATCH_CLASS pattern matching
     
0×99 neobsazeno (neobsazeno)
     
0×9a SETUP_ASYNC_WITH (nově obsazeno, async/await)
0×9b FORMAT_VALUE (nově obsazeno pro formátovací řetězec)
0×9c BUILD_CONST_KEY_MAP (konstruktory)
0×9d BUILD_STRING (konstruktory)
     
0×9e neobsazeno (neobsazeno)
0×9f neobsazeno (neobsazeno)
     
0×a0 LOAD_METHOD (nově obsazeno)
0×a1 CALL_METHOD (nově obsazeno)
0×a2 LIST_EXTEND (nově obsazeno)
0×a3 SET_UPDATE (nově obsazeno)
0×a4 DICT_MERGE (nově obsazeno)
0×a5 DICT_UPDATE (nově obsazeno)
     
0×a6 neobsazeno (neobsazeno)
0×a7 neobsazeno (neobsazeno)
0×a8 neobsazeno (neobsazeno)
0×a9 neobsazeno (neobsazeno)
0×aa neobsazeno (neobsazeno)
0×ab neobsazeno (neobsazeno)
0×ac neobsazeno (neobsazeno)
0×ad neobsazeno (neobsazeno)
0×ae neobsazeno (neobsazeno)
0×af neobsazeno (neobsazeno)
0×b0 neobsazeno (neobsazeno)
0×b1 neobsazeno (neobsazeno)
0×b2 neobsazeno (neobsazeno)
0×b3 neobsazeno (neobsazeno)
0×b4 neobsazeno (neobsazeno)
0×b5 neobsazeno (neobsazeno)
0×b6 neobsazeno (neobsazeno)
0×b7 neobsazeno (neobsazeno)
0×b8 neobsazeno (neobsazeno)
0×b9 neobsazeno (neobsazeno)
0×ba neobsazeno (neobsazeno)
0×bb neobsazeno (neobsazeno)
0×bc neobsazeno (neobsazeno)
0×bd neobsazeno (neobsazeno)
0×be neobsazeno (neobsazeno)
0×bf neobsazeno (neobsazeno)
0×c0 neobsazeno (neobsazeno)
0×c1 neobsazeno (neobsazeno)
0×c2 neobsazeno (neobsazeno)
0×c3 neobsazeno (neobsazeno)
0×c4 neobsazeno (neobsazeno)
0×c5 neobsazeno (neobsazeno)
0×c6 neobsazeno (neobsazeno)
0×c7 neobsazeno (neobsazeno)
0×c8 neobsazeno (neobsazeno)
0×c9 neobsazeno (neobsazeno)
0×ca neobsazeno (neobsazeno)
0×cb neobsazeno (neobsazeno)
0×cc neobsazeno (neobsazeno)
0×cd neobsazeno (neobsazeno)
0×ce neobsazeno (neobsazeno)
0×cf neobsazeno (neobsazeno)
0×d0 neobsazeno (neobsazeno)
0×d1 neobsazeno (neobsazeno)
0×d2 neobsazeno (neobsazeno)
0×d3 neobsazeno (neobsazeno)
0×d4 neobsazeno (neobsazeno)
0×d5 neobsazeno (neobsazeno)
0×d6 neobsazeno (neobsazeno)
0×d7 neobsazeno (neobsazeno)
0×d8 neobsazeno (neobsazeno)
0×d9 neobsazeno (neobsazeno)
0×da neobsazeno (neobsazeno)
0×db neobsazeno (neobsazeno)
0×dc neobsazeno (neobsazeno)
0×dd neobsazeno (neobsazeno)
0×de neobsazeno (neobsazeno)
0×df neobsazeno (neobsazeno)
0×e0 neobsazeno (neobsazeno)
0×e1 neobsazeno (neobsazeno)
0×e2 neobsazeno (neobsazeno)
0×e3 neobsazeno (neobsazeno)
0×e4 neobsazeno (neobsazeno)
0×e5 neobsazeno (neobsazeno)
0×e6 neobsazeno (neobsazeno)
0×e7 neobsazeno (neobsazeno)
0×e8 neobsazeno (neobsazeno)
0×e9 neobsazeno (neobsazeno)
0×ea neobsazeno (neobsazeno)
0×eb neobsazeno (neobsazeno)
0×ec neobsazeno (neobsazeno)
0×ed neobsazeno (neobsazeno)
0×ee neobsazeno (neobsazeno)
0×ef neobsazeno (neobsazeno)
0×f0 neobsazeno (neobsazeno)
0×f1 neobsazeno (neobsazeno)
0×f2 neobsazeno (neobsazeno)
0×f3 neobsazeno (neobsazeno)
0×f4 neobsazeno (neobsazeno)
0×f5 neobsazeno (neobsazeno)
0×f6 neobsazeno (neobsazeno)
0×f7 neobsazeno (neobsazeno)
0×f8 neobsazeno (neobsazeno)
0×f9 neobsazeno (neobsazeno)
0×fa neobsazeno (neobsazeno)
0×fb neobsazeno (neobsazeno)
0×fc neobsazeno (neobsazeno)
0×fd neobsazeno (neobsazeno)
0×fe neobsazeno (neobsazeno)
0×ff neobsazeno (neobsazeno)

19. Repositář s demonstračními příklady

Zdrojové kódy všech prozatím popsaných demonstračních příkladů určených pro programovací jazyk Python 3 (některé přímo pro Python 3.10) byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/most-popular-python-libs. V případě, že nebudete chtít klonovat celý repositář (ten je ovšem stále velmi malý, dnes má velikost zhruba několik desítek kilobajtů), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:

# Demonstrační příklad Stručný popis příkladu Cesta
1 expression.py zdrojový kód, který se bude tokenizovat a parsovat, obsahující jednoduchý výraz https://github.com/tisnik/most-popular-python-libs/blob/master/ast/expression.py
2 err_expression.py zdrojový kód, který se bude tokenizovat a parsovat, obsahující chybný výraz https://github.com/tisnik/most-popular-python-libs/blob/master/ast/err_expression.py
3 async.py zdrojový kód, který se bude tokenizovat a parsovat, obsahující async a await https://github.com/tisnik/most-popular-python-libs/blob/master/ast/async.py
4 primes.py zdrojový kód, který se bude tokenizovat a parsovat, obsahující výpočet celočísel https://github.com/tisnik/most-popular-python-libs/blob/master/ast/primes.py
       
5 print_tokens.py výpis všech typů a hodnot tokenů pro aktuální verzi Pythonu https://github.com/tisnik/most-popular-python-libs/blob/master/ast/print_tokens.py
6 tokenize_expression1.py tokenizace výrazu https://github.com/tisnik/most-popular-python-libs/blob/master/ast/toke­nize_expression1.py
7 tokenize_expression2.py tokenizace výrazu, alternativní způsob otevření zdrojového souboru https://github.com/tisnik/most-popular-python-libs/blob/master/ast/toke­nize_expression2.py
8 tokenize_expression3.py tokenizace výrazu s více operátory https://github.com/tisnik/most-popular-python-libs/blob/master/ast/toke­nize_expression3.py
9 tokenize_expression4.py tokenizace výrazu s více operátory, výpis přesného typu tokenu https://github.com/tisnik/most-popular-python-libs/blob/master/ast/toke­nize_expression4.py
10 tokenize_async.py tokenizace zdrojového kódu async.py https://github.com/tisnik/most-popular-python-libs/blob/master/ast/tokenize_async.py
11 tokenize_primes.py tokenizace zdrojového kódu primes.py https://github.com/tisnik/most-popular-python-libs/blob/master/ast/toke­nize_primes.py
12 tokenize_primes2.py tokenizace zdrojového kódu primes.py, výpis přesného typu tokenu https://github.com/tisnik/most-popular-python-libs/blob/master/ast/toke­nize_primes2.py
       
13 parse_expression.py parsing zdrojového kódu s výrazem https://github.com/tisnik/most-popular-python-libs/blob/master/ast/parse_ex­pression.py
14 parse_expression3_10.py parsing zdrojového kódu s výrazem, vylepšený výpis v Pythonu 3.10 https://github.com/tisnik/most-popular-python-libs/blob/master/ast/parse_ex­pression3_10.py
15 parse_async.py parsing zdrojového kodu async.py https://github.com/tisnik/most-popular-python-libs/blob/master/ast/parse_async.py
16 parse_async3_10.py parsing zdrojového kodu async.py, vylepšený výpis v Pythonu 3.10 https://github.com/tisnik/most-popular-python-libs/blob/master/ast/parse_a­sync3_10.py
17 parse_primes.py parsing zdrojového kodu primes.py https://github.com/tisnik/most-popular-python-libs/blob/master/ast/parse_primes.py
18 parse_primes3_10.py parsing zdrojového kodu primes.py, vylepšený výpis v Pythonu 3.10 https://github.com/tisnik/most-popular-python-libs/blob/master/ast/parse_pri­mes3_10.py
       
19 traverse_expression1.py průchod AST, nejjednodušší varianta https://github.com/tisnik/most-popular-python-libs/blob/master/ast/traver­se_expression1.py
20 traverse_expression2.py průchod AST, vzor Visitor https://github.com/tisnik/most-popular-python-libs/blob/master/ast/traver­se_expression2.py
21 traverse_expression3.py průchod AST, vzor Visitor, odsazení kódu https://github.com/tisnik/most-popular-python-libs/blob/master/ast/traver­se_expression3.py
22 traverse_expression4.py průchod AST, vzor Visitor, odsazení kódu, rozpoznání uzlů https://github.com/tisnik/most-popular-python-libs/blob/master/ast/traver­se_expression4.py
23 traverse_expression5.py průchod AST, vzor Visitor, odsazení kódu, rozpoznání uzlů https://github.com/tisnik/most-popular-python-libs/blob/master/ast/traver­se_expression5.py
24 traverse_expression5.py průchod AST, vzor Visitor, odsazení kódu, rozpoznání uzlů, vylepšení předchozího příkladu https://github.com/tisnik/most-popular-python-libs/blob/master/ast/traver­se_expression6.py
       
25 ifs1.py zdrojový kód s několika zanořenými podmínkami https://github.com/tisnik/most-popular-python-libs/blob/master/ast/ifs1.py
26 ifs2.py zdrojový kód s několika zanořenými úplnými podmínkami https://github.com/tisnik/most-popular-python-libs/blob/master/ast/ifs2.py
27 tokenize_ifs1.py tokenizace zdrojového kódu „ifs1.py“ https://github.com/tisnik/most-popular-python-libs/blob/master/ast/tokenize_ifs1.py
28 tokenize_ifs2.py tokenizace zdrojového kódu „ifs2.py“ https://github.com/tisnik/most-popular-python-libs/blob/master/ast/tokenize_ifs2.py
       
29 compile_tree.py překlad a spuštění AST https://github.com/tisnik/most-popular-python-libs/blob/master/ast/compile_tree.py
       
30 sprites.py zdrojový kód, jehož AST se bude zkoumat https://github.com/tisnik/most-popular-python-libs/blob/master/ast/sprites.py
31 traverse_code1.py průchod AST pro zvolený zdrojový kód https://github.com/tisnik/most-popular-python-libs/blob/master/ast/traver­se_code1.py
32 traverse_code2.py průchod AST, filtrace uzlů s definicí funkce https://github.com/tisnik/most-popular-python-libs/blob/master/ast/traver­se_code2.py
33 traverse_code3.py průchod AST, filtrace uzlů s definicí modulu, třídy a funkce https://github.com/tisnik/most-popular-python-libs/blob/master/ast/traver­se_code3.py
34 traverse_code4.py průchod AST, filtrace uzlů se smyčkou for https://github.com/tisnik/most-popular-python-libs/blob/master/ast/traver­se_code4.py
       
35 dis0.py překlad jednoduché funkce do bajtkódu https://github.com/tisnik/most-popular-python-libs/blob/master/bytecode/dis0.py
36 dis1.py překlad aritmetických výrazů do bajtkódu https://github.com/tisnik/most-popular-python-libs/blob/master/bytecode/dis1.py
37 dis2.py překlad výrazů s Booleovskými operátory do bajtkódu https://github.com/tisnik/most-popular-python-libs/blob/master/bytecode/dis2.py
38 dis3.py překlad výrazů s relačními operátory do bajtkódu https://github.com/tisnik/most-popular-python-libs/blob/master/bytecode/dis3.py
39 dis4.py překlad řídicí struktury typu if-then do bajtkódu https://github.com/tisnik/most-popular-python-libs/blob/master/bytecode/dis4.py
40 dis5.py překlad řídicí struktury typu if-then-else do bajtkódu https://github.com/tisnik/most-popular-python-libs/blob/master/bytecode/dis5.py
41 dis6.py jednoduchá programová smyčka typu while https://github.com/tisnik/most-popular-python-libs/blob/master/bytecode/dis6.py
42 dis7.py programová smyčka typu while se složitějším tělem https://github.com/tisnik/most-popular-python-libs/blob/master/bytecode/dis7.py
43 dis8.py dvě vnořené smyčky typu while – výpis řady prvočísel https://github.com/tisnik/most-popular-python-libs/blob/master/bytecode/dis8.py
44 dis9.py pokus o simulaci programové smyčky typu do-while https://github.com/tisnik/most-popular-python-libs/blob/master/bytecode/dis9.py
45 dis_a.py programový kód s klíčovými slovy asyncawait https://github.com/tisnik/most-popular-python-libs/blob/master/bytecode/dis_a.py

20. Odkazy na Internetu

  1. Inside The Python Virtual Machine
    https://leanpub.com/insidet­hepythonvirtualmachine
  2. module-py_compile
    https://docs.python.org/3­.8/library/py_compile.html
  3. Given a python .pyc file, is there a tool that let me view the bytecode?
    https://stackoverflow.com/qu­estions/11141387/given-a-python-pyc-file-is-there-a-tool-that-let-me-view-the-bytecode
  4. The structure of .pyc files
    https://nedbatchelder.com/blog/200804/the_str­ucture_of_pyc_files.html
  5. Python Bytecode: Fun With Dis
    http://akaptur.github.io/blog/2013/08/14/pyt­hon-bytecode-fun-with-dis/
  6. Python's Innards: Hello, ceval.c!
    http://tech.blog.aknin.na­me/category/my-projects/pythons-innards/
  7. Byterun
    https://github.com/nedbat/byterun
  8. Python Byte Code Instructions
    http://document.ihg.uni-duisburg.de/Documentation/Pyt­hon/lib/node56.html
  9. Python Byte Code Instructions
    https://docs.python.org/3­.2/library/dis.html#python-bytecode-instructions
  10. dis – Python module
    https://docs.python.org/2/li­brary/dis.html
  11. Comparison of Python virtual machines
    http://polishlinux.org/ap­ps/cli/comparison-of-python-virtual-machines/
  12. O-code
    http://en.wikipedia.org/wiki/O-code_machine
  13. Abstract syntax tree
    https://en.wikipedia.org/wi­ki/Abstract_syntax_tree
  14. Lexical analysis
    https://en.wikipedia.org/wi­ki/Lexical_analysis
  15. Parser
    https://en.wikipedia.org/wi­ki/Parsing#Parser
  16. Parse tree
    https://en.wikipedia.org/wi­ki/Parse_tree
  17. Derivační strom
    https://cs.wikipedia.org/wi­ki/Deriva%C4%8Dn%C3%AD_strom
  18. Python doc: ast — Abstract Syntax Trees
    https://docs.python.org/3/li­brary/ast.html
  19. Python doc: tokenize — Tokenizer for Python source
    https://docs.python.org/3/li­brary/tokenize.html
  20. SymbolTable
    https://docs.python.org/3­.8/library/symtable.html
  21. 5 Amazing Python AST Module Examples
    https://www.pythonpool.com/python-ast/
  22. Intro to Python ast Module
    https://medium.com/@wshanshan/intro-to-python-ast-module-bbd22cd505f7
  23. Golang AST Package
    https://golangdocs.com/golang-ast-package
  24. AP8, IN8 Regulární jazyky
    http://statnice.dqd.cz/home:inf:ap8
  25. AP9, IN9 Konečné automaty
    http://statnice.dqd.cz/home:inf:ap9
  26. AP10, IN10 Bezkontextové jazyky
    http://statnice.dqd.cz/home:inf:ap10
  27. AP11, IN11 Zásobníkové automaty, Syntaktická analýza
    http://statnice.dqd.cz/home:inf:ap11
  28. Introduction to YACC
    https://www.geeksforgeeks­.org/introduction-to-yacc/
  29. Introduction of Lexical Analysis
    https://www.geeksforgeeks­.org/introduction-of-lexical-analysis/?ref=lbp
  30. Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů
    https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu/
  31. Pygments – Python syntax highlighter
    http://pygments.org/
  32. Pygments (dokumentace)
    http://pygments.org/docs/
  33. Write your own filter
    http://pygments.org/docs/fil­terdevelopment/
  34. Write your own lexer
    http://pygments.org/docs/le­xerdevelopment/
  35. Write your own formatter
    http://pygments.org/docs/for­matterdevelopment/
  36. Jazyky podporované knihovnou Pygments
    http://pygments.org/languages/
  37. Pygments FAQ
    http://pygments.org/faq/
  38. Compiler Construction/Lexical analysis
    https://en.wikibooks.org/wi­ki/Compiler_Construction/Le­xical_analysis
  39. Compiler Design – Lexical Analysis
    https://www.tutorialspoin­t.com/compiler_design/com­piler_design_lexical_analy­sis.htm
  40. Lexical Analysis – An Intro
    https://www.scribd.com/do­cument/383765692/Lexical-Analysis
  41. Python AST Visualizer
    https://github.com/pombredanne/python-ast-visualizer
  42. What is an Abstract Syntax Tree
    https://blog.bitsrc.io/what-is-an-abstract-syntax-tree-7502b71bde27
  43. Why is AST so important
    https://medium.com/@obernar­dovieira/why-is-ast-so-important-b1e7d6c29260
  44. Emily Morehouse-Valcarcel – The AST and Me – PyCon 2018
    https://www.youtube.com/wat­ch?v=XhWvz4dK4ng
  45. Python AST Parsing and Custom Linting
    https://www.youtube.com/wat­ch?v=OjPT15y2EpE
  46. Chase Stevens – Exploring the Python AST Ecosystem
    https://www.youtube.com/wat­ch?v=Yq3wTWkoaYY
  47. Full Grammar specification
    https://docs.python.org/3/re­ference/grammar.html

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.