提升:精神解析结构并重复使用它的一部分

时间:2016-08-10 15:27:45

标签: c++ boost boost-spirit

我必须在句子中找到变量并用它们的值替换它们。变量可以用不同的形式编写,例如$ varName或$(varName)。

我希望有一个struct VariableHolder可以轻松访问:

struct VariableHolder
{
    string name;    // contains "varName"
    string fromFile;  // contains "$(varName)" or "$varName"
    void setName(ustring n) { name = n; }
}

显然,我想避免多次传递并调用多个解析器。 到目前为止我所拥有的是:

BOOST_FUSION_ADAPT_STRUCT(VariableHolder,
(ustring, fromFile)
)
// variableName is another parser that returns a string
qi::rule<Iterator, VariableHolder()> variable %=
                (qi::char_("$")
                    >> (variableName[phoenix::bind(&VariableHolder::setName, qi::_val, qi::_1)]
                        | (qi::char_("(")
                            >> variableName[phoenix::bind(&VariableHolder::setName, qi::_val, qi::_1)]
                            >> qi::char_(")")))
                    );

哪个不起作用。名称设置正确,但fromFile变量只包含“$”,而不包含任何其他内容。

所以,我向你提问的好人:

  1. 我的想法是只使用BOOST_FUSION_ADAPT_STRUCT调整部分结构,并用语义动作填充其余部分。愚蠢的想法,还是我做错了?
  2. 有没有办法绑定语义动作并仍然得到输出?喜欢

    char_ [doSomething] //这可以调用doSomething,并解析char吗?

1 个答案:

答案 0 :(得分:2)

解决问题:

  
      
  1. 我的想法是只用BOOST_FUSION_ADAPT_STRUCT调整部分结构,然后用语义动作填充其余部分。愚蠢的想法,还是我做错了?
  2.   

不可想象。不是我的推荐(见Boost Spirit: "Semantic actions are evil"?)。但是,你做错了:

如果您不希望它成为公开属性的一部分,则需要lit("$")而不是char_("$")。事实上,'$'将在这里做

  
      
  1. 有没有办法绑定语义动作并仍然得到输出?像

    char_[doSomething] // Can this both call doSomething, and parse a char?
    
  2.   

是。您现在正在执行此操作,因为您使用operator%=代替operator=而无效(请参阅文档:http://www.boost.org/doc/libs/1_61_0/libs/spirit/doc/html/spirit/qi/reference/nonterminal/rule.html#spirit.qi.reference.nonterminal.rule.expression_semantics)。

然而,似乎你真的试图两次使用相同的输入(原始的fromFile和&#34;煮熟的&#34;作为name?)它会适得其反,因为自动规则属性传播name所需的值覆盖fromFile

这里唯一的快捷方法是仅使用SA。我建议让VariableHolder的构造函数负责细节。

  

旁注:看起来好像可选括号表示表达式语法。如果是这样,请在语法中明确说明,而不是在variableName规则中对特殊情况进行硬编码。如果没有,请继续:)

这是一个尝试修复:

<强> Live On Coliru

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

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

struct VariableHolder {
    std::string name;     // contains "varName"
    std::string fromFile; // contains "$(varName)" or "$varName"
};

template <typename It, typename Skipper = qi::ascii::space_type> struct P : qi::grammar<It, VariableHolder(), Skipper> {
    P() : P::base_type(start) {
        auto _name     = phoenix::bind(&VariableHolder::name, qi::_val);
        auto _fromFile = phoenix::bind(&VariableHolder::fromFile, qi::_val);

        variableName = qi::alpha >> +qi::alnum;
        variable = '$' >> (variableName | '(' >> variableName >> ')');

        start = qi::as_string [ qi::raw [ 
                variable [ _name = qi::_1 ]] 
            ] [ _fromFile = qi::_1 ];

        BOOST_SPIRIT_DEBUG_NODES((start)(variable)(variableName))
    }

  private:
    qi::rule<It, std::string(), Skipper> variable;
    qi::rule<It, VariableHolder(), Skipper> start;
    // lexemes
    qi::rule<It, std::string()> variableName;
};

int main() {
    using It = std::string::const_iterator;
    P<It> const p;

    for (std::string const input : { 
            "$foo1",
            "$(bar2)" 
        }) 
    {
        It f = input.begin(), l = input.end();
        VariableHolder data;

        bool ok = qi::phrase_parse(f, l, p, qi::ascii::space, data);

        if (ok) {
            std::cout << "Parse success: " << data.name << " (source: '" << data.fromFile << "')\n";
        } else {
            std::cout << "Parse failure ('" << input << "')\n";
        }

        if (f != l) {
            std::cout << "Remaining unparsed input: '" << std::string(f, l) << "'\n";
        }
    }
}

打印

Parse success: foo1 (source: '$foo1')
Parse success: bar2 (source: '$(bar2)')