単純なマーク付けがなされた文字列の解析 #14

前回示したflex用コードと#10や#11の有限状態機械のC実装とは表現の違いに過ぎないと述べたが、
生成されるプログラムの動作は変えないようにflex用コードの規則部を以下のように変更するとさらによく分かると思う。

...snip
%%
    BEGIN(start);

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

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

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

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

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

これは、複数の開始状態をカンマ区切りでまとめたり、
開始状態のスコープを定義して複数の規則をまとめたりしていた前回のコードを解いて平坦にしたものである。
この形式にすると、まさに#12で図示した状態遷移表そのままであることが分かる。
開始状態と入力文字の組が状態遷移表のstateとcの組であり、
アクション内にBEGINマクロがあればその引数が次に遷移すべき状態を、
return 0;は受容状態への遷移を、
return 1;がエラー状態への遷移を、
そして、そのどれもが無ければ現在の状態自身への遷移を表している。
もちろんprintfやyyerror、yynerrorの呼び出しが行うべきactionそのものである。