每次调用 yylex() 是否为输入生成一个标记或所有标记?

Does each call to `yylex()` generate a token or all the tokens for the input?

我想了解 flex 的底层工作原理。

每次调用 yylex() 是为输入生成一个标记还是所有标记?

为什么两个例子中yylex()被调用的次数不同?

我听说 yylex() 就像一个协程,每次调用它都会使用上次调用留下的剩余输入恢复并生成一个令牌。从这个意义上讲,第一个示例如何只调用一次 yylex() 并生成输入中的所有标记?

/* just like Unix wc */
%{
int chars = 0;
int words = 0;
int lines = 0;
%}
%%
[a-zA-Z]+  { words++; chars += strlen(yytext); }
\n         { chars++; lines++; }
.          { chars++; }
%%
main(int argc, char **argv)
{
  yylex();
  printf("%8d%8d%8d\n", lines, words, chars);
}


$ ./a.out
The boy stood on the burning deck
shelling peanuts by the peck
^D
2 12 63
$

/* recognize tokens for the calculator and print them out */
%{
   enum yytokentype {
     NUMBER = 258,
     ADD = 259,
     SUB = 260,
     MUL = 261,
     DIV = 262,
     ABS = 263,
     EOL = 264
   };
   int yylval;
%}
%%
"+"    { return ADD; }
"-"    { return SUB; }
"*"    { return MUL; }
"/"    { return DIV; }
"|"    { return ABS; }
[0-9]+ { yylval = atoi(yytext); return NUMBER; }
\n     { return EOL; }
[ \t]  { /* ignore whitespace */ }
.      { printf("Mystery character %c\n", *yytext); }
%%
main(int argc, char **argv)
{
  int tok;
  while(tok = yylex()) {
    printf("%d", tok);
    if(tok == NUMBER) printf(" = %d\n", yylval);
    else printf("\n");
  }
}

$ ./a.out
a / 34 + |45
Mystery character a
262
258 = 34
259
263
258 = 45
264
^D
$

Flex 不决定扫描器何时 return(默认 EOF 规则除外)。它构建的扫描器在循环中执行词法操作,直到某个操作 returns。因此,如何构建扫描仪完全取决于您。

但是,经典的 yyparse/yylex 处理模型包括每次需要新标记时调用 yylex() 的解析器。因此,一旦找到令牌,它就会立即期望 yylex() 到 return。

在您的第一个代码示例中,没有解析器,扫描器操作仅限于打印出令牌。虽然这个例子完全正确,依靠扫描器循环重复执行操作,但我更喜欢第二种模型,即使您(还)不打算添加解析器,因为它可以更容易地将令牌处理与令牌生成。

但这并不意味着每个词法动作都将包含一个 return 语句。一些词法模式对应于非标记(例如注释和空格),并且相应的操作很可能什么都不做(除了可能记录输入位置)以便扫描器将继续搜索标记到 return.

(F)lex 扫描器不容易变成协程,所以如果确实需要协程(例如,增量解析异步输入),那么可能首选其他工具。

Bison 确实提供了生成 "push parser" 的可能性,其中扫描器 每次找到标记时都会调用 解析器,而不是 returning 到 解析器。但是 "push" 和传统的 "pull" 模型都与协程没有任何关系,恕我直言,使用这个词来描述 parser/scanner 交互让我觉得不精确和无用(尽管我有非常尊重您可能引用的作者。)