Last active
February 16, 2020 10:06
-
-
Save cppljevans/0fede2c9c2e57f25712528dab4e6aec7 to your computer and use it in GitHub Desktop.
date_parser solution to stackoverflow problem
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Following code modifies: | |
// https://gist.github.com/cppljevans/0fede2c9c2e57f25712528dab4e6aec7#file-date_parser-rm_as_define-cpp | |
//by: | |
// 1) adding a macro, USE_ALTERNATIVES | |
// 2) using BOOST_FUSION_ADAPT_STRUCT | |
// 3) using rule | |
// 4) using force propagation flag in rule. | |
//to: | |
// show that even with attribute propagation, the attributes will not | |
// propage when there's any ALTERNATIVE's. | |
//Result: | |
// When !defined(USE_ALTERNATIVES), runs OK. | |
// when defined(USE_ALTERNATRIVES), fails at compile time. | |
//===================== | |
//#define USE_ALTERNATIVES | |
#include <iostream> | |
#include <vector> | |
template <typename Element> | |
std::ostream& operator<<(std::ostream& os, std::vector<Element>const& seq) | |
{ | |
for(int elm:seq){ os<<elm<<" ";} | |
return os; | |
} | |
namespace boost { | |
namespace fusion { namespace detail { | |
using ::operator<<; | |
}} | |
namespace detail { namespace variant { | |
using ::operator<<; | |
}} | |
}//namespace boost | |
#include <boost/optional/optional_io.hpp> | |
#include <boost/spirit/home/x3.hpp> | |
#include <boost/spirit/home/x3/support/utility/unrefcv.hpp> | |
#include <boost/fusion/include/io.hpp> | |
#include <boost/fusion/include/adapt_struct.hpp> | |
#include <boost/fusion/view/iterator_range/iterator_range.hpp> | |
#include <boost/fusion/iterator/deref.hpp> | |
using TimeType = unsigned; | |
using Day = TimeType; | |
using Month = TimeType; | |
using Year = TimeType; | |
struct Date | |
{ | |
Day _d; | |
Month _m; | |
Year _y; | |
TimeType _hours; | |
TimeType _minutes; | |
TimeType _seconds; | |
TimeType _millisecs; | |
Date(Day d=0, Month m=0, Year y=0, TimeType hours=0, TimeType minutes=0, TimeType seconds=0, TimeType millisecs=0) | |
: _d(d),_m(m),_y(y),_hours(hours),_minutes(minutes),_seconds(seconds),_millisecs(millisecs) | |
{} | |
friend std::ostream& operator<<(std::ostream&sout, Date const& date) { | |
sout | |
<<"{ yr:"<<date._y | |
<<", mo:"<<date._m | |
<<", day:"<<date._d | |
<<", hr:"<<date._hours | |
<<", min:"<<date._minutes | |
<<", sec:"<<date._seconds | |
<<", mil:"<<date._millisecs | |
<<"}"; | |
return sout; | |
} | |
}; | |
BOOST_FUSION_ADAPT_STRUCT(Date,_d,_m,_y,_hours,_minutes,_seconds,_millisecs) | |
namespace x3 = boost::spirit::x3; | |
namespace parsers { | |
static const struct months : x3::symbols<TimeType> { | |
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<TimeType, 10, 4, 4> yyyy; | |
static const x3::uint_parser<TimeType, 10, 1, 2> MM, dd; | |
static const x3::uint_parser<TimeType, 10, 2, 2> H_mm_ss; | |
static const x3::uint_parser<TimeType, 10, 3, 3> zzz; | |
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_ = 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 swap_day_year= | |
[](auto& ctx) | |
{ auto& date_ymd = x3::_attr(ctx); | |
//assert: date_ymd has int's stored in year month day order. | |
using boost::fusion::at_c; | |
std::swap //swap so that day is 1st and year is 3ird: | |
( at_c<0>(date_ymd) | |
, at_c<2>(date_ymd) | |
); | |
}; | |
static const auto date_parser = | |
#ifdef USE_ALTERNATIVES | |
( (year_ > '-' > month_ > '-' > day_ )[swap_day_year] | |
| (day_ > '-' > month_ > '-' > year_) | |
) | |
#else | |
day_ > '-' > month_ > '-' > year_ | |
#endif | |
; | |
static const auto time_parser | |
= | |
#ifdef USE_ALTERNATIVES | |
( hours_ > | |
( ( ':' > minutes_ > ':' > seconds_ > '.' > milliseconds_ ) | |
| ( minutes_ | |
> ( ( seconds_ | |
> ( milliseconds_ | |
| x3::attr(TimeType(0)) | |
) | |
) | |
| x3::repeat(2)[x3::attr(TimeType(0))] | |
) | |
) | |
) | |
) | |
| x3::repeat(4)[x3::attr(TimeType(0))] | |
#else | |
hours_ > ':' > minutes_ > ':' > seconds_ > '.' > milliseconds_ | |
#endif | |
; | |
static const auto date_time_parser = | |
date_parser > time_parser | |
; | |
using date_time_parser_type = x3::unrefcv_t<decltype(date_time_parser)>; | |
using date_time_attr_type = x3::traits::attribute_of<date_time_parser_type,x3::unused_type,void>::type; | |
struct attr2date | |
//Purpose: | |
// copy attr from parser to the user attribute of type, Date. | |
: public boost::static_visitor<> | |
{ | |
Date& _date; | |
unsigned _index; | |
attr2date(Date&date):_date(date),_index(0) | |
{ | |
} | |
void put_then_next(TimeType time_value) | |
{ | |
switch(_index) | |
{ | |
case 0: | |
_date._d=time_value; | |
break; | |
case 1: | |
_date._m=time_value; | |
break; | |
case 2: | |
_date._y=time_value; | |
break; | |
case 3: | |
_date._hours=time_value; | |
break; | |
case 4: | |
_date._minutes=time_value; | |
break; | |
case 5: | |
_date._seconds=time_value; | |
break; | |
case 6: | |
_date._millisecs=time_value; | |
break; | |
default: | |
std::cout<<"invalid index="<<_index<<"!\n"; | |
} | |
++_index; | |
} | |
template<typename... T> | |
void put_then_next(boost::variant<T...>const&nxt) | |
{ | |
(*this)(nxt); | |
} | |
template<typename Seq> | |
auto make_iter_range(Seq const&seq) | |
{ | |
auto beg_iter=boost::fusion::begin(seq); | |
auto end_iter=boost::fusion::end(seq); | |
using beg_type = x3::unrefcv_t<decltype(beg_iter)>; | |
using end_type = x3::unrefcv_t<decltype(end_iter)>; | |
return boost::fusion::iterator_range<beg_type,end_type>(beg_iter,end_iter); | |
} | |
template<typename Beg, typename End> | |
auto next_iter_range(boost::fusion::iterator_range<Beg,End>const&iter_range) | |
{ | |
auto beg_iter=boost::fusion::advance_c<1>(iter_range.first); | |
using beg_type = x3::unrefcv_t<decltype(beg_iter)>; | |
auto end_iter=iter_range.last; | |
return boost::fusion::iterator_range<beg_type,End>(beg_iter,end_iter); | |
} | |
void operator()(TimeType time_value) | |
{ | |
this->put_then_next(time_value); | |
} | |
void operator()(int time_value) | |
{ | |
this->put_then_next(time_value); | |
} | |
template<typename... T> | |
void operator()(boost::variant<T...>const&nxt) | |
{ | |
boost::apply_visitor(*this,nxt); | |
} | |
template <typename Element> | |
void operator()(std::vector<Element>const &vec) | |
{ for(Element val:vec) (*this)(val); | |
} | |
template <typename EndIter> | |
void operator()(boost::fusion::iterator_range<EndIter,EndIter>const& iter_range) | |
{} | |
template <typename BegIter, typename EndIter> | |
void operator()(boost::fusion::iterator_range<BegIter,EndIter>const& iter_range) | |
{ | |
//std::cout<<"iter_range="<<iter_range<<";\n"; | |
using range_type = boost::fusion::iterator_range<BegIter,EndIter>; | |
auto deref_val = boost::fusion::deref(iter_range.first); | |
//std::cout<<"deref_val="<<deref_val<<";\n"; | |
this->put_then_next(deref_val); | |
auto iter_nxt = this->next_iter_range(iter_range); | |
//std::cout<<"iter_nxt="<<iter_nxt<<";\n"; | |
(*this)(iter_nxt); | |
} | |
template <typename... T> | |
void operator()(boost::fusion::deque<T...>const& seq) | |
{ //std::cout<<"seq="<<seq<<";\n"; | |
auto iter_range_seq=make_iter_range(seq); | |
//std::cout<<"make_iter_range="<<iter_range_seq<<";\n"; | |
(*this)(iter_range_seq); | |
} | |
}; | |
Date parse(const std::string& date_str) | |
{ | |
auto first = date_str.begin(); | |
const auto last = date_str.end(); | |
boost::spirit::x3::ascii::space_type space; | |
Date date_attr{}; | |
x3::rule<struct date_time_id, Date, true> const date_time_rule{"date_time_rule"}; | |
bool r = | |
phrase_parse | |
( first | |
, last | |
, date_time_rule%=date_time_parser | |
, space | |
, date_attr | |
); | |
if ( not(r && first == last) ) | |
{ std::cout | |
<< "Parsing of " << date_str | |
<< " failed at " << std::string(first, last) | |
<< ";\n"; | |
} | |
return date_attr; | |
}; | |
} // namespace parsers | |
void print_parse(std::string date_str) | |
{ | |
std::cout<<"date_str="<<date_str<<";\n"; | |
auto date_ast = parsers::parse(date_str); | |
std::cout<<"date_ast="<<date_ast<<";\n"; | |
} | |
int main() | |
{ | |
std::string date_strs[]= | |
{ "03-04-2001 10:11:12.123" | |
#ifdef USE_ALTERNATIVES | |
, "03-04-2001 10 11 12 123" | |
, "03-04-2001 10 11 12" | |
, "03-04-2001 10 11 " | |
, "2000-01-02" | |
#endif | |
}; | |
for(std::string date_str: date_strs) print_parse(date_str); | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Following code copy&pasted from: | |
// https://stackoverflow.com/questions/60045742/boost-spirit-performance-considerations-date-parsing | |
//On: | |
// 2020-02-03.0824.CST | |
//and then modified to use rm all BOOST_SPIRIT_DEFINE and use of x3::rule | |
//and then use default attributes and as last step, copy from default | |
//attributes to the desired user attribute, Date. | |
//===================== | |
#include <iostream> | |
#include <vector> | |
template <typename Element> | |
std::ostream& operator<<(std::ostream& os, std::vector<Element>const& seq) | |
{ | |
for(int elm:seq){ os<<elm<<" ";} | |
return os; | |
} | |
namespace boost { | |
namespace fusion { namespace detail { | |
using ::operator<<; | |
}} | |
namespace detail { namespace variant { | |
using ::operator<<; | |
}} | |
}//namespace boost | |
#include <boost/optional/optional_io.hpp> | |
#include <boost/spirit/home/x3.hpp> | |
#include <boost/spirit/home/x3/support/utility/unrefcv.hpp> | |
#include <boost/fusion/include/io.hpp> | |
#include <boost/fusion/view/iterator_range/iterator_range.hpp> | |
#include <boost/fusion/iterator/deref.hpp> | |
using TimeType = unsigned; | |
using Day = TimeType; | |
using Month = TimeType; | |
using Year = TimeType; | |
struct Date | |
{ | |
Day _d; | |
Month _m; | |
Year _y; | |
TimeType _hours; | |
TimeType _minutes; | |
TimeType _seconds; | |
TimeType _millisecs; | |
Date(Day d=0, Month m=0, Year y=0, TimeType hours=0, TimeType minutes=0, TimeType seconds=0, TimeType millisecs=0) | |
: _d(d),_m(m),_y(y),_hours(hours),_minutes(minutes),_seconds(seconds),_millisecs(millisecs) | |
{} | |
friend std::ostream& operator<<(std::ostream&sout, Date const& date) { | |
sout | |
<<"{ yr:"<<date._y | |
<<", mo:"<<date._m | |
<<", day:"<<date._d | |
<<", hr:"<<date._hours | |
<<", min:"<<date._minutes | |
<<", sec:"<<date._seconds | |
<<", mil:"<<date._millisecs | |
<<"}"; | |
return sout; | |
} | |
}; | |
namespace x3 = boost::spirit::x3; | |
namespace parsers { | |
static const struct months : x3::symbols<TimeType> { | |
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<TimeType, 10, 4, 4> yyyy; | |
static const x3::uint_parser<TimeType, 10, 1, 2> MM, dd; | |
static const x3::uint_parser<TimeType, 10, 2, 2> H_mm_ss; | |
static const x3::uint_parser<TimeType, 10, 3, 3> zzz; | |
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_ = 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 swap_day_year= | |
[](auto& ctx) | |
{ auto& date_ymd = x3::_attr(ctx); | |
//assert: date_ymd has int's stored in year month day order. | |
using boost::fusion::at_c; | |
std::swap //swap so that day is 1st and year is 3ird: | |
( at_c<0>(date_ymd) | |
, at_c<2>(date_ymd) | |
); | |
}; | |
static const auto date_parser = | |
( (year_ > '-' > month_ > '-' > day_ )[swap_day_year] | |
| (day_ > '-' > month_ > '-' > year_) | |
) | |
; | |
static const auto time_parser | |
= | |
( hours_ > | |
( ( ':' > minutes_ > ':' > seconds_ > '.' > milliseconds_ ) | |
| ( minutes_ | |
> ( ( seconds_ | |
> ( milliseconds_ | |
| x3::attr(TimeType(0)) | |
) | |
) | |
| x3::repeat(2)[x3::attr(TimeType(0))] | |
) | |
) | |
) | |
) | |
| x3::repeat(4)[x3::attr(TimeType(0))] | |
; | |
static const auto date_time_parser = | |
date_parser > time_parser | |
; | |
using date_time_parser_type = x3::unrefcv_t<decltype(date_time_parser)>; | |
using date_time_attr_type = x3::traits::attribute_of<date_time_parser_type,x3::unused_type,void>::type; | |
struct attr2date | |
//Purpose: | |
// copy attr from parser to the user attribute of type, Date. | |
: public boost::static_visitor<> | |
{ | |
Date& _date; | |
unsigned _index; | |
attr2date(Date&date):_date(date),_index(0) | |
{ | |
} | |
void put_then_next(TimeType time_value) | |
{ | |
switch(_index) | |
{ | |
case 0: | |
_date._d=time_value; | |
break; | |
case 1: | |
_date._m=time_value; | |
break; | |
case 2: | |
_date._y=time_value; | |
break; | |
case 3: | |
_date._hours=time_value; | |
break; | |
case 4: | |
_date._minutes=time_value; | |
break; | |
case 5: | |
_date._seconds=time_value; | |
break; | |
case 6: | |
_date._millisecs=time_value; | |
break; | |
default: | |
std::cout<<"invalid index="<<_index<<"!\n"; | |
} | |
++_index; | |
} | |
template<typename... T> | |
void put_then_next(boost::variant<T...>const&nxt) | |
{ | |
(*this)(nxt); | |
} | |
template<typename Seq> | |
auto make_iter_range(Seq const&seq) | |
{ | |
auto beg_iter=boost::fusion::begin(seq); | |
auto end_iter=boost::fusion::end(seq); | |
using beg_type = x3::unrefcv_t<decltype(beg_iter)>; | |
using end_type = x3::unrefcv_t<decltype(end_iter)>; | |
return boost::fusion::iterator_range<beg_type,end_type>(beg_iter,end_iter); | |
} | |
template<typename Beg, typename End> | |
auto next_iter_range(boost::fusion::iterator_range<Beg,End>const&iter_range) | |
{ | |
auto beg_iter=boost::fusion::advance_c<1>(iter_range.first); | |
using beg_type = x3::unrefcv_t<decltype(beg_iter)>; | |
auto end_iter=iter_range.last; | |
return boost::fusion::iterator_range<beg_type,End>(beg_iter,end_iter); | |
} | |
void operator()(TimeType time_value) | |
{ | |
this->put_then_next(time_value); | |
} | |
void operator()(int time_value) | |
{ | |
this->put_then_next(time_value); | |
} | |
template<typename... T> | |
void operator()(boost::variant<T...>const&nxt) | |
{ | |
boost::apply_visitor(*this,nxt); | |
} | |
template <typename Element> | |
void operator()(std::vector<Element>const &vec) | |
{ for(Element val:vec) (*this)(val); | |
} | |
template <typename EndIter> | |
void operator()(boost::fusion::iterator_range<EndIter,EndIter>const& iter_range) | |
{} | |
template <typename BegIter, typename EndIter> | |
void operator()(boost::fusion::iterator_range<BegIter,EndIter>const& iter_range) | |
{ | |
//std::cout<<"iter_range="<<iter_range<<";\n"; | |
using range_type = boost::fusion::iterator_range<BegIter,EndIter>; | |
auto deref_val = boost::fusion::deref(iter_range.first); | |
//std::cout<<"deref_val="<<deref_val<<";\n"; | |
this->put_then_next(deref_val); | |
auto iter_nxt = this->next_iter_range(iter_range); | |
//std::cout<<"iter_nxt="<<iter_nxt<<";\n"; | |
(*this)(iter_nxt); | |
} | |
template <typename... T> | |
void operator()(boost::fusion::deque<T...>const& seq) | |
{ //std::cout<<"seq="<<seq<<";\n"; | |
auto iter_range_seq=make_iter_range(seq); | |
//std::cout<<"make_iter_range="<<iter_range_seq<<";\n"; | |
(*this)(iter_range_seq); | |
} | |
}; | |
Date parse(const std::string& date_str) | |
{ | |
auto first = date_str.begin(); | |
const auto last = date_str.end(); | |
boost::spirit::x3::ascii::space_type space; | |
date_time_attr_type date_time_attr_valu; | |
bool r = | |
phrase_parse | |
( first | |
, last | |
, date_time_parser | |
, space | |
, date_time_attr_valu | |
); | |
Date date_attr{}; | |
if ( not(r && first == last) ) | |
{ std::cout | |
<< "Parsing of " << date_str | |
<< " failed at " << std::string(first, last) | |
<< ";\n"; | |
} | |
else | |
{ | |
attr2date viz{date_attr}; | |
viz(date_time_attr_valu); | |
} | |
return date_attr; | |
}; | |
} // namespace parsers | |
void print_parse(std::string date_str) | |
{ | |
std::cout<<"date_str="<<date_str<<";\n"; | |
auto date_ast = parsers::parse(date_str); | |
std::cout<<"date_ast="<<date_ast<<";\n"; | |
} | |
int main() | |
{ | |
print_parse("2000-01-02"); | |
print_parse("03-04-2001 10:11:12.123"); | |
print_parse("03-04-2001 10 11 12 123"); | |
print_parse("03-04-2001 10 11 12"); | |
print_parse("03-04-2001 10 11 "); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment