154 lines
5.7 KiB
Plaintext
154 lines
5.7 KiB
Plaintext
[/==============================================================================
|
|
Copyright (C) 2001-2010 Joel de Guzman
|
|
Copyright (C) 2001-2005 Dan Marsden
|
|
Copyright (C) 2001-2010 Thomas Heller
|
|
|
|
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
===============================================================================/]
|
|
|
|
[section Transforming the Expression Tree]
|
|
|
|
This example will show how to write __phoenix_actions__ that transform the
|
|
Phoenix AST.
|
|
|
|
[:
|
|
"/Lisp macros transform the program structure itself, with the full language
|
|
available to express such transformations./"
|
|
|
|
[@http://en.wikipedia.org/wiki/Lisp_macro#Lisp_macros Wikipedia]
|
|
]
|
|
|
|
What we want to do is to invert some arithmetic operators, i.e. plus will be
|
|
transformed to minus, minus to plus, multiplication to division and division to
|
|
multiplication.
|
|
|
|
Let's start with defining our default action:
|
|
|
|
[def __proto_nary_expr__ [@http://www.boost.org/doc/libs/release/doc/html/boost/proto/nary_expr.html `proto::nary_expr`]]
|
|
[def __proto_vararg__ [@http://www.boost.org/doc/libs/release/doc/html/boost/proto/vararg.html `proto::vararg`]]
|
|
[def __proto_when__ [@http://www.boost.org/doc/libs/release/doc/html/boost/proto/when.html `proto::when`]]
|
|
[def __proto_underscore__ [@http://www.boost.org/doc/libs/release/doc/html/boost/proto/_.html `proto::_`]]
|
|
[def __proto_make_expr__ [@http://www.boost.org/doc/libs/release/doc/html/boost/proto/functional/make_expr.html `proto::functional::make_expr`]]
|
|
|
|
struct invert_actions
|
|
{
|
|
template <typename Rule>
|
|
struct when
|
|
: __proto_nary_expr__
|
|
__proto_underscore__
|
|
, __proto_vararg__
|
|
__proto_when__<__proto_underscore__, phoenix::evaluator(__proto_underscore__, phoenix::_context)
|
|
>
|
|
>
|
|
{};
|
|
};
|
|
|
|
Wow, this looks complicated! Granted you need to know a little bit about __proto__
|
|
(For a good introduction read through the
|
|
[@http://cpp-next.com/archive/2010/08/expressive-c-introduction/ Expressive C++] series).
|
|
|
|
By default, we don't want to do anything, well, not exactly nothing, but just
|
|
continue transformation into its arguments.
|
|
|
|
So, it is done by the following:
|
|
|
|
* For each arguments are passed to evaluator (with the current context, that contains our invert_actions)
|
|
* Create new expression using current expression tag, what is done by __proto_underscore__, with the result of evaluated arguments
|
|
|
|
So, after the basics are set up, we can start by writing the transformations we
|
|
want to have on our tree:
|
|
|
|
// Transform plus to minus
|
|
template <>
|
|
struct invert_actions::when<phoenix::rule::plus>
|
|
: __proto_call__<
|
|
__proto_make_expr__<proto::tag::minus>(
|
|
phoenix::evaluator(proto::_left, phoenix::_context)
|
|
, phoenix::evaluator(proto::_right, phoenix::_context)
|
|
)
|
|
>
|
|
{};
|
|
|
|
What is done is the following:
|
|
|
|
* The left expression is passed to evaluator (with the current context, that contains our invert_actions)
|
|
* The right expression is passed to evaluator (with the current context, that contains our invert_actions)
|
|
* The result of these two __proto_transforms__ are passed to __proto_make_expr__ which returns the freshly created expression
|
|
|
|
After you know what is going on, maybe the rest doesn't look so scary anymore:
|
|
|
|
// Transform minus to plus
|
|
template <>
|
|
struct invert_actions::when<phoenix::rule::minus>
|
|
: __proto_call__<
|
|
__proto_make_expr__<proto::tag::plus>(
|
|
phoenix::evaluator(proto::_left, phoenix::_context)
|
|
, phoenix::evaluator(proto::_right, phoenix::_context)
|
|
)
|
|
>
|
|
{};
|
|
|
|
// Transform multiplies to divides
|
|
template <>
|
|
struct invert_actions::when<phoenix::rule::multiplies>
|
|
: __proto_call__<
|
|
__proto_make_expr__<proto::tag::divides>(
|
|
phoenix::evaluator(proto::_left, phoenix::_context)
|
|
, phoenix::evaluator(proto::_right, phoenix::_context)
|
|
)
|
|
>
|
|
{};
|
|
|
|
// Transform divides to multiplies
|
|
template <>
|
|
struct invert_actions::when<phoenix::rule::divides>
|
|
: __proto_call__<
|
|
__proto_make_expr__<proto::tag::multiplies>(
|
|
phoenix::evaluator(proto::_left, phoenix::_context)
|
|
, phoenix::evaluator(proto::_right, phoenix::_context)
|
|
)
|
|
>
|
|
{};
|
|
|
|
That's it! Now that we have our actions defined, we want to evaluate some of our expressions with them:
|
|
|
|
template <typename Expr>
|
|
// Calculate the result type: our transformed AST
|
|
typename boost::result_of<
|
|
phoenix::evaluator(
|
|
Expr const&
|
|
, phoenix::result_of::context<int, invert_actions>::type
|
|
)
|
|
>::type
|
|
invert(Expr const & expr)
|
|
{
|
|
return
|
|
// Evaluate it with our actions
|
|
phoenix::eval(
|
|
expr
|
|
, phoenix::context(
|
|
int()
|
|
, invert_actions()
|
|
)
|
|
);
|
|
}
|
|
|
|
Run some tests to see if it is working:
|
|
|
|
invert(_1); // --> _1
|
|
invert(_1 + _2); // --> _1 - _2
|
|
invert(_1 + _2 - _3); // --> _1 - _2 + _3
|
|
invert(_1 * _2); // --> _1 / _2
|
|
invert(_1 * _2 / _3); // --> _1 / _2 * _3
|
|
invert(_1 * _2 + _3); // --> _1 / _2 - _3
|
|
invert(_1 * _2 - _3); // --> _1 / _2 + _2
|
|
invert(if_(_1 * _4)[_2 - _3]); // --> if_(_1 / _4)[_2 + _3]
|
|
_1 * invert(_2 - _3)); // --> _1 * _2 + _3
|
|
|
|
__note__ The complete example can be found here: [@../../example/invert.cpp example/invert.cpp]
|
|
|
|
/Pretty simple .../
|
|
|
|
[endsect]
|