bjam_win その9
昨日の予想通りの方法で、テストのターゲット名取得を実装しました。
これでBoostのtest用Jamfile.v2の大半はパースできるようになりました。
以下、実装の解説です。
BBv2のrunルールのプロトタイプ(?)はこうです。
rule run ( sources + : # ソースファイルのリスト args * : # コマンドライン引数 input-files * : # テストの入力用ファイル requirements * : # ビルドに必要な条件 target-name ? : # ターゲット名 default-build * # 既定のvariant )
仮引数の後ろの文字(+/*/?)は拡張BNFの同じ意味です。
つまり、sourcesの1個目(以下、source)だけが必須で、残りはオプションです。
昨日も書きましたが、今回調べたいターゲット名はtarget-nameが空の場合sourceのベース名を使います。
この場合わけ処理をBoost.SpiritとPhoenixでやると大変なので、sourceとtarget-nameを記録して、それをまとめて返すことにしました。
// テスト用パラメータ struct testing_params { std::string source; std::string target_name; testing_params(){} testing_params(const std::string& src, const std::string& name) : source(src), target_name(name) { } };
この戻り値とsource/target-nameの3つを保存するためのクロージャを用意しました。
struct test_closure : boost::spirit::closure< test_closure, testing_params, std::string, std::string > { member1 val; member2 source; member3 target_name; }; boost::spirit::rule<ScannerT, typename test_closure::context_t> run;
クロージャのmember1が戻り値として使われるので、メンバの順序は重要です。
で、肝心のルールはこうです。
run = run_rule // sources >> literal [ run.source = construct_<std::string>(arg1, arg2) ] >> *( varexp ) >> !( ':' >> *( varexp ) ) // args >> !( ':' >> *( varexp ) ) // input-files >> !( ':' >> *( varexp ) ) // requirements // target-name >> !( ':' >> varexp [ run.target_name = construct_<std::string>(arg1, arg2) ] ) >> !( ':' >> *( varexp ) ) // default-build >> eps_p [ run.val = construct_<testing_params>( run.source, run.target_name) ] ;
eps_pはスキャナの位置を進めない特殊なパーサーで、ルールの最後で戻り値を設定するのに使っています。
ターゲット名の自動生成は呼び出し側のセマンティック・アクションで行いました。
statement
= exe
| lib
| test[push_back_test_actor(self.storage)]
| run[push_back_test_actor(self.storage)]
// 以下略
;
セマンティック・アクションはルールのmember1型で結果を受け取れます。
class push_back_test_actor { public: explicit push_back_test_actor(std::vector<std::string>& vs) : vs_(vs) { } void operator()(const testing_params& val) const { std::string s = val.target_name; if (s.empty()) { s = val.source; ::remove_branch_path(s); std::string::size_type pos = s.rfind('.'); if (pos != std::string::npos) s.erase(pos); } // 変数が混じっている場合は未対応なので捨てる if (s.find('$') != std::string::npos) return; vs_.push_back(s); } private: std::vector<std::string>& vs_; };
これは定義どおりですね。
というわけで、今日の差分です。
bjam_grammar.hppの差分
Boost.Spiritにもだいぶ慣れてきました。