Travail sur PEG sur Core Developer Sprint

Dans cet article, je ne parlerai pas des nouvelles fonctionnalités du générateur d'analyseur - je l'ai suffisamment décrit dans les parties précédentes. Au lieu de cela, je veux vous dire ce que j'ai fait chez Core Developer Sprint la semaine dernière avant que tout ne soit effacé de ma mémoire. Bien que la plupart du matériel soit en quelque sorte lié au PEG. Je dois donc montrer du code qui définit la direction dans la mise en œuvre de l'analyseur PEG pour Python 3.9.



Chaque année au cours des quatre dernières années, l'équipe de développement principale de Python s'est réunie pour un sprint hebdomadaire dans un endroit exotique. Ces sprints sont parrainés par l'hôte et la PSF. Pendant les deux premières années, nous avons visité Facebook à Mountain View, l'année dernière, Microsoft avait une ligne à Bellevue, et Bloomberg à Londres a été choisi pour ce sprint. (Je dois dire que ça a l' air plutôt cool.) Gloire au développeur principal Pablo Galindo Salgado pour l'organisation!


Cette fois, plus de 30 développeurs, ainsi que deux Padawans, nous ont réunis. Les gens ont travaillé sur différentes parties: de 3,8 bloqueurs aux nouveaux PEP. J'espère que le blog PSF sur nos réalisations. L'un des principaux points était que le nombre de RP ouverts était inférieur à 1 000, plus de 100 RP attendaient leur examen. Il y a même eu une compétition avec un classement - les 10 premiers participants qui ont organisé un plus grand nombre de RP d'autres personnes.


Pour moi, la raison principale pour assister à de tels événements est toujours une rencontre avec des personnes avec qui je collabore en ligne tout au long de l'année, mais que je ne vois qu'une ou deux fois par an. Ce sprint était en Europe, donc nous avons vu une composition légèrement différente, et c'était particulièrement important. Malgré cela, j'ai également travaillé de manière assez productive, dont je vais parler.


La plupart de mon temps dans le sprint, j'ai travaillé avec Pablo et Emily Morhouse sur un générateur d'analyseur basé sur PEG, qui j'espère remplacera un jour l'actuel générateur d'analyseur basé sur pgen. Ce n'est pas le même code que le générateur sur lequel j'ai écrit, mais il est assez similaire. Pablo a déjà contribué à cette version.


Le premier jour du sprint, lundi, j'ai travaillé principalement sur les articles 7 et 8 de ce cycle. Au départ, j'avais prévu de les publier ensemble, mais je n'avais pas le temps à la fin de la journée. Il l'a donc divisé en deux parties et a publié la première moitié consacrée à la création du métagramme. Vendredi après-midi, j'ai finalement trouvé un peu de temps pour terminer la partie 8. Cependant, j'ai quand même dû omettre l'histoire coupée parce que je n'avais toujours pas un bon exemple.


Mardi, j'ai commencé à travailler sur la grammaire PEG pour Python. Il est encore plus proche du code que de la spécification abstraite avant d'ajouter des actions. Nous avons compris que nous devions vérifier la grammaire développée sur du vrai code Python. Alors pendant que je finissais ma grammaire, Emily faisait des scripts pour les tests par lots. Après cela, mon flux de travail était quelque chose comme ceci:


  1. Exécutez un script pour tester un répertoire avec du code Python
  2. Pour enquêter sur le premier problème sur lequel il est tombé
  3. Corriger la grammaire pour résoudre ce problème
  4. Répétez jusqu'à ce qu'il n'y ait aucun problème
  5. Accéder au répertoire suivant

J'ai commencé avec mon propre code de projet pgen. Au final, ma grammaire a pu analyser toutes les constructions Python utilisées dans pgen, et je suis passé aux modules de bibliothèque standard. D'abord, en se concentrant sur Lib/test , puis sur Lib/asyncio et enfin sur Lib , c'est-à-dire en fait la bibliothèque standard entière (au moins celle écrite en Python). À la fin de la semaine, j'ai pu célébrer: les seuls fichiers de la bibliothèque standard sur lesquels le nouvel analyseur était tombé étaient des fichiers avec de mauvais encodages. Ils existent uniquement en tant que données de test pour vérifier que les problèmes d'encodage seront traités correctement; et certains fichiers pour Python 2 qui sont nécessaires comme cas de test pour lib2to3 .


Ensuite, j'ai ajouté du code pour calculer le temps d'exécution de l'analyseur dans le script d'Emily, et il semble que le nouvel analyseur PEG soit un peu plus rapide que l'ancien analyseur pgen. Cela ne signifie pas que les choses iront plus loin! Il y a plus de 100 règles dans la grammaire (160 lignes), et pour la faire générer AST, nous devrons ajouter une action à chacune (voir Partie 6).


Plus tôt, j'ai effectué une expérience pour voir la taille du fichier augmenter après l'ajout d'actions. J'en suis venu à la conclusion qu'elle deviendrait 2-3 fois plus grande. Voici la grammaire de cette expérience:


 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 

Il y a des tonnes de choses ici que je dois expliquer.


  • Les actions sont écrites en C. Comme dans le générateur Python de la partie 6, chacune d'entre elles est une expression.
  • Le texte entre crochets immédiatement après le nom de la règle détermine le type de résultat pour la méthode de règle correspondante. Par exemple, atom[expr_ty] signifie que expr_ty sera retourné pour atom . Si vous regardez le Include/Python-ast.h dans le référentiel CPython, vous verrez que ce type est la structure utilisée pour représenter les expressions dans l'AST interne.
  • Si l'alternative n'a qu'un seul élément, l'action peut être omise, car le comportement par défaut pour elle consiste simplement à renvoyer le nœud AST résultant. Sinon, l'action doit être spécifiée explicitement.
  • Le code C généré nécessite également un traitement. Par exemple, il est supposé que certains fichiers d'en-tête CPython seront inclus. Par exemple, où le type expr_ty , eh bien, et bien d'autres choses nécessaires.
  • La variable p contient un pointeur sur la structure Parser utilisée par l'analyseur généré. (Et oui, cela signifie qu'il vaut mieux ne pas spécifier un seul élément dans la règle p - sinon le code C généré ne se compilera pas!)
  • EXTRA(node1, node2) est une macro qui se développe en un ensemble d'arguments supplémentaires qui doivent être passés à chaque fonction de construction AST. Cela permet d'économiser beaucoup de temps lors de l'écriture d'une action - sinon, vous devrez spécifier le numéro de ligne de début et de fin, le décalage de colonne, ainsi qu'un pointeur vers l' arène utilisée pour la distribution. (Les nœuds AST ne sont pas des objets Python et utilisent une disposition plus efficace.)
  • En raison d'un comportement intéressant du préprocesseur C dans EXTRA() , nous ne pouvons pas utiliser de macros pour créer un nœud AST, mais nous devons utiliser des fonctions de base. C'est pourquoi vous voyez, par exemple, _Py_Binop(...) , pas Binop(...) . À l'avenir, je réfléchirai à la façon de le résoudre différemment.
  • Pour les éléments répétitifs ( foo* ou foo+ ), le générateur de code crée une règle auxiliaire de type asdl_seq* . Il s'agit de la structure de données qu'AST utilise pour représenter les répétitions. À plusieurs endroits, nous devons créer une telle répétition à partir d'un seul élément, et pour cela, nous avons défini la fonction auxiliaire singleton_seq() .

Peut-être que cela semble étrange, et je ne contesterai pas. Il s'agit d'un prototype et son objectif principal est de démontrer qu'il est en principe possible de générer un AST fonctionnel à l'aide d'un analyseur généré à partir d'une grammaire PEG. Tout cela fonctionne sans aucune modification du tokenizer ou du compilateur de bytecode existant. Un prototype peut compiler des expressions simples et des if , et l'AST résultant peut être compilé en bytecode et exécuté.


D'autres choses que j'ai faites dans le cadre de ce sprint:


  • Persuadé Lukasz Lang de changer PEP 585 (sa proposition pour le futur indice) pour se concentrer sur les génériques, plutôt que sur un ensemble d'idées, comme c'était le cas auparavant. Le nouveau PEP a l'air beaucoup mieux, et lors de la réunion de dactylographie il y a quelques jours, où des représentants des développeurs des utilitaires de vérification de type Python (mypy, pytype et Pyre) étaient présents, il a reçu l'approbation générale. (Ce n'est pas la même chose que l'approbation par le Conseil des gouverneurs!)
  • A aidé Yuri Selivanov à développer une API pour le type de carte figée , qu'il voulait ajouter à stdlib. Plusieurs autres contributeurs ont également contribué à la conception - je pense que nous avons terminé le sprint avec quelques tableaux remplis d'exemples et de fragments d'API. Le résultat est le PEP 603 , et est actuellement en discussion active . (Une remarque: une implémentation du type de données proposé existe déjà dans CPython, dans le cadre de l'implémentation de PEP 567 , le module contextvars . Il s'agit d'une structure de données très intéressante, le Hash Array Mapped Trie , qui combine une table de hachage avec une arborescence de préfixes)
  • Yuri, comme toujours, est plein d'idées. Il a également travaillé sur des groupes d'exceptions (une idée de Trio ) qu'il voulait implémenter dans asyncio en Python 3.9. PEP ne semblait pas être là la dernière fois que j'ai regardé, mais je me souviens certainement d'une planche pleine de diagrammes.
  • Nous avons activement discuté de la proposition de Lucas pour un cycle de publication Python plus court. Cela a abouti au PEP 602 , dans lequel il suggère de les faire chaque année, en octobre. (Il y a une bonne raison à cela: cela est dû au calendrier typique des conférences Python et des sprints principaux.) Ceci est encore très discuté . Il existe au moins deux contre-offres: dans PEP 598, Nick Coglan propose des versions de deux ans, tout en permettant de nouvelles fonctionnalités dans les correctifs; Steve Dower aimerait également voir des versions biennales, mais sans cette fonctionnalité (il n'y avait pas encore de PEP).
  • Trois membres du conseil des gouverneurs qui ont assisté au sprint (Brett Cannon, Carol Willing et moi) se sont réunis et ont discuté de notre vision pour le développement futur du noyau Python. (Je ne veux pas en parler beaucoup, car nous prévoyons d'en parler lors du prochain PyCon aux États-Unis. Cependant, nous proposerons probablement de commencer à collecter des fonds afin que PSF puisse embaucher plusieurs développeurs pour soutenir et accélérer le développement du noyau).
  • J'ai eu une conversation intéressante avec Joanna Nanjeki - l'une de celles qui ont assisté à la cérémonie. Elle a raconté comment elle a découvert Internet à l'âge de 8 ans et a emmené son jeune frère dans un cybercafé pendant que leur mère travaillait. Là, elle a découvert Google et le courrier électronique et y a accroché la première semaine.
  • Le principal événement alcoolique de la semaine a été quelques cocktails Zombie Apocalypse, que certains d'entre nous ont commandés au bar Alchemist. Servie dans une fiole Erlenmeyer de 2 litres avec une grande quantité de fausse fumée résultant du versement d'alcool sec sur un mélange d'alcool régulier, chaque boisson est conçue pour quatre personnes.
  • Vendredi soir, Lisa Roach nous a amenés dans un bon restaurant indien près de son hôtel. C'était à travers quatre stations de métro, ce qui était une vraie aventure (c'était l'heure de pointe, et nous avons presque perdu Christian Hymes plusieurs fois). La nourriture en valait la peine!
  • À un moment donné, nous avons pris une photo de groupe. Cela a l'air assez futuriste, mais c'est vraiment un paysage de Londres.


Dans le prochain article, j'espère partager certains progrès avec les actions de création de nœuds AST.


Licence pour cet article et code cité: CC BY-NC-SA 4.0

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


All Articles