2

The program below tries to parse C++ header include strings, such as "my/file.hpp" and <my/file.hpp>. For reasons I don't understand, my code fails to parse the " headers. Is this a bug in Spirit, or am I missing something obvious?

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <string>
#include <cassert>

using namespace boost::spirit::x3;

int main() {

    auto header_name_brackets = '<' >> *(~char_('>')) >> '>';
    auto header_name_quotes   = '"' >> *(~char_('>')) >> '"'; 

    {
        auto s = std::string{"<my/file.hpp>"};
        std::string parsed;
        assert(phrase_parse(s.begin(), s.end(), header_name_brackets, space, parsed));
    }

    {
        auto s = std::string{"\"my/file.hpp\""};
        std::string parsed;
        // this assert fails, but I don't know why.
        assert(phrase_parse(s.begin(), s.end(), header_name_quotes, space, parsed));
    }
}
Barrett Adair
  • 1,306
  • 10
  • 24

3 Answers3

4

Since you already have more answers than you can accept (:)) here's my $0.02:

template <typename Prefix, typename Postfix>
static auto quoted_(Prefix const& prefix, Postfix const& postfix) {
    using namespace boost::spirit::x3;
    return rule<struct _, std::string, true> {} = omit[prefix] >> *(char_ - postfix) >> omit[postfix];
}

Now you can write

auto header_name_brackets = quoted_('<', '>');
auto header_name_quotes   = quoted_('"');

The second assumes the obvious convenience overload.

Another bug

In fact I think there's a bug that skips whitespace inside the delimiters. Fix it by adding lexeme[]:

template <typename Prefix, typename Postfix>
static auto quoted_(Prefix const& prefix, Postfix const& postfix) {
    using namespace boost::spirit::x3;
    return rule<struct _, std::string, true> {} = lexeme [ 
        omit[prefix] >> *(char_ - postfix) >> omit[postfix] 
    ];
}

See full demo:

Live On Coliru

#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <cassert>

template <typename Prefix, typename Postfix>
static auto quoted_(Prefix const& prefix, Postfix const& postfix) {
    using namespace boost::spirit::x3;
    return rule<struct _, std::string, true> {} = lexeme [ 
        omit[prefix] >> *(char_ - postfix) >> omit[postfix] 
    ];
}

template <typename Prefix>
static auto quoted_(Prefix const& prefix) { return quoted_(prefix, prefix); }

int main() {
    using boost::spirit::x3::space;

    auto header_name_brackets = quoted_('<', '>');
    auto header_name_quotes   = quoted_('"');

    {
        auto s = std::string{"<my/file.hpp>"};
        std::string parsed;
        assert(phrase_parse(s.begin(), s.end(), header_name_brackets, space, parsed));
    }

    {
        auto s = std::string{"\"my/file.hpp\""};
        std::string parsed;
        assert(phrase_parse(s.begin(), s.end(), header_name_quotes, space, parsed));
    }

    std::cout << "Bye\n";
}
sehe
  • 374,641
  • 47
  • 450
  • 633
2

You need to change this rule:

auto header_name_quotes   = '"' >> *(~char_('>')) >> '"'; 

to

auto header_name_quotes   = '"' >> *(~char_('\"')) >> '"'; 
Chris Beck
  • 15,614
  • 4
  • 51
  • 87
2

This works for me:

#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <string>
#include <cassert>

using namespace boost::spirit::x3;

int main() {

    auto header_name_brackets = '<' >> *(~char_('>')) >> '>';
    auto header_name_quotes   = '"' >> *(~char_('"')) >> '"'; 

    {
        auto s = std::string{"<my/file.hpp>"};
        std::string parsed;
        assert(phrase_parse(s.begin(), s.end(), header_name_brackets, space, parsed));
    }

    {
        auto s = std::string{"\"my/file.hpp\""};
        std::string parsed;
        // this assert fails, but I don't know why.
        assert(phrase_parse(s.begin(), s.end(), header_name_quotes, space, parsed));
    }
}

Note that you need to match all chars except " in the second case, just as you did with > in the first.

rainer
  • 6,769
  • 3
  • 23
  • 37
  • Thanks. I was drawing from the C++17 working draft grammar. Indeed, there is both an h-char rule and a q-char rule. This silly oversight has been driving me crazy! In retrospect, I wish my question had been more substantial. – Barrett Adair Aug 20 '16 at 17:50