我想将一些旧的手写解析代码转换为Boost Spirit,并在此过程中学习(更多)精神。旧代码使用流和模板来解析某些数据类型和某些容器的定义。
一些典型的格式:
VECTOR[number_of_items,(item_1, item_2 .... item_n)]
PAIR(p1, p2)
RECT[(left,top)-(right,bottom)]
Point( x, y )
Size( x, y )
解析函数是模板,其中项目类型为模板参数,并使用流作为输入,例如
template<class T> std::istream& operator>>(std::Stream& in, std::vector<T>& v);
template<class T1, class T2> std::istream& operator>>(std::istream& in, std::pair<T1, T2>& p);
template<class T1, class T2> std::istream& operator>>(std::istream& in, RectType<T>& r);
etc.
向量的解析器(流提取器)调用解析器以获取模板类型。
使用这些可以解析整数矩形,双矩形以及字符串和整数对的向量的定义。
是否可以使用Spirit编写为模板类型调用子解析器的模板化解析器?
答案 0 :(得分:5)
由于几乎的其他答案已经明确,因此,在给定属性类型的情况下,Qi已经有了一种动态生成解析器的机制。
这里面向最终用户的位是qi::auto_
。 qi::auto_
is a parser,而不是语法。
这具有明显的优势 [1] 。
qi::locals<>
。 auto_
Qi表达式终端已经定义,因此不需要 at all 使用详细模板参数列表来实例化语法:qi::rule<>
会产生性能开销)让我们看看它是如何使用的:
qi::grammar<>
正如您所看到的,这可以容纳一个队长,并且“神奇地”选择匹配std::vector<std::pair<double, int> > parsed;
bool result_ = qi::phrase_parse(first, last, qi::auto_, qi::space, parsed);
的解析器。现在,要从OP中获取样本格式,您需要 hook into the customization point for the auto_
parser :
parsed
这就是所有需要的。这是一个解析的演示:
namespace boost { namespace spirit { namespace traits {
// be careful copying expression templates. Boost trunk has `qi::copy` for this too, now
#define PARSER_DEF(a) using type = decltype(boost::proto::deep_copy(a)); static type call() { return boost::proto::deep_copy(a); }
template<typename T1, typename T2>
struct create_parser<std::pair<T1, T2> >
{
PARSER_DEF('(' >> create_parser<T1>::call() >> ',' >> create_parser<T2>::call() >> ')');
};
template<typename TV, typename... TArgs>
struct create_parser<std::vector<TV, TArgs...> >
{
PARSER_DEF('[' >> qi::omit[qi::uint_] >> ',' >> '(' >> create_parser<TV>::call() % ',' >> ')' >> ']' );
};
#undef PARSER_DEF
} } }
并将解析后的数据打印为:
VECTOR[ 1 ,
(
PAIR (0.97,
5),
PAIR (1.75,10)
)
]
Parsed:
0.97 5
1.75 10
[1] 潜在的缺点是定制点是固定的,因此您只能关联1 { {1}}任何类型的解析器。滚动您自己的基本模板可以让您获得更多控制,并使您(更多)轻松拥有不同的“解析器风格”。然而,最终它可以充分利用这两个世界,所以我首先是为了方便。
答案 1 :(得分:2)
是的,这是可能的。我会这样实现
#include <boost/spirit/home/qi.hpp>
namespace qi = boost::spirit::qi;
template < typename _Type, typename _Iterator, typename _Enable = void >
struct parser;
template < typename _Type, typename _Iterator >
struct parser < _Type, _Iterator, typename std::enable_if < std::is_arithmetic<_Type> ::value > ::type > :
qi::grammar<_Iterator, _Type() >
{
parser()
: parser::base_type(impl)
{
impl = qi::create_parser<_Type>() ;
}
qi::rule<_Iterator, _Type()> impl;
};
template < typename _Iterator >
struct parser < double, _Iterator> :
qi::grammar<_Iterator, double() >
{
parser()
: parser::base_type(impl)
{
impl = qi::double_;
}
qi::rule<_Iterator, double()> impl;
};
template < typename _First, typename _Second, typename _Iterator >
struct parser < std::pair<_First, _Second>, _Iterator> :
qi::grammar<_Iterator, std::pair<_First, _Second>() >
{
parser()
: parser::base_type(impl)
{
impl = qi::lit('(') >> first >> ',' >> second >> ')';
}
qi::rule<_Iterator, std::pair<_First, _Second>()> impl;
parser<_First, _Iterator> first;
parser<_Second, _Iterator> second;
};
template < typename _Type, typename _Alloc, typename _Iterator >
struct parser < std::vector<_Type, _Alloc>, _Iterator> :
qi::grammar<_Iterator, std::vector<_Type, _Alloc>() >
{
parser()
: parser::base_type(impl)
{
impl = qi::lit('[') >> qi::omit[qi::uint_] >> ",(" >> item % ',' >> ")]";
}
qi::rule<_Iterator, std::vector<_Type, _Alloc>()> impl;
parser<_Type, _Iterator> item;
};
用法示例:
int main(int agrc, char *argv[])
{
typedef std::pair<double, int> pair;
using string = std::string;
string input_ = { "[1,((0.97,5),(1.75,10))]" };
string::const_iterator iterator_ = input_.begin();
string::const_iterator end_ = input_.end();
std::vector<pair> pairs_;
bool result_ = qi::parse(iterator_, end_, parser <std::vector<pair>, string::const_iterator> (), pairs_);
return 0;
}