2009-04-23 81 views
49

我想创建一个使用flex/bison分析器的读取 - 评估打印循环。麻烦的是,flex生成的词法分析器需要FILE *类型的输入,我希望它是char *。无论如何要做到这一点?字符串输入到flex词法分析器

一个建议是创建一个管道,为其提供字符串并打开文件描述符并发送给词法分析器。这很简单,但它感觉很复杂,不是很平台独立。有没有更好的办法?

回答

52

以下例行程序可用于对扫描存储器内的字符串的文件,而不是设定输入缓冲器(如yy_create_buffer一样):

  • YY_BUFFER_STATE yy_scan_string(const char *str):扫描string` NUL封端
  • YY_BUFFER_STATE yy_scan_bytes(const char *bytes, int len):扫描从位置字节开始的len字节(包括可能的NUL)

请注意,这两个函数都会创建,返回相应的YY_BUFFER_STATE句柄(必须使用yy_delet e_buffer()完成后)yylex()扫描字符串或字节的副本。这种行为可能是需要的,因为yylex()修改了它正在扫描的缓冲区的内容)。

如果你想使用避免复制(和yy_delete_buffer):

  • YY_BUFFER_STATE yy_scan_buffer(char *base, yy_size_t size)

样本主:

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

嘿dfa(考虑到它的灵活性,这是适合的)请问您可以添加一些关于双空要求的内容? – 2013-09-08 09:06:04

+3

由于`yy_scan_buffer`需要一个可写缓冲区(它临时修改缓冲区,插入NUL来终止`yytext`,然后恢复原始字符),所以样本main可能会失败,并且它需要两个终止NUL字节。 – 2014-05-08 16:02:15

+0

顺便说一句,在我的C++程序中,我需要用`size_t len`参数声明`yy_scan_bytes`以避免链接器错误。 – coldfix 2015-05-05 15:42:38

17

有关如何扫描内存缓冲区(如字符串)的信息,请参阅Flex手册的this section

+0

哦,男人,我不能相信我错过了。 – bjorns 2009-04-23 14:09:15

+0

http://ftp.gnu.org/old-gnu/Manuals/flex-2.5.4/html_node/flex_12.html – Bastiano9 2015-07-17 23:58:18

8

这是我需要做的:

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

你不能extern typedef,这在你思考时是有意义的。 yy_scan_string()yy_scan_buffer(),和yy_scan_bytes()(见documentation):

8

挠曲可以使用三个中的任一个的功能解析char *。这是第一个示例:

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

yy_scan_buffer()等效语句(这需要一个双空值终止字符串):

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

我的回答重申了一些由@dfa和提供的信息@ jlholland,但他们的答案的代码似乎都不适合我。

1

其他方式,您可以在lex文件中重新定义函数YY_INPUT,然后将您的字符串设置为LEX的输入。如下:

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

在你的主。c,在扫描之前,您需要首先设置lex缓冲区:

set_lexbuf(your_string); 
scanning... 
3

接受的答案不正确。它会导致内存泄漏。

在内部,yy_scan_string调用yy_scan_bytes,yy_scan_bytes又调用yy_scan_buffer。

yy_scan_bytes为输入缓冲区的COPY分配内存。

yy_scan_buffer直接在提供的缓冲区上工作。使用这三种形式,你必须调用yy_delete_buffer来释放flex缓冲区状态信息(YY_BUFFER_STATE)。

但是,使用yy_scan_buffer可以避免内部缓冲区的内部分配/复制/释放。

yy_scan_buffer的原型不接受const char *,你不能期望内容保持不变。

如果你分配内存来保存你的字符串,你负责在你调用yy_delete_buffer之后释放它。

此外,不要忘记当你解析JUST这个字符串时yywrap返回1(非零)。

下面是一个完整的例子。

%% 

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

这里是使用野牛/柔性的解析器您的CPP代码中,根据它解析字符串,并改变一个字符串值 (代码的几个部分被去除,从而有可能是不相关部分的一个小例子有。) 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