Bison &引用;“行的其余部分”;野牛

Bison &引用;“行的其余部分”;野牛,bison,yacc,Bison,Yacc,考虑一下这个简单的lex/yacc定义: 在第1段中: PRINT { return PRINT;} 年: PRINT printlist { statement_t *new = mkstatement(PRINT); new->parms.print.using = NULL; new->parms.print.l = $2; $$ = new; } printlist: expression { printitem_t *new = malloc(siz

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

在第1段中:

PRINT { return PRINT;}
年:

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 }
很简单。现在我想查看和存储评论。经典的解决方案很简单:

"//".*\n
这将整个注释标记为yacc端的单个标记。现在,我可以使用字符串处理提取实际注释,但这就是lex/yacc的用途。那么,我是否缺少一种像打印一样解析REM的简单方法,也就是说,有没有一种简单的方法可以将“行中的所有其他内容”都转换为2美元?我已经尝试了几种方法,但总是会导致lex端匹配每一行,因为它是最长的匹配。

(F)lex不提供任何类似于regex库中“捕获”的机制
yytext
始终是(f)lex模式识别的完整标记

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

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

也许您更愿意在传递文本之前删除注释中的前导空格(如果有)。在讨论这一点之前,我建议您实际上可能希望保留空白。可能注释包含一个缩进良好的代码示例,删除所有注释正文中的前导空格将破坏其格式

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

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

  • 您可以使用a将扫描器置于不同的词汇上下文中,然后使用普通(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\u注释
    %%
    “/”[[:blank:][]*{BEGIN(SC_COMMENT);}
    .*{yylval.str=strdup(yytext);返回TOK_COMMENT;}
    \n{开始(首字母);}
    
    它将注释文本与注释标记本身相关联,从而避免使用额外的解析器规则。但是,如果出于某种原因,您确实想编写一个冗余解析器规则,那么您可以很容易地修改上述内容,以生成两个令牌:

    %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); }
    
    %x SC\u注释
    %%
    “/”[[:blank:][]*{BEGIN(SC_COMMENT);return TOK_COMMENT;}
    .*{yylval.str=strdup(yytext);返回TOK_COMMENT_BODY;}
    \n{开始(首字母);}
    

  • 野牛消耗代币,而不是线。没有内置的行的概念,所以如果你想要一个叫做“行”的东西,你需要制定规则来识别它:在lexer中将它转换成token(s),然后在解析器中解析它。综观这些选项,看起来修剪才是正确的选择。我考虑了状态,这在大多数/*风格注释的引用中都有显示,但在本例中,我发现结果比处理字符串更混乱,这是经典的C。因此,字符串黑客就是这样!
    %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); }