考虑这个简单的lex / yacc定义:
在.l:
PRINT { return PRINT;}
在.y:
PRINT printlist
{
statement_t *new = mkstatement(PRINT);
new->parms.print.using = NULL;
new->parms.print.l = $2;
$$ = new;
}
printlist:
expression
{
printitem_t *new = malloc(sizeof(*new));
new->e = $1;
new->sep = 0;
$$ = g_list_prepend(NULL, new);
} | { strings of expressions }
很简单。现在,我想查看并存储评论。经典的解决方案是简单的lex:
"//".*\n
这会将整个注释标记为yacc方面的单个标记。现在,我可以使用字符串处理来提取实际的注释,但是lex / yacc就是for。因此,我是否缺少一种简单的方法来解析像PRINT这样的REM,也就是说,是否有一种简单的方法可以使“其他所有东西”都变成$ 2?我已经尝试了几件事,但是总是会导致lex边匹配它的每一行,因为它是最长的匹配。
(F)lex不提供类似于正则表达式库中“捕获”的任何机制。 yytext
始终是(f)lex模式识别的完整令牌。
有时,您可以只使用固定偏移量来提取文本令牌的有趣部分。例如,您可能会看到这种(f)lex操作,该操作从字符串文字中删除了引号:(简化;真正的解析器可能会关心反斜杠转义):
["][^"]*["] { yylval.str = strndup(yytext + 1, yyleng - 2); }
这肯定适用于您的注释情况,我会在不使用换行符终止的情况下编写该代码(部分是因为可能没有一个换行符,而部分原因是几乎可以肯定,换行符是在扫描仪中的其他位置处理的,并且可能是与换行符相关的动作):
"//".* { yylval.str = strndup(yytext + 2, yyleng - 2); return TOK_STRING; }
也许您希望在通过文本之前删除注释中的前导空格(如果有)。在讨论之前,让我建议您实际上可能希望将空白留在原处。注释可能包含一个缩进的代码示例,该示例的格式将通过从所有注释主体中删除开头的空格而破坏。
但是,如果您确实想删除空白,那么您将有两种可能:
您可以重新扫描令牌,在前缀之后寻找第一个非空白字符,然后将令牌的其余部分复制到yylval
中。我想这正是您“使用字符串处理”的意思,我可以理解为什么您可能认为它很丑陋。 (尽管在这种情况下,它具有简单性的优点。)
您可以使用start condition将扫描器置于不同的词法上下文中,然后使用常规(f)lex模式来标识注释令牌的有趣部分:
%x SC_COMMENT
%%
"//"[[:blank:]]* { BEGIN(SC_COMMENT); }
<SC_COMMENT>.* { yylval.str = strdup(yytext); return TOK_COMMENT; }
<SC_COMMENT>\n { BEGIN(INITIAL); }
将注释文本与注释令牌本身相关联,从而避免了使用其他解析器规则的麻烦。但是,如果由于某种原因您真的想编写一个冗余的解析器规则,则可以轻松地修改上面的规则以产生两个令牌:
%x SC_COMMENT
%%
"//"[[:blank:]]* { BEGIN(SC_COMMENT); return TOK_COMMENT; }
<SC_COMMENT>.* { yylval.str = strdup(yytext); return TOK_COMMENT_BODY; }
<SC_COMMENT>\n { BEGIN(INITIAL); }