c++ - 灵气中如何在字符串属性开头插入一个字符

标签 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])[];

但我找不到如何做到这一点,如果有其他不涉及语义操作的解决方案会更好。

最佳答案

我会说“向一些不相关的属性添加魔法字符”的想法构成了一个有问题的设计选择。一般来说,我建议将解析和程序逻辑分开。所以我会解析成

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 ] 
    ];

到现在为止,我们为了获得很少的 yield (如果有的话)已经离谱得很远了。我建议要么

  1. 用天真的方式写:

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

  3. 手动编写解析器

完整演示

所有上述变体:

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'

关于c++ - 灵气中如何在字符串属性开头插入一个字符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36503667/

相关文章:

c++ - 我应该为我的目的使用哪种数据结构?

c++ - 使用 vector 和 multimap 的特定图 block 和 Sprite 之间的边界框碰撞

C++ - 未调用函数模板特化

c++ - 如何使用cmake pack redhat风格的rpm,它是major和 “-devel”?

javascript - 像推特一样解​​析日期的问题

java - 如何在groovy中解析xml文件并将其放入pojo中?

c++ - 如何使用正则表达式匹配仅包含中文字母的字符串?

excel - 字符串验证

c++ - 我可以使用 std::shared_ptr 而不是 boost::shared_ptr 构建 boost 库吗

c++ - 如何使用 Boost 预处理器获取类函数可访问的函数列表?