使用boost spirit qi解析器解析enum

时间:2014-08-07 06:31:53

标签: c++ c++11 boost enums boost-spirit-qi

我正在尝试解析char以填写C ++ 11强类型枚举。我需要帮助为枚举编写解析器..它也需要高性能。

我有一个格式如下的字符串

Category | Type | Attributes 

示例:

std::string str1 = "A|D|name=tim, address=3 infinite loop"
std::string str2 = "A|C|name=poc, address=5 overflow street" 

我代表类别和类型如下:

 enum class CATEGORY : char 
 {
     Animal:'A', Bird:'B'
 } 

 enum class TYPE : char 
 {
     Dog:'D', Bird:'B'
 } 

 struct Zoo
 {
      Category category; 
      Type     type; 
      std::string name;
      std::string address;
 }; 

namespace qi = boost::spirit::qi;
namespace repo = boost::spirit::repository;
namespace ascii = boost::spirit::ascii;
template <typename Iterator>
struct ZooBuilderGrammar :  qi::grammar<Iterator, ascii::space_type>
{
 ZooBuilderGrammar():ZooBuilderGrammar::base_type(start_)
 {
    using qi::char_; 
    using qi::_1;
    using qi::lit 
    using boost::phoenix::ref; 

    //need help here 
    start_=char_[/*how to assign enum */ ]>>'|'
         >>char_[ /*how to assign enum */ ]>>'|'
         >>lit;
 } 
 qi::rule<Iterator, ascii::space_type> start_;
};   

我在创建解析器类型时遇到问题,例如内置的ex:qi :: char_到“解析枚举的CATEGORY和TYPE”。

感谢您的帮助..

2 个答案:

答案 0 :(得分:7)

像往常一样,有几种方法:

  1. 语义行为方式(ad-hoc)
  2. 自定义点方式
  3. qi :: symbols方式
  4. 哪个最合适取决于。这三种方法应该同样有效。 symbols<> apprach似乎最安全(不涉及演员表)和灵活:你可以例如将它与变长枚举成员一起使用,在no_case[]等内部使用

    逐案:

    1. 语义行为方式(ad-hoc)

      template <typename Iterator>
      struct ZooBuilderGrammar :  qi::grammar<Iterator, ascii::space_type>
      {
          ZooBuilderGrammar():ZooBuilderGrammar::base_type(start_)
          {
              using namespace qi;
      
              category_ = char_("AB") [ _val = phx::static_cast_<Category>(_1) ];
              type_     = char_("DB") [ _val = phx::static_cast_<Type>(_1) ];
              start_    = category_ >> '|' > type_;
          } 
        private:
          qi::rule<Iterator, Category(),        ascii::space_type> category_;
          qi::rule<Iterator, Type(),            ascii::space_type> type_;
          qi::rule<Iterator, ascii::space_type> start_;
      };   
      

      您可以看到 Live On Coliru 打印:

      Parse success: [A, D]
      Remaining unparsed input '|name=tim, address=3 infinite loop'
      ---------------------------
      expected: tag: char-set
      got: "C|name=poc, address=5 overflow street"
      Expectation failure: boost::spirit::qi::expectation_failure at 'C|name=poc, address=5 overflow street'
      ---------------------------
      

    2. 自定义点方式

      namespace boost { namespace spirit { namespace traits {
          template <typename Enum, typename RawValue> 
          struct assign_to_attribute_from_value<Enum, RawValue, typename enable_if<is_enum<Enum>>::type> {
              static void call(RawValue const& raw, Enum& cat) {
                  cat = static_cast<Enum>(raw);
              }
          };
      }}}
      
      template <typename Iterator>
      struct ZooBuilderGrammar :  qi::grammar<Iterator, Zoo(), ascii::space_type>
      {
          ZooBuilderGrammar():ZooBuilderGrammar::base_type(start_)
          {
              start_ = qi::char_("AB") > '|' > qi::char_("DB");
          } 
      private:
          qi::rule<Iterator, Zoo(), ascii::space_type> start_;
      };   
      

      同样看到 Live On Coliru ,输出相同(显然)


    3. qi::symbols方式

      template <typename Iterator>
      struct ZooBuilderGrammar :  qi::grammar<Iterator, Zoo(), ascii::space_type>
      {
          ZooBuilderGrammar():ZooBuilderGrammar::base_type(start_)
          {
              start_ = category_ > '|' > type_;
          } 
      private:
          struct Category_ : qi::symbols<char,Category> {
              Category_() {
                  this->add("A", Category::Animal)("B", Category::Bird);
              }
          } category_;
          struct Type_ : qi::symbols<char,Type> {
              Type_() {
                  this->add("D", Type::Dog)("B", Type::Bird);
              }
          } type_;
          qi::rule<Iterator, Zoo(), ascii::space_type> start_;
      };   
      

      查看 Live On Coliru


    4. 完整演示

      这恰好是traits方法,但您可以将骨架重用于其他语法:

      #include <boost/spirit/include/qi.hpp>
      #include <boost/spirit/include/phoenix.hpp>
      #include <boost/fusion/adapted/struct.hpp>
      
      enum class Category : char { Animal='A', Bird='B' };
      enum class Type     : char { Dog='D',    Bird='B' };
      
      struct Zoo {
          Category category;
          Type     type;
      }; 
      
      BOOST_FUSION_ADAPT_STRUCT(Zoo, (Category,category)(Type,type))
      
      namespace qi    = boost::spirit::qi;
      namespace ascii = boost::spirit::ascii;
      namespace phx   = boost::phoenix;
      
      namespace boost { namespace spirit { namespace traits {
          template <typename Enum, typename RawValue> 
          struct assign_to_attribute_from_value<Enum, RawValue, typename enable_if<is_enum<Enum>>::type> {
              static void call(RawValue const& raw, Enum& cat) {
                  cat = static_cast<Enum>(raw);
              }
          };
      }}}
      
      template <typename Iterator>
      struct ZooBuilderGrammar :  qi::grammar<Iterator, Zoo(), ascii::space_type>
      {
          ZooBuilderGrammar():ZooBuilderGrammar::base_type(start_)
          {
              start_ = qi::char_("AB") > '|' > qi::char_("DB");
          } 
      private:
          qi::rule<Iterator, Zoo(), ascii::space_type> start_;
      };   
      
      /////////////////////////////////////////////////
      // For exception output
      struct printer {
          typedef boost::spirit::utf8_string string;
      
          void element(string const& tag, string const& value, int depth) const {
              for (int i = 0; i < (depth*4); ++i) std::cout << ' '; // indent to depth
      
              std::cout << "tag: " << tag;
              if (value != "") std::cout << ", value: " << value;
              std::cout << std::endl;
          }
      };
      
      void print_info(boost::spirit::info const& what) {
          using boost::spirit::basic_info_walker;
      
          printer pr;
          basic_info_walker<printer> walker(pr, what.tag, 0);
          boost::apply_visitor(walker, what.value);
      }
      //
      /////////////////////////////////////////////////
      
      int main()
      {
          typedef std::string::const_iterator It;
          static const ZooBuilderGrammar<It> p;
      
          for (std::string const str1 : { 
                  "A|D|name=tim, address=3 infinite loop",
                  "A|C|name=poc, address=5 overflow street" })
          {
              It f(str1.begin()), l(str1.end());
      
              try {
                  Zoo zoo;
                  bool ok = qi::phrase_parse(f,l,p,ascii::space,zoo);
      
                  if (ok)
                      std::cout << "Parse success: [" << static_cast<char>(zoo.category) << ", " << static_cast<char>(zoo.type) << "]\n";
                  else
                      std::cout << "Failed to parse '" << str1 << "'\n";
      
                  if (f!=l)
                      std::cout << "Remaining unparsed input '" << std::string(f,l) << "'\n";
              } catch(qi::expectation_failure<It> const& x)
              {
                  std::cout << "expected: "; print_info(x.what_);
                  std::cout << "got: \"" << std::string(x.first, x.last) << '"' << std::endl;
              }
              std::cout << "---------------------------\n";
          }
      }
      

答案 1 :(得分:4)

我使用qi :: symbols方式作为sehe的sugested,但这样可以提高代码的可读性:

template <typename Iterator>
struct ZooBuilderGrammar :  qi::grammar<Iterator, Zoo(), ascii::space_type>
{
    ZooBuilderGrammar():ZooBuilderGrammar::base_type(start_)
    {
        category_.add
            ("A", Category::Animal)
            ("B", Category::Bird)
            ;
        type_.add
            ("D", Type::Dog)
            ("B", Type::Bird)
            ;
        start_ = category_ > '|' > type_;
    } 
private:
    qi::symbols<char,Type> category_;
    qi::symbols<char,Category> type_;
    qi::rule<Iterator, Zoo(), ascii::space_type> start_;
};