32.1. "parser" --- Python 解析木にアクセスする
**********************************************

======================================================================

"parser" モジュールは Python の内部パーサとバイトコード・コンパイラへ
のインターフェイスを提供します。このインターフェイスの第一の目的は、
Python コードから Python の式の解析木を編集したり、これから実行可能な
コードを作成したりできるようにすることです。これは任意の Python コード
の断片を文字列として構文解析や変更を行うより良い方法です。なぜなら、構
文解析がアプリケーションを作成するコードと同じ方法で実行されるからです
。その上、高速です。

注釈: Python 2.5 以降、抽象構文木 (AST) の生成・コンパイルの段階に割
  り込む には "ast" モジュールを使うのがずっとお手軽です。

このモジュールについて注意すべきことが少しあります。それは作成したデー
タ構造を利用するために重要なことです。この文書は Python コードの解析木
を編集するためのチュートリアルではありませんが、 "parser" モジュールを
使った例をいくつか示しています。

もっとも重要なことは、内部パーサが処理する Python の文法についてよく理
解しておく必要があるということです。言語の文法に関する完全な情報につい
ては、 Python 言語リファレンス を参照してください。標準の Python ディ
ストリビューションに含まれるファイル "Grammar/Grammar" の中で定義され
ている文法仕様から、パーサ自身は作成されています。このモジュールが作成
する ST オブジェクトの中に格納される解析木は、下で説明する "expr()" ま
たは "suite()" 関数によって作られるときに内部パーサから実際に出力され
るものです。 "sequence2st()" が作る ST オブジェクトは忠実にこれらの構
造をシミュレートしています。言語の形式文法が改訂されるために、 "正しい
" と考えられるシーケンスの値が Python のあるバージョンから別のバージョ
ンで変化することがあるということに注意してください。しかし、 Python の
あるバージョンから別のバージョンへテキストのソースのままコードを移せば
、目的のバージョンで正しい解析木を常に作成できます。ただし、インタープ
リタの古いバージョンへ移行する際に、最近の言語コンストラクトをサポート
していないことがあるという制限だけがあります。ソースコードが常に前方互
換性があるのに対して、一般的に解析木はあるバージョンから別のバージョン
への互換性がありません。

"st2list()" または "st2tuple()" から返されるシーケンスのそれぞれの要素
は単純な形式です。文法の非終端要素を表すシーケンスは常に一より大きい長
さを持ちます。最初の要素は文法の生成規則を識別する整数です。これらの整
数は C ヘッダファイル "Include/graminit.h" と Python モジュール
"symbol" の中の特定のシンボル名です。シーケンスに付け加えられている各
要素は、入力文字列の中で認識されたままの形で生成規則の構成要素を表して
います: これらは常に親と同じ形式を持つシーケンスです。この構造の注意す
べき重要な側面は、 "if_stmt" の中のキーワード "if" のような親ノードの
型を識別するために使われるキーワードがいかなる特別な扱いもなくノードツ
リーに含まれているということです。例えば、 "if" キーワードはタプル
"(1, 'if')" と表されます。ここで、 "1" は、ユーザが定義した変数名と関
数名を含むすべての "NAME" トークンに対応する数値です。行番号情報が必要
なときに返される別の形式では、同じトークンが "(1, 'if', 12)" のように
表されます。ここでは、 "12" が終端記号の見つかった行番号を表しています
。

終端要素は同じ方法で表現されますが、子の要素や識別されたソーステキスト
の追加は全くありません。上記の "if" キーワードの例が代表的なものです。
終端記号のいろいろな型は、 C ヘッダファイル "Include/token.h" と
Python モジュール "token" で定義されています。

ST オブジェクトはこのモジュールの機能をサポートするために必要ありませ
んが、三つの目的から提供されています: アプリケーションが複雑な解析木を
処理するコストを償却するため、Python のリストやタプル表現に比べてメモ
リ空間を保全する解析木表現を提供するため、解析木を操作する追加モジュー
ルを C で作ることを簡単にするため。ST オブジェクトを使っていることを隠
すために、簡単な "ラッパー" クラスを Python で作ることができます。

"parser" モジュールは二、三の別々の目的のために関数を定義しています。
もっとも重要な目的は ST オブジェクトを作ることと、 ST オブジェクトを解
析木とコンパイルされたコードオブジェクトのような他の表現に変換すること
です。しかし、 ST オブジェクトで表現された解析木の型を調べるために役に
立つ関数もあります。

参考:

  "symbol" モジュール
     解析木の内部ノードを表す便利な定数。

  "token" モジュール
     便利な解析木の葉のノードを表す定数とノード値をテストするための関
     数。


32.1.1. ST オブジェクトを作成する
=================================

ST オブジェクトはソースコードあるいは解析木から作られます。ST オブジェ
クトをソースから作るときは、"'eval'" と "'exec'" 形式を作成するために
別々の関数が使われます。

parser.expr(source)

   まるで "compile(source, 'file.py', 'eval')" への入力であるかのよう
   に、 "expr()" 関数はパラメータ *source* を構文解析します。解析が成
   功した場合は、 ST オブジェクトは内部解析木表現を保持するために作成
   されます。そうでなければ、適切な例外を発生させます。

parser.suite(source)

   まるで "compile(source, 'file.py', 'exec')" への入力であるかのよう
   に、 "suite()" 関数はパラメータ *source* を構文解析します。解析が成
   功した場合は、 ST オブジェクトは内部解析木表現を保持するために作成
   されます。そうでなければ、適切な例外を発生させます。

parser.sequence2st(sequence)

   この関数はシーケンスとして表現された解析木を受け取り、可能ならば内
   部表現を作ります。木が Python の文法に合っていることと、すべてのノ
   ードが Python のホストバージョンで有効なノード型であることを確認し
   た場合は、 ST オブジェクトが内部表現から作成されて呼び出し側へ返さ
   れます。内部表現の作成に問題があるならば、あるいは木が正しいと確認
   できないならば、 "ParserError" 例外を発生します。この方法で作られた
   ST オブジェクトが正しくコンパイルできると決めつけない方がよいでしょ
   う。 ST オブジェクトが "compilest()" へ渡されたとき、コンパイルによ
   って送出された通常の例外がまだ発生するかもしれません。これは
   ("MemoryError" 例外のような)構文に関係していない問題を示すのかもし
   れないし、 "del f(0)" を解析した結果のようなコンストラクトが原因で
   あるかもしれません。このようなコンストラクトは Python のパーサを逃
   れますが、バイトコードインタープリタによってチェックされます。

   終端トークンを表すシーケンスは、"(1, 'name')" 形式の二つの要素のリ
   ストか、または "(1, 'name', 56)" 形式の三つの要素のリストです。三番
   目の要素が存在する場合は、有効な行番号だとみなされます。行番号が指
   定されるのは、入力木の終端記号の一部に対してです。

parser.tuple2st(sequence)

   これは "sequence2st()" と同じ関数です。このエントリポイントは後方互
   換性のために維持されています。


32.1.2. ST オブジェクトを変換する
=================================

作成するために使われた入力に関係なく、ST オブジェクトはリスト木または
タプル木として表される解析木へ変換されるか、または実行可能なオブジェク
トへコンパイルされます。解析木は行番号情報を持って、あるいは持たずに抽
出されます。

parser.st2list(st, line_info=False, col_info=False)

   この関数は呼び出し側から *st* に ST オブジェクトを受け取り、解析木
   と等価な Python のリストを返します。結果のリスト表現はインスペクシ
   ョンあるいはリスト形式の新しい解析木の作成に使うことができます。リ
   スト表現を作るためにメモリが利用できる限り、この関数は失敗しません
   。解析木がインスペクションのためだけにつかわれるならば、メモリの消
   費量と断片化を減らすために "st2tuple()" を代わりに使うべきです。リ
   スト表現が必要とされるとき、この関数はタプル表現を取り出して入れ子
   のリストに変換するよりかなり高速です。

   *line_info* が真ならば、トークンを表すリストの三番目の要素として行
   番号情報がすべての終端トークンに含まれます。与えられた行番号はトー
   クン *が終わる* 行を指定していることに注意してください。フラグが偽
   または省略された場合は、この情報は省かれます。

parser.st2tuple(st, line_info=False, col_info=False)

   この関数は呼び出し側から *st* に ST オブジェクトを受け取り、解析木
   と等価な Python のタプルを返します。リストの代わりにタプルを返す以
   外は、この関数は "st2list()" と同じです。

   *line_info* が真ならば、トークンを表すリストの三番目の要素として行
   番号情報がすべての終端トークンに含まれます。フラグが偽または省略さ
   れた場合は、この情報は省かれます。

parser.compilest(st, filename='<syntax-tree>')

   組み込みの "exec()" や "eval()" 関数への呼び出しとして使えるコード
   オブジェクトを生成するために、 Python バイトコードコンパイラを ST
   オブジェクトに対して呼び出すことができます。この関数はコンパイラへ
   のインターフェイスを提供し、 *filename* パラメータで指定されるソー
   スファイル名を使って、 *st* からパーサへ内部解析木を渡します。
   *filename* に与えられるデフォルト値は、ソースが ST オブジェクトだっ
   たことを示唆しています。

   ST オブジェクトをコンパイルすることは、コンパイルに関する例外を引き
   起こすことになるかもしれません。例としては、 "del f(0)" の解析木に
   よって発生させられる "SyntaxError" があります: この文は Python の形
   式文法としては正しいと考えられますが、正しい言語コンストラクトでは
   ありません。この状況に対して発生する "SyntaxError" は、実際には
   Python バイトコンパイラによって通常作り出されます。これが "parser"
   モジュールがこの時点で例外を発生できる理由です。解析木のインスペク
   ションを行うことで、コンパイルが失敗するほとんどの原因をプログラム
   によって診断することができます。


32.1.3. ST オブジェクトに対する問い合わせ
=========================================

ST が式または suite として作成されたかどうかをアプリケーションが決定で
きるようにする二つの関数が提供されています。これらの関数のどちらも、
ST が "expr()" または "suite()" を通してソースコードから作られたかどう
か、あるいは、 "sequence2st()" を通して解析木から作られたかどうかを決
定できません。

parser.isexpr(st)

   *st* が "'eval'" 形式を表している場合に、この関数は真を返します。そ
   うでなければ、偽を返します。これは役に立ちます。なぜならば、通常は
   既存の組み込み関数を使ってもコードオブジェクトに対してこの情報を問
   い合わせることができないからです。このどちらのようにも
   "compilest()" によって作成されたコードオブジェクトに問い合わせるこ
   とはできませんし、そのコードオブジェクトは組み込み "compile()" 関数
   によって作成されたコードオブジェクトと同じであることに注意してくだ
   さい。

parser.issuite(st)

   ST オブジェクトが(通常 "suite" として知られる) "'exec'" 形式を表し
   ているかどうかを報告するという点で、この関数は "isexpr()" に酷似し
   ています。追加の構文が将来サポートされるかもしれないので、この関数
   が "not isexpr(st)" と等価であるとみなすのは安全ではありません。


32.1.4. 例外とエラー処理
========================

parser モジュールは例外を一つ定義していますが、Python ランタイム環境の
他の部分が提供する別の組み込み例外を発生させることもあります。各関数が
発生させる例外の情報については、それぞれ関数を参照してください。

exception parser.ParserError

   parser モジュール内部で障害が起きたときに発生する例外。普通の構文解
   析中に発生する組み込みの "SyntaxError" ではなく、一般的に妥当性確認
   が失敗した場合に引き起こされます。例外の引数としては、障害の理由を
   説明する文字列である場合と、 "sequence2st()" へ渡される解析木の中の
   障害を引き起こすシーケンスを含むタプルと説明用の文字列である場合が
   あります。モジュール内の他の関数の呼び出しは単純な文字列値を検出す
   ればよいだけですが、 "sequence2st()" の呼び出しはどちらの例外の型も
   処理できる必要があります。

普通は構文解析とコンパイル処理によって発生する例外を、関数
"compilest()" 、 "expr()" および "suite()" が発生させることに注意して
ください。このような例外には組み込み例外 "MemoryError" 、
"OverflowError" 、 "SyntaxError" および "SystemError" が含まれます。こ
うした場合には、これらの例外が通常その例外に関係する全ての意味を伝えま
す。詳細については、各関数の説明を参照してください。


32.1.5. ST オブジェクト
=======================

ST オブジェクト間の順序と等値性の比較がサポートされています。
("pickle" モジュールを使った) ST オブジェクトのピクルス化もサポートさ
れています。

parser.STType

   "expr()" 、 "suite()" と "sequence2st()" が返すオブジェクトの型。

ST オブジェクトは次のメソッドを持っています:

ST.compile(filename='<syntax-tree>')

   "compilest(st, filename)" と同じ。

ST.isexpr()

   "isexpr(st)" と同じ。

ST.issuite()

   "issuite(st)" と同じ。

ST.tolist(line_info=False, col_info=False)

   "st2list(st, line_info, col_info)" と同じ。

ST.totuple(line_info=False, col_info=False)

   "st2tuple(st, line_info, col_info)" と同じ。


32.1.6. 例: "compile()" のエミュレーション
==========================================

たくさんの有用な演算を構文解析とバイトコード生成の間に行うことができま
すが、もっとも単純な演算は何もしないことです。このため、 "parser" モジ
ュールを使って中間データ構造を作ることは次のコードと等価です

   >>> code = compile('a + 5', 'file.py', 'eval')
   >>> a = 5
   >>> eval(code)
   10

"parser" モジュールを使った等価な演算はやや長くなりますが、 ST オブジ
ェクトとして中間内部解析木が維持されるようにします:

   >>> import parser
   >>> st = parser.expr('a + 5')
   >>> code = st.compile('file.py')
   >>> a = 5
   >>> eval(code)
   10

ST とコードオブジェクトの両方が必要なアプリケーションでは、このコード
を簡単に利用できる関数にまとめることができます:

   import parser

   def load_suite(source_string):
       st = parser.suite(source_string)
       return st, st.compile()

   def load_expression(source_string):
       st = parser.expr(source_string)
       return st, st.compile()
