Eine linksrekursive Grammatik im rekursiven Abstieg parsen

Zur Zeit zerbreche ich mir den Kopf darüber, wie man eine linksrekursive Grammatik mit einem rekursiv-absteigenden Parser parsen kann.

Darauf gekommen bin ich durch das Open-Source-Projekt RpaTk von Martin Stoilov – ein im übrigen sehr geniales Projekt. Es enthält einen grundlegend anderen Ansatz als meine bisherigen Entwicklungen, nämlich einen rekursiv-absteigenden Parser, der Backtracking verwendet und noch einige Geschwindigkeitsoptimierungen durch Caching. Das alles läuft dann auch noch auf Basis einer virtuellen Maschine, die ziemlich rasantes Parsing ermöglicht. Ein sehr durchdachtes Konzept, auch wenn es in direkter Konkurrenz zu den letzten Entwicklungen in der libphorward steht, und dieser auch meilenweit voraus ist – Hut ab. Habe mit dem Autor auch Kontakt aufgenommen, weil ich es einfach verstehen will, wie man mit einem rekursiv-absteigenden Parser eine linksrekursive Grammatik parst.

Nehmen wir mal als Beispiel die Grammatik

und würden diese als plumpen rekursiven Abstieg umsetzen, so wird er noch vor dem lesen des ersten Zeichens in eine Endlosschleife verfallen, weil sich e rekursiv aufruft.

Continue reading

*nerdfroi*

Da sich ja Dennis beschwert hat, das hier nix mehr gepostet wird… ein kleines *nerdfroi*!

Musste den ganzen AST-Generator nochmal umbauen. Jetzt tut er so wie er soll, siehe da.

misc-geniusDas war einiges an “Fummeley”, wie der Mittelalternerd jetzt sagen würde…

libphorward kann ASTs

Jawoll ja!

Nachdem ich die letzten Tage es  geschafft habe, die libphorward und die pggrammar-Erweiterung zumindest skizziert in einen Zustand zu bringen, der letztendlich erreicht werden soll, bin ich heute auch noch dort ziemlich weit gekommen.

Zumindest kann die libphorward jetzt sogar einen abstract syntax tree sowie schon seit vorvorgestern einen syntax tree aus einer geparsten Eingabe generieren!

Demo-Programm:

Erzeugt nun das hier:

Man beachte das, was vor der Linie (Zeile 233) kommt, und das was hier am Ende steht (Zeile 467): Ein abstrakter Syntaxbaum! Hier am Ende: AST der Eingabe (7+3)*2-5 ist sub( mul( add( 7, 3 ), 2 ), 5 ).

Ok klingt vielleicht für die meisten nicht so Spannend, aber es ist für mich nur.. geile Scheiße!!! 😀

Etwas planlos…

pglexer erzeugt nun pgtoken. Schön, hm?

Jedenfalls funktioniert das Ergebnis gut bisher. Es ist möglich, aus verschiedenen Datenquellen (“sources”) zu lesen, z.B. aus einem Dateistrom, String, wide-character String oder über eine Funktion, z.B. getchar(). pglexer übernimmt dabei automatisch das Buffering.

IMG_20140114_231421Aktueller Stand (so wie hier auf SourceForge).

Naja soooo viel hat sich im Grunde nicht getan in der libphorward.
Was aber nun schon mal geht ist:

  • Grammatiken über API-Funktionen definieren
  • Parse-Tabellen für LR(0), LR(1) und LALR(1) mit table compression werden erzeugt
  • Lexer wie oben beschrieben
  • Regex-Library sehr stark verbessert: benutzt jetzt kein llist mehr, nurnoch plist :-), und alle bisher aufgetretenen Bugs sind gefixt.

Todo:

  • pgparser auf einen Stand bringen der erstmal nur UTF-8 unterstützt aber läuft mit dem Ziel, einen Parser direkt über die libphorward zu definieren
  • Rückgabe des Parsetrees als pgast-Struktur, später TBNF-basierte Konstruktion eines AST (abstract syntax tree).
  • Funktion in pggrammar implementieren, die eine Grammatik über sich selbst parst und zurückgibt (pg_grammar_parse()).

UniCC wird auch noch auf plist umgestellt bzw. auf die neue Funktion pregex_dfa_to_matrix() umgeschrieben. Zur Zeit lässt sich der UniCC nicht mit der aktuellen libphorward 0.18 linken, auch wenn er bereits schon auf einige 0.18-spezifische Neuerunge umgebaut worden ist.

So much to code…

…so little time!

Wie wahr doch dieser Spruch ist!

Momentan bin ich wieder ziemlich dabei, Phorward Software weiter zu bringen. Was mich ein wenig nervt ist (wieder mal) mein Perfektionsdrang, der sich zur Zeit in einer radikalen Änderung der libphorward wiederspiegelt, wie auch in der Tatsache, dass der UniCC Parser Generator, ein Projekt an dem ich 6 Jahre (!) sporadisch gearbeitet habe, sozusagen für die Katz war. Das Programm ist Feature complete, und ich sehe darin auch keine große Zukunft – weil es nicht flexibel genug ist. Letztendlich. Hm.

Nein, die Zukunft liegt momentan eher in der libphorward, und der Erweiterung die ich vor einiger Zeit mal vorgestellt hatte als pggrammar. Inzwischen ist aus diesem Anfang schon eine beachtliche Menge Code geworden. pggrammar, oder das, was daraus wird, wird eine Spielwiese für Grammatiken, Lexer und Parser, also das, was UniCC als Code Generator verkörpert, nur in Form einer Library. Das schöne dabei ist, dass man bei einer Library – basierend auf einem objektorientiertem Ansatz – eine extrem flexible Software machen kann, die letztendlich keine Wünsche mehr offen lässt. Zumindest denke ich das.

Zur Zeit versuche ich das, was in diesem Klassendiagramm zu finden ist, in die libphorward einzubauen.

pggrammarDas ganze versuche ich natürlich wieder in C zu programmieren – wo auch schon das nächste Problem liegt. Eigentlich würde C++ hier mehr Sinn machen, zumal ich C++ auch gerne mal wirklich lernen würde. Aber dann kann man es nicht mehr in C benutzen… was mich doch wieder dazu veranlasst, es nicht zu tun, und dafür ein wirrwarr an Strukturen und komisch benannten Funktionen aufzubauen… verdammter Perfektionsdrang. Naja, mal sehen. Es macht ja eigentlich gar keinen Sinn, eine C-Library, in die man echt viel Zeit und Arbeit gesteckt hat, jetzt “einfach mal” auf C++ umzuschreiben – zumal dann ein Großteil der Library Funktionen wieder überflüssig wird, denke man nur an die Funktionen für verkettete Listen, Hash-Tables, dynamische Array… sowie das neue Objekt plist, welches sowohl doppelt verkettete Liste, Array als auch Hashtable sein kann. Krank, nicht?? 🙁

Ursprünglich war geplant, das Regular Expression Modul der libphorward nun darauf umzustellen, dass man auch direkt auf FILE-streams arbeiten kann…aber das wäre wieder nicht flexibel genug. Nach reichlicher Überlegung habe ich mich daher nun dazu entschlossen, anstatt das regex-Modul wieder komplett umzukrempeln (und ich finde gerade das ist mir bisher ziemlich gelungen!) nun die pggrammar-Idee in drei Module der libphorward aufzuteilen:

  1. grammar (pgrammar)
    • pgrammar
    • pterminal
    • pnonterminal
    • pproduction
  2. lexer (plexer)
    • plexer
  3. parser (pparser)
    • pparser

Die Module grammar und parser (was den LR/LALR-Teil angeht) sind momentan ja schon auf einem guten Wege, nur zur Zeit noch vereint als Modul “parser” in der libphorward. Das Modul lexer würde dann die Schnittstelle zwischen dem Modul regex und dem parser aufbauen, wäre aber auch ohne beide Module lauffähig.

Ein Modul der libphorward wird immer anhand des Verzeichnisses in src definiert. Die Funktionen sind alle in einer Library, aber bestimmten Themen zugeordnet.

libphorward_module

Die libphorward würde dann aus folgenden Bereichen bestehen:

  • base (Basis-Funktionen, Datenstrukturen)
    • debug
    • llist** (leider geil, da einfach zu bedienen! – sehr häufig benutzt)
    • hashtab*
    • stack*
    • plist (hash-table, double-linked list, stack als einzelnes Objekt)
  • string (erweiterte String Funktionen)
  • regex (Funktionen für reguläre Ausdrücke, NFA/DFA, Zeichenklassen)
  • union* (dynamische Datentypstruktur)
  • xml* (XML-DOM Tools)
  • util* (System-Werkzeuge)
  • grammar*** (Grammatik-Tools)
  • lexer*** (Tools zur Erstellung lexikalischer Analysatoren)
  • parser*** (Tools zur Erstellung von Parsern (LR, LL) auf Basis von grammar und lexer)

* Modul/Datenstruktur nicht mehr sinnvoll?
** Die Datenstruktur LIST, auch LLIST oder llist genannt, ist ein Phänomen. Die Funktionen dafür habe ich mal anno 2006 oder so programmiert – und diese Library ist unschlagbar geil, weil sie so simpel ist. Einfach verkettete Pointer-Listen ohne viel Schnickschnack: LIST. Selbst in pggrammar habe ich viele davon benutzt, weil sie so extrem simpel ist. Daher wird plist “nur” in Fällen genutzt, wo es wirklich Sinn macht – also alles, was hashtab, llist und/oder array sein soll und muss. Wegoptimieren von LIST? Unmöglich. Aber LIST ist cool!:
*** Modul in Planung!

So sieht’s momentan aus. Tja… viel Gedankenmüll. Und das um sinnlose Software. Aber: Ich find’s geil! 😀 Und ist es nicht das, worauf es ankommt?

String mit Escape-Sequenzen via Regular Expression matchen

Noch ein kurzer Tipp an mich selbst, nach heute wieder überflüssigem rumgesuche und dem bösen Gedanken, die Phorward Foundation Library könnte hier einen Fehler haben.

http://stackoverflow.com/questions/4166194/how-do-i-write-a-non-greedy-match-in-lex-flex