Boost logo

Boost Users :

Subject: Re: [Boost-users] [Proto] implementing an computer algebra system with proto
From: Kim Kuen Tang (kuentang_at_[hidden])
Date: 2009-02-09 16:13:42


Hi Dave and Eric,

i have written an grammar to do some transformations on an expression
and would like to know your opinions.

If the grammar matches the pattern var_+a, then it should transform the
tree to var_=(0-a)/1.
So the resulting callable transform would be :

proto::when<
            //var_+a
            proto::plus<proto::terminal<placeholder>,
proto::terminal<proto::_> >
            ,proto::_make_assign(
                proto::_left
                ,proto::_make_divides(proto::_make_minus(Zero
,proto::_right),One )
            )

How should one implement the two macros Zero and One. My solution is to
implement a callable transform like this
struct ReturnAsTerminal : proto::callable
{
    template<class Sig>
    struct result;

    template<class This, class T>
    struct result<This(T)>
        : proto::result_of::as_expr<typename
boost::remove_reference<T>::type >
    {};

    template<class T>
    typename result< ReturnAsTerminal(T const&) >::type
    operator()(T const& t) const
    {
        return proto::as_expr(t);
    }
};

with

#define Zero ReturnAsTerminal(mpl::int_<0>() )
#define One ReturnAsTerminal(mpl::int_<1>() )

But is this implementation necessary? Can we use predefined functions in
proto to reduce the number of codes?

My second question regards the issue of testing the correctness of my
transformation.
I understand that with proto::matches i am able to test whether a given
expression type matches a grammar. But what if i want to test the
correctness of my transformation?

Is the transformation of the above code really var_=(0-a)/1 and not
something else?

To test the correctness i use boost::is_same to test the type of the
transformation.
BOOST_PROTO_AUTO( expr ,var_+a );
BOOST_PROTO_AUTO( result,
var_=(ReturnAsTerminal()(mpl::int_<0>())-a)/ReturnAsTerminal()(mpl::int_<1>()));
BOOST_MPL_ASSERT( (boost::is_same<
boost::result_of<BeginLeft(BOOST_TYPEOF(expr))>::type,
BOOST_TYPEOF(result)>) );

Is this enough and is this necessary?

Here is the full program:

#include <iostream>

#include <boost/proto/proto.hpp>
#include <boost/proto/proto_typeof.hpp>

#include <boost/mpl/int.hpp>

#include <boost/type_traits/remove_reference.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/typeof/typeof.hpp>

namespace mpl=boost::mpl;
namespace proto=boost::proto;
namespace fusion=boost::fusion;

struct placeholder
{
    friend std::ostream& operator<<(std::ostream& out, placeholder)
    {
        return out<<"var_";
    }
};

proto::terminal<placeholder>::type const var_={{}};
proto::terminal<char>::type const a={'a'};

#if BOOST_WORKAROUND( BOOST_MSVC,BOOST_TESTED_AT(1500) )
#define _left(x) call<proto::_left( proto::call<x> )>
#define _right(x) call<proto::_right( proto::call<x> )>
#define _make_minus(x,y) call<proto::_make_minus(proto::call<x>,
proto::call<y> )>
#define _make_plus(x,y) call<proto::_make_plus(proto::call<x>,
proto::call<y>)>
#define _make_assign(x,y) call<proto::_make_assign(proto::call<x>,
proto::call<y>)>
#define _make_divides(x,y) call<proto::_make_divides(proto::call<x>,
proto::call<y>)>
#define _make_multiplies(x,y)
call<proto::_make_multiplies(proto::call<x>, proto::call<y>)>
#define _make_terminal(x) call<proto::_make_terminal(proto::call<x>)>
#endif

struct ReturnAsTerminal : proto::callable
{
    template<class Sig>
    struct result;

    template<class This, class T>
    struct result<This(T)>
        : proto::result_of::as_expr<typename
boost::remove_reference<T>::type >
    {};

    template<class T>
    typename result< ReturnAsTerminal(T const&) >::type
    operator()(T const& t) const
    {
        return proto::as_expr(t);
    }
};

#define Zero ReturnAsTerminal(mpl::int_<0>() )
#define One ReturnAsTerminal(mpl::int_<1>() )
//

struct BeginLeft
    : proto::or_<
        proto::when<
            //var_+a
            proto::plus<proto::terminal<placeholder>,
proto::terminal<proto::_> >
            ,proto::_make_assign(
                proto::_left
                ,proto::_make_divides(proto::_make_minus(Zero
,proto::_right),One )
            )
>
        ,proto::when<
            //var_-a
            proto::minus<proto::terminal<placeholder>,
proto::terminal<proto::_> >
            ,proto::_make_assign(
                proto::_left
                
,proto::_make_divides(proto::_make_plus(Zero,proto::_right),One )
            )
>
        ,proto::when<
        //var_*a
            proto::multiplies< proto::terminal<placeholder>,
proto::terminal<proto::_> >
            ,proto::_make_assign(
                proto::_right
                ,proto::_make_divides(Zero,proto::_right )
            )
>
        ,proto::when<
        //var_/a
            proto::divides< proto::terminal<placeholder>,
proto::terminal<proto::_> >
            ,proto::_make_assign(
                proto::_left
                
,proto::_make_divides(Zero,proto::_make_divides(One,proto::_right) )
            )
>
>
{};

int main()
{

    BOOST_PROTO_AUTO( expr ,var_+a );
    BOOST_PROTO_AUTO( result,
var_=(ReturnAsTerminal()(mpl::int_<0>())-a)/ReturnAsTerminal()(mpl::int_<1>()));
    BOOST_MPL_ASSERT( (proto::matches<BOOST_TYPEOF(expr) ,BeginLeft>) );
    BOOST_MPL_ASSERT( (boost::is_same<
boost::result_of<BeginLeft(BOOST_TYPEOF(expr))>::type,
BOOST_TYPEOF(result)>) );

    std::cout<<" \n ----------------------------------------------------
\n";
    return 0;
}

Thanks for any and all help and comments.

Cheers,
Kim


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net