首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

使用 Flex 和 Bison 更好地进行错误处理(1)

使用 Flex 和 Bison 更好地进行错误处理(1)

简介正如 UNIX® 开发人员所了解的那样,Flex 和 Bison 的功能非常强大,非常适合开发词法和语法解析器,尤其是语言编译器和解释器。如果我们不熟悉它们所实现的工具 —— 分别是 Lex 和 Yacc —— 可以参考一下本文  一节中有关 Flex 和 Bison 文档的链接,以及其他介绍这两个程序的文章。
本文介绍了更高级的一些主题:用来在编译器和解释器中更好地实现错误处理能力的特性和技术。为了展示这些技术,我使用了一个示例程序 ccalc,它基于 Bison 手册中的计算机实现了一个增强的计算器。我们可以从本文后面  一节下载 ccalc 和相关文件。
增强包括使用了很多变量。在 ccalc 中,变量是通过在初始化中首次使用时定义的,例如 a = 3。如果变量是在初始化之前使用的,那就会产生语义错误,使用值为 0 来创建这个变量,并打印一条消息。      
示例源文件示例源代码中包括 7 个文件:
  • ccalc.c:主程序,以及一些进行输入、输出和错误处理的函数
  • ccalc.h:包括了对所有模块的定义
  • cmath.c:数学函数
  • parse.y:Bison 使用的输入文法
  • lex.l:Flex 的输入
  • makefile:简单的 makefile
  • defs.txt:示例输入文件
这个程序接收两个参数:
  • -debug:产生调试输出
  • filename:输入文件名;默认值为  defs.txt
Bison 使用的设置为了处理变量名和实际值,Bison 的语义类型必须进行增强:
清单 1. 更好的 Bison 语义类型
1
2
3
4
5
6
7
/* generate include-file with symbols and types */
%defines
/* a more advanced semantic type */
%union {
  double      value;
  char        *string;
}




有些文法规则可以产生特定的语义类型,这需要像清单 2 中一样对 Bison 进行声明。要获得一个可移植性更好的 Bison 文法版本,我们需要重新定义 +-*/() 符号。下面这个例子没有使用左括号 (,而是使用了结束符符号 LBRACE,这是由词法分析提供的。另外,操作符的优先顺序也必须进行声明。
对于 Flex 来说,所生成的代码通常都依赖于平台所使用的代码页(codepage)。尽管我们可以使用其他代码页,但是必须要对输入进行转换。因此与 Bison 代码不同,Flex 代码尚不能进行移植。
清单 2. Bison 声明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* terminal symbols */

%token <string>   IDENTIFIER
%token <value>    VALUE
%type <value>     expression

/* operator-precedence
* top-0: -
*     1: * /
*     2: + -
*/

%left ADD SUB
%left MULT DIV
%left NEG

%start program




这段文法与 Bison 手册非常类似,不同之处在于它使用了名字作为终端符号和标识符的简写形式。标识符是在赋值语句中进行定义和初始化的,并且可以在任何允许使用的地方使用。清单 3 给出了一个示例文法:
清单 3. 示例 Bison 文法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
program
    : statement SEMICOLON program
    | statement SEMICOLON
    | statement error SEMICOLON program
    ;

statement
    : IDENTIFIER ASSIGN expression
    | expression
    ;

expression
    : LBRACE expression RBRACE
    | SUB expression %prec NEG
    | expression ADD expression
    | expression SUB expression
    | expression MULT expression
    | expression DIV expression
    | VALUE
    | IDENTIFIER
    ;




program 的第三个输出让这个分析程序可以获得错误,从中搜索分号,然后继续执行(通常错误对于解析器来说都是非常严重的)。
为了让这个例子更加有趣,规则体中的真正数学函数都是以单独函数的形式实现的。在进行高级文法分析时,我们要尽量保证规则简短,并使用函数来实现一些不会直接处理解析的过程:
清单 4. 使用单独的函数来实现数学规则
1
2
3
4
| expression DIV expression
  {
    $$ = ReduceDiv($1, $3);
  }




最后,函数 yyerror() 必须要进行定义。这个函数是在所生成的解析器检测到语法错误时调用的,它又会调用一个小函数 PrintError(),后者会打印增强的错误消息。详细内容请参看源代码。
Flex 的设置Flex 所生成的词法分析器必须要根据语义类型提供终止符号。清单 5 定义了空格、实际值、标识符和符号所使用的语法。
清单 5. 示例 Flex 规则
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
[ \t\r\n]+  {
    /* eat up whitespace */
    }

{DIGIT}+  {
    yylval.value = atof(yytext);
    return VALUE;
    }

{DIGIT}+"."{DIGIT}*        {
    yylval.value = atof(yytext);
    return VALUE;
    }

{DIGIT}+[eE]["+""-"]?{DIGIT}*        {
    yylval.value = atof(yytext);
    return VALUE;
    }

{DIGIT}+"."{DIGIT}*[eE]["+""-"]?{DIGIT}*        {
    yylval.value = atof(yytext);
    return VALUE;
    }

{ID}        {
    yylval.string = malloc(strlen(yytext)+1);
    strcpy(yylval.string, yytext);
    return IDENTIFIER;
    }

"+"       { return ADD; }
"-"       { return SUB; }
"*"       { return MULT; }
"/"       { return DIV; }
"("       { return LBRACE; }
")"       { return RBRACE; }
";"       { return SEMICOLON; }
"="       { return ASSIGN; }




为了帮助调试,我们在程序运行的末尾把所有已知的变量及其当前内容都打印了出来。
思海网络经过近18年的IDC运营,已经积累了丰富的IDC运营经验,推出佛山双线、电信、联通、多个机房供你选择、大带宽独享区域、所有独享线路均由机房核心骨干直连接入服务器。根据不同用户对带宽的需求,我司可提供10M独享~40000M独享带宽租用。多方位的资源,能满足您网络游戏、网页游戏、CDN加速布点、视频资源中心、下载站资源中心、音乐在线播放、电影、P2P、数据库服务器等不同的应用需求。在防护上,有高防、普防等;价格方面:思海网络实行低价优质策略,真正做到物超所值,部分资源可与联通或电信等直接签定合同。
佛山思海大带宽常年优惠促销,低价疯抢,详情请联系QQ:983054746,00001.cn
广东佛山电信千兆独享服务器租用低至16999/月!数量有限,赶快抢购 !-思海网络
返回列表