1

I have written the following simple grammar to detect if a string returned by a server is a failed login. I was using it inlined in the phrase_parse function, but since the code gets called regularly, I wanted to make a static instance of the grammar. What I was using was this:

bool loginFailed() 
{
    namespace qi = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;

    // current data is an re2 stringpiece. so .data returns char*
    const char* front = currentData.data();
    const char* back = currentData.end();

    return qi::phrase_parse(front, back, -qi::int_ >> (ascii::no_case[qi::lit("no")] | ascii::no_case[qi::lit("bad")]), qi::space);
}

Which works. However, when I translated this to the following, the grammar seems to always fail. What I would like to do is in my cpp file:

namespace {
    namespace qi = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;

    struct FailedGrammar : qi::grammar<const char*>
    {
        FailedGrammar() : 
            FailedGrammar::base_type(m_start),
            m_start()
        {
            m_start = -qi::int_ >> (ascii::no_case[qi::lit("no")] | ascii::no_case[qi::lit("bad")]);
        }
        qi::rule<const char*> m_start;
    };

    const FailedGrammar s_failedInstance;
}

and then call like this:

bool loginFailed() 
{
    namespace qi = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;

    // current data is an re2 stringpiece. so .data returns char*
    const char* front = currentData.data();
    const char* back = currentData.end();

    return qi::phrase_parse(front, back, s_failedInstance, qi::space);
}

The string I am trying to recognize is something like this:

0002 NO FAILED LOGIN

where the numbers are optional. I am aware of other ways to do this with re2, however, I am looking for a spirit implementation.

Does anyone have any pointers or potential reasons this fails?

Edit: I found quite a few issues with my grammar just poking around with the debugger. First, I realized to parse a number such as 0002 I should have wrote *(qi::int_). Also, I got rid of the ascii::space in favor of the ascii::blank

joshu
  • 463
  • 8
  • 18
  • "I realized to parse a number such as 0002 I should have wrote *(qi::int_)" - if the number is not optional, either use `qi::uint_` or `+qi::digit` or even `repeat(4)[digit]` – sehe Nov 12 '16 at 01:00
  • Good thinking on the blank. I was almost going to mention it, but I know nothing about your "grammar" and didn't want to complicate – sehe Nov 12 '16 at 01:01

1 Answers1

1

Your first version uses a skipper.

Your second doesn't¹. You could fix it:

struct FailedGrammar : qi::grammar<const char*, qi::space_type>
{
    FailedGrammar() : FailedGrammar::base_type(m_start), m_start()
    {
        m_start = -qi::int_ >> (ascii::no_case[qi::lit("no")] | ascii::no_case[qi::lit("bad")]);
    }
    qi::rule<const char*, qi::space_type> m_start;
};

Note: Using qi::space/qi::lit and ascii::no_case is inconsistent. You can simplify greatly.

Live On Coliru

#include <boost/spirit/include/qi.hpp>
#include <boost/utility/string_ref.hpp>

namespace login_detection {
    using namespace boost::spirit::qi;
    static const rule<const char*> s_failed = skip(space) [ no_case [ -int_ >> lit("no") | "bad" ] ];
}

bool loginFailed2(boost::string_ref response) {
    return parse(response.begin(), response.end(), login_detection::s_failed);
}

int main() {
    return loginFailed2("0002 NO FAILED LOGIN")? 1 : 2;
}

In fact there's little or no reason to have the rule at namespace scope:

bool loginFailed2(boost::string_ref response) {
    using namespace boost::spirit::qi;
    static const rule<const char*> s_failed = skip(space) [ no_case [ -int_ >> lit("no") | "bad" ] ];
    return parse(response.begin(), response.end(), s_failed);
}

I suggest comparing the generated assembly, though, because I think I will be identical to

bool loginFailed2(boost::string_ref response) {
    using namespace boost::spirit::qi;
    return phrase_parse(response.begin(), response.end(), no_case [ -int_ >> lit("no") | "bad" ], space);
}

¹ Boost spirit skipper issues

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