flex动作执行的范围是什么?

时间:2015-12-08 01:26:02

标签: c scope compiler-construction flex-lexer lex

我正在学习使用flex,我想出了一个我没有找到答案的问题(甚至在reference中也没有)。假设我有这段代码:

patt1   { do_foo(42); }
patt2   { do_bar(); }

这可能会正常工作。问题是,do_foo可能需要通过引用接收一个参数(例如,一个int)并用它做一些事情(实际上是foo)。我能想到do_foo到达该变量的唯一方法是将其声明为全局变量,但根据代码运行的范围,可能还有另一种(更清洁,更好)的解决方案。

有什么想法吗?任何帮助将不胜感激。

提前致谢。

3 个答案:

答案 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_ACTIONYY_BREAK宏更加专业化。虽然它们看起来都可能对调试有用,但通常情况下使用flex的内置跟踪工具要好得多。如果您想跟踪列位置而不仅仅是行号,YY_USER_ACTION宏非常有用;您可以找到将其用于此目的的示例。对于编译器在YY_BREAK语句后抱怨break的情况,break宏可以设置为空(而不是return)。

上面代码中未指出的另一个宏是YY_USER_INIT,它将被合并到一次性初始化代码中(上面也没有显示,抱歉)。

这些功能中的大部分都记录在flex手册中。 YY_DECL位于Section 9 ("The Generated Scanner"); YY_USER_ACTIONYY_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); }