使用 lex/yacc 根据上下文不同地解析相同的模式

Parse same pattern differently depending on context with lex/yacc

我的问题是我有一个相同的字符模式,我希望根据它们的上下文以不同的方式解析这些字符。在文件的一部分中,我需要解析形式为“#.#”的版本号,显然,这与浮点数相同。词法分析器总是选择 return 一个浮点数。我想我可以切换规则的顺序来赋予版本 # precedence(?),但是当我需要稍后解析浮点数时,这对我没有任何好处。

我想我可以忘记要求解析器分别 return 版本的每个部分并将浮点数拆分成多个部分,但我希望它为我完成。

版本号的上下文实际上还有更多内容。它的完整形式是 "ABC #.# XYZ",其中 "ABC" 和 "XYZ" 永远不会改变。我尝试了一些方法来利用版本 # 的上下文,但没有成功。

有没有办法为词法分析器提供一些上下文来解析版本的组件?我是否坚持接收浮点数并自己解析它?

你有几种可能性。

最简单的方法是在解析器而不是扫描器中进行字符串到数字的转换。这需要将数字复制为字符串,但开销不应太高:malloc 短字符串在几乎所有平台上都得到了很好的优化。好处是代码非常简单和健壮:

解析器

%union {
      char*  string;
      double number;
      // other types, including version
}

%token <string> DOTTED
%token <number> NUMBER
%type <number> number
%type <version> version
%%
number : NUMBER
       | DOTTED { $$ = atod(); free(); }
version: DOTTED { $$ = make_version(); free(); }

扫描仪

[[:digit:]]+\.[[:digit:]]+     { yylval.string = strdup(yytext); return DOTTED; }
[[:digit:]]+\.?|\.[[:digit:]]+ { yylval.number = atod(yytext); }

上面假设版本号总是单点的,就像在 OP 中一样。在版本号可能有多个点或非数字字符的应用程序中,您最终会得到三种可能的标记类型:明确的数字、明确的版本字符串和可以是其中之一的单点数字字符串。除了将 VERSION 标记类型和明确版本字符串的模式添加到扫描器之外,唯一的变化是将 | VERSION 添加到解析器中的 version 生产。

另一种可能性,如果您可以在扫描仪中轻松确定是否需要数字或版本,则使用开始条件。您也可以从解析器更改条件,但它更复杂,您需要了解解析算法以确保您正确传达状态更改。

最后,您可以在扫描器中执行两个 转换,并在解析器中减少令牌时选择正确的转换。如果版本是一个小而简单的数据结构,这可能是最优的。