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