4

I'm writing a boost::spirit::qi grammar for date parsing.

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/date_time.hpp>

template < typename InputIterator >
struct date_rfc1123_grammar :
            boost::spirit::qi::grammar< InputIterator, boost::gregorian::date()> {
    typedef boost::gregorian::date value_type;
    date_rfc1123_grammar() : date_rfc1123_grammar::base_type(date)
    {
        namespace qi = boost::spirit::qi;
        namespace phx = boost::phoenix;
        using qi::_pass;
        using qi::_val;
        using qi::_2;
        using qi::_3;
        using qi::_4;

        _2digits = qi::uint_parser< std::uint32_t, 10, 2, 2 >();
        _4digits = qi::uint_parser< std::uint32_t, 10, 4, 4 >();
        date = (weekday >> ' ' >> _2digits >> ' ' >> month >> ' ' >> _4digits)
            [
              phx::try_[
                _val = phx::construct< value_type >( _4, _3, _2 )
              ].catch_all[
                _pass = false
              ]
            ];
    }
    boost::spirit::qi::rule< InputIterator, value_type()> date;
    weekday_grammar weekday;
    month_grammar month;
    boost::spirit::qi::rule< InputIterator, std::int32_t() > _2digits;
    boost::spirit::qi::rule< InputIterator, std::int32_t() > _4digits;
};

I rely upon the boost::gregorian::date constructor for arguments checking and would like the parser to fail in case of an exception. But the boost::phoenix::try_[ ].catch_all[ ] construct fails to compile with the following message:

/path_to_file/datetime_parse.hpp:102:8:   required from β€˜tip::http::grammar::parse::date_rfc1123_grammar<InputIterator>::date_rfc1123_grammar() [with InputIterator = boost::spirit::multi_pass<std::istreambuf_iterator<char, std::char_traits<char> > >]’
/path_to_file/grammar_parse_test.hpp:17:7:   required from here
/usr/local/include/boost/proto/traits.hpp:341:13: error: static assertion failed: 0 == Expr::proto_arity_c
             BOOST_STATIC_ASSERT(0 == Expr::proto_arity_c);
             ^

Without the try_.catch_all construct the grammar compiles OK, but I would like the parser to catch the exception and set the _pass flag to false to make the grammar to fail.

OS and compiler info:

$ uname -a
Linux zmij 3.19.0-27-generic #29-Ubuntu SMP Fri Aug 14 21:43:37 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
$ g++ -v
Thread model: posix
gcc version 4.9.2 (Ubuntu 4.9.2-10ubuntu13)

boost version 1.58

zmij
  • 57
  • 7
  • I think you underestimate the time it takes for random helpers to find out exactly what headers you omitted and what other bits are missing to test out your code. You should always include a SSCCE (aka MCVE). Especially since that was trivial (for you!) in this case. – sehe Aug 27 '15 at 11:19
  • Sorry for that @sehe , fixed the question. – zmij Aug 27 '15 at 12:47

1 Answers1

4

I've seen this before, might be local to certain boost/compiler versions.

A workaround would be to include a no-op statement (e.g. _pass=_pass) making it a sequence:

    date = (weekday >> ' ' >> _2digits >> ' ' >> month >> ' ' >> _4digits)
        [
          _pass = _pass,
          phx::try_[
            _val = phx::construct< value_type >( _4, _3, _2 )
          ].catch_all[
            _pass = false
          ]
        ];

See it Live On Coliru

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/date_time/gregorian/greg_date.hpp>

template <typename InputIterator>
struct date_rfc1123_grammar : boost::spirit::qi::grammar< InputIterator, boost::gregorian::date()> 
{
    typedef boost::gregorian::date value_type;
    date_rfc1123_grammar() : date_rfc1123_grammar::base_type(date)
    {
        namespace qi  = boost::spirit::qi;
        namespace phx = boost::phoenix;
        using qi::_pass;
        using qi::_val;
        using qi::_2;
        using qi::_3;
        using qi::_4;

        date = (weekday >> ' ' >> _2digits >> ' ' >> month >> ' ' >> _4digits)
            [
              _pass = _pass,
              phx::try_[
                _val = phx::construct< value_type >( _4, _3, _2 )
              ].catch_all[
                _pass = false
              ]
            ];
    }
    boost::spirit::qi::rule< InputIterator, value_type()> date;
    boost::spirit::qi::rule< InputIterator, uint()> weekday, month;
    boost::spirit::qi::uint_parser< std::uint32_t, 10, 2, 2 > _2digits;
    boost::spirit::qi::uint_parser< std::uint32_t, 10, 4, 4 > _4digits;
};

int main() {
    using It = std::string::const_iterator;
    std::string const input;

    date_rfc1123_grammar<It> g;

    It f = input.begin(), l = input.end();

    boost::gregorian::date d;
    bool ok = boost::spirit::qi::parse(f, l, g, d);

    return ok?1:2;
}
sehe
  • 374,641
  • 47
  • 450
  • 633