Search code examples
c++boundsboost-spirit-qi

Boost.Spirit.Qi - Bounds checking against primitive data types


I need to check that the value of a parsed qi::uint_ is less than 256.


I stumbled across an SO post outlining the following syntax to run checks after a primitive type has been parsed (qi::double_ in this example).

raw [ double_ [_val = _1] ] [ _pass = !isnan_(_val) && px::size(_1)<=4 ]

Here, raw[...] returns an iterator to the parsed qi::double_ value, and the final semantic action is used to "test" the resulting value.


Extrapolating from the previous example, I assumed I could check bounds using a similar approach.

raw [ uint_ [_val = _1] ] [ _pass = _val<=256 ]

Unfortunately, I get the following error.

boost.spirit.qi.bounds.cpp:51:105: error: invalid operands to binary expression ('const boost::spirit::_val_type'
  (aka 'const actor<attribute<0> >') and 'int')
    if (qi::parse(str.begin(), str.end(), qi::raw[qi::uint_[qi::_val = qi::_1]][qi::_pass = qi::_val<=256]).full)
                                                                                            ~~~~~~~~^ ~~~

The documentation and examples are great for basic parsers, but it begins to taper off with more advanced topics; such as this.

How can I convert or extract the unsigned integer value from qi::_val to test against 256?


Solution

  • You have missed the fact that raw[] exposes an iterator range. The other answer used that because the "extra" constraint was referring to the input length (in characters).

    You don't need that, so you'd rather use something direct like:

    Live On Coliru

    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    #include <iostream>
    namespace qi = boost::spirit::qi;
    
    int main ()
    {
        using It = std::string::const_iterator;
        qi::rule<It, double()> r
            = qi::double_ [ qi::_pass = (qi::_1 < 256.0), qi::_val = qi::_1 ];
    
        for (std::string const s: { "1.23", ".123", "2.e6", "inf", "-inf", "3.2323", "nan" })
        {
            It f = s.begin(), l = s.end();
    
            double result;
            if (parse(f, l, r, result))
                 std::cout << "accepted: '" << s << "' -> " << result;
            else std::cout << "rejected: '" << s << "'";
    
            if (f!=l)
                 std::cout << " (remaining: '" << std::string(f,l) << "')\n";
            else std::cout << "\n";
        }
    }
    

    Prints

    accepted: '1.23' -> 1.23
    accepted: '.123' -> 0.123
    rejected: '2.e6' (remaining: '2.e6')
    rejected: 'inf' (remaining: 'inf')
    accepted: '-inf' -> -inf
    accepted: '3.2323' -> 3.2323
    rejected: 'nan' (remaining: 'nan')
    

    Notes:

    1. the [action1, action2] is the Phoenix way of supplying multiple statements (in this case would be very similar to [action1][action2]).

    2. you can even do without the _val= assignment, because that's just what default attribute propagation is.

      In order to enable default attribute propagation on a rule that semantic action(s), use operator%= to define it:

      r %= qi::double_ [ qi::_pass = (qi::_1 < 256.0) ];
      

      Live On Coliru

      That prints the same output.