lex / yacc:为什么词法分析器必须包含解析器的头文件?

时间:2014-02-07 19:19:01

标签: parsing bison yacc lex flex-lexer

我正在尝试更多地学习编译器构造,所以我一直在玩弄flexc ++和bisonc ++;对于这个问题,我会提到flex / bison。

在bison中,使用%token声明来定义标记名称,例如

%token INTEGER
%token VARIABLE

等等。当在语法规范文件上使用bison时,会生成一个头文件y.tab.h,其中包含每个令牌的一些定义指令:

#define INTEGER 258
#define VARIABLE 259

最后,词法分析器包含头文件y.tab.h,为每个标记返回正确的代码:

%{
#include "y.tab.h"
%}

%%

[a-z]   { 
            // some code
            return VARIABLE;
        }

[1-9][0-9]* {
            // some code
            return INTEGER;
        }

因此解析器定义了令牌,然后词法分析器必须使用该信息来知道为每个令牌返回哪些整数代码。

这不是完全奇怪的吗?通常情况下,编译器管道会变成词法分析器 - >解析器 - >代码生成器。为什么lexer必须包含来自解析器的信息?词法分析器应定义标记,然后flex创建包含所有整数代码的头文件。解析器然后包括该头文件。这些依赖关系将反映编译器管道的通常顺序。我错过了什么?

3 个答案:

答案 0 :(得分:2)

与许多事情一样,这只是一次历史性事故。当然,lex可以生成令牌声明(但见下文)。或者可以强制用户编写自己的声明。

yacc / bison生成令牌编号更方便,因为:

  • 终端需要由yacc解析,因为它们是语法产生中的显式元素。另一方面,在lex中,它们是未解析的操作的一部分,lex可以在没有任何关于标记值的明确知识的情况下生成代码;以及

  • yacc(和bison)生成解析表,这些解析表由终端和非终端号码索引;表格的逻辑要求终端和非终端具有不同的代码。 lex无法知道非终端是什么,因此无法生成适当的代码。

第二个参数有点弱,因为在实践中bison - 生成的解析器重新编号令牌ID以使它们适合id编号方案。即便如此,只有bison负责实际数字时才有可能。 (重新编号的原因是使id值连续;对于另一个历史事故,为单字符标记保留代码0到255,为EOF保留0是正常的;但是,并非所有8位代码都是实际使用的大多数扫描仪。)

答案 1 :(得分:0)

在词法分析器中,标记只出现在返回值中:它们是目标语言的一部分(即C ++),而lex本身对它们一无所知。

另一方面,在解析器中,令牌是定义语言的一部分:您可以在实际的解析器定义中编写它们,而不仅仅是在目标语言中。所以yacc必须知道这些令牌。

答案 2 :(得分:0)

阶段的顺序不一定反映在编译器的体系结构中。扫描器是第一阶段,解析器是第二阶段,因此在某种意义上,数据从扫描器流向解析器,但在典型的Bison / Flex生成的编译器中,它是控制所有内容的解析器,它是调用的解析器在解析过程中需要新标记作为输入时,词法分析器作为辅助子程序。

相关问题