如何在lex中使用yy_scan_string

10

我想解析一个字符串,我将在yacc的主函数中将其传递给解析器。我知道可以使用yy_scan_string来实现,但我不知道如何使用它。我在网上和手册中搜索了,但仍然不清楚。请帮帮我。


与以下问题密切相关:https://dev59.com/7XI-5IYBdhLWcg3wTWf4 和 http://stackoverflow.com/q/1909166/15168(虽然不完全是两者之一的重复)。 - Jonathan Leffler
6个回答

20

如果有人需要可重入词法分析器的示例:

int main(void)
{
    yyscan_t scanner;
    YY_BUFFER_STATE buf;
    yylex_init(&scanner);
    buf = yy_scan_string("replace me with the string youd like to scan", scanner);
    yylex(scanner);
    yy_delete_buffer(buf, scanner);
    yylex_destroy(scanner);
    return 0;
}

2
如果其他人在尝试此操作时遇到“符号未定义”或其他类似错误,请记得在词法分析器文件中包含%option reentrant - chacham15

9
这对我很有帮助。我在Bison文件的子程序部分(即第三部分)中有以下代码:
struct eq_tree_node *parse_equation(char *str_input)
{
    struct eq_tree_node *result;

    yy_scan_string(str_input);
    yyparse();
    /* to avoid leakage */
    yylex_destroy();

    /* disregard this. it is the function that I defined to get
    the result of the parsing. */
    result = symtab_get_parse_result();

    return result;
}

1
在 bison 的第一部分中,如何声明 yy_scan_string?另外,我需要在 flex 中添加什么吗? - Ruturaj
在这种情况下,str_input 最好是 const char *。 - Ryan Li

4

这个对我有用... 使用yy_scan_string()

int main(int argc, char **argv)
{
char Command[509];
int ReturnVal;

    char input[40] = "This is my input string";

    /*Copy string into new buffer and Switch buffers*/
    yy_scan_string (input);

    /*Analyze the string*/
    yylex();

    /*Delete the new buffer*/
    yy_delete_buffer(YY_CURRENT_BUFFER);
}

3

我总是向想要学习lex/yacc(或flex/bison)的人推荐这个页面


1
刚刚我检查过了,不再有了。 - Wernsey
提供的示例未使用scan_string。虽然对于一般目的很有用,但对于这个问题不适用。 - bra_racing
1
此文档不包含任何关于yy_scan_string的参考信息。对于正在查找有关此函数信息的人来说,这个答案更具有误导性而非帮助性。 - jlanik

0

这里已经有几个好的答案了。但是对于我的目的,我需要反复在要分析的字符串缓冲区之间进行交换。问题在于,flex需要在每次处理运行后进行清理,并重置其内部解析内容/计数器等。截至撰写本文时,没有任何现有答案演示此操作。

基本上,这相当于在某个地方保留一个YY_BUFFER_STATE yy_buffer_state;,并在切换字符串时调用yy_delete_buffer(yy_buffer_state)。当使用yy_scan_string()分配新字符串进行扫描时,将生成新的YY_BUFFER_STATE,您需要跟踪它。

我尝试展示一个相当完整的示例,但关键是在底部附近的setLexerBuffer() ~

例如:

%{
#include "flex_tokens_and_yylval.h"

extern LexYYLVal yylval;                    // my custom yylval
extern YY_BUFFER_STATE yy_buffer_state;
%}

digit             [0-9]
letter            [a-zA-Z]
udderscore        "_"
sign              [+-]
period            "."

real              {sign}?({digit}*{period}{digit}+)
int               {sign}?{digit}+
identifier        ({letter}|{udderscore})+({letter}|{digit}|{udderscore})*
/* [...]  rest of the scanner rules */

%%

<<EOF>>             { return LEX_EOF; }
{real}              {
                        yylval.data.val_real = strtod( yytext, NULL ); 
                        return LEX_REAL;
                    }

{int}               {
                        yylval.data.val_integer = strtol( yytext, NULL, 10 );
                        return LEX_INTEGER;
                    }
{identifier}        {
                        strncpy( yylval.data.val_string, yytext, MAX_IDENTIFIER_LENGTH );
                        yylval.data.val_string[MAX_IDENTIFIER_LENGTH-1]='\0';
                        return LEX_IDENTIFIER;
                    }
[ \t\n\r]           { /* skip whitespace */ }
/* [...]  rest the scanner outputs */

%%

// NOT THREAD SAFE, DON'T USE FROM MULTIPLE THREADS
LexYYLVal yylval;
int yy_first_ever_run = 1;
char LexEmptyBuffer[3] = { '\n', '\0', '\0' };
YY_BUFFER_STATE yy_buffer_state;

/*
 * Point flex at a new string to process, resetting
 * any old results from a previous parse.
 */
void setLexerBuffer( const char *expression_string )
{
    /* out with the old (If any? How does flex know?) */
    if ( !yy_first_ever_run )
    {
        // This doesn't cause any issues (according to valgrind)
        // but I also don't see any reason to call it before the
        // first lex-run.
        yy_delete_buffer( yy_buffer_state );
    }
    else
    {
        yy_first_ever_run = 0;
    }

    /* just make sure we're pointing at something */
    if ( expression_string == NULL )
    {
        expression_string = LexEmptyBuffer;
    }

    /* reset the scan */    
    yy_buffer_state = yy_scan_string( expression_string );  /* auto-resets lexer state */
}

这样可以让你运行控制循环,例如:

int main( void )
{
    LexResultToken token;

    setLexerBuffer( "12.3 * 0.96" );
    do
    {
        token = yylex();
        printToken( token );
    }
    while( token != LEX_EOF );

    setLexerBuffer( "( A + B ) < ( C * D )" );
    do
    {
        token = yylex();
        printToken( token );
    }
    while( token != LEX_EOF );

    yylex_destroy();

    return 0;
}

这个例子通过valgrind进行了运行,以验证内存正确性。


-1

1
链接已损坏。 - pmw1234

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接