【问题标题】:Assigning data to a given element in a vector within a rule将数据分配给规则内向量中的给定元素
【发布时间】:2011-07-23 01:59:02
【问题描述】:

我正在尝试设置一个解析器,给定一个值,它可以将它分配给向量的某个元素,但我不完全确定如何实现它。

假设以下代码解析字符串(0){**+*+}。它应该为每个+ 增加一次bar.a[0],为每个* 增加一次bar.b[0]。我遇到的问题是我不确定如何使用_a 获取对向量元素的引用:

#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <vector>

//The struct containing the vector.
struct testStruct {
    std::vector<int> a, b;
};

BOOST_FUSION_ADAPT_STRUCT (
    testStruct,
    (std::vector<int>, a)
    (std::vector<int>, b)
)

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

template<typename Iterator>
struct foo : qi::grammar<Iterator, testStruct(), qi::locals<unsigned>, ascii::space_type> {
    foo() : foo::base_type(start) {
        using namespace qi::labels;
        using qi::eps;
        using qi::lit;
        using qi::uint_;
        using phoenix::at_c;

        start = lit('(')
                >> uint_                [_a = _1]
                >> ')'
                >> '{'
                >>  starsOrPlus(
                        at_c<_a>(at_c<0>(_val)),    //This is where I'm not sure what to do.
                        at_c<_a>(at_c<1>(_val))
                    )
                >> '}'
                ;

        starsOrPlus = eps               [_r1 = 0]
                                        [_r2 = 0]
            >> (
                * (
                    (
                        +lit('+')       [_r1 += 1]
                    )
                    ^ (
                        +lit('*')       [_r2 += 1]
                    )
                )
            )
        ;
    }

    qi::rule<Iterator, testStruct(), qi::locals<unsigned>, ascii::space_type> start;

    //Parses stars and pluses. Sets the first uint8_t to the number of *, and the
    //second to the number of +.
    qi::rule<Iterator, void(int&, int&), ascii::space_type> starsOrPlus;
};

//Main program
int main() {
    std::string testString = "(2){**++*+}";

    typedef foo<std::string::const_iterator> foo;
    foo grammar;

    testStruct bar;
    std::string::const_iterator iter = testString.begin();
    std::string::const_iterator end = testString.end();
    bool parsed = phrase_parse(iter, end, grammar, ascii::space, bar);

    if (parsed) {
        //Do something with the data...
    }

    return 0;
}

编译失败,出现以下错误:

main.cpp||In constructor 'foo<Iterator>::foo()':|
main.cpp|36|error: 'boost::spirit::_a' cannot appear in a constant-expression|
main.cpp|36|error: no matching function for call to 'at_c(boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::at_eval<0>, boost::fusion::vector<boost::spirit::attribute<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&)'|
main.cpp|37|error: 'boost::spirit::_a' cannot appear in a constant-expression|
main.cpp|37|error: no matching function for call to 'at_c(boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::at_eval<1>, boost::fusion::vector<boost::spirit::attribute<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&)'|

所以,很明显,我不能在at_c 中使用占位符值。我也知道,即使可以,如果给定位置超出范围,也会出现重新调整矢量大小的问题。

我将如何实现这样的事情?我是不是走错了路?

【问题讨论】:

    标签: boost boost-spirit boost-spirit-qi


    【解决方案1】:

    这应该让你开始:

    #include <vector>
    #include <string>
    #include <iostream>
    #include <boost/fusion/include/adapt_struct.hpp>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    #include <boost/spirit/include/phoenix_fusion.hpp>
    
    struct testStruct
    {
        std::vector<int> a, b;
    };
    
    BOOST_FUSION_ADAPT_STRUCT
    (
        testStruct,
        (std::vector<int>, a)
        (std::vector<int>, b)
    )
    
    namespace bp = boost::phoenix;
    namespace bs = boost::spirit;
    namespace bsq = bs::qi;
    
    template<typename Iterator>
    struct foo : bsq::grammar<Iterator, testStruct(), bsq::locals<unsigned> >
    {
        foo() : foo::base_type(start)
        {
            using namespace bs::labels;
            using bp::at_c;
            using bp::resize;
            using bs::lit;
            using bs::uint_;
    
            start
                =   '('
                    >> uint_
                       [
                           _a = _1,
                           resize(at_c<0>(_val), _1 + 1),
                           resize(at_c<1>(_val), _1 + 1)
                       ]
                    >> "){"
                    >> starsOrPlus(at_c<0>(_val)[_a], at_c<1>(_val)[_a])
                    >> '}'
                ;
    
            starsOrPlus
                =  *(
                        lit('+')[_r1 += 1]
                    |   lit('*')[_r2 += 1]
                    )
                ;
        }
    
        bsq::rule<Iterator, testStruct(), bsq::locals<unsigned> > start;
        bsq::rule<Iterator, void(int&, int&)> starsOrPlus;
    };
    
    void printvec(std::vector<int> const& vec)
    {
        bool first = true;
        for (std::vector<int>::const_iterator it = vec.begin(), it_end = vec.end();
             it != it_end;
             ++it)
        {
            if (first)
                first = false;
            else
                std::cout << ", ";
            std::cout << *it;
        }
    }
    
    int main()
    {
        foo<std::string::const_iterator> grammar;
        testStruct bar;
        std::string const input = "(2){**++*}";
        std::string::const_iterator first = input.begin(), last = input.end();
        if (bsq::parse(first, last, grammar, bar) && first == last)
        {
            std::cout << "bar.a: ";
            printvec(bar.a);
            std::cout << "\nbar.b: ";
            printvec(bar.b);
            std::cout << '\n';
        }
        else
            std::cout << "parse failed\n";
    }
    

    这里值得注意的变化是:

    • testStruct 内部的 vectors 必须调整到足以使所需索引有效的大小
    • 使用operator[] 代替boost::phoenix::at_c 来访问vector 的索引,就像在“普通”代码中一样

    请注意,我取出跳过解析器是为了简化事情(而且因为它似乎没有必要);如果需要,可以将其添加回来——它在这里没有真正的相关性。

    【讨论】:

    • 非常感谢!我不知道为什么我没有考虑过那样做。另外,这肯定是做 starsOrPlus 的更好方法,所以感谢您的建议。
    • 是否需要将 testStruct() 传递给语法。我不能在不明确传递的情况下在语法中使用它吗?假设您的启动规则没有 testStruct 作为属性,但其他规则有,那么呢?
    • @Dilawar : 如果parse 的调用者希望将testStruct 作为“返回值”,则只需将其传递给parse。即如果testStruct只在语法内部使用,解析完成后可以丢弃,那么不,不需要传入parse
    猜你喜欢
    • 2019-08-16
    • 2020-03-18
    • 1970-01-01
    • 1970-01-01
    • 2013-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多