Захват строки до конца строки с помощью Boost.Spirit.Qi

Я пытаюсь написать пользовательскую структуру для захвата строки до EOL или EOS как часть более крупной грамматики, но я не смог ее скомпилировать. Я следовал коду руководства, но Я не мог понять ошибки в приведенном ниже фрагменте.

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

#include <string>
#include <cassert>

namespace qi = boost::spirit::qi;

namespace project {

struct RawValue {
    std::string str;
};

}

BOOST_FUSION_ADAPT_STRUCT(project::RawValue, str)

namespace project::parser {

struct RawValueParser : qi::grammar<std::string::const_iterator, RawValue(), qi::space_type> {
  RawValueParser() : RawValueParser::base_type(expr) {
    using qi::blank;
    using qi::char_;
    using qi::eol;
    using qi::lexeme;

    expr %= lexeme[*(char_ - eol) >> (char_ - blank)];
  }

  qi::rule<iterator_type, RawValue(), skipper_type> expr;
};

}

int main() {
  project::parser::RawValueParser expr;
  project::RawValue result;
  std::string s1 = "test1";
  auto iter = s1.cbegin();
  auto end = s1.cend();
  assert(qi::phrase_parse(iter, end, expr, qi::space, result));
  return 0;
}

https://wandbox.org/permlink/PjaaYwO0Qz18998y

Это должен быть довольно простой пример. Что я делаю неправильно?


person ofo    schedule 22.07.2021    source источник


Ответы (1)


Ошибка компиляции возникает здесь:

test.cpp|21 col 58| required from here
/home/sehe/custom/spirit/include/boost/spirit/home/qi/nonterminal/grammar.hpp|77 col 13| error: static assertion failed: incompatible_start_rule
||    77 |             BOOST_SPIRIT_ASSERT_MSG(
||       |             ^~~~~~~~~~~~~~~~~~~~~~~

Если вы перейдете в указанное место (см. также Как сделать Я вижу ошибки компилятора Boost Spirit):

// If you see the assertion below failing then the start rule
// passed to the constructor of the grammar is not compatible with
// the grammar (i.e. it uses different template parameters).
BOOST_SPIRIT_ASSERT_MSG(
    (is_same<start_type, rule<Iterator_, T1_, T2_, T3_, T4_> >::value)
  , incompatible_start_rule, (rule<Iterator_, T1_, T2_, T3_, T4_>));

Это твоя проблема. Грамматика <It, RawValue(), qi::space_type>, но начальное правило <iterator_type, RawValue(), skipper_type>, где

    static_assert(std::is_same_v<It, iterator_type>);
    static_assert(std::is_same_v<qi::space_type, skipper_type>);
    qi::space_type _y = skipper_type{};

показывает, что второе утверждение терпит неудачу. Третья строка ведет к более подробной диагностике: Live

test.cpp|33 col 29| error: could not convert ‘boost::spirit::qi::grammar<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, project::RawValue(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >, 0> >::skipper_type{}’ from ‘boost::spirit::qi::grammar<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, project::RawValue(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >, 0> >::skipper_type’ {aka ‘boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >’} to ‘boost::spirit::standard::space_type’ {aka ‘boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::te
||    33 |         qi::space_type _y = skipper_type{};
||       |                             ^~~~~~~~~~~~~~
||       |                             |
||       |                             boost::spirit::qi::grammar<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, project::RawValue(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >, 0> >::skipper_type {aka boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >}

Вы можете разобрать все это на части, но достаточно сказать, что ваше предположение о том, что skipper_type будет равняться третьему аргументу шаблона, было неверным.

Просто назовите нужные типы: Live

qi::rule<It, RawValue(), qi::space_type> expr;

Side Notes

  1. lexeme отключает шкипера. Помня об этом, подумайте об упрощении всего процесса: Live (см. также проблемы со шкипером Boost Spirit)

  2. operator %= не имеет значения без семантических действий

  3. expr кажется неправильным

  4. классы символов с отрицанием более эффективны, чем синтаксические анализаторы с вычитанием; Вы имели в виду, что (char_ - blank) означает больше, чем eol?

  5. Конец ввода теперь не принимается вместо eol. Возможно, вы должны позволить это. я бы написал так

    start = *~qi::char_("\n\r") >> (eol | eoi);
    

Simplified Listing

Жить на Coliru

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

#include <iomanip>

namespace qi = boost::spirit::qi;
using It = std::string::const_iterator;

struct RawValue { std::string str; };

BOOST_FUSION_ADAPT_STRUCT(RawValue, str)

struct RawValueParser : qi::grammar<It, RawValue()> {
    RawValueParser() : RawValueParser::base_type(start)
    {
        using namespace qi;
        start = *~qi::char_("\n\r") >> (eol | eoi);
    }

  private:
    qi::rule<It, RawValue()> start;
};

int main() {
    RawValueParser expr;

    for (std::string const s : {
             "test1",
             " test2\tbla   \n",
         }) {

        RawValue result;
        std::cout
            << std::boolalpha
            //<< qi::phrase_parse(begin(s), end(s), expr, qi::space, result)
            << qi::parse(begin(s), end(s), expr, result) << " -> "
            << std::quoted(result.str) << "\n";
    }
}

Отпечатки

true -> "test1"
true -> " test2 bla   "
person sehe    schedule 23.07.2021
comment
Спасибо. Намного лучше. Мой опыт работы с ци показал, что на данный момент она в лучшем случае сработала случайно. :-) Я тоже пытался разобрать спичку. Я мог бы удалить совпадение слева, добавив omit[*nonl_space]. Есть ли идиоматический способ, которым я мог бы также раздеть его? Это то, что я пробовал, но не смог добавить атрибут tail к head: crooked.com/a/f5464205a7292572 (это может вызвать отдельный вопрос. Если это так, дайте мне знать.) - person ofo; 23.07.2021
comment
Я мог бы сделать это следующим образом: но нет ничего постыдного в том, чтобы сделать грамматику проще: cdf1041e18b12794 - person sehe; 23.07.2021