#c #c #parsing #grammar #bison
#c #c #синтаксический анализ #грамматика #bison
Вопрос:
Вот код синтаксического анализатора, который я использую
%{
#include <cstdio>
#include <iostream>
#include <cstring>
#include <stdio.h>
#include "c.ast.hpp"
#include <typeinfo>
#define YYDEBUG 1
using namespace std;
// stuff from flex that bison needs to know about:
extern "C" int yylex();
int yyparse(BlockOfFunctions *ast);
extern "C" FILE *yyin;
void yyerror(BlockOfFunctions *ast, const char *s);
#define TRACE printf("reduce at line %dn", __LINE__);
%}
%token IDENTIFIER I_CONSTANT F_CONSTANT STRING_LITERAL FUNC_NAME SIZEOF
%token PTR_OP INC_OP DEC_OP LEFT_OP RIGHT_OP LE_OP GE_OP EQ_OP NE_OP
%token AND_OP OR_OP MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN ADD_ASSIGN
%token SUB_ASSIGN LEFT_ASSIGN RIGHT_ASSIGN AND_ASSIGN
%token XOR_ASSIGN OR_ASSIGN
%token TYPEDEF_NAME ENUMERATION_CONSTANT
%token TYPEDEF EXTERN STATIC AUTO REGISTER INLINE
%token CONST RESTRICT VOLATILE
%token BOOL CHAR SHORT INT LONG SIGNED UNSIGNED FLOAT DOUBLE VOID
%token COMPLEX IMAGINARY
%token STRUCT UNION ENUM ELLIPSIS
%token CASE DEFAULT IF ELSE SWITCH WHILE DO FOR GOTO CONTINUE BREAK RETURN
%token ALIGNAS ALIGNOF ATOMIC GENERIC NORETURN STATIC_ASSERT THREAD_LOCAL
%start translation_unit
%parse-param {BlockOfFunctions *ast}
%union {
string *str;
TypeSpecifier typespec;
FunctionDefinition *func;
BlockOfFunctions *blockfunc;
Declaration *decl;
vector<Declaration> *decls;
Signature *sig;
}
%type<typespec> type_specifier declaration_specifiers
%type<str> IDENTIFIER
%type<func> external_declaration function_definition
%type<blockfunc> translation_unit
%type<decl> parameter_declaration
%type<decls> parameter_list parameter_type_list
%type<sig> declarator direct_declarator
%%
declaration_specifiers
: type_specifier { TRACE $ = $1; }
;
type_specifier
: VOID {
cout << "creating void" << endl;
$ = TypeSpecifier::Void; }
| INT { cout << "creating int" << endl; $ = TypeSpecifier::Int; }
;
declarator
: direct_declarator { $ = $1; }
;
direct_declarator
: IDENTIFIER {
Signature sig;
string name = *$1;
sig.name = name;
$ = amp;sig;
cout << "creating identifier " << sig.name << endl;
}
| direct_declarator '(' parameter_type_list ')' {
cout << "with argument" << endl;
cout << "got declarator " << *$1 << endl;
cout << "creating declaration " << $3->at(0) << endl;
$ = $1;
}
| direct_declarator '(' ')' {
$ = $1;
cout << "argument less function" << endl;
}
;
parameter_type_list
: parameter_list {
$ = $1;
cout << "creating parameter type list " << $->at(0) << endl;
}
;
parameter_list
: parameter_declaration {
vector<Declaration> params;
cout << "pushing back " << *$1 << endl;
params.push_back(*$1);
$ = amp;params;
cout << "creating parameter declaration " << $->at(0) << endl;
}
;
parameter_declaration
: declaration_specifiers declarator {
cout << "creating param declaration" << endl;
Declaration decl;
string name = $2->name;
decl.type = $1;
decl.name = name;
$ = amp;decl;
}
;
translation_unit
: external_declaration { ast->block.push_back(*$1); }
| translation_unit external_declaration { ast->block.push_back(*$2); }
;
external_declaration
: function_definition { TRACE $ = $1; }
;
function_definition
: declaration_specifiers declarator '{' '}' {
string name = $2->name;
FunctionDefinition fn;
fn.ret = $1;
fn.name = name;
$ = amp;fn;
}
;
%%
#include <stdio.h>
void yyerror(BlockOfFunctions *ast, const char *s)
{
fflush(stdout);
fprintf(stderr, "*** %sn", s);
}
Затем я пытаюсь проанализировать следующий исходный код, используя это
void empty(int a) { }
Но я получаю следующий вывод
bison -t -v -o c.tab.cpp -d c.y
flex -o c.lex.cpp -l c.l
g c.tab.cpp c.lex.cpp cc.cpp -lm -ll -lfl -o cc
./cc examples/test.c
creating void
reduce at line 63
creating identifier empty
creating int
reduce at line 63
creating identifier a
creating param declaration
pushing back declaration: int a
creating parameter declaration declaration: int a
creating parameter type list declaration: void
with argument
got declarator signature: a
creating declaration declaration: void
reduce at line 129
retv = 0
function: void a
Он неправильно анализирует имя функции a
, когда оно должно быть empty
. Я сузил ошибку до точки определенного местоположения: parameter_list
нетерминал правильно анализируется, но когда он перемещается вверх parameter_type_list
, он превращается в совершенно другой объект. Вы можете видеть это из распечатанной информации о времени выполнения.
Очевидно, я делаю что-то не так, но я не могу этого понять. Любая помощь будет оценена.
Ответ №1:
Это утверждение (и другие подобные) являются явным неопределенным поведением:
$ = amp;decl;
Вы пытаетесь сохранить указатель на локальную переменную, время жизни которой подходит к концу. Когда в конечном итоге используется значение этого висячего указателя, оно больше ни на что не ссылается.
Я настоятельно рекомендую вам добавить -Wall
в свой g флаги. Я не знаю, обнаружит ли gcc эту ошибку, особенно без флагов оптимизации, но нет смысла не давать ему возможности предупредить вас.
Не видя вашего гибкого кода, я не могу сказать, передаете ли вы также висячие указатели в качестве семантических значений токенов, что является еще одной распространенной причиной таинственного изменения семантических значений. Возможно, вы захотите это также проверить.
Комментарии:
1. Это интересно, я об этом не подумал. У меня нет опыта работы с C , можете ли вы сказать мне, как правильно это сделать? Я имею в виду возврат указателей на объекты в данном конкретном случае. Мне так или иначе придется вернуться к указателям на объекты, но, как я понимаю, я делаю это неправильно.
2. Я использовал
new
оператор и использовалdelete
объекты позже, когда они больше не будут использоваться. Исправлено все, большое вам спасибо!