Boost logo

Boost Users :

Subject: [Boost-users] [Boost-use​rs][Spirit​] Semantic Action for a map of a map
From: Ryan (boost_at_[hidden])
Date: 2014-12-01 10:03:52


I posted this question to the Boost Spirit mailing list but there doesn't
seem to be much traffic there. I'm hoping that the main mailing list also
has a few Spirit users as well and can possible receive some help.

I'm wanting to add a semantic action for a complex rule. I'm not entirely
sure this is the correct design. I need to parse the following EBNF
grammar.

EBNF
value = (a-zA-Z_0-9)*
tag = (a-zA-Z_) value
tagValue = tag '=' value
list = "@list=" tag '=' tag ',' (tagValue ',')* "@end"
info = tag (',' (tagValue | list) )*

I thought if I defined the basic rules I could then created a custom
instance of that rule with a semantic action. I did that for the first
"tag" in the info definition and it seem to work well. I was also able to
create this custom instance for the "tagValue" rule within the info
definition. The problem is how to create the custom instance for list.
The list result goes into a map of a map that I've created within my
class. I'm not sure how to treat the tag=tag as the key and let the
tagValue be the value.

Any help with the semantic action or changing the design would be
appreciated. This is my first time using Spirit and the learning curve is
proving to be difficult.

Ryan

#include <string>
#include <map>
#include <utility>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/std_pair.hpp>

class Bob {
  typedef std::pair<std::string, std::string> infoPair;
  typedef std::map<std::string, std::string> infoMap
  typedef std::map<std::string, infoMap> infoMap2;

  std::string m_Name;
  infoMap m_Arguments;
  infoMap2 m_ArgumentCollection;

public:
  bool const parseMessage(std::string const& message) {
    namespace qi = boost::spirit::qi;
    namespace phx = boost::phoenix;

    typedef std::string::const_iterator iter;

    qi::rule<iter, std::string()> value = *qi::char_("a-zA-Z_0-9");
    qi::rule<iter, std::string()> tag = qi::char_("a-zA-Z_") >> value;
    qi::rule<iter, infoPair()> tagValue = tag >> '=' >> value;

    qi::rule<iter, std::string()> name = tag[ phx::bind(&Bob::m_Name, this)
= qi::_1 ];
    qi::rule<iter, infoMap()> tagValueMap = tagValue[
      [=](infoPair& p, qi::unused_type, qi::unused_type) {
        m_Arguments[p.first] = p.second; } ];

    //The compiler wants an infoPair. Not really sure that is what I need.
    qi::rule<iter, infoPair()> list = "@list=" >> tag >> '=' >> tag >> ','
>> *(tagValue >> ',') >> "@end";

    //How can I create a custom version of list (like I did for tag) to
assign a semantic action.
    //The action needs to allow tag=tag be the key to m_ArgumentCollection
and place all
    //the repeating tagValue items into its value.

    auto first(message.begin()), last(message.end());

    bool results = qi::parse(first, last, name >> (*(',' >> tagValueMap)));

    //Something like this as the final form to the grammar.
    //bool results = qi::parse(first, last, name >> (*(',' >> (tagValueMap
| customList))));

    if (!results || first != last)
      return false;

    return true;
  }
};

int main(void) {
  Bob bob;

bob.parseMessage("hello,this=great,@list=alice=here,wish=fish,@end,look=good");

  return 0;
}



Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net