no_actions_d
前にno_actions_dは使わないと書いてましたが、使い方が分かったので、これを使ってbjam_grammarを書き直しています。
no_actions_dディレクティブを使う場合、対象となるパーサーのスキャナがno_actions_scanner<Scanner>::typeである必要があります。
この制約は再帰的に適用されるため、それ自身が(呼び出し元を含めて)再帰的に定義されるルールに適用する場合は全てno_actions_scannerにすることになり、結果的にセマンティックアクションが使えなくなります。
このような場合、grammar_defを使って文法のスタートルールを増やし、no_actions_dディレクティブを適用するルールをスタートルールとして呼ぶことで解決できます。
struct bjam_grammar : boost::spirit::grammar<bjam_grammar,vars_closure::context_t> { // スタートルール識別用 enum { jamfile, statements, invoke }; explicit bjam_grammar(/*略*/); template<class ScannerT> struct definition : boost::spirit::grammar_def< boost::spirit::rule<ScannerT, typename vars_closure::context_t> , boost::spirit::same , boost::spirit::same > { definition(const bjam_grammar& self) { block = '{' >> space >> !( // セマンティックアクションなし no_actions_d [ self.use_parser<bjam_grammar::statements>() // (first,last) 取得用ダミー >> eps_p ] [ block.val = construct_<std::string>(arg1, arg2) ] >> space ) >> '}' ; // 中略 jamfile = *(space_p | comment) // セマンティックアクションあり >> !statements >> !space ; this->start_parsers(jamfile, statements, invoke); }
これで制御文やルール呼び出しも実装できるはずです。
(5/3 追記)
BOOST_SPIRIT_RULE_SCANNERTYPE_LIMITを2以上に定義して、scanner_listを使ったほうが簡単でした。
no_actions_d用に専用のno_actions_scanner_listも用意されています。
上の例なら、
rule<typename no_actions_scanner_list<ScannerT>::type> block;
とでもすれば、
no_actions_d[ statements >> eps_p ]
だけで済みます。