hmu*_*ner 6 c++ templates boost-spirit parser-generator boost-spirit-qi
我想将一些旧的手写解析代码转换为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 )
Run Code Online (Sandbox Code Playgroud)
解析函数是模板,其中项目的类型作为模板参数,并使用流作为输入,例如
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.
Run Code Online (Sandbox Code Playgroud)
向量的解析器(流提取器)调用解析器以获取模板类型.
使用这些可以解析整数矩形,双矩形以及字符串和整数对的向量的定义.
是否有可能使用Spirit编写模板解析器来调用模板类型的子解析器?
正如另一个答案几乎已经明确的那样,Qi已经有了一种在给定属性类型的情况下动态生成解析器的机制.
这里面向最终用户的是qi::auto_.qi::auto_是一个解析器,而不是语法.
这具有明显的优势[1].
qi::locals<>.auto_齐表达终端已经被定义,因此没有必要在所有使用详细的模板参数列表来实例化一个语法:qi::rule<>并qi::grammar<>导致性能开销)让我们看看它是如何使用的:
std::vector<std::pair<double, int> > parsed;
bool result_ = qi::phrase_parse(first, last, qi::auto_, qi::space, parsed);
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,这可以容纳一个队长,并且"神奇地"选择匹配的解析器parsed.现在,要从OP获取样本格式,您需要挂钩auto_解析器的自定义点:
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
} } }
Run Code Online (Sandbox Code Playgroud)
这就是所有需要的.这是一个解析的演示:
VECTOR[ 1 ,
(
PAIR (0.97,
5),
PAIR (1.75,10)
)
]
Run Code Online (Sandbox Code Playgroud)
并将解析后的数据打印为:
Parsed:
0.97 5
1.75 10
Run Code Online (Sandbox Code Playgroud)
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/home/qi.hpp>
namespace qi = boost::spirit::qi;
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(lexeme [ lit("PAIR") ] >> '(' >> create_parser<T1>::call() >> ',' >> create_parser<T2>::call() >> ')');
};
template<typename TV, typename... TArgs>
struct create_parser<std::vector<TV, TArgs...> >
{
PARSER_DEF(lexeme [ lit("VECTOR") ] >> '[' >> qi::omit[qi::uint_] >> ',' >> '(' >> create_parser<TV>::call() % ',' >> ')' >> ']' );
};
#undef PARSER_DEF
} } }
#include <boost/spirit/home/karma.hpp>
namespace karma = boost::spirit::karma;
int main()
{
std::string const input("VECTOR[ 1 ,\n"
" ( \n"
" PAIR (0.97, \n"
" 5), \n"
" PAIR (1.75,10) \n"
" ) \n"
"]");
std::cout << input << "\n\n";
auto first = input.begin();
auto last = input.end();
std::vector<std::pair<double, int> > parsed;
bool result_ = qi::phrase_parse(first, last, qi::auto_, qi::space, parsed);
if (first!=last)
std::cout << "Remaining unparsed input: '" << std::string(first, last) << "'\n";
if (result_)
std::cout << "Parsed:\n " << karma::format_delimited(karma::auto_ % karma::eol, " ", parsed) << "\n";
else
std::cout << "Parsing did not succeed\n";
}
Run Code Online (Sandbox Code Playgroud)
[1]潜在的缺点是定制点是固定的,因此您只能将1个auto_解析器与任何类型相关联.滚动您自己的基本模板可以让您获得更多控制,并使您(更多)轻松拥有不同的"解析器风格".然而,最终它可以拥有两全其美,所以我首先要方便.