我正在做一些工作来解析我们项目中的内部配置文件。配置可以是一个简单的字符串,也可以是我们为内部使用而创建的“函数”。该函数可以采用字符串类型参数和表示“条件”的特殊类型。我的 flex 令牌文件的一部分如下所示:
[a-zA-Z][a-zA-Z0-9_/=\.]* SAVE_TOKEN; return TSTRING;
"(" SAVE_TOKEN; return TLPAREN;
")" SAVE_TOKEN; return TRPAREN;
"," SAVE_TOKEN; return TCOMMA;
"==" SAVE_TOKEN; return TIFEQUAL;
"!=" SAVE_TOKEN; return TIFNEQUAL;
我的野牛解析器文件的一部分如下所示:
condition: expr TIFEQUAL expr { $$ = new NCondition($1, TIFEQUAL, $3);}
|expr TIFNEQUAL expr { $$ = new NCondition($1, TIFNEQUAL, $3);}
;
sring: TSTRING { $$ = new NString(*$1); }
;
expr: string {$<nstring>$ = $1;}
| string TLPAREN call_args TRPAREN { $$ = new NFunction($1, *$3); }
;
call_args: { $$ = new CallArg(); }
| expr { $$ = new CallArg(); }
| call_args TCOMMA condition { $1->conds.push_back($3); }
| call_args TCOMMA expr { $1->exprs.push_back($3); }
;
这里的冲突是,一个字符串类型允许使用相等的符号“=”,这也是token TIFEQUA的一部分。考虑这样一个函数:
function(arg1, arg2, arg_cond==condition)
解析器将尝试匹配 TSTRING 标记而不是 TIFEQUAL。我做了一些研究,我意识到 flex 是贪婪的,如果两个模式都匹配,它会尝试匹配最长的一个。这是否意味着我的 conflist 必须在野牛级别解决?如果是,我该如何处理?
这是由于 lex 识别器始终识别输入流中可能最长的标记(正如您所注意到的),并且只能通过更改 lex 模式来“修复”。在 bison 中您无能为力,因为到那时为时已晚——该字符串已被识别为单个
TSTRING
标记。
一个明显的可能性是从
=
模式中删除TSTRING
,因为它通常不是名称/标识符中的合法标记。如果你真的想在它们中允许一些 =
,你需要准确地决定它们什么时候应该是 TSTRING
的一部分,而不是一个单独的操作符。您可以在 =
的末尾禁止两个连续的 =
或一个 TSTRING
:
[a-zA-Z](=?[a-zA-Z0-9_/\.])* SAVE_TOKEN; return TSTRING;
这会导致您输入的
arg_cond==condition
被识别为 3 个标记而不是 1 个。但是,像 a==b==c
这样的东西在理论上可以被识别为 a==b == c
或 a == b==c
时会导致语法错误 - - 但它应该是什么还不清楚。