2009-03-18 8 views
16

Sto cercando di usare flex e bison per creare un filtro, perché voglio ottenere determinati elementi grammaticali da un linguaggio complesso. Il mio piano è usare flex + bison per riconoscere la grammatica e scaricare la posizione degli elementi di interesse. (Quindi utilizzare uno script per afferrare il testo in base alle posizioni scaricate.)In che modo flex supporta esattamente la posizione del bisonte?

Ho trovato flex in grado di supportare una funzione di bisonte chiamata posizione di bisonte, ma come funziona esattamente. Ho provato l'esempio nel documento flex, sembra che yylloc non sia impostato automaticamente da flex, ottengo sempre (1,0)-(1,0). Potrebbe flettere calcolare automaticamente la posizione di ciascun token? In caso contrario, quale funzione di interfaccia è definita per me da implementare? C'è qualche esempio?

Qualunque soluzione migliore per gli strumenti?

migliori saluti, Kevin

Edit:

Ora l'interfaccia per turno yylex a:

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

manuale bisonti non specifica come lexer dovrebbe attuare per impostare correttamente yylloc_param. Per me è difficile tracciare manualmente il numero di colonna di ciascun token.

risposta

6

Dai un'occhiata alla sezione 3.6 of the Bison manual - che sembra coprire i luoghi in dettaglio. In combinazione con ciò che hai trovato nel manuale Flex, potrebbe essere sufficiente.

+0

ho pensato che solo numero di riga è di importazione per me. –

15

La dichiarazione dello yylex probabilmente è cambiata perché è stato utilizzato un reentrant o un parser puro. Sembra che molti documenti sul Web suggeriscano che è necessario se si desidera che le ubicazioni dei bisonti funzionino, ma non è necessario.

Avevo bisogno anche dei numeri di riga e ho trovato la documentazione di Bison confusa al riguardo. La soluzione più semplice (utilizzando il var yylloc globale): Nel file Bison è sufficiente aggiungere la direttiva% posizioni:

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

nel vostro 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 
... 
%% 
... 

La macro YY_USER_ACTION è "chiamato" prima ciascuna delle tue azioni token e aggiornamenti yylloc. Ora è possibile utilizzare i @N/@ $ regole in questo modo:

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

, o utilizzare il yylloc var globale:

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

Non penso che sia abbastanza. Ho provato questo e sempre ottenere un errore non dichiarato 'yylloc 'quando provo a costruire. Deve esserci qualcos'altro che devi fare per abilitare yylloc. – Mike

+1

Hai aggiunto la direttiva% locations? Hai incluso il file .tab.h generato nel lexer? Forse stai usando versioni molto vecchie di bison + flex? Funziona per me con Bison 2.4.1 e Flex 2.5.35. –

11

mi piace la risposta di Shlomi.

Inoltre, stavo cercando di aggiornare anche la posizione della colonna. Trovato http://oreilly.com/linux/excerpts/9780596155971/error-reporting-recovery.html che ha avuto più senso dopo aver letto la risposta di Shlomi.

Purtroppo c'è un errore di battitura in quella pagina per yylloc. L'ho semplificato sotto un po '.

Nel vostro parser aggiungere:

%locations 

nel vostro 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 

Ci potrebbe essere qualcosa da fare con la posizione della colonna che non tiene strettamente traccia delle colonne, ma piuttosto solo continua ad aumentare . Questa è solo la mia ignoranza e mi chiedo se confonda qualcuno. Attualmente sto usando la colonna per mantenere un numero di caratteri del file che nel mio caso è più vantaggioso della posizione della colonna.

Spero che questo aiuti.

+0

Grazie mille per aver postato questo. – Dilawar

+1

Il motivo per cui il numero della colonna continua ad aumentare è perché non lo si imposta mai su 1 su una nuova riga e nessuno su Flex, poiché non sa nemmeno della variabile 'yycolumn'. Apparentemente quello che devi fare è tenere traccia delle newline da te stesso invece di affidarti all'opzione '% yylineno'. – hugomg

+0

Non 'yylval.str = strdup (yytext)' imposta il contenuto del token Bison? Vorresti solo che questa fosse l'azione predefinita in una regola se ogni token fosse una stringa, no? –

4

Così, ho ottenuto questo al "lavoro", ma con un paio di passaggi aggiuntivi (io li ho trascurato qui ... scuse in questo caso):

  1. In parser.y, ho dovuto dire:

    #define YYLEX_PARAM &yylval, &yylloc 
    

    anche con %locations e bison --locations, per farlo passare i dati.

  2. In lexer.l ho dovuto usare -> invece di . per yylloc

  3. Anche in lexer.l, io ripristinare la colonna nell'azione:

    [\n] { yycolumn = 1; } 
    

Ovviamente y un po 'più complesso, per \r ecc, ma almeno ho avuto modo di funzionare.

+0

Aggiunta di --locazioni alla riga di comando OPPURE% posizioni dopo la grammatica significherà che yylloc rientra nel campo del file .y, a patto che ci si riferisca al codice nella sezione finale "%%". –

6

La risposta di Shomi è la soluzione più semplice se ti interessa solo mantenere il numero di riga. Tuttavia, se vuoi anche i numeri di colonna, devi tenerne traccia.

Un modo per fare ciò è aggiungere le regole yycolumn = 1 ovunque venga visualizzata una nuova riga (come suggerito nella risposta di David Elson) ma se si vuole tenere traccia di tutti i posti che potrebbero apparire una nuova riga (spazi, commenti, ecc. ...) in alternativa sta ispezionando il buffer yytext all'inizio di ogni azione:

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(); 

Infine, una cosa da notare è che una volta che si inizia a tenere traccia dei numeri di colonna a mano si potrebbe anche tenere anche traccia dei numeri di riga nello stesso posto e non preoccuparsi dell'utilizzo dell'opzione Flex yylineno.

9

bisonflex aggiornamenti yylloc automaticamente, ma in realtà non è difficile farlo da soli, se si conosce il trucco.

Il trucco per implementare il supporto yylloc è che, anche se yyparse() dichiara yylloc, non lo modifica mai.Ciò significa che se modifichi yylloc in una chiamata al lexer, troverai gli stessi valori nella chiamata successiva. Pertanto, yylloc conterrà la posizione dell'ultimo token. Poiché l'ultima estremità del token è la stessa dell'inizio del token corrente, puoi utilizzare il vecchio valore yylloc per aiutarti a determinare il nuovo valore.

In altre parole, yylex() non deve calcolareyylloc; dovrebbe aggiornamentoyylloc.

Per aggiornare yylloc, dobbiamo prima copiare i valori last_ per first_, e quindi aggiornare i valori last_ per riflettere la lunghezza del token appena abbinato. (Questo non è lo strlen() del token, ma è la lunghezza delle righe e delle colonne.) Possiamo farlo nella macro YY_USER_ACTION, che viene chiamata appena prima che venga eseguita un'azione di lexer; che assicura che se una regola corrisponde ma non restituisce un valore (ad esempio, una regola che salta spazi bianchi o commenti), la posizione di quel non-token viene saltata, piuttosto che essere inclusa all'inizio del token effettivo, o perso in un modo che rende impreciso il rilevamento della posizione.

Ecco una versione destinata a un parser rientrante; si potrebbe modificarlo per un parser non rientrante scambiando le -> operatori per .:

#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++; \ 
     } \ 
    } 

Se preferite, si potrebbe invece mettere quel codice in una funzione ed effettuare la chiamata alla funzione macro, ma il due tecniche sono equivalenti.

+0

Molto più utile delle altre risposte, anche se suggerirei di creare una funzione e chiamarla semplicemente nella macro - 'static void update_loc (YYLTYPE * loc, char * txt) {loc-> first_line = ...}'/'#define YY_USER_ACTION update_loc (yylloc, yytext);'. – Kevin

+0

Non importa quale sia la posizione, si scopre che ho una regola che corrisponde all'intera riga e la 'REJECT'ed. – Kevin

+0

Questa è di gran lunga la risposta più utile e IMO dovrebbe essere accettata – cqcallaw

1

Penso di essere riuscito a farlo funzionare (il merito va allo scrittore del manuale bisonte ltcalc lexical analyzer). Per impostazione predefinita, bisonti crea yylloc che contiene

{ first_line, first_column , last_line , last_column } 

Abbiamo solo bisogno di aggiornare i valori nel nostro analizzatore lessicale. Es:

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

Ora a bisonti, per recuperare quei campi:

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

Per impostazione predefinita questi campi vengono inizializzati a uno, dovremmo inizializzare i campi colonna a zero altrimenti saranno segnalare la colonna sbagliata.

0

Un'aggiunta alla risposta di Shlomi:

Se stai usando% definiscono api.pure in bisonti per creare un parser rientrante, è inoltre necessario specificare l'opzione% bisonti-sedi in flessione. Questo perché in un parser rientrante yylloc non è una variabile globale e deve essere passato al lexer.

Così, nel parser:

%define api.pure 
%locations 

nel lexer:

#include "yourprser.tab.h" 
#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno; 
%option bison-locations 
%option yylineno 
Problemi correlati