33. Pythonコンパイラパッケージ—Pythonドキュメント

提供:Dev Guides
< PythonPython/docs/2.7/library/compiler
移動先:案内検索

33。 Pythonコンパイラパッケージ

バージョン2.6以降非推奨: コンパイラパッケージは、Python3で削除されました。


Pythonコンパイラパッケージは、Pythonソースコードを分析してPythonバイトコードを生成するためのツールです。 コンパイラには、Pythonソースコードから抽象構文ツリーを生成し、ツリーからPython bytecode を生成するためのライブラリが含まれています。

コンパイラパッケージは、Pythonで記述されたPythonソースからバイトコードへのトランスレータです。 組み込みのパーサーと標準のパーサーモジュールを使用して、具体的な構文ツリーを生成します。 このツリーは、抽象構文木(AST)を生成し、次にPythonバイトコードを生成するために使用されます。

パッケージの全機能は、Pythonインタープリターで提供される組み込みコンパイラーを複製します。 これは、その動作にほぼ正確に一致することを目的としています。 同じことをする別のコンパイラを実装するのはなぜですか? このパッケージはさまざまな目的に役立ちます。 組み込みのコンパイラよりも簡単に変更できます。 生成されるASTは、Pythonソースコードの分析に役立ちます。

この章では、コンパイラパッケージのさまざまなコンポーネントがどのように機能するかについて説明します。 参考資料とチュートリアルをブレンドしています。

33.1。 基本的なインターフェース

パッケージのトップレベルは4つの関数を定義します。 コンパイラをインポートすると、これらの関数とパッケージに含まれるモジュールのコレクションを取得できます。

compiler.parse(buf)
buf のPythonソースコードの抽象構文ツリーを返します。 ソースコードにエラーがある場合、関数はSyntaxErrorを発生させます。 戻り値は、ツリーを含むcompiler.ast.Moduleインスタンスです。
compiler.parseFile(path)
path で指定されたファイル内のPythonソースコードの抽象構文ツリーを返します。 parse(open(path).read())と同等です。
compiler.walk(ast, visitor[, verbose])
抽象構文木 ast を予約購入してください。 検出されたノードごとに、 visitor インスタンスで適切なメソッドを呼び出します。
compiler.compile(source, filename, mode, flags=None, dont_inherit=None)

文字列 source 、Pythonモジュール、ステートメント、または式を、execステートメントまたは eval()で実行できるコードオブジェクトにコンパイルします。 この関数は、組み込みの compile()関数に代わるものです。

ファイル名は、実行時のエラーメッセージに使用されます。

mode は、モジュールをコンパイルする場合は「exec」、単一の(対話型)ステートメントをコンパイルする場合は「single」、式をコンパイルする場合は「eval」である必要があります。

flags および dont_inherit 引数は、future関連のステートメントに影響しますが、まだサポートされていません。

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

コンパイラパッケージには、 astconstsfuturemiscpyassempycodegensymbolstransformer、およびvisitor


33.2。 制限事項

コンパイラパッケージのエラーチェックにはいくつか問題があります。 インタプリタは、2つの異なるフェーズで構文エラーを検出します。 エラーの1つのセットはインタープリターのパーサーによって検出され、もう1つのセットはコンパイラーによって検出されます。 コンパイラパッケージはインタプリタのパーサーに依存しているため、エラーチェックの最初のフェーズを無料で取得します。 これは第2フェーズ自体を実装しており、その実装は不完全です。 たとえば、名前が引数リストに複数回表示されても、コンパイラパッケージはエラーを発生させません:def f(x, x): ...

コンパイラの将来のバージョンでは、これらの問題が修正されるはずです。


33.3。 Python抽象構文

compiler.ast モジュールは、Pythonの抽象構文を定義します。 抽象構文木では、各ノードは構文構造を表します。 ツリーのルートはModuleオブジェクトです。

抽象構文は、解析されたPythonソースコードへのより高いレベルのインターフェースを提供します。 parser モジュールとPythonインタープリター用にCで記述されたコンパイラーは、具体的な構文ツリーを使用します。 具体的な構文は、Pythonパーサーに使用される文法の説明と密接に関連しています。 コンストラクトの単一ノードの代わりに、Pythonの優先ルールによって導入されるネストされたノードのレベルがいくつかあることがよくあります。

抽象構文木は、compiler.transformerモジュールによって作成されます。 トランスフォーマーは、組み込みのPythonパーサーに依存して、具体的な構文ツリーを生成します。 具象木から抽象構文木を生成します。

transformerモジュールは、実験的なPython-to-Cコンパイラ用にGregSteinとBillTuttによって作成されました。 現在のバージョンには多くの変更と改善が含まれていますが、抽象構文とトランスフォーマーの基本的な形式はSteinとTuttによるものです。

33.3.1。 ASTノード

compiler.ast モジュールは、各ノードタイプとその要素を説明するテキストファイルから生成されます。 各ノードタイプは、抽象基本クラス compiler.ast.Node を継承し、子ノードの名前付き属性のセットを定義するクラスとして表されます。

class compiler.ast.Node

Node インスタンスは、パーサージェネレーターによって自動的に作成されます。 特定の Node インスタンスに推奨されるインターフェースは、パブリック属性を使用して子ノードにアクセスすることです。 パブリック属性は、ノードタイプに応じて、単一のノードまたはノードのシーケンスにバインドされる場合があります。 たとえば、Classノードのbases属性は基本クラスノードのリストにバインドされ、doc属性は単一のノードにバインドされます。

Node インスタンスには、Nonelineno属性があります。 XXXどのノードのルールが有用なlinenoを持つかわからない。

すべての Node オブジェクトは、次のメソッドを提供します。

getChildren()

子ノードとオブジェクトのフラット化されたリストを発生順に返します。 具体的には、ノードの順序は、Python文法に表示される順序です。 すべての子が Node インスタンスであるわけではありません。 たとえば、関数やクラスの名前はプレーンな文字列です。

getChildNodes()

子ノードのフラット化されたリストを発生順に返します。 このメソッドは getChildren()に似ていますが、 Node インスタンスである子のみを返す点が異なります。

2つの例は、 Node クラスの一般的な構造を示しています。 while ステートメントは、次の文法生成によって定義されます。

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

Whileノードには、 testbody、およびelse_の3つの属性があります。 (属性の自然名がPythonの予約語でもある場合、属性名として使用することはできません。 単語にアンダースコアを追加して正当な識別子にするため、 else ではなくelse_になります。)

if ステートメントは、複数のテストを含めることができるため、より複雑です。

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

Ifノードは、testselse_の2つの属性のみを定義します。 tests属性は、一連のテスト式であり、結果として生じるボディペアです。 if / elif 句ごとに1つのペアがあります。 ペアの最初の要素はテスト式です。 2番目の要素はStmtノードであり、テストがtrueの場合に実行するコードが含まれています。

IfgetChildren()メソッドは、子ノードのフラットリストを返します。 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 等号ごとに1つの割り当てターゲットのリスト
expr 割り当てられている値
AugAssign node
op
expr
Backquote expr
Bitand nodes
Bitor nodes
Bitxor nodes
Break
CallFunc node 呼び出し先の式
args 引数のリスト
star_args 拡張された* -arg値
dstar_args 拡張**-arg値
Class name クラスの名前、文字列
bases 基本クラスのリスト
doc 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 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 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


33.3.2。 割り当てノード

割り当てを表すために使用されるノードのコレクションがあります。 ソースコード内の各割り当てステートメントは、AST内の単一のAssignノードになります。 nodes属性は、各割り当てターゲットのノードを含むリストです。 これが必要なのは、割り当てを連鎖させることができるためです。 a = b = 2。 リスト内の各ノードは、AssAttrAssListAssName、またはAssTupleのいずれかのクラスになります。

各ターゲット割り当てノードは、割り当てられているオブジェクトの種類を記述します。単純な名前の場合はAssNamea = 1。 割り当てられた属性のAssAttr、例: a.x = 1。 リストとタプルの拡張には、それぞれAssListAssTuplea, b, c = a_tuple

ターゲット割り当てノードには、ノードが割り当てに使用されているのか、削除ステートメントに使用されているのかを示すflags属性もあります。 AssNameは、削除ステートメントを表すためにも使用されます。 del x

式に複数の属性参照が含まれている場合、割り当てまたは削除ステートメントには、最終的な属性参照用に1つのAssAttrノードのみが含まれます。 他の属性参照は、AssAttrインスタンスのexpr属性のGetattrノードとして表されます。


33.3.3。 例

このセクションでは、PythonソースコードのASTの簡単な例をいくつか示します。 例は、parse()関数の使用方法、ASTのreprがどのように見えるか、およびASTノードの属性にアクセスする方法を示しています。

最初のモジュールは単一の関数を定義します。 doublelib.pyに保存されていると仮定します。

"""This is an example module.

This is the docstring.
"""

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

以下のインタラクティブインタプリタセッションでは、読みやすさのために長いAST表現を再フォーマットしました。 ASTreprsは修飾されていないクラス名を使用します。 reprからインスタンスを作成する場合は、 compiler.ast モジュールからクラス名をインポートする必要があります。

>>> import compiler
>>> mod = compiler.parseFile("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))))])

33.4。 訪問者を使用してASTを歩く

ビジターパターンは… compiler パッケージは、Pythonのイントロスペクション機能を利用して、ビジターのインフラストラクチャの多くの必要性を排除するビジターパターンのバリアントを使用します。

訪問するクラスは、訪問者を受け入れるようにプログラムする必要はありません。 訪問者は、特に関心のあるクラスの訪問メソッドを定義するだけで済みます。 デフォルトのvisitメソッドで残りを処理できます。

XXX訪問者のための魔法のvisit()メソッド。

compiler.visitor.walk(tree, visitor[, verbose])
class compiler.visitor.ASTVisitor

ASTVisitor は、正しい順序でツリー上を歩く責任があります。 ウォークは、 preorder()の呼び出しから始まります。 各ノードについて、 preorder()への visitor 引数で、「visitNodeType」という名前のメソッドをチェックします。ここで、NodeTypeはノードのクラスの名前です。 Whileノードの場合、visitWhile()が呼び出されます。 メソッドが存在する場合は、ノードを最初の引数として呼び出されます。

特定のノードタイプのvisitorメソッドは、ウォーク中に子ノードがどのように訪問されるかを制御できます。 ASTVisitor は、訪問者に訪問メソッドを追加することにより、訪問者の引数を変更します。 このメソッドは、特定の子ノードにアクセスするために使用できます。 特定のノードタイプの訪問者が見つからない場合は、 default()メソッドが呼び出されます。

ASTVisitor オブジェクトには次のメソッドがあります。

XXXは追加の引数を説明します

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


33.5。 バイトコードの生成

コードジェネレーターは、バイトコードを発行するビジターです。 各visitメソッドは、emit()メソッドを呼び出して、新しいバイトコードを発行できます。 基本的なコードジェネレーターは、モジュール、クラス、および関数に特化しています。 アセンブラは、発行された命令を低レベルのバイトコード形式に変換します。 コードオブジェクトの定数リストの生成やジャンプオフセットの計算などを処理します。