Boost::Spirit中的复合语法

Composite grammar in Boost::Spirit

本文关键字:复合 语法 Spirit Boost      更新时间:2023-10-16

我有以下语法,可以按预期工作。

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; };

但是当我用代替查询时

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

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

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

我不确定我在这里错过了什么。如有任何帮助,我们将不胜感激。

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

Thanx,--巴利加

真正的问题是将标记/non_tag规则的属性定义为query_term(而不是query_term())。

一些小问题似乎是:

  • 使用word而不是non_tag(公开一个不会转换为query_type的std::字符串)
  • 与太空队长一起使用% space实际上没有意义
  • 您可能希望word规则中包含lexeme,因为否则,它将继续"吃掉"字符,而不考虑空白

其他建议:

  • 避免CCD_ 9的过大范围(或完全避免)。您遇到难以发现或难以修复的冲突(例如boost::cref与std::cre夫、std::string与qi::string等)。

  • 尽量少用Phoenix。在这种情况下,我认为使用具有自适应结构的qi::attr会容易得多。

  • 使用BOOstrongPIRIT_DEBUG_*宏来深入了解您的解析器

这是我建议的整个语法:

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;
};

一个完整的输出示例(使用因果报应进行琐碎的单线排列):

// #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;
}

输出:

apple false 
beer true 
banana false 
grape false