c - 如何使用 yacc 从 char 数组中解析?

标签 c yacc lex bnf

我正在尝试从缓冲区解析字符串。

莱克斯代码

%{
#include <stdio.h>
#include "y.tab.h"
%}

%%
"Type:[0-9]+" {
            printf("lex-TYPE\n");
            return TYPE;
        };
%%

Yacc代码

%{
#include <stdio.h>
#include <string.h>
extern char *yytext;
%}

%start general

%token TYPE

%%
general: |
        general TYPE {
                            printf("gen %c\n", yytext[strlen("TYPE:")]);
                        }
        ;
%%

C代码

#include <stdio.h>
#include "y.tab.h"

int main()
{
    printf("6\n");
    yy_scan_buffer("TYPE:0 ");
    printf("8\n");
//    yylex();
    yyparse();

    return 0;
}

void yyerror(char *s)
{
fprintf( stderr, "%s\n" ,s);
}

int yywrap()
{
    return(1);
}

编译

 # lex bnf.lex 
 # yacc -d bnf.yacc
# cc main.c y.tab.c lex.yy.c -o test
# ./test 

输出:

6
8
TYPE:)   <--this is input from keyboard
TYPE:)   <--I don't know why it is copied
^C

我没有看到它识别我的词素,我不明白为什么它不从 yy_scan_buffer 上传我的缓冲区?

我想把不同的字符串作为参数,解析它并做一些魔术。

你能帮帮我吗?

#UPD

C代码

#include <stdio.h>
#include "y.tab.h"

int main()
{
    printf("6\n");
    yyscan_t scanner;
    YY_BUFFER_STATE buf;
    yylex_init(&scanner);
    buf = yy_scan_string("TYPE:102 ", scanner);
    yylex(scanner);
    yy_delete_buffer(buf, scanner);
    yylex_destroy(scanner);
    printf("8\n");
    yylex();
//    yyparse();

    return 0;
}

输出

# cc main.c y.tab.c lex.yy.c -o test
main.c: In function ‘main’:
main.c:7: error: ‘yyscan_t’ undeclared (first use in this function)
main.c:7: error: (Each undeclared identifier is reported only once
main.c:7: error: for each function it appears in.)
main.c:7: error: expected ‘;’ before ‘scanner’
main.c:8: error: ‘YY_BUFFER_STATE’ undeclared (first use in this function)
main.c:8: error: expected ‘;’ before ‘buf’
main.c:9: error: ‘scanner’ undeclared (first use in this function)
main.c:10: error: ‘buf’ undeclared (first use in this function)

这是第二种将字符串传递给yacc的方法,但它有很多错误,我不知道为什么会出现。

你能帮帮我吗?

#UPD

# lex --version
flex 2.5.35
# yacc --version
bison (GNU Bison) 2.3

更新

再试一次

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()
{
    printf("6\n");
    char string[] = "TYPE:12";
    YY_BUFFER_STATE buffer = yy_scan_string(string);
    yyparse();
    yy_delete_buffer(buffer);
    printf("8\n");
//    yylex();
//    yyparse();

    return 0;
}

输出

# ./test 
6
TYPE:128
# 

因此,它没有找到任何词素。为什么?

更新

在 John Bollinger 的回答后,我用他的替换了我的 .lex 和 .c 文件。

删除了类型:[0-9]+

添加到 lex

Type:       {
            printf("lex-TYPE\n");
            return TYPE;
            };
[0-9]+ {
        printf("lex-D\n");
        return DIGIT;
        };

更改了 yacc

%%
general: |
        general TYPE DIGIT {
                            printf("gen %c\n", yytext[0]);
                        }
        ;
%%

现在我明白了

# ./test 
6
8
TYPE:lex-D
syntax error

所以,我终于匹配了模式,但为什么失败了?

最佳答案

TYPE:)   <--this is input from keyboard
TYPE:)   <--I don't know why it is copied

这里似乎有两个问题:

  • 为什么读取的输入是从键盘而不是指定的缓冲区

    • 因为你没有正确设置内存缓冲区,并且
    • 因为您没有正确调用 yy_scan_buffer()
  • 为什么输入回显到输出:

    因为它不匹配任何词法分析器规则,并且因为在提供的扫描器定义中未覆盖的默认规则将其他不匹配的字符写入标准输出。

更详细:

The docs for yy_scan_buffer()指定它

scans in place the buffer starting at base, consisting of size bytes, the last two bytes of which must be YY_END_OF_BUFFER_CHAR (ASCII NUL). These last two bytes are not scanned; thus, scanning consists of base[0] through base[size-2], inclusive.

If you fail to set up base in this manner (i.e., forget the final two YY_END_OF_BUFFER_CHAR bytes), then yy_scan_buffer() returns a NULL pointer instead of creating a new input buffer.

(强调已添加。)

您没有确保所提供缓冲区的最后两个 字符是YY_END_OF_BUFFER_CHAR,缓冲区设置不正确。如果您检查了函数的返回值,您就会发现这个问题,对于可能会失败的函数,您应该始终这样做,并通过它们的返回值(很多)来告知这一点。

此外,yy_scan_buffer() 需要两个 参数,第一个是指向缓冲区的指针,第二个是缓冲区的有效大小。尽管如此,您通过使用错误数量的参数调用它会引发未定义的行为。也许您正在寻找 yy_scan_string()

此外,您的编译器应该警告您调用之前未声明的函数。目前还不清楚任何缓冲区操作机制在多大程度上是为了在外部进行调整,而不是通过你的扫描器规则,但至少你应该限制自己在扫描器定义(包括用户代码部分)中使用它们).

至于为什么输入不匹配,其实有两个问题。首先,你引用了模式。引号并不特殊,因此 Flex 正在寻找包含文字引号的输入。其次,您的输入不包含与模式的 [0-9]+ 部分匹配的内容。这些中的任何一个本身都足以防止显示的输入(或内存缓冲区的预期内容)匹配。

下面是一个扫描器定义的版本,它与主 C 源文件中的补充代码相结合,看起来就像您想要的那样:

%{
#include <stdio.h>
#include "y.tab.h"
%}

%%
Type:[0-9]+ {
            printf("lex-TYPE\n");
            return TYPE;
        };
%%

static YY_BUFFER_STATE my_string_buffer;

int my_scan_string(const char *s) {
    // insist on cleaning up any existing buffer before setting up a new one
    if (my_string_buffer != NULL) return -1;

    // Set up and switch to a buffer for scanning the contents of the
    // specified string.  A copy of the string will be made.
    my_string_buffer = yy_scan_string(s);
    return (my_string_buffer == NULL) ? -1 : 0;
}

void my_cleanup(void) {
    // No effect if my_string_buffer is NULL
    yy_delete_buffer(my_string_buffer);
    // ... but avoid trying to free the same buffer twice
    my_string_buffer = NULL;
}

请注意,单个扫描器规则中的模式已更正,但更重要的是,依赖于扫描器内部接口(interface)的代码出现在扫描器定义的“用户代码”部分中。这确保您无需猜测或复制内部接口(interface)。

这不需要对您的解析器定义进行任何更改,但它确实需要更改您的主要源文件:

#include <stdio.h>
#include "y.tab.h"

int my_scan_string(const char *s);
void my_cleanup(void);

int main()
{
    printf("6\n");
    if (my_scan_string("TYPE:0 ") != 0) {
        fputs("error setting up an internal buffer\n", stderr);
        exit(1);
    }
    printf("8\n");
    yyparse();
    my_cleanup();

    return 0;
}

void yyerror(char *s) {
    fprintf(stderr, "%s\n" ,s);
}

int yywrap(void) {
    return 1;
}

关于c - 如何使用 yacc 从 char 数组中解析?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57524286/

相关文章:

c - 如何读取字符串 "500 600"以在 C 中存储两个值分别为 500 和 600 的变量?

c - sqlite 中的表名长度会影响性能。为什么?

c - 为什么 yytext 会跳过 YACC 中的第一个输入?

macros - 在 yacc/bison lex 中处理#define 宏

c:段错误(选择排序)

c: 未知类型名称

使用 yacc 或 Bison 和 Flex 创建 foreach 关键字

php - PHP 中的 Lex 和 Yacc

c - 如何从 lex/yacc 生成不同的 yyparse 函数以用于同一程序?

c - 动态打开和关闭 flex token