Frage

Ich versuche, Flex und Bison zu verwenden, um einen Filter zu erstellen, weil ich bestimmte Elemente der Grammatik aus einer komplexen Sprache erhalten möchte. Mein Plan ist es flex + Bison zu verwenden, um die Grammatik zu erkennen und entleeren sie die Position von Elementen von Interesse aus. (Dann ein Skript verwenden, um Text nach den Orten deponiert zu greifen.)

fand ich flex kann einen Bison Merkmal Bison Lokationen genannt unterstützen, aber wie funktioniert es genau. Ich habe versucht, das Beispiel in flex-Dokument, wie es scheint die yylloc nicht automatisch von flex gesetzt, ich (1,0)-(1,0) immer bekommen. Könnte flex automatisch jedes Token Standort berechnen? Wenn nicht, ist das, was Schnittstellenfunktion für mich definiert zu implementieren? Gibt es ein Beispiel?

Jede bessere Lösung zu Werkzeugen in Bezug auf?

Mit freundlichen Grüßen, Kevin

Edit:

Nun ist die Schnittstelle für yylex wiederum an:

int yylex(YYSTYPE * yylval_param,YYLTYPE * yylloc_param );

Bison Handbuch nicht angegeben, wie Lexer implementieren sollte yylloc_param richtig eingestellt ist. Für mich ist es schwer zu manuell Spaltennummer jedes Tokens zu verfolgen.

War es hilfreich?

Lösung

Werfen Sie einen Blick auf Abschnitt 3.6 der Bison Handbuch - die Standorte im Detail zu decken scheint. In Kombination mit dem, was Sie in dem Flex-Handbuch, dass ausreichend sein kann.

Andere Tipps

Die yylex Erklärung wahrscheinlich geändert, weil Sie einen einspringenden oder pure-Parser verwendet. Scheint, wie viele Dokumente rund um das Web es ist erforderlich vorschlagen, wenn Sie Bison Standorten arbeiten wollen, aber es ist nicht erforderlich.

Ich brauchte Zeilennummern zu und fand die Bison-Dokumentation in dieser Hinsicht verwirrend. Die einfache Lösung (die globale var yylloc verwenden): In Ihrem Bison-Datei fügen Sie einfach die% Standorten Richtlinie:

%{
...
%}
%locations
...
%%
...

in Ihrem Lexer:

%{
...
#include "yourprser.tab.h"  /* This is where it gets the definition for yylloc from */
#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno;
%}
%option yylineno
...
%%
...

Das YY_USER_ACTION Makro „genannt“ vor jedem Ihrer Token Aktionen und Updates yylloc. Jetzt können Sie die @N / @ $ Regeln wie folgt verwenden:

statement : error ';'   { fprintf(stderr, "Line %d: Bad statement.\n", @1.first_line); }

oder verwenden Sie das yylloc global var:

void yyerror(char *s)
{
  fprintf(stderr, "ERROR line %d: %s\n", yylloc.first_line, s);
}

Ich mag Shlomi Antwort.

Außerdem suche ich nach der Aktualisierung als auch Spaltenposition. Gefunden http://oreilly.com/linux/excerpts/9780596155971/error- Reporting-recovery.html , die mehr Sinn gemacht, nachdem Shlomi Antwort zu lesen.

Leider gibt es einen Tippfehler auf dieser Seite für yylloc. Ich habe es unten ein wenig vereinfacht.

In Ihrem Parser add:

%locations

in Ihrem Lexer:

%{

#include "parser.tab.h"

int yycolumn = 1;

#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno; \
    yylloc.first_column = yycolumn; yylloc.last_column = yycolumn + yyleng - 1; \
    yycolumn += yyleng; \
    yylval.str = strdup(yytext);

%}

%option yylineno

Es könnte etwas los mit Spaltenort sein, die nicht strikt Spur von Spalten zu halten, sondern hält nur erhöhen. Das ist nur meine Unwissenheit und appologize wenn es jemand verwirrt. Ich bin derzeit Spalte eine Datei Zeichenzahl zu halten, was in meinem Fall günstiger als Spaltenort ist.

Ich hoffe, das hilft.

Weder bison noch flex Updates yylloc automatisch, aber es ist eigentlich nicht schwer, es selbst zu tun-wenn Sie kennen den Trick.

Der Trick yylloc Unterstützung der Implementierung ist, dass, obwohl yyparse() yylloc erklärt, es ändert sich nie. Das bedeutet, dass, wenn Sie yylloc in einem Aufruf an die Lexer ändern, werden Sie die gleichen Werte in es beim nächsten Aufruf finden. Somit wird yylloc die Position des letzten Token enthalten. Seit dem Ende der letzten Token die gleichen wie die aktuellen Token des Start ist, können Sie den alten yylloc Wert verwenden, um Ihnen den neuen Wert bestimmen zu helfen.

Mit anderen Worten, yylex() sollte nicht berechnet yylloc; es sollte update yylloc.

So aktualisieren yylloc, müssen wir zuerst die last_ Werte kopieren, first_ und dann die last_ Werte aktualisieren, um die Länge der gerade abgestimmt Token zu reflektieren. (Dies ist nicht der strlen() des Tokens, es ist die Linien-und-Spalten Länge.) Wir können das Makro in dem YY_USER_ACTION tun können, die kurz vor jeder Lexer Aktion aufgerufen wird, durchgeführt wird; die sicherstellt, dass, wenn eine Regel zu, aber es hat keinen Wert zurück (zum Beispiel eine Regel Skipping Leerzeichen oder Kommentare), die Position der Nicht-Token übersprungen wird, eher als zu Beginn des eigentlichen Token enthalten ist, oder in einer Art und Weise verloren, die die Position Tracking ungenau.

macht

Hier ist eine Version für einen Reentry-Parser gemeint; Sie es für einen Nicht-Reentry-Parser ändern könnte durch Vertauschen der -> Operatoren für .:

#define YY_USER_ACTION \
    yylloc->first_line = yylloc->last_line; \
    yylloc->first_column = yylloc->last_column; \
    for(int i = 0; yytext[i] != '\0'; i++) { \
        if(yytext[i] == '\n') { \
            yylloc->last_line++; \
            yylloc->last_column = 0; \
        } \
        else { \
            yylloc->last_column++; \
        } \
    }

Wenn Sie es vorziehen, können Sie stattdessen diesen Code in einer Funktion setzen könnten und den Makro Anruf die Funktion, aber die beiden Techniken sind gleichwertig.

Shomi Antwort ist die einfachste Lösung, wenn Sie nur über das Halten Sie die Zeilennummer kümmern. Wenn Sie jedoch auch Spaltennummern wollen, dann müssen Sie den Überblick behalten.

Eine Möglichkeit, das zu tun ist yycolumn = 1 Regeln überall ein Newline hinzufügen erscheint (wie in David Elson Antwort vorgeschlagen), aber wenn Sie möchten, don Spur halten von allen Orten ein Newline oben zeigen konnte (Leerzeichen, Kommentare usw. ..) eine Alternative ist die yytext Puffer zu Beginn jeder Aktion Inspektion:

static void update_loc(){
  static int curr_line = 1;
  static int curr_col  = 1;

  yylloc.first_line   = curr_line;
  yylloc.first_column = curr_col;

  {char * s; for(s = yytext; *s != '\0'; s++){
    if(*s == '\n'){
      curr_line++;
      curr_col = 1;
    }else{
      curr_col++;
    }
  }}

  yylloc.last_line   = curr_line;
  yylloc.last_column = curr_col-1;
}

#define YY_USER_ACTION update_loc();

Schließlich ist eine Sache zu beachten, dass, wenn Sie die Verfolgung von Spaltennummern von Hand starten könnte man genauso gut auch den Überblick über die Zeilennummern in der gleichen Stelle halten und sie nicht die Mühe mit Flex yylineno Option.

Also, ich habe diese zu „arbeiten“, aber mit ein paar zusätzliche Schritte (Vielleicht habe ich sie hier übersehen ... Entschuldigungen in diesem Fall):

  1. In parser.y , musste ich sagen:

    #define YYLEX_PARAM &yylval, &yylloc
    

    sogar mit %locations und bison --locations, bekommen sie die Daten zu übergeben.

  2. In lexer.l Ich hatte -> statt . für yylloc verwenden

  3. Auch in lexer.l , I zurückgesetzt die Spalte in der Aktion:

    [\n] { yycolumn = 1; }
    

Offensichtlich ein wenig komplexer, für \r etc, aber zumindest habe ich es zu arbeiten.

Ich denke, dass ich es geschafft, um es (funktioniert Kredit geht an den Autor des Bisons Handbuch ltcalc Lexer ). Standardmäßig erstellt Bison yylloc, die enthält

{ first_line, first_column , last_line , last_column }

Wir brauchen nur die Werte in unserem Lexer zu aktualisieren. Ex:

[ \t]     { ++yylloc.last_column; }
[\n]      { yyloc.last_column = 0; return EOL; }
[a-zA-Z]+ { 
            yylloc.last_column += strlen(yytext);
            return IDENTIFIER;
          }

Jetzt in Bison, die Felder abrufen:

statement : IDENTIFIER '=' expression 
            { printf("%d - %d\n", @1.last_line, @1.last_column); }

Standardmäßig diese Felder auf Eins initialisiert werden, sollten wir die Spaltenfelder auf Null sonst initialisiert werden sie in die falsche Spalte melden.

Eine Ergänzung zu Shlomi Antwort:

Wenn Sie mit% definiert api.pure in Bison einen Reentry-Parser zu erstellen, müssen Sie auch% Option Bison-Standorte in flex angeben. Dies liegt daran, in einem yylloc Reentry-Parser ist nicht eine globale Variable, und muss in die Lexer übergeben werden.

Also, im Parser:

%define api.pure
%locations

in der Lexer:

#include "yourprser.tab.h"
#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno;
%option bison-locations
%option yylineno
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top