Анализатор лимонов 0 токен

У меня проблема с использованием (реентерабельного) Flex + Lemon для разбора. Я использую простую грамматику и лексер здесь. Когда я запускаю его, я добавляю число, за которым следует токен EOF (Ctrl-D). Распечатка будет читать:

89

found int of .
AST=0.

Где первая строка - это число, которое я ввел. Теоретически, значение AST должно быть суммой всего, что я ввел.

РЕДАКТИРОВАТЬ: когда я вызываю Parse() вручную, он работает правильно.

Кроме того, лимон, кажется, запускает atom ::= INT правило, даже когда токен равен 0 (токен остановки). Почему это? Я очень смущен этим поведением и не могу найти хорошую документацию.

2 ответа

Решение

Хорошо, я понял это. Причина в том, что между flex и lemon происходит особенно неприятное (и плохо документированное) взаимодействие.

В попытке сохранить память лимон будет удерживать токен без копирования и помещать его во внутренний стек токенов. Тем не менее, flex также пытается сохранить память, изменяя значение, которое yyget_text указывает на то, как он лексирует ввод. Оскорбительная строка в моем примере:

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

Это должно быть:

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

что гарантирует, что значение, на которое указывает lemon при уменьшении стека токенов, будет таким же, как и в исходном значении.

(Примечание: не забудьте, strdup означает, что вам придется освободить эту память в какой-то момент позже. Lemon позволит вам написать токены "деструкторы", которые могут это сделать, или, если вы строите дерево AST, вам следует подождать до конца времени жизни AST.)

Вы также можете попробовать создать тип токена, который содержит указатель на строку и длину строки. У меня был успех с этим.

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

Видишь мое утверждение там? Я использую строку и длину, чтобы распечатать мой токен.

Другие вопросы по тегам