代码之家  ›  专栏  ›  技术社区  ›  Sanjit

提升灵气从子规则分配值

  •  1
  • Sanjit  · 技术社区  · 10 年前

    我试图解析两种不同类型的字符串,并将值分配到结构中。为了提高性能,我尝试使用boost spirit子规则。

    字符串可以是以下类型

       Animal Type | Animal Attributes 
    
    Ex
       DOG | Name=tim | Barks=Yes | Has a Tail=N | Address=3 infinite loop
       BIRD| Name=poc | Tweets=Yes| Address=10 stack overflow street
    

    这些值存储在下面的Dog和Bird结构数组中

    #define BOOST_SPIRIT_USE_PHOENIX_V3
    #include <boost/config/warning_disable.hpp>
    #include <boost/spirit/include/qi.hpp> 
    #include <boost/spirit/repository/include/qi_subrule.hpp>  
    #include <boost/spirit/include/qi_symbols.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    #include <string> 
    #include <iostream> 
    
    using std::cout; 
    using std::endl; 
    using std::cerr; 
    
    struct Dog 
    {
       std::string Name; 
       bool Barks;
       bool HasATail; 
       std::string Address; 
    }; 
    
    struct Bird
    {
        std::string Name; 
        bool Tweets; 
        std::string Address; 
    };
    
    namespace qi = boost::spirit::qi;
    namespace repo = boost::spirit::repository;
    namespace ascii = boost::spirit::ascii;
    namespace phx = boost::phoenix; 
    
    template <typename Iterator>
    struct ZooGrammar : public qi::grammar<Iterator, ascii::space_type>
    {
         ZooGrammar() : ZooGrammar::base_type(start_)
         {
    
            using qi::char_; 
            using qi::lit_; 
            using qi::_1;
            using boost::phoenix::ref; 
    
            boost::spirit::qi::symbols<char, bool> yesno_; 
            yesno_.add("Y", true)("N", false); 
    
             start_ = (
                 dog_ | bird_, 
                 dog_ = "DOG" >> lit_[ref(d.Name) = _1]>> '|'
                         >>"Barks=">>yesno_[ref(d.Barks) = _1] >>'|'
                         >>"Has a Tail=">>yesno_[ref(d.HasATail) = _1] >> '|'
                         >>lit_[ref(d.Address) = _1]
                 , 
                 bird_ = "BIRD" >> lit_[ref(b.Name) = _1]>> '|'
                         >>"Tweets=">>yesno_[ref(b.Tweets) = _1] >>'|'
                         >>lit_[ref(b.Address) = _1]
             );
         } 
    
        qi::rule<Iterator, ascii::space_type> start_; 
        repo::qi::subrule<0> dog_;
        repo::qi::subrule<1> bird_; 
        Bird b;
        Dog d; 
    }; 
    
    
    int main()
    {
        std::string test1="DOG | Name=tim | Barks=Yes | Has a Tail=N | Address=3 infinite loop"; 
        std::string test2="BIRD| Name=poc | Tweets=Yes| Address=10 stack overflow street"; 
        using boost::spirit::ascii::space;
        typedef std::string::const_iterator iterator_type;
        typedef ZooGrammar<iterator_type> grammar;
        iterator_type start = test1.begin(); 
        iterator_type end   = test1.end();
        ZooGrammar g; 
        if(boost::spirit::qi::phrase_parse(start, end, g, space))
        {
            cout<<"matched"<<endl; 
        }
    }
    

    上面的代码使编译器GCC 4.8和4.9崩溃。我不知道我在哪里犯错。

    请在Coliru中测试运行上述代码 link

    提前感谢!

    1 回复  |  直到 10 年前
        1
  •  2
  •   sehe    10 年前

    子规则有点过时。老实说,我甚至不知道《精灵V2》中还有这样的东西。

    我建议使用常规的Spirit V2属性传播,这使内容一下子变得更可读:

    dog_ = qi::lit("DOG") >> '|' >> "Name=" >> lit_   >> '|'
                          >> "Barks="       >> yesno_ >> '|'
                          >> "Has a Tail="  >> yesno_ >> '|'
                          >> "Address="     >> lit_
         ;
    
    bird_ = qi::lit("BIRD") >> '|' >> "Name=" >> lit_   >> '|'
                            >> "Tweets="      >> yesno_ >> '|'
                            >> "Address="     >> lit_
          ;
    
    start_ = dog_ | bird_;
    

    我想象过 lit_ 规则(作为 qi::lit_ 不会响起任何铃声):

    lit_   = qi::lexeme [ *~qi::char_('|') ];
    

    当然,您需要调整属性类型,只要它们没有内置支持(如 boost::variant<Dog, Bird> , std::string bool 它们都是在没有任何附加代码的情况下处理的):

    BOOST_FUSION_ADAPT_STRUCT(Dog, 
        (std::string, Name)(bool, Barks)(bool, HasATail)(std::string, Address))
    BOOST_FUSION_ADAPT_STRUCT(Bird, 
        (std::string, Name)(bool, Tweets)(std::string, Address))
    

    现在,程序扩展到打印一些调试信息,输出为: Live On Coliru

    Matched: [DOG|Name=tim |Barks=Yes|Has a Tail=No|Address=3 infinite loop]
    Matched: [BIRD|Name=poc |Tweets=Yes|Address=10 stack overflow street]
    

    完整样本代码

    //#define BOOST_SPIRIT_DEBUG
    #define BOOST_SPIRIT_USE_PHOENIX_V3
    #include <boost/fusion/adapted/struct.hpp>
    #include <boost/spirit/include/qi.hpp> 
    #include <boost/spirit/include/qi_symbols.hpp>
    
    static const char* YesNo(bool b) { return b?"Yes":"No"; }
    
    struct Dog {
        std::string Name;
        bool        Barks;
        bool        HasATail;
        std::string Address;
    
        friend std::ostream& operator <<(std::ostream& os, Dog const& o) {
            return os << "[DOG|Name=" << o.Name << "|Barks=" << YesNo(o.Barks) << "|Has a Tail=" << YesNo(o.HasATail) << "|Address=" << o.Address << "]";
        }
    }; 
    
    struct Bird {
        std::string Name;
        bool        Tweets;
        std::string Address;
    
        friend std::ostream& operator <<(std::ostream& os, Bird const& o) {
            return os << "[BIRD|Name=" << o.Name << "|Tweets=" << YesNo(o.Tweets) << "|Address=" << o.Address << "]";
        }
    };
    
    typedef boost::variant<Dog, Bird> ZooAnimal;
    
    BOOST_FUSION_ADAPT_STRUCT(Dog, (std::string, Name)(bool, Barks)(bool, HasATail)(std::string, Address))
    BOOST_FUSION_ADAPT_STRUCT(Bird, (std::string, Name)(bool, Tweets)(std::string, Address))
    
    namespace qi    = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;
    
    template <typename Iterator>
    struct ZooGrammar : public qi::grammar<Iterator, ZooAnimal(), ascii::space_type>
    {
        ZooGrammar() : ZooGrammar::base_type(start_)
        {
            using qi::_1;
    
            yesno_.add("Yes", true)("Y", true)("No", false)("N", false); 
    
            dog_ = qi::lit("DOG") >> '|' >> "Name=" >> lit_   >> '|'
                                  >> "Barks="       >> yesno_ >> '|'
                                  >> "Has a Tail="  >> yesno_ >> '|'
                                  >> "Address="     >> lit_
                 ;
    
            bird_ = qi::lit("BIRD") >> '|' >> "Name=" >> lit_   >> '|'
                                    >> "Tweets="      >> yesno_ >> '|'
                                    >> "Address="     >> lit_
                  ;
    
            start_ = dog_ | bird_;
    
            lit_   = qi::lexeme [ *~qi::char_('|') ];
    
            BOOST_SPIRIT_DEBUG_NODES((dog_)(bird_)(start_)(lit_))
        } 
    
      private:
        qi::rule<Iterator, ZooAnimal(),   ascii::space_type> start_;
        qi::rule<Iterator, std::string(), ascii::space_type> lit_;
        qi::rule<Iterator, Dog(),         ascii::space_type> dog_;
        qi::rule<Iterator, Bird(),        ascii::space_type> bird_;
        qi::symbols<char, bool> yesno_; 
    }; 
    
    int main()
    {
        typedef std::string::const_iterator iterator_type;
        typedef ZooGrammar<iterator_type> grammar;
    
        for (std::string const input : { 
                "DOG | Name=tim | Barks=Yes | Has a Tail=N | Address=3 infinite loop",
                "BIRD| Name=poc | Tweets=Yes| Address=10 stack overflow street"
                })
        {
            iterator_type start = input.begin(); 
            iterator_type end   = input.end();
            grammar g; 
    
            ZooAnimal animal;
            if(qi::phrase_parse(start, end, g, ascii::space, animal))
                std::cout << "Matched: " << animal << "\n";
            else
                std::cout << "Parse failed\n";
    
            if (start != end)
                std::cout << "Remaining input: '" << std::string(start, end) << "'\n";
        }
    }