注釈付きでマークされた文字列の解析 #13
前回、文字単位の入力で状態遷移する際のアクションにより解析を行う有限状態機械をflexで実装した。
今度は、文字単位よりももっと大きな塊を抽出するパターンを使って字句解析器を作ってみる。
%{ #include <stdio.h> #include <string.h> #ifdef __STRICT_ANSI__ int fileno(FILE *); #endif void yyerror(const char *msg); void yyerror(const char *msg) { fprintf(stderr, "error: %s\n", msg); } %} %option main nounput noinput %% int is_first = 1; [^()]+ { printf("string [%s]\n", yytext); if (is_first) is_first = 0; } \([^():]+:[^()]+\) { ptrdiff_t c = strchr(yytext, ':') - yytext; printf( "marked-string [%.*s]: annotation[%.*s]\n", c - 1, yytext + 1, yyleng - c - 2, yytext + c + 1 ); if (is_first) is_first = 0; } \([^():]+\) { printf("marked-string [%.*s]\n", yyleng - 2, yytext + 1); if (is_first) is_first = 0; } <<EOF>> { return is_first ? yyerror("unexpected EOF"), 1 : 0; } \(\) { yyerror("null string between parentheses"); return 1; } \(: { yyerror("null marked string"); return 1; } \([^):]*\( { yyerror("illegal left parenthesis"); return 1; } \([^):]* { yyerror("unexpected EOF"); return 1; } \([^):]+:\) { yyerror("null annotation"); return 1; } \([^):]+:\( { yyerror("illegal left parenthesis"); return 1; } \([^):]+:[^)]* { yyerror("unexpected EOF"); return 1; } \) { yyerror("illegal right parenthesis"); return 1; }
状態遷移表をそのまま表記したような前回のソースよりも、
抽出してほしい部分文字列とパターンとの対応が直接的なこちらの方が直感的だと思う。
表記もコンパクトになる。
注釈無しの解析でも同様の趣旨でソースを書いている。
その時、開始状態を使って空入力をエラーにする仕組みを作ったが、
今回はint型の変数is_first
が零か非零かで入力があったかどうかを判断させている。
名前を変えられないYY_START
と開始状態名とを比較する書き方よりも、
真偽値として条件式に利用できるこちらの方がちょっとだけ短く書けていいかも。
注釈付きのパターン\([^():]+:[^()]+\)
に対するアクションは、
strchr
関数でコロンの位置を求めてyytext
におけるコロンのインデックスc
を求め、
このc
とパターンにマッチした文字列の長さyyleng
を使ってmarked-string
とannotation
を分けて表示している。
正当な入力に対する動作は、用意しているテストデータでは問題なさそうだった。
それよりも、正当でない入力に対して、
ソースの末尾にリストしているエラーに対する規則で全部を網羅しているかがちょっと心配だ。
エラーが期待されるテストデータについて、ちゃんとエラーが出てくれてはいるが。
抜けがあった場合は、string [
やmarked-string [
やerror:
で始まらない妙なエコーバックが出力されるはずである。