每次调用 yylex() 是否为输入生成一个标记或所有标记?
Does each call to `yylex()` generate a token or all the tokens for the input?
我想了解 flex 的底层工作原理。
在下面的第一个例子中,似乎 main()
只调用了一次 yylex()
,而 yylex()
为整个输入生成了所有标记。
在第二个示例中,似乎 main()
每次生成令牌都会调用一次 yylex()
,而 yylex()
每次调用都会生成一个令牌。
每次调用 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 交互让我觉得不精确和无用(尽管我有非常尊重您可能引用的作者。)
我想了解 flex 的底层工作原理。
在下面的第一个例子中,似乎
main()
只调用了一次yylex()
,而yylex()
为整个输入生成了所有标记。在第二个示例中,似乎
main()
每次生成令牌都会调用一次yylex()
,而yylex()
每次调用都会生成一个令牌。
每次调用 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 交互让我觉得不精确和无用(尽管我有非常尊重您可能引用的作者。)