REPL für Interpreter mit Flex / Bison

9

Ich habe einen Interpreter für eine C-ähnliche Sprache geschrieben, mit Flex und Bison für den Scanner / Parser. Es funktioniert gut, wenn vollständige Programmdateien ausgeführt werden.

Jetzt versuche ich eine REPL im Interpreter für den interaktiven Gebrauch zu implementieren. Ich möchte, dass es wie die Befehlszeileninterpreter in Ruby oder ML funktioniert:

  1. Zeigen Sie eine Eingabeaufforderung
  2. an
  3. Akzeptieren Sie eine oder mehrere Anweisungen in der Zeile
  4. Wenn der Ausdruck unvollständig ist
    1. zeigt eine Fortsetzungsansage an
    2. ermöglicht es dem Benutzer, weiterhin Zeilen einzugeben
  5. Wenn die Zeile mit einem vollständigen Ausdruck endet
    1. echo das Ergebnis der Auswertung des letzten Ausdrucks
    2. zeigt die Hauptaufforderung
    3. an

Meine Grammatik beginnt mit einer top_level -Produktion, die eine einzelne Anweisung in der Sprache darstellt. Der Lexer ist für den interaktiven Modus auf stdin konfiguriert. Ich benutze den gleichen Scanner und die gleiche Grammatik sowohl im Volldatei- als auch im REPL-Modus, da es keinen semantischen Unterschied zwischen den beiden Schnittstellen gibt.

Meine Hauptauswertungsschleife ist wie folgt aufgebaut.

%Vor%

Dies funktioniert bis auf die Prompt- und Echo-Logik. Wenn der Benutzer mehrere Anweisungen in einer Zeile eingibt, druckt diese Schleife überflüssige Eingabeaufforderungen und Ausdrücke aus. Und wenn der Ausdruck in mehreren Zeilen fortgesetzt wird, gibt dieser Code Fortsetzungsaufforderungen nicht aus. Diese Probleme treten auf, weil die Granularität der Prompt / Echo-Logik eine top_level -Anweisung in der Grammatik ist, aber die Zeilenleselogik tief im Lexer ist.

Was ist der beste Weg, die Evaluierungsschleife neu zu strukturieren, um die REPL-Aufforderung und das Echo zu verarbeiten? Das ist:

  • Wie kann ich eine Eingabeaufforderung pro Zeile anzeigen
  • ?
  • wie kann ich die Fortsetzung Aufforderung zur richtigen Zeit anzeigen
  • Wie kann ich feststellen, wann ein vollständiger Ausdruck der letzte in einer Zeile ist
  • ?

(Ich würde lieber nicht die Scannersprache ändern, um Newline-Token zu übergeben, da dies die Grammatik stark verändern würde. Das Ändern von YY_INPUT und das Hinzufügen einiger Aktionen zur Bison-Grammatik wäre in Ordnung. Außerdem verwende ich die stock Flex 2.5.35 und Bison 2.3, die mit Xcode ausgeliefert werden.)

    
Jay Lieske 09.07.2011, 18:56
quelle

2 Antworten

5

Nachdem ich gesehen habe, wie Sprachen wie Python und SML / NJ mit ihren REPLs umgehen, habe ich eine nette Arbeit in meinem Interpreter bekommen. Anstatt die Prompt / Echo-Logik in der äußersten Parser-Treiberschleife zu haben, lege ich sie in die innerste Lexer-Eingaberoutine. Aktionen im Parser und Lexer setzen Flags, die die Eingabeaufforderung durch die Eingabe-Routine steuern.

Ich verwende einen Wiedereintrittsscanner, also enthält yyextra den zwischen den Ebenen des Interpreters übergebenen Zustand. Es sieht ungefähr so ​​aus:

%Vor%

Die Lexer-Eingabe-Routine:

%Vor%

Die oberste Interpreterschleife wird zu:

%Vor%

Die Flex-Datei erhält folgende neue Definitionen:

%Vor%

Das YY_USER_ACTION behandelt das schwierige Zusammenspiel zwischen Token in der Sprachgrammatik und Eingabezeilen. Meine Sprache ist wie C und ML, in der ein Sonderzeichen (';') benötigt wird, um eine Aussage zu beenden. Im Eingabestream kann auf dieses Zeichen entweder ein Zeilenvorschubzeichen folgen, um das Ende der Zeile zu signalisieren, oder es können Zeichen folgen, die Teil einer neuen Anweisung sind. Die Eingabe-Routine muss die Haupteingabeaufforderung anzeigen, wenn die einzigen Zeichen, die seit dem letzten Anweisungsende gescannt wurden, Zeilenumbrüche oder andere Leerzeichen sind. Andernfalls sollte die Fortsetzung angezeigt werden.

    
Jay Lieske 14.08.2011, 18:20
quelle
0

Ich arbeite auch an einem solchen Dolmetscher, ich bin noch nicht dazu gekommen, eine REPL zu machen, daher könnte meine Diskussion etwas vage sein.

Ist es akzeptabel, wenn bei einer Abfolge von Anweisungen in einer einzigen Zeile nur das Ergebnis des letzten Ausdrucks gedruckt wird? Weil Sie Ihre Top-Level-Grammatikregel wie folgt neu einteilen können:

top_level = top_level Anweisung | Aussage;

Die Ausgabe Ihrer top_level könnte dann eine verkettete Liste von Anweisungen sein, und interpreter.result wäre die Auswertung des Endes dieser Liste.

    
wmercer 10.07.2011 22:36
quelle