Композитная грамматика в Boost :: Spirit - PullRequest
4 голосов
/ 24 марта 2012

У меня есть следующая грамматика, которая работает как ожидалось.

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 с соответствующим флагом в правиле tag и word . Повторно используйте эти атрибуты в правиле запроса.

В предыдущей версии правила тегов и слов встраивались в грамматику запроса.

Я не уверен, что мне здесь не хватает. Любая помощь будет принята с благодарностью.

К вашему сведению: это не окончательный код. Я пробую правила, прежде чем использовать их в рабочем коде.

Thanx, - балига

1 Ответ

4 голосов
/ 26 марта 2012

Реальная проблема заключается в том, что вы определяете атрибут для правил tag / non_tag как query_term (вместо query_term()).

Некоторые незначительные проблемы:

  • с использованием word вместо non_tag (предоставляет std :: string, которая не преобразуется в query_type)
  • использование % space с космическим шкипером не имеет смысла
  • вы, вероятно, хотели lexeme в правиле word, потому что в противном случае он будет просто "съедать" символы независимо от пробела

Другие предложения:

  • избегайте избыточного объема using namespace (или избегайте его полностью). Вы будете сталкиваться с трудными для обнаружения или трудно исправить конфликтами (например, boost :: cref против std :: cref, std :: string против qi :: string и т. Д.).

  • старайтесь не использовать Феникс. В этом случае, я думаю, вам будет гораздо проще использовать qi::attr с адаптированной структурой.

  • используйте макросы BOOST_SPIRIT_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 
...