我的lex模式不能匹配我的输入文件,如何纠正它?

时间:2016-09-21 03:01:20

标签: parsing printing match lex

我有一个匹配的简单模式:head + content + tail,我有一个如下所示的lex文件:

$ cat b.l
%{
#include<stdio.h>
%}
%%
"12" {printf("head\n");}
"34" {printf("tail\n");}
.* {printf("content\n");}
%%

我希望在会面时能够满足&#34; 12&#34;它将打印&#34; head&#34;,当遇到&#34; 34&#34;它将打印&#34; tail&#34;,任何其他连续的字符串,它将打印&#34; content&#34;。

所以我编译并运行它:

lex b.l && gcc lex.yy.c -ll
$ echo '12sdaesre34'|a.out
content

我的期望是,它会打印

head
content
tail

但实际上它只打印&#34;内容&#34;线。我有什么不妥,怎么纠正?

谢谢!

1 个答案:

答案 0 :(得分:2)

(F)lex始终匹配最长的令牌。由于.*会匹配任何不包含换行符的序列,因此它会很乐意匹配12sdaesre34。 (在(f)lex中,.匹配除换行符之外的任何字符。)因此,34不再可用于匹配。

要解决此问题,您必须明确要content匹配的内容。例如,以下内容将匹配任何不包含数字的内容:

[^[:digit:]]+   { printf("content\n"); }

您可能希望将新行添加到不匹配的字符列表中:

[^\n[:digit:]]+   { printf("content\n"); }

或者您想要匹配不包含34的最长序列。这很棘手,但可以做到:

([^3]|3+[^34])+   { printf("content\n"); }

但是,这仍然会与初始12匹配,因此它不足以解决问题。

如果您的输入始终由可能与其他内容穿插的12...34形式的字符串组成,则可以匹配整个12...34序列并将其拆分为三个标记。这无疑是最简单的解决方案,因为开始和结束标记具有已知长度。以下第一个模式匹配一​​个不开始12的字符串,在12的第一个实例之前结束,第二个匹配从12开始并结束的字符串在34的第一个实例(匹配)。这两种模式都不会匹配包含不匹配12的输入;所以增加了第三条规则以匹配该案例;它看起来很像第二条规则,但最后并没有包含34的匹配。因为(f)lex总是匹配最长的令牌,所以第三条规则只有在第二条规则失败时才会成功。

([^1]|1+[^12])*         { puts("content"); }
12([^3]|3+[^34])*34     { puts("head content tail"); }
12([^3]|3+[^34])*       { puts("error"); }

通常,您希望实际捕获content的值以传递给调用程序。在第一条规则中,这只是yytext,但在第二条规则中,内容包含从yyleng-4开始的yytext+2个字符(为了删除前导和尾随分隔符)。

对于大多数用途,如果需要保留匹配的令牌,则必须复制匹配的令牌,因为yytext指向词法扫描器使用的内部数据结构,指针将被下一个模式匹配无效。对于第一条规则,您可以使用strcpy创建字符串的副本,但对于第二条规则,您希望自己制作副本:

([^1]|1+[^12])*         { yylval = strcpy(yytext); ... }
12([^3]|3+[^34])*34     { yylval = malloc(yyleng-3);
                          memcpy(yylval, yytext, yyleng-4);
                          yylval[yyleng-4] = '\0';
                          ...
                        }

那些假设yylvalchar*类型的全局变量,并且代码中的某个地方free()由规则保存的字符串。他们还假设您在省略的代码(yylval)中使用...执行某些操作,或者您返回到调用者,并指示是否遇到了头部和尾部。