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ファイルを生成します。
コンパイラパッケージには、 ast 、consts
、future
、misc
、pyassem
、 pycodegen
、symbols
、transformer
、および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 インスタンスには、
None
のlineno
属性があります。 XXXどのノードのルールが有用なlinenoを持つかわからない。すべての Node オブジェクトは、次のメソッドを提供します。
- getChildren()
子ノードとオブジェクトのフラット化されたリストを発生順に返します。 具体的には、ノードの順序は、Python文法に表示される順序です。 すべての子が Node インスタンスであるわけではありません。 たとえば、関数やクラスの名前はプレーンな文字列です。
- getChildNodes()
子ノードのフラット化されたリストを発生順に返します。 このメソッドは getChildren()に似ていますが、 Node インスタンスである子のみを返す点が異なります。
2つの例は、 Node クラスの一般的な構造を示しています。 while ステートメントは、次の文法生成によって定義されます。
while_stmt: "while" expression ":" suite
["else" ":" suite]
While
ノードには、 test 、body
、およびelse_
の3つの属性があります。 (属性の自然名がPythonの予約語でもある場合、属性名として使用することはできません。 単語にアンダースコアを追加して正当な識別子にするため、 else ではなくelse_
になります。)
if ステートメントは、複数のテストを含めることができるため、より複雑です。
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
If
ノードは、tests
とelse_
の2つの属性のみを定義します。 tests
属性は、一連のテスト式であり、結果として生じるボディペアです。 if / elif 句ごとに1つのペアがあります。 ペアの最初の要素はテスト式です。 2番目の要素はStmt
ノードであり、テストがtrueの場合に実行するコードが含まれています。
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
|
等号ごとに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
。 リスト内の各ノードは、AssAttr
、AssList
、AssName
、またはAssTuple
のいずれかのクラスになります。
各ターゲット割り当てノードは、割り当てられているオブジェクトの種類を記述します。単純な名前の場合はAssName
。 a = 1
。 割り当てられた属性のAssAttr
、例: a.x = 1
。 リストとタプルの拡張には、それぞれAssList
とAssTuple
。 a, 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()
メソッドを呼び出して、新しいバイトコードを発行できます。 基本的なコードジェネレーターは、モジュール、クラス、および関数に特化しています。 アセンブラは、発行された命令を低レベルのバイトコード形式に変換します。 コードオブジェクトの定数リストの生成やジャンプオフセットの計算などを処理します。