如何在精神qi

时间:2016-04-08 15:34:33

标签: c++ parsing boost boost-spirit boost-spirit-qi

我有以下规则:

rule<std::string::const_iterator, std::string()> t_ffind, t_sim, t_hash, t_state;

t_ffind = hold[(attr('$') >> t_sim >> t_hash >> t_state)] | t_sim;

这意味着我可以单独找到t_sim,或者t_hasht_state后跟t_ffind,如果单独t_sim将取t_sim的确切值,在另一种情况下,我还会在字符串的开头插入一个标记字符。

但是如果我写这样的规则我将解析t_ffind = t_sim >> -(qi::hold[t_hash >> t_state]); 两次,那么我将规则修改为:

(t_hash >> t_state)

但如果存在t_ffind = t_sim >> -(qi::hold[t_hash >> t_state])[]; ,仍然存在插入字符的问题,我认为最终解决方案可能是一些语义操作:

CLANG_ENABLE_MODULE_DEBUGGING

但我无法找到如何做到这一点,如果其他解决方案不涉及语义行为会更好。

1 个答案:

答案 0 :(得分:1)

我会说“为一些不相关的属性添加魔法角色”的想法构成了一个值得怀疑的设计选择。一般来说,我建议将解析和程序逻辑分开。所以我要解析

namespace ast {
  struct t_ffind {
      std::string t_sim;
      boost::optional<std::string> t_hash, t_state; // or whatever the types are
  };
}

或者,如果你真的没有理由将哈希/状态标记建模到单独的字段中,你可以这样做

namespace ast {
  struct t_ffind {
      std::string t_sim_hash_state;
      bool sim_only;
  };
}

但是在语义操作中设置sim_only会变得更加复杂。这接近你所面临的问题。

你的愿望

只是为了好玩,让我们看看我们能做些什么。首先,优化t_sim的重复解析就像过早优化一样。但也许您可以使用语义操作来改变_val

t_ffind %= t_sim >> -(as_string[t_hash >> t_state] [ insert(_val, begin(_val), '$') ]);

注意使用as_string[]将t_hash和t_state的属性粘合在一起,这样自动属性传播就能继续工作。我强烈怀疑这可能是一个 - 显然 - 比可能解析t_sim两次更大的性能。

你可以尝试与Spirit争论更多控制权:

t_ffind = (t_sim >> -(as_string[t_hash >> t_state])) 
    [ if_(_2) [ _val = '$' + _1 + *_2 ].else_ [ _val = _1 ] ];

仍在使用as_string中间串联。你可以放弃它:

t_ffind = (t_sim >> -(t_hash >> t_state))
    [ if_(_2) 
        [ _val = '$' + _1 + at_c<0>(*_2) + at_c<1>(*_2) ]
      .else_ 
        [ _val = _1 ] 
    ];

到现在为止,我们的收益微不足道(如果有的话)。我建议

  1. 以天真的方式写作:

    t_ffind = hold[(attr('$') >> t_sim >> t_hash >> t_state)] | t_sim;
    
  2. 修复您的AST以反映您要解析的内容

  3. 手动编写解析器

  4. 完整演示

    以上所有变体:

    <强> Live On Coliru

    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    #include <boost/spirit/include/phoenix_fusion.hpp>
    
    int main() {
        using namespace boost::spirit::qi;
    
        rule<std::string::const_iterator, std::string()> 
            t_sim   = "sim",
            t_hash  = +digit,
            t_state = raw[lit("on")|"off"],
            t_ffind;
    
        for (auto initialize_t_ffind : std::vector<std::function<void()> > {
         [&] { t_ffind = hold[(attr('$') >> t_sim >> t_hash >> t_state)] | t_sim; },
         [&] {
                 // this works:
                 using boost::phoenix::insert;
                 using boost::phoenix::begin;
                 t_ffind %= t_sim >> -(as_string[t_hash >> t_state] [ insert(_val, begin(_val), '$') ]);
             },
         [&] { 
                // this works too:
                using boost::phoenix::if_;
                t_ffind = (t_sim >> -(as_string[t_hash >> t_state])) 
                    [ if_(_2) 
                        [ _val = '$' + _1 + *_2 ]
                      .else_ 
                        [ _val = _1 ] 
                    ];
             },
         [&] {
                 // "total control":
                using boost::phoenix::if_;
                using boost::phoenix::at_c;
                t_ffind = (t_sim >> -(t_hash >> t_state))
                    [ if_(_2) 
                        [ _val = '$' + _1 + at_c<0>(*_2) + at_c<1>(*_2) ]
                      .else_ 
                        [ _val = _1 ] 
                    ];
            } })
    
         {
             initialize_t_ffind();
    
             for (std::string const s : { "sim78off", "sim" })
             {
                 auto f = s.begin(), l = s.end();
                 std::string result;
                 if (parse(f, l, t_ffind, result)) {
                     std::cout << "Parsed: '" << result << "'\n";
                 } else {
                     std::cout << "Parse failed\n";
                 }
    
                 if (f != l) {
                     std::cout << "Remaining input: '" << std::string(f,l) << "'\n";
                 }
             }
         }
    }
    

    打印:

    Parsed: '$sim78off'
    Parsed: 'sim'
    Parsed: '$sim78off'
    Parsed: 'sim'
    Parsed: '$sim78off'
    Parsed: 'sim'
    Parsed: '$sim78off'
    Parsed: 'sim'