Токенизировать строку стиля «Braced Initializer» в стиле C ++ (с Boost?)

У меня есть строка (даже вложенные строки), которые отформатированы как список инициализированных C ++ скобок. Я хочу маркировать их по одному уровню за один раз в вектор строк.

Поэтому, когда я ввожу "{one, two, three}" в функцию следует вывести трехэлементный вектор

"one",

"two",

"three"

Чтобы усложнить это, он должен поддерживать цитируемые токены и сохранять вложенные списки:

Строка ввода: "{one, {2, \"three four\"}}, \"five, six\", {\"seven, eight\"}}"

Выход представляет собой вектор из четырех элементов:

"one",

"{2, \"three four\"}",

"five, six",

"{\"seven, eight\"}"

Я посмотрел на несколько других сообщений SO:

Использование Boost Tokenizer escaped_list_separator с различными параметрами

Усиление раскола не происходит внутри скобок или скобок

И использовал их для запуска решения, но это кажется слишком сложным для токенизатора (из-за скобок):

#include <boost/algorithm/string.hpp>
#include <boost/tokenizer.hpp>

std::vector<std::string> TokenizeBracedList(const std::string& x)
{
std::vector<std::string> tokens;

std::string separator1("");
std::string separator2(",\n\t\r");
std::string separator3("\"\'");

boost::escaped_list_separator<char> elements(separator1, separator2, separator3);
boost::tokenizer<boost::escaped_list_separator<char>> tokenizer(x, elements);

for(auto i = std::begin(tokenizer); i != std::end(tokenizer); ++i)
{
auto token = *i;
boost::algorithm::trim(token);
tokens.push_back(token);
}

return tokens;
}

При этом, даже в тривиальном случае, он не удаляет открывающие и закрывающие скобки.

Boost и C ++ 17 являются честной игрой для решения.

2

Решение

Простой (плоский) дубль

Определение плоской структуры данных, такой как:

using token  = std::string;
using tokens = std::vector<token>;

Мы можем определить синтаксический анализатор X3, например:

namespace Parser {
using namespace boost::spirit::x3;

rule<struct list_, token> item;

auto quoted   = lexeme [ '"' >> *('\\' >> char_ | ~char_('"')) >> '"' ];
auto bare     = lexeme [ +(graph-','-'}') ];

auto list     = '{' >> (item % ',') >> '}';
auto sublist  = raw [ list ];

auto item_def = sublist | quoted | bare;

BOOST_SPIRIT_DEFINE(item)
}

Live On Wandbox

#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <iomanip>

using token  = std::string;
using tokens = std::vector<token>;

namespace x3 = boost::spirit::x3;

namespace Parser {
using namespace boost::spirit::x3;

rule<struct list_, token> item;

auto quoted   = lexeme [ '"' >> *('\\' >> char_ | ~char_('"')) >> '"' ];
auto bare     = lexeme [ +(graph-','-'}') ];

auto list     = '{' >> (item % ',') >> '}';
auto sublist  = raw [ list ];

auto item_def = sublist | quoted | bare;

BOOST_SPIRIT_DEFINE(item)
}

int main() {
for (std::string const input : {
R"({one, "five, six"})",
R"({one, {2, "three four"}, "five, six", {"seven, eight"}})",
})
{
auto f = input.begin(), l = input.end();

std::vector<std::string> parsed;
bool ok = phrase_parse(f, l, Parser::list, x3::space, parsed);

if (ok) {
std::cout << "Parsed: " << parsed.size() << " elements\n";
for (auto& el : parsed) {
std::cout << " - " << std::quoted(el, '\'') << "\n";
}
} else {
std::cout << "Parse failed\n";
}

if (f != l)
std::cout << "Remaining unparsed: " << std::quoted(std::string{f, l}) << "\n";
}
}

Печать

Parsed: 2 elements
- 'one'
- 'five, six'
Parsed: 4 elements
- 'one'
- '{2, "three four"}'
- 'five, six'
- '{"seven, eight"}'

Вложенные данные

Изменение структуры данных на более конкретную / реалистичную:

namespace ast {
using value = boost::make_recursive_variant<
double,
std::string,
std::vector<boost::recursive_variant_>
>::type;
using list = std::vector<value>;
}

Теперь мы можем изменить грамматику, так как нам больше не нужно лечить sublist как будто это строка:

namespace Parser {
using namespace boost::spirit::x3;

rule<struct item_, ast::value> item;

auto quoted   = lexeme [ '"' >> *('\\' >> char_ | ~char_('"')) >> '"' ];
auto bare     = lexeme [ +(graph-','-'}') ];

auto list     = x3::rule<struct list_, ast::list> {"list" }
= '{' >> (item % ',') >> '}';

auto item_def = list | double_ | quoted | bare;

BOOST_SPIRIT_DEFINE(item)
}

Все «все еще работает»: Live On Wandbox

#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <iomanip>

namespace ast {
using value = boost::make_recursive_variant<
double,
std::string,
std::vector<boost::recursive_variant_>
>::type;
using list = std::vector<value>;
}

namespace x3 = boost::spirit::x3;

namespace Parser {
using namespace boost::spirit::x3;

rule<struct item_, ast::value> item;

auto quoted   = lexeme [ '"' >> *('\\' >> char_ | ~char_('"')) >> '"' ];
auto bare     = lexeme [ +(graph-','-'}') ];

auto list     = x3::rule<struct list_, ast::list> {"list" }
= '{' >> (item % ',') >> '}';

auto item_def = list | double_ | quoted | bare;

BOOST_SPIRIT_DEFINE(item)
}

struct pretty_printer {
using result_type = void;
std::ostream& _os;
int _indent;

pretty_printer(std::ostream& os, int indent = 0) : _os(os), _indent(indent) {}

void operator()(ast::value const& v) { boost::apply_visitor(*this, v); }

void operator()(double v)            { _os << v; }
void operator()(std::string s)       { _os << std::quoted(s); }
void operator()(ast::list const& l)  {
_os << "{\n";
_indent += 2;
for (auto& item : l) {
_os << std::setw(_indent) << "";
operator()(item);
_os << ",\n";
}
_indent -= 2;
_os << std::setw(_indent) << "" << "}";
}
};

int main() {
pretty_printer print{std::cout};

for (std::string const input : {
R"({one, "five, six"})",
R"({one, {2, "three four"}, "five, six", {"seven, eight"}})",
})
{
auto f = input.begin(), l = input.end();

ast::value parsed;
bool ok = phrase_parse(f, l, Parser::item, x3::space, parsed);

if (ok) {
std::cout << "Parsed: ";
print(parsed);
std::cout << "\n";
} else {
std::cout << "Parse failed\n";
}

if (f != l)
std::cout << "Remaining unparsed: " << std::quoted(std::string{f, l}) << "\n";
}
}

Печать:

Parsed: {
"one",
"five, six",
}
Parsed: {
"one",
{
2,
"three four",
},
"five, six",
{
"seven, eight",
},
}
3

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

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

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector