On 10/25/2010 9:26 AM, Eric Niebler wrote:
> On 10/25/2010 4:44 AM, Thomas Heller wrote:
>> Thank you very much! So, we are good to changing the internals of
>> phoenix3 to use this extension mechanism?
> 
> Yes. But today I'm going to made some changes, based on my experience
> playing with this code last night. In particular, it should be possible
> to have *only* the named rules be customization points; for all other
> rules, actions are attached to rules the old way via proto::when. I
> think that if we pass the Actions bundle as the Data parameter to the
> transforms, these things become possible.

Oh /man/ this makes things so much simpler. By passing the actions at
the data parameter, not only do we get the above benefits, but we don't
need to parameterize the actions on any action sub-classes. The
implementation is totally trivial and very simple:

Here is a grammar with actions, some of which are specified externally:

    struct my_grammar
      : proto::or_<
            proto::when< int_terminal, proto::external >
          , proto::when< char_terminal, proto::external >
          , proto::when<
                proto::plus< my_grammar, my_grammar >
              , proto::fold< _, int(), my_grammar >
            >
        >
    {};

Duh, right? Now you define the actions for the two customization points:

    struct my_actions
    {
        template <typename Grammar>
        struct when;
    };

    template<>
    struct my_actions::when<int_terminal>
      : proto::call<print(proto::_value)>
    {};

    template<>
    struct my_actions::when<char_terminal>
      : proto::call<print(char(boost::mpl::char_<'x'>()))>
    {};

You evaluate expressions by passing them to my_grammar per usual,
passing an instance of my_actions as the (little used) data parameter:

    my_actions act;
    my_grammar()(a + i, 0, act);

Voila! The implementation is trivial: one specialization of proto::when
on the new (incomplete) proto::external type. God, why didn't I think of
this sooner?

The naming issue goes away completely. There is no fancy new proto
transform to be named. Also, proto::named_rule goes away, too.

I'm attaching Thomas' (now laughably trivial) traversal_proto.cpp
example, along with my mini Phoenix, both ported to this new scheme. The
mini-Phoenix takes the extra step of bundling the actions with the
parameters into the phoenix environment (doing the unpacking/repacking
under the covers as necessary).

One potential further simplification would be to give users a nicer way
to map rules to actions. I'll think about it.

-- 
Eric Niebler
BoostPro Computing
http://www.boostpro.com
#include <boost/proto/proto.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/include/make_vector.hpp>
#include <iostream>

#ifdef _MSC_VER
#pragma warning(disable: 4355)
#endif

namespace boost { namespace proto
{
    struct external;

    template<typename Grammar>
    struct when<Grammar, external>
    {
        typedef typename Grammar::proto_grammar proto_grammar;

        template<typename Expr, typename State, typename Data>
        struct impl
          : Data::template when<Grammar>::template impl<Expr, State, Data>
        {};

        template<typename Expr, typename State, typename Data>
        struct impl<Expr, State, Data &>
          : Data::template when<Grammar>::template impl<Expr, State, Data &>
        {};
    };
}}

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

// A transform that packs the state and data parameter
struct _phoenix_env
  : proto::transform<_phoenix_env>
{
    template<typename Expr, typename State, typename Data>
    struct impl
      : proto::transform_impl<Expr, State, Data>
    {
        typedef fusion::vector2<State, Data> result_type;

        result_type operator()(
            typename impl::expr_param
          , typename impl::state_param s
          , typename impl::data_param d
        ) const
        {
            return fusion::vector2<State, Data>(s, d);
        };
    };
};

namespace functional
{
    template<typename N, typename Callable = proto::callable>
    struct at
      : proto::callable
    {
        typedef typename boost::remove_reference<N>::type index;

        template<typename Sig>
        struct result;

        template<typename This, typename Seq>
        struct result<This(Seq)>
          : fusion::result_of::at_c<
                typename boost::remove_reference<Seq>::type
              , index::value
            >
        {};

        template<typename Seq>
        typename result<at(Seq &)>::type
        operator()(Seq &seq) const
        {
            return fusion::at_c<index::value>(seq);
        }
    };

    typedef at<mpl::int_<0> > params;
    typedef at<mpl::int_<1> > actions;

    struct param_at
      : proto::callable
    {
        template<typename Sig>
        struct result;

        template<typename This, typename N, typename Env>
        struct result<This(N, Env)>
          : boost::result_of<at<N>(typename 
boost::result_of<params(Env)>::type)>
        {};

        template<typename N, typename Env>
        typename result<param_at(N, Env &)>::type
        operator()(N const &, Env &env) const
        {
            return at<N>()(params()(env));
        }
    };
}

// A collection of semantic actions, indexed by phoenix grammar rules
struct PhoenixDefaultActions
{
    template<typename Rule>
    struct when;
};

struct PhoenixGrammar;

// The Phoenix "default" rule, which handles most operands by
// delegating to proto::_default.
struct PhoenixRuleDefault
  : proto::_default< PhoenixGrammar >
{};

// Add to the collection of semantic actions the thing to
// do in the "default" case.
template<>
struct PhoenixDefaultActions::when<PhoenixRuleDefault>
  : proto::_default< PhoenixGrammar >
{};

// An openly extensible collection of phoenix rules and actions.
// "Tag" here is an expression tag.
struct PhoenixGrammar
  : proto::switch_<PhoenixGrammar>
{
    template<typename Tag>
    struct case_
      : proto::when<PhoenixRuleDefault, proto::external>
    {};
};

// Hide the fact that there is an Actions parameter being
// passed around as Data. The user shouldn't care.
struct PhoenixEval
  : proto::call<
        PhoenixGrammar(_, functional::params(proto::_state), 
functional::actions(proto::_state))
    >
{};

// Define placeholders
template<typename I>
struct placeholder
{
    typedef I type;
    typedef typename I::value_type value_type;
    static value_type const value = I::value;
};

// Here is the arg1 placeholder
proto::terminal<placeholder<mpl::int_<0> > >::type const arg1 = {};

// The phoenix rule for matching placeholder
// (Can later be generalized with boost::is_placeholer)
struct PhoenixRulePlaceholder
  : proto::terminal<placeholder<_> >
{};

// The action to take for placeholders
template<>
struct PhoenixDefaultActions::when<PhoenixRulePlaceholder>
  : proto::call<functional::param_at(proto::_value, _phoenix_env)>
{};

// Customization point for special terminal handling
template<typename T>
struct is_custom_terminal
  : mpl::false_
{};

// Customization point for special terminal handling
template<typename T>
struct get_custom_terminal;

// Phoenix rule for matching custom terminals
struct PhoenixRuleCustomTerminal
  : proto::if_<is_custom_terminal<proto::_value>()>
{};

// Phoenix action to take for custom terminals
template<>
struct PhoenixDefaultActions::when<PhoenixRuleCustomTerminal>
  : proto::lazy<get_custom_terminal<proto::_value>(proto::_value)>
{};

// Rules for terminals
template<>
struct PhoenixGrammar::case_<proto::tag::terminal>
  : proto::or_<
        proto::when<PhoenixRulePlaceholder, proto::external>
      , proto::when<PhoenixRuleCustomTerminal, proto::external>
      , proto::when<PhoenixRuleDefault, proto::external>
    >
{};

// Create a phoenix terminal, stored by value
template<typename T>
typename proto::terminal<T>::type const val(T t)
{
    return proto::terminal<T>::type::make(t);
}

// Create a phoenix terminal, stored by reference
template<typename T>
typename proto::terminal<boost::reference_wrapper<T> >::type const ref(T &t)
{
    return proto::terminal<boost::reference_wrapper<T> 
>::type::make(boost::ref(t));
}

// Create a phoenix terminal, stored by const reference
template<typename T>
typename proto::terminal<boost::reference_wrapper<T const> >::type const cref(T 
const &t)
{
    return proto::terminal<boost::reference_wrapper<T const> 
>::type::make(boost::cref(t));
}

// Call out boost::reference_wrapper for special handling
template<typename T>
struct is_custom_terminal<boost::reference_wrapper<T> >
  : mpl::true_
{};

// Special handling for boost::reference_wrapper
template<typename T>
struct get_custom_terminal<boost::reference_wrapper<T> >
{
    typedef T &result_type;

    T &operator()(boost::reference_wrapper<T> r) const
    {
        return r;
    }
};

//-----
// Begin if_/then_/else_ implementation
//-----

// Define some tags for additional statements
namespace tag
{
    struct if_then {};
    struct if_then_else {};
}

template<typename Cond, typename Then, typename Else>
struct if_then_else
  : proto::nary_expr<tag::if_then_else, Cond, Then, Else>
{};

template<typename Cond, typename Then>
struct else_gen
{
    else_gen(Cond const& cond, Then const& then)
      : cond(cond)
      , then(then)
    {}

    template <typename Else>
    typename if_then_else<Cond, Then, Else>::type const
    operator[](Else const &else_) const
    {
        return if_then_else<Cond, Then, Else>::type::make(cond, then, else_);
    }

    Cond const &cond;
    Then const &then;
};

template<typename Expr>
struct if_expr
  : Expr
{
    if_expr(Expr const& expr)
      : Expr(expr)
      , else_(proto::child_c<0>(*this), proto::child_c<1>(*this))
    {}

    typedef typename proto::result_of::child_c<Expr, 0>::type cond_type;
    typedef typename proto::result_of::child_c<Expr, 1>::type then_type;
    else_gen<cond_type, then_type> else_;
};

template<typename Cond, typename Then>
struct if_then
  : proto::binary_expr<tag::if_then, Cond, Then>
{};

template<typename Cond>
struct if_gen
{
    explicit if_gen(Cond const& cond)
      : cond(cond)
    {}

    template<typename Then>
    if_expr<typename if_then<Cond const &, Then const &>::type> const
    operator[](Then const &then) const
    {
        return if_then<Cond const &, Then const &>::type::make(cond, then);
    }

    Cond const &cond;
};

template <typename Cond>
if_gen<Cond> const if_(Cond const &cond)
{
    return if_gen<Cond>(cond);
}

// Callable for evaluating if_/then_ statements
//template<typename Actions>
struct if_then_eval : proto::callable
{
    typedef void result_type;

    template <typename Cond, typename Then, typename Env>
    result_type operator()(Cond const& cond, Then const& then, Env& env) const
    {
        if(PhoenixEval()(cond, env))
        {
            PhoenixEval()(then, env);
        }
    }
};

// Phoenix rule for matching if_/then_ statements
struct PhoenixRuleIfThen
  : if_then<PhoenixGrammar, PhoenixGrammar>
{};

// Phoenix action to take for if_/then_ statements
template<>
struct PhoenixDefaultActions::when<PhoenixRuleIfThen>
  : proto::call<if_then_eval(proto::_child0, proto::_child1, _phoenix_env)>
{};

// Bind actions to rules for if_/then_ statements
template<>
struct PhoenixGrammar::case_<tag::if_then>
  : proto::when<PhoenixRuleIfThen, proto::external>
{};

// Callable for evaluating if_/then_/else_ statements
struct if_then_else_eval : proto::callable
{
    typedef void result_type;

    template <typename Cond, typename Then, typename Else, typename Env>
    result_type operator()(Cond const& cond, Then const& then, Else const& 
else_, Env& env) const
    {
        if(PhoenixEval()(cond, env))
        {
            PhoenixEval()(then, env);
        }
        else
        {
            PhoenixEval()(else_, env);
        }
    }
};

// Phoenix rule for matching custom terminals
struct PhoenixRuleIfThenElse
  : if_then_else<PhoenixGrammar, PhoenixGrammar, PhoenixGrammar>
{};

// Phoenix action to take for custom terminals
template<> //typename Actions>
struct PhoenixDefaultActions::when<PhoenixRuleIfThenElse>
  : proto::call<if_then_else_eval(proto::_child0, proto::_child1, 
proto::_child2, _phoenix_env)>
{};

// Bind actions to rules for terminals
template<>
struct PhoenixGrammar::case_<tag::if_then_else >
  : proto::when<PhoenixRuleIfThenElse, proto::external>
{};

//-----
// End if_/then_/else_ implementation
//-----

// With this set of semantic actions, we can easily force
// arg1 to always evaluate to int(0). Nice little example
// of how the Phoenix expression evaluator can be customized.
struct MyActions
{
    template<typename Rule>
    struct when
      : PhoenixDefaultActions::when<Rule>
    {};
};

template<>
struct MyActions::when<PhoenixRulePlaceholder>
  : proto::make<int()>
{};

int main()
{
    PhoenixEval phoenix_eval;
    typedef fusion::vector1<int> params_type;
    fusion::vector2< params_type, PhoenixDefaultActions > env(params_type(42), 
PhoenixDefaultActions());
    proto::literal<int> i(1), j(2);

    int k = phoenix_eval(i + j, env);
    std::cout << k << std::endl; // should be 3

    k = phoenix_eval(i + arg1, env);
    std::cout << k << std::endl; // should be 43

    k = phoenix_eval(cref(7), env);
    std::cout << k << std::endl; // should be 7

    phoenix_eval( if_(i == 1)[ ++i ], env);
    std::cout << i.get() << std::endl; // should be 2

    phoenix_eval( if_(i == 1)[ ++i ].else_[ --i ], env);
    std::cout << i.get() << std::endl; // should be 1

    // Test for custom semantic actions
    fusion::vector2< params_type, MyActions > my_env(params_type(42), 
MyActions());
    k = phoenix_eval( arg1 + 7, my_env );
    std::cout << k << std::endl; // should be 7
}
#include <boost/proto/proto.hpp>
#include <string>
#include <iostream>
#include <boost/mpl/char.hpp>

namespace boost { namespace proto
{
    struct external;

    // This specialization uses the Data parameter as a collection
    // of actions that can be indexed by the specified rule.
    template<typename Grammar>
    struct when<Grammar, external>
    {
        typedef typename Grammar::proto_grammar proto_grammar;

        template<typename Expr, typename State, typename Data>
        struct impl
          : Data::template when<Grammar>::template impl<Expr, State, Data>
        {};

        template<typename Expr, typename State, typename Data>
        struct impl<Expr, State, Data &>
          : Data::template when<Grammar>::template impl<Expr, State, Data &>
        {};
    };
}}

namespace mpl = boost::mpl;
namespace proto = boost::proto;
using proto::_;

struct int_terminal
  : proto::terminal<int>
{};

struct char_terminal
  : proto::terminal<char>
{};

struct my_grammar
  : proto::or_<
        proto::when< int_terminal, proto::external >
      , proto::when< char_terminal, proto::external >
      , proto::when<
            proto::plus< my_grammar, my_grammar >
          , proto::fold< _, int(), my_grammar >
        >
    >
{};

struct print : proto::callable
{
    typedef std::ostream & result_type;

    template<typename T>
    result_type operator()(T t) const
    {
        return std::cout << t << ", ";
    }
};

struct my_actions
{
    template <typename Grammar>
    struct when;
};

template<>
struct my_actions::when<int_terminal>
  : proto::call<print(proto::_value)>
{};

template<>
struct my_actions::when<char_terminal>
  : proto::call<print(char(boost::mpl::char_<'x'>()))>
{};

int main()
{
    proto::literal<int> i(8), j(9);
    proto::literal<char> a('a'), b('b');

    my_actions act;
    my_grammar()(a + i, 0, act);
    std::cout.put('\n');
}
_______________________________________________
proto mailing list
proto@lists.boost.org
http://lists.boost.org/mailman/listinfo.cgi/proto

Reply via email to