野牛的其余部分

问题描述 投票:0回答:1

考虑这个简单的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边匹配它的每一行,因为它是最长的匹配。

bison yacc
1个回答
1
投票

(F)lex不提供类似于正则表达式库中“捕获”的任何机制。 yytext始终是(f)lex模式识别的完整令牌。

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

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

这肯定适用于您的注释情况,我会在不使用换行符终止的情况下编写该代码(部分是因为可能没有一个换行符,而部分原因是几乎可以肯定,换行符是在扫描仪中的其他位置处理的,并且可能是与换行符相关的动作):

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

也许您希望在通过文本之前删除注释中的前导空格(如果有)。在讨论之前,让我建议您实际上可能希望将空白留在原处。注释可能包含一个缩进的代码示例,该示例的格式将通过从所有注释主体中删除开头的空格而破坏。

但是,如果您确实想删除空白,那么您将有两种可能:

  1. 您可以重新扫描令牌,在前缀之后寻找第一个非空白字符,然后将令牌的其余部分复制到yylval中。我想这正是您“使用字符串处理”的意思,我可以理解为什么您可能认为它很丑陋。 (尽管在这种情况下,它具有简单性的优点。)

  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); }
    
© www.soinside.com 2019 - 2024. All rights reserved.