#22. コンパイラパス

コンパイラ パスは、AST およびシンボル テーブル情報を実行可能コード オブジェクトに変換します。

初期段階では構造的な質問に答えます。```text id="lfk0j9" tokenizer: What lexical units are in the source?

parser: What syntax tree do these tokens form?

symbol table: What scope does each name belong to? コンパイラーは実行に関する質問に答えます。text id="ju2gsy" Which bytecode instructions should be emitted? Which constants belong in co_consts? Which names belong in co_names? Which local variables belong in co_varnames? Where should jumps go? How large can the evaluation stack grow? Which exception table entries are needed? Which nested code objects must be created? ```出力はcode物体。そのコード オブジェクトは、CPython 評価ループによって実行できます。

22.1 コンパイルパイプラインでの位置

コンパイラは、解析とスコープ分析の後に待機します。```text id="9mc7xw" source ↓ tokenization ↓ parsing ↓ AST ↓ symbol table ↓ compiler passes ↓ code object ↓ interpreter execution


Python レベルでは、`compile()`関数はこのステージを公開します。```python id="2sweqj"
src = "x = 1 + 2\n"
code = compile(src, "<input>", "exec")

print(code)
```結果を調べることができます`dis`:

```python id="24znqy"
import dis

code = compile("x = 1 + 2\n", "<input>", "exec")
dis.dis(code)
```## 22.2 コンパイラへの主な入力

コンパイラは、初期段階の 2 つの主要なプロダクトを消費します。

|入力 |役割 |
| ------------- | ------------------------------------------------------------------ |
| AST |ステートメント、式、演算子、およびソースの場所について説明します。
|シンボルテーブル |名前の範囲分類を説明する |

ASTは次のように述べています。```text id="iqtzc2"
there is an assignment
target is Name("x")
value is BinOp(Constant(1), Add, Constant(2))
```シンボルテーブルには次のように記載されています。```text id="2kd8cu"
x is local/global in this scope
```これらを組み合わせると、バイトコードの生成が可能になります。

たとえば、コンパイラは次のいずれかを選択します。```text id="1lrzcy"
STORE_FAST
STORE_NAME
STORE_GLOBAL
STORE_DEREF
```スコープ情報に応じて。

## 22.3 コンパイラ出力: コード オブジェクト

コンパイラはコード オブジェクトを生成します。

コード オブジェクトには、バイトコードと実行メタデータが含まれます。

重要なフィールドには次のものが含まれます。```text id="6p1zp9"
co_code             bytecode bytes
co_consts           constants
co_names            global and attribute names
co_varnames         local variable names
co_freevars         free variable names
co_cellvars         cell variable names
co_filename         source filename
co_name             function or module name
co_qualname         qualified name
co_firstlineno      first source line
co_flags            execution flags
co_stacksize        required value stack size
```例:```python id="z2p4ol"
def add(a, b):
    return a + b

code = add.__code__

print(code.co_name)
print(code.co_varnames)
print(code.co_consts)
print(code.co_names)
print(code.co_stacksize)
```コード オブジェクトは不変の実行データです。関数オブジェクトは、グローバル、デフォルト、アノテーション、クロージャ セルなどの実行時コンテキストを含むコード オブジェクトをラップします。

## 22.4 コンパイルは再帰的です

ネストされた関数、ラムダ、内包表記、クラス、およびジェネレーター式は、ネストされたコード オブジェクトを作成します。

例:```python id="nxf9b9"
def outer(x):
    def inner(y):
        return x + y
    return inner
```モジュール コード オブジェクトには、次の関数コード オブジェクトが含まれています。`outer`

`outer`コード オブジェクトには、次の関数コード オブジェクトが含まれています。`inner`

概念的には:```text id="g7e4jo"
module code object
    constants:
        code object for outer
            constants:
                code object for inner
```実行時に実行すると、`def outer`から関数オブジェクトを作成します`outer`コードオブジェクト。実行中`def inner`内部`outer`必要に応じてクロージャーセルを使用して、別の関数オブジェクトを作成します。

## 22.5 ステートメントのコンパイル

通常、ステートメントはアクションを実行するバイトコードを出力します。

例:```python id="f8r2kh"
x = 1
```コンパイルパターン:```text id="vbnwbg"
compile right-hand expression
store result into target
```簡略化されたバイトコード:```text id="ozi020"
LOAD_CONST 1
STORE_NAME x
```例:```python id="jflq5g"
return x
```コンパイルパターン:```text id="j5nviu"
compile return expression
emit RETURN_VALUE
```簡略化されたバイトコード:```text id="5k1c90"
LOAD_FAST x
RETURN_VALUE
```正確なバイトコードは Python のバージョンによって異なりますが、構造的な考え方は安定しています。

## 22.6 式のコンパイル

式は、評価スタックに値を残すバイトコードを発行します。

例:```python id="thj50j"
a + b
```コンパイルパターン:```text id="f51gg5"
load a
load b
apply binary operation
```簡略化されたバイトコード:```text id="esgtwo"
LOAD_FAST a
LOAD_FAST b
BINARY_OP +
```式ステートメントは通常、その結果を破棄します。```python id="77sckz"
f()
```コンパイルパターン:```text id="u882gm"
load function
call function
pop result
```簡略化されたバイトコード:```text id="fo9egq"
LOAD_NAME f
CALL
POP_TOP
```コンパイラはスタック規律を維持します。すべての式には既知のスタック効果があります。

## 22.7 名前のコンパイル

名前のバイトコードはスコープの分類によって異なります。

例:```python id="7v96c6"
def f(a):
    return a

aはローカル変数です。text id="spceye" LOAD_FAST a 例:```python id="o6h1if" x = 1

def f(): return x


`x`内部からグローバルです`f`:

```text id="xmpdg1"
LOAD_GLOBAL x
```例:```python id="yptzi2"
def outer():
    x = 1

    def inner():
        return x

x内部からのクロージャ変数ですinner:

LOAD_DEREF x
```これが、バイトコードの発行に先立ってシンボル テーブルの分析が行われる理由です。

## 22.8 ターゲットのコンパイル

代入ターゲットは、値式とは異なる方法でコンパイルされます。

例:```python id="xpt040"
x = value
```対象操作:```text id="jfu667"
STORE_FAST or STORE_NAME or STORE_GLOBAL or STORE_DEREF
```例:```python id="rtgk3q"
obj.attr = value
```対象操作:```text id="r72bwa"
compile obj
compile value
STORE_ATTR attr
```例:```python id="s5jn7u"
items[i] = value
```対象操作:```text id="yyzjb4"
compile items
compile i
compile value
STORE_SUBSCR
```同じ AST 形状でもコンテキストに応じて異なるコンパイルが可能です。

比較する:```python id="i5tdmi"
obj.attr
```値として使用されます:```text id="14wnr4"
LOAD_ATTR attr
```そして:```python id="c46iv2"
obj.attr = 1
```ターゲットとして使用されます:```text id="7uswmv"
STORE_ATTR attr
```次のような AST コンテキスト`Load`、`Store`、 そして`Del`この区別を推進します。

## 22.9 制御フローのコンパイル

制御フローにはジャンプとラベルが必要です。

例:```python id="5u72qc"
if x:
    a = 1
else:
    a = 2
```コンパイルパターン:```text id="c7me6c"
compile test x
jump to else block if false
compile body
jump to end
else label:
compile else body
end label:
continue
```簡略化されたバイトコード形状:```text id="ukb3dq"
LOAD_NAME x
POP_JUMP_IF_FALSE else_label

LOAD_CONST 1
STORE_NAME a
JUMP_FORWARD end_label

else_label:
LOAD_CONST 2
STORE_NAME a

end_label:
```コンパイラは最初にシンボリック ジャンプまたは内部ラベルを発行します。後のアセンブリでは、それらが具体的なバイト オフセットまたは命令オフセットに解決されます。

## 22.10 ループ

ループには開始ラベル、終了ラベル、および場合によっては継続ターゲットが必要です。

例:```python id="28xqrv"
while x:
    work()
```コンパイルパターン:```text id="aq977s"
loop_start:
compile test
jump to loop_end if false
compile body
jump to loop_start
loop_end:
```例:```python id="u0a0kq"
for item in items:
    work(item)
```コンパイルパターン:```text id="fs8o3k"
compile iterable
get iterator
loop_start:
get next item or jump to loop_end
store item
compile body
jump to loop_start
loop_end:

breakループの出口にジャンプします。continueループ継続点にジャンプします。

ネストされたループにはコンパイラ ブロック スタックが必要なので、breakそしてcontinue正しい囲みループをターゲットにします。

22.11 例外処理

例外処理のコンパイルは、制御フローのメタデータが必要なため、より複雑になります。

例:python id="zbie3d" try: risky() except ValueError: recover() finally: cleanup() コンパイラは以下のバイトコードを生成する必要があります。```text id="7fd19n" normal execution exception matching handler entry handler cleanup finally execution reraising when needed


概念的には:```text id="9wg6kk"
protected bytecode range:
    risky()

handler:
    if exception matches ValueError:
        recover()

finally:
    cleanup()
```コンパイラは、以下を含む Python の正確な例外セマンティクスを保持する必要があります。`else``finally``raise`、例外チェーン、および例外ハンドラー変数のクリーンアップ。

## 22.12 関数定義のコンパイル

関数定義は 2 つの層でコンパイルされます。

例:```python id="9423bh"
def add(a, b):
    return a + b
```レイヤ 1: 関数本体をネストされたコード オブジェクトにコンパイルします。```text id="8pamlr"
code object for add:
    LOAD_FAST a
    LOAD_FAST b
    BINARY_OP +
    RETURN_VALUE
```レイヤ 2: 関数オブジェクトを作成する外側のステートメントをコンパイルします。```text id="ndgkj2"
LOAD_CONST <code object add>
MAKE_FUNCTION
STORE_NAME add
```この区別が中心です。

コンパイラが次のことを認識した場合、関数本体は実行されません。`def`。実行時に、`def`ステートメントは関数オブジェクトを作成し、それを関数名にバインドします。

## 22.13 デフォルト、アノテーション、およびデコレータ

関数定義には、デフォルト、アノテーション、デコレータが含まれる場合があります。

例:```python id="06culw"
@trace
def f(x: int, y=1) -> int:
    return x + y
```コンパイルでは以下を処理する必要があります。```text id="st02n8"
default argument value y=1
parameter annotation x: int
return annotation -> int
function body code object
function object creation
decorator application
name binding
```概念的な実行時の順序:```text id="e9t5yf"
evaluate decorator expressions
evaluate default values
evaluate annotations according to current rules
create function object
apply decorators from bottom to top
bind final function object to name
```コンパイラは、この順序を実装するバイトコードを出力します。

## 22.14 クラス定義のコンパイル

クラス定義もレイヤーでコンパイルされます。

例:```python id="c7bkv3"
class C(Base):
    x = 1

    def f(self):
        return self.x
```クラス本体はコードオブジェクトになります。

実行時:```text id="s2rsuu"
evaluate base classes
create class namespace
execute class body code object in that namespace
call metaclass machinery
bind resulting class object to name
```コンパイラは、内部クラス構築プロトコルを呼び出すバイトコードを発行します。

クラス本体は実行可能なコードです。任意のステートメントを含めることができます。```python id="we8528"
class C:
    print("building class")
    x = compute()
```これらのステートメントは、クラス定義の実行時に実行されます。

## 22.15 クロージャのコンパイル

クロージャには、シンボル テーブルの分析とバイトコード生成の間で調整された作業が必要です。

例:```python id="ovqkwa"
def outer():
    x = 1

    def inner():
        return x

    return inner
```コンパイラは次のことを行う必要があります。```text id="ovv4rt"
create a cell for x in outer
compile inner with x as a free variable
create inner function with closure tuple
load x through closure access in inner
```内部の簡略化された形状`outer`:

```text id="3irffr"
MAKE_CELL x
LOAD_CONST 1
STORE_DEREF x
LOAD_CLOSURE x
BUILD_TUPLE 1
LOAD_CONST <code object inner>
MAKE_FUNCTION closure
STORE_FAST inner
LOAD_FAST inner
RETURN_VALUE
```内部`inner`:

```text id="31h0a6"
LOAD_DEREF x
RETURN_VALUE
```正確な命令はバージョンによって異なりますが、クロージャ メカニズムは変わりません。キャプチャされた変数はセル内に存在します。

## 22.16 理解のコンパイル

内包表記はネストされたコード オブジェクトとしてコンパイルされます。

例:```python id="5w7ihk"
values = [x * x for x in range(5)]
```概念的には、CPython は内包表記の暗黙的な入れ子関数に似たコードを作成します。

外側のコードは反復可能オブジェクトを評価し、内包コードを呼び出します。内包コードはループして結果リストを構築します。

これは、内包変数が周囲のスコープに漏れない理由を説明します。```python id="in5tuc"
x = 100
values = [x for x in range(3)]
print(x)
```外側`x`残っている`100`

理解の`x`内包コードオブジェクトに対してローカルです。

## 22.17 ジェネレーターのコンパイル

ジェネレーター関数のコンパイル方法は、通常の関数とは異なります。

例:```python id="esmh0i"
def gen():
    yield 1
    yield 2
```コンパイラは、コード オブジェクトをジェネレータ フラグでマークします。

電話がかかると、`gen()`本体はすぐには実行されません。ジェネレーターオブジェクトを返します。

ジェネレーターが進むと本体が実行されます。

コンパイルでは降伏点を生成する必要があります。```text id="xk7fji"
LOAD_CONST 1
YIELD_VALUE
resume point
LOAD_CONST 2
YIELD_VALUE
resume point
RETURN_VALUE
```ジェネレータ フレームは一時停止および再開できます。コード オブジェクト フラグは、ランタイムにコードの構築方法と実行方法を指示します。

## 22.18 コルーチンと非同期コンパイル

非同期関数はコルーチン コード オブジェクトを生成します。

例:```python id="5d36kx"
async def fetch():
    result = await client.get()
    return result
```コンパイラは、コード オブジェクトをコルーチン コードとしてマークします。

awaitable 式の周りに await 機構を発行します。

概念的には:```text id="g68r60"
call client.get()
obtain awaitable
suspend until awaitable completes
store result
return result
```非同期関数、非同期ジェネレーター、`async for` そして`async with`すべて特別なバイトコード パターンとコード フラグが必要です。

## 22.19 パターンマッチングのコンパイル

パターン マッチングには専用のコンパイル ロジックがあります。

例:```python id="jz9doq"
match value:
    case 0:
        result = "zero"
    case [x, y]:
        result = x + y
```コンパイラは次の目的でコードを出力する必要があります。```text id="1gk5a7"
evaluate subject once
try first pattern
jump to body if matched
try next pattern if failed
bind captured names only on successful match
evaluate guards when present
skip remaining cases after match
```パターンマッチングは普通じゃない`if`構文でも通常の割り当てでもありません。独自の一致ルールと拘束ルールがあります。

## 22.20 定数の処理

コンパイラは定数を次の場所に保存します。`co_consts`

例:```python id="j0h9t8"
x = 123
y = "hello"
```コード オブジェクト定数には次のものが含まれる場合があります。```text id="4721fz"
None
123
"hello"
nested code objects
tuples of constants
frozensets used by optimized membership tests
```検査:```python id="qrm79o"
code = compile("x = 123\ny = 'hello'\n", "<input>", "exec")
print(code.co_consts)
```定数は、バイトコード命令のインデックスによって参照されます。

## 22.21 名前と変数のテーブル

コンパイラはいくつかの名前テーブルを構築します。

|コードオブジェクトフィールド |意味 |
| ----------------- | ----------------------------------------------- |
|`co_names`|グローバル、属性、インポートに使用される名前 |
|`co_varnames`|高速ローカル変数名 |
|`co_cellvars`|ネストされたスコープによってキャプチャされたローカル |
|`co_freevars`|囲んでいるスコープからキャプチャされた変数 |

例:```python id="m1cvy7"
x = 1

def f(a):
    b = len(a)
    return x + b
```内部`f`:

```text id="8bgw2w"
co_varnames:
    a, b

co_names:
    len, x
```閉鎖が関係する場合は、`co_cellvars`そして`co_freevars`現れる。

## 22.22 スタックサイズの計算

CPython バイトコードは値スタックを使用します。

コンパイラは、コード オブジェクトに必要な最大スタック深さを計算する必要があります。

例:```python id="kt5lcp"
x = a + b * c
```考えられるスタックの進化:```text id="q0x4gy"
LOAD a        stack: a
LOAD b        stack: a, b
LOAD c        stack: a, b, c
MULTIPLY      stack: a, result
ADD           stack: result
STORE x       stack:
```最大スタック深さ: 3

コードオブジェクトはこれを次のように保存します`co_stacksize`

インタプリタはこれを使用してフレーム ストレージのサイズを設定します。

## 22.23 行番号と位置テーブル

コンパイラは、バイトコードとソース位置の間のマッピングを記録します。

これらのマッピングは以下をサポートします。```text id="srmq4m"
tracebacks
debuggers
profilers
coverage tools
stepping behavior
error locations
```例:```python id="st4v12"
def f():
    x = 1
    y = 2
    return x + y
```各バイトコード範囲をソース行および列情報に関連付けることができます。

最新の CPython は、古いバージョンよりも正確な位置情報を追跡するため、トレースバックとデバッグが向上します。

## 22.24 組み立て

コンパイラは、多くの場合、最終バイトコード バイトの前に中間命令表現を出力します。

アセンブリでは次のことが解決されます。```text id="ic8am2"
labels
jump targets
instruction offsets
extended arguments
line tables
exception tables
final bytecode layout
```これが必要なのは、ジャンプ オフセットが命令サイズに依存し、オフセットに拡張引数が必要な場合に命令サイズが変わる可能性があるためです。

概念的には:```text id="nxzhpt"
emit symbolic instructions
compute instruction offsets
resolve jumps
insert extended arguments if needed
recompute if sizes changed
build final bytecode bytes
build metadata tables
create code object
```## 22.25 ASTの最適化

CPython は、AST またはコンパイラ レベルで最適化を実行します。

例としては次のものが挙げられます。```text id="45a56p"
constant folding
removing unreachable assert code under optimization
simplifying literal containers for membership tests
basic expression simplifications
```例:```python id="hspw89"
x = 1 + 2
```次のように書かれているかのようにコンパイルできます。```python id="99zdo5"
x = 3
```検査:```python id="3ltxiz"
import dis

dis.dis(compile("x = 1 + 2\n", "<input>", "exec"))
```最適化は保守的です。 CPython は、副作用、例外、評価順序を含む Python セマンティクスを保持する必要があります。

## 22.26 評価順序

コンパイラは Python の評価順序を保持する必要があります。

例:```python id="0rat4c"
result = f() + g()

f()前に実行する必要がありますg()

例:```python id="3cgy5k" obj.attr = value()


例:```python id="9uqpy4"
d[key()] = value()
```コンパイラーは、別の順序の方が高速である可能性があるからといって、操作の順序を自由に変更することはできません。

Python の動的セマンティクスにより、評価順序が観察可能な動作の一部になります。

## 22.27 コンパイル時のエラー処理

一部のエラーは、解析ではなくコンパイル中に検出されます。

:```python id="uxlk51"
return 1
```モジュールレベルで。```python id="nlyacf"
break
```ループの外側。```python id="kggjx7"
continue
```ループの外側。

パーサーはこれらのステートメントのノードを構築できます。コンパイラは、コンテキストによってそれらが不正になると、それらを拒否します。

これは、構文形式の AST が依然としてコンパイルに失敗する可能性があることを意味します。

## 22.28 コンパイル用のパブリック API

Python はコンパイルを公開します`compile()`

例:```python id="njqphs"
code = compile("x = 1\n", "<input>", "exec")
exec(code)
```モード:

|モード |意味 |
| -------- | -------------------------------------- |
|`exec`|モジュールまたはステートメント シーケンスをコンパイルする |
|`eval`|単一の式をコンパイルする |
|`single`|対話型入力をコンパイルする |

:```python id="s246ja"
compile("x = 1", "<input>", "exec")
compile("1 + 2", "<input>", "eval")
compile("print(1)", "<input>", "single")

compile()AST をコンパイルすることもできます。```python id="mcvj8n" import ast

tree = ast.parse("x = 1\n") code = compile(tree, "", "exec") exec(code)


重要なコンパイラ関連のファイルには次のものがあります。```text id="jwz8yn"
Python/compile.c
Python/symtable.c
Python/ast.c
Python/ast_opt.c
Python/flowgraph.c
Python/assemble.c
Include/cpython/code.h
Lib/dis.py
Lib/test/test_compile.py
Lib/test/test_dis.py
```概念的な役割:

|エリア |役割 |
| -------------- | ---------------------------------- |
|`symtable.c`|範囲分類 |
|`compile.c`| AST から命令へのコンパイル |
|`flowgraph.c`|制御フロー グラフの処理 |
|`assemble.c`|最終的なバイトコードアセンブリ |
|`code.h`|コードオブジェクトの定義 |
|`dis.py`|バイトコード検査 |
|コンパイラのテスト |動作と回帰の保護 |

正確なファイル構成はバージョン間で変わる可能性がありますが、概念的なパイプラインは安定しています。

## 22.30 最小限のメンタルモデル

このモデルを使用します。```text id="ti9m3l"
The AST gives syntax.
The symbol table gives scope.
The compiler walks the AST and emits bytecode instructions.
Nested executable constructs produce nested code objects.
Control flow becomes jumps and exception tables.
Names become local, global, or closure bytecode.
Constants and names are stored in code object tables.
Assembly resolves labels, jumps, stack size, and metadata.
The result is an immutable code object ready for execution.
```コンパイラ パスでは、CPython が構文とスコープを実行可能な命令に変換します。