Проблемы с синтаксическим анализом простого исходного кода C с использованием Bison

#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 объекты позже, когда они больше не будут использоваться. Исправлено все, большое вам спасибо!