2012年09月02日

N6XBasicChecker(3)boost::spirit事始め

なんか夏休み中にいろんなイベントが重なったので、色々脱線してしまいましたが、N6XBasicCheckerの内部の解説を再開したいと思います。

spiritはboostと呼ばれるC++ライブラリ群の1つで、パーサージェネレータとして機能します。
特徴としては
・テンプレートクラスとして実装され、ヘッダーをインクルードするだけで利用できる。
 spirit自体をビルドしてアプリからリンクする必要がないので、特にWindowsではちょっとだけ楽です。
・結構速い
 ベーマガ掲載のBASICリスト100本を2〜3秒でパースします。

ところで、こことかこことかこことかこことかこことかを見ていただけると解ると思いますが、日本語でboost::spiritを形容するのに最も使われている単語は「変態」です。
どのへんが変態かというと、
・非常に入り組んだテンプレートのため、コンパイル時間が非常に長い
 N6XBasicCheckerの場合、構文解析機のCPP1本のコンパイルで1分を超えます。
・テンプレート、演算子オーバーロード、ラムダ式のオンパレードのため、出来上がった構文定義はおよそC++とは思えないコードになる。
 N6XBasicCheckerの場合、最も複雑なIF分の定義はこうです。

BasicRule st_then
= (L("then") >> ((linenumber[phx::bind(&ParserStatus::registerReferredLineNumber, ref(status), _1)]
>> -qi::as_wstring[+(char_ - lit(":"))][phx::bind(&ParserStatus::warnRedundantContent, ref(status), _1)]) | statement)
>> *(L(":") >> -statement))
| st_goto;
BasicRule st_if
= L("if") >> qi::as_wstring[*(char_ - lit("then") - lit("goto"))]
[phx::bind(&partial_parse, _1, ref(status), logical_expression)]
>> qi::as_wstring[*(char_ - lit("else"))]
[phx::bind(&partial_parse, _1, ref(status), st_then)]
>> -((L("else") >> statement)
| (L("else") >> linenumber[phx::bind(&ParserStatus::registerReferredLineNumber, ref(status), _1)]
>> -qi::as_wstring[+(char_ - lit(":"))][phx::bind(&ParserStatus::warnRedundantContent, ref(status), _1)]));

 変態なコードしてるだろ…C++なんだぜ…動くんだぜこれ…。
・コンパイルエラーが変態
 テンプレートを使ったコードでコンパイルエラーを出すと、クラス名がやたら長くなって追えなくなることはC++プログラマーなら皆経験していると思いますが、spiritのコンパイルエラーの長さは突き抜けています。
 長すぎてブログの本文に貼れないのでw、下のリンクから雰囲気を感じてください。
 コンパイルエラーメッセージ
 こいつを読み取ってエラーを修正するのはとてもじゃないですが不可能なので、ソースコードはこまめにコミットしながら進んで、ちょっと取り返しがつかなくなったら即変更を破棄してやり直しというアプローチが必要になります。
 つまりgit必須です。(Subversionでもいいですが)

これだけ変態なツールを使いこなせるだろうかという不安はありましたが、変態として名高いP6erのためのツールを変態として名高いboost::spiritで作るというのはそれはそれで一興です。

次回はboost::spiritを使った実際のコードの骨格について書こうと思います。
最後に、私は変態じゃないです。
posted by eighttails at 11:21| Comment(0) | N6XBasicChecker | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント: