2009-04-23 14 views
49

Chcę utworzyć pętlę read-eval-print przy użyciu parsera flex/bison. Problem polega na tym, że generowany przez flexy lexer chce wejść typu PLIK * i chciałbym aby był to znak *. Czy jest to mimo wszystko?Wprowadzanie ciągu znaków do modułu elastycznego lexer

Jedną z sugestii było utworzenie potoku, podanie go, otwarcie deskryptora pliku i wysłanie do lexera. Jest to dość proste, ale czuje się zawiłe i nie jest niezależne od platformy. Czy istnieje lepszy sposób?

Odpowiedz

52

Poniższe procedury są dostępne do konfigurowania buforów wejściowych do skanowania w pamięci ciągi zamiast plików (jak yy_create_buffer robi):

  • YY_BUFFER_STATE yy_scan_string(const char *str): skanuje znakiem NUL string`
  • YY_BUFFER_STATE yy_scan_bytes(const char *bytes, int len): skany len bajtów (w tym ewentualnie nuls) zaczynając od lokalizacji bajtów

należy zauważyć, że obie te funkcje create, powrót uchwyt odpowiedni YY_BUFFER_STATE (który trzeba usunąć z yy_delet e_buffer() po zakończeniu), yylex() skanuje kopię ciągu lub bajtów. Takie zachowanie może być pożądane, ponieważ yylex() modyfikuje zawartość bufora, który skanuje).

Jeśli chcesz uniknąć kopię (i yy_delete_buffer) przy użyciu:

  • YY_BUFFER_STATE yy_scan_buffer(char *base, yy_size_t size)

próbka główny:

int main() { 
    yy_scan_buffer("a test string"); 
    yylex(); 
} 
+0

Hej dfa (który jest odpowiedni biorąc pod uwagę jego flex) czy mógłbyś dodać coś o wymogu podwójnej wartości zerowej ? –

+3

Najprawdopodobniej główna próba zakończy się niepowodzeniem, ponieważ 'yy_scan_buffer' wymaga zapisywalnego bufora (tymczasowo modyfikuje bufor, wstawia NUL-y, aby zakończyć' yytext', a następnie przywraca oryginalne znaki), I wymaga DWÓCH kończących bajtów NUL. –

+0

Przy okazji, w moim programie C++, musiałem zadeklarować 'yy_scan_bytes' z parametrem' size_t len', aby uniknąć błędów linkera. – coldfix

17

Zapoznaj się z this section podręcznika Flex, aby uzyskać informacje na temat skanowania buforów w pamięci, takich jak łańcuchy.

+0

Oh człowieku, nie mogę uwierzyć, że to przegapiłem. – bjorns

+0

http://ftp.gnu.org/old-gnu/Manuals/flex-2.5.4/html_node/flex_12.html – Bastiano9

8

Oto, co potrzebne do zrobienia:

extern yy_buffer_state; 
typedef yy_buffer_state *YY_BUFFER_STATE; 
extern int yyparse(); 
extern YY_BUFFER_STATE yy_scan_buffer(char *, size_t); 

int main(int argc, char** argv) { 

    char tstr[] = "line i want to parse\n\0\0"; 
    // note yy_scan_buffer is is looking for a double null string 
    yy_scan_buffer(tstr, sizeof(tstr)); 
    yy_parse(); 
    return 0; 
} 

nie można pominąć typedef, co ma sens, gdy się nad tym zastanowić.

+1

Nie można wykonać polecenia zewnętrznego bez zdefiniowanego typu [linia 1]. –

+1

Należy tylko określić pojedyncze '\ 0'', jako że drugie jest niejawnie (z powodu literału). – a3f

8

Flex można analizować char * użyciu jednego z trzech funkcji: yy_scan_string(), yy_scan_buffer() i yy_scan_bytes() (patrz documentation). Oto przykład pierwszy:

typedef struct yy_buffer_state * YY_BUFFER_STATE; 
extern int yyparse(); 
extern YY_BUFFER_STATE yy_scan_string(char * str); 
extern void yy_delete_buffer(YY_BUFFER_STATE buffer); 

int main(){ 
    char string[] = "String to be parsed."; 
    YY_BUFFER_STATE buffer = yy_scan_string(string); 
    yyparse(); 
    yy_delete_buffer(buffer); 
    return 0; 
} 

Odpowiedniki wypowiedzi dla yy_scan_buffer() (co wymaga podwójnie nul String):

char string[] = "String to be parsed.\0"; 
YY_BUFFER_STATE buffer = yy_scan_buffer(string, sizeof(string)); 

Moja odpowiedź powtarza niektóre z informacji dostarczonych przez @dfa i @ jlholland, ale żaden z kodów ich odpowiedzi nie działał dla mnie.

+1

jak rozpozna, co to jest struct yy_buffer_state? –

+0

Nie będzie, * ale nie obchodzi *. Wszystko, co kiedykolwiek robimy, to deklarowanie [nieprzezroczystych wskaźników] (http://pl.wikipedia.org/wiki/Opaque_pointer) do nieznanej struktury o nazwie 'yy_buffer_state', którą kompilator wie, że ma szerokość 4 bajtów (lub cokolwiek innego wskaźnik w twoim systemie size is): ponieważ nigdy nie uzyskujemy dostępu do żadnej z jego zmiennych składowych, nie trzeba wiedzieć o składzie tej struktury. Możesz zastąpić 'struct yy_buffer_state' w' typedef' przez 'void' lub' int' lub cokolwiek, ponieważ rozmiar wskaźnika będzie taki sam dla każdego. – sevko

1

W inny sposób, możesz przedefiniować funkcję YY_INPUT w pliku lex, a następnie ustawić swój ciąg na wejście LEX. Jak poniżej:

#undef YY_INPUT 
#define YY_INPUT(buf) (my_yyinput(buf)) 

char my_buf[20]; 

void set_lexbuf(char *org_str) 
{ strcpy(my_buf, org_str); } 

void my_yyinput (char *buf) 
{ strcpy(buf, my_buf);  } 

W głównym.c, przed skanowaniem musisz najpierw ustawić bufor lex:

set_lexbuf(your_string); 
scanning... 
3

Przyjęta odpowiedź jest niepoprawna. Spowoduje to wycieki pamięci.

Wewnętrznie, yy_scan_string wywołuje yy_scan_bytes, które z kolei wywołuje yy_scan_buffer.

yy_scan_bytes przydziela pamięć dla KOPIOWANIA bufora wejściowego.

yy_scan_buffer działa bezpośrednio na dostarczonym buforze.

We wszystkich trzech formularzach MUSISZ wywołać yy_delete_buffer, aby zwolnić informacje o stanie elastycznego bufora (YY_BUFFER_STATE).

Jednak przy pomocy yy_scan_buffer unika się wewnętrznego przydziału/kopiowania/usuwania wewnętrznego bufora.

Prototyp dla yy_scan_buffer NIE przyjmuje oznaczenia const * i NIE NALEŻY oczekiwać, że zawartość pozostanie niezmieniona.

Jeśli przeznaczyłeś pamięć na ciąg znaków, jesteś odpowiedzialny za jej zwolnienie PO POWINIENU zadzwonić yy_delete_buffer.

Pamiętaj również, aby mieć yywrap powrót 1 (niezerowy) podczas analizowania JUST ten ciąg.

Poniżej znajduje się przykład CAŁKOWICIE.

%% 

<<EOF>> return 0; 

. return 1; 

%% 

int yywrap() 
{ 
    return (1); 
} 

int main(int argc, const char* const argv[]) 
{ 
    FILE* fileHandle = fopen(argv[1], "rb"); 
    if (fileHandle == NULL) { 
     perror("fopen"); 
     return (EXIT_FAILURE); 
    } 

    fseek(fileHandle, 0, SEEK_END); 
    long fileSize = ftell(fileHandle); 
    fseek(fileHandle, 0, SEEK_SET); 

    // When using yy_scan_bytes, do not add 2 here ... 
    char *string = malloc(fileSize + 2); 

    fread(string, fileSize, sizeof(char), fileHandle); 

    fclose(fileHandle); 

    // Add the two NUL terminators, required by flex. 
    // Omit this for yy_scan_bytes(), which allocates, copies and 
    // apends these for us. 
    string[fileSize] = '\0'; 
    string[fileSize + 1] = '\0'; 

    // Our input file may contain NULs ('\0') so we MUST use 
    // yy_scan_buffer() or yy_scan_bytes(). For a normal C (NUL- 
    // terminated) string, we are better off using yy_scan_string() and 
    // letting flex manage making a copy of it so the original may be a 
    // const char (i.e., literal) string. 
    YY_BUFFER_STATE buffer = yy_scan_buffer(string, fileSize + 2); 

    // This is a flex source file, for yacc/bison call yyparse() 
    // here instead ... 
    int token; 
    do { 
     token = yylex(); // MAY modify the contents of the 'string'. 
    } while (token != 0); 

    // After flex is done, tell it to release the memory it allocated.  
    yy_delete_buffer(buffer); 

    // And now we can release our (now dirty) buffer. 
    free(string); 

    return (EXIT_SUCCESS); 
} 
+0

Masz nazywać yylex/yyparse za każdym razem? – velocirabbit

0

tutaj jest mały przykład za korzystanie bizony/Flex jako parsera wewnątrz kodu cpp do analizowania ciąg i zmienia wartość łańcuchową według niego (kilka części kodu zostały usunięte, więc nie może być części nieistotnych tam.) parser.y:

%{ 
#include "parser.h" 
#include "lex.h" 
#include <math.h> 
#include <fstream> 
#include <iostream> 
#include <string> 
#include <vector> 
using namespace std; 
int yyerror(yyscan_t scanner, string result, const char *s){ 
    (void)scanner; 
    std::cout << "yyerror : " << *s << " - " << s << std::endl; 
    return 1; 
    } 
    %} 

%code requires{ 
#define YY_TYPEDEF_YY_SCANNER_T 
typedef void * yyscan_t; 
#define YYERROR_VERBOSE 0 
#define YYMAXDEPTH 65536*1024 
#include <math.h> 
#include <fstream> 
#include <iostream> 
#include <string> 
#include <vector> 
} 
%output "parser.cpp" 
%defines "parser.h" 
%define api.pure full 
%lex-param{ yyscan_t scanner } 
%parse-param{ yyscan_t scanner } {std::string & result} 

%union { 
    std::string * sval; 
} 

%token TOKEN_ID TOKEN_ERROR TOKEN_OB TOKEN_CB TOKEN_AND TOKEN_XOR TOKEN_OR TOKEN_NOT 
%type <sval> TOKEN_ID expression unary_expression binary_expression 
%left BINARY_PRIO 
%left UNARY_PRIO 
%% 

top: 
expression {result = *$1;} 
; 
expression: 
TOKEN_ID {$$=$1; } 
| TOKEN_OB expression TOKEN_CB {$$=$2;} 
| binary_expression {$$=$1;} 
| unary_expression {$$=$1;} 
; 

unary_expression: 
TOKEN_NOT expression %prec UNARY_PRIO {result = " (NOT " + *$2 + ") " ; $$ = &result;} 
; 
binary_expression: 
expression expression %prec BINARY_PRIO {result = " (" + *$1+ " AND " + *$2 + ") "; $$ = &result;} 
| expression TOKEN_AND expression %prec BINARY_PRIO {result = " (" + *$1+ " AND " + *$3 + ") "; $$ = &result;} 
| expression TOKEN_OR expression %prec BINARY_PRIO {result = " (" + *$1 + " OR " + *$3 + ") "; $$ = &result;} 
| expression TOKEN_XOR expression %prec BINARY_PRIO {result = " (" + *$1 + " XOR " + *$3 + ") "; $$ = &result;} 
; 

%% 

lexer.l : 

%{ 
#include <string> 
#include "parser.h" 

%} 
%option outfile="lex.cpp" header-file="lex.h" 
%option noyywrap never-interactive 
%option reentrant 
%option bison-bridge 

%top{ 
/* This code goes at the "top" of the generated file. */ 
#include <stdint.h> 
} 

id  ([a-zA-Z][a-zA-Z0-9]*)+ 
white  [ \t\r] 
newline [\n] 

%% 
{id}     {  
    yylval->sval = new std::string(yytext); 
    return TOKEN_ID; 
} 
"(" {return TOKEN_OB;} 
")" {return TOKEN_CB;} 
"*" {return TOKEN_AND;} 
"^" {return TOKEN_XOR;} 
"+" {return TOKEN_OR;} 
"!" {return TOKEN_NOT;} 

{white}; // ignore white spaces 
{newline}; 
. { 
return TOKEN_ERROR; 
} 

%% 

usage : 
void parse(std::string& function) { 
    string result = ""; 
    yyscan_t scanner; 
    yylex_init_extra(NULL, &scanner); 
    YY_BUFFER_STATE state = yy_scan_string(function.c_str() , scanner); 
    yyparse(scanner,result); 
    yy_delete_buffer(state, scanner); 
    yylex_destroy(scanner); 
    function = " " + result + " "; 
} 

makefile: 
parser.h parser.cpp: parser.y 
    @ /usr/local/bison/2.7.91/bin/bison -y -d parser.y 


lex.h lex.cpp: lexer.l 
    @ /usr/local/flex/2.5.39/bin/flex lexer.l 

clean: 
    - \rm -f *.o parser.h parser.cpp lex.h lex.cpp