使用 yylex() 从输入字符串中获取标记类型列表

Use yylex() to get the list of token types from an input string

我有一个使用 Bison 和 Flex 制作的 CLI,它变得越来越大和复杂,我正在尝试获取给定输入字符串的完整标记序列(yytokentype 或相应的 yytranslate Bison 符号编号)到解析器。

理想情况下,每次调用 yyerror() 时,我都想存储在解析期间识别的标记序列。我不需要知道 yylval 的、状态、动作等,只需要知道从字符串输入到缓冲区中产生的标记列表。

如果不存在执行此操作的直接方法,那么只需使用从字符串 --> yytokentypes 开始的独立方法即可。

下面的代码只有调试打印输出,一旦我弄清楚如何获取令牌,我将更改为将其存储在我想要的地方。

// When an error condition is reached, yylex() to get the yytokentypes
void yyerror(const char *s)
{
    std::cerr<<"LEX\n";
    int tok; // yytokentype
    do
    {
        tok = yylex();
        std::cerr<<tok<<",";
    }while(tok);
    std::cerr<<"LEX\n";
}

好的,我想出了一种无需重新标记输入字符串即可执行此操作的方法。 Flex 允许您定义 YY_DECL,默认情况下在生成的词法分析器文件中找到它以生成 yylex() 声明:

#ifndef YY_DECL
  //some other stuff
#define YY_DECL int yylex (void)
#endif /* !YY_DECL */

这就到位了

/** The main scanner function which does all the work.
 */
YY_DECL
{
    // Body of yylex() which returns the yytokentype
}

我能做的一件棘手的事情是通过 YY_DECL 重新定义 yylex() 以在每个标记返回给调用者之前捕获它。这使我可以为每次调用存储 yytokentype,而无需一点点更改解析器的行为。下面我只是打印出来进行测试:

#define YY_DECL                                 \
int yylex2(void);                               \
int yylex (void)                                \
{                                               \
    int ret;                                    \
    ret = yylex2();                             \
    std::cerr<<"yylex2 returns: "<<ret<<"\n";   \
    return ret;                                 \
}                                               \

一个更简单的解决方案是使用 YY_DECL 宏更改词法分析器的名称,然后在末尾添加 yylex 的定义:

%{
// ...
#include "parser.tab.h"
#define YY_DECL static int wrapped_lexer(void)
%}

%%
  /* rules */
%%
int yylex(void) {
  int token = wrapped_lexer();
  /* do something with the token */
  return token;
}

话虽如此,除非出于某种原因只读一次源代码,否则仅在遇到错误时才重新扫描输入整体上可能比在遇到错误时保存标记列表更快. Lexing 确实非常快,在许多用例中,语法正确的输入比错误的输入更常见。