Bloops Bloops - 3 months ago 15
C++ Question

Parse only specific numbers with Boost.Spirit

How can I build a Boost.Spirit parser that matches only numbers in a certain range?

Consider the simple parser

qi::uint_
. It matches all unsigned integers. Is it possible to construct a parser that matches the numbers
0
to
12345
but not
12346
and larger?

Answer

One way is to attach to the qi::unit_ parser a semantic action that checks the parser's attribute and sets the semantic action's third parameter accordingly:

#include <iostream>
#include <string>
#include <vector>

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

namespace qi = boost::spirit::qi;

int main() {
  qi::rule<std::string::const_iterator, unsigned(), qi::ascii::space_type> rule;

  const auto not_greater_than_12345 = [](const unsigned& attr, auto&, bool& pass) {
    pass = !(attr > 12345U);
  };
  rule %= qi::uint_[not_greater_than_12345];

  std::vector<std::string> numbers{"0", "123", "1234", "12345", "12346", "123456"};
  for (const auto& number : numbers) {
    unsigned result;
    auto iter = number.cbegin();
    if (qi::phrase_parse(iter, number.cend(), rule, qi::ascii::space, result) &&
        iter == number.cend()) {
      std::cout << result << '\n';  // 0 123 1234 12345
    }
  }
}

Live on Wandbox

The semantic action can be written more concisely with the Phoenix placeholders _pass and _1:

#include <iostream>
#include <string>
#include <vector>

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

namespace qi = boost::spirit::qi;

int main() {
  qi::rule<std::string::const_iterator, unsigned(), qi::ascii::space_type> rule;

  rule %= qi::uint_[qi::_pass = !(qi::_1 > 12345U)];

  std::vector<std::string> numbers{"0", "123", "1234", "12345", "12346", "123456"};
  for (const auto& number : numbers) {
    unsigned result;
    auto iter = number.cbegin();
    if (qi::phrase_parse(iter, number.cend(), rule, qi::ascii::space, result) &&
        iter == number.cend()) {
      std::cout << result << '\n';  // 0 123 1234 12345
    }
  }
}

Live on Wandbox


From Semantic Actions with Parsers

The possible signatures for functions to be used as semantic actions are:

...
template <typename Attrib, typename Context>
void fa(Attrib& attr, Context& context, bool& pass);

... Here Attrib is the attribute type of the parser attached to the semantic action. ... The third parameter, pass, can be used by the semantic action to force the associated parser to fail. If pass is set to false the action parser will immediately return false as well, while not invoking p and not generating any output.