正如另一个答案几乎已经阐明的那样,Qi 已经有了一种机制,可以在给定属性类型的情况下动态生成解析器。
这里面向最终用户的位是qi::auto_。 qi::auto_ is a parser,而不是 语法。
这具有明显的优势[1]。
- 最重要的是,它允许用户通过自己选择的skipper 或
qi::locals<> 在语法中使用解析器。
- 另外,
auto_ Qi 表达式终端已经定义,因此完全不需要使用详细的模板参数列表来实例化语法:
- 最后,解析器返回一个表达式模板,因此不会进行类型擦除,因此以这种方式组合多个 auto_ 解析器的效率不低于手动编写语法(而两者都包装在
qi::rule<> 和qi::grammar<> 会产生性能开销)
让我们看看它是如何使用的:
std::vector<std::pair<double, int> > parsed;
bool result_ = qi::phrase_parse(first, last, qi::auto_, qi::space, parsed);
如您所见,这可以容纳一个船长,并“神奇地”选择匹配parsed 的解析器。现在,要从 OP 获取示例格式,您需要 hook into the customization point for the auto_ parser:
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
看Live On Coliru
完整代码清单
#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";
}
[1] 一个潜在的缺点是自定义点是固定的,因此您只能关联 1 个 @ 987654337@ 任何类型的解析器。滚动您自己的基本模板为您提供更多控制权,并使您能够(更)轻松地拥有不同的“解析器风格”。但是,最终有可能两全其美,所以我会先去方便。