On 10/23/2010 3:57 AM, Thomas Heller wrote:
> On Saturday 23 October 2010 07:13:56 e...@boostpro.com wrote:
>> Actually, I may have been hasty. I've had a hard time porting my
>> mini-Phoenix to proto::algorithm. Thomas, can you, either with your
>> version or mine? Proto::switch_ doesn't play nicely with it.
> 
> Yes you are right ... there is a pitfall to that ...
> Having the cases look like:
> 
> struct cases::case_<Tag> : some_rule {};
> 
> There is no way the grammar can pick up some_rule to index the action.

Another way of phrasing it is: we have given "some_rule" a name so we
can index on it, but we haven't given Proto access to the rule name in
all cases.

This is half the problem. The other half is getting Proto to know when
it has found a named rule, or when it should keep looking. For instance:

struct cases::case_<Tag> :
  proto::or_<
      some_rule
    , some_other_rule
  >
{};

Clearly, we don't want to index on cases::case_<Tag>. We want Proto to
look inside the or_ and pick either some_rule or some_other_rule, and
use those to look up the action.

Both of these problems can be solved if we explicitly bind a name to a
rule in a way that Proto can find it. We can do this with a "named_rule"
template. Now, instead of this:

  struct some_rule
    : proto::or_< ... >
  {};

we have this:

  struct some_rule
    : proto::named_rule<
          some_rule
        , proto::or_< ... >
      >
  {};

This eliminates the pitfalls you mentioned in your earlier mail and
solves the look-up ambiguities.

I've tweaked both the traversal example you sent around as well as my
over toy Phoenix. Tell me what you guys think.

-- 
Eric Niebler
BoostPro Computing
http://www.boostpro.com
#include "stdafx.h"
#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/repetition/enum.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum_params_with_a_default.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <iostream>

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

namespace boost { namespace proto
{
    namespace detail
    {
        // Define has_which trait
        BOOST_MPL_HAS_XXX_TRAIT_DEF(which)
        BOOST_MPL_HAS_XXX_TRAIT_DEF(rule_name)

        template<typename Grammar, typename EnableIf = void>
        struct get_rule_name
        {
            typedef Grammar type;
        };

        template<typename Grammar>
        struct get_rule_name<
            Grammar
          , typename boost::enable_if_c<has_rule_name<Grammar>::value >::type
        >
        {
            typedef typename Grammar::rule_name type;
        };

        template<typename Expr, typename Grammar, typename EnableIf = void>
        struct get_which
          : get_rule_name<Grammar>
        {};

        // Only proto::or_, proto::switch_ and proto::if_ have branches and
        // need to define a which member.
        template<typename Expr, typename Grammar>
        struct get_which<
            typename Expr
          , typename Grammar
          , typename boost::enable_if_c<
                mpl::and_<
                    has_which<proto::matches<Expr, Grammar> >
                  , mpl::not_<has_rule_name<Grammar> >
                >::value
            >::type
        >
          : get_which<Expr, typename proto::matches<Expr, Grammar>::which>
        {};
    }

    template<typename Name, typename Grammar>
    struct named_rule
      : Grammar
    {
        typedef Name rule_name;
    };

    template<typename Grammar, typename Actions>
    struct algorithm
      : proto::transform<algorithm<Grammar, Actions> >
    {
        typedef Grammar proto_grammar;

        // Cheating! relies on Proto implementation details (the
        // presence of a nested which typedef in proto::matches).
        template <typename Expr, typename State, typename Data>
        struct impl
          : Actions::template
                when<typename detail::get_which<Expr, Grammar>::type, 
Actions>::template
                    impl<Expr, State, Data>
        {};
    };

    template <typename Grammar, typename Actions>
    struct is_callable<algorithm<Grammar, Actions> >
      : mpl::true_
    {};
}}

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

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

// The phoenix grammar and evaluator algorithm, parameterized
// by a collection of semantic actions.
template<typename Actions = PhoenixDefaultActions>
struct PhoenixEval;

// This is the grammar of Phoenix expressions. It is
// independent of the semantic actions.
typedef PhoenixEval<> PhoenixGrammar;

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

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

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

// 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 PhoenixEval
  : proto::algorithm< proto::switch_<PhoenixGrammarCases>, 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::named_rule<
        PhoenixRulePlaceholder
      , proto::terminal<placeholder<_> >
    >
{};

// The action to take for placeholders
template<typename Actions>
struct PhoenixDefaultActions::when<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::named_rule<
        PhoenixRuleCustomTerminal
      , proto::if_<is_custom_terminal<proto::_value>()>
    >
{};

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

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

// 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<Actions>()(cond, env))
        {
            PhoenixEval<Actions>()(then, env);
        }
    }
};

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

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

// Bind actions to rules for if_/then_ statements
template<>
struct PhoenixGrammarCases::case_<tag::if_then>
  : PhoenixRuleIfThen
{};

// 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(PhoenixEval<Actions>()(cond, env))
        {
            PhoenixEval<Actions>()(then, env);
        }
        else
        {
            PhoenixEval<Actions>()(else_, env);
        }
    }
};

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

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

// Bind actions to rules for terminals
template<>
struct PhoenixGrammarCases::case_<tag::if_then_else >
  : PhoenixRuleIfThenElse
{};

//-----
// 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, typename Actions = MyActions>
    struct when
      : PhoenixDefaultActions::when<Rule, Actions>
    {};

    template<typename Actions>
    struct when<PhoenixRulePlaceholder, Actions>
      : proto::make<int()>
    {};
};

int main()
{
    PhoenixEval<> 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; // 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
    PhoenixEval<MyActions> my_eval;
    k = my_eval( arg1 + 7, env );
    std::cout << k << std::endl; // should be 7
}
#include "stdafx.h"
#include <string>
#include <iostream>
#include <boost/utility/enable_if.hpp>
#include <boost/mpl/and.hpp>
#include <boost/mpl/not.hpp>
#include <boost/mpl/has_xxx.hpp>

namespace boost { namespace proto
{
    namespace detail
    {
        // Define has_which trait
        BOOST_MPL_HAS_XXX_TRAIT_DEF(which)
        BOOST_MPL_HAS_XXX_TRAIT_DEF(rule_name)

        template<typename Grammar, typename EnableIf = void>
        struct get_rule_name
        {
            typedef Grammar type;
        };

        template<typename Grammar>
        struct get_rule_name<
            Grammar
          , typename boost::enable_if_c<has_rule_name<Grammar>::value >::type
        >
        {
            typedef typename Grammar::rule_name type;
        };

        template<typename Expr, typename Grammar, typename EnableIf = void>
        struct get_which
          : get_rule_name<Grammar>
        {};

        // Only proto::or_, proto::switch_ and proto::if_ have branches and
        // need to define a which member.
        template<typename Expr, typename Grammar>
        struct get_which<
            typename Expr
          , typename Grammar
          , typename boost::enable_if_c<
                mpl::and_<
                    has_which<proto::matches<Expr, Grammar> >
                  , mpl::not_<has_rule_name<Grammar> >
                >::value
            >::type
        >
          : get_which<Expr, typename proto::matches<Expr, Grammar>::which>
        {};
    }

    template<typename Name, typename Grammar>
    struct named_rule
      : Grammar
    {
        typedef Name rule_name;
    };

    template<typename Grammar, typename Actions>
    struct algorithm
      : proto::transform<algorithm<Grammar, Actions> >
    {
        typedef Grammar proto_grammar;

        // Cheating! relies on Proto implementation details (the
        // presence of a nested which typedef in proto::matches).
        template <typename Expr, typename State, typename Data>
        struct impl
          : Actions::template
                when<typename detail::get_which<Expr, Grammar>::type, 
Actions>::template
                    impl<Expr, State, Data>
        {};
    };

    template <typename Grammar, typename Actions>
    struct is_callable<algorithm<Grammar, Actions> >
      : mpl::true_
    {};
}}

namespace mpl = boost::mpl;
namespace proto = boost::proto;

struct nothing
  : proto::named_rule<nothing, proto::not_<proto::_> >
{};

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

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

struct plus_int_int
  : proto::named_rule<plus_int_int, proto::plus<int_terminal, int_terminal> >
{};

//struct my_grammar
//  : proto::or_<
//        int_terminal
//      , char_terminal
//      , plus_int_int
//    >
//{};

struct my_grammar
  : proto::switch_<my_grammar>
{
    template<typename Tag>
    struct case_
      : nothing
    {};
};

template<>
struct my_grammar::case_<proto::tag::terminal>
  : proto::or_<
        int_terminal
      , char_terminal
    >
{};

template<>
struct my_grammar::case_<proto::tag::plus>
  : //proto::or_<
        plus_int_int
    //>
{};

struct my_actions;

struct print : proto::callable
{
    typedef std::string result_type;

    result_type operator()(int) const
    {
        return "int";
    }

    result_type operator()(char) const
    {
        return "char";
    }

    template <typename T1, typename T2>
    result_type operator()(T1 res, T2 t2) const
    {
        res += " + ";
        res += t2;
        return res;
    }
};

struct my_actions
{
    template <typename Grammar, typename Actions = my_actions>
    struct when;

    template <typename Actions>
    struct when<int_terminal, Actions>
      : proto::call<print(proto::_value)>
    {};
    
    template <typename Actions>
    struct when<char_terminal, Actions>
      : proto::call<print(proto::_value)>
    {};
    
    template <typename Actions>
    struct when<plus_int_int, Actions>
      : proto::call<
            print(
                proto::call<proto::algorithm<my_grammar, Actions>(proto::_left)>
              , proto::call<proto::algorithm<my_grammar, 
Actions>(proto::_right)>
            )
        >
    {};
};

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

    std::cout << proto::algorithm<char_terminal, my_actions>()(a)  << "\n"; // 
printing char
    proto::assert_matches_not<proto::algorithm<int_terminal, my_actions> >(a);
    //std::cout << proto::algorithm<int_terminal, my_actions>()(a)   << "\n"; 
// precondition violation
    proto::assert_matches_not<proto::algorithm<char_terminal, my_actions> >(i);
    //std::cout << proto::algorithm<char_terminal, my_actions>()(i)  << "\n"; 
// precondition violation
    std::cout << proto::algorithm<int_terminal, my_actions>()(j)   << "\n"; // 
printing int
    std::cout << proto::algorithm<my_grammar, my_actions>()(j)     << "\n"; // 
printing int
    std::cout << proto::algorithm<my_grammar, my_actions>()(a)     << "\n"; // 
printing char
    std::cout << proto::algorithm<my_grammar, my_actions>()(i + i) << "\n"; // 
printing int + int
}
_______________________________________________
proto mailing list
proto@lists.boost.org
http://lists.boost.org/mailman/listinfo.cgi/proto

Reply via email to