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.

Advertencia

The parser module is deprecated and will be removed in future versions of Python. For the majority of use cases you can leverage the Abstract Syntax Tree (AST) generation and compilation stage, using the ast module.

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