使用boost spirit qi解析器解析枚举

parse enum using boost spirit qi parser

本文关键字:枚举 qi boost spirit 使用      更新时间:2023-10-16

我试图解析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_来"解析enum CATEGORY和type"。

提前感谢你的帮助。

通常有几种方法:

  1. 语义动作方式(ad-hoc)
  2. 自定义方向
  3. qi::symbols way

哪个是最合适的取决于。这三种方法应该同样有效。symbols<>方法似乎是最安全的(不涉及强制类型转换)和灵活的:例如,您可以将其用于变长enum成员,在no_case[]中使用它等。

逐字输入:

  1. 语义动作方式(临时):

    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


<

完整演示/h3>

这恰好是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";
    }
}

我会按照sehe的建议使用qi::符号的方式,但是这样可以提高代码的可读性:

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_;
};