最近一段时间在学习作一个c编译器,使用Lex作词法分析器,使用Yacc作语法分析器,其实这两个东东大家都非常熟悉,关于使用它们写出的编译器的文章代码也很多,但我发现一个问题,就是对于语法出错的问题很少谈到,比如int a = 2,这种语法在c语言中来看应该是错误的:应该以分号结尾,等等诸如此类的问题,很少有资料谈到如何处理,本人就在这抛砖引玉,写出自己的一些心得,一起进步。
我把:http://www.ibm.com/developerworks/cn/linux/l-flexbison.html 中使用 Flex 和 Bison 更好地进行错误处理中的ccalc作为一个例子,讲讲如何作出错处理。
在parse.y的语法中有这样一段:
program
: statement SEMICOLON program
| statement SEMICOLON
| statement error SEMICOLON program
{
yyerrok;
}
;
statement
: IDENTIFIER
{
var = VarGet($1, &@1);
}
ASSIGN expression
{
VarSetValue(var, $4);
}
| expression
;
上面红色粗体的语法的意思等同于:
$tmp : IDENTIFIER { var = VarGet($1, &@1); }
;
statement : $tmp ASSIGN expression { VarSetValue(var, $3);}
;
注意: tmp只是一个临时的任意值,只是为了说明问题。
假设现在在defs.txt 中输入这样一句: a = 4 b
在cygwin中执行: ./ccalc.exe defs.txt -debug > a
//把调试信息写入到了当前文件夹中的文件a中。
在文件a中内容为:
=== sample program for IBM developerWorks ===
=== ccalc - a sample calculator with variables ===
*** author: [email protected] ***
debugging activated
reading file 'defs.txt'
|....+....:....+....:....+....:....+....:....+....:....+....:....+....:
1 |a = 4 bGetNextChar() => 'a'0x61 at 1
GetNextChar() => ' '0x20 at 2
Token 'a' at 1:1 next at 2
GetNextChar() => '='0x3d at 3
Token ' ' at 2:2 next at 3
Token '=' at 3:3 next at 3
get var a
GetNextChar() => ' '0x20 at 4
GetNextChar() => '4'0x34 at 5
Token ' ' at 3:3 next at 5
GetNextChar() => ' '0x20 at 6
Token '4' at 5:5 next at 6
GetNextChar() => 'b'0x62 at 7
Token ' ' at 6:6 next at 7
Token 'b' at 1:1 next at 0
set var a to 4.000000
...... !.......^-EOF
Error: syntax error, unexpected IDENTIFIER, expecting SEMICOLON
final content of variables
Name------------------ Value----------
'a ' 4
看到红色粗体的错误吧,当移进4的时候,匹配了关键字VALUE,归约为Expression,当移进b的时候,a = 3就因为匹配上面语法规则中红色粗体的语法规则而归约了,因此在归约后形成了非终结符statement,statement后面应该输入;才应该是正确的语法,但现在因为是输入了b,一个IDENTIFIER 终结符,所以就出错了,这个错误是Bison给出的,我们如何能够知道非终结符statement后面不是一个;呢? 这就设计到一个程序的设计问题,我们可以把每次在lex.l中匹配到的终结符用全局变量保存下来,在归约
statement
: IDENTIFIER
{
var = VarGet($1, &@1);
}
ASSIGN expression
{
VarSetValue(var, $4);
}
的时候,判断最后一个匹配的Token验证是否为;如果不是,就弹出自己的提示信息,以实现脚本的错误处理。