注釈付きでマークされた文字列の解析 #12

さて、作成した状態遷移図を基に有限状態機械の実装をする。
手書きで直接Cによる実装をするなら、
状態と入力文字を対のキーとして二重switch文か二次元テーブルでアクションを呼ぶのが手軽だろう。
しかし、対のキーそのものとそれに対応するアクションと遷移先の状態が、
注釈無しの場合と比べて少しだけ変わったり増えたりするだけで、
プログラムの構造自体は変わり映えしないと思うので止めておく。

注釈無しの時の#13や#14で示した状態遷移表のflex用ソースによる表現を使ってみよう。
この表現は表形式に近いように表記することができるのでそれなりに書きやすく見やすいし、
有限状態機械の動作エンジンや駆動用データはflexの自動生成に任せればよいので記述量も多くないと思うからだ。

parser.l
%{
#include <stdio.h>

#ifdef __STRICT_ANSI__
int fileno(FILE *);
#endif

void yyerror(const char *msg);
void yynerror(const char *msg);

void yyerror(const char *msg)
{
    fprintf(stderr, "error: %s\n", msg);
}

void yynerror(const char *msg)
{
    fputc('\n', stderr);
    yyerror(msg);
}
%}

%option main nounput noinput

%x start restart string m_head m_body a_head a_body

%%
    BEGIN(start);

<start>{
  [^()]     { printf("string [%c", yytext[0]); BEGIN(string); }
  \(        { printf("marked-string ["); BEGIN(m_head); }
  \)        { yyerror("illegal right parenthesis"); return 1; }
  <<EOF>>   { yyerror("unexpected EOF"); return 1; }
}

<restart>{
  [^()]     { printf("string [%c", yytext[0]); BEGIN(string); }
  \(        { printf("marked-string ["); BEGIN(m_head); }
  \)        { yyerror("illegal right parenthesis"); return 1; }
  <<EOF>>   return 0;
}

<string>{
  [^()]     ECHO;
  \(        { printf("]\nmarked-string ["); BEGIN(m_head); }
  \)        { yynerror("illegal right parenthesis"); return 1; }
  <<EOF>>   { printf("]\n"); return 0; }
}

<m_head>{
  [^():]    { ECHO; BEGIN(m_body); }
  :         { yynerror("null marked string"); return 1; }
  \(        { yynerror("illegal left parenthesis"); return 1; }
  \)        { yynerror("null string between parentheses"); return 1; }
  <<EOF>>   { yynerror("unexpected EOF"); return 1; }
}

<m_body>{
  [^():]    ECHO;
  :         { printf("]: annotation["); BEGIN(a_head); }
  \(        { yynerror("illegal left parenthesis"); return 1; }
  \)        { printf("]\n"); BEGIN(restart); }
  <<EOF>>   { yynerror("unexpected EOF"); return 1; }
}

<a_head>{
  [^()]     { ECHO; BEGIN(a_body); }
  \(        { yynerror("illegal left parenthesis"); return 1; }
  \)        { yynerror("null annotation"); return 1; }
  <<EOF>>   { yynerror("unexpected EOF"); return 1; }
}

<a_body>{
  [^()]     ECHO;
  \(        { yynerror("illegal left parenthesis"); return 1; }
  \)        { printf("]\n"); BEGIN(restart); }
  <<EOF>>   { yynerror("unexpected EOF"); return 1; }
}

注釈無しの時の#14ほどにはフラットにせず、
状態-入力文字対のうちの同一状態に関する入力文字パターンを開始状態のスコープでまとめている。
複数の状態をまとめることはしていないので表記や生成コードの複雑性やメモリ効率は#13と#14の中間程度だと思う。
基本的に注釈無しの場合と似たようなものとなるのは状態遷移図自体が似ているからだ。
コロンが入力されたときの一部の扱いが変わっているだけである。
生成されたコードの動作も特に問題ないようだ。