2

Boost.Spirit に MSVC マングル シンボルを解析させようとしています。これらは次の形式を取ります。

?myvolatileStaticMember@myclass@@2HC これは、「volatile int myclass::myvolatileStaticMember」を意味します。

解析の「鍵」は二重記号「@@」です。@@ の前には、絶対名前空間階層でシンボルを完全に表すために、0 個以上の追加の "@" 識別子が続く C++ 識別子で構成されるシンボルの名前があります。@@ の後は、識別子 (変数、関数など) の指定です。

これで、Boost.Spirit に@@ の前の部分または @@の後の部分を解析させることができます。Boost.Spirit で @@ を見つけて、その前に来るものを 1 つのカスタム パーサーにフィードし、その後に来るものを別のカスタム パーサーにフィードする方法をまだ理解していません。

@@ の前の部分のパーサーは次のとおりです。

// This grammar is for a MSVC mangled identifier
template<typename iterator> struct msvc_name : grammar<iterator, SymbolType(), locals<SymbolType, string>>
{
    SymbolTypeDict &typedict;
    void name_writer(SymbolType &val, const string &i) const { val.name=i; }
    void dependent_writer(SymbolType &val, const string &i) const
    {
        SymbolTypeDict::const_iterator dt=typedict.find(i);
        if(dt==typedict.end())
        {
            auto _dt=typedict.emplace(make_pair(i, SymbolType(SymbolTypeQualifier::None, SymbolTypeType::Namespace, i)));
            dt=_dt.first;
        }
        val.dependents.push_back(&dt->second);
    }
    // These work by spreading the building of a templated type over multiple calls using local variables _a and _b
    // We accumulate template parameters into _a and accumulate mangled symbolness into _b
    void begin_template_dependent_writer(SymbolType &, SymbolType &a, string &b, const string &i) const
    {
        a=SymbolType(SymbolTypeQualifier::None, SymbolTypeType::Class, i);
        b=i;
    }
    void add_template_constant_dependent_writer(SymbolType &a, string &b, long long constant) const
    {
        string i("_c"+to_string(constant));
        SymbolTypeDict::const_iterator dt=typedict.find(i);
        if(dt==typedict.end())
        {
            auto _dt=typedict.emplace(make_pair(i, SymbolType(SymbolTypeQualifier::None, SymbolTypeType::Constant, to_string(constant))));
            dt=_dt.first;
        }
        a.templ_params.push_back(&dt->second);
        b.append(i);
    }
    void add_template_type_dependent_writer(SymbolType &a, string &b, SymbolTypeType type) const
    {
        string i("_t"+to_string(static_cast<int>(type)));
        SymbolTypeDict::const_iterator dt=typedict.find(i);
        if(dt==typedict.end())
        {
            auto _dt=typedict.emplace(make_pair(i, SymbolType(SymbolTypeQualifier::None, type)));
            dt=_dt.first;
        }
        a.templ_params.push_back(&dt->second);
        b.append(i);
    }
    void finish_template_dependent_writer(SymbolType &val, SymbolType &a, string &b) const
    {
        SymbolTypeDict::const_iterator dt=typedict.find(b);
        if(dt==typedict.end())
        {
            auto _dt=typedict.emplace(make_pair(b, a));
            dt=_dt.first;
        }
        val.dependents.push_back(&dt->second);
    }
    msvc_name(SymbolTypeDict &_typedict) : msvc_name::base_type(start), typedict(_typedict)
    {
        identifier=+(char_ - '@');
        identifier.name("identifier");
        template_dependent_identifier=+(char_ - '@');
        template_dependent_identifier.name("template_dependent_identifier");
        dependent_identifier=+(char_ - '@');
        dependent_identifier.name("dependent_identifier");
        start = identifier [ boost::phoenix::bind(&msvc_name::name_writer, this, _val, _1) ] >> *(
            lit("@@") >> eps
            | (("@?$" > template_dependent_identifier [ boost::phoenix::bind(&msvc_name::begin_template_dependent_writer, this, _val, _a, _b, _1) ])
                > "@" > +(( "$0" > constant [ boost::phoenix::bind(&msvc_name::add_template_constant_dependent_writer, this, _a, _b, _1) ])
                    | type [ boost::phoenix::bind(&msvc_name::add_template_type_dependent_writer, this, _a, _b, _1) ])
                >> eps [ boost::phoenix::bind(&msvc_name::finish_template_dependent_writer, this, _val, _a, _b) ])
            | ("@" > dependent_identifier [ boost::phoenix::bind(&msvc_name::dependent_writer, this, _val, _1) ]))
            ;
        BOOST_SPIRIT_DEBUG_NODE(start);
        start.name("msvc_name");
        on_error<boost::spirit::qi::fail, iterator>(start,
            cerr << boost::phoenix::val("Parsing error: Expected ") << _4 << boost::phoenix::val(" here: \"")
                << boost::phoenix::construct<string>(_3, _2) << boost::phoenix::val("\"") << endl);
    }

    rule<iterator, SymbolType(), locals<SymbolType, string>> start;
    rule<iterator, string()> identifier, template_dependent_identifier, dependent_identifier;
    msvc_type type;
    msvc_constant<iterator> constant;
};

「lit("@@") >> eps」に注意してください。@@ が表示されたら、一致を停止させようとしています。ここで、完全にマングルされたシンボルに一致するはずの部分を次に示します。

template<typename iterator> struct msvc_symbol : grammar<iterator, SymbolType()>
{
    SymbolTypeDict &typedict;
    /* The key to Microsoft symbol mangles is the operator '@@' which consists of a preamble
    and a postamble. Immediately following the '@@' operator is:
    Variable:
    3<type><storage class>
    Static member variable:
    2<type><storage class>
    Function:
    <near|far><calling conv>[<stor ret>]   <return type>[<parameter type>...]<term>Z
    <Y   |Z  ><A|E|G       >[<?A|?B|?C|?D>]<MangledToSymbolTypeType...>      <@>Z
    Member Function:
    <protection>[<const>]<calling conv>[<stor ret>]   <return type>[<parameter type>...]<term>Z
    <A-V       >[<A-D>  ]<A|E|G       >[<?A|?B|?C|?D>]<MangledToSymbolTypeType...>      <@>Z
    */
    msvc_symbol(SymbolTypeDict &_typedict) : msvc_symbol::base_type(start), typedict(_typedict), name(_typedict), variable(_typedict)
    {
        start="?" >> name >> ("@@" >> variable);
        BOOST_SPIRIT_DEBUG_NODE(start);
        on_error<boost::spirit::qi::fail, iterator>(start,
            cerr << boost::phoenix::val("Parsing error: Expected ") << _4 << boost::phoenix::val(" here: \"")
                << boost::phoenix::construct<string>(_3, _2) << boost::phoenix::val("\"") << endl);
    }

    rule<iterator, SymbolType()> start;
    msvc_name<iterator> name;
    msvc_variable<iterator> variable;
};

つまり、「?」に一致します。簡単に十分;)。問題は、「?」の後にすべてを送信することです。msvc_name パーサーに渡されるため、@@ 以降のビットが msvc_variable に移動し、残りが msvc_name に移動する代わりに、msvc_name は @@ を含むすべてを消費します。これは直感的ではありません。括弧が最初にそれを行うことを意味すると考える人がいるからです。

したがって、: start="?" >> name >> ("@@" >> variable); start="?" >> name >> variable; ... に置き換えると、すべて正常に動作します。

しかし、私はこのようにしないことを本当に望んでいます。理想的には、Boost.Spirit が @@ で msvc_symbol できれいに分割され、いわば「正しいことを行う」ことを望んでいます。私はおそらく再帰的に十分に考えていないと思いますか?いずれにせよ、私は困惑しています。

注: はい、@@ で文字列を分割し、2 つの個別のパーサーを実行できることは承知しています。それは私が求めていることではありません - むしろ、Boost.Spirit を構成して、フレーズの終わりを最初の前に解析する方法を尋ねています。

また、注意: スキッパーを使用して @@ 空白を作成し、そのように分割できることは承知しています。問題は、@@ の前にあるものが非常に具体的であり、@@ の後にあることです。したがって、実際には空白ではありません。

助けていただける方に、事前に感謝します。これに関連する質問をGoogleとStackoverflowで検索することから、Boost.Spiritの「左から右への貪欲さ」を克服することは、多くの人々にとって問題です。

ニール

4

1 に答える 1

2

いくつかのことができるようです:

  1. "@" が必要な場所で二重 "@@" を明示的に禁止します。こちらもご覧ください

  2. 最初にトークン化します (Spirit Lex を使用しますか?)

ここでは、最初のアプローチの実例を示します。

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
namespace qi = boost::spirit::qi;

template<typename T> T reversed(T c) {  return T(c.rbegin(), c.rend()); }

int main (int argc, char** argv)
{
    const std::string input("?myvolatileStaticMember@myclass@@2HC");

    auto f = begin(input), l = end(input);

    auto identifier = +~qi::char_("@");
    auto delimit    = qi::lit("@") - "@@";

    std::vector<std::string> qualifiedName;
    std::string typeId;

    if (qi::parse(f,l,
                '?' >> identifier % delimit >> "@@" >> +qi::char_,
                qualifiedName,
                typeId))
    {
        using namespace boost::spirit::karma;
        qualifiedName = reversed(qualifiedName);
        std::cout << "Qualified name: "  << format(auto_ % "::" << "\n", qualifiedName);
        std::cout << "Type indication: '" << typeId << "'\n";
    }
}

出力:

Qualified name: myclass::myvolatileStaticMember
Type indication: '2HC'
于 2012-12-04T22:39:48.520 に答える