32.7. "tokenize" --- Pythonソースのためのトークナイザ
*****************************************************

**ソースコード:** Lib/tokenize.py

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

"tokenize" モジュールでは、Python で実装された Python ソースコードの字
句解析器を提供します。さらに、このモジュールの字句解析器はコメントもト
ークンとして返します。このため、このモジュールはスクリーン上で表示する
際の色付け機能 (colorizers) を含む "清書出力器 (pretty-printer)" を実
装する上で便利です。

トークンストリームの扱いをシンプルにするために、全ての 演算子
(operator) と デリミタ (delimiter) トークンはジェネリックな "token.OP"
トークンタイプとして返されます。正確な型は
"tokenize.generate_tokens()" からの戻り値のタプルの 2 番目のフィールド
(実際のマッチしたトークン文字列を含んでいます) を特定の演算子トークン
を識別する文字シーケンスかチェックすれば決定出来ます。

第一のエントリポイントはジェネレータ(*generator*)です:

tokenize.generate_tokens(readline)

   "generate_tokens()" ジェネレータは一つの引数 *readline* を必要とし
   ます。この引数は呼び出し可能オブジェクトで、組み込みファイルオブジ
   ェクトにおける "readline()" メソッドと同じインタフェースを提供して
   いなければなりません (ファイルオブジェクト 節を参照してください)。
   この関数は呼び出しのたびに入力内の一行を文字列で返さなければなりま
   せん。もしくは、 *readline* を "StopIteration" を送出することで完了
   を知らせる呼び出し可能オブジェクトとすることもできます。

   このジェネレータは次の 5 要素のタプルを返します; トークンタイプ; ト
   ークン文字列; ソース内でそのトークンがどの行、列で開始するかを示す
   int の "(srow, scol)" タプル; どの行、列で終了するかを示す int の
   "(erow, ecol)" タプル; トークンが見つかった行。 (タプルの最後の要素
   にある) 行は *論理* 行で、継続行が含まれます。

   バージョン 2.2 で追加.

後方互換性のために古いエントリポイントが残されています:

tokenize.tokenize(readline[, tokeneater])

   "tokenize()" 関数は二つのパラメータを取ります: 一つは入力ストリーム
   を表し、もう一つは "tokenize()" のための出力メカニズムを与えます。

   最初のパラメータ、 *readline* は、組み込みファイルオブジェクトの
   "readline()" メソッドと同じインタフェイスを提供する呼び出し可能オブ
   ジェクトでなければなりません ( ファイルオブジェクト 節を参照)。この
   関数は呼び出しのたびに入力内の一行を文字列で返さなければなりません
   。もしくは、 *readline* を "StopIteration" を送出することで完了を知
   らせる呼び出し可能オブジェクトとすることもできます。

   バージョン 2.5 で変更: "StopIteration" サポートの追加。

   二番目のパラメータ *tokeneater* も呼び出し可能オブジェクトでなけれ
   ばなりません。この関数は各トークンに対して一度だけ呼び出され、
   "generate_tokens()" が生成するタプルに対応する 5 つの引数をとります
   。

"token" モジュールの全ての定数は "tokenize" でも公開されており、これに
加え、以下の二つのトークン値が "tokenize()" の *tokeneater* 関数に渡さ
れる可能性があります:

tokenize.COMMENT

   コメントであることを表すために使われるトークン値です。

tokenize.NL

   終わりではない改行を表すために使われるトークン値。NEWLINE トークン
   は Pythonコードの論理行の終わりを表します。NLトークンはコードの論理
   行が複数の物理行にわたって続いているときに作られます。

もう一つの関数がトークン化プロセスを逆転するために提供されています。こ
れは、スクリプトを字句解析し、トークンのストリームに変更を加え、変更さ
れたスクリプトを書き戻すようなツールを作成する際に便利です。

tokenize.untokenize(iterable)

   トークンの列を Python ソースコードに変換します。 *iterable* は少な
   くとも二つの要素、トークンタイプおよびトークン文字列、からなるシー
   ケンスを返さなければいけません。 その他のシーケンスの要素は無視され
   ます。

   再構築されたスクリプトは一つの文字列として返されます。得られる結果
   はもう一度字句解析すると入力と一致することが保証されるので、変換が
   ロスレスでありラウンドトリップできることは間違いありません。この保
   証はトークン型およびトークン文字列に対してのものでトークン間のスペ
   ース (コラム位置)のようなものは変わることがあり得ます。

   バージョン 2.5 で追加.

exception tokenize.TokenError

   docstring や複数行にわたることが許される式がファイル内のどこかで終
   わっていない場合に送出されます。例えば:

      """Beginning of
      docstring

   もしくは:

      [1,
       2,
       3

閉じていないシングルクォート文字列は送出されるべきエラーの原因とならな
いことに注意してください。それらは "ERRORTOKEN" とトークン化され、続い
てその内容がトークン化されます。

スクリプト書き換えの例で、浮動小数点数リテラルを Decimal オブジェクト
に変換します:

   def decistmt(s):
       """Substitute Decimals for floats in a string of statements.

       >>> from decimal import Decimal
       >>> s = 'print +21.3e-5*-.1234/81.7'
       >>> decistmt(s)
       "print +Decimal ('21.3e-5')*-Decimal ('.1234')/Decimal ('81.7')"

       >>> exec(s)
       -3.21716034272e-007
       >>> exec(decistmt(s))
       -3.217160342717258261933904529E-7

       """
       result = []
       g = generate_tokens(StringIO(s).readline)   # tokenize the string
       for toknum, tokval, _, _, _  in g:
           if toknum == NUMBER and '.' in tokval:  # replace NUMBER tokens
               result.extend([
                   (NAME, 'Decimal'),
                   (OP, '('),
                   (STRING, repr(tokval)),
                   (OP, ')')
               ])
           else:
               result.append((toknum, tokval))
       return untokenize(result)
