Разбор количества именованных наборов других именованных наборов

Поэтому я хочу написать … ну … не такой простой парсер с boost :: spirit :: qi. Я знаком с основами бодрости духа, познакомившись с ним впервые за последние пару часов.

В основном мне нужно разобрать это:

# comment

# other comment

set "Myset A"{
figure "AF 1"{
i 0 0 0
i 1 2 5
i 1 1 1
f 3.1 45.11 5.3
i 3 1 5
f 1.1 2.33 5.166
}

figure "AF 2"{
i 25 5 1
i 3 1 3
}
}

# comment

set "Myset B"{
figure "BF 1"{
f 23.1 4.3 5.11
}
}

set "Myset C"{
include "Myset A" # includes all figures from Myset A

figure "CF"{
i 1 1 1
f 3.11 5.33 3
}
}

В это:

struct int_point { int x, y, z; };
struct float_point { float x, y, z; };

struct figure
{
string name;
vector<int_point> int_points;
vector<float_point> float_points;
};

struct figure_set
{
string name;
vector<figure> figures
};

vector<figure_set> figure_sets; // fill with the data of the input

Теперь, очевидно, что кто-то написал бы это для меня, было бы слишком много, но не могли бы вы дать несколько советов о том, что читать и как структурировать грамматику и парсеры для этой задачи?

А также … может случиться так, что boost :: spirit — не лучшая библиотека, которую я мог бы использовать для этой задачи. Если да, то какой?

РЕДАКТИРОВАТЬ:
Вот где я дошел до сих пор. Но я еще не знаю, как дальше: http://liveworkspace.org/code/212c31dfc0b6fbdf6c462d8d931c0e9f

Я могу прочитать одну цифру, но пока не знаю, как разобрать набор цифр.

6

Решение

Вот мой взгляд на это

Я считаю, что правило, которое будет для вас блокирующим, будет

figure  = eps >> "figure">> name         [ at_c<0>(_val) = _1 ] >> '{' >>
*(
ipoints [ push_back(at_c<1>(_val), _1) ]
| fpoints [ push_back(at_c<2>(_val), _1) ]
) >> '}';

На самом деле это признак того, что вы разбираете смешанные i а также f линии в отдельные контейнеры.

Смотрите ниже альтернативу.

Вот мой полный код: test.cpp

//#define BOOST_SPIRIT_DEBUG // before including Spirit
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <fstream>

namespace Format
{
struct int_point   { int x, y, z;   };
struct float_point { float x, y, z; };

struct figure
{
std::string              name;
std::vector<int_point>   int_points;
std::vector<float_point> float_points;

friend std::ostream& operator<<(std::ostream& os, figure const& o);
};

struct figure_set
{
std::string           name;
std::set<std::string> includes;
std::vector<figure>   figures;

friend std::ostream& operator<<(std::ostream& os, figure_set const& o);
};

typedef std::vector<figure_set> file_data;
}

BOOST_FUSION_ADAPT_STRUCT(Format::int_point,
(int, x)(int, y)(int, z))
BOOST_FUSION_ADAPT_STRUCT(Format::float_point,
(float, x)(float, y)(float, z))
BOOST_FUSION_ADAPT_STRUCT(Format::figure,
(std::string, name)
(std::vector<Format::int_point>, int_points)
(std::vector<Format::float_point>, float_points))
BOOST_FUSION_ADAPT_STRUCT(Format::figure_set,
(std::string, name)
(std::set<std::string>, includes)
(std::vector<Format::figure>, figures))

namespace Format
{
std::ostream& operator<<(std::ostream& os, figure const& o)
{
using namespace boost::spirit::karma;
return os << format_delimited(
"\n    figure" << no_delimit [ '"' << string << '"' ] << "\n    {"<< *("\n       i" << int_ << int_ << int_)
<< *("\n       f" << float_ << float_ << float_)
<< "\n    }", ' ', o);
}

std::ostream& operator<<(std::ostream& os, figure_set const& o)
{
using namespace boost::spirit::karma;
return os << format_delimited(
"\nset" << no_delimit [ '"' << string << '"' ] << "\n{"<< *("\n    include " << no_delimit [ '"' << string << '"' ])
<< *stream
<< "\n}", ' ', o);
}
}

namespace /*anon*/
{
namespace phx=boost::phoenix;
namespace qi =boost::spirit::qi;

template <typename Iterator> struct skipper
: public qi::grammar<Iterator>
{
skipper() : skipper::base_type(start, "skipper")
{
using namespace qi;

comment = '#' >> *(char_ - eol) >> (eol|eoi);
start   = comment | qi::space;

BOOST_SPIRIT_DEBUG_NODE(start);
BOOST_SPIRIT_DEBUG_NODE(comment);
}

private:
qi::rule<Iterator> start, comment;
};

template <typename Iterator> struct parser
: public qi::grammar<Iterator, Format::file_data(), skipper<Iterator> >
{
parser() : parser::base_type(start, "parser")
{
using namespace qi;
using phx::push_back;
using phx::at_c;

name    = eps >> lexeme [ '"' >> *~char_('"') >> '"' ];

include = eps >> "include" >> name;
ipoints = eps >> "i"       >> int_         >> int_   >> int_;
fpoints = eps >> "f"       >> float_       >> float_ >> float_;

figure  = eps >> "figure">> name         [ at_c<0>(_val) = _1 ] >> '{' >>
*(
ipoints [ push_back(at_c<1>(_val), _1) ]
| fpoints [ push_back(at_c<2>(_val), _1) ]
) >> '}';
set     = eps >> "set" >> name >> '{' >> *include >> *figure >> '}';
start   = *set;
}

private:
qi::rule<Iterator, std::string()        , skipper<Iterator> > name, include;
qi::rule<Iterator, Format::int_point()  , skipper<Iterator> > ipoints;
qi::rule<Iterator, Format::float_point(), skipper<Iterator> > fpoints;
qi::rule<Iterator, Format::figure()     , skipper<Iterator> > figure;
qi::rule<Iterator, Format::figure_set() , skipper<Iterator> > set;
qi::rule<Iterator, Format::file_data()  , skipper<Iterator> > start;
};
}

namespace Parser {

bool parsefile(const std::string& spec, Format::file_data& data)
{
std::ifstream in(spec.c_str());
in.unsetf(std::ios::skipws);

std::string v;
v.reserve(4096);
v.insert(v.end(), std::istreambuf_iterator<char>(in.rdbuf()), std::istreambuf_iterator<char>());

if (!in)
return false;

typedef char const * iterator_type;
iterator_type first = &v[0];
iterator_type last = first+v.size();

try
{
parser<iterator_type>  p;
skipper<iterator_type> s;
bool r = qi::phrase_parse(first, last, p, s, data);

r = r && (first == last);

if (!r)
std::cerr << spec << ": parsing failed at: \"" << std::string(first, last) << "\"\n";
return r;
}
catch (const qi::expectation_failure<char const *>& e)
{
std::cerr << "FIXME: expected " << e.what_ << ", got '" << std::string(e.first, e.last) << "'" << std::endl;
return false;
}
}
}

int main()
{
Format::file_data data;
bool ok = Parser::parsefile("input.txt", data);

std::cerr << "Parse " << (ok?"success":"failed") << std::endl;
std::cout << "# figure sets exported automatically by karma\n\n";

for (auto& set : data)
std::cout << set;
}

Он выводит проанализированные данные в качестве проверки: output.txt

Parse success
# figure sets exported automatically by karmaset "Myset A"{
figure "AF 1"{
i 0 0 0
i 1 2 5
i 1 1 1
i 3 1 5
f 3.1 45.11 5.3
f 1.1 2.33 5.166
}
figure "AF 2"{
i 25 5 1
i 3 1 3
}
}
set "Myset B"{
figure "BF 1"{
f 23.1 4.3 5.11
}
}
set "Myset C"{
include  "Myset A"figure "CF"{
i 1 1 1
f 3.11 5.33 3.0
}
}

Вы заметите, что

  • порядок точечных линий изменен (все int_points предшествовать всем float_points)
  • также добавляются несущественные цифры, например, в последней строке 3.0 вместо 3 чтобы показать, что тип, если float.
  • Вы «забыли» (?) о включении в свой вопрос

альтернатива

Есть что-то, что сохраняет фактические линии точек в исходном порядке:

typedef boost::variant<int_point, float_point> if_point;

struct figure
{
std::string            name;
std::vector<if_point>  if_points;
}

Теперь правила становятся простыми:

name    = eps >> lexeme [ '"' >> *~char_('"') >> '"' ];

include = eps >> "include" >> name;
ipoints = eps >> "i"       >> int_         >> int_   >> int_;
fpoints = eps >> "f"       >> float_       >> float_ >> float_;

figure  = eps >> "figure" >> name >> '{' >> *(ipoints | fpoints) >> '}';
set     = eps >> "set"    >> name >> '{' >> *include >> *figure  >> '}';
start   = *set;

Обратите внимание на элегантность в

figure  = eps >> "figure" >> name >> '{' >> *(ipoints | fpoints) >> '}';

И вывод остается в точном порядке ввода: output.txt

Еще раз, полный демонстрационный код (только на github): test.cpp

Бонусное обновление

Наконец, я сделал свою первую правильную грамматику Кармы, чтобы вывести результаты:

name    = no_delimit ['"' << string << '"'];
include = "include" << name;
ipoints = "\n        i" << int_   << int_   << int_;
fpoints = "\n        f" << float_ << float_ << float_;

figure  = "figure" << name << "\n    {" << *(ipoints | fpoints) << "\n    }";
set     = "set"    << name << "\n{"<< *("\n   " << include)
<< *("\n   " << figure)  << "\n}";

start   = "# figure sets exported automatically by karma\n\n"<< set % eol;

На самом деле это было намного удобнее, чем я ожидал. Посмотрите это в последней версии полностью обновленная суть: test.hpp

11

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

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