处理 Flex 启动条件时 Bison 解析器上的错误标记

Wrong tokens on Bison parser when dealing with Flex start conditions

在尝试重新创建由缩进定义的 Python 块时,我一开始就偶然发现了这一点。

当我单独尝试 lexer/scanner 时,它 returns 我得到了预期的结果,正确地使用了我设置的开始条件。但是当它与 Bison 解析器耦合时,正确的状态没有保留,我收到了来自意外状态的令牌。

我的预期行为是在一行的开头为 tabs/spaces 返回 "INDENT" 个标记,并在找到另一个符号(不是 tab/space)后返回 "OTHER" 每个符号的标记,直到开始新的一行。

第一种情况,词法分析器返回预期结果

scanner.l

%{
  #include <iostream>
%}

%option noyywrap

%x INDENT
%%

  BEGIN(INDENT);

<INDENT>[ \t] { std::cout << "INDENT "; }
<INDENT>.|\n { yyless(0); BEGIN(INITIAL); }

\n { std::cout << std::endl; BEGIN(INDENT); }
. { std::cout << "OTHER "; }

%%

int main(){
  yylex();
  return 0;
}

正在输入“ test ”("test"前后两个空格)returns "INDENT INDENT OTHER OTHER OTHER OTHER OTHER OTHER".

第二种情况,解析器返回意外结果

scanner.l

%{
  #include <iostream>

  #include "parser.h"
%}

%option noyywrap

%x INDENT
%%

  BEGIN(INDENT);

<INDENT>[ \t] { return T_INDENT; }
<INDENT>.|\n { yyless(0); BEGIN(INITIAL); }

\n { BEGIN(INDENT); return T_NEWLINE; }
. { return T_OTHER; }

%%

parser.y

%{
  #include <iostream>

  extern int yylex();

  void yyerror(const char *s);
%}

%define parse.error verbose

%token T_INDENT T_OTHER T_NEWLINE

%%

program : program symbol
        | %empty
        ;

symbol : T_INDENT { std::cout << "INDENT "; }
       | T_NEWLINE { std::cout << std::endl; }
       | T_OTHER { std::cout << "OTHER "; }
       ;

%%

void yyerror(const char *s){
  std::cout << s;
}

int main(){
  yyparse();
  return 0;
}

正在输入“ 测试 ”(与之前相同)returns "INDENT INDENT OTHER OTHER OTHER OTHER INDENT INDENT"。虽然预期结果与上述相同。

Bison 解析器似乎收到了错误的标记,就好像它没有遵守开始条件一样。我已经阅读了一些关于解析器由于前瞻行为而弄乱了开始条件的内容,但我不确定问题出在这里面,也不确定我将如何应对它。

因为你有

BEGIN(INDENT)

规则部分没有模式,它被逐字复制到 yylex 函数的顶部,因此每次调用 yylex 时它 运行s。因此,每次 bison 调用 yylex 获取新令牌时,状态都会重置为 INDENT,您将获得 T_INDENT 个令牌。

在您的 "First Case" 示例中,词法分析器直到 EOF 才 return,因此您只调用它一次,并且它只设置一次 INDENT 状态。

如果您希望此代码仅在您第一次调用 yylex 时 运行,则需要将其设置为仅 运行 一次。类似于:

        { static bool not_first_time;
          if (!not_first_time) {
            BEGIN(INDENT);
            not_first_time = true; } }

或者,进行设置,使 INITIAL 成为预期的初始状态。