"rest of line" 野牛

"rest of line" in bison

考虑这个简单的 lex/yacc 定义:

在.l:

PRINT { return PRINT;}

在 .y 中:

PRINT printlist
{
  statement_t *new = mkstatement(PRINT);
  new->parms.print.using = NULL;
  new->parms.print.l = ;
  $$ = new;
}

printlist:
expression
{
  printitem_t *new = malloc(sizeof(*new));
  new->e = ;
  new->sep = 0;
  $$ = g_list_prepend(NULL, new);
} | { strings of expressions }

很简单。现在我想查看和存储评论。经典的解决方案是简单的 lex:

"//".*\n

这将整个评论标记为 yacc 端的单个标记。现在我可以使用字符串处理提取实际评论,但这有点像 lex/yacc 是 for。所以我是否缺少一种像 PRINT 一样解析 REM 的简单方法,也就是说,是否有一种简单的方法可以将 "everything else on the line" 获取为 $2?我尝试了几种方法,但总是会导致 lex 端匹配每一行,因为它是最长的匹配。

(F)lex 不提供任何类似于正则表达式库中的 "captures" 的机制。 yytext 始终是 (f)lex 模式识别的完整标记。

有时您可以只使用固定偏移量来提取文本标记中有趣的部分。例如,您可能会看到这种 (f)lex 操作,它从字符串文字中删除引号:(简化;真正的解析器可能会关心反斜杠转义):

["][^"]*["]   { yylval.str = strndup(yytext + 1, yyleng - 2); }

这肯定适用于您的评论案例,我会在没有终止换行符的情况下编写(部分原因是可能没有换行符,部分原因是换行符几乎肯定会在扫描仪的其他地方处理,并且可能有一些与换行符相关的操作):

"//".*         { yylval.str = strndup(yytext + 2, yyleng - 2); return TOK_STRING; }

也许您希望在通过文本之前删除注释中的前导空格(如果有)。在讨论之前,让我建议您实际上可能想要保留空格。也许评论包含一个缩进很好的代码示例,其格式将通过从所有评论正文中删除前导空格来破坏。

但是如果你真的想删除空格,那么你有两种可能:

  1. 您可以重新扫描令牌,查找前缀后的第一个非空白字符,然后将令牌的其余部分复制到 yylval。我想这正是您所说的 "using string processing" 的意思,我明白您为什么会认为它很丑陋。 (虽然在这种情况下它具有简单的优点。)

  2. 您可以使用 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); }