Trabajar en PEG en Core Developer Sprint

En este artículo no hablaré sobre las nuevas características del generador de analizadores: lo he descrito lo suficiente en las partes anteriores. En cambio, quiero contarte lo que hice en Core Developer Sprint la semana pasada antes de que todo se borre de mi memoria. Aunque la mayor parte del material está relacionado de alguna manera con el PEG. Así que tengo que mostrar un código que establece la dirección en la implementación del analizador PEG para Python 3.9.



Cada año durante los últimos cuatro años, el equipo de desarrollo central de Python se ha reunido para un sprint semanal en un lugar exótico. Estos sprints son patrocinados por el anfitrión y PSF. Durante los primeros dos años, visitamos Facebook en Mountain View, el año pasado Microsoft tuvo una línea en Bellevue, y Bloomberg en Londres fue elegido para este sprint. (Tengo que decir que se ve muy bien.) ¡Gloria al desarrollador principal Pablo Galindo Salgado por organizarse!


Esta vez, más de 30 desarrolladores, así como dos Padawans, nos reunieron. La gente trabajó en varias partes: desde 3.8 bloqueadores hasta nuevas PEP. Espero que el blog de PSF sobre nuestros logros. Uno de los puntos principales fue que el número de RP abiertos era inferior a 1000, más de 100 RP estaban esperando su revisión. Incluso hubo una competencia con una tabla de clasificación: los 10 principales participantes que organizaron un mayor número de relaciones públicas de otras personas.


Para mí, siempre la razón principal para asistir a tales eventos es una reunión con personas con las que colaboro en línea durante todo el año, pero a las que veo solo una o dos veces al año. Este sprint fue en Europa, por lo que vimos una composición ligeramente diferente, y esto fue especialmente importante. A pesar de esto, también trabajé bastante productivamente, de lo que hablaré.


La mayor parte de mi tiempo en el sprint, trabajé con Pablo y Emily Morhouse en un generador de analizador basado en PEG, que espero que algún día reemplace el generador de analizador actual basado en pgen. Este no es el mismo código que el generador sobre el que escribí, pero es bastante similar. Pablo ya ha contribuido a esta versión.


El primer día del sprint, el lunes, trabajé principalmente en los artículos 7 y 8 de este ciclo. Inicialmente, planeaba publicarlos juntos, pero no tuve tiempo al final del día. Entonces lo dividió en dos partes y publicó la primera mitad dedicada a la creación del metagrama. El viernes por la tarde, finalmente encontré algo de tiempo para terminar la Parte 8. Sin embargo, aún tuve que omitir la historia cortada porque todavía no tenía un buen ejemplo.


El martes, comencé a trabajar en la gramática PEG para Python. Está aún más cerca del código que de la especificación abstracta antes de agregar acciones. Entendimos que necesitábamos verificar la gramática desarrollada en código Python real. Entonces, mientras terminaba mi gramática, Emily estaba haciendo guiones para pruebas por lotes. Después de eso, mi flujo de trabajo fue algo como esto:


  1. Ejecute un script para probar algún directorio con código Python
  2. Para investigar el primer problema en el que cayó
  3. Corregir la gramática para resolver este problema.
  4. Repita hasta que no haya problemas
  5. Ir al siguiente directorio

Comencé con mi propio código de proyecto pgen. Al final, mi gramática pudo analizar todas las construcciones de Python utilizadas en pgen, y pasé a los módulos de biblioteca estándar. Primero, enfocándose en Lib/test , luego Lib/asyncio y finalmente Lib , que es, de hecho, toda la biblioteca estándar (al menos la escrita en Python). Al final de la semana pude celebrar: los únicos archivos en la biblioteca estándar en los que cayó el nuevo analizador eran archivos con codificaciones incorrectas. Existen únicamente como datos de prueba para verificar que los problemas de codificación se manejarán de la manera correcta; y algunos archivos para Python 2 que son necesarios como casos de prueba para lib2to3 .


Luego agregué un código para calcular el tiempo de ejecución del analizador en el script de Emily, y parece que el nuevo analizador PEG es un poco más rápido que el antiguo analizador pgen. ¡Esto no significa que las cosas irán más arriba! Hay más de 100 reglas en la gramática (160 líneas), y para que genere AST, necesitaremos agregar una acción a cada una (ver Parte 6).


Anteriormente, realicé un experimento para ver cuánto aumentará el tamaño del archivo después de agregar acciones. Llegué a la conclusión de que se volverá 2-3 veces más grande. Aquí está la gramática de este experimento:


 start[mod_ty]: a=stmt* ENDMARKER{ Module(a, NULL, p->arena) } stmt[stmt_ty]: compound_stmt | simple_stmt compound_stmt[stmt_ty]: pass_stmt | if_stmt pass_stmt[stmt_ty]: a='pass' NEWLINE { _Py_Pass(EXTRA(a, a)) } if_stmt[stmt_ty]: | 'if' c=expr ':' t=suite e=[else_clause] { _Py_If(c, t, e, EXTRA(c, c)) } else_clause[asdl_seq*]: | 'elif' c=expr ':' t=suite e=[else_clause] { singleton_seq(p, _Py_If(c, t, e, EXTRA(c, c))) } | 'else' ':' s=suite { s } suite[asdl_seq*]: | a=simple_stmt { singleton_seq(p, a) } | NEWLINE INDENT b=stmt+ DEDENT { b } simple_stmt[stmt_ty]: a=expr_stmt NEWLINE { a } expr_stmt[stmt_ty]: a=expr { _Py_Expr(a, EXTRA(a, a)) } expr[expr_ty]: | l=expr '+' r=term { _Py_BinOp(l, Add, r, EXTRA(l, r)) } | l=expr '-' r=term { _Py_BinOp(l, Sub, r, EXTRA(l, r)) } | term term[expr_ty]: | l=term '*' r=factor { _Py_BinOp(l, Mult, r, EXTRA(l, r)) } | l=term '/' r=factor { _Py_BinOp(l, Div, r, EXTRA(l, r)) } | factor factor[expr_ty]: | l=primary '**' r=factor { _Py_BinOp(l, Pow, r, EXTRA(l, r)) } | primary primary[expr_ty]: | f=primary '(' e=expr ')' { _Py_Call(f, singleton_seq(p, e), NULL, EXTRA(f, e)) } | atom atom[expr_ty]: | '(' e=expr ')' { e } | NAME | NUMBER | STRING 

Hay toneladas de cosas aquí que tengo que explicar.


  • Las acciones están escritas en C. Al igual que en el generador Python de la parte 6, cada una de ellas es una expresión.
  • El texto entre corchetes inmediatamente después del nombre de la regla determina el tipo de resultado para el método de la regla correspondiente. Por ejemplo, atom[expr_ty] significa que expr_ty se devolverá para atom . Si observa el Include/Python-ast.h en el repositorio de CPython, verá que este tipo es la estructura utilizada para representar expresiones en el AST interno.
  • Si la alternativa tiene solo un elemento, la acción se puede omitir, ya que el comportamiento predeterminado es simplemente devolver el nodo AST resultante. De lo contrario, la acción debe especificarse explícitamente.
  • El código C generado también necesita algo de procesamiento. Por ejemplo, se supone que se incluirán algunos archivos de encabezado CPython. Por ejemplo, donde expr_ty el tipo expr_ty , bueno, y muchas otras cosas necesarias.
  • La variable p contiene un puntero a la estructura del Parser utilizada por el analizador generado. (Y sí, esto significa que es mejor no especificar un solo elemento en la regla p ; de lo contrario, el código C generado no se compilará).
  • EXTRA(node1, node2) es una macro que se expande en un conjunto de argumentos adicionales que deben pasarse a cada función de construcción AST. Esto ahorra mucho tiempo al escribir una acción; de lo contrario, tendría que especificar el número de línea inicial y final, el desplazamiento de columna y también un puntero al campo utilizado para la distribución. (Los nodos AST no son objetos de Python y usan un diseño más eficiente).
  • Debido a un comportamiento interesante del preprocesador C en EXTRA() , no podemos usar macros para crear un nodo AST, pero debemos usar funciones básicas. Es por eso que ves, por ejemplo, _Py_Binop(...) , no Binop(...) . En el futuro pensaré en cómo resolverlo de manera diferente.
  • Para elementos repetidos ( foo* o foo+ ), el generador de código crea una regla auxiliar del tipo asdl_seq* . Esta es la estructura de datos que AST usa para representar repeticiones. En varios lugares, necesitamos crear tal repetición desde un solo elemento, y para esto hemos definido la función auxiliar singleton_seq() .

Quizás algo de esto suene extraño, y no discutiré. Este es un prototipo, y su objetivo principal es demostrar que, en principio, es posible generar un AST que funcione utilizando un analizador generado a partir de una gramática PEG. Todo esto funciona sin ningún cambio en el compilador de tokenizer o bytecode existente. Un prototipo puede compilar expresiones simples y if , y el AST resultante puede compilarse en bytecode y ejecutarse.


Otras cosas que hice como parte de este sprint:


  • Convencí a Lukasz Lang de cambiar la PEP 585 (su propuesta para el futuro tipo de insinuación) para centrarse en los genéricos, en lugar de en un conjunto de ideas, como era antes. El nuevo PEP se ve mucho mejor, y en la reunión de mecanografía hace varios días, donde representantes de desarrolladores de utilidades de comprobación de tipo Python (mypy, pytype y Pyre) estuvieron presentes, recibió la aprobación general. (¡Esto no es lo mismo que el respaldo del Consejo de Gobierno!)
  • Ayudó a Yuri Selivanov a desarrollar una API para el tipo de mapa congelado , que quería agregar a stdlib. Varios otros contribuyentes también contribuyeron al diseño: creo que terminamos el sprint con algunas tablas llenas de ejemplos y fragmentos de API. El resultado es PEP 603 , y actualmente se encuentra en discusión activa . (Una nota: ya existe una implementación del tipo de datos propuesto en CPython, como parte de la implementación de PEP 567 , el módulo contextvars . Esta es una estructura de datos muy interesante, el Hash Array Mapped Trie , que combina una tabla hash con un árbol de prefijos)
  • Yuri, como siempre, está lleno de ideas. También trabajó en grupos de excepción (una idea de Trio ) que quería implementar en asyncio en Python 3.9. PEP no parecía estar allí la última vez que miré, pero definitivamente recuerdo un tablero lleno de diagramas.
  • Hemos estado discutiendo activamente la propuesta de Lucas para un ciclo de lanzamiento de Python más corto. Esto dio como resultado PEP 602 , en el que sugiere hacerlos anualmente, en octubre. (Hay una buena razón para esto: esto se debe a la programación típica de las conferencias de Python y los sprints centrales). Esto todavía se discute mucho . Hay al menos dos contraofertas: en PEP 598, Nick Coglan ofrece lanzamientos de dos años, al tiempo que permite nuevas características en parches; A Steve Dower también le gustaría ver lanzamientos bienales, pero sin esta característica (todavía no había PEP).
  • Tres miembros del Consejo de Gobierno que asistieron al sprint (Brett Cannon, Carol Willing y yo) nos reunimos y discutimos nuestra visión para el desarrollo futuro del núcleo de Python. (No quiero hablar mucho de esto, porque planeamos hablar de eso en la próxima PyCon en los Estados Unidos. Sin embargo, probablemente sugeriremos comenzar a recaudar fondos para que PSF pueda contratar a varios desarrolladores para apoyar y acelerar el desarrollo del kernel).
  • Tuve una interesante conversación durante el almuerzo con Joanna Nanjeki, una de las personas que asistieron a la ceremonia. Ella contó la historia de cómo se enteró de Internet cuando tenía 8 años y llevó a su hermano menor a un cibercafé mientras su madre trabajaba. Allí descubrió Google y el correo electrónico y estuvo allí la primera semana.
  • El principal evento alcohólico de la semana fue un par de cócteles Zombie Apocalypse, que algunos de nosotros pedimos en el bar Alchemist. Servido en un matraz Erlenmeyer de 2 litros con una gran cantidad de humo falso como resultado de verter alcohol seco en una mezcla de alcohol regular, cada bebida está diseñada para cuatro personas.
  • El viernes por la noche, Lisa Roach nos llevó a un buen restaurante indio cerca de su hotel. Fue a través de cuatro estaciones de metro, lo cual fue una verdadera aventura (era hora pico, y casi perdimos a Christian Hymes varias veces). La comida valió la pena!
  • En algún momento, tomamos una foto grupal. Parece bastante futurista, pero en realidad es un paisaje de Londres.


En el próximo artículo, espero compartir algunos progresos con acciones para crear nodos AST.


Licencia para este artículo y código citado: CC BY-NC-SA 4.0

Source: https://habr.com/ru/post/471994/


All Articles