//#define BOOST_PROTO_MAX_ARITY 10
#include <iostream>
#include <boost/xpressive/proto/proto.hpp>
#include <boost/xpressive/proto/context.hpp>
#include <boost/xpressive/proto/debug.hpp>

namespace proto=boost::proto;

struct meta_domain : proto::domain<> {}; 

template<typename, template<typename, typename> class> struct meta_context;

template <typename Expr>
struct meta_expression : proto::extends<Expr, meta_expression<Expr>, meta_domain> {
  typedef proto::extends<Expr, meta_expression<Expr>, meta_domain> base_type;

    
  meta_expression (Expr const& expr = Expr()) : base_type (expr) {}

  using base_type::operator =;
};

namespace boost { namespace proto {
    template<typename Expr>
    struct generate<meta_domain, Expr> {
      typedef meta_expression<Expr> type;
      
      static type make (Expr const& expr) {
        return type (expr);
      }
    };
  } } 				// end namespace boost::proto

#define META_TERMINAL(NAME)                                                  \
  struct NAME##_tag {};                                                 \
  inline char const* proto_tag_name (NAME##_tag) { return #NAME; }      \
  std::ostream& operator << (std::ostream& os, const NAME##_tag) { os << "[" #NAME "]" ; return os; } \
  meta_expression<proto::terminal<NAME##_tag>::type> NAME

#define META_EXPR(TAG, WHAT, EXPR)                      \
template<> struct expr_for<proto::tag::TAG, WHAT##_tag> { \
  typedef BOOST_TYPEOF (EXPR) type;                        \
  static type expr; \
};\
expr_for<proto::tag::TAG, WHAT##_tag>::type expr_for<proto::tag::TAG,WHAT##_tag>::expr = EXPR

META_TERMINAL (_1);
META_TERMINAL (_2);
META_TERMINAL (max);
META_TERMINAL (left);
META_TERMINAL (right);
META_TERMINAL (value);

template<typename Tag, typename What> struct expr_for;

META_EXPR (plus,       left,  (1 + max (left (_1), left (_2), left (_1) + (right (_2)- right (_1)), left (_2) - (right (_2) - right (_1)))));
//META_EXPR (plus,       left,  (1 + max (left (_1))));
META_EXPR (plus,       right, (max (right (_1), right (_2))));
META_EXPR (multiplies, left,  (left (_1) + left (_2)));
META_EXPR (multiplies, right, (right (_1) + right (_2)));

int main (int,char**) {
  display_expr(expr_for<proto::tag::multiplies, left_tag>::expr);
  display_expr(expr_for<proto::tag::plus, left_tag>::expr);
}

/// Local Variables:
/// mode:c++
/// comment-column:60
/// fill-column:150
/// compilation-read-command:nil
/// compile-command:"g++ -I. -ope3 pe3.cpp -lgmpxx -lgmp"
/// End:


