c++ - 使用 Bison 解析简单 C 源代码的问题
问题描述
这是我正在使用的解析器代码
%{
#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 %d\n", __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;
$$ = &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);
$$ = ¶ms;
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;
$$ = &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;
$$ = &fn;
}
;
%%
#include <stdio.h>
void yyerror(BlockOfFunctions *ast, const char *s)
{
fflush(stdout);
fprintf(stderr, "*** %s\n", 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
,它变成了一个完全不同的对象。您可以从运行时打印的信息中看到这一点。
显然我做错了什么,但我无法弄清楚。任何帮助,将不胜感激。
解决方案
该声明(和其他类似声明)是明确的未定义行为:
$$ = &decl;
您正在尝试存储指向其生命周期即将结束的局部变量的指针。当最终使用该悬空指针的值时,它不再引用任何内容。
我强烈建议您添加-Wall
到您的 g++ 标志中。我不知道 gcc 是否会检测到这个错误,尤其是在没有优化标志的情况下,但不给它机会警告你是没有意义的。
如果没有看到您的弹性代码,我无法判断您是否也将悬空指针作为标记的语义值传递,这是神秘地改变语义值的另一个常见原因。您可能也想检查一下。
推荐阅读
- android - FileProvider从相机拍照的问题
- python - 为什么错误地给数组赋值?
- javascript - 为 iframe 设置自定义 HTTP 请求标头
- haskell - 如何用 '.' 分割字符串 在 Haskell 中,但如果句点介于两个数字之间,例如:(2.5) 那么不要拆分?
- python - 用引用在python中拆分句子
- java - 如何编写表示带有子对象的 json 对象的 java 类?
- node.js - 如何在我的 MongoDB 数组中推送一个 {key: value} 对象?
- python - Numpy dot 操作完成很快但持续占用大量 CPU 时间
- kotlin - 关于 Kotlin 中密封类的密封子类
- java - Apache Storm 和 Docker Swarm,我在哪里可以看到我的标准输出的输出?