88 lines
1.7 KiB
C++
88 lines
1.7 KiB
C++
#pragma once
|
|
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <istream>
|
|
#include "util/for_each.hpp"
|
|
|
|
namespace ztu
|
|
{
|
|
template<bool Repeating, typename F>
|
|
struct prefixed_line_parser {
|
|
const std::string_view prefix;
|
|
F parse;
|
|
};
|
|
|
|
template<bool Repeating>
|
|
struct line_repeating_type {};
|
|
|
|
static constexpr auto is_repeating = line_repeating_type<true>{};
|
|
static constexpr auto is_not_repeating = line_repeating_type<false>{};
|
|
|
|
template<bool Repeating, typename F>
|
|
prefixed_line_parser<Repeating, F> make_line_parser(std::string_view prefix, line_repeating_type<Repeating>, F&& f)
|
|
{
|
|
return { prefix, std::forward<F>(f) };
|
|
}
|
|
|
|
template<typename E, bool... Rs, class... Fs>
|
|
E parse_lines(std::istream& in, const bool pedantic, prefixed_line_parser<Rs, Fs>&&... parsers)
|
|
{
|
|
|
|
auto ec = E{};
|
|
|
|
std::string line;
|
|
while (std::getline(in, line)) [[likely]]
|
|
{
|
|
parse_current_line:
|
|
|
|
auto line_updated = false;
|
|
|
|
for_each::argument(
|
|
[&]<bool R, typename F>(prefixed_line_parser<R, F>&& parser) -> bool
|
|
{
|
|
if (line.starts_with(parser.prefix))
|
|
{
|
|
const auto prefix_length = parser.prefix.length();
|
|
ec = parser.parse(line.substr(prefix_length));
|
|
|
|
if constexpr (R)
|
|
{
|
|
while (std::getline(in, line))
|
|
{
|
|
if (line.starts_with(parser.prefix)) [[likely]]
|
|
{
|
|
ec = parser.parse(line.substr(prefix_length));
|
|
}
|
|
else [[unlikely]]
|
|
{
|
|
line_updated = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
},
|
|
std::forward<prefixed_line_parser<Rs, Fs>>(parsers)...
|
|
);
|
|
|
|
if (pedantic) {
|
|
if (ec != E{}) [[unlikely]] {
|
|
return ec;
|
|
}
|
|
}
|
|
|
|
if (line_updated)
|
|
{
|
|
goto parse_current_line;
|
|
}
|
|
}
|
|
|
|
return E{};
|
|
}
|
|
}
|