从语义操作返回的值会干扰规则属性



下面的程序是一个人工示例(从我正在研究的一个更大的语法中缩减而来),以展示一种奇怪的行为。

程序按原样运行的输出是"hello",是不正确的。

如果我从quoted_string规则中删除(在本例中无用)语义操作,输出是预期的"foo=hello"。

#define BOOST_RESULT_OF_USE_DECLTYPE
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <vector>
#include <string>
#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include "utils.hpp"
namespace t {
    using std::vector;
    using std::string;
    namespace qi = boost::spirit::qi;
    namespace phx = boost::phoenix;
    template <typename Iterator, typename Skipper=qi::space_type>
    struct G1 : qi::grammar<Iterator, string(), Skipper> {
        template <typename T>
        using rule = qi::rule<Iterator, T, Skipper>;
        qi::rule<Iterator, string(), qi::locals<char>> quoted_string;
        rule<string()> start;
        G1() : G1::base_type(start, "G1") {
            {
                using qi::_1;
                using qi::_a;
                using attr_signature = vector<char>;
                auto handler = [](attr_signature const& elements) -> string {
                    string output;
                    for(auto& e : elements) {
                        output += e;
                    }
                    return output;
                };
                quoted_string = (qi::omit[qi::char_("'"")[_a = _1]]
                    >> +(qi::char_ - qi::char_(_a))
                    >> qi::lit(_a))[qi::_val = phx::bind(handler, _1)];
            }
            start = qi::string("foo") >> -(qi::string("=") >> quoted_string);
        }
    };
    string parse(string const input) {
        G1<string::const_iterator> g;
        string result;
        phrase_parse(begin(input), end(input), g, qi::standard::space, result);
        return result;
    }
};
int main() {
    using namespace std;
    auto r = t::parse("foo='hello'");
    cout << r << endl;
}

我肯定能找到一个解决方案,但我要弄清楚我错过了什么

就像@cv_and_he解释的那样,您正在用handler(_1)的结果覆盖属性。由于属性是通过引用传递的,因此会丢失原始值。

自动属性传播规则知道如何连接"字符串"容器值,所以为什么不使用默认实现呢?

    quoted_string %= qi::omit[qi::char_("'"")[_a = _1]]
        >> +(qi::char_ - qi::char_(_a))
        >> qi::lit(_a);

(注意 %= ;这允许自动传播(即使在存在语义操作的情况下)。

或者,您可以从SA内部推回:

 >> +(qi::char_ - qi::char_(_a)) [ phx::push_back(qi::_val, _1) ]

并且,如果您确实需要在handler中进行一些处理,则使其通过引用获取字符串:

auto handler = [](attr_signature const& elements, std::string& attr) {
    for(auto& e : elements) {
        attr += e;
    }
};
quoted_string = (qi::omit[qi::char_("'"")[_a = _1]]
    >> +(qi::char_ - qi::char_(_a))
    >> qi::lit(_a)) [ phx::bind(handler, _1, qi::_val) ];

所有这些方法都有效。

对于真正繁重的事情,我过去使用自定义字符串类型与boost::spirit::traits自定义点来进行转换:

  • http://www.boost.org/doc/libs/1_55_0/libs/spirit/doc/html/spirit/advanced/customize.html

最新更新