I have a map of string-rule pairs and I would like to create a “joint rule”(rule_t joint_rule;) of them somehow. If I do this this way:
joint_rule = convert_logformat["%h"] >> convert_logformat["%t"];
than the joint rule with the parse_phrase matches the string
std::string entry = "127.0.0.1 [16/Aug/2012:01:50:02 +0000]";
But if I create the joint rule this way:
for (it = convert_logformat.begin(); it != convert_logformat.end(); it++)
{
joint_rule = joint_rule.copy() >> (*it).second.copy();
}
It does not match the same string. Why? How could I achieve something similar to the latter?
Relevant code:
template <typename Iterator>
bool parse_logentry(Iterator first, Iterator last, std::vector<char>& ip, std::vector<char>& timestamp, std::vector<char>& req, unsigned int& status, unsigned int& transferred_bytes, std::vector<char>& referer, std::vector<char>& ua)
{
using boost::spirit::qi::char_;
using boost::spirit::qi::int_;
using boost::spirit::qi::uint_;
using boost::spirit::qi::phrase_parse;
using boost::spirit::ascii::space;
using boost::spirit::ascii::space_type;
using boost::phoenix::ref;
using boost::phoenix::push_back;
using boost::spirit::qi::_1;
using boost::spirit::qi::lexeme;
using boost::spirit::qi::rule;
typedef boost::spirit::qi::rule<Iterator, std::string(), space_type> rule_t;
rule_t ip_rule, timestamp_rule, user_rule, req_rule, ref_rule, ua_rule, bytes_rule, status_rule;
ip_rule %= lexeme[(+char_("0-9."))[ref(ip) = _1]];
timestamp_rule %= lexeme[('[' >> +(~char_(']')) >> ']')[ref(timestamp) = _1]];
user_rule %= lexeme[(+~char_(" "))];
req_rule %= lexeme[('"' >> +(~char_('"')) >> '"')[ref(req) = _1]];
ref_rule %= lexeme[('"' >> +(~char_('"')) >> '"')[ref(referer) = _1]];
ua_rule %= lexeme[('"' >> +(~char_('"')) >> '"')[ref(ua) = _1]];
bytes_rule %= uint_[ref(transferred_bytes) = _1];
status_rule %= uint_[ref(status) = _1];
std::map<std::string, rule_t> convert_logformat;
typename std::map<std::string, rule_t>::iterator it;
convert_logformat.insert(std::pair<std::string, rule_t>("%h", ip_rule));
convert_logformat.insert(std::pair<std::string, rule_t>("%t", timestamp_rule));
//convert_logformat.insert(std::pair<std::string, rule_t>("%r", req_rule));
//convert_logformat.insert(std::pair<std::string, rule_t>("%>s", status_rule));
//convert_logformat.insert(std::pair<std::string, rule_t>("%b", bytes_rule));
//convert_logformat.insert(std::pair<std::string, rule_t>("%u", user_rule));
//convert_logformat.insert(std::pair<std::string, rule_t>("%{User-agent}i", ua_rule));
//convert_logformat.insert(std::pair<std::string, rule_t>("%{Referer}i", ref_rule));
rule_t joint_rule;
//joint_rule = convert_logformat["%h"] >> convert_logformat["%t"];
for (it = convert_logformat.begin(); it != convert_logformat.end(); it++)
{
joint_rule = joint_rule.copy() >> (*it).second.copy();
std::cout << (*it).first << ": " << typeid((*it).second).name() << "\n";
}
std::cout << "convert_logformath: " << typeid(convert_logformat["%h"]).name() << "\n";
bool r = phrase_parse(first, last, joint_rule, space);
if (first != last)
return false;
return r;
}
Ahem. It is really quite simple. You should initialize your variables 🙂
Change the first line to
And it works:
Your parser lacks some (good) common practice. See below for tidied up source (C++11).
See the code live at http://liveworkspace.org/code/a7f2f94840d63fce43d8c3f56236330e
Note that, among other things, this setup allows you to enable debugging of your grammar by simply defining BOOST_SPIRIT_DEBUG at the start.