尽管使用 Flex 和 Bison 生成程式很简单,但是要让这些程式产生用户友好的语法和语义错误消息却很困难。本文将介绍 Flex 和 Bison 的错误处理特性,并展示如何使用他们,然后周详介绍他们的一些缺陷。

简介

正如 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 语义类型
/* generate include-file with symbols and types */

            辠ines

            /* a more advanced semantic type */

            %union {

            double      value;

            char        *string;

            }

            

有些文法规则能够产生特定的语义类型,这需要像清单 2 中相同对 Bison 进行声明。要获得一个可移植性更好的 Bison 文法版本,我们需要重新定义 -*/() 符号。下面这个例子没有使用左括号 (,而是使用了结束符符号 LBRACE,这是由词法分析提供的。另外,操作符的优先顺序也必须进行声明。

对于 Flex 来说,所生成的代码通常都依赖于平台所使用的代码页(codepage)。尽管我们能够使用其他代码页,但是必须要对输入进行转换。因此和 Bison 代码不同,Flex 代码尚不能进行移植。


清单 2. Bison 声明
/* 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 文法
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 的第三个输出让这个分析程式能够获得错误,从中搜索分号,然后继续执行(通常错误对于解析器来说都是很严重的)。

为了让这个例子更加有趣,规则体中的真正数学函数都是以单独函数的形式实现的。在进行高级文法分析时,我们要尽量确保规则简短,并使用函数来实现一些不会直接处理解析的过程:

文章整理:西部数码--专业提供域名注册虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!