如何使用qi创建通用解析器
How do you create a generic parser using qi?
我正在尝试使用qi创建通用解析器元素,因为我不幸的是(必须支持MSVC)不能使用X3。这个想法是有一个模板化的结构体:
template<class T> struct parse_type;
我可以这样使用:
template<class T> T from_string(std::string const& s)
{
T res;
parse_type<T> t;
...
if (phrase_parse(...,parse_type<T>(),...,t))
}
或像这样的专门化
template<class T,class Alloc>
struct parse_type<std::vector<T,Alloc>>
{
// Parse a vector using rule '[' >> parse_type<T> % ',' > ']';
}
主要目的是为了方便解析std::tuple, boost::optional和boost::variant(由于qi的贪婪特性,最后一个不能自动执行)。
我希望能得到关于如何处理这个问题的反馈。目前,我的结构基于qi::grammar,但是X3中不支持语法,我想在MSVC编译时使用X3,并且我也对必须提供skipper感到有点不舒服。另一种方法是在parse_type中使用返回适当规则的静态函数。我在考虑这是不是一个更干净的方法?
如有任何反馈,我们将不胜感激。
Update2:用运行时失败的可编译示例替换代码片段。下面是代码:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <string>
#include <string>
#include <iostream>
#include <iostream>
// Support to simplify
using iter = std::string::const_iterator;
void print(std::vector<int> const& v)
{
std::cout << '[';
for (auto i: v) std::cout << i << ',';
std::cout << "]";
}
namespace qi = boost::spirit::qi;
// My rule factory - quite useless if you do not specialise
template<class T> struct ps_rule;
// An example of using the factory
template<class T>
T from_string(std::string const& s)
{
T result;
iter first { std::begin(s) };
auto rule = ps_rule<T>::get();
phrase_parse(first,std::end(s),rule,qi::space,result);
return result;
}
// Specialising rule for int
template<>
struct ps_rule<int>
{
static qi::rule<iter,int()> get() { return qi::int_; }
};
// ... and for std::vector (where the elements must have rules)
template<class T,class Alloc>
struct ps_rule<std::vector<T,Alloc>>
{
static qi::rule<iter,std::vector<T,Alloc>()> get()
{
qi::rule<iter,std::vector<T,Alloc>()> res;
res.name("Vector");
res =
qi::lit('{')
>> ps_rule<T>::get() % ','
>> '}';
return res;
}
};
int main()
{
// This one works like a charm.
std::cout << ((from_string<int>("100") == 100) ? "OKn":"Failedn");
std::vector<int> v {1,2,3,4,5,6};
// This one fails
std::cout << ((from_string<std::vector<int>>("{1,2,3,4,5,6}") == v) ? "OKn":"Failedn");
}
代码在boost/function_template.hpp第766行失败:
result_type operator()(BOOST_FUNCTION_PARMS) const
{
if (this->empty())
boost::throw_exception(bad_function_call());
return get_vtable()->invoker
(this->functor BOOST_FUNCTION_COMMA BOOST_FUNCTION_ARGS);
}
这段代码是boost::function4中的成员函数,boost::fusion::vector0> &boost::精神::unused_type const&>问题是get_vtable返回一个无效的指针。
您的主要问题是qi::rule
的复制构造函数使用对原始规则的引用,在您的示例中是一个局部变量。可以避免此问题的一种方法是使用qi::rule
的copy
成员函数,但这需要稍微更改ps_rule
专门化的返回类型。
static typename boost::proto::terminal<qi::rule<iter,std::vector<T,Alloc>()>>::type get()
{
//[...] (same as before)
return res.copy();
}
一旦你这样做,同样的问题出现在你的ps_rule<int>
上,即使它看起来是独立工作的。你可以做一些类似的事情,但在这种情况下,规则是不需要的,它会更好(甚至从性能的角度来看),只是使用这样的:
static qi::int_type get() { return qi::int_; }
完整样本(在WandBox上运行)
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>
// Support to simplify
using iter = std::string::const_iterator;
void print(std::vector<int> const& v)
{
std::cout << '[';
for (auto i: v) std::cout << i << ',';
std::cout << "]";
}
namespace qi = boost::spirit::qi;
// My rule factory - quite useless if you do not specialise
template<class T> struct ps_rule;
// An example of using the factory
template<class T>
T from_string(std::string const& s)
{
T result;
iter first { std::begin(s) };
auto rule = ps_rule<T>::get();
qi::phrase_parse(first,std::end(s),rule,qi::space,result);
return result;
}
// Specialising rule for int
template<>
struct ps_rule<int>
{
static qi::int_type get() { return qi::int_; }
};
// ... and for std::vector (where the elements must have rules)
template<class T,class Alloc>
struct ps_rule<std::vector<T,Alloc>>
{
static typename boost::proto::terminal<qi::rule<iter,std::vector<T,Alloc>()>>::type get()
{
qi::rule<iter,std::vector<T,Alloc>()> res;
res.name("Vector");
res =
qi::lit('{')
>> ps_rule<T>::get() % ','
>> '}';
return res.copy();
}
};
int main()
{
// This one works like a charm.
std::cout << ((from_string<int>("100") == 100) ? "OKn":"Failedn");
std::vector<int> v {1,2,3,4,5,6};
std::cout << ((from_string<std::vector<int>>("{1,2,3,4,5,6}") == v) ? "OKn":"Failedn");
std::vector<std::vector<int> > vv {{1,2,3},{4,5,6}};
std::cout << ((from_string<std::vector<std::vector<int>>>("{{1,2,3},{4,5,6}}") == vv) ? "OKn":"Failedn");
}
PS:如果您使用Spirit自己的机制在主模板中自动创建解析器,则可以节省大量专门化。下面是一个例子:
- 在 boost::qi 中使用过多的替代运算符会导致分段错误
- 如何在 qi 符号表中使用 std::function
- 如何在 x3 中使用 qi::_1/qi::_N 重写 qi 解析器
- 如何使用BoostSpirit.Qi增量解析(并对其执行操作)大文件
- Boost :: Spirit :: Qi语法使用具有不同迭代剂类型的语法
- 使用spirit::qi时如何忽略来自spirit::lex的令牌属性
- qi%运算符使用(1)分隔符属性,(2)接受尾随分隔符
- 如何使用Qi :: Hold []解析器指令.(带有boost ::交换的属性类型的问题)
- 使用BoostSpirit.Qi解析为矢量
- 使用 LLVM 的 libc++ 时,__1 符号从何而来?
- 使用boost::spirit::qi来解析带有分隔符的数字
- 使用提升 Qi 解析为结构的随机顺序
- 在std::cout之后使用std::cin时,换行符从何而来
- 通过使用boost spirit qi解析器迭代填充BGL图
- 使用提升::精神::qi 重新合成中间值
- boost:spirit::qi 解析器使用多种语法和 phoenix::construct
- 为什么以及在何处在C++中使用引用和指针
- 在 Qi 中使用 Boost Phoenix 在语法中引用以前的匹配项
- 如何使用qi创建通用解析器
- 如何使用qi解析和验证整数的有序列表