DISABLE ADBLOCK

ADBlock is blocking some content on the site

ADBlock errore

Boost Spirit QI issues with strings and skipping

StackOverflow https://stackoverflow.com/questions/20301770
 Checked

Question

So I've begun playing with the Boost Spirit library and its absolutely amazing! But along the way i have ran into many errors - many on my behalf for not fully reading the documentation.. But after going through as much as I could find, i am stumped on attributes - strings in regards to skipping.

At the moment I'd like to detect an ASM alike label and print it in a console like so:

    Identifier = qi::lexeme[qi::alpha >> (*qi::alnum | qi::lit("_"))]
    Label      = Identifier >> ":"
    Syntax     = Label[phx::bind(&Grammar::print, this, qi::_1)];

where phx::bind calls a simple print string function to cout. The rules are defined like so:

    qi::rule<Iterator, std::string(), ascii::space_type> Identifier;
    qi::rule<Iterator, std::string(), ascii::space_type> Label;
    qi::rule<Iterator,                ascii::space_type> Syntax;

Which works BUT the problem is, I don't want the skipper to skip between the Identifier and the ":" literal.

I have tried:

    Label = qi::lexeme [Identifier >> ":"]
    Label = qi::no_skip[.................]
    etc

But i receive error messages such as converting to parameter 4 - cannot convert unused_skipper<..> to char_class<..> which makes some sense. I also tried removing the skipper type on the rule definition which didn't work. I then tried mixing it up and making some rules attributes strings whilst others not, - and converting them to strings using as_string - worked, but output was empty. So here I am. stumped.

Is there something I'm not understanding about attribute propagation? Or perhaps something about attributes in general. Maybe even a method to return a string from a non-skipped sequence?

Would someone please be able to enlighten me on my errors? & Any future tips on attributes?

Many Thanks, Adam.

Solution

The lexeme solution does work.

The no_skip and lexeme directive require that the contained parsers don't use a skipper, so you need to change Identifier into

qi::rule<Iterator, std::string()> Identifier;

Incidentally, then lexeme inside it becomes redundant:

qi::rule<It, std::string()                > Identifier = qi::alpha >> *(qi::alnum | qi::char_("_"));
qi::rule<It, std::string(), qi::space_type> Label      = qi::lexeme [ Identifier >> ':' ];
qi::rule<It,                qi::space_type> Syntax     = Label [ handle_label_(qi::_1) ];

In fact you can further reduce it (by relying on pre-skipping in the Syntax rule):

static const rule<std::string::const_iterator, std::string()> 
    Identifier = alpha >> *(alnum | char_("_")),
    Label      = Identifier >> ':';

A full test program: See it Live on Coliru

#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/phoenix/function/adapt_function.hpp>
#include <boost/spirit/include/qi.hpp>

namespace qi = boost::spirit::qi;

void handle_label(std::string const& s) { std::cout << "Parsed label '" << s << ":'\n"; }
BOOST_PHOENIX_ADAPT_FUNCTION(void, handle_label_, handle_label, 1)

bool using_lexeme(std::string const& input)
{
    using namespace qi;
    static const rule<std::string::const_iterator, std::string()> 
        Identifier = alpha >> *(alnum | char_("_")),
        Label      = Identifier >> ':';

    auto f(input.begin()), l(input.end());
    return phrase_parse(f, l, Label [ handle_label_(_1) ], space);
}

int main()
{
    assert( using_lexeme("some_id:"));
    assert( using_lexeme("some_id: "));
    assert( using_lexeme(" some_id:"));
    assert(!using_lexeme("some_id :"));
}

Two further notes:

  • no_skip doesn't pre-skip either (lexeme means more like "keep together"). Try the difference and see how the third test case would fail

  • Your use of lit('_') would prevent underscores from being appended to the string attribute.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow