在做本次编译原理实验时出现了一个奇怪的问题
在老师给的文档里有这样一个测试案例
int main(){
int i = 3;
%666
if(i - 2 == 1){ // note
i = i + 8
}
float p[i = 2.52;
}
计划输出
Error type A at line 3 : Mysterious characters '%'
Error type B at line 4: Missing ';'.
Error type B at line 6: Missing ';'.
Error type B at line 7: Missing ']'.
但实际上的输出结果是
多出了一个missing2和一个missing1‘;’的报错
由于我给’;'编了号,可以对应查找出报错的原因,报错的missing在下面的定义位置,报错的原因是函数声明语句缺少;
//表示全局变量 结构体或函数定义
ExtDef: Specifier ExtDecList SEMI { $$ ; }
| Specifier SEMI { $$ ; }
| Specifier FunDec CompSt { $$; }
| Specifier FunDec SEMI{$$;}
| Specifier ExtDecList error {yyError("Missing1 ';'");} //全局变量定义语句缺少';'
| Specifier FunDec error {yyError("Missing2 ';'");} //函数声明语句缺少';'
| Specifier error {yyError("Missing3 ';'");} //(全局)结构体定义语句缺少';'
;
但是在测试样例中并没有函数声明,这到底是为啥呢
经过一系列print的查找,发现在语句匹配的过程中无法进入到语句块中(在这一步放入输入语句无法输出)
//语句块{}
CompSt: LC DefList StmtList RC { $$ ; }
;
语句块除了左右的两个’{’ '}'外由中间两部分构成,分别是变量和其他语句,如下所示
//其他语句和表达式
StmtList: Stmt StmtList { $$ ; }
| { $$ ; }
;
Stmt: Exp SEMI { $$ ; }
| CompSt { $$ ;}
| RETURN Exp SEMI { $$ ;}
| IF LP Exp RP Stmt %prec LOWER_THAN_ELSE{ $$ ; }//表示该优先级较低
| IF LP Exp RP Stmt ELSE Stmt { $$ ; }
| WHILE LP Exp RP Stmt { $$ ; }
| RETURN Exp error {yyError("Missing4 ';'");} //return语句结尾缺少';'
| ELSE Stmt {yyError("'else' without a previous 'if'");} //错误使用if-else语句
| Exp error {yyError("Missing5 ';'");} //语句结尾缺少';'
;
//局部变量定义
DefList: Def DefList { $$ ; }
| { $$ ;}
;
Def: Specifier DecList SEMI { $$ ; }
|Specifier DecList error {yyError("Missing6 ';'");} //局部变量定义缺少';
;
而在C–语法中对语句块的定义是
CompSt: LC DefList StmtList RC//左括号+变量+其他语句+右括号
再看我们的测试样例中在大括号内其实是变量定义和其他语句穿插进行,因此无法和CompSt
的定义匹配上,此类在C–的语法中没有定义,会被匹配成error
因此会被匹配到Specifier FunDec error {yyError("Missing2 ';'");} //函数声明语句缺少';'
该代码块相当于匹配成了 int main() error
一个函数声明缺少;
int main(){
int i = 3;//DefList
%666
if(i - 2 == 1){ // StmtList
i = i + 8
}
float p[i = 2.52;//DefList
}
想要解决这个问题必须去修改C–语法,让CompSt
语法定义满足测试样例的需求,可以实现变量与表达式穿插放置
我们首先想到的是以下方法
CompSt: LC DefList StmtList DefList RC
会发生移进-归约冲突,且还是不能满足二者随意交替进行的情况
syntax.y: warning: 4 shift/reduce conflicts [-Wconflicts-sr]
当遇到一个代码行时有两种以上选择时就会出现该种冲突
最终的解决方法
原理:将变量和其他表达式合并成为一部分。由于DefList
和StmtList
的结构是相同的,只有头部的Stmt
和Def
不同,尾部是一个循环体,因此将二者合并,将Stmt
的定义加入Def
的部分
注意:不能直接将DefList
删掉,因为在Struct
结构体定义中也需要用到局部变量
//语句块{}
CompSt: LC StmtList RC { $$ ; }
;
StmtList: Stmt StmtList { $$ ; }
| { $$ ; }
;
Stmt: Def{ $$ ;}
| Exp SEMI { $$ ; }
| CompSt { $$ ;}
| RETURN Exp SEMI { $$ ;}
| IF LP Exp RP Stmt %prec LOWER_THAN_ELSE{ $$ ; }//表示该优先级较低
| IF LP Exp RP Stmt ELSE Stmt { $$ ; }
| WHILE LP Exp RP Stmt { $$ ; }
| RETURN Exp error {yyError("Missing4 ';'");} //return语句结尾缺少';'
| ELSE Stmt {yyError("'else' without a previous 'if'");} //错误使用if-else语句
| Exp error {yyError("Missing5 ';'");} //语句结尾缺少';'
;
//变量定义
DefList: Def DefList { $$ ; }
| { $$ ;}
;
一点其他的理解:
为什么最开始没有匹配上语句块也会报语句块内部的一些错比如说float p[i = 2.52;
缺少右括号也可以正常输出
这是由于bison的错误匹配模式是一个栈的移进移出
匹配error时会先匹配到DefList
,在DefList
中匹配到Exp
中此行的错误,当匹配到这一句时就会直接输出语句报错
Exp:| Exp LB Exp error {yyError("Missing ']'");} //表达式中变量索引缺少']'
当匹配到代码结尾时发现不符合最初的CompSt
语法定义,则不予该行匹配,匹配到了
| Specifier FunDec error {yyError("Missing2 ';'");} //函数声明语句缺少';'
注:此代码为不完整代码且包含错误部分(dog)
发现了更改语法对语法树生成的一些影响
float f, m[6];
struct ms{
int kk;
} ms;
int main(){
int q[10];
i = 3;
ms.kk = i && fun();
}
int fun(int a, float f[2]){
if(i - 2 == 1){
q[1] = i + ms.kk;
}
}
上述这段代码中老师给出答案中的语法树
Program (1)
ExtDefList (1)
ExtDef (1)
Specifie (1)
TYPE : float
ExtDecList (1)
VarDec (1)
ID : f
COMMA : ,
ExtDecList (1)
VarDec (1)
VarDec (1)
ID : m
LB : [
INT : 6
RB : ]
SEMI : ;
ExtDefList (3)
ExtDef (3)
Specifier (3)
StructSpecif (3)
STRUCT : struct
OptTag (3)
ID : ms
LC : {
DefList (4)
Def (4)
Specifie (4)
TYPE : int
DefList (4)
Dec (4)
VarDec (4)
ID : kk
SEMI : ;
Empty : Empty
RC : }
ExtDecList (5)
VarDec (5)
ID : ms
SEMI : ;
ExtDefList (7)
ExtDef (7)
Specifie (7)
TYPE : int
FunDec (7)
ID : main
LP : (
RP : )
CompSt (7)
LC : {
DefList (8)
Def (8)
Specifie (8)
TYPE : int
DefList (8)
Dec (8)
VarDec (8)
VarDec (8)
ID : q
LB : [
INT : 10
RB : ]
SEMI : ;
Empty : Empty
StmtList (9)
EStmt (9)
Exp (9)
Exp (9)
ID : i
ASSIGNOP : =
Exp (9)
INT : 3
SEMI : ;
StmtList (10)
EStmt (10)
Exp (10)
Exp (10)
Exp (10)
ID : ms
DOT : .
ID : kk
ASSIGNOP : =
Exp (10)
Exp (10)
ID : i
AND : &&
Exp (10)
ID : fun
LP : (
RP : )
SEMI : ;
Empty : Empty
RC : }
ExtDefList (13)
ExtDef (13)
Specifie (13)
TYPE : int
FunDec (13)
ID : fun
LP : (
VarList (13)
ParamDec (13)
Specifie (13)
TYPE : int
VarDec (13)
ID : a
COMMA : ,
VarList (13)
ParamDec (13)
Specifie (13)
TYPE : float
VarDec (13)
VarDec (13)
ID : f
LB : [
INT : 2
RB : ]
RP : )
CompSt (13)
LC : {
Empty : Empty
StmtList (14)
Stmt (14)
IF : if
LP : (
Exp (14)
Exp (14)
Exp (14)
ID : i
MINUS : -
Exp (14)
INT : 2
RELOP : ==
Exp (14)
INT : 1
RP : )
Stmt (14)
CompSt (14)
LC : {
Empty : Empty
StmtList (15)
EStmt (15)
Exp (15)
Exp (15)
ID : q
ITEM (15)
LB : [
INT : 1
RB : ]
ASSIGNOP : =
Exp (15)
Exp (15)
ID : i
PLUS : +
Exp (15)
Exp (15)
ID : ms
DOT : .
ID : kk
SEMI : ;
Empty : Empty
RC : }
Empty : Empty
RC : }
Empty : Empty
而我们生成的语法树
Program (1)
ExtDefList (1)
ExtDef (1)
Specifier (1)
TYPE: float
ExtDecList (1)
VarDec (1)
ID: f
COMMA: ,
ExtDecList (1)
VarDec (1)
VarDec (1)
ID: m
LB: [
INT: 6
RB: ]
SEMI: ;
ExtDefList (3)
ExtDef (3)
Specifier (3)
StructSpecifier (3)
STRUCT: struct
OptTag (3)
ID: ms
LC: {
DefList (4)
Def (4)
Specifier (4)
TYPE: int
DecList (4)
Dec (4)
VarDec (4)
ID: kk
SEMI: ;
Empty: Empty
RC: }
ExtDecList (5)
VarDec (5)
ID: ms
SEMI: ;
ExtDefList (7)
ExtDef (7)
Specifier (7)
TYPE: int
FunDec (7)
ID: main
LP: (
RP: )
CompSt (7)
LC: {
StmtList (8)
Def (8)
Specifier (8)
TYPE: int
DecList (8)
Dec (8)
VarDec (8)
VarDec (8)
ID: q
LB: [
INT: 10
RB: ]
SEMI: ;
StmtList (9)
Stmt (9)
Exp (9)
Exp (9)
ID: i
ASSIGNOP: =
Exp (9)
INT: 3
SEMI: ;
StmtList (10)
Stmt (10)
Exp (10)
Exp (10)
Exp (10)
ID: ms
DOT: .
ID: kk
ASSIGNOP: =
Exp (10)
Exp (10)
ID: i
AND: &&
Exp (10)
ID: fun
LP: (
RP: )
SEMI: ;
Empty: Empty
RC: }
ExtDefList (13)
ExtDef (13)
Specifier (13)
TYPE: int
FunDec (13)
ID: fun
LP: (
VarList (13)
ParamDec (13)
Specifier (13)
TYPE: int
VarDec (13)
ID: a
COMMA: ,
VarList (13)
ParamDec (13)
Specifier (13)
TYPE: float
VarDec (13)
VarDec (13)
ID: f
LB: [
INT: 2
RB: ]
RP: )
CompSt (13)
LC: {
StmtList (14)
Stmt (14)
IF: if
LP: (
Exp (14)
Exp (14)
Exp (14)
ID: i
MINUS: -
Exp (14)
INT: 2
RELOP: ==
Exp (14)
INT: 1
RP: )
Stmt (14)
CompSt (14)
LC: {
StmtList (15)
Stmt (15)
Exp (15)
Exp (15)
Exp (15)
ID: q
LB: [
Exp (15)
INT: 1
RB: ]
ASSIGNOP: =
Exp (15)
Exp (15)
ID: i
PLUS: +
Exp (15)
Exp (15)
ID: ms
DOT: .
ID: kk
SEMI: ;
Empty: Empty
RC: }
Empty: Empty
RC: }
Empty: Empty
empty的个数是匹配不上的
以CompSt(13)处的语法树为例
CompSt (13)
LC : {
Empty : Empty
StmtList (14)
Stmt (14)
IF : if
LP : (
int fun(int a, float f[2]){
if(i - 2 == 1){
q[1] = i + ms.kk;
}
由于语句块中没有DefList的部分,因此在原有的C–语法中会把第一部分输出为空,而我们的C–语法将DefList和StmtList合并后,会将第一个if语句直接匹配到StmtList 因此会少一个Empty 语法树的内部结构也会有一些差异