C语言 lemon解析器解析0标记

cld4siwp  于 2023-04-19  发布在  其他
关注(0)|答案(2)|浏览(171)

我在使用(可重入)Flex + Lemon进行解析时遇到了问题。我使用了一个简单的语法和lexer here。当我运行它时,我将输入一个数字,后跟一个EOF标记(Ctrl-D)。打印输出将显示:

89

found int of .
AST=0.

其中第一行是我输入的数字。理论上,AST值应该是我输入的所有内容的总和。

EDIT:手动调用Parse()时运行正常

另外,即使token为0(停止token),lemon似乎也会运行atom ::= INT规则。为什么会这样?我对这种行为感到非常困惑,我找不到任何好的文档。

de90aj5v

de90aj5v1#

好吧,我想通了。原因是flex和lemon之间发生了一种特别讨厌的(而且记录得很差的)交互。
为了保存内存,lemon会保留一个token而不复制,并将其推送到内部token堆栈。然而,flex也会尝试通过更改yyget_text在lexes输入时指向的值来节省内存。我的示例中的违规行是:

// in the do loop of main.c...
Parse(parser, token, yyget_text(lexer));

这应该是:

Parse(parser, token, strdup(yyget_text(lexer)));

这将确保lemon在以后减少令牌堆栈时指向的值与您最初传入的值相同。
(Note:不要忘记,strdup意味着你必须在以后的某个时候释放内存。Lemon将允许你编写可以做到这一点的令牌“析构函数”,或者如果你正在构建一个AST树,你应该等到AST生命周期结束。)

piwo6bdm

piwo6bdm2#

你也可以尝试创建一个标记类型,它包含一个指向字符串的指针 * 和字符串的长度 *。
token.h

#ifndef Token_h
#define Token_h

typedef struct Token {
    int code;
    char * string;
    int string_length;
} Token;

#endif // Token_h

main.c

int main(int argc, char** argv) {
    // Set up the scanner
    yyscan_t scanner;
    yylex_init(&scanner);
    yyset_in(stdin, scanner);

    // Set up the parser
    void* parser = ParseAlloc(malloc);
    
    // Do it!
    Token t;
    do {
        t.code = yylex(scanner);
        t.string = yyget_text(scanner);
        t.string_length = yyget_leng(scanner);
        Parse(parser, t.code, t);
    } while (t.code > 0);

    if (-1 == t.code) {
        fprintf(stderr, "The scanner encountered an error.\n");
    }

    // Cleanup the scanner and parser
    yylex_destroy(scanner);
    ParseFree(parser, free);
    return 0;
}

language.y(节选)

...

%%

...

class_interface   ::= INTERFACE IDENTIFIER(A) class_inheritance END.
{
    printf("defined class %.*s\n", A.string_length, A.string);
}

...

%%

...

language.l(节选)

...

%option reentrant

...

%%

...

%%

...

看到我的printf语句了吗?我用字符串和长度来打印我的标记。

相关问题