Hi Andrew, just my thoughts on this chat. Andrew Durward schrieb:
The central concept is that for any expression (a # b) where # is {+, -, *}, the result can be expressed as (x + y) where x is the floating-point
How about transform the expression a+b to sum_approx(a,b)+sum_diff(a,b) instead to fusion::cons<sum_approx(a,b),fusion::cons<sum_diff(a,b)> > ? The advantage of this approach is that you still get an ast where childs are held in terminal (instead in cons).
approximation of (a # b), and y is an error term whose value can also be computed as a floating-point value.
In toying with proto, I've been able to transform simple expressions successfully but I can't seem to figure out how to define a grammar that will handle arbitrary combinations of {+, -, *} recursively. Here's what I've got so far:
struct sum_approx{/* */}; struct sum_error{/* */};
struct diff_approx{/* */}; struct diff_error{/* */};
struct prod_approx{/* */}; struct prod_error{/* */};
struct Transform : proto::or_< proto::when< proto::plus< proto::terminal< _ >, proto::terminal< _ > > , fusion::cons< proto::_make_function( sum_approx() , proto::_left , proto::_right )
I dont see the meaning behind this step. If you use proto::_make_function you create a child in the ast, but you put this child in a fusion::cons.
, fusion::cons< proto::_make_function( sum_error() , proto::_left , proto::_right ) > >() > // ... similar substitutions for minus and multiplies > {};
So the expression (a + b) yields the sequence: sum_approx(a,b) sum_error(a,b)
Here's where I'm stuck. In the case of the expression (a + b)*(c - d), the addition and subtraction should be replaced by the appropriate sequences before the multiplication gets expanded to yield the final sequence: prod_approx( sum_approx(a,b), diff_approx(c,d) ) prod_error( sum_approx(a,b), diff_approx(c,d) ) prod_approx( sum_approx(a,b), diff_error(c,d) ) prod_error( sum_approx(a,b), diff_error(c,d) ) prod_approx( sum_error(a,b), diff_approx(c,d) ) prod_error( sum_error(a,b), diff_approx(c,d) ) prod_approx( sum_error(a,b), diff_error(c,d) ) prod_error( sum_error(a,b), diff_error(c,d) )
Are you sure that you want to get a sequence of expression instead an ast. In case you really want to get a sequence you can use proto::tag::comma to separate your child.
Obviously I need a recursive transform here but I'm not sure how to pass it the sequences generated by both the left and right subtrees.
Cheers, Kim Here is a toy project. Hth. # include <boost/cstdlib.hpp> # include <boost/proto/proto.hpp> # include <boost/fusion/container/list/cons.hpp> # include <boost/proto/tags.hpp> # include <boost/proto/make_expr.hpp> # define PRECISION_MAKE_tag(name) \ struct name \ { \ friend std::ostream& operator<< (std::ostream& s, name const&) \ { \ return s<< #name; \ } \ }; # define PRECISION_MAKE_terminal(name) \ proto::terminal< tag::name >::type const name = {{}} namespace proto = boost::proto; namespace tag{ PRECISION_MAKE_tag(a); PRECISION_MAKE_tag(b); PRECISION_MAKE_tag(c); PRECISION_MAKE_tag(d); PRECISION_MAKE_tag(sum_approx); PRECISION_MAKE_tag(sum_error); PRECISION_MAKE_tag(diff_approx); PRECISION_MAKE_tag(diff_error); } namespace mpl = boost::mpl; namespace fusion = boost::fusion; using proto::_; struct transform { BOOST_PROTO_CALLABLE() template<typename Sig> struct result; template<typename This,typename A0,typename A1> struct result<This(proto::tag::plus const,A0,A1)> : proto::result_of::make_expr< proto::tag::plus , typename proto::result_of::make_expr< proto::tag::function ,tag::sum_approx, A0, A1 >::type , typename proto::result_of::make_expr< proto::tag::function ,tag::sum_error, A0, A1 >::type > {}; template<typename This,typename A0,typename A1> struct result<This(proto::tag::minus const,A0,A1)> : proto::result_of::make_expr< proto::tag::minus, A0, A1 > {}; template<typename A0,typename A1> typename result<transform(proto::tag::plus const ,A0 const&, A1 const&)>::type operator()(proto::tag::plus const ,A0 const& a0,A1 const& a1) const { return proto::make_expr<proto::tag::plus>( proto::make_expr<proto::tag::function>( tag::sum_approx() , boost::ref(a0) , boost::ref(a1)) ,proto::make_expr<proto::tag::function>( tag::sum_error() , boost::ref(a0) , boost::ref(a1)) ); //return proto::make_expr<proto::tag::plus>(boost::ref(a0), boost::ref(a1)); }; template<typename A0, typename A1> typename result<transform(proto::tag::minus const ,A0 const&, A1 const&)>::type operator()(proto::tag::minus const ,A0 const& a0, A1 const& a1) const { return proto::make_expr<proto::tag::minus>(boost::ref(a0), boost::ref(a1)); }; }; struct Transform : proto::or_< proto::when< proto::plus< proto::terminal< _ >, proto::terminal< _ > > , transform( proto::tag::plus() , proto::_value(proto::_left) , proto::_value(proto::_right) ) > ,proto::when< proto::minus< proto::terminal< _ >, proto::terminal< _ > > , transform( proto::tag::minus() , proto::_value(proto::_left) , proto::_value(proto::_right) ) > , proto::otherwise<proto::nary_expr<_, proto::vararg<Transform> > > > {}; PRECISION_MAKE_terminal(a); PRECISION_MAKE_terminal(b); PRECISION_MAKE_terminal(c); PRECISION_MAKE_terminal(d); # undef PRECISION_MAKE_tag # undef PRECISION_MAKE_terminal int main() { proto::display_expr(a+b); BOOST_AUTO(expr, Transform()( (a+b)*(c-d) ) ); proto::display_expr(expr); return boost::exit_success; }