[Spirit] Grammatik mit Block-Einrückung realisieren
-
Hallo,
die Frage richtet sich an Leute mit Erfahrung in Boost::Spirit.
Ich möchte eine Grammatik implementieren, die keine explitziten Blockgrenzen verwendet sondern Blöcke ausschließlich über ihr Einrückungslevel erkennt, ähnlich wie in Haskell oder Python. Dabei ist die Einrückung aber beliebig variabel, lediglich "mehr als" die des umschließenden Blocks.
Ich bin zur Zeit relativ ratlos, wie man sowas in Spirit realisieren könnte und bevor ich anfange, rumzuhacken, wollte ich fragen, ob es dafür vielleicht eine gewiefte Lösung gibt.
Als Beispiel zur Veranschaulichung, folgender Code wäre gültig:
function main(): print(sum(1, 6)) # gibt 21 aus. function sum(from, to): if (from == to) return to return from + sum(from + 1, to)
-
Hmm, also ich hab das selber noch nicht gemacht, aber hast du das schon mit blank_p probiert? Oder vielleicht mit einem lexeme_d[ch_p ('\t')]?
-
.filmor schrieb:
Hmm, also ich hab das selber noch nicht gemacht, aber hast du das schon mit blank_p probiert? Oder vielleicht mit einem lexeme_d[ch_p ('\t')]?
Hi,
das Problem ist ja nicht das Matchen der Zeichen sondern die Tatsache, dass ich im Vornherein nicht weiß, wieviele Zeichen ich matchen muss, dies aber für die Semantik durchaus relevant ist. Ich brauche im Prinzip ein Prädikat, das die aktuelle Einrückungstiefe mit der vorhergehenden vergleicht, und zwar mehrstufig (Blöcke können ja geschachtelt auftreten).
-
Du könntest dir einen Actor schreiben, der bei jedem \t oder Leerzeichen einen Counter erhöht und beim ersten anderen Buchstaben diesen benutzt. Du kannst auch in einem ersten Parservorgang die Leerzeichen am Anfang interpretieren und die einzelnen Zeilen dann zusammen mit den Blockinformationen in eine Liste eintragen und später dann einzeln parsen und interpretieren.
-
Hi,
.filmor schrieb:
Du könntest dir einen Actor schreiben, der bei jedem \t oder Leerzeichen einen Counter erhöht und beim ersten anderen Buchstaben diesen benutzt.
Vielen Dank für die Antwort. Der Actor in Verbindung mit einem Zähler ist natürlich eine Lösung, wenn ich den aktuellen Zählstand auf einen Stack schiebe und mit dem obersten Element desselben abgleiche.
Ein Problem gibt es da allerdings noch, nämlich die Tatsache, dass Tabs und Leerzeichen in einer Datei nicht gemischt auftreten können (was Einrückung betrifft, ansonsten aber schon), um Fehler zu vermeiden. D.h. es muss noch einen intelligenten Weg geben, Tab- und Leerzeichen-Einrückungen zu unterscheiden (das ist trivial: separate Zähler, aber diese Lösung ist alles andere als elegant). Ein einfacher Test, ob Tabs in der Datei vorkommen, reicht dabei nicht.
Du kannst auch in einem ersten Parservorgang die Leerzeichen am Anfang interpretieren und die einzelnen Zeilen dann zusammen mit den Blockinformationen in eine Liste eintragen und später dann einzeln parsen und interpretieren.
Ich möchte das ganze wenn möglich in einem einzigen Pass erledigen.
Trotzdem Danke für die Idee.
-
Ich habe mir mal angeschaut, wie Python das ganze macht. Hier werden zwei Pässe verwendet. Im ersten Pass werden zusätzliche Tokens "indent" und "dedent" generiert, wenn sich der Indent-Level erhöht bzw. erniedrigt. Dadurch kann man die Grammatik kontextfrei machen.
Im Zusammenspiel mit dem multi_pass-Iterator in Spirit kann man eine Policy entwickeln, die als Eingabe keinen Zeichenstrom annimmt sondern einen Symbolstrom, den man dann schon um die Symbole für indent und dedent bereichert hat. Das ist echt fein, besonders, weil man damit ANTLR- (oder lex-) kompatible Tokenströme verwenden kann, wenn man sich einen entsprechenden Funktor baut (lex ist sogar schon vorgefertigt).