为什么Boost.Spirit正确地将标识符解析为std :: string,而不是解析为仅由std :: string组成的自适应结构?

GMa*_*ckG 4 c++ parsing boost-spirit boost-fusion boost-spirit-qi

我为标识符定义了一个规则:以字母字符开头,后跟任意数量的字母数字字符.当我直接解析为std::string包含单个的自适应结构时,我有不同的结果std::string.

如果我的语法属性是std::string,Qi将正确地调整字符序列.但是使用结构,只存储第一个字符.我不太清楚为什么会这样.(注意,如果结构"真正"适应,或者它是由Fusion内联定义的,则没有区别.)

这是一个SSCCE,可配置为调试:

// Options:
//#define DEFINE_STRUCT_INLINE
//#define DEBUG_RULE

#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

#include <boost/fusion/adapted/struct/define_struct_inline.hpp>
#include <boost/fusion/include/define_struct_inline.hpp>

#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

#include <iostream>
#include <string>

namespace qi = boost::spirit::qi;

#ifdef DEFINE_STRUCT_INLINE
    namespace example
    {
        BOOST_FUSION_DEFINE_STRUCT_INLINE(
            identifier_result,
            (std::string, name)
            )
    }
#else
    namespace example
    {
        struct identifier_result
        {
            std::string name;
        };
    }

    BOOST_FUSION_ADAPT_STRUCT(
        example::identifier_result,
        (std::string, name)
        )
#endif

namespace example
{
    typedef std::string identifier_result_str;

    template <typename Iterator, typename Result>
    struct identifier_parser : qi::grammar<Iterator, Result()>
    {
        identifier_parser() :
        identifier_parser::base_type(identifier, "identifier_parser")
        {
            identifier %=
                qi::alpha >>
                *qi::alnum
                ;

            identifier.name("identifier");

            #ifdef DEBUG_RULE
                debug(identifier);
            #endif
        }

        qi::rule<Iterator, Result()> identifier;
    };
}

std::string strip(example::identifier_result identifier)
{
    return identifier.name;
}

std::string strip(std::string str)
{
    return str;
}

template <typename Result>
void test_parse(const std::string& input)
{
    using namespace example;

    auto&& first = input.cbegin();
    auto&& last = input.cend();

    auto&& parser = identifier_parser<std::string::const_iterator, Result>();
    auto&& skipper = qi::space;

    Result result;
    qi::phrase_parse(first, last, parser, skipper, result);

    std::cout << "Result of the parse is: \'"
              << strip(result) << "\'" << std::endl;
}

int main()
{
    using namespace example;

    test_parse<identifier_result>(" validId1 ");
    test_parse<identifier_result>(" %error1% ");

    test_parse<identifier_result_str>(" validId2 ");
    test_parse<identifier_result_str>(" %error2% ");
}
Run Code Online (Sandbox Code Playgroud)

输出是:

解析的结果是:'v'
解析的结果是:''
解析的结果是:'validId2'
解析的结果是:''

正如预期的那样,两个错误情况都不匹配.但在第一种情况下,我的结构只捕获第一个字符.我想保留结构用于组织目的.

如果我调试节点,我得到这个输出:

<identifier>
  <try>validId1 </try>
  <success> </success>
  <attributes>[[[v]]]</attributes>
</identifier>

[ ... ]

<identifier>
  <try>validId2 </try>
  <success> </success>
  <attributes>[[v, a, l, i, d, I, d, 2]]</attributes>
</identifier>
Run Code Online (Sandbox Code Playgroud)

所以我可以看到规则正在消耗整个标识符,它只是没有正确存储它.我唯一的"提示"就是v在第一种情况下嵌套在内[[[.]]],而正确的情况只是[[.]].但我不知道该怎么做.:)

为什么会出现这种情况?

Mik*_*e M 5

只是为了让你前进,你必须在一个额外的规则包裹你的字符串.

我不知道确切的解释,但你想要做的是用一系列char解析器解析一个字符串.使用stringas属性类型qi能够使用属性作为容器来存储多个字符,使用结构只是不知道如何执行此操作.也许它有助于提供struct容器属性,但我没有经验.而且只是解析一个可能有点过分的字符串.

只需更改解析器有助于:

namespace example
{
    typedef std::string identifier_result_str;

    template <typename Iterator, typename Result>
    struct identifier_parser : qi::grammar<Iterator, Result()>
    {
        identifier_parser() :
        identifier_parser::base_type(identifier, "identifier_parser")
        {
            string %=
                qi::alpha >>
                *qi::alnum
                ;

            identifier = string;
            identifier.name("identifier");

            #ifdef DEBUG_RULE
                debug(identifier);
            #endif
        }

        qi::rule<Iterator, Result()> identifier;
        qi::rule<Iterator, std::string()> string;
    };
}
Run Code Online (Sandbox Code Playgroud)