我有很多看起来像这样的规则:
cmd_BC = (dlm > timestamp > dlm > cid > dlm > double_)
[
_val = lazy_shared<dc::BoardControl>(_1, _2, _3)
];
我想让它更具可读性,例如:
cmd_BC = param(timestamp) > param(cid) > param(double_)
甚至
cmd_BC = params(timestamp, cid, double_)
正如sehe指出的,它归结为有一些方法可以自动期望分隔符。这里有什么选择?我自己,我看到三种可能性,都是有缺陷的:
写一个包装函数。我试着运气不好以下代码:
template <typename T>
auto param(const T & parser) -> decltype(qi::lit(dlm) > parser)
{
return qi::lit(dlm) > parser;
}
但它没有编译,在
失败 // report invalid argument not found (N is out of bounds)
BOOST_SPIRIT_ASSERT_MSG(
(N < sequence_size::value),
index_is_out_of_bounds, ());
我也试过return (...).alias()
,但它也没有编译。
答案 0 :(得分:2)
这个解决方案不是很“精神-y”,不幸的是“需要C ++ 11”(我不知道如何在c ++ 03中获得所需的结果类型),但似乎有效。灵感来自示例here。
PS:哦,我没看到你的编辑。你有几乎相同的例子。
更新:使用带有_1,_2和_3
的语义操作添加了另一个测试
Update2:在注释中的葡萄藤建议后更改了operator()和operator []的签名。
更新3:添加了一个构造组合解析器的可变运算符(),并在使用boost :: proto找到更好的解决方案后删除了operator []。稍微改变了一些例子。
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/proto/proto.hpp>
namespace qi = boost::spirit::qi;
namespace proto = boost::proto;
namespace phx = boost::phoenix;
using namespace boost::proto;
//This is a proto grammar/transform that creates the prefixed parser. The parser created depends on the parser passed (if it's a kleene or not)
// in _make_xxx "_" corresponds to the supplied parser and "_state" to the delimiter
struct CreatePrefixedParser: //you can use _make_greater instead of _make_shift_right if you want to use "expectation"
or_ <
when < dereference<_>, //If it's a kleene parser...
_make_shift_right ( //create the parser -> dlm >> *(parser -dlm)
_state,
_make_dereference (
_make_minus ( _child_c<0> ( _ ),
_state ) ) ) > ,
when < unary_plus<_>, //If it's a +parser
_make_shift_right ( //create the parser -> dlm >> +(parser -dlm)
_state,
_make_unary_plus (
_make_minus ( _child_c<0> ( _ ),
_state ) ) ) > ,
otherwise < //if it's any other parser
_make_shift_right ( //create the parser -> dlm >> (parser -dlm)
_state,
_make_minus ( _,
_state ) ) >
> {};
//-------------------------------------------------------------
//this combines the parsers this way: parser1, parser2, parser3, parser4 -> parser1>>(parser2 >>(parser3 >> parser4)))
//you can use make_expr<tag::greater> if you want to use "expectation"
//I have absolutely no idea when "deep_copy" is required but it seems to work this way
template<typename Delim, typename First, typename ... Rest>
struct myparser
{
static auto combine ( Delim dlm_, const First& first, const Rest&...rest ) ->
decltype ( make_expr<tag::shift_right> ( CreatePrefixedParser() ( deep_copy ( first ), dlm_ ), myparser<Delim, Rest...>::combine ( dlm_, rest... ) ) )
{
return make_expr<tag::shift_right> ( CreatePrefixedParser() ( deep_copy ( first ), dlm_ ), myparser<Delim, Rest...>::combine ( dlm_, rest... ) );
}
};
template<typename Delim, typename Last>
struct myparser<Delim, Last>
{
static auto combine ( Delim dlm_, const Last& last ) -> decltype ( CreatePrefixedParser() ( deep_copy ( last ), dlm_ ) )
{
return CreatePrefixedParser() ( deep_copy ( last ), dlm_ );
}
};
//-----------------------------------------------------------------
template <typename T>
struct prefixer
{
T dlm_;
prefixer ( T dlm ) : dlm_ ( dlm ) {}
template <typename ... Args>
auto operator() ( const Args&... args ) ->
decltype ( deep_copy ( myparser<T, Args...>::combine ( dlm_, args... ) ) )
{
return deep_copy ( myparser<T, Args...>::combine ( dlm_, args... ) );
}
};
template <typename T>
prefixer<T> make_prefixer ( T dlm )
{
return prefixer<T> ( dlm );
}
int main()
{
std::string test = "lameducklamedog";
std::string::const_iterator f ( test.begin() ), l ( test.end() );
auto param = make_prefixer ( qi::lit ( "lame" ) );
qi::rule<std::string::const_iterator> dog = qi::lit ( "do" ) > qi::char_ ( 'g' );
//qi::rule<std::string::const_iterator> duck = qi::lit ( "duck" ) | qi::int_;
qi::rule<std::string::const_iterator,std::string()> quackdog = (param (*qi::alpha) >> param( dog ));
std::string what;
if ( qi::parse ( f, l, quackdog, what ) && f == l )
std::cout << "the duck and the dog are lame, specially the " << what << std::endl;
else
std::cerr << "Uhoh\n" << std::string(f,l) << std::endl;
test = "*-*2.34*-*10*-*0.16*-*12.5";
std::string::const_iterator f2 ( test.begin() ), l2 ( test.end() );
auto param2 = make_prefixer ( qi::lit ( "*-*" ) );
double d;
qi::rule<std::string::const_iterator> myrule = ( param2 ( qi::double_, qi::int_, qi::double_ , qi::double_) ) [phx::ref ( d ) = qi::_1 + qi::_2 + qi::_3 + qi::_4];
if ( qi::parse ( f2, l2, myrule ) && f2 == l2 )
std::cout << "the sum of the numbers is " << d << std::endl;
else
std::cerr << "Uhoh\n";
}
答案 1 :(得分:1)
如果我理解正确,您正在寻找一种方法自动期望或忽略分隔符表达式(dlm
)?
这是精神中船长的经典地形。如果分隔符是可变的,例如,这是特别有用的。空白(可接受的空白量不等);
bool ok = qi::phrase_parse(
first, last, // input iterators
timestamp > cid > double_, // just specify the expected params
qi::space); // delimiter, e.g. any amount of whitespace
请注意使用phrase_parse
启用带有队长的语法。
%
解析器指令你可以明确地去划分语法:
dlm = qi::lit(','); // as an example, delimit by single comma
rule = timestamp > dlm > cid > dlm > double_;
这很乏味。可能对您有用的东西(取决于应该执行的输入验证的数量:
dlm = qi::lit(','); // as an example, delimit by single comma
rule = (timestamp | cid | double_) % dlm;
(这将产生variant<timestampt_t, cid_t, double>
)
您可以滚动自己的解析器指令,类似于karma::delimit
,但是用于输入。
Hartmut Kaiser在本文档文章中概述了这个想法:
如果你有兴趣,我可以看看我是否可以将这项工作作为一个例子(我以前没用过)。说实话,我很惊讶这样的东西还不存在, 我认为它会成为Spirit Repository
答案 2 :(得分:1)
这是我最终满意的解决方案。它基于这三个答案:
我决定不接受任意二元仿函数,因为我怀疑它在解析的上下文中会有任何实际目的。所以,
#include <boost/proto/deep_copy.hpp>
template <typename D>
struct prefixer
{
template<typename... T>
struct TypeOfPrefixedExpr;
template<typename T>
struct TypeOfPrefixedExpr<T>
{
typedef typename boost::proto::result_of::deep_copy
< decltype ( std::declval<D>() > std::declval<T>() ) >::type type;
};
template<typename T, typename... P>
struct TypeOfPrefixedExpr<T, P...>
{
typedef typename boost::proto::result_of::deep_copy
< decltype ( std::declval<D>() > std::declval<T>()
> std::declval<typename TypeOfPrefixedExpr<P...>::type>() ) >::type type;
};
D dlm_;
prefixer ( D && dlm ) : dlm_ ( dlm ) {}
template <typename U>
typename TypeOfPrefixedExpr<U>::type operator() (U && parser )
{
return boost::proto::deep_copy ( dlm_ > parser );
}
template <typename U, typename ... Tail>
typename TypeOfPrefixedExpr<U, Tail...>::type
operator() (U && parser, Tail && ... tail )
{
return boost::proto::deep_copy ( dlm_ > parser > (*this)(tail ...) );
}
};
template <typename D>
prefixer<D> make_prefixer ( D && dlm )
{
return prefixer<D> ( std::forward<D>(dlm) );
}
它的使用方式如下:
auto params = make_prefixer(qi::lit(dlm));
cmd_ID = params(string) [ _val = lazy_shared<dc::Auth> (_1) ];
cmd_NAV = params(timestamp, double_, double_, double_, double_, double_)
[
_val = lazy_shared<dc::Navigation>( _1, _2, _3, _4, _5, _6 )
];
cmd_BC = params(timestamp, cid, double_)
[
_val = lazy_shared<dc::BoardControl>(_1, _2, _3)
];