My input are multiple key, value pairs e.g.:
A=1, B=2, C=3, ..., A=4
I want to parse the input into the following type:
std::map< char, std::vector< int > > m
Values for equal keys shall be appended to the vector. So the parsed output should be equal to:
m['A']={1,4};
m['B']={2};
m['C']={3};
What is the simplest solution using 'boost::spirit::qi' ?
Here is one way to do it:
A few comments about the implementation:
qi::phrase_parse
is a Boost.Spirit algorithm that takes a pair of iterators, a parser, and a skip parser, and runs the parsers on the input denoted by the iterators. In the process, it updates the beginning iterator (it
in this example) so that it points to the end of the consumed input upon return. The returnedres
value indicates whether the parsers have succeeded (i.e. the consumed input could be successfully parsed). There are other forms ofqi::phrase_parse
that allow extracting attributes (which is the parsed data, in terms of Boost.Spirit) but we're not using attributes here because you have a peculiar requirement of the resulting container structure.The skip parser is used to skip portions of the input between the elements of the main parser. In this case,
qi::space
means that any whitespace characters will be ignored in the input, so that e.g. "A = 1" and "A=1" can both be parsed similarly. There isqi::parse
family of algorithms which do not have a skip parser and therefore require the main parser to handle all input without skips.The
(qi::char_ >> '=' >> qi::int_)
part of the main parser matches a single character, followed by the equals sign character, followed by a signed integer. The equals sign is expressed as a literal (i.e. it is equivalent to theqi::lit('=')
parser), which means it only matches the input but does not result in a parsed data. Therefore the result of this parser is an attribute that is a sequence of two elements - a character and an integer.The
% ','
part of the parser is a list parser, which parses any number of pieces of input described by the parser on the left (which is the parser described above), separated by the pieces described by the parser on the right (i.e. with comma characters in our case). As before, the comma character is a literal parser, so it doesn't produce output.The
[inserter]
part is a semantic action, which is a function that is called by the parser every time it matches a portion of input string. The parser passes all its parsed output as the first argument to this function. In our case the semantic action is attached to the parser described in bullet #3, which means a sequence of a character and an integer is passed. Boost.Spirit uses afusion::vector
to pass these data. The other two arguments of the semantic action are not used in this example and can be ignored.The
inserter
function in this example is a lambda function, but it could be any other kind of function object, including a regular function, a function generated bystd::bind
, etc. The important part is that it has the specified signature and that the type of its first argument is compatible with the attribute of the parser, to which it is attached as a semantic action. So, if we had a different parser in bullet #3, this argument would have to be changed accordingly.fusion::at_c< N >()
in theinserter
obtains the element of the vector at indexN
. It is very similar tostd::get< N >()
when applied tostd::tuple
.