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…

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…

Coding parsers…

…wird mit der libphorward jetzt immer geiler! Seit gestern gibts ein neues README-File mit schönem Demoprogramm und neuer Featureliste… damit mal Tacheles gesprochen wird, wozu das Ding eigentlich da ist:

Schneller kann man keinen Parser definieren, ausführen und den Parsebaum als Ergebnis sehen… und alles direkt im C-Code!

Alleinstellungsmerkmal?? 🙂 I hope so…

libphorward selfhosted!

Seit Freitag ist die libphorward nun self-hosted.

Was heißt das nun konkret? Nun es hört sich ein wenig an wie autofellatio, ist aber noch viel, viel geiler! Die libphorward wurde nun um ein weiteres Modul, das Programm ppgram2c  erweitert.

ppgram2c ist ein Code-Generator, welcher eine Backus-Naur-notierte Parser-Beschreibung aus einer Datei einliest und anschließend als C-Programm wieder ausgibt, also sozusagen die Funktionsaufrufe, die nötig sind, um die gewünschte Grammatik direkt in C mit dem libphorward Funktion des parser-Moduls zu definieren.

Aus der BNF-Notation für die eigene BNF-Sprache mit der Syntax:

Ich muss gestehen, das diese Grammatik noch recht unschön definiert ist, was aber daran liegt, das noch ein paar Bugs vorliegen (equal, percent und semicolon mussten wegen Mehrfachverwendung als eigene Symbole definiert werden, sollte später so nicht sein).

Aus dieser Grammatik erzeugt ppgram2c nun folgenden C-Code (mit ein wenig awk-Hokuspokus drumherum):

Also wer’s nachtesten will, es ist

ppgram2c –indent 1 src/parse/gram.syn

was hier im Hintergrund aufgerufen wird. Es erzeugt das meiste von dem Code above.

OK nun zum Highlight des ganzen: Der Parser parst seine eigene Grammatik und erzeugt den Code für sich selbst. Folglich konnte der Parser erfolgreich durch sein eigenes Duplikat ersetzt werden. Die nachfolgenden Parser der libphorward sind allesamt Klone von sich selbst, also werden mit sich selbst geparst und dann selbst Bestandteil der Toolchain. Diesen Mechanismus nennt man im Compilerbau bootstrapping, und wurde bisher bei all meinen Compilerprojekten erfolgreich umgesetzt, also JS/CC, UniCC (welcher min_lalr1 benutzt) und nun auch der libphorward.

Damit wurden der rekursive-absteigende Parser der libphorward sowie die handgeschriebene LALR(1)-Grammatik hinfällig, da sie durch ihren eigenen Klon ersetzt wurden. (Hoffentlich hat der Klon keinen Fehler…)

Damit ist die libphorward selfhosted (selbst-gastgebend) und kann sich selbst compilieren (zumindest den Parser ;-)). Was nun folgt ist pure geile Scheiße! 😀

Man liest sich…