我试图解析一种具有某种"转义序列的语言"使用flex / bison。目前,我很难定义词法分析器。通过示例解释最简单:
Text --> {if} test {literal} text 43.21 {if} foo {/literal} {if}
---- ---- --------- ------------------- ---------- ---- etc.
Desired --> IF TEXT (ignore) TEXT (ignore) IF
Token
如您所见,该语言包含一些终端符号,例如IF
或TEXT
,这些符号非常简单。
但是,{literal}
和{/literal}
之间的所有内容都是TEXT
,即使它包含的字符串本来也是特殊的令牌。
到目前为止,我可以为词法分析器找到最好的,它使用Start Conditions在不同状态之间跳转:如果遇到{literal}
,它会激活LITERAL
规则。
%{
#include <stdio.h>
#define YY_DECL int yylex()
%}
%x LITERAL
%%
[^{]+ {printf("TEXT: %s\n", yytext);}
"{if}" {printf("IF\n");}
"{literal}" {BEGIN(LITERAL);}
<LITERAL>[^{]+ {printf("TEXT: %s\n", yytext);}
<LITERAL>"{" {printf("TEXT: %s\n", yytext);}
<LITERAL>"{/literal}" {BEGIN(INITIAL);}
%%
main() {yylex();}
但是如何离开LITERAL
州?将此定义与上面的示例一起使用
IF
TEXT: test
TEXT: text 43.21
TEXT: {
TEXT: if} foo
IF
换句话说,TEXT
标记中的{literal}
令牌会在{
处拆分。我怎么能避免这个?
答案 0 :(得分:2)
{literal}
内的文字在{
分开,因为您匹配{
;如果您不希望拆分文本,则需要使用LITERAL
开始条件中的规则扩展匹配,而不是每个创建新匹配。这是一个相当常见的(f)lex习语,并且有一个专门为此目的而设计的功能:yymore
:
yymore()
告诉扫描器下次匹配规则时,相应的令牌应该附加到yytext的当前值而不是替换它。
(来自flex manual。)
使用这个方便的功能,我们可以写:
"{literal}" {BEGIN(LITERAL);}
<LITERAL>[^{]+ {yymore();}
<LITERAL>"{" {yymore();}
<LITERAL>"{/literal}" {
/* Now we have to provide the token, but we've matched
* 10 extra characters, the close marker, and so the
* token is the text from yytext with length yyleng-10.
* Here we just print it out, but normally we'd copy
* yytext to a temporary for future processing.
* Most compilers will optimize out the call to strlen.
*/
BEGIN(INITIAL);
printf("TEXT: %.*s\n",
(int)(yyleng - strlen("{/literal}")),
yytext);
}
以上假设LITERAL
状态字面意思是字面意思:),也就是说,它仅以{/literal}
标记终止,并始终识别{/literal}
标记,无论上下文。但是,它并不依赖于此;您可以在文字扫描中进行更复杂的令牌识别,只要您在除关闭标记的操作之外的每个操作中始终使用yymore()
。
如果我的假设是正确的,可以使用另一种解决方案:只需将整个文字与正则表达式匹配即可。用非贪婪的匹配(或者甚至直接作为有限状态机)编写正则表达式会更容易,但不幸的是flex并没有实现那些,所以必须做很长的事情,而且它确实很长。幸运的是,结束标记以一个未包含在结束标记内的字符开始,因此可以相当容易地机械地生成正则表达式。在这里,我使用了flex定义来避免一个非常长的行,并使模式更明显:
l1 [{]
l2 "/"[{]
l3 "/l"[{]
l4 "/li"[{]
l5 "/lit"[{]
l6 "/lite"[{]
l7 "/liter"[{]
l8 "/litera"[{]
l9 "/literal"[{]
loop [{](l1|l2|l3|l4|l5|l6|l7|l8|l9)*
n1 [^{/]
n2 "/"[^{l]
n3 "/l"[^{i]
n4 "/li"[^{t]
n5 "/lit"[^{e]
n6 "/lite"[^{r]
n7 "/liter"[^{a]
n8 "/litera"[^{l]
n9 "/literal"[^{}]
next n1|n2|n3|n4|n5|n6|n7|n8|n9
prefix "{literal}"
middle ([^{]|{loop}{next})*
suffix {loop}"/literal}"
literal {prefix}{middle}{suffix}
%%
{literal} {
/* The token includes both the {literal} opener and
* the {/literal} closer, so we need to get rid of
* both of them.
*/
printf("TEXT: %.*s\n",
(int)(yyleng - strlen("{literal}") - strlen("{/literal}")),
yytext + strlen("{literal}"));
}