Arbeiten Sie an PEG im Core Developer Sprint

In diesem Artikel werde ich nicht auf die neuen Funktionen des Parser-Generators eingehen - ich habe es in den vorherigen Teilen ausführlich beschrieben. Stattdessen möchte ich Ihnen erzählen, was ich letzte Woche bei Core Developer Sprint getan habe, bevor alles aus meinem Gedächtnis gelöscht wurde. Obwohl das meiste Material irgendwie mit PEG verwandt ist. Ich muss also Code zeigen, der die Richtung für die Implementierung des PEG-Parsers für Python 3.9 festlegt.



In den letzten vier Jahren hat sich das Python-Kernentwicklungsteam jedes Jahr zu einem wöchentlichen Sprint an einem exotischen Ort versammelt. Diese Sprints werden vom Gastgeber und PSF gesponsert. In den ersten zwei Jahren besuchten wir Facebook bei Mountain View, letztes Jahr hatte Microsoft eine Leitung bei Bellevue und Bloombergs Londoner Büro wurde für diesen Sprint ausgewählt. (Ich muss sagen, dass es ziemlich cool aussieht.) Ehre sei dem Kernentwickler Pablo Galindo Salgado für die Organisation!


Diesmal haben uns mehr als 30 Entwickler sowie zwei Padawans versammelt. Die Leute arbeiteten an verschiedenen Teilen: von 3,8 Blockern bis zu neuen PEPs. Ich hoffe der PSF-Blog über unsere Erfolge. Einer der Hauptpunkte war, dass die Anzahl der offenen PRs weniger als 1000 betrug und mehr als 100 PRs auf ihre Überprüfung warteten. Es gab sogar einen Wettbewerb mit einer Rangliste - den Top 10 Teilnehmern, die eine größere Anzahl von PRs anderer Leute inszenierten.


Für mich ist der Hauptgrund für die Teilnahme an solchen Veranstaltungen immer ein Treffen mit Menschen, mit denen ich das ganze Jahr über online zusammenarbeite, die ich aber nur ein- oder zweimal im Jahr sehe. Dieser Sprint war in Europa, also haben wir eine etwas andere Zusammensetzung gesehen, und das war besonders wichtig. Trotzdem habe ich auch sehr produktiv gearbeitet, worüber ich sprechen werde.


Die meiste Zeit im Sprint habe ich mit Pablo und Emily Morhouse an einem PEG-basierten Parser-Generator gearbeitet, von dem ich hoffe, dass er eines Tages den aktuellen Pgen-basierten Parser-Generator ersetzen wird. Dies ist nicht derselbe Code wie der Generator, über den ich geschrieben habe, aber er ist ziemlich ähnlich. Pablo hat bereits zu dieser Version beigetragen.


Am ersten Tag des Sprints, Montag, habe ich hauptsächlich an den Artikeln 7 und 8 dieses Zyklus gearbeitet. Anfangs wollte ich sie zusammen veröffentlichen, hatte aber am Ende des Tages keine Zeit. Also teilte er es in zwei Teile und veröffentlichte die erste Hälfte , die der Erstellung des Metagramms gewidmet war. Am Freitagnachmittag fand ich endlich Zeit, Teil 8 zu beenden. Allerdings musste ich die Schnittgeschichte immer noch weglassen, weil ich immer noch kein gutes Beispiel hatte.


Am Dienstag begann ich mit der Arbeit an der PEG-Grammatik für Python. Es ist noch näher am Code als an der abstrakten Spezifikation, bevor Aktionen hinzugefügt werden. Wir haben verstanden, dass wir die entwickelte Grammatik auf echtem Python-Code überprüfen müssen. Während ich meine Grammatik beendete, machte Emily Skripte für Batch-Tests. Danach war mein Workflow ungefähr so:


  1. Führen Sie ein Skript aus, um ein Verzeichnis mit Python-Code zu testen
  2. Um das erste Problem zu untersuchen, auf das er fiel
  3. Korrigieren Sie die Grammatik, um dieses Problem zu lösen
  4. Wiederholen, bis keine Probleme mehr auftreten
  5. Zum nächsten Verzeichnis wechseln

Ich habe mit meinem eigenen pgen-Projektcode begonnen. Am Ende konnte meine Grammatik alle in pgen verwendeten Python-Konstrukte analysieren, und ich ging zu den Standardbibliotheksmodulen über. Konzentrieren Sie sich zuerst auf Lib/test , dann auf Lib/asyncio und schließlich auf Lib , Lib/asyncio die gesamte Standardbibliothek (zumindest die in Python geschriebene). Am Ende der Woche konnte ich feiern: Die einzigen Dateien in der Standardbibliothek, auf die der neue Analysator fiel, waren Dateien mit schlechten Codierungen. Sie existieren ausschließlich als Testdaten, um zu überprüfen, ob Codierungsprobleme korrekt behandelt werden. und einige Dateien für Python 2, die als Testfälle für lib2to3 benötigt werden .


Dann habe ich Code hinzugefügt, um die Parser-Laufzeit in Emilys Skript zu berechnen, und es sieht so aus, als ob der neue PEG-Parser etwas schneller ist als der alte pgen-Parser. Dies bedeutet nicht, dass es weiter bergauf geht! Die Grammatik enthält mehr als 100 Regeln (160 Zeilen). Damit AST generiert wird, müssen Sie jeder eine Aktion hinzufügen (siehe Teil 6).


Zuvor habe ich einige Experimente durchgeführt, um festzustellen, wie stark sich die Dateigröße nach dem Hinzufügen von Aktionen erhöht. Ich bin zu dem Schluss gekommen, dass es 2-3 mal größer wird. Hier ist die Grammatik dieses Experiments:


 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 

Hier gibt es Unmengen von Dingen, die ich erklären muss.


  • Die Aktionen sind in C geschrieben. Wie im Python-Generator aus Teil 6 ist jede von ihnen ein Ausdruck.
  • Der Text in eckigen Klammern unmittelbar nach dem Regelnamen bestimmt den Ergebnistyp für die entsprechende Regelmethode. Beispielsweise bedeutet atom[expr_ty] , dass expr_ty für atom . Wenn Sie sich die Include/Python-ast.h im CPython-Repository Include/Python-ast.h , werden Sie Include/Python-ast.h , dass dieser Typ die Struktur ist, die zur Darstellung von Ausdrücken im internen AST verwendet wird.
  • Wenn die Alternative nur ein Element enthält, kann die Aktion weggelassen werden, da das Standardverhalten darin besteht, einfach den resultierenden AST-Knoten zurückzugeben. Andernfalls muss die Aktion explizit angegeben werden.
  • Der generierte C-Code muss ebenfalls verarbeitet werden. Beispielsweise wird davon ausgegangen, dass einige CPython-Headerdateien enthalten sind. Zum Beispiel, wenn der Typ expr_ty , gut und viele andere notwendige Dinge.
  • Die Variable p enthält einen Zeiger auf die vom generierten Analysator verwendete Parser Struktur. (Und ja, dies bedeutet, dass es besser ist, kein einzelnes Element in der p Regel anzugeben - andernfalls wird der generierte C-Code nicht kompiliert!)
  • EXTRA(node1, node2) ist ein Makro, das sich zu einer Reihe zusätzlicher Argumente erweitert, die an jede AST-Konstruktionsfunktion übergeben werden müssen. Dies spart viel Zeit beim Schreiben einer Aktion. Andernfalls müssten Sie die Start- und Endzeilennummer, den Spaltenversatz sowie einen Zeiger auf die für die Verteilung verwendete Arena angeben. (AST-Knoten sind keine Python-Objekte und verwenden ein effizienteres Layout.)
  • Aufgrund eines interessanten Verhaltens des C-Präprozessors in EXTRA() können wir keine Makros zum Erstellen eines AST-Knotens verwenden, sondern müssen grundlegende Funktionen verwenden. Aus diesem Grund sehen Sie beispielsweise _Py_Binop(...) und nicht Binop(...) . In Zukunft werde ich darüber nachdenken, wie ich es anders lösen kann.
  • Für sich wiederholende Elemente ( foo* oder foo+ ) erstellt der Codegenerator eine Hilfsregel vom Typ asdl_seq* . Dies ist die Datenstruktur, mit der AST Wiederholungen darstellt. An mehreren Stellen müssen wir eine solche Wiederholung nur aus einem Element erstellen, und dafür haben wir die Hilfsfunktion singleton_seq() .

Vielleicht klingt etwas davon seltsam, und ich werde nicht streiten. Dies ist ein Prototyp, dessen Hauptzweck darin besteht, zu demonstrieren, dass es im Prinzip möglich ist, einen funktionierenden AST unter Verwendung eines aus einer PEG-Grammatik generierten Parsers zu generieren. All dies funktioniert ohne Änderungen am vorhandenen Tokenizer oder Bytecode-Compiler. Ein Prototyp kann einfache Ausdrücke und if kompilieren, und der resultierende AST kann in Bytecode kompiliert und ausgeführt werden.


Andere Dinge, die ich im Rahmen dieses Sprints getan habe:


  • Ich überzeugte Lukasz Lang, PEP 585 (seinen Vorschlag für den zukünftigen Typ-Hinweis) so zu ändern, dass er sich eher auf Generika als auf eine Reihe von Ideen konzentriert, wie es zuvor war. Das neue PEP sieht viel besser aus, und bei dem Tipptreffen vor einigen Tagen, bei dem Vertreter von Entwicklern von Python-Dienstprogrammen zur Typprüfung (mypy, pytype und Pyre) anwesend waren, erhielt er die allgemeine Genehmigung. (Dies ist nicht dasselbe wie die Billigung durch den EZB-Rat!)
  • Hilf Yuri Selivanov bei der Entwicklung einer API für den Frozenmap- Typ, die er zu stdlib hinzufügen wollte. Mehrere andere Mitwirkende haben ebenfalls zum Design beigetragen - ich denke, wir haben den Sprint mit ein paar Boards voller Beispiele und API-Fragmente beendet. Das Ergebnis ist PEP 603 und wird derzeit aktiv diskutiert . (Ein Hinweis: Eine Implementierung des vorgeschlagenen Datentyps ist bereits in CPython als Teil der Implementierung von PEP 567 , dem contextvars Modul, vorhanden. Dies ist eine sehr interessante Datenstruktur, der Hash Array Mapped Trie , der eine Hash-Tabelle mit einem contextvars kombiniert.)
  • Yuri ist wie immer voller Ideen. Er arbeitete auch an Ausnahmegruppen (eine Idee von Trio ), die er in Python 3.9 in asyncio implementieren wollte. PEP schien beim letzten Mal nicht da zu sein, aber ich erinnere mich definitiv an eine Tafel voller Diagramme.
  • Wir haben Lucas 'Vorschlag für einen kürzeren Python-Release-Zyklus aktiv diskutiert. Dies führte zu PEP 602 , in dem er vorschlägt, sie jährlich im Oktober herzustellen. (Dafür gibt es einen guten Grund: Dies liegt am typischen Zeitplan für Python-Konferenzen und Kernsprints.) Dies wird immer noch sehr viel diskutiert . Es gibt mindestens zwei Gegenangebote: In PEP 598 bietet Nick Coglan zweijährige Veröffentlichungen an, während neue Funktionen in Patches berücksichtigt werden. Steve Dower würde gerne auch alle zwei Jahre Veröffentlichungen sehen, aber ohne diese Funktion (es gab noch kein PEP).
  • Drei Mitglieder des EZB-Rates, die am Sprint teilnahmen (Brett Cannon, Carol Willing und ich), kamen zusammen und diskutierten unsere Vision für die zukünftige Entwicklung des Python-Kerns. (Ich möchte nicht viel darüber sprechen, da wir planen, auf der nächsten PyCon in den USA darüber zu sprechen. Wir werden jedoch wahrscheinlich vorschlagen, mit dem Sammeln von Spenden zu beginnen, damit PSF mehrere Entwickler einstellen kann, um die Kernelentwicklung zu unterstützen und zu beschleunigen.)
  • Ich hatte ein interessantes Gespräch zum Mittagessen mit Joanna Nanjeki - einer von denen, die an der Zeremonie teilnahmen. Sie erzählte die Geschichte, wie sie im Alter von 8 Jahren vom Internet erfuhr und ihren jüngeren Bruder in ein Internetcafé brachte, während ihre Mutter arbeitete. Dort entdeckte sie Google und E-Mail und hing dort die erste Woche.
  • Das wichtigste alkoholische Ereignis der Woche waren ein paar Zombies der Zombie-Apokalypse, die einige von uns an der Alchemist-Bar bestellten. Jedes Getränk wird in einem 2-Liter-Erlenmeyerkolben mit einer großen Menge Kunstrauch serviert, der beim Eingießen von trockenem Alkohol auf eine normale Alkoholmischung entsteht. Es ist für vier Personen ausgelegt.
  • Am Freitagabend brachte uns Lisa Roach in ein gutes indisches Restaurant in der Nähe ihres Hotels. Es war durch vier U-Bahn-Stationen, was ein echtes Abenteuer war (es war Hauptverkehrszeit und wir haben Christian Hymes mehrmals mehrmals fast verloren). Das Essen hat sich gelohnt!
  • Irgendwann machten wir ein Gruppenfoto. Es sieht ziemlich futuristisch aus, aber es ist wirklich eine Londoner Landschaft.


Im nächsten Artikel möchte ich einige Fortschritte mit Aktionen zum Erstellen von AST-Knoten teilen.


Lizenz für diesen Artikel und zitierten Code: CC BY-NC-SA 4.0

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


All Articles