在我看来,您不是在寻找动态的“差异”表达式,而是在寻找动态的“可变参数替代(a | b | c ...)”表达式:
expr - a - b - c
相当于expr - (a|b|c)
然后,您可以使用以下任一方法轻松实现差异:
expr - orCombine(alternatives)
或者
!orCombine(alternatives) >> expr
现在,完成这项工作有许多不完善的地方,我将首先解释这一点。幸运的是,有一种更简单的方法 using qi::symbols
,我将在此之后立即演示。
棘手的东西
如果您愿意,您可以按需“生成”替代解析器表达式,并带有相当多的魔法。我在这个答案中展示了如何做到这一点:
但
- 它充满了陷阱(因为原始表达式不适合复制)1
它方便地使用可变参数以避免中间存储(注意deepcopy_
未定义行为的 to ward):
template<typename ...Expr>
void parse_one_of(Expr& ...expressions)
{
auto parser = boost::fusion::fold(
boost::tie(expressions...),
qi::eps(false),
deepcopy_(arg2 | arg1)
);
看到您如何需要替代解析器的真正动态组合,我看不出如何在不增加复杂性和出现细微错误的机会的情况下适应您的需求(相信我,我已经尝试过)。
因此,我推荐一种“滥用”现有“动态”解析器的经过验证的真实方法:
简化使用qi::symbols
这个想法迷失地借鉴了著名的“Nabialek Trick”。它使用 qi::symbols,因此具有出色的运行时性能特征2。
事不宜迟,这是一个如何使用它的示例,从字符串文字向量开始:
template <typename It, typename Skipper = qi::space_type>
struct parser : qi::grammar<It, std::string(), Skipper>
{
parser() : parser::base_type(start)
{
static const std::vector<std::string> not_accepted { "}}", "]]" };
using namespace qi;
exclude = exclusions(not_accepted);
start = *(char_ - exclude);
BOOST_SPIRIT_DEBUG_NODE(start);
}
private:
qi::rule<It, std::string(), Skipper> start;
typedef qi::symbols<char, qi::unused_type> Exclude;
Exclude exclude;
template<typename Elements>
Exclude exclusions(Elements const& elements) {
Exclude result;
for(auto& el : elements)
result.add(el);
return result;
}
};
完整的工作示例在这里:http://coliru.stacked-crooked.com/view?id=ddbb2549674bfed90e3c8df33b048574-7616891f9fd25da6391c2728423de797并打印
parse success
data: 123
trailing unparsed: ']] 4'
完整代码
备查:
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
template <typename It, typename Skipper = qi::space_type>
struct parser : qi::grammar<It, std::string(), Skipper>
{
parser() : parser::base_type(start)
{
static const std::vector<std::string> not_accepted { "}}", "]]" };
using namespace qi;
exclude = exclusions(not_accepted);
start = *(char_ - exclude);
BOOST_SPIRIT_DEBUG_NODE(start);
}
private:
qi::rule<It, std::string(), Skipper> start;
typedef qi::symbols<char, qi::unused_type> Exclude;
Exclude exclude;
template<typename Elements>
Exclude exclusions(Elements const& elements) {
Exclude result;
for(auto& el : elements)
result.add(el);
return result;
}
};
int main()
{
const std::string input = "1 2 3]] 4";
typedef std::string::const_iterator It;
It f(begin(input)), l(end(input));
parser<It> p;
std::string data;
bool ok = qi::phrase_parse(f,l,p,qi::space,data);
if (ok)
{
std::cout << "parse success\n";
std::cout << "data: " << data << "\n";
}
else std::cerr << "parse failed: '" << std::string(f,l) << "'\n";
if (f!=l) std::cerr << "trailing unparsed: '" << std::string(f,l) << "'\n";
}
1我相信这个问题即将在即将到来的新版本Spirit中移除(目前被称为“Spirit X3”为实验版)
2它使用Tries来查找匹配项