【问题标题】:Boost:spirit Parsing into structure and reusing parts of itBoost:spirit 解析成结构并重用部分结构
【发布时间】:2016-08-10 15:27:45
【问题描述】:

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

我希望有一个结构 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?

【问题讨论】:

    标签: c++ boost boost-spirit


    【解决方案1】:

    回答问题:

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

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

    如果您不希望 lit("$") 成为暴露属性的一部分,则需要 char_("$")。其实'$'这里就可以了

    1. 有没有办法绑定语义动作并仍然获得输出?喜欢

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

    是的。您现在正在执行此操作,因为您使用的是 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,“熟”为name?)它适得其反,因为自动规则属性传播也是 用您想要的 fromFile 值覆盖 name

    在这里,唯一快速的方法是仅使用 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)')
    

    【讨论】:

    • 感谢您的快速回答,一如既往地详细:) 但​​我想我的脑子坏了。为什么你可以得到一个规则的两个不同的输出?我猜通过使用operator= 你只能得到解析器,而 qi::raw 给你完整的输出?这在内部如何运作?每个规则都存储其“正常”输出和原始输出?
    • 确实,raw [] 暴露了原始匹配的输入序列。您可以同时使用这两个属性的原因是语义操作的附加位置。第一个附加在子解析器表达式(公开名称)上,fromFile 操作附加到raw [] 指令本身的结果
    • 一个适当的 qi::rule 只有 synthesises 一个属性自然(尽管可以从像 with_raw 这样的自定义指令合成一个元组。
    猜你喜欢
    • 1970-01-01
    • 2020-04-03
    • 1970-01-01
    • 2018-01-01
    • 2021-07-17
    • 1970-01-01
    • 2019-01-08
    • 2012-02-21
    • 1970-01-01
    相关资源
    最近更新 更多