2

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?

Zak
  • 12,213
  • 21
  • 59
  • 105

1 Answers1

2

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.

sehe
  • 374,641
  • 47
  • 450
  • 633
  • I'm not sure where it is getting used, but `#include ` is a must. – Zak Jul 10 '18 at 22:27
  • 1
    It's being used in the operators: `_1 < 256.0` is an expression using `boost::phoenix::operator<`, and `_pass = x` uses `boost::phoenix::operator=`. These are known as expression templates and is what makes Spirit "DSL" possible. – sehe Jul 10 '18 at 23:06