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 ]

だけで済みます。