"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; }
也许您希望在通过文本之前删除注释中的前导空格(如果有)。在讨论之前,让我建议您实际上可能想要保留空格。也许评论包含一个缩进很好的代码示例,其格式将通过从所有评论正文中删除前导空格来破坏。
但是如果你真的想删除空格,那么你有两种可能:
您可以重新扫描令牌,查找前缀后的第一个非空白字符,然后将令牌的其余部分复制到 yylval
。我想这正是您所说的 "using string processing" 的意思,我明白您为什么会认为它很丑陋。 (虽然在这种情况下它具有简单的优点。)
您可以使用 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); }
考虑这个简单的 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; }
也许您希望在通过文本之前删除注释中的前导空格(如果有)。在讨论之前,让我建议您实际上可能想要保留空格。也许评论包含一个缩进很好的代码示例,其格式将通过从所有评论正文中删除前导空格来破坏。
但是如果你真的想删除空格,那么你有两种可能:
您可以重新扫描令牌,查找前缀后的第一个非空白字符,然后将令牌的其余部分复制到
yylval
。我想这正是您所说的 "using string processing" 的意思,我明白您为什么会认为它很丑陋。 (虽然在这种情况下它具有简单的优点。)您可以使用 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); }