#20. AST

抽象構文ツリーは通常 AST と呼ばれ、解析後の Python ソース コードの構造化表現です。

トークナイザーは、トークンのフラット ストリームを生成します。パーサーは文法を認識します。 AST は、結果をステートメントと式のツリーとして記録します。

このソースの場合:python x = 1 + 2 AST は次のような形になります。```text Module Assign targets: Name id="x", ctx=Store value: BinOp left: Constant value=1 op: Add right: Constant value=2


## 20.1 コンパイルパイプラインでの位置

AST は解析とコンパイラ分析の間に位置します。```text
source bytes
    
tokenization
    
parsing
    
AST
    
AST validation
    
symbol table
    
compiler
    
code object
    
bytecode execution
```パーサーは AST を構築します。後のフェーズではそれを消費します。

AST は次のような質問に答えます。```text
What statements are in this module?
What expression is assigned to this name?
What is the body of this function?
What names are loaded, stored, or deleted?
What is the test expression of this if statement?
What is the element expression of this comprehension?
What source position produced this node?
```次のような質問には答えません。```text
Is this name local or global?
What bytecode should be emitted?
What stack size is needed?
What constants should be placed in co_consts?
What exception table entries are needed?
```これらはコンパイラの後の段階に属します。

## 20.2 具象構文ツリーと抽象構文ツリー

パーサーは、具体的な構文ツリーまたは抽象的な構文ツリーを生成できます。

具体的な構文ツリーは文法の詳細を保持します。ソースを忠実に表現しています。

抽象構文ツリーは意味構造を保持します。これにより、後のコンパイラ段階で必要のない詳細が削除されます。

例えば:```python
x = (((1)))
```具体的な構文には 3 組のかっこが含まれています。

AST はこれを次のように単純に表すことができます。```text
Assign
    target: Name "x"
    value: Constant 1
```括弧は解析に影響を与えますが、括弧自体が実行時の動作を作成するわけではありません。

同じく:```python
x = 1 + 2
```実行時の動作に影響を与えるため、AST は追加を保持します。```text
BinOp
    left: Constant 1
    op: Add
    right: Constant 2
```したがって、AST は、実際のコンパイラという意味では抽象的です。すべてのスペルではなく、操作が保持されます。

## 20.3 Python レベルの AST アクセス

Python は、`ast`モジュール。

例:```python
import ast

tree = ast.parse("x = 1 + 2\n")
print(ast.dump(tree, indent=4))
```一般的な出力:```text
Module(
    body=[
        Assign(
            targets=[
                Name(id='x', ctx=Store())],
            value=BinOp(
                left=Constant(value=1),
                op=Add(),
                right=Constant(value=2)))],
    type_ignores=[])
```これは、CPython が内部で使用する一般的な構造と同じであり、Python オブジェクトとして公開されます。

`ast`モジュールは次の場合に役立ちます。```text
static analysis
code generation
linting
refactoring
macro-like source transforms
security review tools
documentation tools
teaching compiler structure
```しかし、パブリック AST API は依然として抽象化されたものです。 CPython の内部 C 表現とパブリック Python AST クラスは関連していますが、同じ物理オブジェクトではありません。

## 20.4 モジュールノード

通常の Python ファイルのルートは次のとおりです。`Module`

例:```python
x = 1
print(x)
```AST形状:```text
Module
    body:
        Assign
        Expr
    type_ignores:
        []
````body`ステートメントの順序付きリストです。

モジュール レベルでは、コンパイラはステートメントを上から下に実行するバイトコードを発行します。

解析モードに応じて、さまざまなルート ノード タイプがあります。

|解析モード |ルートノード | |
| ------------------ | -------------- | -------------------- |
|`exec`             | `Module`       | `x = 1`              |
| `eval`             | `Expression`   | `1 + 2`              |
| `single`           | `Interactive`| REPL 入力 |
|関数型モード |`FunctionType`|レガシータイプのコメント |

例:```python
import ast

print(type(ast.parse("x = 1", mode="exec")).__name__)
print(type(ast.parse("1 + 2", mode="eval")).__name__)
print(type(ast.parse("print(1)", mode="single")).__name__)
```文法開始モードにより、ルート AST の形状が変更されます。

## 20.5 ステートメントノード

ステートメントはアクションを表します。

一般的なステートメント ノードには次のものがあります。

|ノード |ソース例 |
| ------------------ | ------------------------ |
|`Assign`           | `x = 1`                  |
| `AnnAssign`        | `x: int = 1`             |
| `AugAssign`        | `x += 1`                 |
| `Return`           | `return x`               |
| `If`               | `if x: ...`              |
| `For`              | `for x in xs: ...`       |
| `While`            | `while x: ...`           |
| `FunctionDef`      | `def f(): ...`           |
| `AsyncFunctionDef` | `async def f(): ...`     |
| `ClassDef`         | `class C: ...`           |
| `Try`              | `try: ... except: ...`   |
| `With`             | `with open(p) as f: ...` |
| `Import`           | `import os`              |
| `ImportFrom`       | `from os import path`    |
| `Expr`             | `f()`ステートメントとして |
|`Pass`             | `pass`                   |
| `Break`            | `break`                  |
| `Continue`         | `continue`               |
| `Raise`            | `raise ValueError()`     |
| `Assert`           | `assert x`               |
| `Delete`           | `del x`                  |
| `Global`           | `global x`               |
| `Nonlocal`         | `nonlocal x`|

ステートメント ノードは、文法がステートメントを想定している場所に表示されます。ステートメント ノードは通常、`body``orelse``finalbody`、または同様のリスト。

例:```python
if x:
    a = 1
else:
    a = 2
```AST形状:```text
If
    test: Name "x", Load
    body:
        Assign a = 1
    orelse:
        Assign a = 2
````else`ブロックは次のように表されます。`orelse`分野。

## 20.6 式ノード

式は値を生成します。

一般的な式ノードには次のものがあります。

|ノード |ソース例 |
| -------------- | ------------------ |
|`Constant`     | `1``"x"``None` |
| `Name`         | `x`                |
| `BinOp`        | `a + b`            |
| `UnaryOp`      | `-x`               |
| `BoolOp`       | `a and b`          |
| `Compare`      | `a < b`            |
| `Call`         | `f(x)`             |
| `Attribute`    | `obj.name`         |
| `Subscript`    | `xs[i]`            |
| `List`         | `[1, 2]`           |
| `Tuple`        | `(1, 2)`           |
| `Dict`         | `{"a": 1}`         |
| `Set`          | `{1, 2}`           |
| `ListComp`     | `[x for x in xs]`  |
| `GeneratorExp` | `(x for x in xs)`  |
| `Lambda`       | `lambda x: x + 1`  |
| `IfExp`        | `a if cond else b` |
| `Yield`        | `yield x`          |
| `Await`        | `await task`       |
| `NamedExpr`    | `(x := f())`|

例:```python
result = obj.method(x, y=2)
```AST形状:```text
Assign
    target:
        Name "result", Store
    value:
        Call
            func:
                Attribute
                    value: Name "obj", Load
                    attr: "method"
                    ctx: Load
            args:
                Name "x", Load
            keywords:
                keyword arg="y", value=Constant 2
```AST は、呼び出しターゲット、位置引数、キーワード引数、および属性アクセスを記録します。

## 20.7 式コンテキスト

一部の式ノードはコンテキストを伝えます。

最も重要なコンテキストは次のとおりです。

|コンテキスト |意味 |
| ------- | ------------------------ |
|`Load`|値を読み取る |
|`Store`|ターゲットに割り当てる |
|`Del`|バインディングまたはターゲットを削除する |

例:```python
x = x + 1
```AST形状:```text
Assign
    target:
        Name "x", Store
    value:
        BinOp
            left: Name "x", Load
            op: Add
            right: Constant 1
```同じスペルでも、`x`、異なる意味で 2 回表示されます。

属性を使用した例:```python
obj.value = obj.value + 1
```AST形状:```text
Assign
    target:
        Attribute obj.value, Store
    value:
        BinOp
            left: Attribute obj.value, Load
            op: Add
            right: Constant 1
```コンテキストはコード生成に不可欠です。名前のロード、名前の保存、名前の削除には、異なるバイトコード命令が使用されます。

## 20.8 定数

最新の Python AST が使用するもの`Constant`多くのリテラル値の場合。

:```python
1
3.14
"hello"
b"bytes"
None
True
False
...
```それぞれは次のように表示されます。```text
Constant(value=...)
```コンテナ リテラルは別個のノードです。```python
[1, 2]
(1, 2)
{"a": 1}
{1, 2}
```AST形状:```text
List
    elts:
        Constant 1
        Constant 2

Tuple
    elts:
        Constant 1
        Constant 2

Dict
    keys:
        Constant "a"
    values:
        Constant 1

Set
    elts:
        Constant 1
        Constant 2
```コンパイラは後でいくつかの定数を折りたたむ場合があります。例えば:```python
x = 1 + 2
```定数にコンパイルできる`3`最適化ルールに応じてバイトコードで。ただし、AST は最適化前のバイナリ操作を記録します。

## 20.9 AST ノードとしてのオペレーター

オペレーターは小さな AST ノード オブジェクトとして表されます。

:

|出典 | AST 演算子 |
| -------- | ------------ |
|`a + b`  | `Add`        |
| `a - b`  | `Sub`        |
| `a * b`  | `Mult`       |
| `a / b`  | `Div`        |
| `a // b` | `FloorDiv`   |
| `a % b`  | `Mod`        |
| `a ** b` | `Pow`        |
| `a @ b`  | `MatMult`    |
| `a << b` | `LShift`     |
| `a >> b` | `RShift`     |
| `a \| b` | `BitOr`      |
| `a & b`  | `BitAnd`     |
| `a ^ b`  | `BitXor`|

例:```python
x = a + b
```AST:```text
BinOp
    left: Name "a"
    op: Add
    right: Name "b"
```演算子は文字列として保存されません。それを構造的に表現しています。

これにより、ダウンストリームのコンパイラ コードが簡素化されます。テキスト演算子を再度解析するのではなく、演算子ノード タイプをオンに切り替えることができます。

## 20.10 比較

Python は連鎖比較をサポートしているため、比較では特別な AST 形状が使用されます。

例:```python
a < b < c
```これはつまり:```python
a < b and b < c
```それ以外は`b`は一度評価されます。

AST形状:```text
Compare
    left: Name "a"
    ops:
        Lt
        Lt
    comparators:
        Name "b"
        Name "c"
```これは二項演算とは異なります。

次のようなバイナリ式:```python
a + b + c
```入れ子になっている:```text
BinOp
    left:
        BinOp
            left: a
            op: Add
            right: b
    op: Add
    right: c
```連鎖比較は 1 つとして表されます。`Compare`複数の演算子とコンパレータを備えたノード。

## 20.11 ブール演算

ブール演算にも独特の AST 形状があります。

例:```python
a and b and c
```AST形状:```text
BoolOp
    op: And
    values:
        Name "a"
        Name "b"
        Name "c"
```同様に:```python
a or b or c
```用途`BoolOp``Or`

この形状により、短絡構造が維持されます。コンパイラは、オペランドを左から右に評価するバイトコードを出力し、結果がわかった時点で停止します。

ブール演算は通常の関数呼び出しではありません。特別な評価ルールがあります。

## 20.12 関数の定義

関数定義は次のように表されます。`FunctionDef`

例:```python
def add(a: int, b: int = 0) -> int:
    return a + b
```AST形状:```text
FunctionDef
    name: "add"
    args:
        arguments
            args:
                arg "a", annotation: Name "int"
                arg "b", annotation: Name "int"
            defaults:
                Constant 0
    returns:
        Name "int"
    body:
        Return
            BinOp
                Name "a"
                Add
                Name "b"
    decorator_list:
        []
```関数定義ノードには以下が格納されます。```text
function name
argument structure
body statements
decorators
return annotation
type comment
source location
```関数本体は解析中に実行されません。これは、後で関数のコード オブジェクトの一部になります。実行時に、`def`ステートメントは関数オブジェクトを作成します。

## 20.13 引数

関数の引数は、`arguments`ノード。

すべての Python パラメーター形式をサポートする必要があります。```python
def f(a, b=1, /, c=2, *args, d, e=3, **kwargs):
    pass
```AST は以下を区別する必要があります。```text
positional-only parameters
positional-or-keyword parameters
varargs parameter
keyword-only parameters
keyword-only defaults
kwargs parameter
defaults
annotations
```この構造は、単純な名前のリストよりも詳細です。

コンパイラは後でそれを使用して、正しい引数の数とフラグを持つコード オブジェクトを構築します。

## 20.14 クラス定義

クラス定義は次のように表されます。`ClassDef`

例:```python
class Point(Base):
    x = 0
    y = 0
```AST形状:```text
ClassDef
    name: "Point"
    bases:
        Name "Base"
    keywords:
        []
    body:
        Assign x = 0
        Assign y = 0
    decorator_list:
        []
```クラス本体は実行可能ブロックとしてコンパイルされます。実行時に、CPython はクラス本体を実行して名前空間を生成し、メタクラス機構を呼び出してクラス オブジェクトを作成します。

AST はクラス定義構造のみを記録します。クラスは構築されません。

## 20.15 内包表記

内包表記には専用の AST ノードがあります。

:```python
[x for x in xs]
{x for x in xs}
{k: v for k, v in pairs}
(x for x in xs)
```AST ノード タイプ:

|ソースフォーム | ASTノード |
| -------------------- | -------------- |
|リスト内包表記 |`ListComp`|
|集合内包表記 |`SetComp`|
|辞書の理解 |`DictComp`|
|ジェネレータ式 |`GeneratorExp`|

それぞれが 1 つ以上を使用します`comprehension`ノード。

例:```python
[x * x for x in xs if x > 0]
```AST形状:```text
ListComp
    elt:
        BinOp x * x
    generators:
        comprehension
            target: Name "x", Store
            iter: Name "xs", Load
            ifs:
                Compare x > 0
            is_async: 0
```内包表記は式ノードですが、ローカル バインディング動作を導入します。後のコンパイラ フェーズでは、スコープを慎重に処理します。

## 20.16 パターンマッチングAST

Python パターン マッチングでは、専用の AST ノードを使用します。

例:```python
match value:
    case 0:
        result = "zero"
    case [x, y]:
        result = x + y
```AST形状:```text
Match
    subject:
        Name "value"
    cases:
        match_case
            pattern: MatchValue Constant 0
            body:
                Assign result = "zero"
        match_case
            pattern: MatchSequence
                MatchAs name="x"
                MatchAs name="y"
            body:
                Assign result = x + y
```パターン構文の意味が異なるため、パターン ノードは式ノードとは別のものです。

例えば:```python
case x:
```という名前の変数をロードしません`x`。値を名前に取り込みます`x`

このため、パターン マッチングでは通常の式 AST ノードをどこでも再利用できません。パターン固有の構造が必要です。

## 20.17 非同期 AST ノード

非同期構文は AST にも現れます。

:```python
async def fetch():
    await client.get(url)
```AST形状:```text
AsyncFunctionDef
    name: "fetch"
    body:
        Expr
            Await
                Call
                    Attribute client.get
```非同期ループとコンテキスト マネージャーにも専用のフォームがあります。```python
async for item in stream:
    process(item)

async with lock:
    run()
```AST ノード:```text
AsyncFor
AsyncWith
Await
AsyncFunctionDef
```これらのノードにより、後のコンパイラ フェーズでコルーチン対応のバイトコードを生成できるようになります。

## 20.18 ソースの場所

AST ノードはソース位置を保持します。

共通フィールドは次のとおりです。```text
lineno
col_offset
end_lineno
end_col_offset
```例:```python
import ast

tree = ast.parse("x = 1 + 2\n")
assign = tree.body[0]

print(assign.lineno)
print(assign.col_offset)
print(assign.end_lineno)
print(assign.end_col_offset)
```ソースの場所は以下によって使用されます。```text
tracebacks
syntax errors
debuggers
profilers
coverage tools
AST transformers
editor tooling
```AST を手動で生成する場合、ソースの場所が不足しているとコンパイル エラーが発生する可能性があります。ヘルパーさん`ast.fix_missing_locations()`可能な場合は、不足している位置データを埋めます。

例:```python
import ast

tree = ast.Module(
    body=[
        ast.Assign(
            targets=[ast.Name(id="x", ctx=ast.Store())],
            value=ast.Constant(value=1),
        )
    ],
    type_ignores=[],
)

tree = ast.fix_missing_locations(tree)
code = compile(tree, "<ast>", "exec")
exec(code)
```## 20.19 AST の検証

AST をコンパイルする前に、CPython はそれを検証します。

検証により、次のような不正なツリーが検出されます。```text
Name with no context
assignment target with Load context
expression where statement is required
statement where expression is required
invalid source location fields
invalid operator node placement
missing required fields
```無効な AST の例:```python
import ast

tree = ast.Module(
    body=[
        ast.Assign(
            targets=[ast.Name(id="x", ctx=ast.Load())],
            value=ast.Constant(value=1),
        )
    ],
    type_ignores=[],
)

tree = ast.fix_missing_locations(tree)
compile(tree, "<ast>", "exec")
```ターゲット`x`もっている`Load`コンテキストですが、代入には必要があります`Store` CPython はこれを拒否します。

これは重要です。`ast`このモジュールを使用すると、ユーザーは手動でツリーを構築できます。コンパイラは、ユーザーが作成したすべての AST が有効であると想定してはなりません。

## 20.20 AST 変換

AST ツールはコンパイル前にコードを変更できます。

: すべての整定数を置き換えます`1``42````python
import ast

class ReplaceOne(ast.NodeTransformer):
    def visit_Constant(self, node):
        if node.value == 1:
            return ast.copy_location(ast.Constant(value=42), node)
        return node

tree = ast.parse("x = 1\n")
tree = ReplaceOne().visit(tree)
tree = ast.fix_missing_locations(tree)

code = compile(tree, "<ast>", "exec")
ns = {}
exec(code, ns)

print(ns["x"])
```これは次のように出力します:```text
42
```AST 変換は強力ですが、制約があります。変換されたツリーは引き続き AST 有効性ルールに従う必要があります。

## 20.21 AST とバイトコード

AST とバイトコードは異なるレベルを表します。

このソースの場合:```python
x = 1 + 2
```AST:```text
Assign
    Name "x", Store
    BinOp
        Constant 1
        Add
        Constant 2
```バイトコードは次のようになります。```text
LOAD_CONST 3
STORE_NAME x
RETURN_CONST None
```バイトコードには、最適化された定数がすでに含まれている可能性があります。また、ソース構文ではなく、実行命令も含まれます。

比較:

|レイヤー | |を表しますプリザーブ |
| -------- | -------------------- | -------------------------------------------------- |
|トークン |字句ソース単位 |スペル、公開トークナイザーのコメント、位置 |
| AST |構文構造 |ステートメント、式、コンテキスト、位置 |
|バイトコード |実行計画 |スタック操作、定数、名前、ジャンプ |

AST はバイトコードよりもソースに近いですが、トークンほど正確ではありません。

## 20.22 AST およびシンボルテーブル

シンボル テーブル パスは AST を読み取り、名前を分類します。

例:```python
x = 1

def f():
    return x
```ASTは次のように述べています。```text
module assigns x
function f loads x
```シンボル テーブルは次のことを決定します。```text
x is global from inside f
f is local to module scope
```クロージャーを使用した例:```python
def outer():
    x = 1

    def inner():
        return x

    return inner
```AST は、ネストされた関数と名前の使用法を記録します。シンボル テーブルは次のことを決定します。```text
x is local to outer
x is free in inner
x must be stored in a closure cell
```これは純粋な解析ではありません。 AST を介したスコープ分析が必要です。

## 20.23 AST とコンパイラの生成

コンパイラは AST を実行し、バイトコードを出力します。

簡略化したモデル:```text
compile Module:
    compile each statement

compile Assign:
    compile right-hand expression
    compile each target in Store context

compile BinOp:
    compile left expression
    compile right expression
    emit binary operation instruction

compile If:
    compile test
    emit conditional jump
    compile body
    emit jump over else
    compile else body
```コンパイラは、制御フローとスタック管理ロジックを備えたツリー ウォーカーです。

それはAST構造が正しいかどうかに依存します。不正な形式の AST は、間違ったバイトコード、クラッシュ、または無効な実行時状態を引き起こす可能性があるため、検証が存在します。

## 20.24 CPython AST ソース ファイル

CPython の重要な領域には次のものが含まれます。```text
Parser/Python.asdl
Python/Python-ast.c
Python/ast.c
Python/ast_opt.c
Python/symtable.c
Python/compile.c
Lib/ast.py
```ASDL ファイルは AST ノード スキーマを定義します。生成された C コードは、AST 構造体とコンストラクターを提供します。パーサーと AST コードは文法の一致からノードを構築します。オプティマイザとコンパイラはツリーを消費します。

概念的な役割:

|エリア |役割 |
| -------------- | ------------------------------------- |
| ASDL スキーマ | AST ノード タイプとフィールドを定義します。
|パーサーのアクション |文法一致から AST ノードを作成する |
| AST 検証 |不正なツリーを拒否する |
| AST オプティマイザー |ツリーレベルの単純化を実行します。
|記号表 |名前と範囲を分類する |
|コンパイラ | AST からバイトコードを出力 |
|`Lib/ast.py`| Python レベルの AST ヘルパー |

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

このモデルを使用します。```text
The AST is CPythons structured syntax tree.
It is built by the parser.
It removes most formatting details.
It records statements, expressions, operators, contexts, and source locations.
It separates syntax structure from later semantic analysis.
The symbol table pass reads the AST to classify names.
The compiler walks the AST to emit bytecode.
```AST は、構文と実行の間の中心となるハンドオフ オブジェクトです。