0

I have been playing around with parsing with Boost Spirit and was wondering if anyone could help me get this to work. I have a simple parser that takes a file containing a pair of entries on each line. Something similar to the following:

Foo 04B
Bar 1CE
Bam 456

My code below currently parses this out and places each pair into a std::map and it seems to work correctly. What I really want to do is parse out the second string on each line and convert it to an integer. I have looked at int_parser and how you can specify the base but have been unable to get a similar setup to compile.

namespace qi = boost::spirit::qi;
std::map<std::string, std::string> results;

void insert(std::pair<std::string, std::string> p) {
    results[p.first] = p.second;
}

template <typename Iterator>
bool parse_numbers(Iterator first, Iterator last) {
    using qi::char_;
    using qi::parse;

    qi::rule<Iterator, std::pair<std::string, std::string>()> assignment;
    assignment = +(~char_(' '))  >> +(char_);

    bool r = parse(
    first,
    last,
    assignment[&insert]);

    if (first != last)
        return false;

    return r;
}

int main(int argc, char* argv[]) {
    std::ifstream ifs;
    std::string str;
    ifs.open (argv[1], std::ifstream::in);

    while (getline(ifs, str)) {
        if (!parse_numbers(str.begin(), str.end())) {
            std::cout << "Parsing failed\n";
        }
    }

    return 0;
}

What I would really like if to parse it out directly as a std::pair<std::string, int>. Any help is appreciated.

More information:

I was trying to declare a parser similar to this one: uint_parser<unsigned, 16> hex_value; and then I was trying to replace the +(char_) in my rule to +(hex_value).

tgai
  • 1,117
  • 2
  • 14
  • 29
  • The code you're showing here seems to work correctly. And you have some code elsewhere that you need help with, because it's producing one or more compiler errors. Is that right? – Drew Dormann Jun 29 '15 at 20:27
  • Added a little more information on what I was trying. I think it should just be a small modification to the code above but I keep getting crazy compile errors that I don't understand. – tgai Jun 29 '15 at 20:43

1 Answers1

3

I'm a little confused with everything complicated going on there (especially the semantic action [&insert] which seems to gratuitously use a global variable).

See Boost Spirit: "Semantic actions are evil"? if you're interested in my stance on premature use of semantic actions.

In this case you could just use Boost Fusion adaptation of std::pair<>

#include <boost/fusion/adapted/std_pair.hpp>

and now you can simply assign directly into a map:

    std::map<std::string, int> results;

    if (ifs >> qi::phrase_match(
                (qi::lexeme[+qi::graph] >> qi::int_parser<int, 16>{}) % qi::eol,
                qi::blank, results)
            )

Demo

As you surmised I

  • used int_parser<int,16>
  • replaced the input file with std::cin for simplicity (NOTE you didn't check argc...)
  • uses phrase_match as a nice way to parse from input stream iterators implicitly
  • parsers multiple lines in one swoop

Live On Coliru

#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_match.hpp>
#include <map>

namespace qi = boost::spirit::qi;

int main() {
    std::cin.unsetf(std::ios::skipws);

    std::map<std::string, int> results;

    if (std::cin >> qi::phrase_match(
                (qi::lexeme[+qi::graph] >> qi::int_parser<int, 16>{}) % qi::eol,
                qi::blank, results)
       )
    {
        std::cout << "Parse success:\n";
        for(auto& entry : results)
            std::cout << "'" << entry.first << "' -> " << entry.second << "\n";
    } else {
        std::cout << "Parse failed\n";
    }
}

Output:

Parse success:
'Bam' -> 1110
'Bar' -> 462
'Foo' -> 75
Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633