Итак, некоторые предположения, приведенные в качестве примеров, не дают ясного смысла.Но вот что:
Идет со случайным перечислением:
enum class Level { One, Two, Three, LEVEL };
Sidenote: std::set<>
может потребоваться быть последовательным контейнером, потому что обычно groupby
операции не являютсякоммутативный (порядок имеет значение).Конечно, я не знаю о вашем домене,
Адаптация:
BOOST_FUSION_ADAPT_STRUCT(ComputeTask, level, aggregator, dataname, groupby)
BOOST_FUSION_ADAPT_STRUCT(Schedule, tasks)
Обратите внимание, что я тонко помещаю адаптированные поля в грамматическом порядке.Это очень помогает в будущем.
Простейшая грамматика, которая приходит на ум:
template <typename It>
struct Parser : qi::grammar<It, Schedule()> {
Parser() : Parser::base_type(_start) {
using namespace qi;
_any_word = lexeme [ +char_("a-zA-Z0-9-_./") ];
_operation = _any_word; // TODO
_group_field = _any_word; // TODO
_dataname = _any_word; // TODO
_level = no_case [ _level_sym ];
_groupby = '(' >> -(_group_field % ',') >> ')';
_task = _level >> _operation >> _dataname >> "on" >> _groupby;
_entry = _level >> ':' >> _task;
_schedule = _entry % eol;
_start = skip(blank) [ _schedule ];
BOOST_SPIRIT_DEBUG_NODES((_start)(_schedule)(_task)(_groupby)(_level)(_operation)(_dataname)(_group_field))
}
private:
struct level_sym : qi::symbols<char, Level> {
level_sym() { this->add
("one", Level::One)
("two", Level::Two)
("three", Level::Three)
("level", Level::LEVEL);
}
} _level_sym;
// lexemes
qi::rule<It, std::string()> _any_word;
qi::rule<It, std::string()> _operation, _dataname, _group_field; // TODO
qi::rule<It, Level()> _level;
using Skipper = qi::blank_type;
using Table = decltype(Schedule::tasks);
using Entry = std::pair<Level, ComputeTask>;
qi::rule<It, std::set<std::string>(), Skipper> _groupby;
qi::rule<It, ComputeTask(), Skipper> _task;
qi::rule<It, Entry(), Skipper> _entry;
qi::rule<It, Table(), Skipper> _schedule;
qi::rule<It, Schedule()> _start;
};
Я изменил ввод, чтобы в расписании были уникальные ключи для Level
, в противном случае только одназапись будет фактически результатом.
int main() {
Parser<std::string::const_iterator> const parser;
for (std::string const input : { R"(ONE: level operation name on(data1, data2, data3)
TWO: level operation name on()
THREE: level operation name on(data1, data2))" })
{
auto f = begin(input), l = end(input);
Schedule s;
if (parse(f, l, parser, s)) {
std::cout << "Parsed\n";
for (auto& [level, task] : s.tasks) {
std::cout << level << ": " << task << "\n";
}
} else {
std::cout << "Failed\n";
}
if (f != l) {
std::cout << "Remaining unparsed input: " << std::quoted(std::string(f,l)) << "\n";
}
}
}
Печать
Parsed
One: LEVEL operation name on (data1, data2, data3)
Two: LEVEL operation name on ()
Three: LEVEL operation name on (data1, data2)
И, дополнительно с BOOST_SPIRIT_DEBUG
определено:
<_start>
<try>ONE: level operation</try>
<_schedule>
<try>ONE: level operation</try>
<_level>
<try>ONE: level operation</try>
<success>: level operation na</success>
<attributes>[One]</attributes>
</_level>
<_task>
<try> level operation nam</try>
<_level>
<try>level operation name</try>
<success> operation name on(d</success>
<attributes>[LEVEL]</attributes>
</_level>
<_operation>
<try>operation name on(da</try>
<success> name on(data1, data</success>
<attributes>[[o, p, e, r, a, t, i, o, n]]</attributes>
</_operation>
<_dataname>
<try>name on(data1, data2</try>
<success> on(data1, data2, da</success>
<attributes>[[n, a, m, e]]</attributes>
</_dataname>
<_groupby>
<try>(data1, data2, data3</try>
<_group_field>
<try>data1, data2, data3)</try>
<success>, data2, data3)\nTWO:</success>
<attributes>[[d, a, t, a, 1]]</attributes>
</_group_field>
<_group_field>
<try>data2, data3)\nTWO: l</try>
<success>, data3)\nTWO: level </success>
<attributes>[[d, a, t, a, 2]]</attributes>
</_group_field>
<_group_field>
<try>data3)\nTWO: level op</try>
<success>)\nTWO: level operati</success>
<attributes>[[d, a, t, a, 3]]</attributes>
</_group_field>
<success>\nTWO: level operatio</success>
<attributes>[[[d, a, t, a, 1], [d, a, t, a, 2], [d, a, t, a, 3]]]</attributes>
</_groupby>
<success>\nTWO: level operatio</success>
<attributes>[[LEVEL, [o, p, e, r, a, t, i, o, n], [n, a, m, e], [[d, a, t, a, 1], [d, a, t, a, 2], [d, a, t, a, 3]]]]</attributes>
</_task>
<_level>
<try>TWO: level operation</try>
<success>: level operation na</success>
<attributes>[Two]</attributes>
</_level>
<_task>
<try> level operation nam</try>
<_level>
<try>level operation name</try>
<success> operation name on()</success>
<attributes>[LEVEL]</attributes>
</_level>
<_operation>
<try>operation name on()\n</try>
<success> name on()\nTHREE: le</success>
<attributes>[[o, p, e, r, a, t, i, o, n]]</attributes>
</_operation>
<_dataname>
<try>name on()\nTHREE: lev</try>
<success> on()\nTHREE: level o</success>
<attributes>[[n, a, m, e]]</attributes>
</_dataname>
<_groupby>
<try>()\nTHREE: level oper</try>
<_group_field>
<try>)\nTHREE: level opera</try>
<fail/>
</_group_field>
<success>\nTHREE: level operat</success>
<attributes>[[]]</attributes>
</_groupby>
<success>\nTHREE: level operat</success>
<attributes>[[LEVEL, [o, p, e, r, a, t, i, o, n], [n, a, m, e], []]]</attributes>
</_task>
<_level>
<try>THREE: level operati</try>
<success>: level operation na</success>
<attributes>[Three]</attributes>
</_level>
<_task>
<try> level operation nam</try>
<_level>
<try>level operation name</try>
<success> operation name on(d</success>
<attributes>[LEVEL]</attributes>
</_level>
<_operation>
<try>operation name on(da</try>
<success> name on(data1, data</success>
<attributes>[[o, p, e, r, a, t, i, o, n]]</attributes>
</_operation>
<_dataname>
<try>name on(data1, data2</try>
<success> on(data1, data2)</success>
<attributes>[[n, a, m, e]]</attributes>
</_dataname>
<_groupby>
<try>(data1, data2)</try>
<_group_field>
<try>data1, data2)</try>
<success>, data2)</success>
<attributes>[[d, a, t, a, 1]]</attributes>
</_group_field>
<_group_field>
<try>data2)</try>
<success>)</success>
<attributes>[[d, a, t, a, 2]]</attributes>
</_group_field>
<success></success>
<attributes>[[[d, a, t, a, 1], [d, a, t, a, 2]]]</attributes>
</_groupby>
<success></success>
<attributes>[[LEVEL, [o, p, e, r, a, t, i, o, n], [n, a, m, e], [[d, a, t, a, 1], [d, a, t, a, 2]]]]</attributes>
</_task>
<success></success>
<attributes>[[[One, [LEVEL, [o, p, e, r, a, t, i, o, n], [n, a, m, e], [[d, a, t, a, 1], [d, a, t, a, 2], [d, a, t, a, 3]]]], [Two, [LEVEL, [o, p, e, r, a, t, i, o, n], [n, a, m, e], []]], [Three, [LEVEL, [o, p, e, r, a, t, i, o, n], [n, a, m, e], [[d, a, t, a, 1], [d, a, t, a, 2]]]]]]</attributes>
</_schedule>
<success></success>
<attributes>[[[[One, [LEVEL, [o, p, e, r, a, t, i, o, n], [n, a, m, e], [[d, a, t, a, 1], [d, a, t, a, 2], [d, a, t, a, 3]]]], [Two, [LEVEL, [o, p, e, r, a, t, i, o, n], [n, a, m, e], []]], [Three, [LEVEL, [o, p, e, r, a, t, i, o, n], [n, a, m, e], [[d, a, t, a, 1], [d, a, t, a, 2]]]]]]]</attributes>
</_start>
Полный список
Live On Coliru
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted.hpp>
#include <vector>
#include <map>
#include <set>
#include <iostream>
#include <iomanip>
#include <experimental/iterator>
enum class Level { One, Two, Three, LEVEL };
struct ComputeTask {
std::string dataname;
Level level;
std::string aggregator;
std::set<std::string> groupby;
};
struct Schedule {
std::map<Level, ComputeTask> tasks;
};
//////////////////////
// FOR DEBUG DEMO ONLY
static inline std::ostream& operator<<(std::ostream& os, Level l) {
switch(l) {
case Level::One: return os << "One";
case Level::Two: return os << "Two";
case Level::Three: return os << "Three";
case Level::LEVEL: return os << "LEVEL";
}
return os << "?";
}
static inline std::ostream& operator<<(std::ostream& os, ComputeTask const& task) {
os << task.level << ' ' << task.aggregator << ' ' << task.dataname << " on (";
copy(begin(task.groupby), end(task.groupby), std::experimental::make_ostream_joiner(os, ", "));
return os << ')';
}
/////////////
// FOR PARSER
BOOST_FUSION_ADAPT_STRUCT(ComputeTask, level, aggregator, dataname, groupby)
BOOST_FUSION_ADAPT_STRUCT(Schedule, tasks)
namespace qi = boost::spirit::qi;
template <typename It>
struct Parser : qi::grammar<It, Schedule()> {
Parser() : Parser::base_type(_start) {
using namespace qi;
_any_word = lexeme [ +char_("a-zA-Z0-9-_./") ];
_operation = _any_word; // TODO
_group_field = _any_word; // TODO
_dataname = _any_word; // TODO
_level = no_case [ _level_sym ];
_groupby = '(' >> -(_group_field % ',') >> ')';
_task = _level >> _operation >> _dataname >> "on" >> _groupby;
_entry = _level >> ':' >> _task;
_schedule = _entry % eol;
_start = skip(blank) [ _schedule ];
BOOST_SPIRIT_DEBUG_NODES((_start)(_schedule)(_task)(_groupby)(_level)(_operation)(_dataname)(_group_field))
}
private:
struct level_sym : qi::symbols<char, Level> {
level_sym() { this->add
("one", Level::One)
("two", Level::Two)
("three", Level::Three)
("level", Level::LEVEL);
}
} _level_sym;
// lexemes
qi::rule<It, std::string()> _any_word;
qi::rule<It, std::string()> _operation, _dataname, _group_field; // TODO
qi::rule<It, Level()> _level;
using Skipper = qi::blank_type;
using Table = decltype(Schedule::tasks);
using Entry = std::pair<Level, ComputeTask>;
qi::rule<It, std::set<std::string>(), Skipper> _groupby;
qi::rule<It, ComputeTask(), Skipper> _task;
qi::rule<It, Entry(), Skipper> _entry;
qi::rule<It, Table(), Skipper> _schedule;
qi::rule<It, Schedule()> _start;
};
int main() {
Parser<std::string::const_iterator> const parser;
for (std::string const input : { R"(ONE: level operation name on(data1, data2, data3)
TWO: level operation name on()
THREE: level operation name on(data1, data2))" })
{
auto f = begin(input), l = end(input);
Schedule s;
if (parse(f, l, parser, s)) {
std::cout << "Parsed\n";
for (auto& [level, task] : s.tasks) {
std::cout << level << ": " << task << "\n";
}
} else {
std::cout << "Failed\n";
}
if (f != l) {
std::cout << "Remaining unparsed input: " << std::quoted(std::string(f,l)) << "\n";
}
}
}