Gefräßige Parser

Das Definieren der Syntax für eine Programmiersprache wäre eigentlich ganz einfach, wenn nicht so viele Entscheidungen zu treffen wären. Denn während einige Entscheidungen eher trivialer Natur sind, tut man sich mit anderen deutlich schwerer, können sie doch weit reichende Konsequenzen haben.

In Pocket speichern vorlesen Druckansicht 29 Kommentare lesen
Lesezeit: 5 Min.
Von
  • Michael Wiedeking

Der Grund dafür, dass ich mich in letzter Zeit ein bisschen rar gemacht habe, ist eigentlich ein ganz einfacher: Ich versuche mich in einer Programmiersprache. Nicht dass ich das machen würde, weil es im Moment fast jeder macht, sondern weil ich jetzt endlich die Zeit dazu habe. Vor über einem Vierteljahrhundert ist der Wunsch aufgekeimt, aber immer wieder war alles andere wichtiger. Jetzt kann ich mich ihm jedoch in gewünschtem Umfang widmen, und so nimmt das, was in all den Jahren herangereift ist, endlich Form an.

Das Definieren einer Syntax an und für sich gesehen würde sich eigentlich recht einfach gestalten, wenn nicht so viele Entscheidungen zu treffen wären, von denen einige eher trivialer Natur sind, andere jedoch weitreichende Konsequenzen haben. Leider stellt sich manchmal nicht gleich heraus, welche Entscheidung zu welcher der beiden Kategorien gehört.

So habe ich etwa den Unicode wie in Java – und inzwischen in fast allen anderen der neueren Sprachen – als Zeichensatz der Wahl auserkoren. Damit tritt allerdings das Problem auf, dass man Zahlen nicht nur mit den uns bekannten Ziffern 0 bis 9 darstellen kann, sondern beispielsweise auch mit arabischen oder thailändischen Ziffern (und dabei soll mal egal sein, dass wir unsere Zahlen mit arabischen Ziffern schreiben und die Araber ihrerseits eher von indischen Ziffern sprechen). Darüber hinaus gibt es noch römische Zahlen mit lateinischen Ziffern, Brüche und Darstellungen ganz anderer Zahlensysteme, die auch numerische Werte liefern.

Hier muss man sich nun entscheiden, ob man nur die einen Ziffern will oder alle, ob man Zahlen nur aus Ziffern einer Art erlauben oder etwa arabische mit thailändischen Ziffern zu einer Zahl kombinieren will. Und von dieser Komplexität sind typischerweise die einfachen Entscheidungen.

Zu den unangenehmeren Entscheidungen gehören diejenigen, deren Konsequenzen möglicherweise zu Beginn gar nicht auffallen. Hier sei beispielhaft ein Problem erwähnt, das die C-orientierten Sprachen im Zusammenhang mit ihrem Parser haben, der den Programmtext in Token zerlegt. In C gibt es nämlich den Präfix-Operator ++, den Infix-Operator + und den Suffix-Operator ++. Mit deren Hilfe lässt sich der nicht besonders sinnvolle, aber mögliche Ausdruck

a++ + ++b

schreiben. Was passiert aber, wenn man die Leerzeichen weglässt?

a+++++b

Jetzt entsteht das Problem, dass entschieden werden muss, wie die Zeichen zusammenzufassen sind. Die Parser entscheiden sich hier oft für eine gefräßige Variante, was bedeutet, dass so viel wie möglich gelesen wird, um ein Token zu bilden. In diesem Fall würde der Ausdruck als

a++ ++ +b

ausgewertet werden. Der ist – nebenbei bemerkt – natürlich nicht gültig, denn es können keine zwei derartigen Suffix-Operatoren hintereinander oder zwei derartige Präfix-Operatoren voreinander stehen.

Mit Hilfe der gierigen Regelungen lassen sich auch die Operatoren << und >> von < bzw. > unterscheiden. Leider hatte dies bei C++ einen sehr unschönen Nebeneffekt, als die Templates eingeführt wurden. Warum auch immer entschied man sich dazu, die Typ-Parameter in spitze Klammern zu setzen:

List<T>

So weit, so gut. Was ist aber nun, wenn die in der Liste enthaltenen Elemente wieder Listen sind?

List<List<T>>

Der gierige Leser sieht sofort, dass dies nicht funktioniert, denn die beiden schließenden Klammern > und > werden nicht als solche erkannt, sondern als das Token >> für das Shiften nach rechts. Das Problem ließ sich leider nur lösen, indem man ein zusätzliches – ausgesprochen hässliches – Leerzeichen ergänzte:

List<List<T> >

Typographie scheint zwar nicht jedermanns Sache zu sein, aber auch dieses Problem ist inzwischen behoben. Man kann es ja auch leicht lösen, indem man im Falle des > nicht ganz so gierig ist. Hierbei ist übrigens wichtig, dass es bei dieser Schönheitskorrektur weniger um Ästhetik ging als darum, unschuldigen Anfängern nicht mehr klarmachen zu müssen, warum ausgerechnet an dieser Stelle ein völlig überflüssiges Leerzeichen stehen muss.

Dieser Entscheidung kann man insofern verzeihen, weil es nicht vorhersehbar war, dass Größer- und Kleinerzeichen derart missbraucht werden würden. In Hinblick auf die Kompatibilität kann man sich aber leider keine derartigen Fehler erlauben, denn die Korrektur eines solcher Fehlers führt möglicherweise dazu, dass sich "alte" Quellen nicht mehr übersetzen lassen, was bei entsprechend großer Code-Basis inakzeptabel ist.

Bei der Definition einer Programmiersprache gilt es also solche Fehler zu vermeiden wie den Assuan-Staudamm: Gut gemeint, Erfolg versprechend, mit überraschend fatalen Nebeneffekten und irreversibel. ()