У меня есть много правил, которые выглядят так:
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()
, но он тоже не компилировался.
Это решение не очень «дух-у» и, к сожалению, «требует C ++ 11» (я не уверен, как получить требуемый тип результата в C ++ 03), но, похоже, оно работает. Вдохновленный примером Вот.
PS: О, я не видел твою правку. У вас есть почти тот же пример.
Обновление: добавлен еще один тест с использованием семантического действия с _1, _2 и _3
Обновление 2: изменена подпись operator () и operator [], следуя советам лоз в комментариях.
Обновление 3: добавлен оператор variadic (), который создает комбинированный синтаксический анализатор, и удален оператор [] после нахождения лучшего решения с boost :: proto. Немного изменились примеры.
#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";
}
Если я правильно понимаю, вы ищете способ автоматически ожидать или игнорировать выражение разделителя (dlm
)?
Это классическая местность для Skippers in Spirit. Это особенно полезно, если разделитель является переменным, например, пробелы (допустимо различное количество пробелов);
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
, но для ввода.
Идея изложена в этой статье документации Хартмута Кайзера:
Если вам интересно, я могу посмотреть, смогу ли я сделать эту работу в качестве примера (я не использовал это раньше). Честно говоря, я удивлен, что что-то подобное еще не существует, и Я думаю, что это будет основным кандидатом на Духовное хранилище
Вот решение, которым я, наконец, доволен. Это основано на этих трех ответах:
Я решил не принимать его произвольными двоичными функторами, так как сомневаюсь, что это будет иметь какую-либо реальную цель в контексте анализа. Так,
#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)
];