32.2. "ast" --- 抽象構文木
**************************

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

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

"ast" モジュールは、Python アプリケーションで Python の抽象構文木を処
理しやすくするものです。抽象構文そのものは、Python のリリースごとに変
化する可能性があります。このモジュールを使用すると、現在の文法をプログ
ラム上で知る助けになるでしょう。

抽象構文木を作成するには、 "ast.PyCF_ONLY_AST" を組み込み関数
"compile()" のフラグとして渡すか、あるいはこのモジュールで提供されてい
るヘルパー関数 "parse()" を使います。その結果は、 "ast.AST" を継承した
クラスのオブジェクトのツリーとなります。抽象構文木は組み込み関数
"compile()" を使って Python コード・オブジェクトにコンパイルすることが
できます。


32.2.1. Node クラス
===================

class ast.AST

   このクラスは全ての AST ノード・クラスの基底です。実際のノード・クラ
   スは 後ほど 示す "Parser/Python.asdl" ファイルから派生したものです
   。これらのクラスは "_ast" C モジュールで定義され、 "ast" にもエクス
   ポートし直されています。

   抽象文法の左辺のシンボル一つずつにそれぞれ一つのクラスがあります (
   たとえば "ast.stmt" や "ast.expr")。それに加えて、右辺のコンストラ
   クタ一つずつにそれぞれ一つのクラスがあり、これらのクラスは左辺のツ
   リーのクラスを継承しています。たとえば、 "ast.BinOp" は "ast.expr"
   から継承しています。代替を伴った生成規則 (production rules with
   alternatives) (別名 "sums") の場合、左辺は抽象クラスとなり、特定の
   コンストラクタ・ノードのインスタンスのみが作成されます。

   _fields

      各具象クラスは属性 "_fields" を持っており、すべての子ノードの名
      前をそこに保持しています。

      具象クラスのインスタンスは、各子ノードに対してそれぞれひとつの属
      性を持っています。この属性は、文法で定義された型となります。たと
      えば "ast.BinOp" のインスタンスは "left" という属性を持っており
      、その型は "ast.expr" です。

      これらの属性が、文法上 (クエスチョンマークを用いて) オプションで
      あるとマークされている場合は、その値が "None" となることもありま
      す。属性が0個以上の複数の値をとりうる場合 (アスタリスクでマーク
      されている場合) は、値は Python のリストで表されます。全ての属性
      は AST を "compile()" でコンパイルする際には存在しなければならず
      、そして妥当な値でなければなりません。

   lineno
   col_offset

      "ast.expr" や "ast.stmt" のサブクラスのインスタンスにはさらに
      "lineno" や "col_offset" といった属性があります。 "lineno" はソ
      ーステキスト上の行番号 (1 から数え始めるので、最初の行の行番号は
      1 となります)、そして "col_offset" はノードが生成した最初のトー
      クンの UTF-8 バイトオフセットとなります。 UTF-8 オフセットが記録
      される理由は、パーサが内部で UTF-8 を使用するからです。

   クラス "ast.T" のコンストラクタは引数を次のように解析します:

   * 位置引数があるとすれば、 "T._fields" にあるのと同じだけの個数が無
     ければなりません。これらの引数はそこにある名前を持った属性として
     割り当てられます。

   * キーワード引数があるとすれば、それらはその名前の属性にその値を割
     り当てられます。

   たとえば、 "ast.UnaryOp" ノードを生成して属性を埋めるには、次のよう
   にすることができます

      node = ast.UnaryOp()
      node.op = ast.USub()
      node.operand = ast.Num()
      node.operand.n = 5
      node.operand.lineno = 0
      node.operand.col_offset = 0
      node.lineno = 0
      node.col_offset = 0

   もしくはよりコンパクトにも書けます

      node = ast.UnaryOp(ast.USub(), ast.Num(5, lineno=0, col_offset=0),
                         lineno=0, col_offset=0)


32.2.2. 抽象文法 (Abstract Grammar)
===================================

抽象文法は、現在次のように定義されています:

   -- ASDL's 7 builtin types are:
   -- identifier, int, string, bytes, object, singleton, constant
   --
   -- singleton: None, True or False
   -- constant can be None, whereas None means "no value" for object.

   module Python
   {
       mod = Module(stmt* body)
           | Interactive(stmt* body)
           | Expression(expr body)

           -- not really an actual node but useful in Jython's typesystem.
           | Suite(stmt* body)

       stmt = FunctionDef(identifier name, arguments args,
                          stmt* body, expr* decorator_list, expr? returns)
             | AsyncFunctionDef(identifier name, arguments args,
                                stmt* body, expr* decorator_list, expr? returns)

             | ClassDef(identifier name,
                expr* bases,
                keyword* keywords,
                stmt* body,
                expr* decorator_list)
             | Return(expr? value)

             | Delete(expr* targets)
             | Assign(expr* targets, expr value)
             | AugAssign(expr target, operator op, expr value)
             -- 'simple' indicates that we annotate simple name without parens
             | AnnAssign(expr target, expr annotation, expr? value, int simple)

             -- use 'orelse' because else is a keyword in target languages
             | For(expr target, expr iter, stmt* body, stmt* orelse)
             | AsyncFor(expr target, expr iter, stmt* body, stmt* orelse)
             | While(expr test, stmt* body, stmt* orelse)
             | If(expr test, stmt* body, stmt* orelse)
             | With(withitem* items, stmt* body)
             | AsyncWith(withitem* items, stmt* body)

             | Raise(expr? exc, expr? cause)
             | Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)
             | Assert(expr test, expr? msg)

             | Import(alias* names)
             | ImportFrom(identifier? module, alias* names, int? level)

             | Global(identifier* names)
             | Nonlocal(identifier* names)
             | Expr(expr value)
             | Pass | Break | Continue

             -- XXX Jython will be different
             -- col_offset is the byte offset in the utf8 string the parser uses
             attributes (int lineno, int col_offset)

             -- BoolOp() can use left & right?
       expr = BoolOp(boolop op, expr* values)
            | BinOp(expr left, operator op, expr right)
            | UnaryOp(unaryop op, expr operand)
            | Lambda(arguments args, expr body)
            | IfExp(expr test, expr body, expr orelse)
            | Dict(expr* keys, expr* values)
            | Set(expr* elts)
            | ListComp(expr elt, comprehension* generators)
            | SetComp(expr elt, comprehension* generators)
            | DictComp(expr key, expr value, comprehension* generators)
            | GeneratorExp(expr elt, comprehension* generators)
            -- the grammar constrains where yield expressions can occur
            | Await(expr value)
            | Yield(expr? value)
            | YieldFrom(expr value)
            -- need sequences for compare to distinguish between
            -- x < 4 < 3 and (x < 4) < 3
            | Compare(expr left, cmpop* ops, expr* comparators)
            | Call(expr func, expr* args, keyword* keywords)
            | Num(object n) -- a number as a PyObject.
            | Str(string s) -- need to specify raw, unicode, etc?
            | FormattedValue(expr value, int? conversion, expr? format_spec)
            | JoinedStr(expr* values)
            | Bytes(bytes s)
            | NameConstant(singleton value)
            | Ellipsis
            | Constant(constant value)

            -- the following expression can appear in assignment context
            | Attribute(expr value, identifier attr, expr_context ctx)
            | Subscript(expr value, slice slice, expr_context ctx)
            | Starred(expr value, expr_context ctx)
            | Name(identifier id, expr_context ctx)
            | List(expr* elts, expr_context ctx)
            | Tuple(expr* elts, expr_context ctx)

             -- col_offset is the byte offset in the utf8 string the parser uses
             attributes (int lineno, int col_offset)

       expr_context = Load | Store | Del | AugLoad | AugStore | Param

       slice = Slice(expr? lower, expr? upper, expr? step)
             | ExtSlice(slice* dims)
             | Index(expr value)

       boolop = And | Or

       operator = Add | Sub | Mult | MatMult | Div | Mod | Pow | LShift
                    | RShift | BitOr | BitXor | BitAnd | FloorDiv

       unaryop = Invert | Not | UAdd | USub

       cmpop = Eq | NotEq | Lt | LtE | Gt | GtE | Is | IsNot | In | NotIn

       comprehension = (expr target, expr iter, expr* ifs, int is_async)

       excepthandler = ExceptHandler(expr? type, identifier? name, stmt* body)
                       attributes (int lineno, int col_offset)

       arguments = (arg* args, arg? vararg, arg* kwonlyargs, expr* kw_defaults,
                    arg? kwarg, expr* defaults)

       arg = (identifier arg, expr? annotation)
              attributes (int lineno, int col_offset)

       -- keyword arguments supplied to call (NULL identifier for **kwargs)
       keyword = (identifier? arg, expr value)

       -- import name with optional 'as' alias.
       alias = (identifier name, identifier? asname)

       withitem = (expr context_expr, expr? optional_vars)
   }



32.2.3. "ast" ヘルパー
======================

ノード・クラスの他に、 "ast" モジュールは以下のような抽象構文木をトラ
バースするためのユーティリティ関数やクラスも定義しています:

ast.parse(source, filename='<unknown>', mode='exec')

   *source* を解析して AST ノードにします。"compile(source, filename,
   mode, ast.PyCF_ONLY_AST)" と等価です。

   警告:

     十分に大きい文字列や複雑な文字列によって Python の抽象構文木コン
     パイラのスタックの深さの限界を越えることで、 Python インタプリタ
     をクラッシュさせることができます。

ast.literal_eval(node_or_string)

   式ノードまたは Python のリテラルまたはコンテナのディスプレイ表現を
   表す文字列を安全に評価します。与えられる文字列またはノードは次のリ
   テラルのみからなるものに限られます: 文字列、バイト列、数、タプル、
   リスト、辞書、集合、ブール値、 "None" 。

   この関数は Python の式を含んだ信頼出来ない出どころからの文字列を、
   値自身を解析することなしに安全に評価するのに使えます。この関数は、
   例えば演算や添え字を含んだ任意の複雑な表現を評価するのには使えませ
   ん。

   警告:

     十分に大きい文字列や複雑な文字列によって Python の抽象構文木コン
     パイラのスタックの深さの限界を越えることで、 Python インタプリタ
     をクラッシュさせることができます。

   バージョン 3.2 で変更: バイト列リテラルと集合リテラルが受け取れるよ
   うになりました。

ast.get_docstring(node, clean=True)

   与えられた *node* (これは "FunctionDef", "ClassDef", "Module" のい
   ずれかのノードでなければなりません) のドキュメント文字列を返します
   。もしドキュメント文字列が無ければ "None" を返します。 *clean* が真
   ならば、ドキュメント文字列のインデントを "inspect.cleandoc()" を用
   いて一掃します。

ast.fix_missing_locations(node)

   "compile()" はノード・ツリーをコンパイルする際、 "lineno" と
   "col_offset" 両属性をサポートする全てのノードに対しそれが存在するも
   のと想定します。生成されたノードに対しこれらを埋めて回るのはどちら
   かというと退屈な作業なので、このヘルパーが再帰的に二つの属性がセッ
   トされていないものに親ノードと同じ値をセットしていきます。再帰の出
   発点が *node* です。

ast.increment_lineno(node, n=1)

   *node* から始まるツリーの全てのノードの行番号を *n* ずつ増やします
   。これはファイルの中で別の場所に「コードを動かす」ときに便利です。

ast.copy_location(new_node, old_node)

   ソースの場所 ("lineno" と "col_offset") を *old_node* から
   *new_node* に可能ならばコピーし、 *new_node* を返します。

ast.iter_fields(node)

   *node* にある "node._fields" のそれぞれのフィールドを "(フィールド
   名, 値)" のタプルとして yield します。

ast.iter_child_nodes(node)

   *node* の直接の子ノード全てを yield します。すなわち、yield される
   のは、ノードであるような全てのフィールドおよびノードのリストである
   ようなフィールドの全てのアイテムです。

ast.walk(node)

   *node* の全ての子孫ノード(*node* 自体を含む)を再帰的に yield します
   。順番は決められていません。この関数はノードをその場で変更するだけ
   で文脈を気にしないような場合に便利です。

class ast.NodeVisitor

   抽象構文木を渡り歩いてビジター関数を見つけたノードごとに呼び出すノ
   ード・ビジターの基底クラスです。この関数は "visit()" メソッドに送ら
   れる値を返してもかまいません。

   このクラスはビジター・メソッドを付け加えたサブクラスを派生させるこ
   とを意図しています。

   visit(node)

      ノードを訪れます。デフォルトの実装では "self.visit_*classname*"
      というメソッド (ここで *classname* はノードのクラス名です) を呼
      び出すか、そのメソッドがなければ "generic_visit()" を呼び出しま
      す。

   generic_visit(node)

      このビジターはノードの全ての子について "visit()" を呼び出します
      。

      注意して欲しいのは、専用のビジター・メソッドを具えたノードの子ノ
      ードは、このビジターが "generic_visit()" を呼び出すかそれ自身で
      子ノードを訪れない限り訪れられないということです。

   トラバースの途中でノードを変化させたいならば "NodeVisitor" を使って
   はいけません。そうした目的のために変更を許す特別なビジター
   ("NodeTransformer") があります。

class ast.NodeTransformer

   "NodeVisitor" のサブクラスで抽象構文木を渡り歩きながらノードを変更
   することを許すものです。

   "NodeTransformer" は抽象構文木(AST)を渡り歩き、ビジター・メソッドの
   戻り値を使って古いノードを置き換えたり削除したりします。ビジター・
   メソッドの戻り値が "None" ならば、ノードはその場から取り去られ、そ
   うでなければ戻り値で置き換えられます。置き換えない場合は戻り値が元
   のノードそのものであってもかまいません。

   それでは例を示しましょう。Name (たとえば "foo") を見つけるたび全て
   "data['foo']" に書き換える変換器 (transformer) です:

      class RewriteName(NodeTransformer):

          def visit_Name(self, node):
              return copy_location(Subscript(
                  value=Name(id='data', ctx=Load()),
                  slice=Index(value=Str(s=node.id)),
                  ctx=node.ctx
              ), node)

   操作しようとしているノードが子ノードを持つならば、その子ノードの変
   形も自分で行うか、またはそのノードに対し最初に "generic_visit()" メ
   ソッドを呼び出すか、それを行うのはあなたの責任だということを肝に銘
   じましょう。

   文のコレクションであるようなノード (全ての文のノードが当てはまりま
   す) に対して、このビジターは単独のノードではなくノードのリストを返
   すかもしれません。

   たいてい、変換器の使い方は次のようになります:

      node = YourTransformer().visit(node)

ast.dump(node, annotate_fields=True, include_attributes=False)

   *node* 中のツリーのフォーマットされたダンプを返します。主な使い道は
   デバッグです。返される文字列は名前とフィールドの値を表示します。こ
   れを使うとコードは評価できなくなりますので、評価が必要ならば
   *annotate_fields* に "False" をセットしなければなりません。行番号や
   列オフセットのような属性はデフォルトではダンプされません。これが欲
   しければ、*include_attributes* を "True" にセットすることができます
   。

参考: 外部ドキュメント Green Tree Snakes には Python AST についての詳細が
    書かれています。
