我正在为支持嵌套评论的语言编写flex扫描程序,如下所示:
/*
/**/
*/
我习惯使用支持递归调用lex扫描器的ocaml / ocamllex非常优秀。但我现在切换到c ++ / flex,如何处理这样的嵌套注释?
答案 0 :(得分:4)
假设只有注释可以嵌套在注释中,堆栈对于使用简单计数器可以实现的目标来说是一个非常昂贵的解决方案。例如:
%x SC_COMMENT
%%
int comment_nesting = 0; /* Line 4 */
"/*" { BEGIN(SC_COMMENT); }
<SC_COMMENT>{
"/*" { ++comment_nesting; }
"*"+"/" { if (comment_nesting) --comment_nesting;
else BEGIN(INITIAL); }
"*"+ ; /* Line 11 */
[^/*\n]+ ; /* Line 12 */
[/] ; /* Line 13 */
\n ; /* Line 14 */
}
一些解释:
第4行:在yylex
函数顶部插入第一条规则之前的缩进行,可用于声明和初始化局部变量。我们使用它在每次调用yylex
时将注释嵌套深度初始化为0。必须维护的不变量是comment_nesting
状态下INITIAL
始终为0。
第11-13行:一个更简单的解决方案是单一模式.|\n
。 ,但这会导致每个评论字符被视为单独的子语句。即使相应的操作什么都不做,这也会导致扫描循环被破坏,并且每个字符都会执行动作切换语句。因此,尝试一次匹配多个字符通常会更好。
我们需要注意 / 和 * 字符;我们只能忽略那些我们确定不是 * / 的一部分的星号,它终止了(可能是嵌套的)注释。因此第11行和第12行。(第12行将不匹配星号序列,后面跟着 / ,因为这些已经与上面的模式匹配,在第9行。)我们需要忽略 / 如果后面没有 * 。因此第13行。
第14行:但是,匹配太大的令牌也可能是次优的。
首先,flex没有针对大型令牌进行优化,而且注释可能非常大。如果flex需要在令牌中间重新填充其缓冲区,它将在新缓冲区中保留打开令牌,然后从令牌的开头重新扫描。
其次,弹性扫描仪可以自动跟踪当前的行号,并且相对有效地进行。扫描程序仅在与可能与换行符匹配的模式匹配的标记中检查换行符。但是需要扫描整个比赛。
我们通过将评论中的换行符作为单独的标记进行匹配来减少这两个问题的影响。 (第14行,也见第12行)这将yylineno
扫描限制为单个字符,并且还限制了内部注释令牌的预期长度。注释本身可能非常大,但每行可能被限制在合理的长度,从而避免了缓冲区重新填充时可能的二次重新扫描。
答案 1 :(得分:2)
我通过使用yy_push_state,yy_pop_state解决此问题并启动这样的条件:
%x comment
%%
"/*" {
yy_push_state(comment);
}
<comment>{
"*/" {
yy_pop_state();
}
"/*" {
yy_push_state(comment);
}
}
%%
通过这种方式,我可以处理任何级别的嵌套注释。
答案 2 :(得分:0)
与嵌套的东西相同:Stacks
堆叠openings
,直到找到各自的closings
。如果堆栈末端不均匀,您将错过开头或关闭元素。