Next: , Previous: Handling Comments, Up: Useful Code


9.2 文字列リテラルの処理

文字列は、 それが入力として与えられた時に破棄されないという点で、 コメントとは若干異なります。 しかし、 基本的なアプローチは同じです。 第1の方法としては、 input()を使って文字列を処理することができます。 コードは以下のようになります。

     
     
     
     /*
      * string1.lex: input()を使って文字列を処理する
      */
     
     %{
     #include <stdio.h>
     #include <malloc.h>
     #include <ctype.h>
     
     
     #define ALLOC_SIZE 32 /* バッファの(再)割り当て用 */
     
     #define isodigit(x) ((x) >= '0' && (x) <= '7')
     #define hextoint(x) (isdigit((x)) ? (x) - '0'\
                                        : ((x) - 'A') + 10)
     void yyerror(char *message)
     {
       printf("\nError: %s\n",message);
     }
     
     %}
     %%
     
     \" {
        int  inch,count,max_size;
        char *buffer;
        int  temp;
        buffer   = malloc(ALLOC_SIZE);
        max_size = ALLOC_SIZE;
        inch     = input();
        count    = 0;
        while(inch != EOF && inch != '"' && inch != '\n'){
           if(inch == '\\'){
             inch = input();
             switch(inch){
             case '\n': inch = input(); break;
             case 'b' : inch = '\b';    break;
             case 't' : inch = '\t';    break;
             case 'n' : inch = '\n';    break;
             case 'v' : inch = '\v';    break;
             case 'f' : inch = '\f';    break;
             case 'r' : inch = '\r';    break;
             case 'X' :
             case 'x' : inch = input();
                        if(isxdigit(inch)){
                          temp = hextoint(toupper(inch));
                          inch = input();
                          if(isxdigit(inch)){
                            temp = (temp << 4) +
                                  hextoint(toupper(inch));
                          } else {
                            unput(inch);
                          }
                          inch = temp;
                        } else {
                          unput(inch);
                          inch = 'x';
                        }
                break;
             default:
                if(isodigit(inch)){
                   temp = inch - '0';
                   inch = input();
                   if(isodigit(inch)){
                     temp = (temp << 3) + (inch - '0');
                   } else {
                     unput(inch);
                     goto done;
                   }
                   inch = input();
                   if(isodigit(inch)){
                     temp = (temp << 3) + (inch - '0');
                   } else {
                     unput(inch);
                   }
                done:
                   inch = temp;
                }
             }
          }
           buffer[count++] = inch;
           if(count >= max_size){
              buffer = realloc(buffer,max_size + ALLOC_SIZE);
              max_size += ALLOC_SIZE;
           }
           inch = input();
        }
        if(inch == EOF || inch == '\n'){
          yyerror("Unterminated string.");
        }
        buffer[count] = '\0';
        printf("String = \"%s\"\n",buffer);
        free(buffer);
      }
     .
     \n
     %%

このスキャナは、 複数行にわたる文字列や、 様々なエスケープ・シーケンスを処理します。 また、 文字列がどのような長さでも構わないように、 動的バッファを使っています。 これと同じことをスタート状態を使って行うコードは、 以下のようになります。

     
     
     
     
     /*
      * string2.lex: スタート状態を使って文字列をスキャンする例
      */
     
     %{
     #include <ctype.h>
     #define isodigit(x) ((x) >= '0' && (x) <= '7')
     #define hextoint(x) (isdigit((x)) ? (x) - '0' \
                                       : ((x) - 'A') + 10)
     char *buffer      = NULL;
     int  buffer_size  = 0;
     void yyerror(char *message)
     {
       printf("\nError: %s\n",message);
     }
     
     %}
     
     %x STRING
     
     hex (x|X)[0-9a-fA-F]{1,2}
     oct [0-7]{1,3}
     %%
     
     \"                {
                         buffer      = malloc(1);
                         buffer_size = 1; strcpy(buffer,"");
                         BEGIN(STRING);
                       }
     <STRING>\n        {
                          yyerror("Unterminated string");
                          free(buffer);
                          BEGIN(INITIAL);
                       }
     <STRING><<EOF>>   {
                          yyerror("EOF in string");
                          free(buffer);
                          BEGIN(INITIAL);
                       }
     <STRING>[^\\\n"]  {
                         buffer_size += yyleng;
                         buffer = realloc(buffer,buffer_size+1);
                         strcat(buffer,yytext);
                       }
     
     <STRING>\\\n      /* エスケープされた改行を無視する */
     <STRING>\\{hex} {
                         int temp =0,loop = 0, foo;
                         for(loop=yyleng-2; loop>0; loop--){
                           temp <<= 4;
                           foo    = toupper(yytext[yyleng-loop]);
                           temp += hextoint(foo);
                         }
                         buffer = realloc(buffer,buffer_size+1);
                         buffer[buffer_size-1] = temp;
                         buffer[buffer_size]   = '\0';
                         buffer_size += 1;
                       }
     <STRING>\\{oct} {
                         int temp =0,loop = 0;
                         for(loop=yyleng-1; loop>0; loop--){
                           temp  <<= 3;
                           temp  += (yytext[yyleng-loop] - '0');
                         }
                         buffer = realloc(buffer,buffer_size+1);
                         buffer[buffer_size-1] = temp;
                         buffer[buffer_size]   = '\0';
                         buffer_size += 1;
                       }
     <STRING>\\[^\n]   {
                         buffer = realloc(buffer,buffer_size+1);
                         switch(yytext[yyleng-1]){
                         case 'b' : buffer[buffer_size-1] = '\b';
                                    break;
                         case 't' : buffer[buffer_size-1] = '\t';
                                    break;
                         case 'n' : buffer[buffer_size-1] = '\n';
                                    break;
                         case 'v' : buffer[buffer_size-1] = '\v';
                                    break;
                         case 'f' : buffer[buffer_size-1] = '\f';
                                    break;
                         case 'r' : buffer[buffer_size-1] = '\r';
                                    break;
                         default  : buffer[buffer_size-1] =
                                         yytext[yyleng-1];
                         }
                         buffer[buffer_size] = '\0';
                         buffer_size += 1;
                       }
     <STRING>\"        {
                         printf("string = \"%s\"",buffer);
                         free(buffer);
                         BEGIN(INITIAL);
                       }
     %%

このスキャナは、 string1.lexよりもモジュール化されていて、 おそらくはより分かりやすいでしょう。 エラーのルールは、 INITIAL状態に戻るようになっていることに注意してください。 こうしないと、 スキャナは不当な文字列と正当な文字列とを結合してしまいます。 ここでも、 Flexのバッファ(YY_BUF_SIZE)が十分に大きいということをあてにせず、 動的バッファを使いました。 内部バッファが十分に大きいという確信が持てるのであれば、 yytextだけを使うことも可能です。 この場合には、 yytextの右端が確実に最初の位置に留まるようにすることが重要です。 より詳しい情報については、 Flex and Cyymoreの項を参照してください。