Why doesn't the following Boost.Proto
grammar match dereference operator, and what's the
correct way to do this?
#include <iostream>
#include <boost/proto/proto.hpp>
namespace proto = boost::proto;
using proto::_;
struct has_deref : proto::or_<
proto::dereference<_>,
proto::nary_expr<_, proto::vararg<has_deref>>
>
{};
template<class Expr>
void test_expr(const Expr &e)
{
proto::display_expr(e);
std::cout << "matches? " << std::boolalpha << proto::matches<Expr,
has_deref>::value << std::endl;
}
int main()
{
test_expr(proto::lit(1) + *proto::lit(2));
}
According to your definition your has_deref
grammar matches expressions that are either:
complement
, negate
or unary_plus
; binary like: subscript
or plus
; ternary like if_else_
, or directly n-ary like function
) expression whose arguments recursively match the has_deref
grammar.In your example you have a plus<terminal<int>,dereference<terminal<int> > >
which is something like binary_expr<tag::plus,terminal<int>,unary_expr<tag::dereference,terminal<int> > >
. When trying to match your expression (I think) Proto tries first the first element of your grammar (dereference<_>
) and obviously fails. Then tries the second one and binary_expr<tag::plus,...>
matches nary_expr<_,...>
and so it recursively tries to match the grammar to both argument expressions of the plus
. The second one matches directly, but the first one (terminal<int>
) does not match either of the possibilities and so the whole expression fails to match.
One possible (and sadly clunky) approach to do what you want could be something like:
struct has_deref : proto::or_< //ignoring if_then_else and function
proto::dereference<_>,
proto::unary_expr<_, has_deref>,
proto::binary_expr<_, has_deref, _>,
proto::binary_expr<_, _, has_deref>
>
{};
that matches expressions that are either:
has_deref
.has_deref
and its second is ANY expression (including expressions that match has_deref
) .has_deref
and its second is one that does.Here is an example that checks that several expressions match this new grammar (extended to also check for functions with at most 2 arguments):
#include <iostream>
#include <boost/proto/proto.hpp>
namespace proto = boost::proto;
using proto::_;
template <typename Grammar>
struct function_contains_at_least_one : proto::or_<//this is awful, there should be a better way
proto::function<Grammar>,
proto::function<Grammar,_>,
proto::function<_,Grammar>,
proto::function<Grammar,_,_>,
proto::function<_,Grammar,_>,
proto::function<_,_,Grammar>
>
{};
struct has_deref : proto::or_< //ignoring if_else_
proto::dereference<_>,
proto::unary_expr<_,has_deref>,
proto::binary_expr<_, _,has_deref>,
proto::binary_expr<_,has_deref,_>,
function_contains_at_least_one<has_deref>
>
{};
template<class Expr>
void test_expr(const std::string& repr, const Expr &e)
{
std::cout << repr << " matches 'has_deref'? " << std::boolalpha << proto::matches<Expr,
has_deref>::value << std::endl;
//display_expr(e);
}
#define TEST_EXPR( EXPR ) test_expr(#EXPR,EXPR)
int main()
{
using proto::lit;
TEST_EXPR(lit(1) + lit(2));
TEST_EXPR(lit(1) + *lit(2));
TEST_EXPR(*lit(1) + *lit(2));
TEST_EXPR(*(lit(1) * (lit("foo")+lit(2))));
TEST_EXPR(+-*lit(1)[~*lit(2)++]);
//testing functions
TEST_EXPR( lit(1)() );
TEST_EXPR( (*lit(1))() );
TEST_EXPR( lit(1)(lit(2)) );
TEST_EXPR( (*lit(1))(lit(2)) );
TEST_EXPR( lit(1)(*lit(2)) );
TEST_EXPR( lit(1)(lit(2),lit(3)) );
TEST_EXPR( (*lit(1))(*lit(2),*lit(3)) );
}