I'm trying to parse a duration from RFC3339 using Boost.Spirit, but I'm having trouble.
The grammar is defined in RFC3339 Appendix A:
dur-second = 1*DIGIT "S"
dur-minute = 1*DIGIT "M" [dur-second]
dur-hour = 1*DIGIT "H" [dur-minute]
dur-time = "T" (dur-hour / dur-minute / dur-second)
dur-day = 1*DIGIT "D"
dur-month = 1*DIGIT "M" [dur-day]
dur-year = 1*DIGIT "Y" [dur-month]
dur-date = (dur-day / dur-month / dur-year) [dur-time]
duration = "P" (dur-date / dur-time)
(I dropped support for weeks.)
Which I translated to this grammar:
auto second = copy(int_ >> "S");
auto minute = copy(int_ >> "M" >> -second);
auto hour = copy(int_ >> "H" >> -minute);
auto time = copy("T" >> (hour | minute | second));
auto day = copy(int_ >> "D");
auto month = copy(int_ >> "M" >> -day);
auto year = copy(int_ >> "Y" >> -month);
auto date = copy((day | month | year) >> -time);
m_start = "P" >> (date | time);
I believe this grammar is a faithful representation of the grammar in RFC3339, but I could be wrong.
The trouble I'm having is getting the information out. I usually create a POD structure to pass in that gets filled out automatically, but as written, I'm getting a compile error. First, the code:
#include "boost/fusion/adapted/struct.hpp"
#include "boost/spirit/include/qi.hpp"
#include "boost/spirit/include/qi_copy.hpp"
#include <cstdlib>
struct Duration {
int years;
int months;
int days;
int hours;
int minutes;
int seconds;
};
BOOST_FUSION_ADAPT_STRUCT(
Duration,
(int, years)(int, months)(int, days)(int, hours)(int, minutes)(int, seconds))
struct Duration_grammar :
boost::spirit::qi::grammar<std::string::const_iterator, Duration()> {
Duration_grammar() : Duration_grammar::base_type{m_start}
{
using boost::spirit::qi::copy;
using boost::spirit::qi::int_;
auto second = copy(int_ >> "S");
auto minute = copy(int_ >> "M" >> -second);
auto hour = copy(int_ >> "H" >> -minute);
auto time = copy("T" >> (hour | minute | second));
auto day = copy(int_ >> "D");
auto month = copy(int_ >> "M" >> -day);
auto year = copy(int_ >> "Y" >> -month);
auto date = copy((day | month | year) >> -time);
m_start = "P" >> (date | time);
}
private:
boost::spirit::qi::rule<Duration_grammar::iterator_type, Duration()> m_start;
};
int main(int argc, char** argv)
{
auto const value = std::string{"P1Y"};
auto begin = value.begin();
auto end = value.end();
auto duration = Duration{};
if (boost::spirit::qi::parse(begin, end, Duration_grammar{}, duration) &&
(begin == end)) {
return EXIT_SUCCESS;
} else {
return EXIT_FAILURE;
}
}
And the error:
/usr/include/boost/spirit/home/qi/detail/assign_to.hpp:153:20: error: no matching function for call to 'Duration::Duration(const int&)'
153 | attr = static_cast<Attribute>(val);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
It seems like the attribute of m_start
is not a sequence of six integers like the Duration
POD structure would like, but I'm not sure how to approach this. I went with the auto
and copy
approach because I couldn't figure out the actual attribute types that each rule would have. Do I need a different grammar to get what I want (i.e., is copying the grammar from RFC3339 a bad approach)?