Boost.Spirit.Qi:在解析时动态创建"difference"解析器
Boost.Spirit.Qi: dynamically create "difference" parser at parse time
可以通过二进制-
(减号)运算符创建一个"差异"解析器:
rule = qi::char_ - qi::lit("}}")
甚至复合差异:
rule = qi::char_ - qi::lit("}}") - qi::lit("]]")
但是我如何在解析时生成差异解析器的整个结果呢?
我猜它可能是像下面这样的形式:
phoenix::function<difference_parser_impl> difference_parser;
rule = qi::lazy(difference_parser(qi::char_, {"}}", "]]"}));
这里,{..., ..., ...}
部分实际上是一个静态容器,但这不是重点;我可以处理那部分。
我已经找到了模板qi::difference<Left, Right>
-但我不知道如何使用它
在我看来,你不是在寻找一个动态的"差异"表达式,而是一个动态的"可变的替代(a|b|c…)"表达式:
expr - a - b - c
等价于expr - (a|b|c)
你可以很容易地实现差异使用:
expr - orCombine(alternatives)
或
!orCombine(alternatives) >> expr
现在,完成这项工作有很多不明确的地方,我将首先解释。幸运的是,有一种更简单的方法,使用qi::symbols
,我将在后面演示。
棘手的东西
如果您愿意,您可以使用一些魔法,按需"生成"替代解析器表达式。我在下面的回答中展示了如何做到这一点:
- 从可选解析器表达式的可变列表生成Spirit解析器表达式
,
- 它充满了陷阱(因为原型表达式不适合复制)1
它方便地使用变量以避免中间存储(注意
deepcopy_
以防止未定义行为):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把戏"。它使用qi::符号,因此具有优秀的运行时性能特征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 successn";
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我相信这个问题将在即将到来的精灵新版本(目前被称为"精灵X3"的实验版本)中被删除
2使用Tries查找匹配
- 如何创建一个CMake变量,除非显式重写,否则使用默认值
- 使用std::multimap迭代器创建std::list
- 在全局变量中保存类的实例以重新创建类(创建"backup")
- 使用CMake创建QML插件
- 如何在c++中为模板函数实例创建快捷方式
- 在C++中,是否可以基于给定的标识符创建基类的新实例,反之亦然
- 创建一个函数以在输入为负数或零时输出字符串.第一次执行用户定义的函数
- OpenCV EqualizeHist()从彩色图像创建黑白图像
- 试图在visual studio上用C++创建一个桌面应用程序
- std::threads可以从Windows DLL中的全局变量创建/销毁吗?
- 如何在C++20中创建模板别名的推导指南
- 如何为模板化对象创建模板向量?VS正在投掷C3203
- 如何创建一个空的全局类并在启动时实例化它
- 无法创建抽象类的实例
- 链接到自行创建的dll失败
- 为什么我不能在不创建字符串变量的情况下使用函数的字符串输出
- 有没有一种方法可以创建一个带有哈希表的数据库,该哈希表具有恒定时间查找功能
- 如何在C++类内存结构中创建"spacer"?
- 终端不会为C++文件创建.exe文件吗
- Boost.Spirit.Qi:在解析时动态创建"difference"解析器