32. Python コンパイラパッケージ — Python 2.7ja1 documentation

### ナビゲーション

- 索引
- モジュール |
- 次へ |
- 前へ |
- Python 2.7ja1 documentation »
- Python 標準ライブラリ »

# 32. Python コンパイラパッケージ

バージョン 2.6 で撤廃: `compiler` パッケージは Python 3.0 で削除されました。

Python compiler パッケージは Python のソースコードを分析したり Python バイトコードを生成するためのツールです。compiler は Python のソースコードから抽象的な構文木を生成し、その構文木から Python バイトコード (bytecode) を生成するライブラリをそなえています。

`compiler` パッケージは、Python で書かれた Python ソースコードからバイトコードへの変換プログラムです。これは組み込みの構文解析器と標準ライブラリの `parser` を使って、構文木を生成します。この構文木から抽象構文木 AST (Abstract Syntax Tree) が生成され、その後 Python バイトコードが得られます。

このパッケージの機能は、Python インタプリタに内蔵されている組み込みのコンパイラがすべて含んでいるものです。これはその機能と正確に同じものになるよう意図してつくられています。なぜ同じことをするコンパイラをもうひとつ作る必要があるのでしょうか? このパッケージはいろいろな目的に使うことができるからです。これは組み込みのコンパイラよりも簡単に変更できますし、これが生成する AST は Python ソースコードを解析するのに有用です。

この章では `compiler` パッケージのいろいろなコンポーネントがどのように動作するのかを説明します。そのため説明はリファレンスマニュアル的なものと、チュートリアル的な要素がまざったものになっています。

## 32.1. 基本的なインターフェイス

このパッケージのトップレベルでは4つの関数が定義されています。 `compiler` モジュールを import すると、これらの関数およびこのパッケージに含まれている一連のモジュールが使用可能になります。

`compiler.` `parse`(buf)

  buf 中の Python ソースコードから得られた抽象構文木 AST を返します。ソースコード中にエラーがある場合、この関数は `SyntaxError` を発生させます。返り値は `compiler.ast.Module` インスタンスであり、この中に構文木が格納されています。

`compiler.` `parseFile`(path)

  path で指定されたファイル中の Python ソースコードから得られた抽象構文木 AST を返します。これは `parse(open(path).read())` と等価な働きをします。

`compiler.` `walk`(ast, visitor[, verbose])

  ast に格納された抽象構文木の各ノードを先行順序 (pre-order) でたどっていきます。各ノードごとに visitor インスタンスの該当するメソッドが呼ばれます。

`compiler.` `compile`(source, filename, mode, flags=None, dont_inherit=None)

  文字列 source 、Python モジュール、文あるいは式を exec 文あるいは `eval()` 関数で実行可能なバイトコードオブジェクトにコンパイルします。この関数は組み込みの `compile()` 関数を置き換えるものです。

  filename は実行時のエラーメッセージに使用されます。

  mode は、モジュールをコンパイルする場合は ‘exec’、 (対話的に実行される)単一の文をコンパイルする場合は ‘single’、式をコンパイルする場合には ‘eval’ を渡します。

  引数 flags および dont_inherit は将来的に使用される文に影響しますが、いまのところはサポートされていません。

`compiler.` `compileFile`(source)

  ファイル source をコンパイルし、.pyc ファイルを生成します。

`compiler` パッケージは以下のモジュールを含んでいます: `ast`, `consts`, `future`, `misc`, `pyassem`, `pycodegen`, `symbols`, `transformer`, そして `visitor` 。

## 32.2. 制限

compiler パッケージにはエラーチェックにいくつか問題が存在します。構文エラーはインタープリタの2つの別々のフェーズによって認識されます。ひとつはインタープリタのパーザによって認識されるもので、もうひとつはコンパイラによって認識されるものです。 compiler パッケージはインタープリタのパーザに依存しているので、最初の段階のエラーチェックは労せずして実現できています。しかしその次の段階は、実装されてはいますが、その実装は不完全です。たとえば compiler パッケージは引数に同じ名前が 2度以上出てきていてもエラーを出しません: `def f(x, x): ...`

compiler の将来のバージョンでは、これらの問題は修正される予定です。

## 32.3. Python 抽象構文

`compiler.ast` モジュールは Python の抽象構文木 AST を定義します。 AST では各ノードがそれぞれの構文要素をあらわします。木の根は `Module` オブジェクトです。

抽象構文木 AST は、パーズされた Python ソースコードに対する高水準のインターフェイスを提供します。 Python インタプリタにおける `parser` モジュールとコンパイラは C で書かれおり、具体的な構文木を使っています。具体的な構文木は Python のパーザ中で使われている構文と密接に関連しています。ひとつの要素に単一のノードを割り当てる代わりに、ここでは Python の優先順位に従って、何層にもわたるネストしたノードがしばしば使われています。

抽象構文木 AST は、 `compiler.transformer` (変換器) モジュールによって生成されます。 transformer は組み込みの Python パーザに依存しており、これを使って具体的な構文木をまず生成します。つぎにそこから抽象構文木 AST を生成します。

`transformer` モジュールは、実験的な Python-to-C コンパイラ用に Greg Stein と Bill Tutt によって作られました。現行のバージョンではいくつもの修正と改良がなされていますが、抽象構文木 AST と transformer の基本的な構造は Stein と Tutt によるものです。

### 32.3.1. AST ノード

`compiler.ast` モジュールは、各ノードのタイプとその要素を記述したテキストファイルからつくられます。各ノードのタイプはクラスとして表現され、そのクラスは抽象基底クラス `compiler.ast.Node` を継承し子ノードの名前属性を定義しています。

class `compiler.ast.` `Node`

  `Node` インスタンスはパーザジェネレータによって自動的に作成されます。ある特定の `Node` インスタンスに対する推奨されるインターフェイスとは、子ノードにアクセスするために public な (訳注: 公開された) 属性を使うことです。 public な属性は単一のノード、あるいは一連のノードのシーケンスに束縛されている (訳注: バインドされている) かもしれませんが、これは `Node` のタイプによって違います。たとえば `Class` ノードの `bases` 属性は基底クラスのノードのリストに束縛されており、 `doc` 属性は単一のノードのみに束縛されている、といった具合です。

  各 `Node` インスタンスは `lineno` 属性をもっており、これは `None` かもしれません。 XXX どういったノードが使用可能な lineno をもっているかの規則は定かではない。

  `Node` オブジェクトはすべて以下のメソッドをもっています:

  `getChildren`()

    子ノードと子オブジェクトを、これらが出てきた順で、平らなリスト形式にして返します。とくにノードの順序は、 Python 文法中に現れるものと同じになっています。すべての子が `Node` インスタンスなわけではありません。たとえば関数名やクラス名といったものは、ただの文字列として表されます。

  `getChildNodes`()

    子ノードをこれらが出てきた順で平らなリスト形式にして返します。このメソッドは `getChildren()` に似ていますが、 `Node` インスタンスしか返さないという点で異なっています。

`Node` クラスの一般的な構造を説明するため、以下に 2つの例を示します。 `while` 文は以下のような文法規則により定義されています:

    while_stmt:     "while" expression ":" suite
                   ["else" ":" suite]

`While` ノードは 3つの属性をもっています: `test`, `body` および `else_` です。 (ある属性にふさわしい名前が Python の予約語としてすでに使われているとき、その名前を属性名にすることはできません。そのため、ここでは名前が正規のものとして受けつけられるようにアンダースコアを後につけてあります、そのため `else_` は `else` のかわりです。)

`if` 文はもっとこみ入っています。なぜならこれはいくつもの条件判定を含む可能性があるからです。

    if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]

`If` ノードでは、 `tests` および `else_` の 2つだけの属性が定義されています。 `tests` 属性には条件式とその後の動作のタプルがリスト形式で入っています。おのおのの `if` / `elif` 節ごとに 1タプルです。各タプルの最初の要素は条件式で、2番目の要素はもしその式が真ならば実行されるコードをふくんだ `Stmt` ノードになっています。

`If` の `getChildren()` メソッドは、子ノードの平らなリストを返します。 `if` / `elif` 節が 3つあって `else` 節がない場合なら、 `getChildren()` は 6要素のリストを返すでしょう: 最初の条件式、最初の `Stmt` 、2番目の条件式…といった具合です。

以下の表は `compiler.ast` で定義されている `Node` サブクラスと、それらのインスタンスに対して使用可能なパブリックな属性です。ほとんどの属性の値じたいは `Node` インスタンスか、インスタンスのリストです。この値がインスタンス型以外の場合、その型は備考の中で記されています。これら属性の順序は、 `getChildren()` および `getChildNodes()` が返す順です。

ノード型 | 属性 | 値
--- | --- | ---
`Add` | `left` | 左オペランド
| `right` | 右オペランド
`And` | `nodes` | オペランドのリスト
`AssAttr` |  | 代入のターゲットとなる属性
| `expr` | ドットの左側の式
| `attrname` | 属性名, 文字列
| `flags` | XXX
`AssList` | `nodes` | 代入先のリスト要素のリスト
`AssName` | `name` | 代入先の名前
| `flags` | XXX
`AssTuple` | `nodes` | 代入先のタプル要素のリスト
`Assert` | `test` | テストされる式
| `fail` | `AssertionError` の値
`Assign` | `nodes` | 代入ターゲットのリスト、等号ごとに一つ
| `expr` | 代入される値
`AugAssign` | `node` |
| `op` |
| `expr` |
`Backquote` | `expr` |
`Bitand` | `nodes` |
`Bitor` | `nodes` |
`Bitxor` | `nodes` |
`Break` |  |
`CallFunc` | `node` | 呼び出される式
| `args` | 引数のリスト
| `star_args` | 拡張された *-引数の値
| `dstar_args` | 拡張された **-引数の値
`Class` | `name` | クラス名, 文字列
| `bases` | 基底クラスのリスト
| `doc` | ドキュメント文字列, 文字列または `None`
| `code` | クラス文の本体
`Compare` | `expr` |
| `ops` |
`Const` | `value` |
`Continue` |  |
`Decorators` | `nodes` | 関数デコレータ式のリスト
`Dict` | `items` |
`Discard` | `expr` |
`Div` | `left` |
| `right` |
`Ellipsis` |  |
`Expression` | `node` |
`Exec` | `expr` |
| `locals` |
| `globals` |
`FloorDiv` | `left` |
| `right` |
`For` | `assign` |
| `list` |
| `body` |
| `else_` |
`From` | `modname` |
| `names` |
`Function` | `decorators` | `Decorators` か `None`
| `name` | def に使われた名前, 文字列
| `argnames` | 引数名の文字列としてのリスト
| `defaults` | デフォルト値のリスト
| `flags` | xxx
| `doc` | ドキュメント文字列, 文字列または `None`
| `code` | 関数の本体
`GenExpr` | `code` |
`GenExprFor` | `assign` |
| `iter` |
| `ifs` |
`GenExprIf` | `test` |
`GenExprInner` | `expr` |
| `quals` |
`Getattr` | `expr` |
| `attrname` |
`Global` | `names` |
`If` | `tests` |
| `else_` |
`Import` | `names` |
`Invert` | `expr` |
`Keyword` | `name` |
| `expr` |
`Lambda` | `argnames` |
| `defaults` |
| `flags` |
| `code` |
`LeftShift` | `left` |
| `right` |
`List` | `nodes` |
`ListComp` | `expr` |
| `quals` |
`ListCompFor` | `assign` |
| `list` |
| `ifs` |
`ListCompIf` | `test` |
`Mod` | `left` |
| `right` |
`Module` | `doc` | ドキュメント文字列, 文字列または `None`
| `node` | モジュールの本体, `Stmt`
`Mul` | `left` |
| `right` |
`Name` | `name` |
`Not` | `expr` |
`Or` | `nodes` |
`Pass` |  |
`Power` | `left` |
| `right` |
`Print` | `nodes` |
| `dest` |
`Printnl` | `nodes` |
| `dest` |
`Raise` | `expr1` |
| `expr2` |
| `expr3` |
`Return` | `value` |
`RightShift` | `left` |
| `right` |
`Slice` | `expr` |
| `flags` |
| `lower` |
| `upper` |
`Sliceobj` | `nodes` | 文のリスト
`Stmt` | `nodes` |
`Sub` | `left` |
| `right` |
`Subscript` | `expr` |
| `flags` |
| `subs` |
`TryExcept` | `body` |
| `handlers` |
| `else_` |
`TryFinally` | `body` |
| `final` |
`Tuple` | `nodes` |
`UnaryAdd` | `expr` |
`UnarySub` | `expr` |
`While` | `test` |
| `body` |
| `else_` |
`With` | `expr` |
| `vars` |
| `body` |
`Yield` | `value` |

### 32.3.2. 代入ノード

代入をあらわすのに使われる一群のノードが存在します。ソースコードにおけるそれぞれの代入文は、抽象構文木 AST では単一のノード `Assign` になっています。 `nodes` 属性は各代入の対象にたいするノードのリストです。これが必要なのは、たとえば `a = b = 2` のように代入が連鎖的に起こるためです。このリスト中における各 `Node` は、次のうちどれかのクラスになります: `AssAttr`, `AssList`, `AssName` または `AssTuple` 。

代入対象の各ノードには代入されるオブジェクトの種類が記録されています。 `AssName` は `a = 1` などの単純な変数名、 `AssAttr` は `a.x = 1` などの属性に対する代入、 `AssList` および `AssTuple` はそれぞれ、 `a, b, c = a_tuple` などのようなリストとタプルの展開をあらわします。

代入対象ノードはまた、そのノードが代入で使われるのか、それとも del 文で使われるのかをあらわす属性 `flags` も持っています。 `AssName` は `del x` などのような del 文をあらわすのにも使われます。

ある式がいくつかの属性への参照をふくんでいるときは、代入あるいは del 文はただひとつだけの `AssAttr` ノードをもちます – 最終的な属性への参照としてです。それ以外の属性への参照は `AssAttr` インスタンスの `expr` 属性にある `Getattr` ノードによってあらわされます。

### 32.3.3. サンプル

この節では、Python ソースコードに対する抽象構文木 AST のかんたんな例をいくつかご紹介します。これらの例では `parse()` 関数をどうやって使うか、AST の repr 表現はどんなふうになっているか、そしてある AST ノードの属性にアクセスするにはどうするかを説明します。

最初のモジュールでは単一の関数を定義しています。かりにこれは `/tmp/doublelib.py` に格納されていると仮定しましょう。

    """This is an example module.

    This is the docstring.
    """

    def double(x):
        "Return twice the argument"
        return x * 2

以下の対話的インタプリタのセッションでは、見やすさのため長い AST の repr を整形しなおしてあります。 AST の repr では qualify されていないクラス名が使われています。 repr 表現からインスタンスを作成したい場合は、 `compiler.ast` モジュールからそれらのクラス名を import しなければなりません。

    >>> import compiler
    >>> mod = compiler.parseFile("/tmp/doublelib.py")
    >>> mod
    Module('This is an example module.\n\nThis is the docstring.\n',
           Stmt([Function(None, 'double', ['x'], [], 0,
                          'Return twice the argument',
                          Stmt([Return(Mul((Name('x'), Const(2))))]))]))
    >>> from compiler.ast import *
    >>> Module('This is an example module.\n\nThis is the docstring.\n',
    ...    Stmt([Function(None, 'double', ['x'], [], 0,
    ...                   'Return twice the argument',
    ...                   Stmt([Return(Mul((Name('x'), Const(2))))]))]))
    Module('This is an example module.\n\nThis is the docstring.\n',
           Stmt([Function(None, 'double', ['x'], [], 0,
                          'Return twice the argument',
                          Stmt([Return(Mul((Name('x'), Const(2))))]))]))
    >>> mod.doc
    'This is an example module.\n\nThis is the docstring.\n'
    >>> for node in mod.node.nodes:
    ...     print node
    ...
    Function(None, 'double', ['x'], [], 0, 'Return twice the argument',
             Stmt([Return(Mul((Name('x'), Const(2))))]))
    >>> func = mod.node.nodes[0]
    >>> func.code
    Stmt([Return(Mul((Name('x'), Const(2))))])

## 32.4. Visitor を使って AST をわたり歩く

visitor パターンは… `compiler` パッケージは、Python のイントロスペクション機能を利用して visitor のために必要な大部分のインフラを省略した、visitor パターンの変種を使っています。

visit されるクラスは、visitor を受け入れるようにプログラムされている必要はありません。 visitor が必要なのはただそれがとくに興味あるクラスに対して visit メソッドを定義することだけです。それ以外はデフォルトの visit メソッドが処理します。

XXX visitor 用の魔法の `visit()` メソッド。

`compiler.visitor.` `walk`(tree, visitor[, verbose])

class `compiler.visitor.` `ASTVisitor`

  `ASTVisitor` は構文木を正しい順序でわたり歩くようにします。それぞれのノードはまず `preorder()` の呼び出しではじまります。各ノードに対して、これは ‘visitNodeType’ という名前のメソッドに対する `preorder()` 関数への visitor 引数をチェックします。ここで NodeType の部分はそのノードのクラス名です。たとえば `While` ノードなら、 `visitWhile()` が呼ばれるわけです。もしそのメソッドが存在している場合、それはそのノードを第一引数として呼び出されます。

  ある特定のノード型に対する visitor メソッドでは、その子ノードをどのようにわたり歩くかが制御できます。 `ASTVisitor` は visitor に visit メソッドを追加することで、その visitor 引数を修正します。特定のノード型に対する visitor が存在しない場合、 `default()` メソッドが呼び出されます。

  `ASTVisitor` オブジェクトには以下のようなメソッドがあります:

  XXX 追加の引数を記述

  `default`(node[, ...])
  `dispatch`(node[, ...])
  `preorder`(tree, visitor)

## 32.5. バイトコード生成

バイトコード生成器はバイトコードを出力する visitor です。 visit メソッドが呼ばれるたびにこれは `emit()` メソッドを呼び出し、バイトコードを出力します。基本的なバイトコード生成器はモジュール、クラス、および関数によって拡張できます。アセンブラがこれらの出力された命令を低レベルのバイトコードに変換します。これはコードオブジェクトからなる定数のリスト生成や、分岐のオフセット計算といった処理をおこないます。

### ナビゲーション

- 索引
- モジュール |
- 次へ |
- 前へ |
- Python 2.7ja1 documentation »
- Python 標準ライブラリ »