#include "stdafx.h" #include #include #include #include #include #include #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 struct get_rule_name { typedef Grammar type; }; template struct get_rule_name< Grammar , typename boost::enable_if_c::value >::type > { typedef typename Grammar::rule_name type; }; template struct get_which : get_rule_name {}; // Only proto::or_, proto::switch_ and proto::if_ have branches and // need to define a which member. template struct get_which< typename Expr , typename Grammar , typename boost::enable_if_c< mpl::and_< has_which > , mpl::not_ > >::value >::type > : get_which::which> {}; } template struct named_rule : Grammar { typedef Name rule_name; }; template struct algorithm : proto::transform > { typedef Grammar proto_grammar; // Cheating! relies on Proto implementation details (the // presence of a nested which typedef in proto::matches). template struct impl : Actions::template when::type, Actions>::template impl {}; }; template struct is_callable > : 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 struct when; }; // The phoenix grammar and evaluator algorithm, parameterized // by a collection of semantic actions. template 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 struct PhoenixDefaultActions::when : proto::_default< PhoenixEval > {}; // An openly extensible collection of phoenix rules and actions. // "Tag" here is an expression tag. struct PhoenixGrammarCases { template 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 struct PhoenixEval : proto::algorithm< proto::switch_, Actions > {}; // Define placeholders template 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 > >::type const arg1 = {}; // Simple TR1-style function object for indexing a Fusion sequence struct at : proto::callable { template struct result; template struct result : fusion::result_of::at_c< typename boost::remove_reference::type , boost::remove_reference::type::value > {}; template typename fusion::result_of::at_c::type operator()(N, Env &env) const { return fusion::at_c(env); } }; // The phoenix rule for matching placeholder // (Can later be generalized with boost::is_placeholer) struct PhoenixRulePlaceholder : proto::named_rule< PhoenixRulePlaceholder , proto::terminal > > {}; // The action to take for placeholders template struct PhoenixDefaultActions::when : proto::call {}; // Customization point for special terminal handling template struct is_custom_terminal : mpl::false_ {}; // Customization point for special terminal handling template struct get_custom_terminal; // Phoenix rule for matching custom terminals struct PhoenixRuleCustomTerminal : proto::named_rule< PhoenixRuleCustomTerminal , proto::if_()> > {}; // Phoenix action to take for custom terminals template struct PhoenixDefaultActions::when : proto::lazy(proto::_value)> {}; // Rules for terminals template<> struct PhoenixGrammarCases::case_ : proto::or_< PhoenixRulePlaceholder , PhoenixRuleCustomTerminal , PhoenixRuleDefault > {}; // Create a phoenix terminal, stored by value template typename proto::terminal::type const val(T t) { return proto::terminal::type::make(t); } // Create a phoenix terminal, stored by reference template typename proto::terminal >::type const ref(T &t) { return proto::terminal >::type::make(boost::ref(t)); } // Create a phoenix terminal, stored by const reference template typename proto::terminal >::type const cref(T const &t) { return proto::terminal >::type::make(boost::cref(t)); } // Call out boost::reference_wrapper for special handling template struct is_custom_terminal > : mpl::true_ {}; // Special handling for boost::reference_wrapper template struct get_custom_terminal > { typedef T &result_type; T &operator()(boost::reference_wrapper 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 struct if_then_else : proto::nary_expr {}; template struct else_gen { else_gen(Cond const& cond, Then const& then) : cond(cond) , then(then) {} template typename if_then_else::type const operator[](Else const &else_) const { return if_then_else::type::make(cond, then, else_); } Cond const &cond; Then const &then; }; template 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::type cond_type; typedef typename proto::result_of::child_c::type then_type; else_gen else_; }; template struct if_then : proto::binary_expr {}; template struct if_gen { explicit if_gen(Cond const& cond) : cond(cond) {} template if_expr::type> const operator[](Then const &then) const { return if_then::type::make(cond, then); } Cond const &cond; }; template if_gen const if_(Cond const &cond) { return if_gen(cond); } // Callable for evaluating if_/then_ statements template struct if_then_eval : proto::callable { typedef void result_type; template 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 : proto::named_rule< PhoenixRuleIfThen , if_then > {}; // Phoenix action to take for if_/then_ statements template struct PhoenixDefaultActions::when : proto::call(proto::_child0, proto::_child1, proto::_state)> {}; // Bind actions to rules for if_/then_ statements template<> struct PhoenixGrammarCases::case_ : PhoenixRuleIfThen {}; // Callable for evaluating if_/then_/else_ statements template struct if_then_else_eval : proto::callable { typedef void result_type; template 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 : proto::named_rule< PhoenixRuleIfThenElse , if_then_else > {}; // Phoenix action to take for custom terminals template struct PhoenixDefaultActions::when : proto::call(proto::_child0, proto::_child1, proto::_child2, proto::_state)> {}; // Bind actions to rules for terminals template<> struct PhoenixGrammarCases::case_ : 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 struct when : PhoenixDefaultActions::when {}; template struct when : proto::make {}; }; int main() { PhoenixEval<> phoenix_eval; fusion::vector1 env(42); proto::literal 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 my_eval; k = my_eval( arg1 + 7, env ); std::cout << k << std::endl; // should be 7 }