"parser" --- Acceder a árboles de análisis sintáctico de Python
***************************************************************

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

El módulo "parser" proporciona una interfaz para el analizador
sintáctico interno de Python y para el compilador de código de bytes.
El propósito principal de esta interfaz es permitir que el código
Python edite el árbol de análisis sintáctico de una expresión Python y
cree código ejecutable a partir de este. Esto es mejor que intentar
analizar y modificar una cadena de caracteres que conforma un
fragmento de código Python arbitrario, porque el análisis se realiza
de una manera idéntica al código que construye la aplicación. También
es más rápido.

Nota:

  A partir de Python 2.5, es más práctico realizar estas operaciones
  entre la generación del árbol de sintaxis abstracta (AST) y la etapa
  de compilación, utilizando para ello el módulo "ast".

Hay algunas cosas a tener en cuenta sobre este módulo, que son
importantes para hacer un uso correcto de las estructuras de datos
creadas. Este no es un tutorial sobre cómo editar los árboles de
análisis sintáctico para código Python, no obstante, sí que se
proporcionan algunos ejemplos de uso del módulo "parser".

Lo más importante es que se requiere una buena comprensión de la
gramática de Python procesada por el analizador sintáctico interno.
Para obtener información completa sobre la sintaxis del lenguaje,
consulta Referencia del Lenguaje Python. El analizador sintáctico en
sí se crea a partir de una especificación gramatical definida en el
archivo "Grammar/Grammar" en la distribución estándar de Python. Los
árboles de análisis sintáctico almacenados en los objetos ST creados
por este módulo son la salida real del analizador interno cuando son
creados por las funciones "expr()" o "suite()", descritas a
continuación. Los objetos ST creados por "sequence2st()" simulan
fielmente esas estructuras. Ten en cuenta que los valores de las
secuencias que se consideran "correctas" variarán de una versión de
Python a otra, a medida que se revise la gramática formal del
lenguaje. Por el contrario, portar código fuente en forma de texto de
una versión de Python a otra siempre permitirá crear árboles de
análisis correctos en la versión de destino, con la única restricción
de la migración a una versión anterior del intérprete que no admita
construcciones del lenguaje más recientes. Los árboles de análisis
sintáctico no suelen ser compatibles de una versión a otra, aunque el
código fuente suele ser compatible con versiones posteriores dentro de
una misma serie de versiones principales.

Cada elemento de las secuencias retornadas por "st2list()" o
"st2tuple()" tiene una forma simple. Las secuencias que representan
elementos no terminales en la gramática siempre tienen una longitud
mayor que uno. El primer elemento es un número entero que identifica
una regla de producción gramatical. Estos números enteros reciben
nombres simbólicos en el archivo de cabecera "Include/graminit.h" de C
y en el módulo "symbol" de Python. Cada elemento adicional de la
secuencia representa un componente de la producción tal como se
reconoce en la cadena de entrada: siempre son secuencias que tienen la
misma forma que la original. Un aspecto importante de esta estructura
que debe tenerse en cuenta es que las palabras clave utilizadas para
identificar el tipo de nodo principal, como la palabra clave "if" en
una "if_stmt", se incluyen en el árbol de nodos sin ningún tratamiento
especial. Por ejemplo, la palabra clave "if" está representada por la
tupla "(1, 'if')", donde "1" es el valor numérico asociado con todos
los tokens "NAME", incluidos los nombres de variables y funciones
definidos por el usuario. En una forma alternativa, que se retorna
cuando se solicita información sobre el número de línea, el mismo
token podría representarse como "(1, 'if', 12)", donde el "12"
representa el número de línea en el que se encontró el símbolo
terminal.

Los elementos terminales se representan de la misma manera, pero sin
ningún elemento secundario y sin la adición del texto fuente que se
identificó. El anterior ejemplo de la palabra clave "if" es
representativo de esto. Los diversos tipos de símbolos terminales se
definen en el archivo de cabecera "Include/token.h" de C y en el
módulo "token" de Python.

Los objetos ST no son necesarios para soportar la funcionalidad de
este módulo, pero se proporcionan para tres propósitos: para permitir
que una aplicación amortice el coste de procesar árboles de análisis
sintáctico complejos, para proporcionar una representación en forma de
árbol de análisis sintáctico que preserve espacio en memoria, en
comparación con la representación una lista o tupla de Python, y para
facilitar la creación de módulos adicionales en C que manipulen
árboles de análisis sintáctico. Se puede crear una simple clase
"contenedora" en Python para ocultar el uso de objetos ST.

El módulo "parser" define funciones para varios propósitos distintos.
Los más importantes son crear objetos ST y convertir objetos ST en
otras representaciones, como árboles de análisis sintáctico y objetos
de código compilado. También existen funciones que sirven para
consultar el tipo de árbol de análisis sintáctico representado por un
objeto ST.

Ver también:

  Módulo "symbol"
     Constantes útiles que representan los nodos internos del árbol de
     análisis sintáctico.

  Módulo "token"
     Constantes útiles que representan nodos hoja del árbol de
     análisis sintáctico y funciones para probar valores de nodos.


Crear objetos ST
================

Los objetos ST pueden crearse a partir del código fuente o de un árbol
de análisis sintáctico. Al crear un objeto ST a partir del código
fuente, se utilizan diferentes funciones para crear las formas
"'eval'" y "'exec'".

parser.expr(source)

   La función "expr()" analiza el parámetro *source* como si fuera una
   entrada para "compile(source, 'file.py', 'eval')". Si el análisis
   sintáctico tiene éxito, se crea un objeto ST para contener la
   representación del árbol de análisis sintáctico interno; de lo
   contrario, se lanza una excepción apropiada.

parser.suite(source)

   La función "suite()" analiza el parámetro *source* como si fuera
   una entrada válida para "compile(source, 'file.py', 'exec')". Si el
   análisis sintáctico tiene éxito, se crea un objeto ST para contener
   la representación del árbol de análisis sintáctico interno; de lo
   contrario, se lanza una excepción apropiada.

parser.sequence2st(sequence)

   Esta función acepta un árbol de análisis sintáctico representado
   como una secuencia y construye una representación interna si es
   posible. Si puede validar que el árbol se ajusta a la gramática de
   Python y que todos los nodos son tipos de nodo válidos en la
   versión anfitriona de Python, se crea un objeto ST a partir de la
   representación interna y se retorna a quien la invocó. Si hay un
   problema creando la representación interna, o si el árbol no se
   puede validar, se lanza una excepción "ParserError". No se debe dar
   por supuesto que un objeto ST creado de esta manera se compila
   correctamente; las excepciones normalmente generadas en el proceso
   de compilación aún pueden iniciarse cuando el objeto ST se pasa a
   "compilest()". Esto puede indicar problemas no relacionados con la
   sintaxis (como una excepción "MemoryError"), pero también puede
   deberse a construcciones como el resultado de analizar "del f(0)",
   que escapa al analizador de Python pero es verificado por el
   compilador de código de bytes.

   Las secuencias que representan tokens terminales pueden
   representarse como listas de dos elementos de la forma "(1,
   'nombre')" o como listas de tres elementos de la forma "(1,
   'nombre', 56)". Si el tercer elemento está presente, se supone que
   es un número de línea válido. El número de línea puede
   especificarse para cualquier subconjunto de los símbolos terminales
   en el árbol de entrada.

parser.tuple2st(sequence)

   Esta es la misma función que "sequence2st()". Este punto de entrada
   se mantiene solo por compatibilidad con versiones anteriores.


Convertir objetos ST
====================

Los objetos ST, independientemente de la entrada utilizada para
crearlos, pueden convertirse en árboles de análisis sintáctico
representados como árboles de listas o tuplas, o pueden compilarse en
objetos de código ejecutable. Los árboles de análisis sintáctico se
pueden extraer con o sin información de numeración de línea.

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

   Esta función acepta un objeto ST de quien la invoca mediante el
   argumento *st* y retorna una lista de Python que representa el
   árbol de análisis sintáctico equivalente. La representación de la
   lista resultante se puede utilizar para la inspección o la creación
   de un nuevo árbol de análisis sintáctico en forma de lista. Esta
   función no falla mientras haya memoria disponible para construir la
   representación de la lista. Si el árbol de análisis sintáctico sólo
   va a ser usado con fines de inspección, se debe usar "st2tuple()"
   en su lugar para reducir el consumo de memoria y la fragmentación.
   Cuando se requiere la representación en forma de lista, esta
   función es significativamente más rápida que recuperar una
   representación en forma de tupla y convertirla posteriormente en
   listas anidadas.

   Si *line_info* es verdadero, la información del número de línea se
   incluirá para todos los tokens terminales como un tercer elemento
   de la lista que representa el token. Ten en cuenta que el número de
   línea proporcionado especifica la línea en la que el token
   *termina*. Esta información se omite si el indicador es falso o se
   omite.

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

   Esta función acepta un objeto ST de quien la llama mediante el
   argumento *st* y retorna una tupla de Python que representa el
   árbol de análisis sintáctico equivalente. Aparte de retornar una
   tupla en lugar de una lista, esta función es idéntica a
   "st2list()".

   Si *line_info* es verdadero, la información del número de línea se
   incluirá para todos los tokens terminales como un tercer elemento
   de la lista que representa el token. Esta información se omite si
   el indicador es falso o se omite.

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

   El compilador de bytes de Python se puede invocar en un objeto ST
   para producir objetos de código que se pueden usar como parte de
   una llamada a las funciones incorporadas "exec()" o "eval()". Esta
   función proporciona la interfaz para el compilador, pasando el
   árbol de análisis sintáctico interno de *st* al analizador
   sintáctico, utilizando el nombre del archivo fuente especificado
   por el parámetro *filename*. El valor predeterminado proporcionado
   para *filename* indica que la fuente era un objeto ST.

   La compilación de un objeto ST puede resultar en excepciones
   relacionadas con la compilación. Un ejemplo sería la excepción
   "SyntaxError" causada por el árbol de análisis sintáctico para "del
   f(0)": esta declaración se considera legal dentro de la gramática
   formal de Python pero no es una construcción del lenguaje legal. La
   excepción "SyntaxError" lanzada para esta condición es realmente
   generada por el compilador de bytes de Python, por lo que puede ser
   lanzada en este punto por el módulo "parser". La mayoría de las
   causas de errores de compilación se pueden diagnosticar
   programáticamente mediante la inspección del árbol de análisis
   sintáctico.


Consultas en objetos ST
=======================

Se proporcionan dos funciones que permiten a una aplicación determinar
si un ST se creó como una expresión o una suite. Ninguna de estas
funciones se puede utilizar para determinar si un ST se creó a partir
del código fuente a través de "expr()" o "suite()", o desde un árbol
de análisis mediante "sequence2st()".

parser.isexpr(st)

   Esta función retorna "True" cuando *st* representa una forma
   "'eval'" y retorna "False" en caso contrario. Esto es útil, ya que
   los objetos de código normalmente no se pueden consultar para esta
   información utilizando las funciones incorporadas existentes. Ten
   en cuenta que los objetos de código creados por "compilest()"
   tampoco pueden consultarse así, además, son idénticos a los creados
   por la función incorporada "compile()".

parser.issuite(st)

   Esta función es un espejo de "isexpr()", en el sentido de que
   informa si un objeto ST representa una forma "'exec'", comúnmente
   conocida como "suite". No es seguro asumir que esta función es
   equivalente a "not isexpr(st)", ya que es posible que se admitan
   fragmentos sintácticos adicionales en el futuro.


Manejo de errores y excepciones
===============================

El módulo parser define una única excepción, pero también puede lanzar
otras excepciones incorporadas en otras partes del entorno de
ejecución de Python. Consulta cada función para obtener información
sobre las excepciones que puede generar.

exception parser.ParserError

   Excepción lanzada cuando se produce un fallo dentro del módulo
   parser. Esto generalmente se produce ante fallos de validación, en
   lugar de la excepción incorporada "SyntaxError", lanzada durante el
   análisis normal. El argumento de la excepción puede ser una cadena
   de caracteres que describa la razón del error, o también una tupla
   que contenga la secuencia causante del fallo en el árbol de
   análisis pasado a "sequence2st()" y una cadena explicativa. Las
   llamadas a "sequence2st()" deben poder manejar ambos tipos de
   excepciones, mientras que las llamadas a otras funciones en el
   módulo solo necesitarán tener en cuenta los valores de cadena
   simples.

Ten en cuenta que las funciones "compilest()", "expr()" y "suite()"
pueden lanzar excepciones que normalmente son generadas por el proceso
de análisis y compilación. Estas incluyen las excepciones incorporadas
"MemoryError", "OverflowError", "SyntaxError" y "SystemError". En
estos casos, estas excepciones tienen todo el significado que
normalmente se asocia a ellas. Consulta las descripciones de cada
función para obtener información detallada.


Objetos ST
==========

Se admiten comparaciones de orden y de igualdad entre objetos ST.
También se admite la serialización de objetos ST (utilizando el módulo
"pickle").

parser.STType

   El tipo de los objetos retornados por "expr()", "suite()" y
   "sequence2st()".

Los objetos ST tienen los siguientes métodos:

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

   Igual que "compilest(st, filename)".

ST.isexpr()

   Igual que "isexpr(st)".

ST.issuite()

   Igual que "issuite(st)".

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

   Igual que "st2list(st, line_info, col_info)".

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

   Igual que "st2tuple(st, line_info, col_info)".


Ejemplo: Emulación de "compile()"
=================================

Si bien muchas operaciones útiles pueden tener lugar entre el análisis
y la generación del códigos de bytes, la operación más simple es no
hacer nada. Para este propósito, usar el módulo "parser" para producir
una estructura de datos intermedia es equivalente al siguiente código:

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

La operación equivalente usando el módulo "parser" es algo más larga y
permite que el árbol de análisis sintáctico interno intermedio se
conserve como un objeto ST:

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

Una aplicación que necesita tanto ST como objetos de código puede
empaquetar este código en funciones fácilmente disponibles:

   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()
