Flex令人困惑,无法逐字符转换字符串

问题描述

我想使用flex根据简单规则转换字符串。我有一些规则,例如第一个字符保持不变,第二个和第三个字符可能会更改。就像第二个字符是字母一样,它变成下面规则中列出的数字。如果第三个字母是数字,它将变成一个特定的字母。

%%
           /*^[a-z] {char *yycopy = strdup( yytext ); unput(yycopy[0]);}*/
[ajs]  {putchar('1');}
[bkt]  {putchar('2');}      
[clu]  {putchar('3');}
[dmv]  {putchar('4');}
[1] {putchar('j');}
[2] {putchar('k');}
[3] {putchar('l');}
[4] {putchar('m');}  /*more number rules till 9*/
%%
int yywrap(void){return 1;}

int main( int argc,char **argv )
             {
             ++argv,--argc;  /* skip over program name */
             if ( argc > 0 )
                     yyin = fopen( argv[0],"r" );
             else
                     yyin = stdin;

             while (yylex());
             }

如果字符串中不同位置的字符有不同的规则,我如何使用开始条件来更改特定字符(即第二个和第三个字符的规则不同)。

解决方法

您可以使用BEGIN操作切换启动条件。 Flex永远不会自动更改启动条件,因此,当您需要返回到初始启动条件(称为INITIAL)时,必须明确地执行此操作(BEGIN(INITIAL))。

您通常需要使用%x命令在(f)lex序言中声明开始条件名称。 (%s也可以,但是具有不同的语义。有关详细信息,请参见the Flex manual。)

通过使用尖括号中的开始条件名称来启动规则来指示开始条件适用于规则。您可以在尖括号中放置一个以上的起始条件。用逗号分隔它们,不要使用空格。也不要在尖括号后放置空格;它们是模式的一部分,(f)lex模式不能包含未引用的空格字符。

BEGIN是一个宏,不需要在开始条件名称前后加上括号,但是我建议始终使用它们,因此您不必担心宏会扩展为什么。起始条件名称是小整数(enum常量或预处理器宏),但没有任何保证其值的值,因此请不要作假设。

就是这样。因此,您可以通过以下方式实现您的天文数字编纂器:

%x SECOND THIRD REST
%%
[a-z]          ECHO; BEGIN(SECOND);
<SECOND>[ajs]  putchar('1'); BEGIN(THIRD);
  /* More SECOND rules */
<THIRD>1       putchar('j'); BEGIN(REST);
  /* More THIRD rules */
<*>.*\n?       ECHO; BEGIN(INITIAL);

(我故意不添加任何<REST>规则,因为结尾处的后备覆盖了它。我还故意在第一个规则中省略了锚点,因为我的规则保证INITIAL的开始条件是9nly在行首生效。请参阅最后一条规则。最后一条规则指定了一个可选的换行符,以防文件不以换行符结尾,尽管在技术上无效,但偶尔会发生这种情况。)