2

I want to parse string which consists of CC[n], where 1 <= n <= 4 or from SERVICE[k], where 1 <= k <= 63.

Valid strings: "CC1", "CC2", "CC3", "CC4", "SERVICE1", "SERVICE2", ..., "SERVICE63".

I wrote the next expression:

( '"' >> (qi::raw["CC" >> qi::uint_] | qi::raw["SERVICE" >> qi::uint_]) >> '"' >> qi::eoi)

But how I can limit n and k?

In output I need to got full string CC1, CC2, ... SERVICE63

2 Answers2

2

The simplest way would be to use symbols<>.

The elaborate way is to validate the numbers in semantic actions.

My recommendation is is either symbols OR separate semantic validation from parsing (i.e. parse the numbers raw and validate the AST after the parse)

Symbols

This is likely the more flexible, most efficient, and allows you to be strongtyped in your AST domain. It sidesteps the compilation overhead and complexity of semantic actions: Boost Spirit: "Semantic actions are evil"?

Live On Coliru

#include <boost/spirit/include/qi.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;

int main() {
    qi::symbols<char> cc, service;
    cc += "CC1", "CC2", "CC3", "CC4";
    service += "SERVICE1", "SERVICE2", "SERVICE3", "SERVICE4", "SERVICE5",
        "SERVICE6", "SERVICE7", "SERVICE8", "SERVICE9", "SERVICE10",
        "SERVICE11", "SERVICE12", "SERVICE13", "SERVICE14", "SERVICE15",
        "SERVICE16", "SERVICE17", "SERVICE18", "SERVICE19", "SERVICE20",
        "SERVICE21", "SERVICE22", "SERVICE23", "SERVICE24", "SERVICE25",
        "SERVICE26", "SERVICE27", "SERVICE28", "SERVICE29", "SERVICE30",
        "SERVICE31", "SERVICE32", "SERVICE33", "SERVICE34", "SERVICE35",
        "SERVICE36", "SERVICE37", "SERVICE38", "SERVICE39", "SERVICE40",
        "SERVICE41", "SERVICE42", "SERVICE43", "SERVICE44", "SERVICE45",
        "SERVICE46", "SERVICE47", "SERVICE48", "SERVICE49", "SERVICE50",
        "SERVICE51", "SERVICE52", "SERVICE53", "SERVICE54", "SERVICE55",
        "SERVICE56", "SERVICE57", "SERVICE58", "SERVICE59", "SERVICE60",
        "SERVICE61", "SERVICE62", "SERVICE63";

    for (std::string const input : {
             // valid:
             "CC1",
             "CC2",
             "CC3",
             "CC4",
             "SERVICE1",
             "SERVICE2",
             "SERVICE63",
             // invalid:
             "CC0",
             "CC5",
             "SERVICE0",
             "SERVICE64",
         }) {

        bool valid = parse(begin(input), end(input), service|cc);
        std::cout << std::quoted(input) << " -> "
                  << (valid ? "valid" : "invalid") << "\n";
    }
}

Prints

"CC1" -> valid
"CC2" -> valid
"CC3" -> valid
"CC4" -> valid
"SERVICE1" -> valid
"SERVICE2" -> valid
"SERVICE63" -> valid
"CC0" -> invalid
"CC5" -> invalid
"SERVICE0" -> invalid
"SERVICE64" -> invalid

Bonus: the strongtyped idea: http://coliru.stacked-crooked.com/a/2cb07d4da9aad39e

Semantic Actions

In a nutshell:

qi::rule<It, intmax_t(intmax_t min, intmax_t max)> constrained_num =
    qi::uint_[_pass = (_1 >= _r1 && _1 <= _r2)];

qi::rule<It> cc      = "CC" >> constrained_num(1, 4),
             service = "SERVICE" >> constrained_num(1, 63);

Live On Coliru

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;
using It     = std::string::const_iterator;

int main() {
    using namespace qi::labels;
    qi::rule<It, intmax_t(intmax_t min, intmax_t max)> constrained_num =
        qi::uint_[_pass = (_1 >= _r1 && _1 <= _r2)];

    qi::rule<It> cc      = "CC" >> constrained_num(1, 4),
                 service = "SERVICE" >> constrained_num(1, 63);

    for (std::string const input : {
            // valid:
             "CC1",
             "CC2",
             "CC3",
             "CC4",
             "SERVICE1",
             "SERVICE2",
             "SERVICE63",
             // invalid:
             "CC0",
             "CC5",
             "SERVICE0",
             "SERVICE64",
         }) {

        bool valid = parse(begin(input), end(input), service|cc);
        std::cout << std::quoted(input) << " -> "
                  << (valid ? "valid" : "invalid") << "\n";
    }
}
    

Prints the same as above

sehe
  • 374,641
  • 47
  • 450
  • 633
1

To limit uint_ range, you can perform a range-checking in a semantic action. It can be implemented, for example, as lambda or, more concisely, as a Boost.Phenix expression.

The following code parses these numbers into a vector (omitting the strings):

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


int main()
{
    std::string input =  "CC1 CC2 CC3 CC4 SERVICE1 SERVICE2";
    std::vector<unsigned int> out;

    using namespace boost::spirit::qi;
    phrase_parse(
        input.begin(), 
        input.end(), 
        *(lexeme[lit("CC") >> uint_ [ _pass = (_1>=1 && _1<=4) ]] | 
        lexeme[lit("SERVICE") >> uint_ [ _pass = (_1>=1 && _1<=63) ]]), 
        ascii::space, 
        out
    );
    for (auto i : out)
        std::cout << i << std::endl;
}
Igor R.
  • 14,716
  • 2
  • 49
  • 83