Search code examples
c++boostboost-spirit

Ignore all characters up to a given word


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");
}

Solution

  • 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.