2017-01-08 5 views
1

Ich benutze Lex und Bison Parser Generatoren. Ich habe meine .lex-Datei, die die Syntax definiert und. Ypp-Datei, die die Semantik definiert. in meinem .ypp Ich habe diese Zeile:Woher weiß man, welche Zeile in yylineno gedruckt wird?

Statement : Type ID ASSIGN Exp {check_types_match($1.type, $4.type)} SC 
  • Typ int oder boolean sein kann.
  • ID ist eine Kennung.
  • ASSIGN ist das = Symbol.
  • Exp kann viele Dinge sein, unter ihnen ist Exp: true, die den Typ des Ausdrucks als Boolean speichert.
  • SC ist ein Semlikon ";".
  • check_types_match prüft, ob ein Typ nicht übereinstimmt, und gibt die Zeile (yyyleno) des Fehlers aus, falls vorhanden.

in dieser einfachen Eingabedatei:

int x = true 
; 

ich, dass der Fehler in Zeile 2 und nicht in Zeile 1. Wie kann ich es mache den Fehler in Zeile 1 statt drucken?

Antwort

1

Die Aussage nicht als solche erkannt wird, bis Sie das Semikolon erreichen, die 2. im Moment also auf der Linie ist, dass check_types_match genannt wird, yylineno muss 2.

Linie zeigen Wenn Sie einen Fehler produzieren wollen Wenn Sie eine Nachricht mit einer anderen Zeilennummer haben, müssen Sie sicher entscheiden, welche Zeile gedruckt werden soll. Hier haben Sie mindestens zwei Möglichkeiten, da der Fehler zwischen dem Token int und dem Token true liegt. In diesem Fall sind beide von denen auf der Linie 1, aber was ist, wenn der Programmtext lautete:

int x = 
    true; 

Es scheint vernünftig, dass eine dieser Tokens als Ursache für den Fehler gekennzeichnet werden sollte, so dass das Problem reduziert sich auf Bezifferung Aus welcher Zeile der Token angezeigt wurde. Da dieses Token eine uralte Geschichte ist, wenn die Reduktion stattfindet, ist der einzige Weg, dies zu tun, sich an den Ort jedes Tokens zu erinnern, das noch benötigt wird, normalerweise ist jedes Token noch auf dem Parser-Stack.

Glücklicherweise hat bison eine einfache Möglichkeit, das zu tun. Bei Bedarf wird ein Standortstapel parallel zum Parserstapel verwaltet, und Sie können dann auf das Standortobjekt für Token 1 zugreifen, indem Sie einfach auf @1 verweisen. Noch besser, wenn Sie einfach einen Verweis auf ein location-Objekt irgendwo in Ihrer bison-Datei verwenden, ist es ausreichend, bison davon zu überzeugen, diese Informationen zu pflegen. So könnten Sie Ihre Aktion ändern:

Statement : Type ID ASSIGN Exp {check_types_match($1.type, $4.type, @1)} SC 

(. Oder @4, wenn Sie denken, dass es sinnvoller ist, den Fehler zu den Exp zuzuschreiben)

Natürlich ist es nie ganz so einfach. Es ist auch notwendig, für bison zu arrangieren, um den Standort jedes eingehenden Tokens zu kennen, und auch zu verstehen, wie man einen Ort für ein neu erzeugtes Nicht-Terminal erstellt (wie Exp im obigen Beispiel).)

Da ein Lokationsobjekt sich auf den Ort einer Folge von Tokens beziehen kann (wie im Nicht-Terminal-Fall), der sich über mehrere Zeilen erstrecken kann, ist es normal, dass das Lokationsobjekt sowohl einen Start als auch anzeigt Endpunkt. Außerdem ist es üblich, dass sowohl eine Zeilennummer als auch ein Spaltenoffset exakte Fehlermeldungen erzeugen sollen. Folglich hat das Standardverzeichnis Objekt den folgenden Typ:

typedef struct YYLTYPE { 
    int first_line; 
    int first_column; 
    int last_line; 
    int last_column; 
} YYLTYPE; 

Und standardmäßig die Lage Objekt für einen Nicht-Terminal berechnet wird, als ob Sie so etwas wie

@$.first_line = @1.first_line; 
@$.first_column = @1.first_column; 
@$.last_line = @N.last_line; 
@$.last_column = @N.last_column; 

geschrieben hatte, wo N das ist Index des letzten Grammatiksymbols auf der rechten Seite. (Da bison keine Notation für "die Anzahl der Grammatiksymbole" hat und keine Variablen in $N Konstrukte erlaubt, kann man das nicht schreiben. Aber das ist die Idee.)

Da ist das alles ziemlich gut, was du willst, es gibt kein Problem von bisons Seite. Aber Sie müssen auch die Informationen von flex in erster Linie erhalten.

Wenn Sie die einfache Schnittstelle zwischen flex und bison verwenden, die auf globale Variablen beruht, dann wird der Name des Orts-Objekt mit dem aktuellen Token entspricht, ist yylloc (ähnlich yylval). flex kann automatisch erstellen yylineno, aber es speichert es nicht automatisch in yylloc, noch hat es einen eingebauten Mechanismus zum Verfolgen von Spaltennummern noch den Fall behandeln, wo das zurückgegebene Token über mehr als eine Zeile verteilt ist (was möglich sein könnte für String-Konstanten, zum Beispiel).

Die ganze Infrastruktur zu bekommen ist etwas außerhalb des Umfangs dieser Frage, da Sie nur nach Zeilennummerninformationen fragen. Wenn Sie nur Zeilennummern verfolgen müssen, und Sie haben nicht mehrzeiligen Token, wäre es ausreichend, die folgende für jede Flex-Regel hinzuzufügen:

yylloc.first_line = yylloc.last_line = yylineno; 

Wenn Sie tun Token mehrzeiligen Sie die folgende könnte statt:

yylloc.first_line = yylloc.last_line; 
yylloc.last_line = yylineno; 

, die hinzugefügt jede Token Aktion werden müßten, auch diejenigen, die etwas (Kommentare und Leerzeichen) nicht tun. Zum Glück hat flex ein Makro, das am Anfang jeder Aktion hinzugefügt wird, so dass Sie Ihre gesamte Flex-Datei nicht komplizieren müssen. Es ist ausreichend, etwas hinzufügen wie:

#define YY_USER_ACTION do {    \ 
    yylloc.first_line = yylloc.last_line; \ 
    yylloc.last_line = yylineno;   \ 
} while(0) 

(. Wenn Sie Spaltennummern-Tracking am Ende auch Sie benötigen, um das zu ändern)

Sie müssen auch sicherstellen, dass yylloc.last_line-1 initialisiert wird; Andernfalls beginnt Ihr erstes Token in Zeile 0.

Für weitere Informationen lesen Sie bitte das Handbuch:

Wenn Sie einspringenden/pure Scanner und Parser verwenden, müssen Sie in der Dokumentation entnehmen wie das Location-Objekt ohne Globals übergeben wird. Beachten Sie, dass die Deklaration %bison-locations nicht immer das ist, was Sie wollen (und das ist definitiv nicht das, was Sie wollen, wenn Sie keinen Reentrant/reinen Scanner und Parser verwenden.)

+0

Vielen Dank für die ausführliche Antwort! Es hat mir geholfen :) Prost! – Loay

Verwandte Themen