灵气中如何在字符串属性开头插入一个字符
How to insert a character at the beginning of a string attribute in 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_hash
和 t_state
,如果单独 t_ffind
将取 [=13 的确切值=],在另一种情况下,我也会在字符串的开头插入一个标记字符。
但如果我这样写规则,我将解析 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])[];
但我找不到如何做到这一点,如果有其他不涉及语义动作的解决方案会更好。
我会说 "adding a magic character to some unrelated attribute" 的想法构成了一个有问题的设计选择。一般来说,我建议将解析和程序逻辑分开。所以我会解析成
namespace ast {
struct t_ffind {
std::string t_sim;
boost::optional<std::string> t_hash, t_state; // or whatever the types are
};
}
或者,如果您真的没有理由将 hash/state 标记建模到单独的字段中,您可以这样做
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 ]
];
到现在为止,我们为了获得很少的收益(如果有的话)已经离谱得很远了。我建议
天真的写法:
t_ffind = hold[(attr('$') >> t_sim >> t_hash >> t_state)] | t_sim;
修复你的 AST 以反映你正在解析的东西
手动编写解析器
完整演示
以上所有变化:
#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'
我有以下规则:
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_hash
和 t_state
,如果单独 t_ffind
将取 [=13 的确切值=],在另一种情况下,我也会在字符串的开头插入一个标记字符。
但如果我这样写规则,我将解析 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])[];
但我找不到如何做到这一点,如果有其他不涉及语义动作的解决方案会更好。
我会说 "adding a magic character to some unrelated attribute" 的想法构成了一个有问题的设计选择。一般来说,我建议将解析和程序逻辑分开。所以我会解析成
namespace ast {
struct t_ffind {
std::string t_sim;
boost::optional<std::string> t_hash, t_state; // or whatever the types are
};
}
或者,如果您真的没有理由将 hash/state 标记建模到单独的字段中,您可以这样做
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 ]
];
到现在为止,我们为了获得很少的收益(如果有的话)已经离谱得很远了。我建议
天真的写法:
t_ffind = hold[(attr('$') >> t_sim >> t_hash >> t_state)] | t_sim;
修复你的 AST 以反映你正在解析的东西
手动编写解析器
完整演示
以上所有变化:
#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'