Parser Combinators

Eher durch Zufall bin ich letztens auf einen auch sehr interessanten Ansatz für einen Parsing Algorithmus gekommen, der mal so gar nichts mit dem was ich bisher so gemacht habe zu tun hat: Parser Combinators.

Die Idee dahinter: Man definiert Anstatt einer Grammatik mehrere, ineinander verschachtelte Mini-Parser, wobei ein Parser entweder ein Zeichen absorbiert (LIT, CCL) oder aber mehrere Parser entsprechend so Kombiniert, das man damit Alternativen (OR), Sequenzen (AND, SEQ) und semantische Operationen (JOIN) umsetzen kann. Der Aufruf der Funktionen definiert dann also die syntaktikalische Struktur, und es werden entsprechende Ergebnisse zurückgeliefert.

Sehr schön ist das ganze z.B. hier von Daniel Holden erklärt. Auf Basis dieses Blog-Eintrags habe ich mal einen Ansatz für eine Parser Combinator Library in Python in unter 100 Zeilen programmiert.

Es ist verblüffend: Man erhält bereits aus der Eingabe 1337+42*23+5 die Ausgabe
[‘1337’, ‘+’, [[’42’, ‘*’, ’23’], ‘+’, ‘5’]], was ja quasi schon einem Syntaxbaum entspricht.

Daniel hat auch die C-Library mpc (Micro Parser Combinators) geschrieben, welche das ganze für C umsetzt. Und da wären wir schon wieder beim nächsten Problem: Die Sache ist eigentlich so elegant, das ich mir wieder die Frage stelle, ob die ganze Arbeit die ich bisher in die Phorward Library gesteckt habe es überhaupt noch Wert ist… oder ob man vielleicht sich einfach aus Spaß lieber damit beschäftigen sollte. Ernstaft nutzen würde es sowieso keiner, das musste ich ja schon inzwischen mehrfach schmerzlich feststellen. Aber naja, vielleicht muss man sich dabei einfach mal von dem Gedanken trennen, mit sowas erfolgreich zu sein… oder was heißt erfolgreich sein, es hat Spaß gemacht das zu programmieren, aber wenn es keiner nutzt und es eigentlich gar keinen Nutzen hat ausser das es einfach da ist und nicht genutzt wird, das ist irgendwie extrem demotivierend und ärgerlich.

Es gibt natürlich auch Paper dazu, wie man Parser Combinators sogar generalisiert hinbekommt. Also sozusagen das, was ich bereits mit GLR parsing mal im Ansatz versucht hatte zu verstehen (und kläglich gescheitert bin), nur eben genau anders herum (GLL).

Vielversprechende Papers dazu:

Denn wie auch Fefe in seinem Blog letztens schrieb, und was auch auf mich persönlich sehr stark zutrifft, und ich mit libphorward, UniCC, JS/CC, pynetree und min_lalr1 inzwischen eigentlich bis zur Gänze ausgereizt haben sollte:

Ich habe meine libc auch nicht geschrieben, damit ich eine libc habe (obwohl das ein schöner Seiteneffekt ist). Ich habe die geschrieben, damit ich das Problemumfeld verstanden habe. Und das habe ich jetzt. Genau so mit dem Webserver und dem LDAP-Server. Du merkst, ob du etwas verstanden hast, wenn du es jemand anderem erklären konntest. Und Programmieren ist ja am Ende wie jemand anderem was erklären, nur dass derjenige sehr maschinenlesbare Erklärungen braucht. 🙂

Ich treffe immer wieder Leute, die denken, man programmiert Open Source-Software, um Teil einer Community zu sein. Oder um am Ende die Software zu haben. Nein. Du programmierst, damit du am Ende das Problem verstanden hast.

Ich habe in meiner Karriere ein-zwei Mal Quellcode bei Plattenausfällen oder versehentlichem Löschen verloren. Das fühlt sich an wie eine Katastrophe, ist es aber nicht. Wenn du für die erste Version ein Jahr brauchtest, geht das nochmal hinschreiben in 1-2 Monaten. Denn diesmal hast du ja das Problemfeld verstanden. DAS ist die eigentliche Währung im Leben. Problemfelder verstanden haben.

Na denn… vielleicht auf zu neuen Ufern, und den Kram nun mal hinter sich lassen?

libphorward 0.22

Langsam, aber stetig tut sich etwas bei meiner hobbymäßig weiterentwickelten C library libphorward, die auch als Phorward Toolkit, Phorward Foundation Libraries oder inzwischen auch einfach nur noch als The Phorward library bezeichnet wird.

Irgendwie scheine ich ein wenig mehr Disziplin dort hinein bekommen zu haben. Das liegt zum Teil daran, dass sich etwas bei UniCC getan hat, aber auch weil die Library inzwischen eine feste Basis hat, die nur noch geringfügig geändert und erweitert, aber nicht mehr komplett verworfen oder bereinigt wird. Leider ist immernoch nicht ganz genau klar, was der exakte Zweck der Library oder des Toolkits nun wirklich sein soll. In der Dokumentation dazu steht inzwischen folgender Abschnitt unter dem Titel “Intention behind this library”:

The cornerstone for this library was laid in 2006 during some experimenting with several algorithms and their implementation. It rapidly turned out to become a general purpose library serving as the base toolchain for several, mostly unfinished software projects which had their origin at J.M.K S.F. Software Technologies, which was later renamed to Phorward Software Technologies.

The library was then released to the public in 2011, together with the open source parser generator UniCC. Since then, it was gradually extended to newer and more enhanced features, mostly on tools relating to parser and compiler construction, but also features already known from other projects and remastered in a more generic and powerful way.

The final destination of the Phorward library is not entirely clear yet. For now, it mostly serves as a kind of playground for different projections, but it is also used by the UniCC parser generator and the meanwhile discontinued RapidBATCH scripting language as its foundation library.

Aber die Version 0.22 formiert sich langsam, und hat einige Neuerungen zu bieten, wie man dem Changelog detailliert entnehmen kann…

Phorward BNF

Das Endprodukt der zahlreichen Überarbeitungen in der Parsing Library, die bereits 2012 innerhalb der libphorward begonnen wurde, ist nun wieder ein neue Grammatik-Beschreibungssprache, die nun den offiziellen Namen PBNF trägt. Diese ähnelt der von pynetree und bis libphorward v0.21 verwendeten BNF-Sprache, hat aber eine deutlich vereinfachte Syntax, die auch UniCCv2 verwenden können soll.

Hier das vielfach verwendete 4-function-calculator Beispiel:

AST-Knoten werden jetzt mit dem = oder := Operatoren definiert.

Command-line tools

Die command-line tools pregex, plex und pparse sind jetzt einheitlich und flexibel nutzbar:

Und dann ist auch noch die Doku verbessert worden. Ich denke in Kürze ist es soweit, das v0.22 offiziell released wird, und es geht mit v0.23 weiter… bis hoffentlich bald, v1.0?

GLR parsing

Update 24.02.2018: Das was ich hier vorher geschrieben hatte war totaler Murks. Jetzt wälze ich erstmal nochmal die Paper dazu und dann sollte ich das nochmal ganz neu aufrollen…

So mal wieder nach längerer Zeit ein neues Posting hier. Muss mal wieder ein paar Gedanken sammeln. Beschäftige mich seit ca. zwei Wochen nun endlich mal mit GLR Parsern, obwohl ich in dem Buch Parsing Techniques von Grune und Jacobs, welches ich schon seit einiger Zeit sporadisch wälze, noch gar nicht in dem Kapitel angekommen bin (zzt. Kapitel 7.2 auf Seite 210, von ca. 640 Seiten). Aber das Parse-Verfahren habe ich mir schon mehrfach angeschaut, es aber nie wirklich umgesetzt.

Aber für die Umsetzung erstmal ein wenig Forschung betrieben…

…und einen (eigenen!) Algorithmus aufgebaut…

…der auch direkt unter Verwendung eines Graph-structured Stack arbeitet. Ich finde bei diesen Informatikbüchern ja immer interessant, dass manche Sache sehr theoretisch behandelt werden. Da wird dann einfach mal der komplette Parse-Stack kopiert und nach einem missglückten Shift dann komplett verworfen. Naja, wenn man das ganze implementieren will, noch dazu in einer Programmiersprache ohne Garbage Collection, dann kopiert man sicherlich nicht erstmal alle Element eines dynamisch allokierten Stacks und deren dynamsiche Stack-Elemente, um diese dann einen Shift später wieder frei zu geben. Mega-Ineffizient.

Naja, und nachdem ich zuerst dachte, es könnte auch klappen, wenn man den Lookahead global hält, wurde ich eines besseren belehrt, denn das geht (rein logisch gesehen) nicht. Es scheint aber so ungefähr zu funktionieren, wie hier, nur das da momentan noch das Stack merging fehlt (zusammenführen mehrerer Stack-Stränge bei einem gemeinsamen Shift). Dazu muss ich aber erstmal noch ein bisschen forschen und testen.

Was es jetzt noch zu lösen gilt (und wo warscheinlich die meiste arbeit drin steckt):

  • Einen eindeutigen (den besten?) Syntaxbaum aus den (bei ambigen Grammatiken dann mehrpfadigen) Parse-Graphen erzeugen
  • Effiziente Garbage Collection
  • Lookahead-Caching bzw. geht das nicht doch mit einem Lookahead?

Bin mal gespannt…

Parsergeneratoren – Dilemma?

Ja…wohin nun?

Da haben wir den Salat! Einmal den UniCC gepuschelt, schon fragt man sich, warum man den ganzen aufwand der letzten Jahre getrieben hat, einen Parser Generator (und zwar exakt denselben Algorithmus) in die libphorward einzubauen.

Und dann stellt man auch wieder fest, das gewisse Java-Werkzeuge aus dem Bereich (okay, in dem Fall ALL(*)) noch besser sind… ach kein Plan.

RapidBATCH 6

Wenn man nach diesem Posting aus 2014 geht wäre es viel lieber schon viel früher so gekommen. Wie dem auch sei: RapidBATCH ist jetzt frei!

Ich habe aus einem altem Backup und mit den aktuellen Tools, der libphorward und UniCC, die unfertigen Sourcen von RapidBATCH 6 portiert und halbwegs zum laufen bekommen.

Hier das offiziellen RapidBATCH open source project: https://github.com/phorward/rapidbatch

Zumindest funktioniert ein Großteil der damaligen Testfiles. Mir ist dabei aufgefallen, dass ich damals die drei Module von RapidBATCH, also Compiler, Virtual Machine und den eigentlichen Funktionsumfang, so dermaßen auseindergerupft hatte, weil ich dachte “je modularer desto besser” und auch weil es mal Pläne gab, RapidBATCH als Domain-specific Language zu vermarkten ohne die Sourcen freizugeben 😀 naja  fail, leider alles verbockte Scheiße.

Egal, irgendwie muss es ja weitergehen, vielleicht nun auch so.

Ach ja ich bin jetzt Open Source mäßig bei GitHub, wohne mit meiner Freundin zusammen und bin umgezogen. Jo. Das wars auch schon…

A step Phorward.

Manchmal ist es so, dass man Dinge mal einige Zeit zur Seite legt, bevor man damit wieder weitermacht. Letzte Woche war es bei mir wieder mal soweit, die etwas angestaubten Spielsachen der alten Zeit wieder rauszukramen… meine private Software ;). Da ist ja wirklich viel passiert die letzten, ja man kann sagen 10 Jahre, und dennoch gab es nicht wirklich was neues.

Und doch, es macht mir immernoch Spaß. Zumindest es in einer gewissen Weise auch zu einem Ende zu bringen oder sich vielleicht auch von manchen Dingen zu trennen, an denen man sehr lange festgehalten hat, wo man aber letztendlich keine weitere Zeit mehr reinstecken möchte oder auch sollte.

Jedenfalls hat meine Website phorward-software.com ein Update erfahren… es gibt jetzt dort einen aktualisierten Projekte-Bereich für die Projekte phorward, pynetree, JS/CC und UniCC.

  • Vor allem an phorward (libphorward, bzw. das Phorward Toolkit) habe ich immernoch Bock weiterzumachen, da es für mich die einzige Möglichkeit ist, weiterhin was in C zu coden und diese Library wirklich alle Themen abdeckt, die ich persönlich super interessant finde. Die libphorward ist ja quasi mein eigenes Steckenpferd einer libmega (Insider wissen, was gemeint ist!) die richtig geile C-Funktionen bereithält (plist, pstack, pstr), und noch dazu add-ons für reguläre Ausdrücke (pregex), Lexing (plex), Parsing (pparse) und sogar virtuelle Maschinen (pvm) bereitstellt. Ursprünglicher Zweck dahinter war es immernoch, einmal ein RapidBATCH damit neu zu entwickeln. Das steht aber weiterhin noch in den Sternen.
  • pynetree ist ja das im letzten Jahr gestartete Projekt einer Python-Parser-Bibliothek mit linksrekursiven Grammatiken, die durch einen Top-Down-Packrat-Parser geparst werden, und sich gemeinsam mit phorward eine eigene TBNF-Sprache teilt. Hier wird auf jeden Fall noch weitergemacht!
  • JS/CC, der JavaScript-Compiler-Compiler, den ich bereits 2007 veröffentlich habe, hat neue Freunde gefunden. Er wird von mir nicht mehr weiterentwickelt oder verfolgt, scheint aber durch die Programmierer Andrew Brobston und Sergiy Shatunov eine gelungende Anbindung an Node.JS gefunden zu haben und wird in einem ganz anderen Rahmen als ursprünglich gedacht weiterentwickelt. Finde ich aber gut, immerhin ein Open Source Projekt welches von Menschen gebraucht, genutzt und sogar Weiterentwickelt wird. JS/CC wird jetzt unter jscc.brobston.com, respektive bei GitHub unter https://github.com/abrobston/jscc weiterentwickelt.
  • UniCC, das eigentlich als “universeller Parser Generator” gedachte Vorzeigeprojekt von mir vor inzwischen auch schon wieder 4 Jahren wird jetzt mehr oder minder eingestampft. Es erfolgt jetzt noch einmal ein Update des Benutzerhandbuchs sowie eine komplette Neu-Lizenzierung der Software unter der BSD-Lizenz, dann wird sie freigelassen. Immerhin: Der UniCC compiliert nach wie vor mit der aktuellsten libphorward, die als Unterbau fungiert. Einziger mir bekannter Einsatz dieses Tools ist das Projekt impact bei MEGA, dessen gesamte Formelsprache in UniCC entwickelt worden ist, und immerhin sogar auf 8 verschiedenen Architekturen läuft.

Alles in allem Plane ich auch, die Webseite unter phorward.info nochmal neu aufzusetzen, warscheinlich als ViUR Projekt und auch mehr mit dem Open Source Hintergedanken. Sozusagen als “mein Software Blog”, ohne es weiterhin auf dieser kommerziellen Schiene, auf der Phorward Software ja ursprünglich aufbaute, zu fahren. Da ist nämlich nichts mehr.

pyParse (bzw. jetzt pynetree)

Es gibt wieder was neues aus der Welt des Compilerbaus von mir:

Nachdem ich mir mal letzte Woche das Paper Packrat Parsers Can Support Left Recursion [Warth, Douglass, Millstein] durchgelesen hatte und den dort gezeigten Algorithmus implementierte, fiel der Groschen: Linksrekursive Grammatiken lassen sich nun DOCH mit einem Nicht-LR-Parser parsen! Also das, wonach ich die ganze Zeit gesucht hatte… oft war ich nah dran, aber es hat dann doch nicht gereicht oder anderweitige Probleme aufgezeigt, die das “proof of concept” nicht werden lassen wollten. Die Lösung ist nun ein modifizierter Packrat-Parser! So ungefährt muss es auch mein lieber Martin Stoilov mit seinem rpatk gelöst haben.

Da ich mir erst nicht so ganz sicher war, ob mein Ansatz jetzt wirklich funktioniert, musste ich zuerst noch ein paar Tests durchführen bzw. es etwas voranbringen… die waren aber diesmal wirklich erfolgreich! Man kann damit arbeiten wie mit einer Grammatik für einen LR-Parser. Einzig und allein die Reihenfolge der Produktionen ist hier zu beachten, was bei einem LR-Parser (wie z.B. libphorward) nicht der Fall ist.

Habe das ganze mal in Python ausprogrammiert und inzwischen schon zu einem kleinen Open Source Projekt gemacht, welches auf den Namen pyParse pynetree hört, und unter der MIT-Lizenz steht.

und gibt

aus.

pyparse kann inzwischen sogar eine BNF-Grammatik ähnlich der libphorward interpretieren (selbstverständlich ist der Parser dafür in pyParse entwickelt!)

pyParse wird wohl dann als erstes mal in ViUR eingebaut… GEIL! 😀

Schadenfroyde

Ihr werdet fallen… genau so tief und bodenlos, wie euer Niveau und eure Frechheit.