Анализатор лимонов 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);
}
Видишь мое утверждение там? Я использую строку и длину, чтобы распечатать мой токен.