On 10/14/2010 12:27 PM, Eric Niebler wrote:
<snip>
> - A new set of actions can be created easily by delegating
>   to MyActions::action by default, and specializing only those
>   rules that need custom handling.

The code I sent around actually falls short on this promise. Here's an
updated design that corrects the problem. The key is realizing that the
actions need to be parameterized by ... the actions. That way, they can
be easily subclassed.

That is, we now have this:

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

struct MyActions
{
    // Inherit default behavior from PhoenixDefaultActions
    template<typename Rule, typename Actions = MyActions>
    struct action
      : PhoenixDefaultActions::action<Rule, Actions>
    {};

    // Specialize action<> for custom handling of certain
    // grammar rules here...
};

If you don't ever pass MyActions to PhoenixDefaultActions, then any
specializations of MyActions::actions will never be considered.

-- 
Eric Niebler
BoostPro Computing
http://www.boostpro.com
#include "stdafx.h"
#include <iostream>
namespace mpl = boost::mpl;
namespace proto = boost::proto;
namespace fusion = boost::fusion;
using proto::_;

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

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

//struct MyActions
//{
//    template<typename Rule>
//    struct action
//      : PhoenixDefaultActions::action<Rule, MyActions>
//    {};
//};

// Simple utility for binding a semantic action to a rule
template<typename Rule, typename Actions>
struct rule_with_action
  : proto::when<Rule, typename Actions::template action<Rule, Actions> >
{};

// The phoenix grammar and evaluator algorithm, parameterized
// by a collection of semantic actions.
template<typename Actions = PhoenixDefaultActions>
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<typename Actions>
struct PhoenixDefaultActions::action<PhoenixRuleDefault, Actions>
  : proto::_default<PhoenixGrammar<Actions> >
{};

// An openly extensible collection of phoenix rules and actions.
// "Tag" here is an expression tag.
template<typename Tag, typename Actions>
struct PhoenixAlgorithmCases
  : rule_with_action< PhoenixRuleDefault, Actions >
{};

// Simple wrapper for PhoenixAlgorithmCases to make it
// suitable for use with proto::switch_
template<typename Actions>
struct PhoenixAlgorithmCasesImpl
{
    template<typename Tag>
    struct case_
      : PhoenixAlgorithmCases<Tag, Actions>
    {};
};

// The phoenix grammar and evaluator is just a
// collection of phoenix rules, parameterized by
// the actions to take for each rule.
template<typename Actions>
struct PhoenixGrammar
  : proto::switch_<PhoenixAlgorithmCasesImpl<Actions> >
{};

// 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 = {};

// Simple TR1-style function object for indexing a Fusion sequence
struct at : proto::callable
{
    template <typename Sig>
    struct result;

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

    template<typename N, typename Env>
    typename fusion::result_of::at_c<Env, N::value>::type
    operator()(N, Env &env) const
    {
        return fusion::at_c<N::value>(env);
    }
};

// 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<typename Actions>
struct PhoenixDefaultActions::action<PhoenixRulePlaceholder, Actions>
  : proto::call<at(proto::_value, proto::_state)>
{};

// 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<typename Actions>
struct PhoenixDefaultActions::action<PhoenixRuleCustomTerminal, Actions>
  : proto::lazy<get_custom_terminal<proto::_value>(proto::_value)>
{};

// Bind actions to rules for terminals
template<typename Actions>
struct PhoenixAlgorithmCases<proto::tag::terminal, Actions>
  : proto::or_<
        rule_with_action< PhoenixRulePlaceholder,    Actions >
      , rule_with_action< PhoenixRuleCustomTerminal, Actions >
      , rule_with_action< PhoenixRuleDefault,        Actions >
    >
{};

// Create a phoenix terminal, stored by value
template<typename T>
typename proto::terminal<T>::type 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 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 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(PhoenixGrammar<Actions>()(cond, env))
        {
            PhoenixGrammar<Actions>()(then, env);
        }
    }
};

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

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

// Bind actions to rules for if_/then_ statements
template<typename Actions>
struct PhoenixAlgorithmCases<tag::if_then, Actions>
  : rule_with_action< PhoenixRuleIfThen, Actions >
{};

// Callable for evaluating if_/then_/else_ statements
template<typename Actions>
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(PhoenixGrammar<Actions>()(cond, env))
        {
            PhoenixGrammar<Actions>()(then, env);
        }
        else
        {
            PhoenixGrammar<Actions>()(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::action<PhoenixRuleIfThenElse, Actions>
  : proto::call<if_then_else_eval<Actions>(proto::_child0, proto::_child1, 
proto::_child2, proto::_state)>
{};

// Bind actions to rules for terminals
template<typename Actions>
struct PhoenixAlgorithmCases<tag::if_then_else, Actions>
  : rule_with_action< PhoenixRuleIfThenElse, Actions >
{};

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


int main()
{
    PhoenixGrammar<> phoenix_eval;
    fusion::vector1<int> env(42);
    proto::literal<int> i(1), j(2);

    int k = phoenix_eval(i + j);
    std::cout << k << std::endl;

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

    k = phoenix_eval(cref(42), env);
    std::cout << k << std::endl;

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

    phoenix_eval( if_(i == 1)[ ++i ].else_[ --i ], env );
    std::cout << i.get() << std::endl;
}
_______________________________________________
proto mailing list
proto@lists.boost.org
http://lists.boost.org/mailman/listinfo.cgi/proto

Reply via email to