在Boost.Spirit中解析一个短语的结尾

时间:2012-12-04 21:56:54

标签: c++ boost boost-spirit boost-spirit-qi

我正在尝试让Boost.Spirit解析MSVC错位符号。采取以下形式:

?myvolatileStaticMember@myclass@@2HC 这意味着“volatile int myclass :: myvolatileStaticMember”。

解析的“密钥”是符号“@@”的双精度。在@@之前是符号的名称,该符号由C ++标识符后跟零个或多个“@”附加标识符组成,以完全表示其绝对名称空间层次结构中的符号。在@@之后是标识符的规范(变量,函数等)

现在,我可以让Boost.Spirit解析 @@ 之前的部分或 @@之后的部分。我还没想出如何让Boost.Spirit找到@@并将之前的内容提供给一个自定义解析器,然后将其提供给另一个自定义解析器。

这是@@:

之前部分的解析器
// 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(”@@“)&gt;&gt; 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中以@@ cleanly分割并“按照原样做正确的事情”。我想我可能没有足够的递归思考?无论哪种方式,我都难过。

注意:是的我知道我可以在@@处打破字符串并运行两个单独的解析器。这不是我要问的 - 相反,我问的是如何配置Boost.Spirit来在开头之前解析一个短语的结尾。

另请注意:我知道可以使用一个队长来制作@@空格并以这种方式进行拆分。问题是@@之前的内容非常具体,就像@@之后的内容一样。因此,它不是真正的空白。

非常感谢能够提供帮助的任何人。从Google和Stackoverflow搜索与此相关的问题,克服Boost.Spirit的“从左到右的贪婪”对很多人来说都是一个问题。

尼尔

1 个答案:

答案 0 :(得分:2)

看起来你可以做很多事情:

  1. 明确地禁止双重“@@”,您希望“@”。另见

  2. 首先标记(使用Spirit Lex?)

  3. 在这里,我向您展示第一种方法的工作示例:

    #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'