2

I have this example code, which parses the string str correctly. How to I make it work if there any extra characters before and/or after the string? For example if I did str = std::string("AAA") + str + std::string("AAA")

frame.h

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

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

namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
namespace ascii = boost::spirit::ascii;

struct frame
{
  std::string addr;
  std::string func;
  std::string file;
  std::string fullname;
  std::string line;

  std::map<std::string, std::string> kv;
};

template <typename Iterator>
struct argsArray : qi::grammar<Iterator, std::map<std::string, std::string>()>
{
  argsArray() : argsArray::base_type(query)
  {
    query =
        qi::lit("args=[") >> pair >> *(qi::lit(',') >> pair) >> qi::lit(']');
    pair = qi::lit("{name=") >> quoted_string >> qi::lit(",value=") >>
           quoted_string >> qi::lit("}");
    key = qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9");
    quoted_string %= boost::spirit::lexeme['"' >> +(qi::char_ - '"') >> '"'];
  }
  qi::rule<Iterator, std::map<std::string, std::string>()> query;
  qi::rule<Iterator, std::pair<std::string, std::string>()> pair;
  qi::rule<Iterator, std::string()> key;
  qi::rule<Iterator, std::string()> quoted_string;
};

template <typename Iterator>
struct frameParser : qi::grammar<Iterator, frame(), ascii::space_type>
{
  frameParser() : frameParser::base_type(frame_rule)
  {
    static const auto _addr = phx::bind(&frame::addr, qi::_r1);
    static const auto _func = phx::bind(&frame::func, qi::_r1);
    static const auto _file = phx::bind(&frame::file, qi::_r1);
    static const auto _fullname = phx::bind(&frame::fullname, qi::_r1);
    static const auto _line = phx::bind(&frame::line, qi::_r1);
    static const auto _kv = phx::bind(&frame::kv, qi::_r1);

    func = qi::lit("func=") >> quoted_string;
    addr = qi::lit("addr=") >> quoted_string;
    file = qi::lit("file=") >> quoted_string;
    fullname = qi::lit("fullname=") >> quoted_string;
    line = qi::lit("line=") >> quoted_string;
    func_rule = func[_func = qi::_1];
    addr_rule = addr[_addr = qi::_1];
    file_rule = file[_file = qi::_1];
    fullname_rule = fullname[_fullname = qi::_1];
    line_rule = line[_line = qi::_1];

    kv_rule = arrTest[_kv = qi::_1];
    quoted_string %= boost::spirit::lexeme['"' >> +(qi::char_ - '"') >> '"'];

    frame_rule = qi::lit("frame={") >>
                 (addr_rule(qi::_val) ^ qi::lit(',') ^ func_rule(qi::_val) ^
                  qi::lit(',') ^ file_rule(qi::_val) ^ qi::lit(',') ^
                  fullname_rule(qi::_val) ^ qi::lit(',') ^ line_rule(qi::_val) ^
                  qi::lit(',') ^ kv_rule(qi::_val)) >>
                 qi::lit('}');

    BOOST_SPIRIT_DEBUG_NODES(
        (frame_rule)(func_rule)(addr_rule)(fullname_rule)(line_rule))
  }

  qi::rule<Iterator, void(frame&), ascii::space_type> func_rule, addr_rule,
      file_rule, fullname_rule, line_rule, kv_rule;
  qi::rule<Iterator, frame(), ascii::space_type> frame_rule;
  qi::rule<Iterator, std::string()> addr, func, file, fullname, line;
  qi::rule<Iterator, std::string()> quoted_string;
  argsArray<Iterator> arrTest;
};

test.cc

#include <iostream>
#include "gtest/gtest.h"
#include "parser/frame.h"

TEST(ParseFrameString, Test1)
{
  std::string str = R"(frame={addr="0x0000000000414008",)"
                    R"(func="main",)"
                    R"(args=[{name="argc",value="1"},)"
                    R"({name="argv",value="0x7fffffffe1a8"}],)"
                    R"(file="/home/stiopa/development/gdbFront/main.cc",)"
                    R"(fullname="/home/stiopa/development/gdbFront/main.cc",)"
                    R"(line="90"}")";

  typedef std::string::const_iterator It;
  const frameParser<It> g;
  It iter(str.begin()), end(str.end());
  frame frame;
  bool r = phrase_parse(iter, end, g, boost::spirit::ascii::space, frame);

  EXPECT_EQ(r, true);
  EXPECT_EQ(frame.addr, "0x0000000000414008");
  EXPECT_EQ(frame.func, "main");

  std::map<std::string, std::string> kv{{"argc", "1"},
                                        {"argv", "0x7fffffffe1a8"}};
  EXPECT_EQ(frame.kv, kv);

  EXPECT_EQ(frame.file, "/home/stiopa/development/gdbFront/main.cc");
  EXPECT_EQ(frame.fullname, "/home/stiopa/development/gdbFront/main.cc");
  EXPECT_EQ(frame.line, "90");
}
stiopa
  • 137
  • 9
  • Please don't include unnecessary external references (google-test, in this instance). Making the code self contained makes it possible for anyone to compile and answer. – sehe Apr 13 '17 at 01:13

2 Answers2

2

The simple, low-tech solution would be to use qi::seek from the repository:

#include <boost/spirit/repository/include/qi_seek.hpp>
namespace qir = boost::spirit::repository::qi;

And then:

bool r = phrase_parse(iter, end, qir::seek[g], boost::spirit::ascii::space, frame);

DEMO

Live On Coliru

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

#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/spirit/repository/include/qi_seek.hpp>

namespace qi = boost::spirit::qi;
namespace qir = boost::spirit::repository::qi;
namespace phx = boost::phoenix;
namespace ascii = boost::spirit::ascii;

struct frame
{
    std::string addr;
    std::string func;
    std::string file;
    std::string fullname;
    std::string line;

    std::map<std::string, std::string> kv;
};

template <typename Iterator>
struct argsArray : qi::grammar<Iterator, std::map<std::string, std::string>()>
{
    argsArray() : argsArray::base_type(query)
    {
        query =
            qi::lit("args=[") >> pair >> *(qi::lit(',') >> pair) >> qi::lit(']');
        pair = qi::lit("{name=") >> quoted_string >> qi::lit(",value=") >>
            quoted_string >> qi::lit("}");
        key = qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9");
        quoted_string %= boost::spirit::lexeme['"' >> +(qi::char_ - '"') >> '"'];
    }
    qi::rule<Iterator, std::map<std::string, std::string>()> query;
    qi::rule<Iterator, std::pair<std::string, std::string>()> pair;
    qi::rule<Iterator, std::string()> key;
    qi::rule<Iterator, std::string()> quoted_string;
};

template <typename Iterator>
struct frameParser : qi::grammar<Iterator, frame(), ascii::space_type>
{
    frameParser() : frameParser::base_type(frame_rule)
    {
        static const auto _addr = phx::bind(&frame::addr, qi::_r1);
        static const auto _func = phx::bind(&frame::func, qi::_r1);
        static const auto _file = phx::bind(&frame::file, qi::_r1);
        static const auto _fullname = phx::bind(&frame::fullname, qi::_r1);
        static const auto _line = phx::bind(&frame::line, qi::_r1);
        static const auto _kv = phx::bind(&frame::kv, qi::_r1);

        func = qi::lit("func=") >> quoted_string;
        addr = qi::lit("addr=") >> quoted_string;
        file = qi::lit("file=") >> quoted_string;
        fullname = qi::lit("fullname=") >> quoted_string;
        line = qi::lit("line=") >> quoted_string;
        func_rule = func[_func = qi::_1];
        addr_rule = addr[_addr = qi::_1];
        file_rule = file[_file = qi::_1];
        fullname_rule = fullname[_fullname = qi::_1];
        line_rule = line[_line = qi::_1];

        kv_rule = arrTest[_kv = qi::_1];
        quoted_string %= boost::spirit::lexeme['"' >> +(qi::char_ - '"') >> '"'];

        frame_rule = qi::lit("frame={") >>
            (addr_rule(qi::_val) ^ qi::lit(',') ^ func_rule(qi::_val) ^
             qi::lit(',') ^ file_rule(qi::_val) ^ qi::lit(',') ^
             fullname_rule(qi::_val) ^ qi::lit(',') ^ line_rule(qi::_val) ^
             qi::lit(',') ^ kv_rule(qi::_val)) >>
            qi::lit('}');

        BOOST_SPIRIT_DEBUG_NODES(
                (frame_rule)(func_rule)(addr_rule)(fullname_rule)(line_rule))
    }

    qi::rule<Iterator, void(frame&), ascii::space_type> func_rule, addr_rule,
        file_rule, fullname_rule, line_rule, kv_rule;
    qi::rule<Iterator, frame(), ascii::space_type> frame_rule;
    qi::rule<Iterator, std::string()> addr, func, file, fullname, line;
    qi::rule<Iterator, std::string()> quoted_string;
    argsArray<Iterator> arrTest;
};


#include <iostream>
//#include "parser/frame.h"

int main()
{
    std::string str = R"(frame={addr="0x0000000000414008",)"
        R"(func="main",)"
        R"(args=[{name="argc",value="1"},)"
        R"({name="argv",value="0x7fffffffe1a8"}],)"
        R"(file="/home/stiopa/development/gdbFront/main.cc",)"
        R"(fullname="/home/stiopa/development/gdbFront/main.cc",)"
        R"(line="90"}")";
    str = "AAA" + str + "AAA";

    typedef std::string::const_iterator It;
    const frameParser<It> g;
    It iter(str.begin()), end(str.end());
    frame frame;
    bool r = phrase_parse(iter, end, qir::seek[g], boost::spirit::ascii::space, frame);

    assert(r == true);
    assert(frame.addr == "0x0000000000414008");
    assert(frame.func == "main");

    std::map<std::string, std::string> kv{{"argc", "1"},
        {"argv", "0x7fffffffe1a8"}};

    assert(frame.kv == kv);

    assert(frame.file == "/home/stiopa/development/gdbFront/main.cc");
    assert(frame.fullname == "/home/stiopa/development/gdbFront/main.cc");
    assert(frame.line == "90");
}

Tests still pass.

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

And here's a free code review. Please see

Live On Coliru

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

#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/repository/include/qi_seek.hpp>

namespace qi    = boost::spirit::qi;
namespace qir   = boost::spirit::repository::qi;
namespace phx   = boost::phoenix;
namespace ascii = boost::spirit::ascii;

struct frame {
    std::string addr;
    std::string func;
    std::string file;
    std::string fullname;
    std::string line;

    std::map<std::string, std::string> kv;
};

BOOST_FUSION_ADAPT_STRUCT(frame, addr, func, file, fullname, line, kv)

template <typename Iterator>
struct argsArray : qi::grammar<Iterator, std::map<std::string, std::string>()>
{
    argsArray() : argsArray::base_type(query)
    {
        query = "args=[" >> pair >> *(',' >> pair) >> ']';
        pair  = "{name=" >> quoted_string >> ",value=" >> quoted_string >> "}";
        key   = qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9");
        quoted_string = '"' >> +(qi::char_ - '"') >> '"';
    }
  private:
    qi::rule<Iterator, std::map<std::string, std::string>()> query;
    qi::rule<Iterator, std::pair<std::string, std::string>()> pair;
    qi::rule<Iterator, std::string()> key;
    qi::rule<Iterator, std::string()> quoted_string;
};

template <typename Iterator>
struct frameParser : qi::grammar<Iterator, frame(), ascii::space_type>
{
    frameParser() : frameParser::base_type(frame_rule)
    {
        quoted_string = '"' >> +(qi::char_ - '"') >> '"';
        delim         = (&qi::lit('}')) | ',';
        field_rule    = qi::lexeme [ qi::lit(qi::_r1) >> '=' ] >> quoted_string >> delim;
        kv_rule       = arrTest >> delim;

        frame_rule = "frame={" >>
            (field_rule(+"addr") ^ 
             field_rule(+"func") ^
             field_rule(+"file") ^
             field_rule(+"fullname") ^
             field_rule(+"line") ^
             kv_rule
            ) >> '}';

        BOOST_SPIRIT_DEBUG_NODES((frame_rule)(field_rule))
    }
  private:
    qi::rule<Iterator> delim;
    qi::rule<Iterator, std::string(char const*), ascii::space_type> field_rule;
    qi::rule<Iterator, std::map<std::string, std::string>()> kv_rule;
    qi::rule<Iterator, frame(), ascii::space_type> frame_rule;
    qi::rule<Iterator, std::string()> quoted_string;
    argsArray<Iterator> arrTest;
};

#include <iostream>
//#include "parser/frame.h"

int main()
{
    std::string str = R"(frame={addr="0x0000000000414008",)"
        R"(func="main",)"
        R"(args=[{name="argc",value="1"},)"
        R"({name="argv",value="0x7fffffffe1a8"}],)"
        R"(file="/home/stiopa/development/gdbFront/main.cc",)"
        R"(fullname="/home/stiopa/development/gdbFront/main.cc",)"
        R"(line="90"}")";
    str = "AAA" + str + "AAA";

    typedef std::string::const_iterator It;
    const frameParser<It> g;
    It iter(str.begin()), end(str.end());
    frame frame;
    bool r = phrase_parse(iter, end, qir::seek[g], boost::spirit::ascii::space, frame);

    if (iter != end)
        std::cout << "Remaining unparsed: '" << std::string(iter,end) << "'\n";

    assert(r == true);
    assert(frame.addr == "0x0000000000414008");
    assert(frame.func == "main");

    std::map<std::string, std::string> kv{{"argc", "1"},
        {"argv", "0x7fffffffe1a8"}};

    assert(frame.kv == kv);

    assert(frame.file == "/home/stiopa/development/gdbFront/main.cc");
    assert(frame.fullname == "/home/stiopa/development/gdbFront/main.cc");
    assert(frame.line == "90");
}

Prints:

Remaining unparsed: '"AAA'

Tests still pass.

Note your original sample input had a trailing ", which you simply ignored.

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
  • 1
    I saw you doing code reviews in different threads and was quietly hoping that you would do one here as well. Very much appreciated! This looks so much better than the code I posted... The only problem I had was with BOOST _FUSION_ADAPT, which I think might have been due to boost version I'm using(1.55). I had to modify it to: using stringMap = std::map; BOOST_FUSION_ADAPT_STRUCT(frame, (std::string, addr) (std::string, func) (std::string, file) (std::string, fullname) (std::string, line) (stringMap, kv)) to make it compile. – stiopa Apr 14 '17 at 19:59
  • Yes, that's the old way of specifying types when Boost ResultOf cannot use decltype (BOOST_RESULT_OF_USE_DECLTYPE could help if your compiler is capable, but some libraries had inconsistent support; try BOOST_RESULT_OF_USE_DECLTYPE_WITH_TR1_FALLBACK or so (quoting from memory)) – sehe Apr 14 '17 at 21:36