var_expand_grammar
簡易bjamインタプリタ実装の手始めとして、変数の展開を行うパーサー(?)var_expand_grammarを作りました。
bjam(というかjam)の変数展開は直積集合を形成するのが特徴的です。
X = a b c ; ECHO t$(X) ; # 「ta tb tc」を出力
これを素直に表現するため、変数の値はvector<string>としました。
直積を求めるファンクタはこうなります。
struct append_str_vec_actor { typedef std::vector<std::string> result_type; std::vector<std::string> operator()( const std::vector<std::string>& lhs, const std::vector<std::string>& rhs) const { std::vector<std::string> result; for (std::size_t i = 0; i < lhs.size(); ++i) { for (std::size_t j = 0; j < rhs.size(); ++j) result.push_back(lhs[i] + rhs[j]); } return result; } };
積なので、初期値は空ベクトルではなく、空文字列一つのベクタにする必要があります。
パーサーはこんな感じになります。
#include <boost/spirit/core.hpp> #include <boost/spirit/attribute/closure.hpp> #include <boost/spirit/phoenix/binders.hpp> // 変数テーブル class variables { ... }; struct vars_closure : boost::spirit::closure< vars_closure, std::vector<std::string> > { member1 val; }; // 変数を展開するファンクタ class vars_expand_actor; // 一つの文字列からなるベクタを作成するファンクタ struct make_str_vec_actor; struct var_expand_grammar : boost::spirit::grammar<var_expand_grammar,vars_closure::context_t> { explicit var_expand_grammar(const variables& t) : table(t) { } const variables& table; template<class ScannerT> struct definition { typedef boost::spirit::rule< ScannerT, typename vars_closure::context_t> vars_rule_t; vars_rule_t top, expr, variable; definition(const var_expand_grammar& self) { using namespace boost::spirit; using namespace phoenix; variable = "$(" >> expr [ variable.val = bind(vars_expand_actor(self.table))(arg1) ] >> ')' ; expr = // 空文字列1個で初期化 (arg1 == arg2) eps_p[expr.val = bind(make_str_vec_actor())(arg1, arg2)] >> *( variable [ expr.val = bind(append_str_vec_actor())(expr.val, arg1) ] | (+(anychar_p - '$' - ')')) [ expr.val = bind(append_str_vec_actor())( expr.val, bind(make_str_vec_actor())(arg1, arg2) ) ] ) ; top = expr[ self.val = arg1 ] ; } const vars_rule_t& start() const { return top; } }; };
bindだらけですが、PhoenixのbindはBoost.Lambdaのほど読みにくくないので、まだ意味が分かりますね。
今日の成果物