2

我下面的代码成功地将字符串解析"-5.24 * [HelloWorld : w]"为 structsummand但我需要可以将例如字符串解析"-5.24 * [HelloWorld : w] -7 * [HelloWorld : b] -8.24 * [HelloWorld : h]"vector<summand>. 所以我的问题是如何做到这一点?

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>

#include <iostream>
#include <string>
#include <vector>

enum RectProperty {
    RectPropertyNone                = 0,
    RectPropertyLeft                = 1<<0,
    RectPropertyRight               = 1<<1,
    RectPropertyCentreX             = 1<<2,
    RectPropertyWidth               = 1<<3,
    RectPropertyTop                 = 1<<4,
    RectPropertyBottom              = 1<<5,
    RectPropertyCentreY             = 1<<6,
    RectPropertyHeight               = 1<<7
};

namespace client
{
    namespace spirit = boost::spirit;
    namespace qi = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;
    namespace phoenix = boost::phoenix;

    struct rectProperties_ : qi::symbols<char, unsigned>
    {
        rectProperties_()
        {
        add
        ("cx"   , RectPropertyCentreX)
        ("cy"   , RectPropertyCentreY)
        ("w"    , RectPropertyWidth)
        ("h"    , RectPropertyHeight)
        ("l"    , RectPropertyLeft)
        ("r"    , RectPropertyRight)
        ("t"    , RectPropertyTop)
        ("b"    , RectPropertyBottom)
        ;
        }

    } rectProperties;

    struct summand
    {
        float factor;
        std::string nodeName;
        RectProperty property;
    };

    std::vector<summand> summands;

    void addToVector(summand const& sum)
    {
        summands.push_back(sum);
    }
}

BOOST_FUSION_ADAPT_STRUCT(client::summand,
                      (float, factor)
                      (std::string, nodeName)
                      (RectProperty, property)
                      );

namespace client {

template <typename Iterator>
struct summand_parser : qi::grammar<Iterator, summand(), ascii::space_type>
{
    summand_parser() : summand_parser::base_type(start)
    {
        using spirit::float_;
        using ascii::no_case;
        using ascii::alpha;
        using ascii::alnum;

        using qi::lexeme;
        using qi::lit;


        start %= float_ >> -lit('*') >> '[' >> lexeme[alpha >> *alnum] >> ':' >> no_case[rectProperties] >> ']';
    }

    qi::rule<Iterator, summand(), ascii::space_type> start;
};
}

float computeSimpleMathExpression(const char * pStr)
{
    using boost::spirit::ascii::space;
    typedef std::string::const_iterator iterator_type;
    typedef client::summand_parser<iterator_type> summand_parser;

    summand_parser g; // Our grammar
    std::string str("-5.24 * [ HelloWorld : w  ]");

    client::summand sum;
    std::string::const_iterator iter = str.begin();
    std::string::const_iterator end = str.end();
    bool r = phrase_parse(iter, end, g, space, sum);

    if (r && iter == end)
    {
        std::cout << "-------------------------\n";
        std::cout << "Parsing succeeded\n";
        std::cout << "got: " << boost::fusion::as_vector(sum) << std::endl;
        std::cout << str << " Parses OK: " << std::endl;
    }
    else
    {
        std::cout << "-------------------------\n";
        std::cout << "Parsing failed\n";
        std::cout << "-------------------------\n";
    }
    return 0;
}

我试图改变

start %= float_ >> -lit('*') >> '[' >> lexeme[alpha >> *alnum] >> ':' >> no_case[rectProperties] >> ']';

start %= (float_ >> -lit('*') >> '[' >> lexeme[alpha >> *alnum] >> ':' >> no_case[rectProperties] >> ']')[&addToVector];

但这会导致编译时错误:

/Users/apple/Documents/ios/framework/boost.framework/Headers/spirit/home/support/action_dispatch.hpp:204:15: No viable conversion from 'boost::fusion::vector3<float, boost::fusion::vector2<char, std::__1::vector<char, std::__1::allocator<char> > >, unsigned int>' to 'const client::summand'

/Users/apple/Documents/ios/framework/boost.framework/Headers/spirit/home/qi/detail/assign_to.hpp:152:20: No matching conversion for static_cast from 'const boost::fusion::vector3<float, boost::fusion::vector2<char, std::__1::vector<char, std::__1::allocator<char> > >, unsigned int>' to 'client::summand'
4

1 回答 1

2

你需要在语义动作中有“惰性函子”。菲尼克斯将这些“演员”命名为“演员”。要将常规函数用作演员,只需绑定它:

    summand_rule %= 
           float_ 
        >> -lit('*') 
        >> '[' 
        >> lexeme[alpha >> *alnum] 
        >> ':' 
        >> no_case[rectProperties] 
        >> ']';

    start %= summand_rule [phoenix::bind(&addToVector, qi::_1)];

查看Coliru 上的编译示例,打印

pushed: (-5.24 HelloWorld 8)

现在,我得到了一个明显的印象,你只是试图解析一个和数向量,在这种情况下,你应该只使用operator%(列表解析器)或operator*(Kleen 星)。

这是使用问题中的三和输入文本并使用的示例

    qi::rule<Iterator, std::vector<summand>(), ascii::space_type> start;
    // defined as
    start = *summand_rule;

没有其他要求!演示程序Live on Coliru

int main()
{
    std::vector<client::summand> parsed;
    parseSummandsInto("-5.24 * [HelloWorld : w] -7 * [HelloWorld : b] -8.24 * [HelloWorld : h]", parsed);

    for(auto const& summand : parsed)
        std::cout << "pushed: " << boost::fusion::as_vector(summand) << std::endl;
}

印刷

pushed: (-5.24 HelloWorld 8)
pushed: (-7 HelloWorld 32)
pushed: (-8.24 HelloWorld 128)

笔记:

  • 消除了对phoenix语义动作的需要
  • 消除了对全局变量的需求client::summands
  • 消除了对addToVector
  • 总共减少33 行代码(89 对 122)

完整代码

备查:

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>

#include <iostream>
#include <string>
#include <vector>

enum RectProperty {
    RectPropertyNone                = 0,
    RectPropertyLeft                = 1<<0,
    RectPropertyRight               = 1<<1,
    RectPropertyCentreX             = 1<<2,
    RectPropertyWidth               = 1<<3,
    RectPropertyTop                 = 1<<4,
    RectPropertyBottom              = 1<<5,
    RectPropertyCentreY             = 1<<6,
    RectPropertyHeight              = 1<<7
};

namespace client
{
    namespace spirit = boost::spirit;
    namespace qi     = boost::spirit::qi;
    namespace ascii  = boost::spirit::ascii;

    struct rectProperties_ : qi::symbols<char, unsigned>
    {
        rectProperties_() {
            add ("cx"   , RectPropertyCentreX)
                ("cy"   , RectPropertyCentreY)
                ("w"    , RectPropertyWidth)
                ("h"    , RectPropertyHeight)
                ("l"    , RectPropertyLeft)
                ("r"    , RectPropertyRight)
                ("t"    , RectPropertyTop)
                ("b"    , RectPropertyBottom)
                ;
        }
    } rectProperties;

    struct summand {
        float factor;
        std::string nodeName;
        RectProperty property;
    };

}

BOOST_FUSION_ADAPT_STRUCT(client::summand,
                      (float, factor)
                      (std::string, nodeName)
                      (RectProperty, property)
                      )

namespace client {

template <typename Iterator>
struct summand_parser : qi::grammar<Iterator, std::vector<summand>(), ascii::space_type>
{
    summand_parser() : summand_parser::base_type(start)
    {
        using namespace ascii;

        summand_rule %= qi::float_ >> -qi::lit('*') >> '[' >> qi::lexeme[alpha >> *alnum] >> ':' >> no_case[rectProperties] >> ']';
        start = *summand_rule;
    }

    qi::rule<Iterator, summand(), ascii::space_type> summand_rule;
    qi::rule<Iterator, std::vector<summand>(), ascii::space_type> start;
};
}

void parseSummandsInto(std::string const& str, std::vector<client::summand>& summands)
{
    typedef std::string::const_iterator It;
    static const client::summand_parser<It> g;

    It iter = str.begin(), 
       end = str.end();

    bool r = phrase_parse(iter, end, g, boost::spirit::ascii::space, summands);

    if (r && iter == end)
        return;
    else
        throw "Parse failed";
}

int main()
{
    std::vector<client::summand> parsed;
    parseSummandsInto("-5.24 * [HelloWorld : w] -7 * [HelloWorld : b] -8.24 * [HelloWorld : h]", parsed);

    for(auto const& summand : parsed)
        std::cout << "pushed: " << boost::fusion::as_vector(summand) << std::endl;
}
于 2013-10-28T09:25:06.397 回答