парсинг на несколько членов вектора

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

v  1.5 2.0 2.5
v  3.0 3.5 4.0
f 1 2 3
f 4 5 6
v  4.5 5.0 5.5
v  6.0 6.5 7.0
f 7 8 9
f 10 11 12

Проблема в том, что он анализирует только первые 4 строки и останавливается на третьей встреченной букве ‘v’. Полный код приведен ниже. Как мне изменить этот код, чтобы он также анализировал остальную часть ввода в той же структуре? Я пытался изменить правило запуска из start = vertex >> elements в start = *(vertex >> elements), но это только дает огромную ошибку компиляции. То же самое касается start = +(vertex >> elements), Любые идеи, как я должен изменить правила?

#include <iostream>
#include <sstream>
#include <fstream>

#include "boost/spirit/include/qi.hpp"#include "boost/spirit/include/support_iso8859_1.hpp"#include "boost/fusion/include/adapt_struct.hpp"

struct ElemParseData
{
std::vector<float> verts;
std::vector<unsigned int> idx;
};

BOOST_FUSION_ADAPT_STRUCT(
ElemParseData,
(std::vector<float>, verts)
(std::vector<unsigned int>, idx)
)bool doParse( ElemParseData &parseData, const std::string &data )
{
namespace qi      = boost::spirit::qi;
namespace iso8859 = boost::spirit::iso8859_1;

struct objGram : qi::grammar<std::string::const_iterator, ElemParseData(), iso8859::space_type>
{
objGram() : objGram::base_type(start)
{
vertex   = *('v' >> qi::double_ >> qi::double_ >> qi::double_);
elements = *('f' >> qi::int_ >> qi::int_ >> qi::int_);

start = vertex >> elements;
}

qi::rule<std::string::const_iterator, ElemParseData(), iso8859::space_type> start;
qi::rule<std::string::const_iterator, std::vector<float>(), iso8859::space_type> vertex;
qi::rule<std::string::const_iterator, std::vector<unsigned int>(), iso8859::space_type> elements;

} objGrammar;

std::string::const_iterator f = data.cbegin();
bool res = qi::phrase_parse( f, data.cend(), objGrammar, iso8859::space, parseData );// print everything that hasn't been processed by the parser
std::cout << "#### Trail ####" << std::endl;
std::cout << std::string(f, data.cend()) << std::endl;

return res;
}int main( int argc, char* argv[] )
{
std::stringstream ss;
std::filebuf fb;
if ( fb.open("parsetest.txt", std::ios::in) )
{
std::istream is(&fb);
while (is)
ss << char(is.get());
fb.close();
}ElemParseData parseData;
bool res = doParse( parseData, ss.str() );// print results
std::cout << std::endl << "Parsing result: " << res << std::endl;
std::cout << "---######### ResultData #########---" << std::endl;
std::cout << "---- Begin vertex data ----" << std::endl;
std::vector<float>::iterator it;
for ( it = parseData.verts.begin(); it != parseData.verts.end(); ++it )
std::cout << *it << std::endl;
std::cout << "---- End vertex data ----" << std::endl;

std::cout << std::endl;

std::cout << "---- Begin index data ----" << std::endl;
std::vector<unsigned int>::iterator idxIt;
for ( idxIt = parseData.idx.begin(); idxIt != parseData.idx.end(); ++idxIt )
std::cout << *idxIt << std::endl;
std::cout << "---- End index data ----" << std::endl;

std::cout << "Press enter to exit" << std::endl;
std::cin.get();
}

П.С .: При необходимости можно найти ошибку компиляции Вот.

РЕДАКТИРОВАТЬ: Я пытаюсь написать анализатор Wavefront .OBJ. Входные данные, приведенные здесь, являются лишь упрощением моей проблемы.

5

Решение

Есть несколько способов 🙂

  1. Пользовательские атрибуты
  2. То же самое с использованием семантических действий
  3. Все в семантических действиях, на уровне детализации

1. Пользовательские атрибуты

Самое чистое, IMO бы заменить адаптацию Fusion Sequence (BOOST_FUSION_ADAPT_STRUCT) по специальным признакам атрибута контейнера для Spirit:

namespace boost { namespace spirit { namespace traits {

template<>
struct is_container<ElemParseData, void> : mpl::true_ { };
template<>
struct container_value<ElemParseData, void> {
typedef boost::variant<float, unsigned int> type;
};
template <>
struct push_back_container<ElemParseData, std::vector<float>, void> {
static bool call(ElemParseData& c, std::vector<float> const& val) {
c.verts.insert(c.verts.end(), val.begin(), val.end());
return true;
}
};
template <>
struct push_back_container<ElemParseData, std::vector<unsigned int>, void> {
static bool call(ElemParseData& c, std::vector<unsigned int> const& val) {
c.idx.insert(c.idx.end(), val.begin(), val.end());
return true;
}
};
}}}

Без изменений в грамматике это просто приведет к тому же эффекту. Тем не менее, теперь вы можете изменить синтаксический анализатор, ожидая нужную грамматику:

    vertex   = 'v' >> qi::double_ >> qi::double_ >> qi::double_;
elements = 'f' >> qi::int_ >> qi::int_ >> qi::int_;

start = *(vertex | elements);

И из-за черт, Дух будет «просто знать», как вставить в ElemParseData, Видеть это жить на Колиру

2. То же самое с использованием семантических действий

Вы можете связать это в семантических действиях:

    start = *(
vertex   [phx::bind(insert, _val, _1)]
| elements [phx::bind(insert, _val, _1)]
);

С insert член типа inserter:

struct inserter {
template <typename,typename> struct result { typedef void type; };

template <typename Attr, typename Vec>
void operator()(Attr& attr, Vec const& v) const { dispatch(attr, v); }
private:
static void dispatch(ElemParseData& data, std::vector<float> vertices) {
data.verts.insert(data.verts.end(), vertices.begin(), vertices.end());
}
static void dispatch(ElemParseData& data, std::vector<unsigned int> indices) {
data.idx.insert(data.idx.end(), indices.begin(), indices.end());
}
};

Это выглядит в значительной степени одинаково, и это делает то же самое: жить на Колиру

3. Все в семантических действиях, на уровне детализации

Это единственное решение, которое не требует никакой сантехники, кроме, возможно, включения boost/spirit/include/phoenix.hpp:

struct objGram : qi::grammar<std::string::const_iterator, ElemParseData(), iso8859::space_type>
{
objGram() : objGram::base_type(start)
{
using namespace qi;

auto add_vertex = phx::push_back(phx::bind(&ElemParseData::verts, _r1), _1);
auto add_index  = phx::push_back(phx::bind(&ElemParseData::idx,   _r1), _1);
vertex   = 'v' >> double_ [add_vertex] >> double_ [add_vertex] >> double_ [add_vertex];
elements = 'f' >> int_    [add_index]  >> int_    [add_index]  >> int_    [add_index] ;

start = *(vertex(_val) | elements(_val));
}

qi::rule<std::string::const_iterator, ElemParseData(), iso8859::space_type> start;
qi::rule<std::string::const_iterator, void(ElemParseData&), iso8859::space_type> vertex, elements;
} objGrammar;

Замечания:

  • Один небольшой преимущество здесь было бы что меньше копирование значений
  • Недостатком является то, что вы теряете «атомарность» (если линия не удается проанализировать после, скажем, второго значения, первые два значения будут помещены в ElemParseData участники безвозвратно).

В цикле чтения есть ошибка, предпочитайте более простые параметры:

std::filebuf fb;
if (fb.open("parsetest.txt", std::ios::in))
{
ss << &fb;
fb.close();
}

Или посмотрим boost::spirit::istream_iterator

11

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]