在实际开发中,哪怕是最简单的问题,通常也是缠绕的。这也是让程序员最为费心的问题之一。比如识别注释。注释以 /*开始,然后什么也不用管,直到遇到*/结束。自C++发明以来,又增加了//前导的单行注释。但是光根据这样的注释规则并不能识别注释,因为源程序中还有字符串。字符串中含有的注释符并不作为注释解释。所以识别注释必须同时识别字符串。字符串的格式是以"开始,然后是任意字符,直到再以引号结束。
为了避免过于繁琐,这里用lex文法来描述这个问题。lex的格式是:
模式 识别动作
于是,是这样一些规则:
%%
“.*” {printf("{string-S}");ECHO;printf("{String-E}");}
/*.**/ {printf("/*…commentA…*/\n");}
//.**\n {printf("//…commentB…\n"); }
. {ECHO;}
为了防止与lex元符号冲突,实际的代码还需加上\转义符保护。像这样:
%%
\".*\" {printf("{string}");ECHO;printf("{\\String}");}
\/\*.*\*\/ {printf("/*...commentA...*/\n");}
\/\/.*\n {printf("//....commentB...\n"); }
. {ECHO;}
%%
int yywrap()
{
return 1;
}
int main()
{
yylex();
return 0;
}
这就基本可以用了。但是还不严格。这里注释不能越行,字符串里也不能含有引号。为了满足这两个要求,规则还要更复杂。
%%
\"([^\\"]|\\\")*\" {printf("{string}");ECHO;printf("{\\String}");}
\/\*([^\*]|\*[^\/])*\*\/ {printf("/*...commentA...*/\n");}
\/\/.*\n {printf("//....commentB...\n"); }
. {ECHO;}
%%
int yywrap()
{
return 1;
}
int main()
{
yylex();
return 0;
}
这样就可以了。实际开发中遇到的缠绕问题会更复杂。但总是可以为每个状态节点分配一个不同的字符,从而转化为词法识别的问题。值得一提的是,
最后,软件设计的单位,和问题缠绕的粒度相关。