Я проводил рефакторинг (во время обучения) некоторых существующих процедур парсинга дат массируемой рукой с помощью Boost Spirit. Моей главной целью было попытаться создать единый и унифицированный интерфейс, чтобы иметь возможность анализировать даты, представленные несколькими различными способами. Ранее у меня было несколько разных сигнатур функций для преобразования различных форматов даты (например, parseDateYYYYMMDD
и parseDateDDMMYYYY
или parseDateStrangeFormatXYZ
) в доменные, указанные c объекты даты. С помощью Spirit кажется, что я могу создать основную грамматику, способную объединить все эти форматы, и будет только одна parseDate
К сожалению, когда я сравниваю производительность реализации Boost Spirit с рукой В массируемом коде производительность примерно в 5 раз ниже (64-битная сборка в режиме выпуска), даже с самым обычным форматом даты YYYY-MM-dd hh:mm:ss.zzz
, который не должен выполнять обратное отслеживание. Я надеюсь, что смогу хотя бы сравниться со старым кодом. Я хотел бы получить некоторую обратную связь, есть ли возможность для улучшения грамматики (и, возможно, AST) таким образом, чтобы она была оптимальной для производительности. В настоящее время я собираю части даты в векторе, и когда парсер успешно завершает работу, я присоединяю действие semanti c для создания объектов Date с компонентами, собранными из вектора. Может быть, одним слабым местом является вектор здесь? Возможно ли много грамматического выделения / освобождения в грамматике?
Мои объекты Date должны иметь конструкторы
// Date without time part
Date(Day d, Month m, Year y)
// Date with time part
Date(Day d, Month m, Year y, Size hours, Size minutes, Size seconds)
Вот фрагмент кода для анализа (это в. cpp file)
namespace {
namespace x3 = boost::spirit::x3;
namespace parsers {
template<typename T>
auto as = [](auto p) { return x3::rule<struct _, T>{} %= x3::as_parser(p); };
auto kwd = [](auto p, auto q) { return p > q; };
static const struct months : x3::symbols<int> {
months() {
add
("Jan" , 1)
("Feb" , 2)
("Mar" , 3)
("Apr" , 4)
("May" , 5)
("Jun" , 6)
("Jul" , 7)
("Aug" , 8)
("Sep" , 9)
("Oct" , 10)
("Nov" , 11)
("Dec" , 12)
("January" , 1)
("February" , 2)
("March" , 3)
("April" , 4)
("May" , 5)
("June" , 6)
("July" , 7)
("August" , 8)
("September" , 9)
("October" , 10)
("November" , 11)
("December" , 12)
;
}
} months;
static const x3::uint_parser<int, 10, 4, 4> yyyy;
static const x3::uint_parser<int, 10, 1, 2> MM, dd;
static const x3::uint_parser<int, 10, 2, 2> H_mm_ss;
static const x3::uint_parser<int, 10, 3, 3> zzz;
// ADL markers
struct ql_date_class {};
struct ql_date_time_class {};
static const auto ql_date = x3::rule<ql_date_class , Date>{"ql-date"};
static const auto ql_date_time = x3::rule<ql_date_time_class, Date>{"ql-date-time"};
auto validate_H = [](auto& ctx) {
const auto& x = x3::_attr(ctx);
x3::_pass(ctx) = (x >= 0 && x < 24);
};
auto validate_mm_ss = [](auto& ctx) {
const auto& x = x3::_attr(ctx);
x3::_pass(ctx) = (x >= 0 && x < 60);
};
auto validate_yyyy = [](auto& ctx) {
const auto& x = x3::_attr(ctx);
x3::_pass(ctx) = (x > 1900 && x < 2200);
};
auto validate_MM = [](auto& ctx) {
const auto& x = x3::_attr(ctx);
x3::_pass(ctx) = (x >= 1 && x <= 12);
};
auto validate_dd = [](auto& ctx) {
const auto& x = x3::_attr(ctx);
x3::_pass(ctx) = (x >= 1 && x <= 31);
};
static const auto year_ = yyyy[validate_yyyy];
static const auto month_ = as<int>(months | MM[validate_MM]);
static const auto day_ = dd[validate_dd];
static const auto hours_ = H_mm_ss[validate_H];
static const auto minutes_ = H_mm_ss[validate_mm_ss];
static const auto seconds_ = H_mm_ss[validate_mm_ss];
static const auto milliseconds_ = zzz;
auto date_parser = as<std::vector<int>>(
year_ >
as<std::vector<int>>(
kwd('-', as<std::vector<int>>(month_ > '-' > day_))
|
kwd('.', as<std::vector<int>>(month_ > '.' > day_))
|
kwd('/', as<std::vector<int>>(month_ > '/' > day_))
)
|
day_ >
as<std::vector<int>>(
kwd('-', as<std::vector<int>>(month_ > '-' > year_))
|
kwd('.', as<std::vector<int>>(month_ > '.' > year_))
|
kwd('/', as<std::vector<int>>(month_ > '/' > year_))
)[([](auto& ctx) { std::swap(x3::_attr(ctx)[0], x3::_attr(ctx)[2]); })]
)
;
static const auto time_parser = as<std::vector<int>>(
hours_ >
as<std::vector<int>>(
(
':' > minutes_ >
as<std::vector<int>>(
(
':' > seconds_ >
as<int>(
'.' > milliseconds_
|
x3::attr(int(0))
)
)
|
(
x3::repeat(2)[x3::attr(int(0))]
)
)
)
|
(
minutes_ >
as<std::vector<int>>(
(
seconds_ >
as<int>(
milliseconds_
|
x3::attr(int(0))
)
)
|
(
x3::repeat(2)[x3::attr(int(0))]
)
)
)
)
)
;
auto make_ql_date = [](auto& ctx) {
x3::_val(ctx) = Date(
x3::_attr(ctx)[2],
static_cast<Month>(x3::_attr(ctx)[1]),
x3::_attr(ctx)[0]
);
};
auto make_ql_date_time = [](auto& ctx) {
using boost::fusion::at_c;
x3::_val(ctx) = Date(
at_c<0>(x3::_attr(ctx))[2],
static_cast<Month>(at_c<0>(x3::_attr(ctx))[1]),
at_c<0>(x3::_attr(ctx))[0],
at_c<1>(x3::_attr(ctx))[0],
at_c<1>(x3::_attr(ctx))[1],
at_c<1>(x3::_attr(ctx))[2],
at_c<1>(x3::_attr(ctx))[3]
);
};
static const auto ql_date_def =
date_parser[make_ql_date]
;
static const auto ql_date_time_def =
(
date_parser >
as<std::vector<int>>(
(
x3::no_skip[x3::omit[x3::char_('T') | ' ' ]] >
time_parser
)
|
(
x3::repeat(4)[x3::attr(int(0))]
)
)
)[make_ql_date_time]
;
BOOST_SPIRIT_DEFINE(
ql_date,
ql_date_time
)
auto try_parse = [](const std::string& date, const auto& p)
-> boost::optional<Date>
{
auto ast = Date();
auto first = date.begin();
const auto last = date.end();
boost::spirit::x3::ascii::space_type space;
bool r = phrase_parse(first, last, p, space, ast);
if (!r || first != last) {
return boost::none;
}
else {
return ast;
}
};
auto parse = [](const std::string& date, const auto& p) {
auto ast = Date();
auto first = date.begin();
const auto last = date.end();
boost::spirit::x3::ascii::space_type space;
bool r = phrase_parse(first, last, parsers::ql_date, space, ast);
QL_REQUIRE(r && first == last,
"Parsing of " << date << " failed at " << std::string(first, last));
return ast;
};
} // namespace parsers
} // namespace anonymous
Вот бесплатные функции, которые используют анализаторы для формирования объектов Date.
boost::optional<Date> DateTimeParser::maybeParseDate(const std::string& date) {
return parsers::try_parse(date, parsers::ql_date);
}
Date DateTimeParser::parseDate(const std::string& date) {
return parsers::parse(date, parsers::ql_date);
}
boost::optional<Date>
DateTimeParser::maybeParseDateTime(const std::string& date) {
return parsers::try_parse(date, parsers::ql_date_time);
}
Date DateTimeParser::parseDateTime(const std::string& date) {
return parsers::parse(date, parsers::ql_date_time);
}