我编写了一个flex lexer来处理BYOND的.dmi文件格式的文本。里面的内容是由'='分隔的(键,值)对。有效密钥本质上都是关键字(例如“宽度”),无效密钥不是错误:它们只是被忽略。
有趣的是,BYOND的.dmi解析器的当前状态在'='之前使用所有作为其关键字,并且只是忽略任何多余的垃圾。这意味着“\ twidth123”被识别为“宽度”。
我的问题的关键在于允许这种不规则性。在这样做时,我生成的词法分析器从~40-50KB扩展到~13-14MB。作为参考,我提出了以下人为的例子:
%option c++ noyywrap
fill [^=#\n]*
%%
{fill}version{fill} { return 0; }
{fill}width{fill} { return 0; }
{fill}height{fill} { return 0; }
{fill}state{fill} { return 0; }
{fill}dirs{fill} { return 0; }
{fill}frames{fill} { return 0; }
{fill}delay{fill} { return 0; }
{fill}loop{fill} { return 0; }
{fill}rewind{fill} { return 0; }
{fill}movement{fill} { return 0; }
{fill}hotspot{fill} { return 0; }
%%
fill是用于将关键字与“之前的任何内容”合并的规则。在上面运行flex会在我的计算机上产生~13MB lex.yy.cc。只需在填充规则中删除kleene星(*)即可生成45KB的lex.yy.cc文件;然而,显然,这会使词法分析器不正确。
是否有任何技巧,弹性选项或词法分析器黑客可以避免这种疯狂的扩张?我唯一能想到的是:
答案 0 :(得分:1)
使fill
成为一个单独的规则,不执行任何操作,并将其从所有其他规则中删除,并将其定义与空格分开以便清晰起见:
whitespace [ \t\f]
fill [^#=\n]
%%
{whitespace}+ ;
{fill}+ ;
我可能还会避免在词法分析器中构建关键字,只使用执行表查找的identifier [a-zA-Z]+
规则。最后添加一条规则来捕获=
:
. return yytext[0];
让解析器处理所有特殊字符。
答案 1 :(得分:1)
这不是一个真正的问题,flex是"擅长",但如果精确定义它就可以解决。特别是,如果 = 之前的随机字符串包含多个关键字,则必须知道应返回哪些关键字。例如,假设输入为:
garbage_widtheight_moregarbage = 42
现在,是设置width
还是height
?
请记住,弹性扫描程序将选择匹配最长的规则和具有相同长匹配的规则,这是词法描述中的第一个。
所以OP中提出的模型:
fill [^=#\n]*
%%
{fill}width{fill} { return 0; }
{fill}height{fill} { return 0; }
/* SNIP */
总是更喜欢width
到height
,因为匹配的长度相同(都在 = 之前的最后一个字符处终止)和{{1模式在文件中排在第一位。如果规则以相反的顺序编写,则width
将是首选。
另一方面,如果您删除了第二个height
:
{fill}
然后输入中的 last 关键字(在这种情况下,{fill}width{fill} { return 0; }
{fill}height{fill} { return 0; }
)将是首选,因为那个具有更长的匹配。
然而,最可能的要求是识别 first 关键字,因此前面的任何一个都不起作用。为了匹配第一个关键字,首先必须匹配{em>最短可能的height
序列。由于flex不会实现非贪婪的重复,因此只能逐个字符地进行。
以下是一个使用开始条件的示例。请注意,我们会在实际找到{fill}
之前保留关键字令牌,以防=
找不到。
=
在转义转义代码中,您可能希望翻译 /* INITIAL: beginning of a line
* FIND_EQUAL: keyword recognized, looking for the =
* VALUE: = recognized, lexing the right-hand side
* NEXT_LINE: find the next line and continue the scan
*/
%x FIND_EQUAL VALUE
%%
int keyword;
"[#=]".* /* Skip comments and lines with no recognizable keyword */
version { keyword = KW_VERSION; BEGIN(FIND_EQUAL); }
width { keyword = KW_WIDTH; BEGIN(FIND_EQUAL); }
height { keyword = KW_HEIGHT; BEGIN(FIND_EQUAL); }
/* etc. */
.|\n /* Skip any other single character, or newline */
<FIND_EQUAL>{
[^=#\n]*"=" { BEGIN(VALUE); return keyword; }
"#".* { BEGIN(INITIAL); }
\n { BEGIN(INITIAL); }
}
<VALUE>{
"#".* { BEGIN(INITIAL); }
\n { BEGIN(INITIAL); }
[[:blank:]]+ ; /* Ignore space and tab characters */
[[:digit:]]+ { yylval.ival = atoi(yytext);
BEGIN(NEXT_LINE); return INTEGER;
}
[[:digit:]]+"."[[:digit:]]*|"."[[:digit:]]+ {
yylval.fval = atod(yytext);
BEGIN(NEXT_LINE); return FLOAT;
}
\"([^"]|\\.)*\" { char* s = malloc(yyleng - 1);
yylval.sval = s;
/* Remove quotes and escape characters */
yytext[yyleng - 1] = '\0';
do {
if (*++yytext == '\\') ++yytext;
*s++ = *yytext;
} while (*yytext);
BEGIN(NEXT_LINE); return STRING;
}
/* Other possible value token types */
. BEGIN(NEXT_LINE); /* bad character in value */
}
<NEXT_LINE>.*\n? BEGIN(INITIAL);
之类的内容。您可能还希望避免使用物理换行符的字符串值。还有一堆etceteras。它仅用作模型。