Boost :: Spirit中的复合语法

Dud*_*ore 4 c++ parsing boost boost-spirit

我有以下符合预期的语法。

struct query_term {
    std::string term;
    bool is_tag;

    query_term(const std::string &a, bool tag = false): term(a), is_tag(tag) { } };

template<typename Iterator> struct query_grammar: grammar<Iterator, std::vector<query_term>(), space_type> {
    query_grammar():
        query_grammar::base_type(query) {

        word %= +alnum;
        tag  =  (omit[word >> ':'] >> word[_val = phoenix::construct<query_term>(_1, true)]);
        non_tag  =  word[_val = phoenix::construct<query_term>(_1, false)];
        query = (
                  (omit[word >> ':'] >> word[push_back(_val, phoenix::construct<query_term>(_1, true))])
                |
                  word[push_back(_val,
                            phoenix::construct<query_term>(_1))
                  ]
                ) % space;
    };

    qi::rule<Iterator, std::string(), space_type> word;
    qi::rule<Iterator, query_term, space_type> tag;
    qi::rule<Iterator, query_term, space_type> non_tag;
    qi::rule<Iterator, std::vector<query_term>(), space_type> query; };
Run Code Online (Sandbox Code Playgroud)

但是当我将查询替换为

query = (
          tag[phoenix::push_back(_val, _1)]
        |
          word[push_back(_val,
                    phoenix::construct<query_term>(_1))
          ]
        ) % space;
Run Code Online (Sandbox Code Playgroud)

代码无法编译。基本上,我试图将语法拆分为可在较大语法中重用的组件。解析单词或标签时,请在标签单词规则中创建带有适当标志的query_term对象。在查询规则中重新使用这些属性。

在以前的版本中,标记和单词规则内联在查询语法中。

我不确定我在这里缺少什么。任何帮助将不胜感激。

仅供参考:这不是最终代码。在生产代码中使用规则之前,我正在尝试这些规则。

谢谢-

seh*_*ehe 5

The real issue is that you define the attribute for the tag/non_tag rules as query_term (instead of query_term()).

Some minor issues appear to be:

  • using word instead of non_tag (exposes a std::string which doesn't convert to a query_type)
  • using % space with a space skipper doesn't really make sense
  • you probably wanted lexeme in the word rule because otherwise, it will just keep 'eating' chars regardless of whitespace

Other suggestions:

  • avoid excess scope of using namespace (or avoid it completely). You will run into hard-to-spot or hard-to-fix conflicts (e.g. boost::cref vs. std::cref, std::string vs. qi::string etc).

  • try to stay low on the Phoenix usage. In this case, I think you'd be far easier off using qi::attr with an adapted struct.

  • use BOOST_SPIRIT_DEBUG_* macros to get insight in your parser

Here is the entire grammar, the way I'd suggest it:

template<typename Iterator> struct query_grammar: qi::grammar<Iterator, std::vector<query_term>(), qi::space_type>
{
    query_grammar() : query_grammar::base_type(query)
    {
        using namespace qi;

        word    = lexeme[ +alnum ];

        tag     = omit[word >> ':'] >> word >> attr(true);

        non_tag = word >> attr(false);

        query   = *(tag | non_tag);
    };

    qi::rule<Iterator, std::string()            , qi::space_type> word;
    qi::rule<Iterator, query_term()             , qi::space_type> tag, non_tag;
    qi::rule<Iterator, std::vector<query_term>(), qi::space_type> query;
};
Run Code Online (Sandbox Code Playgroud)

A fully working example with output (trivially onelined using karma):

// #define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>

namespace qi    = boost::spirit::qi;
namespace karma = boost::spirit::karma;

struct query_term {
    std::string term;
    bool is_tag;
};

BOOST_FUSION_ADAPT_STRUCT(query_term, (std::string,term)(bool,is_tag));

template<typename Iterator> struct query_grammar: qi::grammar<Iterator, std::vector<query_term>(), qi::space_type>
{
    query_grammar() : query_grammar::base_type(query)
    {
        using namespace qi;

        word    = lexeme[ +alnum ];

        tag     = omit[word >> ':'] >> word >> attr(true);

        non_tag = word >> attr(false);

        query   = *(tag | non_tag);

        BOOST_SPIRIT_DEBUG_NODE(word);
        BOOST_SPIRIT_DEBUG_NODE(tag);
        BOOST_SPIRIT_DEBUG_NODE(non_tag);
        BOOST_SPIRIT_DEBUG_NODE(query);
    };

    qi::rule<Iterator, std::string()            , qi::space_type> word;
    qi::rule<Iterator, query_term()             , qi::space_type> tag, non_tag;
    qi::rule<Iterator, std::vector<query_term>(), qi::space_type> query;
};


int main()
{
    const std::string input = "apple tag:beer banana grape";
    typedef std::string::const_iterator It;

    query_grammar<It> parser;
    std::vector<query_term> data;

    It f(input.begin()), l(input.end());
    bool ok = qi::phrase_parse(f, l, parser, qi::space, data);

    if (ok)
        std::cout << karma::format(karma::delimit [ karma::auto_ ] % karma::eol, data) << '\n';
    if (f!=l)
        std::cerr << "Unparsed: '" << std::string(f,l) << "'\n";

    return ok? 0 : 255;
}
Run Code Online (Sandbox Code Playgroud)

Output:

apple false 
beer true 
banana false 
grape false 
Run Code Online (Sandbox Code Playgroud)