Этот пример покажет, как писатьДействия, преобразующие АСТ Феникса.
"Макросы Lisp трансформируют саму структуру программы, используя весь язык для выражения таких преобразований.".
Википедия
Мы хотим перевернуть некоторые арифметические операторы, т. е. плюс будет преобразован в минус, минус в плюс, умножение на деление и деление на умножение.
Давайте начнем с определения наших действий по умолчанию:
struct invert_actions
{
template <typename Rule>
struct when
: proto::_
{};
};
По умолчанию мы не хотим ничего делать, ну, не совсем ничего, а просто возвращаем выражение. Это делаетсяпрото::, которая, будучи преобразованной, просто передает текущее выражение. Сделайте это действие преобразованием идентичности.
Итак, после того, как основы установлены, мы можем начать с написания преобразований, которые мы хотим иметь на нашем дереве:
template <>
struct invert_actions::when<phoenix::rule::plus>
: proto::call
<
proto::functional::make_expr
<proto::tag::minus>(
phoenix::evaluator(proto::_left, phoenix::_context)
, phoenix::evaluator(proto::_right, phoenix::_context)
)
>
{};
Вау, это выглядит сложно! Конечно, вам нужно знать немного оBoost.Proto(Для хорошего введения прочитайте сериюExpressive C++).
Что делается, так это следующее:
- Левое выражение передается оценщику (с текущим контекстом, который содержит наши инвертные действия).
- Правильное выражение передается оценщику (с текущим контекстом, который содержит наши инвертные действия).
- Результат этих двухПротопревращенийпередается<
proto::functional::make_expr
>, который возвращает вновь созданное выражение.
После того, как вы знаете, что происходит, может быть, все остальное уже не так страшно:
template <>
struct invert_actions::when<phoenix::rule::minus>
: proto::call
<
proto::functional::make_expr
<proto::tag::plus>(
phoenix::evaluator(proto::_left, phoenix::_context)
, phoenix::evaluator(proto::_right, phoenix::_context)
)
>
{};
template <>
struct invert_actions::when<phoenix::rule::multiplies>
: proto::call
<
proto::functional::make_expr
<proto::tag::divides>(
phoenix::evaluator(proto::_left, phoenix::_context)
, phoenix::evaluator(proto::_right, phoenix::_context)
)
>
{};
template <>
struct invert_actions::when<phoenix::rule::divides>
: proto::call
<
proto::functional::make_expr
<proto::tag::multiplies>(
phoenix::evaluator(proto::_left, phoenix::_context)
, phoenix::evaluator(proto::_right, phoenix::_context)
)
>
{};
Вот так! Теперь, когда мы определили наши действия, мы хотим оценить некоторые из наших выражений с ними:
template <typename Expr>
typename boost::result_of<
phoenix::evaluator(
Expr const&
, phoenix::result_of::context<int, invert_actions>::type
)
>::type
invert(Expr const & expr)
{
return
phoenix::eval(
expr
, phoenix::context(
int()
, invert_actions()
)
);
}
Проведите несколько тестов, чтобы увидеть, работает ли он:
invert(_1);
invert(_1 + _2);
invert(_1 + _2 - _3);
invert(_1 * _2);
invert(_1 * _2 / _3);
invert(_1 * _2 + _3);
invert(_1 * _2 - _3);
invert(if_(_1 * _4)[_2 - _3]);
_1 * invert(_2 - _3));
Полный пример можно найти здесь:example/invert.cpp
Довольно просто...