从野牛解析器调用 flex yy_push_state()

Call flex yy_push_state() from bison parser

是否可以从 bison 生成的解析器调用 yy_push_state()?如何做到这一点?

context:
    /* empty */ { $$ = NULL; yy_push_state(SOME_STATE); }
;

rule:
    context operator STRING { create_expr(, ); }
;

我希望能够从解析器调用 yy_push_state(),并且还想知道这是否是一种可接受的做法。如果不是,有什么替代方案来传达它应该推送状态的词法分析器?

In this specific case, only the parser knows when to push SOME_STATE.

这当然有可能,但有一个巨大的警告。在您提供的示例中,为什么您觉得需要这样做对我来说一点也不明显;可能有替代方案,但在不了解有关用例的更多详细信息的情况下无法提供任何建议。

这是警告。在您提供的示例中,状态推送是由标记生产生成的;从概念上(甚至可能在实践中)你可以使用中间规则操作:

rule:                 { yy_push_state(SOME_STATE); }
      operator STRING { create_expr(, ); }

状态推送会在空产减少时发生;在读取 operator 的第一个标记之前可能会或可能不会发生,但在大多数情况下会在之后发生。因此,例如,如果打算更改词法分析器以识别(或不识别)特定于上下文的运算符,那么它很可能会失败。

bison 如果在解析的那一点完全不需要先行标记,通常会立即减少(没有先行标记),但不能保证这种行为,并且不应依赖恕我直言。其他解析器(例如 yacc)不这样做;较旧的 bison 版本没有 IIRC,并且至少不同的解析器类型(IELR、GLR)可能对是否需要先行标记有不同的看法。

所以总的来说,最好为可能的情况做好准备,即先行令牌已被读取(这就是为什么需要复制 yytext,例如),同时要小心不要假设它会被阅读。

如果您的状态更改足够稳健,那么继续在解析器中执行 yy_push_state

例如,假设 operation 不可为空,并且状态更改将更改识别 STRING 的规则,但不会对任何标记的词法扫描产生任何影响可能会出现在 operator 中。在那种情况下,yy_push_state 是安全的。

我看到这个 hack 尝试的一个地方是试图解析像 awk 和 javascript 这样的语言,其中 / 可能是除法运算符或正则表达式文字的开头。在那种情况下,可以让解析器改变正则表达式情况下的词法状态:

// Lexer
"/"  { return '/';
       /* No semantics, the parser will know what it means */
     }
<REGEX> {
   /* Lots of rules here. But unescaped / is just the same as above */
   "/"  { return '/';
          /* No semantics, the parser will know what it means */
        }
}

// Parser
expr: { BEGIN(REGEX); } '/' regex { BEGIN(INITIAL); } '/'
    | expr '/' expr
    | ...

在上面的例子中,状态变化对词法分析器如何处理 / 没有影响,所以如果那个斜杠被识别为开始(或结束)一个正则表达式,状态更改将在扫描 / 令牌之前或之后(更有可能)发生。如果词法分析器尝试(不必要,但这似乎是一种诱惑)return 不同的标记用于 / 的两种不同用途,这将不会起作用;一个好的准则是,词法分析器对标记的语义了解得越少越好。