我正在学习使用flex,我想出了一个我没有找到答案的问题(甚至在reference中也没有)。假设我有这段代码:
patt1 { do_foo(42); }
patt2 { do_bar(); }
这可能会正常工作。问题是,do_foo
可能需要通过引用接收一个参数(例如,一个int)并用它做一些事情(实际上是foo)。我能想到do_foo
到达该变量的唯一方法是将其声明为全局变量,但根据代码运行的范围,可能还有另一种(更清洁,更好)的解决方案。
有什么想法吗?任何帮助将不胜感激。
提前致谢。
答案 0 :(得分:1)
Flex动作的执行范围是什么?
它们在生成的词法分析器的switch
函数中的巨型yylex()
语句的范围内执行。
问题是,do_foo可能需要通过引用接收一个参数(例如,一个int)并用它做一些事情(实际上是foo)。
可能需要接受什么样的争论?调用动作时唯一运行的是解析器和词法分析器。不是你的代码。所以你甚至无法通过论证,更不用说接受它了。
我能想到do_foo到达该变量的唯一方法是将其声明为全局变量,但根据代码运行的范围,可能还有另一种(更干净,更好)的解决方案。
没有。它可能是生成的词法分析器的文件范围中的static
变量,您可以在第二个%%
之后的代码部分中声明它,以及getter和setter。但它在yylex()
方法中不能是任何类型的局部变量,因为您无法控制何时调用它。
如果有意义,它可以存储在解析器的%union
中。
答案 1 :(得分:1)
实际上,生成的扫描程序看起来像这样,遗漏了很多细节,主要与缓冲区管理有关:
int yylex() {
/* A bit of setup */
while (1) {
do {
yy_current_state = next_state(yy_current_state, get_next_char());
} while (has_no_action(yy_current_state));
yy_act = yy_accept[yy_current_state];
switch (yy_act) {
case 1: /* First action block */
break;
case 2: /* Second action block */
break;
/* etc. */
}
}
}
因此很容易看出动作的去向以及它们所处的范围。为了使这些信息有用,你需要看到可以插入的钩子,所以让我们用一些显式钩子再次写出来:< / p>
YY_DECL {
/* Some declarations */
/******** Prelude block *********/
/* A bit of setup */
while (1) {
do {
yy_current_state = next_state(yy_current_state, get_next_char());
} while (has_no_action(yy_current_state));
yy_act = yy_accept[yy_current_state];
switch (yy_act) {
case 1: YY_USER_ACTION /**** User defined macro ****/
/******** First action block *********/
YY_BREAK
case 2: YY_USER_ACTION
/******** Second action block *********/
YY_BREAK
/* etc. */
}
}
}
最有趣的功能之一是“前奏块”。在你的(f)lex输入文件中,它看起来像这样:
%option ...
%%
/* Prelude block: indented lines before the first pattern */
int locvar = 0;
patt1 { /* first action block */ }
patt2 { /* second action block */ }
这些宏都有合理的默认值:
/* The default definition of YY_DECL will be different if you've
* asked for a reentrant lexer
*/
#ifndef YY_DECL
extern int yylex(void);
#define YY_DECL int yylex(void);
#endif
/* Code executed at the beginning of each rule, after yytext and yyleng
* have been set up.
*/
#ifndef YY_USER_ACTION
#define YY_USER_ACTION
#endif
/* Code executed at the end of each rule. */
#ifndef YY_BREAK
#define YY_BREAK break;
#endif
出于您的目的,其中最有趣的是YY_DECL
。如果要将参数传递给yylex
,可以通过定义此宏来修改原型。如果在yylex
调用期间还需要局部变量,则可以在prelude块中声明它们。 (这对于“推”词法分析器更有用,但它甚至可用于普通词法分析器。)
YY_USER_ACTION
和YY_BREAK
宏更加专业化。虽然它们看起来都可能对调试有用,但通常情况下使用flex的内置跟踪工具要好得多。如果您想跟踪列位置而不仅仅是行号,YY_USER_ACTION
宏非常有用;您可以找到将其用于此目的的示例。对于编译器在YY_BREAK
语句后抱怨break
的情况,break
宏可以设置为空(而不是return
)。
上面代码中未指出的另一个宏是YY_USER_INIT
,它将被合并到一次性初始化代码中(上面也没有显示,抱歉)。
这些功能中的大部分都记录在flex手册中。 YY_DECL
位于Section 9 ("The Generated Scanner"); YY_USER_ACTION
和YY_USER_INIT
位于Section 13 ("Miscellaneous Macros")(以及其他一些功能)。 (YY_BREAK
在该部分的最后描述。)
prelude块是一个Posix功能,因此它也可以在lex
中使用,并在Posix(以及Section 5.2 of the Flex manual)中记录:
在规定任何规则之前出现在规则部分开头的任何此类输入(以
<blank>
或%{
和%}
分隔符行开头)都应写入{{ 1}}在lex.yy.c
函数的变量声明之后和yylex()
中的第一行代码之前。因此,可以在此处声明yylex()
本地的用户变量,以及在进入yylex()
时执行的应用程序代码。
答案 2 :(得分:-1)
您可以创建宏,而不是声明全局变量。由于它是一个预处理指令,它可能“更清洁”。
#define FOO 42
patt1 { do_foo(FOO); }