我们正在尝试纠正我们的词法分析解析器中的错误。我们正在使用flex而且我们必须支持多行字符串。问题是当字符串的结尾与开头不在同一行时“我们不会计算新行。我们有两个新行的行结尾和\ n从程序员插入字符串中是否有一种方法可以理解行尾并在规则中以某种方式计算它?
答案 0 :(得分:1)
如果您使用%option yylineno
,那么flex将为您维护行数(在变量yylineno
中)。只要你不打电话input
,它就会正确。
有两个小问题:
yylineno
的值对于读取的最后一个字符是正确的,这是令牌中的最后一个字符。但是,如果您有多行令牌,您通常也想知道令牌开头的行号;您需要将yylineno
的值存储在上一个标记的末尾。
幸运的是,您可以将宏YY_USER_ACTION
定义为一些C代码;这将在每个操作的开头插入(包括没有任何明确操作的规则。所以这样的事情将确保以前的值始终可用yylineno_start
:
#define YY_USER_ACTION \
yylineno_start = yylineno_saved; \
yylineno_saved = yylineno;
当然,您还需要声明这些变量。
Flex不跟踪列位置。但也许这对你没关系。否则,您可以向上面提到的YY_USER_ACTION
添加更多代码。简单的方法是保存总字符数,并记录当前行末尾的总字符数。保持总字符数很容易;您每次只需添加yyleng
的值。要保持行开头的计数,您需要检查yylineno
的值是否已更改,如果是,则在令牌中向后搜索以查找最后一个换行符。这听起来效率低下;大部分时间,扫描时间非常短。
以下是仅使用行号跟踪的最小解决方案:
%option yylineno
%option noinput nounput noyywrap nodefault
%{
int yylineno_saved = 1;
#define YY_USER_ACTION \
yylineno_start = yylineno_saved; \
yylineno_saved = yylineno;
%}
%%
int yylineno_start;
[[:space:]] // Ignore whitespace including newlines
[[:digit:]]+ { printf("Integer %s at line %d\n", yytext, yylineno); }
\"(\\(.|\n)|[^\\"])*\" { printf("String from line %d to line %d\n",
yylineno_start, yylineno);
}
. // Ignore everything else
%%
int main(int argc, char** argv) {
return yylex();
}
这是一个更复杂的一个,它也跟踪角色位置,如上面第2点所示。这个使用yylloc
全局变量,这是将完整的令牌边界传递给bison的常用方法。 (请注意,此代码不会与less
或more
合作。如果您使用这些功能,则需要为它们编写包装。)
%option yylineno
%option noinput nounput noyywrap nodefault
%{
/* The following would usually be generated by bison if you
* enable location tracking in your bison definition.
*/
struct YYLTYPE {
int first_line;
int first_column;
int last_line;
int last_column;
};
struct YYLTYPE yylloc = {1,1,1,1};
/* We also need to keep the absolute character position, and the
* position at the beginning of the current line.
*/
int char_position = 0;
int line_start = 0;
#define YY_USER_ACTION \
char_position += yyleng; \
if (yylineno != yylloc.last_line) { \
char* p = yytext + yyleng; \
line_start = char_position; \
while (*--p != '\n') --line_start; \
} \
yylloc.first_line = yylloc.last_line; \
yylloc.first_column = yylloc.last_column; \
yylloc.last_line = yylineno; \
yylloc.last_column = char_position - line_start + 1;
/* Just for show */
void show_with_loc(const char* msg) {
printf("[%d:%d->%d:%d] %s",
yylloc.first_line, yylloc.first_column,
yylloc.last_line, yylloc.last_column,
msg);
}
%}
%%
[[:space:]] // Ignore whitespace including newlines
[[:digit:]]+ { show_with_loc("Integer\n"); }
\"(\\(.|\n)|[^\\"])*\" { show_with_loc("String\n"); }
. // Ignore everything else
%%
int main(int argc, char** argv) {
return yylex();
}
答案 1 :(得分:0)
您可以在Flex中使用state or start condition的概念。
<STRING>
(双引号)时,都会启动一个名为"
的状态。<STRING>
内部,您可以编写不同的规则集,例如 - 当您获得反斜杠后跟字符串中的新行时,您知道它是一个多行字符串。您还可以在\n
州内单独检测换行<STRING>
。 "
(双引号)后,结束<STRING>
州并返回<INITIAL>
州。 source code : string.l
%option noyywrap
%x STRING
%{
int line_count = 1;
%}
%%
\" {
printf("%d: String started\n", line_count);
BEGIN(STRING);
}
<STRING>"\\\n" { line_count++; }
<STRING>\" {
printf("%d: String ended\n", line_count);
BEGIN(INITIAL);
}
<STRING>"\\n" {
printf("new line\n");
}
<STRING>. {
printf("%s\n", yytext);
}
\n { line_count++; }
. {}
%%
int main(int argc,char *argv[]){
yyin = fopen(argv[1], "r"); // taking input from a file
yylex();
printf("\nTotal Lines: %d\n", line_count);
return 0;
}
尝试此输入。
"single line"
"multi\
line"