32.2. "ast" --- Árvores de Sintaxe Abstrata
*******************************************

**Código-fonte:** Lib/ast.py

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

O módulo "ast" ajuda os aplicativos Python a processar árvores da
gramática de sintaxe abstrata do Python. A sintaxe abstrata em si pode
mudar em cada lançamento do Python; este módulo ajuda a descobrir
programaticamente como é a gramática atual.

Uma árvore de sintaxe abstrata pode ser gerada passando
"ast.PyCF_ONLY_AST" como um sinalizador para a função interna
"compile()", ou usando o auxiliar "parse()" fornecido neste módulo. O
resultado será uma árvore de objetos cujas classes são herdadas de
"ast.AST". Uma árvore de sintaxe abstrata pode ser compilada em um
objeto de código Python usando a função interna "compile()".


32.2.1. Classes de nó
=====================

class ast.AST

   Esta é a base de todas as classes de nós AST. As classes de nós
   reais são derivadas do arquivo "Parser/Python.asdl", o qual é
   reproduzido abaixo. Elas são definidas no módulo C "_ast" e
   reexportadas em "ast".

   Há uma classe definida para cada símbolo do lado esquerdo na
   gramática abstrata (por exemplo, "ast.stmt" ou "ast.expr"). Além
   disso, existe uma classe definida para cada construtor no lado
   direito; essas classes herdam das classes para as árvores do lado
   esquerdo. Por exemplo, "ast.BinOp" herda de "ast.expr". Para regras
   de produção com alternativas ("somas"), a classe do lado esquerdo é
   abstrata: apenas instâncias de nós construtores específicos são
   criadas.

   _fields

      Cada classe concreta possui um atributo "_fields" que dá os
      nomes de todos os nós filhos.

      Cada instância de uma classe concreta tem um atributo para cada
      nó filho, do tipo definido na gramática. Por exemplo, as
      instâncias "ast.BinOp" possuem um atributo "left" do tipo
      "ast.expr".

      Se estes atributos estiverem marcados como opcionais na
      gramática (usando um ponto de interrogação), o valor pode ser
      "None". Se os atributos puderem ter valor zero ou mais (marcados
      com um asterisco), os valores serão representados como listas do
      Python. Todos os atributos possíveis devem estar presentes e ter
      valores válidos ao compilar uma AST com "compile()".

   lineno
   col_offset

      Instâncias das subclasses "ast.expr" e "ast.stmt" possuem
      atributos "lineno" e "col_offset". O "lineno" é o número da
      linha do texto fonte (indexado em 1, de modo que a primeira
      linha é a linha 1) e o "col_offset" é o deslocamento de bytes
      UTF-8 do primeiro token que gerou o nó. O deslocamento UTF-8 é
      registrado porque o analisador usa o UTF-8 internamente.

   O construtor de uma classe "ast.T" analisa seus argumentos da
   seguinte forma:

   * Se houver argumentos posicionais, deve haver tantos quanto houver
     itens em "T._fields"; eles serão atribuídos como atributos desses
     nomes.

   * Se houver argumentos de palavra-chave, eles definirão os
     atributos dos mesmos nomes para os valores fornecidos.

   Por exemplo, para criar e popular um nó "ast.UnaryOp", você poderia
   usar

      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

   ou a forma mais compacta

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


32.2.2. Gramática Abstrata
==========================

A gramática abstrata está atualmente definida da seguinte forma:

   -- 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. Auxiliares de "ast"
===========================

Além das classes de nós, o módulo "ast" define essas funções e classes
utilitárias para percorrer árvores de sintaxe abstrata:

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

   Analisa a fonte em um nó AST. Equivalente a "compile(source,
   filename, mode, ast.PyCF_ONLY_AST)".

   Aviso:

     É possível travar o interpretador Python com uma string
     suficientemente grande/complexa devido às limitações de
     profundidade da pilha no compilador de AST do Python.

ast.literal_eval(node_or_string)

   Avalia com segurança um nó de expressão ou uma string contendo um
   literal Python ou exibição de contêiner. A string ou o nó fornecido
   pode consistir apenas nas seguintes estruturas literais de Python:
   strings, bytes, números, tuplas, listas, dicts, conjuntos,
   booleanos e "None".

   Isso pode ser usado para avaliar com segurança strings contendo
   valores Python de fontes não confiáveis sem a necessidade de
   analisar os valores por si próprio. Não é capaz de avaliar
   expressões arbitrariamente complexas, por exemplo, envolvendo
   operadores ou indexação.

   Aviso:

     É possível travar o interpretador Python com uma string
     suficientemente grande/complexa devido às limitações de
     profundidade da pilha no compilador de AST do Python.

   Alterado na versão 3.2: Agora permite bytes e literais de
   conjuntos.

ast.get_docstring(node, clean=True)

   Return the docstring of the given *node* (which must be a
   "FunctionDef", "ClassDef" or "Module" node), or "None" if it has no
   docstring.  If *clean* is true, clean up the docstring's
   indentation with "inspect.cleandoc()".

ast.fix_missing_locations(node)

   Quando você compila uma árvore de nós com "compile()", o compilador
   espera atributos :attr:` lineno` e "col_offset" para cada nó que os
   suporta. Isso é tedioso para preencher nós gerados, portanto, esse
   auxiliar adiciona esses atributos recursivamente, onde ainda não
   estão definidos, definindo-os para os valores do nó pai. Ele
   funciona recursivamente a partir do *nó*.

ast.increment_lineno(node, n=1)

   Incrementa o número da linha de cada nó na árvore, começando em
   *node* por *n*. Isso é útil para "mover código" para um local
   diferente em um arquivo.

ast.copy_location(new_node, old_node)

   Copia o local de origem ("lineno" e "col_offset") de *old_node*
   para *new_node* se possível, e retorna *new_node*.

ast.iter_fields(node)

   Produz uma tupla de "(fieldname, value)" para cada campo em
   "node._fields" que esteja presente em *node*.

ast.iter_child_nodes(node)

   Produz todos os nós filhos diretos de *node*, ou seja, todos os
   campos que são nós e todos os itens de campos que são listas de
   nós.

ast.walk(node)

   Produz recursivamente todos os nós descendentes na árvore começando
   em *node* (incluindo o próprio *node*), em nenhuma ordem
   especificada. Isso é útil se você quiser apenas modificar nós no
   lugar e não se importar com o contexto.

class ast.NodeVisitor

   Uma classe base de visitante de nó que percorre a árvore de sintaxe
   abstrata e chama uma função de visitante para cada nó encontrado.
   Esta função pode retornar um valor que é encaminhado pelo método
   "visit()".

   Esta classe deve ser uma subclasse, com a subclasse adicionando
   métodos visitantes.

   visit(node)

      Visita um nó. A implementação padrão chama o método chamado
      "self.visit_*nomedaclasse*"  sendo *nomedaclasse* o nome da
      classe do nó, ou "generic_visit()" se aquele método não existir.

   generic_visit(node)

      Este visitante chama "visit()" em todos os filhos do nó.

      Observe que nós filhos de nós que possuem um método de visitante
      personalizado não serão visitados, a menos que o visitante chame
      "generic_visit()" ou os visite por conta própria.

   Não use o "NodeVisitor" se você quiser aplicar mudanças nos nós
   durante a travessia. Para isso existe um visitante especial
   ("NodeTransformer") que permite modificações.

class ast.NodeTransformer

   A subclasse "NodeVisitor" que percorre a árvore de sintaxe abstrata
   e permite a modificação de nós.

   O "NodeTransformer" percorrerá a AST e usará o valor de retorno dos
   métodos do visitante para substituir ou remover o nó antigo. Se o
   valor de retorno do método visitante for "None", o nó será removido
   de seu local, caso contrário, ele será substituído pelo valor de
   retorno. O valor de retorno pode ser o nó original, caso em que não
   há substituição.

   Aqui está um exemplo de transformador que rescreve todas as
   ocorrências de procuras por nome ("foo") para "data['foo']":

      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)

   Tenha em mente que, se o nó em que você está operando tiver nós
   filhos, você deve transformar os nós filhos por conta própria ou
   chamar o método "generic_visit()" para o nó primeiro.

   Para nós que faziam parte de uma coleção de instruções (que se
   aplica a todos os nós de instrução), o visitante também pode
   retornar uma lista de nós em vez de apenas um único nó.

   Normalmente você usa o transformador assim:

      node = YourTransformer().visit(node)

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

   Retorna um despejo formatado da árvore em *node*. Isso é útil
   principalmente para fins de depuração. A string retornada mostrará
   os nomes e os valores dos campos. Isso torna o código impossível de
   ser avaliado, portanto, se a avaliação for desejada,
   *annotate_fields* deve ser definido como "False". Atributos como
   números de linha e deslocamentos de coluna não são despejados por
   padrão. Se isso é desejado, *include_attributes* pode ser definido
   como "True".

Ver também:

  Green Tree Snakes, an external documentation resource, has good
  details on working with Python ASTs.
