c++ - 使用 boost::spirit 解析时如何将结构 "summand"放入 vector 中?

标签 c++ parsing stl boost-spirit-qi

下面的代码成功解析了类似 "-5.24 * [HelloWorld : w]" 的字符串进入结构summand但我需要可以解析例如 "-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'

最佳答案

您需要在语义操作中拥有“惰性仿函数”。菲尼克斯将这些人命名为“ Actor ”。要使用常规函数作为参与者,只需绑定(bind)它:

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

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

查看编译示例 live on Coliru ,打印

pushed: (-5.24 HelloWorld 8)

现在,我得到的明显印象是您只是试图解析被加数 vector ,在这种情况下您应该只使用 operator% (列表解析器)或 operator*(克莱恩星)。

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

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

关于c++ - 使用 boost::spirit 解析时如何将结构 "summand"放入 vector 中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19621746/

相关文章:

c++ - 从字符串序列中提取最后 2 个单词,以空格分隔

c++ - 是否有用于 boost::numeric::interval 长度的内置函数

c++ - 从 C++ 开始,密码验证器

python - 在 Python 中将文件拆分为字典

c++ - 如何在初始化列表中初始化普通数组成员变量?

c++ - 返回 STL 列表作为参数

javascript - JQuery ui 不与 mfc htmldialog 一起工作?

c++ - 再次: why cannot static member variables be declared inline?

parsing - 在Prolog中使用差异列表的上下文无关文法如何运作?

算法名称 - 匹配 AST 中的子树