前: Example-Pascal Lexical Scanner, 上: More Examples


7.3 専門用語の変換

ここでは、 スタート状態を使って、 Flexにより生成されるスキャナの内部に小規模のパーサを作る方法の例を示します。 このコードはThe New Hackers Dictionaryprep.ai.mit.edu、 およびその他の多くのインターネットftpサイトから入手可能なテキスト形式のもの) を入力として受け取り、 すぐに製版および印刷できる状態のTexinfoフォーマットのドキュメントに変換するものです。 このコードはjargon2910.asciiを使ってテスト済みです。

典型的な使い方は以下のとおりです。

     j2t < jargon > jargon.texi
     tex jargon.texi
     lpr -d jargon.dvi

このプログラムは、 使用に耐えるinfoファイルに変換可能なファイルは作成しませんが、 こうした機能は大した困難もなく追加することが可能です。 この例は非常に長いものですが、 大して複雑でもないので、 尻込みしないで研究してみてください。

     
     
     
     
     /*
      * j2t.lex : スタート状態を利用(ひょっとして悪用!)する例
      */
     
     %{
     #define MAX_STATES 1024
     #define TRUE  1
     #define FALSE 0
     #define CHAPTER   "@chapter"
     #define SECTION   "@section"
     #define SSECTION  "@subsection"
     #define SSSECTION "@subsubsection"
     int  states[MAX_STATES];
     int  statep = 0;
     
     int  need_closing = FALSE;
     
     char buffer[YY_BUF_SIZE];
     
     extern char *yytext;
     
     
     
     
     /*
      * このプログラムが生成する*.texinfoファイルの先頭部分を作る。
      * これは標準的なTexinfoヘッダである
      */
     
     void print_header(void)
     {
        printf("\\input texinfo @c -*-texinfo-*-\n");
        printf("@c           %c**start of header\n",'%');
        printf("@setfilename       jargon.info\n");
        printf("@settitle          The New Hackers Dictionary\n");
        printf("@synindex          fn cp\n");
        printf("@synindex          vr cp\n");
        printf("@c           %c**end of header\n",'%');
        printf("@setchapternewpage odd\n");
        printf("@finalout\n");
        printf("@c @smallbook\n");
        printf("\n");
        printf("@c ====================================================\n\n");
        printf("@c This file was produced by j2t.  Any mistakes are *not*\n");
        printf("@c the fault of the jargon file editors.\n");
        printf("@c ====================================================\n\n");
        printf("@titlepage\n");
        printf("@title    The New Hackers Dictionary\n");
        printf("@subtitle Version 2.9.10\n");
        printf("@subtitle Generated by j2t\n");
        printf("@author Eric S. Raymond, Guy L. Steel, and Mark Crispin\n");
        printf("@end titlepage\n");
        printf("@page\n");
        printf("@c ====================================================\n");
        printf("\n\n");
        printf("@unnumbered Preface\n");
        printf("@c          *******\n");
     }
     
     
     
     /*
      * 生成されるTexinfoファイルの末尾の部分を作成する
      */
     
     void print_trailer(void)
     {
        printf("\n");
        printf("@c ====================================================\n");
     
        printf("@contents\n");   /* 目次を表示する */
        printf("@bye\n\n");
     }
     
     
     
     
     /*
      * 後でそれを見つけることができるよう、節または章に下線を引く
      */
     
     void write_underline(int len, int space, char ch)
     {
       int loop;
     
       printf("@c ");
       for(loop=3; loop<space; loop++){
         printf(" ");
       }
     
       while(len--){
         printf("%c",ch);
       }
       printf("\n\n");
     }
     
     
     
     
     /*
      * Texinfoにおいて特殊な意味を持つ文字をチェックし、エスケープする
      */
     
     char *check_and_convert(char *string)
     {
       int  buffpos = 0;
       int  len,loop;
       len = strlen(string);
       for(loop=0; loop<len; loop++){
         if(string[loop] == '@' ||
            string[loop] == '{' ||
            string[loop] == '}')
         {
           buffer[buffpos++] = '@';
           buffer[buffpos++] = string[loop];
         } else {
           buffer[buffpos++] = string[loop];
         }
       }
       buffer[buffpos] = '\0';
       return(buffer);
     }
     
     
     
     
     /*
      * 章、節、項のヘッダを書き出す
      */
     
     void write_block_header(char *type)
     {
       int loop;
       int len;
       (void)check_and_convert(yytext);
       len = strlen(buffer);
       for(loop=0; buffer[loop] != '\n';loop++)
       buffer[loop] = '\0';
       printf("%s %s\n",type,buffer);
       write_underline(strlen(buffer),strlen(type)+1,'*');
     }
     
     %}
     
     
     
     /*
      * Flexの記述情報がここから始まる
      */
     
     %x HEADING EXAMPLE ENUM EXAMPLE2
     %x BITEM BITEM_ITEM
     %s LITEM LITEM2
     
     %%
     
     
     ^#[^#]*"#"  /* ヘッダとフッタをスキップする */
     
     
     
     
                          /*
                           * 章は、その下にアスタリスクを持ち、コロンで終わる
                           */
     ^[^\n:]+\n[*]+\n      write_block_header(CHAPTER);
     
     ^"= "[A-Z]" ="\n"="*  { /* 個々のカテゴリごとに節を作成する */
                             if(need_closing == TRUE){
                               printf("@end table\n\n\n");
                             }
                             need_closing = TRUE;
                             write_block_header(SECTION);
                             printf("\n\n@table @b\n");
                           }
     "Examples:"[^\.]+     ECHO;
     
     
     "*"[^*\n]+"*"         { /* @emph{}(強調された)テキスト */
                             yytext[yyleng-1] = '\0';
                             (void)check_and_convert(&yytext[1]);
                             printf("@i{%s}",buffer);
                           }
     
     "{{"[^}]+"}}"         { /* 特別な強調 */
                             yytext[yyleng-2] = '\0';
                             (void)check_and_convert(&yytext[2]);
                             printf("@strong{%s}",buffer);
                           }
     
     "{"[^}]+"}"           { /* 特別な強調 */
                             yytext[yyleng-1] = '\0';
                             (void)check_and_convert(&yytext[1]);
                             printf("@b{%s}",buffer);
                           }
     
      /* 特殊なTexinfo文字をエスケープする */
     <INITIAL,LITEM,LITEM2,BITEM,ENUM,EXAMPLE,EXAMPLE2>"@"  printf("@@");
     <INITIAL,LITEM,LITEM2,BITEM,ENUM,EXAMPLE,EXAMPLE2>"{"  printf("@{");
     <INITIAL,LITEM,LITEM2,BITEM,ENUM,EXAMPLE,EXAMPLE2>"}"  printf("@}");
     
     
     
      /*
       * @exampleコードを再生成する
       */
     
     ":"\n+[^\n0-9*]+\n"     "[^ ]   {
                             int loop;
                             int len;
                             int cnt;
                             printf(":\n\n@example \n");
                             strcpy(buffer,yytext);
                             len = strlen(buffer);
                             cnt = 0;
                             for(loop=len; loop > 0;loop--){
                               if(buffer[loop] == '\n')
                                  cnt++;
                               if(cnt == 2)
                                   break;
                             }
                             yyless(loop+1);
                             statep++;
                             states[statep] = EXAMPLE2;
                             BEGIN(EXAMPLE2);
                           }
     <EXAMPLE,EXAMPLE2>^\n  {
                           printf("@end example\n\n");
                           statep--;
                           BEGIN(states[statep]);
                         }
     
     
     
      /*
       * @enumerateリストを再生成する
       */
     
     ":"\n+[ \t]*[0-9]+"."   {
                           int loop;
                           int len;
                           printf(":\n\n@enumerate \n");
                           strcpy(buffer,yytext);
                           len = strlen(buffer);
                           for(loop=len; loop > 0;loop--){
                             if(buffer[loop] == '\n')
                                break;
                           }
                           yyless(loop);
                           statep++;
                           states[statep] = ENUM;
                           BEGIN(ENUM);
                         }
     <ENUM>"@"           printf("@@");
     <ENUM>":"\n+"     "[^0-9]    {
                         printf(":\n\n@example\n");
                         statep++;
                         states[statep] = EXAMPLE;
                         BEGIN(EXAMPLE);
                       }
     
     <ENUM>\n[ \t]+[0-9]+"." {
                         printf("\n\n@item ");
                        }
     <ENUM>^[^ ] |
     <ENUM>\n\n\n[ \t]+[^0-9] {
                         printf("\n\n@end enumerate\n\n");
                         statep--;
                         BEGIN(states[statep]);
                       }
     
     
     
      /*
       * 1種類の@itemizeリストを再生成する
       */
     
     ":"\n+":"         {
                         int loop;
                         int len;
     
                         printf(":\n\n@itemize @bullet \n");
                         yyless(2);
                         statep++;
                         states[statep] = LITEM2;
                         BEGIN(LITEM2);
                       }
     <LITEM2>^":".+":" {
                         (void)check_and_convert(&yytext[1]);
                         buffer[strlen(buffer)-1]='\0';
                         printf("@item @b{%s:}\n",buffer);
                       }
     <LITEM2>\n\n\n+[^:\n] {
                         printf("\n\n@end itemize\n\n");
                         ECHO;
                         statep--;
                         BEGIN(states[statep]);
                       }
     
     
     
     
     
      /*
       * リビジョン・ヒストリ部からリストを作成する。
       * ここで"Version"が必要なのは、そうしないと他のルール
       * と衝突するからである
       */
     
     :[\n]+"Version"[^:\n*]+":" {
                         int loop;
                         int len;
                         printf(":\n\n@itemize @bullet \n");
                         strcpy(buffer,yytext);
                         len = strlen(buffer);
                         for(loop=len; loop > 0;loop--){
                           if(buffer[loop] == '\n')
                              break;
                         }
                         yyless(loop);
                         statep++;
                         states[statep] = LITEM;
                         BEGIN(LITEM);
                       }
     <LITEM>^.+":"     {
                         (void)check_and_convert(yytext);
                         buffer[strlen(buffer)-1]='\0';
                         printf("@item @b{%s}\n\n",buffer);
                       }
     <LITEM>^[^:\n]+\n\n[^:\n]+\n  {
                         int loop;
     
                         strcpy(buffer,yytext);
                         for(loop=0; buffer[loop] != '\n'; loop++);
                         buffer[loop] = '\0';
                         printf("%s\n",buffer);
                         printf("@end itemize\n\n");
                         printf("%s",&buffer[loop+1]);
                         statep--;
                         BEGIN(states[statep]);
                       }
     
     
     
      /*
       * @itemize @bulletリストを再生成する
       */
     
     ":"\n[ ]*"*"      {
                         int loop;
                         int len;
                         printf(":\n\n@itemize @bullet \n");
                         len = strlen(buffer);
                         for(loop=0; loop < len;loop++){
                           if(buffer[loop] == '\n')
                              break;
                         }
                         yyless((len-loop)+2);
                         statep++;
                         states[statep] = BITEM;
                         BEGIN(BITEM);
                       }
     <BITEM>^" "*"*"   {
                         printf("@item");
                         statep++;
                         states[statep] = BITEM_ITEM;
                         BEGIN(BITEM_ITEM);
                       }
     <BITEM>"@"          printf("@@");
     <BITEM>^\n        {
                         printf("@end itemize\n\n");
                         statep--;
                         BEGIN(states[statep]);
                       }
     <BITEM_ITEM>[^\:]* {
                          printf(" @b{%s}\n\n",check_and_convert(yytext));
                        }
     <BITEM_ITEM>":"   {
                         statep--;
                         BEGIN(states[statep]);
                       }
     
     
     
      /*
       * @chapter、@section等を再作成する
       */
     
     ^:[^:]*           {
                         (void)check_and_convert(&yytext[1]);
                         statep++;
                         states[statep] = HEADING;
                         BEGIN(HEADING);
                       }
     <HEADING>:[^\n]   {
                         printf("@item @b{%s}\n",buffer);
                         write_underline(strlen(buffer),6,'~');
                         statep--;
                         BEGIN(states[statep]);
                       }
     <HEADING>:\n"*"*  {
                         if(need_closing == TRUE){
                           printf("@end table\n\n\n");
                           need_closing = FALSE;
                         }
                         printf("@chapter %s\n",buffer);
                         write_underline(strlen(buffer),9,'*');
                         statep--;
                         BEGIN(states[statep]);
                       }
     <HEADING>:\n"="*  {
                         if(need_closing == TRUE){
                          printf("@end table\n\n\n");
                           need_closing = FALSE;
                         }
                         printf("@section %s\n",buffer);
                         write_underline(strlen(buffer),9,'=');
                         statep--;
                         BEGIN(states[statep]);
                       }
     <HEADING>"@"        printf("@@");
     <HEADING>:\n"-"*  {
                         if(need_closing == TRUE){
                           printf("@end table\n\n\n");
                           need_closing = FALSE;
                         }
                         printf("@subsection %s\n",buffer);
                         write_underline(strlen(buffer),12,'-');
                         statep--;
                         BEGIN(states[statep]);
                       }
     
     
     
      /*
       * @exampleテキストを再作成する
       */
     
     ^"     "          {
                         printf("@example\n");
                         statep++;
                         states[statep] = EXAMPLE;
                         BEGIN(EXAMPLE);
                       }
     <EXAMPLE>^"     "
     .                 ECHO;
     
     %%
     
     
     
      /*
       * 初期化して実行する
       */
     
     int main(int argc, char *argv[])
     {
       states[0] = INITIAL;
       statep    = 0;
       print_header();
       yylex();
       print_trailer();
       return(0);
     }


このプログラムは、 ASCIIの専門用語ファイルを読み込んで、 いくつかのよく見られるパターンを検索します。 このパターンは、 オリジナルのTexinfo形式の専門用語ファイルを単なるASCIIテキストに変換した際に作成されたものです。 この変換の過程で、 多くのマークアップ情報が失われているために、 ある出力結果の元になったオリジナルの情報がであったか、 あるいは、 そのオリジナルの候補が2つ3つあったとしても、 そのうちのどれがその出力結果をもたらしたかを正確に決定することが困難であるという事情のため、 この検索作業はいくらか複雑なものになります。 よく見られるパターンをいくつか挙げると、 以下のようになります。

章、節、項
これらの先頭にはいずれも同じパターンが来ます。
          :some text:\n
     

この後ろに、 (章の場合は)アスタリスクによる下線、 (節の場合は)等号による下線、 (項の場合は)マイナス記号による下線が続きます。

強調
これは少し難しいのですが、 一般的には強調は(イタリックの場合は)*...*、 (強調文字(strong)の場合は){{...}}、 (太字(bold)の場合は){...}の対によって示されます。 ここでは、 この3種類を検索して、 コマンドを出力します。
実例、および列挙されたリスト
ともにコロンで始まり、 その後ろに、 1つ以上の改行、 少なくとも5つの空白、 そして最後に数字もしくは何らかのテキストが続きます。 例えば、 列挙されたリストは以下のようになります。
          ...enumerated:
          
                0.some text
                1.some more text
     

また、 実例は以下のようになります。1

          ...example:
          
                some text
     

項目化されマークを付けられたリスト
実例、および列挙されたリストによく似ていますが、 違いは、 項目の先頭にコロン、 またはアスタリスクがあり、 末尾にコロンがあるという点です。

ここでの例は、 パースされているものが何であるかを示すヒントとしてこのようなパターンを使い、 その特定のセクション用の部分的なパーサを (ほとんどの場合、排他的)スタート状態を使って作ります。 ASCII版の専門用語ファイルを持っているのであれば、 スキャナのどの部分がそのファイル中の何にマッチするかを検証してみる値打ちがあります。 例えば、 HEADING状態において@itemを生成するルールが、 すべての専門用語のエントリを処理するルールでもあるということは、 おそらく一見しただけでは明らかではないでしょう。


脚注

[1] 訳注:some textの部分に、インデントされたテキストが記されます。