Flex 和 Bison 给出分段错误(核心已转储)
Flex and Bison gives Segmentation fault (core dumped)
我正在尝试使用 flex 和 bison 为一种名为 "FUNC" 的简单语言创建编译器。但它给了我一个分段错误,在花了几个小时之后,我仍然无法修复它。如果你们能提供帮助,我将不胜感激。谢谢!
弹性文件 "func.lex"
%{
#include "tokens.h"
//#include "y.tab.h"
%}
DIGIT [0-9]
IDENT [a-zA-Z][A-Za-z0-9]*
%%
"function" {return FUNCTION;}
"returns" {return RETURNS;}
"begin" {return BEGIN;}
"end" {return END;}
"read" {return READ;}
"write" {return WRITE;}
"if" {return IF;}
"then" {return THEN;}
"else" {return ELSE;}
"variables" {return VARIABLES;}
"while" {return WHILE;}
"loop" {return LOOP;}
"Less" {return LESS;}
"LessEq" {return LESSEQ;}
"Eq" {return EQ;}
"NEq" {return NEQ;}
"(" {return LB;}
")" {return RB;}
"Plus" {return PLUS;}
"Times" {return TIMES;}
"Minus" {return MINUS;}
"Divide" {return DIVIDE;}
"," {return COMMA;}
":=" {return ASSIGN;}
";" {return SEMI;}
{DIGIT}+ {return NUMBER;}
{IDENT} {return NAME;}
<<EOF>> {return EOF;}
[ \t\n]+ /* eat up whitespace */
%%
int yywrap() { return EOF; }
Yacc 文件"func.y"
%{
//#include "tokens.h"
#include <stdio.h>
#include <stdlib.h>
extern FILE * yyin;
extern char * yytext;
extern int yylex(void);
extern int yyparse();
void yyerror( const char *s);
int yylex(void);
int symb;
%}
/*****************bison declarations**********************/
%union //defining all possible semantic data types (strings and digits)
{
int NUMBER;
char * NAME;
_Bool COND; //return value of conditional expressions. one of our $$ can have value 0 or 1
}
%start program
%type <NUMBER> NUMBER
%type <NAME> NAME
%token FUNCTION RETURNS VARIABLES BEGIN END COMMA SEMI ASSIGN
READ WRITE
IF THEN ELSE
WHILE LOOP
LB RB
LESS LESSEQ EQ NEQ
PLUS MINUS TIMES DIVIDE
NAME NUMBER //same case as that used in "operations" below (same as FUNC syntax)
%%
//grammar rules
program: funcs
; //<program> ::= <funcs>
funcs: func ";" //<funcs> ::= <func>; [<funcs>]
|func ";" funcs
;
func: FUNCTION NAME "("")" BEGIN commands END FUNCTION /*<func> ::= function <name>([<args>])[returns <name>] [variables <args>] begin <commands> end function*/
|FUNCTION NAME "(" args ")" BEGIN commands END FUNCTION
|FUNCTION NAME "(" args ")" RETURNS NAME BEGIN commands END FUNCTION
|FUNCTION NAME "("")" RETURNS NAME BEGIN commands END FUNCTION
|FUNCTION NAME "("")" BEGIN commands VARIABLES args END FUNCTION
|FUNCTION NAME "(" args ")" BEGIN commands VARIABLES args END FUNCTION
|FUNCTION NAME "("")" RETURNS NAME BEGIN commands VARIABLES args END FUNCTION
|FUNCTION NAME "(" args ")" RETURNS NAME BEGIN commands VARIABLES args END FUNCTION
;
args: NAME //<args> ::= <name> [,<args>]
|NAME "," args
;
commands: command ";" //<commands> ::= <command>; [<commands>]
|command ";" commands
;
command: assign //<command> ::= <assign> | <if> | <while> | read <name> | write <expr>
|if
|while
|read
|write
;
assign: NAME ":=" expr {$<NAME>$==$<NUMBER>3;} //<assign> ::= <name> := <expr>
//assign: NAME ASSIGN expr {=;}
;
if: IF condexpr THEN commands END IF //<if> ::= if <condexpr> then <commands> [else <commands>] end if
|IF condexpr THEN commands ELSE commands END IF
;
while: WHILE condexpr LOOP commands END LOOP
; //<for> ::= while <condexpr> loop <commands> end loop
read: READ NUMBER
|READ NAME
;
write: WRITE expr
;
condexpr: bop "(" expr "," expr ")"
; //<condexpr> ::= <bop> ( <exprs> )
bop: LESS //<bop> ::= Less | LessEq | Eq | NEq
|LESSEQ
|EQ
|NEQ
;
Less: LESS "(" NUMBER "," NUMBER ")" {if($<NUMBER>3<$<NUMBER>5)$<COND>$=1;} ;
LessEq: LESSEQ "(" NUMBER "," NUMBER ")" {if($<NUMBER>3<=$<NUMBER>5)$<COND>$=1;} ;
Eq: EQ "(" NUMBER "," NUMBER ")" {if($<NUMBER>3=$<NUMBER>5)$<COND>$=1;} ;
NEq: NEQ "(" NUMBER "," NUMBER ")" {if($<NUMBER>3!=$<NUMBER>5)$<COND>$=1;} ;
exprs: expr //<expr> [,<exprs>]
|expr "," exprs
;
expr: NAME
|NUMBER
|NAME "(" exprs ")" //<name>[( <exprs> )] | <number>
;
/***************idk if we need this. dunno which file to describe these operations in ***********************/
Plus: PLUS "(" NUMBER "," NUMBER ")" {$<NUMBER>$=+; } ; //S1=plus =( =expr = =expr =)
Minus: MINUS "(" NUMBER "," NUMBER ")" {$<NUMBER>$=-; } ;
Times: TIMES "(" NUMBER "," NUMBER ")" {$<NUMBER>$=*; } ;
Divide: DIVIDE "(" NUMBER "," NUMBER ")" {$<NUMBER>$=/; } ;
%%
//c code
/*
int main(int c, char * * argv) {
if ((yyin = fopen(argv[1], "r")) == NULL) {
printf("can't open %s\n", argv[1]);
exit(0);
}
symb = yylex();
yyparse();
// program(1);
fclose(yyin);
}
*/
int main (char * * argv)
{
if ((yyin = fopen(argv[1], "r")) == NULL) {
printf("can't open %s\n", argv[1]);
exit(0);
}
yylex();
}
void yyerror(const char *s)
{
extern int yylineno; // defined and maintained in lex.c
extern char *yytext; // defined and maintained in lex.c
/*std::cerr << "ERROR: " << s << " at symbol \"" << yytext;
std::cerr << "\" on line " << yylineno << std::endl;
exit(1);*/
printf("parse error Message: ", s);
fflush(1);
exit(-1);
}
/*int yyerror(char *s)
{
return yyerror(string(s));
}*/
你的词法定义有各种错误;由于缺乏有关该问题的详细信息,我不知道它们中的任何一个在多大程度上对您的问题有所贡献,因此我将仅列出它们:
使用bison头文件;不要替换你自己的。
%{
#include "tokens.h"
//#include "y.tab.h"
%}
bison 生成的头文件包含 YYSTYPE
的定义,解析器和扫描器必须就此定义达成一致。它还包含各种标记的正确定义,这些标记在两个文件中也必须相同。您没有显示 token.h
的内容,但它的使用并不能提供任何信心;如果您这样做是为了掩盖其他问题,请在继续之前解决其他问题。
不要替换非标准的 EOF 处理
<<EOF>> {return EOF;}
词法分析器和扫描器之间的协议是词法分析器将 return 标记 id 0 表示输入结束。 EOF
的值通常是 -1,这不是一个有效的令牌号(令牌号是 非负整数 ,并且不会被生成的解析器正确处理。默认情况下,(f)lex 会插入一个适当的默认文件结束规则,它会做正确的事情,您应该依赖该行为。
如果不需要yywrap,告诉flex不要依赖
一个好习惯是将以下定义放在 (f)lex 定义的序言中:
%option noyywrap noinput nounput nodefault
(除非你需要这些功能之一,你应该知道你需要哪些。) noyywrap
选项从生成的词法分析器中删除调用 yywrap
的代码,以便词法分析器当遇到来自输入流的 EOF 时,立即 return 结束输入指示。 noinput
和 nounput
删除 input()
和 unput()
函数的定义,如果它们未被您的词法分析器操作使用,将导致编译器警告。 (顺便说一句,您确实在启用编译器警告的情况下进行编译,对吗?不启用编译器警告是忽略您搬起石头砸自己脚这一事实的好方法。)
nodefault
选项删除了 (f)lex 为无法识别的输入字符生成的默认规则,并在可能无法识别某些输入字符时发出警告。 (这不影响默认的 <<EOF>>
操作。无法识别的输入的默认 flex 操作是 ECHO
,这意味着无法识别的字符将简单地发送到标准输出而不会产生任何类型的错误消息。那就是(几乎)从来不是你想要的,也可以用来掩盖真正的错误。
如果您使用 yywrap
,表示输入结束的常规 return 值是 1
("true"),而不是 EOF
,尽管EOF
会起作用。
如果解析器需要一个语义值,您需要提供一个。
在您的解析器中,您声称 NAME
标记具有 NAME
类型的语义值。 (将令牌名称重新用作标记名是 极其 不明智的;您应该解决这个问题。)但是, return 是 NAME
令牌的 flex 操作并不填写语义值。最可能的结果是解析器在期望有效的 char*
时会收到 NULL
,这肯定会导致段错误。
您的解析器定义也有一些问题,应该更正。首先是我在上面提出的关于 %union
声明中标记名的要点。其次,您不需要在解析器代码中 #include "y.tab.h"
,因为它的内容已经被插入。
解析器代码中最令人困惑的部分是在整个语法中使用命名类型语法 $<tag>1
。 不要那样做。您应该使用正确的类型正确声明所有标记和非终结符:
%token <str> NAME
%type <number> expr
(假设有一组更标准的标记名。)如果您提供明确的标记名,生成的解析器将使用该标记名,从而绕过类型安全检查。 (而且,很明显,如果你绕过类型安全检查,在规则中使用不正确的联合成员会更容易,导致谁知道会发生什么后果)
我正在尝试使用 flex 和 bison 为一种名为 "FUNC" 的简单语言创建编译器。但它给了我一个分段错误,在花了几个小时之后,我仍然无法修复它。如果你们能提供帮助,我将不胜感激。谢谢!
弹性文件 "func.lex"
%{
#include "tokens.h"
//#include "y.tab.h"
%}
DIGIT [0-9]
IDENT [a-zA-Z][A-Za-z0-9]*
%%
"function" {return FUNCTION;}
"returns" {return RETURNS;}
"begin" {return BEGIN;}
"end" {return END;}
"read" {return READ;}
"write" {return WRITE;}
"if" {return IF;}
"then" {return THEN;}
"else" {return ELSE;}
"variables" {return VARIABLES;}
"while" {return WHILE;}
"loop" {return LOOP;}
"Less" {return LESS;}
"LessEq" {return LESSEQ;}
"Eq" {return EQ;}
"NEq" {return NEQ;}
"(" {return LB;}
")" {return RB;}
"Plus" {return PLUS;}
"Times" {return TIMES;}
"Minus" {return MINUS;}
"Divide" {return DIVIDE;}
"," {return COMMA;}
":=" {return ASSIGN;}
";" {return SEMI;}
{DIGIT}+ {return NUMBER;}
{IDENT} {return NAME;}
<<EOF>> {return EOF;}
[ \t\n]+ /* eat up whitespace */
%%
int yywrap() { return EOF; }
Yacc 文件"func.y"
%{
//#include "tokens.h"
#include <stdio.h>
#include <stdlib.h>
extern FILE * yyin;
extern char * yytext;
extern int yylex(void);
extern int yyparse();
void yyerror( const char *s);
int yylex(void);
int symb;
%}
/*****************bison declarations**********************/
%union //defining all possible semantic data types (strings and digits)
{
int NUMBER;
char * NAME;
_Bool COND; //return value of conditional expressions. one of our $$ can have value 0 or 1
}
%start program
%type <NUMBER> NUMBER
%type <NAME> NAME
%token FUNCTION RETURNS VARIABLES BEGIN END COMMA SEMI ASSIGN
READ WRITE
IF THEN ELSE
WHILE LOOP
LB RB
LESS LESSEQ EQ NEQ
PLUS MINUS TIMES DIVIDE
NAME NUMBER //same case as that used in "operations" below (same as FUNC syntax)
%%
//grammar rules
program: funcs
; //<program> ::= <funcs>
funcs: func ";" //<funcs> ::= <func>; [<funcs>]
|func ";" funcs
;
func: FUNCTION NAME "("")" BEGIN commands END FUNCTION /*<func> ::= function <name>([<args>])[returns <name>] [variables <args>] begin <commands> end function*/
|FUNCTION NAME "(" args ")" BEGIN commands END FUNCTION
|FUNCTION NAME "(" args ")" RETURNS NAME BEGIN commands END FUNCTION
|FUNCTION NAME "("")" RETURNS NAME BEGIN commands END FUNCTION
|FUNCTION NAME "("")" BEGIN commands VARIABLES args END FUNCTION
|FUNCTION NAME "(" args ")" BEGIN commands VARIABLES args END FUNCTION
|FUNCTION NAME "("")" RETURNS NAME BEGIN commands VARIABLES args END FUNCTION
|FUNCTION NAME "(" args ")" RETURNS NAME BEGIN commands VARIABLES args END FUNCTION
;
args: NAME //<args> ::= <name> [,<args>]
|NAME "," args
;
commands: command ";" //<commands> ::= <command>; [<commands>]
|command ";" commands
;
command: assign //<command> ::= <assign> | <if> | <while> | read <name> | write <expr>
|if
|while
|read
|write
;
assign: NAME ":=" expr {$<NAME>$==$<NUMBER>3;} //<assign> ::= <name> := <expr>
//assign: NAME ASSIGN expr {=;}
;
if: IF condexpr THEN commands END IF //<if> ::= if <condexpr> then <commands> [else <commands>] end if
|IF condexpr THEN commands ELSE commands END IF
;
while: WHILE condexpr LOOP commands END LOOP
; //<for> ::= while <condexpr> loop <commands> end loop
read: READ NUMBER
|READ NAME
;
write: WRITE expr
;
condexpr: bop "(" expr "," expr ")"
; //<condexpr> ::= <bop> ( <exprs> )
bop: LESS //<bop> ::= Less | LessEq | Eq | NEq
|LESSEQ
|EQ
|NEQ
;
Less: LESS "(" NUMBER "," NUMBER ")" {if($<NUMBER>3<$<NUMBER>5)$<COND>$=1;} ;
LessEq: LESSEQ "(" NUMBER "," NUMBER ")" {if($<NUMBER>3<=$<NUMBER>5)$<COND>$=1;} ;
Eq: EQ "(" NUMBER "," NUMBER ")" {if($<NUMBER>3=$<NUMBER>5)$<COND>$=1;} ;
NEq: NEQ "(" NUMBER "," NUMBER ")" {if($<NUMBER>3!=$<NUMBER>5)$<COND>$=1;} ;
exprs: expr //<expr> [,<exprs>]
|expr "," exprs
;
expr: NAME
|NUMBER
|NAME "(" exprs ")" //<name>[( <exprs> )] | <number>
;
/***************idk if we need this. dunno which file to describe these operations in ***********************/
Plus: PLUS "(" NUMBER "," NUMBER ")" {$<NUMBER>$=+; } ; //S1=plus =( =expr = =expr =)
Minus: MINUS "(" NUMBER "," NUMBER ")" {$<NUMBER>$=-; } ;
Times: TIMES "(" NUMBER "," NUMBER ")" {$<NUMBER>$=*; } ;
Divide: DIVIDE "(" NUMBER "," NUMBER ")" {$<NUMBER>$=/; } ;
%%
//c code
/*
int main(int c, char * * argv) {
if ((yyin = fopen(argv[1], "r")) == NULL) {
printf("can't open %s\n", argv[1]);
exit(0);
}
symb = yylex();
yyparse();
// program(1);
fclose(yyin);
}
*/
int main (char * * argv)
{
if ((yyin = fopen(argv[1], "r")) == NULL) {
printf("can't open %s\n", argv[1]);
exit(0);
}
yylex();
}
void yyerror(const char *s)
{
extern int yylineno; // defined and maintained in lex.c
extern char *yytext; // defined and maintained in lex.c
/*std::cerr << "ERROR: " << s << " at symbol \"" << yytext;
std::cerr << "\" on line " << yylineno << std::endl;
exit(1);*/
printf("parse error Message: ", s);
fflush(1);
exit(-1);
}
/*int yyerror(char *s)
{
return yyerror(string(s));
}*/
你的词法定义有各种错误;由于缺乏有关该问题的详细信息,我不知道它们中的任何一个在多大程度上对您的问题有所贡献,因此我将仅列出它们:
使用bison头文件;不要替换你自己的。
%{ #include "tokens.h" //#include "y.tab.h" %}
bison 生成的头文件包含
YYSTYPE
的定义,解析器和扫描器必须就此定义达成一致。它还包含各种标记的正确定义,这些标记在两个文件中也必须相同。您没有显示token.h
的内容,但它的使用并不能提供任何信心;如果您这样做是为了掩盖其他问题,请在继续之前解决其他问题。不要替换非标准的 EOF 处理
<<EOF>> {return EOF;}
词法分析器和扫描器之间的协议是词法分析器将 return 标记 id 0 表示输入结束。
EOF
的值通常是 -1,这不是一个有效的令牌号(令牌号是 非负整数 ,并且不会被生成的解析器正确处理。默认情况下,(f)lex 会插入一个适当的默认文件结束规则,它会做正确的事情,您应该依赖该行为。如果不需要yywrap,告诉flex不要依赖
一个好习惯是将以下定义放在 (f)lex 定义的序言中:
%option noyywrap noinput nounput nodefault
(除非你需要这些功能之一,你应该知道你需要哪些。)
noyywrap
选项从生成的词法分析器中删除调用yywrap
的代码,以便词法分析器当遇到来自输入流的 EOF 时,立即 return 结束输入指示。noinput
和nounput
删除input()
和unput()
函数的定义,如果它们未被您的词法分析器操作使用,将导致编译器警告。 (顺便说一句,您确实在启用编译器警告的情况下进行编译,对吗?不启用编译器警告是忽略您搬起石头砸自己脚这一事实的好方法。)nodefault
选项删除了 (f)lex 为无法识别的输入字符生成的默认规则,并在可能无法识别某些输入字符时发出警告。 (这不影响默认的<<EOF>>
操作。无法识别的输入的默认 flex 操作是ECHO
,这意味着无法识别的字符将简单地发送到标准输出而不会产生任何类型的错误消息。那就是(几乎)从来不是你想要的,也可以用来掩盖真正的错误。如果您使用
yywrap
,表示输入结束的常规 return 值是1
("true"),而不是EOF
,尽管EOF
会起作用。如果解析器需要一个语义值,您需要提供一个。
在您的解析器中,您声称
NAME
标记具有NAME
类型的语义值。 (将令牌名称重新用作标记名是 极其 不明智的;您应该解决这个问题。)但是, return 是NAME
令牌的 flex 操作并不填写语义值。最可能的结果是解析器在期望有效的char*
时会收到NULL
,这肯定会导致段错误。
您的解析器定义也有一些问题,应该更正。首先是我在上面提出的关于 %union
声明中标记名的要点。其次,您不需要在解析器代码中 #include "y.tab.h"
,因为它的内容已经被插入。
解析器代码中最令人困惑的部分是在整个语法中使用命名类型语法 $<tag>1
。 不要那样做。您应该使用正确的类型正确声明所有标记和非终结符:
%token <str> NAME
%type <number> expr
(假设有一组更标准的标记名。)如果您提供明确的标记名,生成的解析器将使用该标记名,从而绕过类型安全检查。 (而且,很明显,如果你绕过类型安全检查,在规则中使用不正确的联合成员会更容易,导致谁知道会发生什么后果)