Атрибуты последовательности и оператора списка в boost.spirit qi? - PullRequest
2 голосов
/ 12 октября 2019

Я хочу разобрать что-то вроде

"{xxxx}
{xxxx}"

, которое разделено eol на vector<vector<wchar_t>>: ({xxxx},{xxxx}), чтобы "{" и "}" оставались вместе с внутренними символами. Мой код:

#define BOOST_SPIRIT_UNICODE

#include <iostream>
#include<boost/spirit/include/qi.hpp>
#include<string>
#include<vector>

using namespace std;
namespace sw=boost::spirit::standard_wide;
namespace qi= boost::spirit::qi;
using boost::spirit::standard_wide::char_;

int main()
{
    wstring s = L"{\"id\":23,\"text\":\"sf\nsf\"}\n{\"id\":23,\"text\":\"sfsf\"}";
    qi::rule<wstring::iterator, vector<vector<wchar_t>>(), sw::blank_type> ru;
    ru = (qi::char_(L"{") >> *(char_-char_(L"}")) >> char_(L"}")) % qi::eol;
    vector<vector<wchar_t>> result;
    qi::phrase_parse(s.begin(), s.end(), ru, sw::blank, result);

    for (auto& v : result) {
        //cout << "Size of string: " << v.size() << endl;
        for (auto& s : v) {
            wcout << s;
        };
        cout << endl;
    };
    std::cout << "Size of result"<<result.size()<<endl ;
}

Однако вывод:

{
"id":23,"text":"sf
sf"
}
{
"id":23,"text":"sfsf"
}
Size of result6

Похоже, что "{" становится единственным элементом типа vector<wchar_t> для внешнего вектора.

Тогда рассмотрим правило:

ru = (qi::char_(L"{") >> *(char_-char_(L"}")) >> char_(L"}")) % qi::eol;

Согласно документации, *(char_-char_(L"}")) должно быть vector<A>. А поскольку a: A, b: vector<A> --> (a >> b): vector<A>, то я думаю, что (qi::char_(L"{") >> *(char_-char_(L"}")) >> char_(L"}")) должно быть vector<wchar_t>. Это противоречит результату.

Где я ошибаюсь?

1 Ответ

2 голосов
/ 12 октября 2019

А поскольку a: A, b: vector -> (a >> b): vector, то я думаю, что (qi :: char_ (L "{") >> * (char_-char_ (L "}")) >> char_ (L "}")) должен быть вектором. Это противоречит результату.

На самом деле это не то, что происходит. Применение модернизированного трюка из Определение типов параметров в семантическом действии Spirit

struct sense_f {
    template <typename T> void operator()(T&&) const {
        std::cout << boost::core::demangle(typeid(T).name()) << "\n";
    }
};
static const boost::phoenix::function<sense_f> sense;

Мы можем напечатать фактический тип атрибута:

ru = (char_(L'{') >> *(char_ - char_(L'}')) >> char_(L'}')) [sense(qi::_0)] % qi::eol;

, который будет печатать Live On Coliru :

boost::fusion::vector<wchar_t, std::vector<wchar_t, std::allocator<wchar_t> >, wchar_t>

Простое решение

При условии, что вам не нужно захватывать {}Вы можете просто сделать их литералами вместо char_:

ru = (L'{' >> *(char_ - L'}') >> L'}') [sense(qi::_0)] % qi::eol;

, которые будут печатать Live On Coliru :

boost::fusion::vector<std::vector<wchar_t, std::allocator<wchar_t> >&>

Действительно, если вы также заставите его распространять атрибут:

ru %= (L'{' >> *(char_ - L'}') >> L'}') [sense(qi::_0)] % qi::eol;

Программа напечатает:

boost::fusion::vector<std::vector<wchar_t, std::allocator<wchar_t> >&>
boost::fusion::vector<std::vector<wchar_t, std::allocator<wchar_t> >&>
"\"id\":23,\"text\":\"sf
sf\""
"\"id\":23,\"text\":\"sfsf\""

Обратите внимание, что существует совместимость атрибутов между std::vector<wchar_t> иstd::wstring, поэтому я использовал последний.

Бонус

Если вы действительно хотите включить {} и любой промежуточный пробел, используйте qi::raw:

ru %= qi::raw [L'{' >> *(char_ - L'}') >> L'}'] [sense(qi::_0)] % qi::eol;

Теперь он печатает:

boost::fusion::vector<boost::iterator_range<__gnu_cxx::__normal_iterator<wchar_t const*, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > > >&>
boost::fusion::vector<boost::iterator_range<__gnu_cxx::__normal_iterator<wchar_t const*, std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > > >&>
"{\"id\":23,\"text\":\"sf
sf\"}"
"{\"id\":23,\"text\":\"sfsf\"}"

Как вы можете видеть, даже iterator_range<It> имеет атрибут совместимости с std::wstring, потому что вход также является последовательностью wchar_t.

Конечно, отключите действие sense, если вы не хотите этот вывод.

Полный список

Окончательный результат с использованием подхода qi::raw:

Live On Coliru

#define BOOST_SPIRIT_UNICODE

#include <boost/spirit/include/qi.hpp>
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>

namespace sw = boost::spirit::standard_wide;
namespace qi = boost::spirit::qi;
using sw::char_;

int main() {
    std::wstring s = LR"({"id":23,"text":"sf
sf"}
{"id":23,"text":"sfsf"})";

    using Data = std::vector<std::wstring>;
    using It = std::wstring::const_iterator;

    qi::rule<It, Data(), sw::blank_type> ru
        = qi::raw [L'{' >> *(char_ - L'}') >> L'}'] % qi::eol;

    Data result;
    It f = s.begin(), l = s.end();

    if (qi::phrase_parse(f, l, ru, sw::blank, result)) {
        for (auto& s : result) {
            std::wcout << std::quoted(s) << std::endl;
        };
    } else {
        std::wcout << "Parse failed\n";
    }

    if (f!=l) {
        std::wcout << L"Remaining unparsed: " << std::quoted(std::wstring(f,l)) << std::endl;
    }
}
...