4

在其他线程中,我已经阅读了如何在语义操作中将符号添加到符号表中,但我不知道如何删除它。

我的问题背后的想法是,我想允许重命名解析文本中的关键字。因此,给出了几个带值的关键字,但用户可以重新分配它们:

reassign(keyword)(mykeyword)

我有一个语义动作规则

using namespace boost::spirit::qi;
...
qi::symbols<char, TYPE> keywords;
...
key_replace = ( lit("reassign") >> lit("(") >> keywords >> lit(")") >> 
                                   lit("(") >> lexeme [ raw [ ( alpha >> *( alnum | '_' ) ) ] ] >> lit (")") ) 
              [ boost::phoenix::bind(keywords.remove, _1) ]; // TODO: replace not remove

问题是,我没有得到对符号本身的引用,而是对存储值的引用。所以调用 remove 不起作用。

在解析过程中如何获取对已解析符号的引用?

有没有更简单的方法来交换符号,同时在解析过程中保留值?

4

1 回答 1

3

天真的“凤凰”方式是

 rule_assign = key >> value
        [ phx::bind(keywords.add, _1, _2) ];
 rule_remove = key
        [ phx::bind(keywords.remove, _1) ];

 // and voila: (BROKEN)
 rule_replace = key >> value
        [ phx::bind(keywords.remove, _1),
          phx::bind(keywords.add, _1, _2)
        ];

后者不起作用,我相信由于第一个绑定返回一个operator,自身重载的对象,并且它比phoenix's更受欢迎operator,

我建议你写一个小助手来解决这个问题:

struct assign_symbol_f
{
    assign_symbol_f(Symbols& sym) : sym(sym) {}

    typedef bool result_type;

    template<typename Key, typename Value>
    bool operator()(Key const& key, Value const& value) const
    {
        bool replaced = (nullptr != sym.find(key));

        sym.remove(key); 
        sym.add(key, value);

        return replaced;
    }

private:
    Symbols& sym;
};

这透明地分配或重新分配symbols树中的项目。按如下方式使用它:

rule_replace_or_add = key >> value
        [ phx::bind(assign_symbol_f(keywords), qi::_1, qi::_2) ];

现在,您可以拆分内容并更具体:

assign_symbol_f assign_sym(keywords);

rule_assign = key >> value
        [ qi::_pass = !phx::bind(assign_sym, _1, _2) ];

rule_replace = key >> value
        [ qi::_pass =  phx::bind(assign_sym, _1, _2) ];

奖金

作为奖励,您可以通过为您的函子创建一个惰性actor来获得一些语法糖

phx::function<assign_symbol_f> assign_sym;

// use it like
rule_assign = key >> value
        [ qi::_pass = assign_sym(_1, _2) ];

rule_replace = key >> value
        [ qi::_pass = assign_sym(_1, _2) ];

看妈!没有了phx::bind

完整演示

完成一个基本的测试套件:)

#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <string>

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

typedef qi::symbols<char, int> Symbols;

struct assign_symbol_f
{
    assign_symbol_f(Symbols& sym) : sym(sym) {}

    typedef bool result_type;

    template<typename Key, typename Value>
    bool operator()(Key const& key, Value const& value) const
    {
        bool replaced = (nullptr != sym.find(key));

        sym.remove(key); 
        sym.add(key, value);

        return replaced;
    }

private:
    Symbols& sym;
};

template <typename Iter> struct parser : qi::grammar<Iter, qi::space_type> 
{
    parser(Symbols &dict) 
        : parser::base_type(start), 
          assign_sym(dict)
    {
        using namespace qi;
        identifier = +graph;

        add_rule = lit("+") >> (identifier >> int_)
            [ assign_sym(_1, _2) ]
            ;

        del_rule = lit("-") >> identifier
            [ phx::bind(dict.remove, _1) ]
            ;

        start = (del_rule | add_rule) % ";";
    }

  private:
    phx::function<assign_symbol_f> assign_sym;

    qi::rule<Iter, qi::space_type> start, del_rule, add_rule;
    qi::rule<Iter, std::string()> identifier; // no skipper
};

bool execute(std::string const& test, Symbols& dict)
{
    auto f = test.begin(), l = test.end();

    parser<std::string::const_iterator> prsr(dict);
    return 
        qi::phrase_parse(f, l, prsr, qi::space)
        && (f == l);
}

int main() {
    Symbols dict;

    assert(execute("+foo 3; +bar 4; -foo", dict));

    assert(!dict.find("foo"));
    assert( dict.find("bar") && (4 == dict.at("bar")));
    assert(!dict.find("zap"));

    assert(execute("+zap -42; +bar 5; +foo 33", dict));

    assert( dict.find("zap") && (-42 == dict.at("zap")));
    assert( dict.find("bar") && (5   == dict.at("bar"))); // replaced
    assert( dict.find("foo") && (33  == dict.at("foo")));
}
于 2013-07-10T13:47:44.233 回答