Entrada de string para flexionar lexer

Eu quero criar um loop de leitura-eval-impressão usando o analisador de flex / bison. O problema é que o lexer gerado flex requer input do tipo FILE * e eu gostaria que ele fosse char *. Há alguma maneira de fazer isso?

Uma sugestão foi criar um pipe, alimentar a string e abrir o descritor de arquivo e enviá-lo ao lexer. Isso é bastante simples, mas parece complicado e não é muito independente da plataforma. Existe uma maneira melhor?

As rotinas a seguir estão disponíveis para configurar buffers de input para varredura de strings em memory em vez de arquivos (como faz o yy_create_buffer):

  • YY_BUFFER_STATE yy_scan_string(const char *str) : varre uma string terminada em NUL
  • YY_BUFFER_STATE yy_scan_bytes(const char *bytes, int len) : verifica len bytes (incluindo possivelmente NULs) iniciando em bytes de localização

Observe que ambas as funções criam, retornam um identificador YY_BUFFER_STATE correspondente (que você deve excluir com yy_delete_buffer () quando terminar com ele) para que yylex () digitalize uma cópia da cadeia ou dos bytes. Esse comportamento pode ser desejável, pois yylex () modifica o conteúdo do buffer que está sendo varrido.

Se você quiser evitar a cópia (e yy_delete_buffer) usando:

  • YY_BUFFER_STATE yy_scan_buffer(char *base, yy_size_t size)

amostra principal:

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

Consulte esta seção do manual do Flex para obter informações sobre como verificar buffers na memory, como seqüências de caracteres.

O flex pode analisar o char * usando qualquer uma das três funções: yy_scan_string() , yy_scan_buffer() e yy_scan_bytes() (veja a documentação ). Aqui está um exemplo do primeiro:

 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; } 

As instruções equivalentes para yy_scan_buffer() (que requer uma string duplamente terminada com nulo):

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

Minha resposta reitera algumas das informações fornecidas por @dfa e @jlholland, mas nenhum dos códigos de resposta deles parecia estar funcionando para mim.

Aqui está o que eu precisava fazer:

 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; } 

você não pode externar o typedef, o que faz sentido quando você pensa sobre isso.

A resposta aceita está incorreta. Isso causará vazamentos de memory.

Internamente, yy_scan_string chama yy_scan_bytes que, por sua vez, chama yy_scan_buffer.

yy_scan_bytes aloca memory para um COPY do buffer de input.

yy_scan_buffer funciona diretamente no buffer fornecido.

Com todos os três formulários, você DEVE chamar yy_delete_buffer para liberar as informações do estado do buffer flexível (YY_BUFFER_STATE).

No entanto, com yy_scan_buffer, você evita a alocação interna / cópia / livre do buffer interno.

O protótipo para yy_scan_buffer NÃO leva um const char * e você NÃO DEVE esperar que o conteúdo permaneça inalterado.

Se você alocou memory para manter sua string, você é responsável por liberá-la DEPOIS de chamar yy_delete_buffer.

Além disso, não se esqueça de ter o yywrap return 1 (diferente de zero) quando estiver analisando APENAS esta string.

Abaixo está um exemplo COMPLETO.

 %% <> 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 (ie, 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); } 

De outra forma, você pode redefinir a function YY_INPUT no arquivo lex e, em seguida, definir sua string para a input de LEX. Como abaixo:

 #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); } 

No seu main.c, antes de digitalizar, você precisa definir o buffer do lex primeiro:

 set_lexbuf(your_string); scanning... 

Aqui está um pequeno exemplo para usar bison / flex como um analisador dentro de seu código cpp para analisar a string e alterar um valor de string de acordo com ele (poucas partes do código foram removidas, então pode haver partes irrelevantes lá). parser.y:

 %{ #include "parser.h" #include "lex.h" #include  #include  #include  #include  #include  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  #include  #include  #include  #include  } %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  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  #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  } 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 
    Intereting Posts