/* Implementation of subroutines for the GNU C++ pretty-printer.
   Copyright (C) 2003 Free Software Foundation, Inc.
   Contributed by Gabriel Dos Reis <gdr@integrable-solutions.net>

This file is part of GCC.

GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2, or (at your option) any later
version.

GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING.  If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.  */

#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "real.h"
#include "cxx-pretty-print.h"
#include "cp-tree.h"
#include "toplev.h"

static void pp_cxx_unqualified_id (cxx_pretty_printer *, tree);
static void pp_cxx_nested_name_specifier (cxx_pretty_printer *, tree);
static void pp_cxx_qualified_id (cxx_pretty_printer *, tree);
static void pp_cxx_assignment_expression (cxx_pretty_printer *, tree);
static void pp_cxx_expression (cxx_pretty_printer *, tree);
static void pp_cxx_template_argument_list (cxx_pretty_printer *, tree);
static void pp_cxx_type_specifier_seq (cxx_pretty_printer *, tree);
static void pp_cxx_ptr_operator (cxx_pretty_printer *, tree);
static void pp_cxx_type_id (cxx_pretty_printer *, tree);
static void pp_cxx_direct_abstract_declarator (cxx_pretty_printer *, tree);
static void pp_cxx_declarator (cxx_pretty_printer *, tree);
static void pp_cxx_abstract_declarator (cxx_pretty_printer *, tree);
static void pp_cxx_template_parameter (cxx_pretty_printer *, tree);

#define pp_cxx_whitespace(PP)  pp_c_whitespace (pp_c_base (PP))
#define pp_cxx_left_paren(PP)  pp_c_left_paren (pp_c_base (PP))
#define pp_cxx_right_paren(PP) pp_c_right_paren (pp_c_base (PP))
#define pp_cxx_left_brace(PP)  pp_c_left_brace (pp_c_base (PP))
#define pp_cxx_right_brace(PP) pp_c_right_brace (pp_c_base (PP))
#define pp_cxx_dot(PP)         pp_c_dot (pp_c_base (PP))
#define pp_cxx_arrow(PP)       pp_c_arrow (pp_c_base (PP))
#define pp_cxx_semicolon(PP)   pp_c_semicolon (pp_c_base (PP))

static inline void
pp_cxx_nonconsecutive_character (cxx_pretty_printer *pp, int c)
{
  const char *p = pp_last_position_in_text (pp);

  if (p != NULL && *p == c)
    pp_cxx_whitespace (pp);
  pp_character (pp, c);
  pp_base (pp)->padding = pp_none;
}

#define pp_cxx_begin_template_argument_list(PP) \
  pp_cxx_nonconsecutive_character (PP, '<')
#define pp_cxx_end_template_argument_list(PP) \
  pp_cxx_nonconsecutive_character (PP, '>')

#define pp_cxx_identifier(PP, ID) pp_c_identifier (pp_c_base (PP), ID)
#define pp_cxx_tree_identifier(PP, T) pp_c_tree_identifier (pp_c_base (PP), T)

#define pp_cxx_cv_qualifier_seq(PP, T)   \
   pp_c_type_qualifier_list (pp_c_base (PP), T)
#define pp_cxx_storage_class_specifier(PP, T) \
   pp_c_storage_class_specifier (pp_c_base (PP), T)
#define pp_cxx_expression_list(PP, T)    \
   pp_c_expression_list (pp_c_base (PP), T)
#define pp_cxx_space_for_pointer_operator(PP, T)  \
   pp_c_space_for_pointer_operator (pp_c_base (PP), T)
#define pp_cxx_init_declarator(PP, T)    \
   pp_c_init_declarator (pp_c_base (PP), T)
#define pp_cxx_call_argument_list(PP, T) \
   pp_c_call_argument_list (pp_c_base (PP), T)

static void
pp_cxx_colon_colon (cxx_pretty_printer *pp)
{
  pp_colon_colon (pp);
  pp_base (pp)->padding = pp_none;
}


/* Expressions.  */

static inline bool
is_destructor_name (tree name)
{
  return name == complete_dtor_identifier
    || name == base_dtor_identifier
    || name == deleting_dtor_identifier;
}

/* conversion-function-id:
      operator conversion-type-id

   conversion-type-id:
      type-specifier-seq conversion-declarator(opt)

   conversion-declarator:
      ptr-operator conversion-declarator(opt)  */
static inline void
pp_cxx_conversion_function_id (cxx_pretty_printer *pp, tree t)
{
  pp_cxx_identifier (pp, "operator");
  pp_cxx_type_specifier_seq (pp, TREE_TYPE (t));
}

static inline void
pp_cxx_template_id (cxx_pretty_printer *pp, tree t)
{
  pp_cxx_unqualified_id (pp, TREE_OPERAND (t, 0));
  pp_cxx_begin_template_argument_list (pp);
  pp_cxx_template_argument_list (pp, TREE_OPERAND (t, 1));
  pp_cxx_end_template_argument_list (pp);
}

/* unqualified-id:
     identifier
     operator-function-id
     conversion-function-id
     ~ class-name
     template-id  */
static void
pp_cxx_unqualified_id (cxx_pretty_printer *pp, tree t)
{
  enum tree_code code = TREE_CODE (t);
  switch (code)
    {
    case RESULT_DECL:
      pp_cxx_identifier (pp, "<return-value>");
      break;

    case OVERLOAD:
      t = OVL_CURRENT (t);      
    case VAR_DECL:
    case PARM_DECL:
    case CONST_DECL:
    case TYPE_DECL:
    case FUNCTION_DECL:
    case NAMESPACE_DECL:
    case FIELD_DECL:
    case LABEL_DECL:
    case USING_DECL:
    case TEMPLATE_DECL:
      t = DECL_NAME (t);
      
    case IDENTIFIER_NODE:
      if (t == NULL)
        pp_cxx_identifier (pp, "<anonymous>");
      else if (IDENTIFIER_TYPENAME_P (t))
        pp_cxx_conversion_function_id (pp, t);
      else
        {
          if (is_destructor_name (t))
            {
              pp_complement (pp);
              /* FIXME: Why is this necessary? */
              if (TREE_TYPE (t))
                t = constructor_name (TREE_TYPE (t));
            }
          pp_cxx_tree_identifier (pp, t);
        }
      break;

    case TEMPLATE_ID_EXPR:
      pp_cxx_template_id (pp, t);
      break;

    case BASELINK:
      pp_cxx_unqualified_id (pp, BASELINK_FUNCTIONS (t));
      break;

    case RECORD_TYPE:
    case UNION_TYPE:
    case ENUMERAL_TYPE:
      pp_cxx_unqualified_id (pp, TYPE_NAME (t));
      break;

    case TEMPLATE_TYPE_PARM:
      t = TYPE_FIELDS (t);
    case TEMPLATE_PARM_INDEX:
      pp_cxx_unqualified_id (pp, TEMPLATE_PARM_DECL (t));
      break;

    default:
      pp_unsupported_tree (pp, t);
      break;
    }
}

static inline void
pp_cxx_template_keyword_if_needed (cxx_pretty_printer *pp, tree scope, tree t)
{
  if (TREE_CODE (t) == TEMPLATE_ID_EXPR
      && TYPE_P (scope) && dependent_type_p (scope))
    pp_cxx_identifier (pp, "template");
}

/* nested-name-specifier:
      class-or-namespace-name :: nested-name-specifier(opt)
      class-or-namespace-name :: template nested-name-specifier   */
static void
pp_cxx_nested_name_specifier (cxx_pretty_printer *pp, tree t)
{
  if (t != NULL && t != pp->enclosing_scope)
    {
      tree scope = TYPE_P (t) ? TYPE_CONTEXT (t) : DECL_CONTEXT (t);
      pp_cxx_nested_name_specifier (pp, scope);
      pp_cxx_template_keyword_if_needed (pp, scope, t);
      pp_cxx_unqualified_id (pp, t);
      pp_cxx_colon_colon (pp);
    }
}

/* qualified-id:
      nested-name-specifier template(opt) unqualified-id  */
static void
pp_cxx_qualified_id (cxx_pretty_printer *pp, tree t)
{
  switch (TREE_CODE (t))
    {
    case PTRMEM_CST:
      pp_cxx_nested_name_specifier (pp, PTRMEM_CST_CLASS (t));
      pp_cxx_unqualified_id (pp, PTRMEM_CST_MEMBER (t));
      break;

    case OVERLOAD:
      t = OVL_CURRENT (t);
    case FUNCTION_DECL:
      if (DECL_FUNCTION_MEMBER_P (t))
        pp_cxx_nested_name_specifier (pp, DECL_CONTEXT (t));
      pp_cxx_unqualified_id
        (pp, DECL_CONSTRUCTOR_P (t) ? DECL_CONTEXT (t) : t);
      break;

    case OFFSET_REF:
    case SCOPE_REF:
      pp_cxx_nested_name_specifier (pp, TREE_OPERAND (t, 0));
      pp_cxx_unqualified_id (pp, TREE_OPERAND (t, 1));
      break;

    default:
      {
        tree scope = TYPE_P (t) ? TYPE_CONTEXT (t) : DECL_CONTEXT (t);
        if (scope != pp->enclosing_scope)
          {
            pp_cxx_nested_name_specifier (pp, scope);
            pp_cxx_template_keyword_if_needed (pp, scope, t);
          }
        pp_cxx_unqualified_id (pp, t);
      }
      break;
    }
}

/* id-expression:
      unqualified-id
      qualified-id   */
static inline void
pp_cxx_id_expression (cxx_pretty_printer *pp, tree t)
{
  if (TREE_CODE (t) == OVERLOAD)
    t = OVL_CURRENT (t);
  if (DECL_P (t) && DECL_CONTEXT (t))
    pp_cxx_qualified_id (pp, t);
  else
    pp_cxx_unqualified_id (pp, t);
}

/* primary-expression:
     literal
     this
     :: identifier
     :: operator-function-id
     :: qualifier-id
     ( expression )
     id-expression   */
static void
pp_cxx_primary_expression (cxx_pretty_printer *pp, tree t)
{
  switch (TREE_CODE (t))
    {
    case STRING_CST:
    case INTEGER_CST:
    case REAL_CST:
      pp_c_constant (pp_c_base (pp), t);
      break;

    case BASELINK:
      t = BASELINK_FUNCTIONS (t);
    case VAR_DECL:
    case PARM_DECL:
    case FIELD_DECL:
    case FUNCTION_DECL:
    case OVERLOAD:
    case CONST_DECL:
    case TEMPLATE_DECL:
      pp_cxx_id_expression (pp, t);
      break;

    case RESULT_DECL:
    case TEMPLATE_TYPE_PARM:
    case TEMPLATE_PARM_INDEX:
      pp_cxx_unqualified_id (pp, t);
      break;

    default:
      pp_c_primary_expression (pp_c_base (pp), t);
      break;
    }
}

/* postfix-expression:
     primary-expression
     postfix-expression [ expression ]
     postfix-expression ( expression-list(opt) )
     simple-type-specifier ( expression-list(opt) )
     typename ::(opt) nested-name-specifier identifier ( expression-list(opt) )
     typename ::(opt) nested-name-specifier template(opt)
                                       template-id ( expression-list(opt) )
     postfix-expression . template(opt) ::(opt) id-expression
     postfix-expression -> template(opt) ::(opt) id-expression
     postfix-expression . pseudo-destructor-name
     postfix-expression -> pseudo-destructor-name
     postfix-expression ++
     postfix-expression --
     dynamic_cast < type-id > ( expression )
     static_cast < type-id > ( expression )
     reinterpret_cast < type-id > ( expression )
     const_cast < type-id > ( expression )
     typeid ( expression )
     typeif ( type-id )  */

static void
pp_cxx_postfix_expression (cxx_pretty_printer *pp, tree t)
{
  enum tree_code code = TREE_CODE (t);
  
  switch (code)
    {
    case AGGR_INIT_EXPR:
    case CALL_EXPR:
      {
        tree fun = TREE_OPERAND (t, 0);
        tree args = TREE_OPERAND (t, 1);
        tree saved_scope = pp->enclosing_scope;

        if (TREE_CODE (fun) == ADDR_EXPR)
          fun = TREE_OPERAND (fun, 0);

        /* In templates, where there is no way to tell whether a given
           call uses an actual member function.  So the parser builds
           FUN as a COMPONENT_REF or a plain IDENTIFIER_NODE until
           instantiation time.  */
        if (TREE_CODE (fun) != FUNCTION_DECL)
          ;
        else if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fun))
          {
            tree object = code == AGGR_INIT_EXPR && AGGR_INIT_VIA_CTOR_P (t)
              ? TREE_OPERAND (t, 2)
              : TREE_VALUE (args);
            
            while (TREE_CODE (object) == NOP_EXPR)
              object = TREE_OPERAND (object, 0);

            if (TREE_CODE (object) == ADDR_EXPR)
              object = TREE_OPERAND (object, 0);
            
            if (TREE_CODE (TREE_TYPE (object)) != POINTER_TYPE)
              {
                pp_cxx_postfix_expression (pp, object);
                pp_cxx_dot (pp);
              }
            else 
              {
                pp_cxx_postfix_expression (pp, object);
                pp_cxx_arrow (pp);
              }
            args = TREE_CHAIN (args);
            pp->enclosing_scope = strip_pointer_operator (TREE_TYPE (object));
          }

        pp_cxx_postfix_expression (pp, fun);
        pp->enclosing_scope = saved_scope;
        pp_cxx_call_argument_list (pp, args);
      }
      if (code == AGGR_INIT_EXPR && AGGR_INIT_VIA_CTOR_P (t))
        {
          pp_separate_with (pp, ',');
          pp_cxx_postfix_expression (pp, TREE_OPERAND (t, 2));
        }
      break;

    case BASELINK:
    case VAR_DECL:
    case PARM_DECL:
    case FIELD_DECL:
    case FUNCTION_DECL:
    case OVERLOAD:
    case CONST_DECL:
    case TEMPLATE_DECL:
    case RESULT_DECL:
      pp_cxx_primary_expression (pp, t);
      break;

    case DYNAMIC_CAST_EXPR:
    case STATIC_CAST_EXPR:
    case REINTERPRET_CAST_EXPR:
    case CONST_CAST_EXPR:
      if (code == DYNAMIC_CAST_EXPR)
        pp_identifier (pp, "dynamic_cast");
      else if (code == STATIC_CAST_EXPR)
        pp_identifier (pp, "static_cast");
      else if (code == REINTERPRET_CAST_EXPR)
        pp_identifier (pp, "reinterpret_cast");
      else
        pp_identifier (pp, "const_cast");
      pp_cxx_begin_template_argument_list (pp);
      pp_cxx_type_id (pp, TREE_TYPE (t));
      pp_cxx_end_template_argument_list (pp);
      pp_left_paren (pp);
      pp_cxx_expression (pp, TREE_OPERAND (t, 0));
      pp_right_paren (pp);
      break;

    case EMPTY_CLASS_EXPR:
      pp_cxx_type_id (pp, TREE_TYPE (t));
      pp_left_paren (pp);
      pp_right_paren (pp);
      break;

    case TYPEID_EXPR:
      t = TREE_OPERAND (t, 0);
      pp_cxx_identifier (pp, "typeid");
      pp_left_paren (pp);
      if (TYPE_P (t))
        pp_cxx_type_id (pp, t);
      else
        pp_cxx_expression (pp, t);
      pp_right_paren (pp);
      break;

    case PSEUDO_DTOR_EXPR:
      pp_cxx_postfix_expression (pp, TREE_OPERAND (t, 0));
      pp_cxx_dot (pp);
      pp_cxx_qualified_id (pp, TREE_OPERAND (t, 1));
      pp_cxx_colon_colon (pp);
      pp_complement (pp);
      pp_cxx_unqualified_id (pp, TREE_OPERAND (t, 2));
      break;

    default:
      pp_c_postfix_expression (pp_c_base (pp), t);
      break;
    }
}

/* new-expression:
      ::(opt) new new-placement(opt) new-type-id new-initializer(opt)
      ::(opt) new new-placement(opt) ( type-id ) new-initializer(opt)

   new-placement:
      ( expression-list )

   new-type-id:
      type-specifier-seq new-declarator(opt)

   new-declarator:
      ptr-operator new-declarator(opt)
      direct-new-declarator

   direct-new-declarator
      [ expression ]
      direct-new-declarator [ constant-expression ]

   new-initializer:
      ( expression-list(opt) )  */
static void
pp_cxx_new_expression (cxx_pretty_printer *pp, tree t)
{
  enum tree_code code = TREE_CODE (t);
  switch (code)
    {
    case NEW_EXPR:
    case VEC_NEW_EXPR:
      if (NEW_EXPR_USE_GLOBAL (t))
        pp_cxx_colon_colon (pp);
      pp_cxx_identifier (pp, "new");
      if (TREE_OPERAND (t, 0))
        {
          pp_cxx_call_argument_list (pp, TREE_OPERAND (t, 0));
          pp_space (pp);
        }
      /* FIXME: array-types are built with one more element.  */
      pp_cxx_type_id (pp, TREE_OPERAND (t, 1));
      if (TREE_OPERAND (t, 2))
        {
          pp_left_paren (pp);
          t = TREE_OPERAND (t, 2);
          if (TREE_CODE (t) == TREE_LIST)
            pp_c_expression_list (pp_c_base (pp), t);
          else if (t == void_zero_node)
            ;                   /* OK, empty initializer list.  */
          else
            pp_cxx_expression (pp, t);
          pp_right_paren (pp);
        }
      break;

    default:
      pp_unsupported_tree (pp, t);
    }
}

/* delete-expression:
      ::(opt) delete cast-expression
      ::(opt) delete [ ] cast-expression   */
static void
pp_cxx_delete_expression (cxx_pretty_printer *pp, tree t)
{
  enum tree_code code = TREE_CODE (t);
  switch (code)
    {
    case DELETE_EXPR:
    case VEC_DELETE_EXPR:
      if (DELETE_EXPR_USE_GLOBAL (t))
        pp_cxx_colon_colon (pp);
      pp_cxx_identifier (pp, "delete");
      if (code == VEC_DELETE_EXPR)
        {
          pp_left_bracket (pp);
          pp_right_bracket (pp);
        }
      pp_c_cast_expression (pp_c_base (pp), TREE_OPERAND (t, 0));
      break;      
      
    default:
      pp_unsupported_tree (pp, t);
    }
}

/* unary-expression:
      postfix-expression
      ++ cast-expression
      -- cast-expression
      unary-operator cast-expression
      sizeof unary-expression
      sizeof ( type-id )
      new-expression
      delete-expression

   unary-operator: one of
      *   &   +   -  !

   GNU extensions:
      __alignof__ unary-expression
      __alignof__ ( type-id )  */
static void
pp_cxx_unary_expression (cxx_pretty_printer *pp, tree t)
{
  enum tree_code code = TREE_CODE (t);
  switch (code)
    {
    case NEW_EXPR:
    case VEC_NEW_EXPR:
      pp_cxx_new_expression (pp, t);
      break;

    case DELETE_EXPR:
    case VEC_DELETE_EXPR:
      pp_cxx_delete_expression (pp, t);
      break;
      
    default:
      pp_c_unary_expression (pp_c_base (pp), t);
      break;
    }
}

/* cast-expression:
      unary-expression
      ( type-id ) cast-expression  */
static void
pp_cxx_cast_expression (cxx_pretty_printer *pp, tree t)
{
  switch (TREE_CODE (t))
    {
    case CAST_EXPR:
      pp_cxx_type_id (pp, TREE_TYPE (t));
      pp_cxx_call_argument_list (pp, TREE_OPERAND (t, 0));
      break;

    default:
      pp_c_cast_expression (pp_c_base (pp), t);
      break;
    }
}

/* pm-expression:
      cast-expression
      pm-expression .* cast-expression
      pm-expression ->* cast-expression  */
static void
pp_cxx_pm_expression (cxx_pretty_printer *pp, tree t)
{
  switch (TREE_CODE (t))
    {
      /* Handle unfortunate OFFESET_REF overloading here.  */
    case OFFSET_REF:
      if (TYPE_P (TREE_OPERAND (t, 0)))
        {
          pp_cxx_qualified_id (pp, t);
          break;
        }
      /* Else fall through.  */
    case MEMBER_REF:
    case DOTSTAR_EXPR:
      pp_cxx_pm_expression (pp, TREE_OPERAND (t, 0));
      pp_cxx_dot (pp);
      pp_star(pp);
      pp_cxx_cast_expression (pp, TREE_OPERAND (t, 1));
      break;


    default:
      pp_cxx_cast_expression (pp, t);
      break;
    }
}

/* multiplicative-expression:
      pm-expression
      multiplicative-expression * pm-expression
      multiplicative-expression / pm-expression
      multiplicative-expression % pm-expression  */
static void
pp_cxx_multiplicative_expression (cxx_pretty_printer *pp, tree e)
{
  enum tree_code code = TREE_CODE (e);
  switch (code)
    {
    case MULT_EXPR:
    case TRUNC_DIV_EXPR:
    case TRUNC_MOD_EXPR:
      pp_cxx_multiplicative_expression (pp, TREE_OPERAND (e, 0));
      pp_space (pp);
      if (code == MULT_EXPR)
	pp_star (pp);
      else if (code == TRUNC_DIV_EXPR)
	pp_slash (pp);
      else
	pp_modulo (pp);
      pp_space (pp);
      pp_cxx_pm_expression (pp, TREE_OPERAND (e, 1));
      break;

    default:
      pp_cxx_pm_expression (pp, e);
      break;
    }
}

/* conditional-expression:
      logical-or-expression
      logical-or-expression ?  expression  : assignment-expression  */
static void
pp_cxx_conditional_expression (cxx_pretty_printer *pp, tree e)
{
  if (TREE_CODE (e) == COND_EXPR)
    {
      pp_c_logical_or_expression (pp_c_base (pp), TREE_OPERAND (e, 0));
      pp_space (pp);
      pp_question (pp);
      pp_space (pp);
      pp_cxx_expression (pp, TREE_OPERAND (e, 1));
      pp_space (pp);
      pp_cxx_assignment_expression (pp, TREE_OPERAND (e, 2));
    }
  else
    pp_c_logical_or_expression (pp_c_base (pp), e);
}

static void
pp_cxx_assignment_operator (cxx_pretty_printer *pp, tree t)
{
  const char *op;

  switch (TREE_CODE (t))
    {
    case NOP_EXPR:
      op = "=";
      break;

    case PLUS_EXPR:
      op = "+=";
      break;

    case MINUS_EXPR:
      op = "-=";
      break;

    case TRUNC_DIV_EXPR:
      op = "/=";
      break;

    case TRUNC_MOD_EXPR:
      op = "%=";
      break;

    default:
      op = tree_code_name[TREE_CODE (t)];
      break;
    }

  pp_cxx_identifier (pp, op);
}


/* assignment-expression:
      conditional-expression
      logical-or-expression assignment-operator assignment-expression
      throw-expression

   throw-expression:
       throw assignment-expression(opt)

   assignment-operator: one of
      =    *=    /=    %=    +=    -=    >>=    <<=    &=    ^=    |=  */
static void
pp_cxx_assignment_expression (cxx_pretty_printer *pp, tree e)
{
  switch (TREE_CODE (e))
    {
    case MODIFY_EXPR:
    case INIT_EXPR:
      pp_c_logical_or_expression (pp_c_base (pp), TREE_OPERAND (e, 0));
      pp_space (pp);
      pp_equal (pp);
      pp_space (pp);
      pp_cxx_assignment_expression (pp, TREE_OPERAND (e, 1));
      break;

    case THROW_EXPR:
      pp_cxx_identifier (pp, "throw");
      if (TREE_OPERAND (e, 0))
        pp_cxx_assignment_expression (pp, TREE_OPERAND (e, 0));
      break;

    case MODOP_EXPR:
      pp_c_logical_or_expression (pp_c_base (pp), TREE_OPERAND (e, 0));
      pp_cxx_assignment_operator (pp, TREE_OPERAND (e, 1));
      pp_cxx_assignment_expression (pp, TREE_OPERAND (e, 2));
      break;

    default:
      pp_cxx_conditional_expression (pp, e);
      break;
    }
}

static void
pp_cxx_expression (cxx_pretty_printer *pp, tree t)
{
  switch (TREE_CODE (t))
    {
    case STRING_CST:
    case INTEGER_CST:
    case REAL_CST:
      pp_c_constant (pp_c_base (pp), t);
      break;

    case RESULT_DECL:
      pp_cxx_unqualified_id (pp, t);
      break;

#if 0      
    case OFFSET_REF:
#endif       
    case SCOPE_REF:
    case PTRMEM_CST:
      pp_cxx_qualified_id (pp, t);
      break;

    case OVERLOAD:
      t = OVL_CURRENT (t);
    case VAR_DECL:
    case PARM_DECL:
    case FIELD_DECL:
    case CONST_DECL:
    case FUNCTION_DECL:
    case BASELINK:
    case TEMPLATE_DECL:
    case TEMPLATE_TYPE_PARM:
    case TEMPLATE_PARM_INDEX:
      pp_cxx_primary_expression (pp, t);
      break;

    case CALL_EXPR:
    case DYNAMIC_CAST_EXPR:
    case STATIC_CAST_EXPR:
    case REINTERPRET_CAST_EXPR:
    case CONST_CAST_EXPR:
#if 0      
    case MEMBER_REF:
#endif      
    case EMPTY_CLASS_EXPR:
    case TYPEID_EXPR:
    case PSEUDO_DTOR_EXPR:
    case AGGR_INIT_EXPR:
      pp_cxx_postfix_expression (pp, t);
      break;

    case NEW_EXPR:
    case VEC_NEW_EXPR:
      pp_cxx_new_expression (pp, t);
      break;

    case DELETE_EXPR:
    case VEC_DELETE_EXPR:
      pp_cxx_delete_expression (pp, t);
      break;

    case CAST_EXPR:
      pp_cxx_cast_expression (pp, t);
      break;

    case OFFSET_REF:
    case MEMBER_REF:
    case DOTSTAR_EXPR:
      pp_cxx_pm_expression (pp, t);
      break;

    case MULT_EXPR:
    case TRUNC_DIV_EXPR:
    case TRUNC_MOD_EXPR:
      pp_cxx_multiplicative_expression (pp, t);
      break;

    case COND_EXPR:
      pp_cxx_conditional_expression (pp, t);
      break;

    case MODIFY_EXPR:
    case INIT_EXPR:
    case THROW_EXPR:
    case MODOP_EXPR:
      pp_cxx_assignment_expression (pp, t);
      break;

    default:
      pp_c_expression (pp_c_base (pp), t);
      break;      
    }
}


/* Declarations.  */

/* function-specifier:
      inline
      virtual
      explicit   */
static void
pp_cxx_function_specifier (cxx_pretty_printer *pp, tree t)
{
  switch (TREE_CODE (t))
    {
    case FUNCTION_DECL:
      if (DECL_VIRTUAL_P (t))
        pp_cxx_identifier (pp, "virtual");
      else if (DECL_CONSTRUCTOR_P (t) && DECL_NONCONVERTING_P (t))
        pp_cxx_identifier (pp, "explicit");
      else
        pp_c_function_specifier (pp_c_base (pp), t);

    default:
      break;
    }
}

/* decl-specifier-seq:
      decl-specifier-seq(opt) decl-specifier

   decl-specifier:
      storage-class-specifier
      type-specifier
      function-specifier
      friend
      typedef  */
static void
pp_cxx_decl_specifier_seq (cxx_pretty_printer *pp, tree t)
{
  switch (TREE_CODE (t))
    {
    case VAR_DECL:
    case PARM_DECL:
    case CONST_DECL:
    case FIELD_DECL:
      pp_cxx_storage_class_specifier (pp, t);
      pp_cxx_decl_specifier_seq (pp, TREE_TYPE (t));
      break;
      
    case TYPE_DECL:
      pp_cxx_identifier (pp, "typedef");
      pp_cxx_decl_specifier_seq (pp, TREE_TYPE (t));
      break;

    case RECORD_TYPE:
      if (TYPE_PTRMEMFUNC_P (t))
        {
          tree pfm = TYPE_PTRMEMFUNC_FN_TYPE (t);
          pp_cxx_decl_specifier_seq (pp, TREE_TYPE (TREE_TYPE (pfm)));
          pp_cxx_whitespace (pp);
          pp_cxx_ptr_operator (pp, t);
        }
      break;

    case FUNCTION_DECL:
      /* Constructors don't have return types.  And conversion functions
         do not have a type-specifier in their return types.  */
      if (DECL_CONSTRUCTOR_P (t) || DECL_CONV_FN_P (t))
        pp_cxx_function_specifier (pp, t);
      else if (DECL_NONSTATIC_MEMBER_FUNCTION_P (t))
        pp_cxx_decl_specifier_seq (pp, TREE_TYPE (TREE_TYPE (t)));
      else
        default:
      pp_c_declaration_specifiers (pp_c_base (pp), t);
      break;
    }
}

/* simple-type-specifier:
      ::(opt) nested-name-specifier(opt) type-name
      ::(opt) nested-name-specifier(opt) template(opt) template-id
      char
      wchar_t
      bool
      short
      int
      long
      signed
      unsigned
      float
      double
      void  */
static void
pp_cxx_simple_type_specifier (cxx_pretty_printer *pp, tree t)
{
  switch (TREE_CODE (t))
    {
    case RECORD_TYPE:
    case UNION_TYPE:
    case ENUMERAL_TYPE:
      pp_cxx_qualified_id (pp, t);
      break;

    case TEMPLATE_TYPE_PARM:
    case TEMPLATE_PARM_INDEX:
      pp_cxx_unqualified_id (pp, t);
      break;

    case TYPENAME_TYPE:
      pp_cxx_identifier (pp, "typename");
      pp_cxx_nested_name_specifier (pp, TYPE_CONTEXT (t));
      pp_cxx_unqualified_id (pp, TYPE_NAME (t));
      break;

    default:
      pp_c_type_specifier (pp_c_base (pp), t);
      break;
    }
}

/* type-specifier-seq:
      type-specifier type-specifier-seq(opt)

   type-specifier:
      simple-type-specifier
      class-specifier
      enum-specifier
      elaborated-type-specifier
      cv-qualifier   */

static void
pp_cxx_type_specifier_seq (cxx_pretty_printer *pp, tree t)
{
  switch (TREE_CODE (t))
    {
    case TEMPLATE_DECL:
    case TEMPLATE_TYPE_PARM:
    case TYPE_DECL:
    case BOUND_TEMPLATE_TEMPLATE_PARM:
      pp_c_type_qualifier_list (pp_c_base (pp), t);
      pp_cxx_simple_type_specifier (pp, t);
      break;

    case METHOD_TYPE:
      pp_cxx_type_specifier_seq (pp, TREE_TYPE (t));
      pp_cxx_space_for_pointer_operator (pp, TREE_TYPE (t));
      pp_cxx_nested_name_specifier (pp, TYPE_METHOD_BASETYPE (t));
      break;

    default:
      if (!(TREE_CODE (t) == FUNCTION_DECL && DECL_CONSTRUCTOR_P (t)))
        pp_c_specifier_qualifier_list (pp_c_base (pp), t);
    }
}

/* ptr-operator:
      * cv-qualifier-seq(opt)
      &
      ::(opt) nested-name-specifier * cv-qualifier-seq(opt)  */

static void
pp_cxx_ptr_operator (cxx_pretty_printer *pp, tree t)
{
  if (!TYPE_P (t) && TREE_CODE (t) != TYPE_DECL)
    t = TREE_TYPE (t);
  switch (TREE_CODE (t))
    {
    case REFERENCE_TYPE:
    case POINTER_TYPE:
      if (TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE
          || TYPE_PTR_TO_MEMBER_P (TREE_TYPE (t)))
        pp_cxx_ptr_operator (pp, TREE_TYPE (t));
      if (TREE_CODE (t) == POINTER_TYPE)
        {
          pp_star (pp);
          pp_cxx_cv_qualifier_seq (pp, t);
        }
      else
        pp_ampersand (pp);
      break;

    case RECORD_TYPE:
      if (TYPE_PTRMEMFUNC_P (t))
        {
          pp_cxx_left_paren (pp);
          pp_cxx_nested_name_specifier (pp, TYPE_PTRMEMFUNC_OBJECT_TYPE (t));
          pp_star (pp);
          break;
        }
    case OFFSET_TYPE:
      if (TYPE_PTR_TO_MEMBER_P (t))
        {
          pp_cxx_nested_name_specifier (pp, TYPE_PTRMEM_CLASS_TYPE (t));
          pp_star (pp);
          pp_cxx_cv_qualifier_seq (pp, t);
          break;
        }
      /* else fall through.  */

    default:
      pp_unsupported_tree (pp, t);
      break;
    }
}

static inline tree
pp_cxx_implicit_parameter_type (tree mf)
{
  return TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (mf))));
}

/*
   parameter-declaration:
      decl-specifier-seq declarator
      decl-specifier-seq declarator = assignment-expression
      decl-specifier-seq abstract-declarator(opt)
      decl-specifier-seq abstract-declarator(opt) assignment-expression  */
static inline void
pp_cxx_parameter_declaration (cxx_pretty_printer *pp, tree t)
{
  pp_cxx_decl_specifier_seq (pp, t);
  if (TYPE_P (t))
    pp_cxx_abstract_declarator (pp, t);
  else
    pp_cxx_declarator (pp, t);
}

/* parameter-declaration-clause:
      parameter-declaration-list(opt) ...(opt)
      parameter-declaration-list , ...

   parameter-declaration-list:
      parameter-declaration
      parameter-declaration-list , parameter-declaration  */
static void
pp_cxx_parameter_declaration_clause (cxx_pretty_printer *pp, tree t)
{
  tree args = TYPE_P (t) ? NULL : FUNCTION_FIRST_USER_PARM (t);
  tree types = TYPE_P (t) ? TYPE_ARG_TYPES (t) : FUNCTION_FIRST_USER_PARMTYPE (t);
  const bool abstract = args == NULL
    || pp_c_base (pp)->flags & pp_c_flag_abstract;
  bool first = true;

  /* Skip artificial parameter for nonstatic member functions.  */
  if (TREE_CODE (t) == METHOD_TYPE)
    types = TREE_CHAIN (types);

  pp_cxx_left_paren (pp);
  for (; args; args = TREE_CHAIN (args), types = TREE_CHAIN (types))
    {
      if (!first)
        pp_separate_with (pp, ',');
      first = false;
      pp_cxx_parameter_declaration (pp, abstract ? TREE_VALUE (types) : args);
      if (!abstract && pp_c_base (pp)->flags & pp_cxx_flag_default_argument)
        {
          pp_cxx_whitespace (pp);
          pp_equal (pp);
          pp_cxx_whitespace (pp);
          pp_cxx_assignment_expression (pp, TREE_PURPOSE (types));
        }
    }
  pp_cxx_right_paren (pp);
}

/* exception-specification:
      throw ( type-id-list(opt) )

   type-id-list
      type-id
      type-id-list , type-id   */
static void
pp_cxx_exception_specification (cxx_pretty_printer *pp, tree t)
{
  tree ex_spec = TYPE_RAISES_EXCEPTIONS (t);

  if (!TYPE_NOTHROW_P (t) && ex_spec == NULL)
    return;
  pp_cxx_identifier (pp, "throw");
  pp_cxx_left_paren (pp);
  for (; ex_spec && TREE_VALUE (ex_spec); ex_spec = TREE_CHAIN (ex_spec))
    {
      pp_cxx_type_id (pp, TREE_VALUE (ex_spec));
      if (TREE_CHAIN (ex_spec))
        pp_separate_with (pp, ',');
    }
  pp_cxx_right_paren (pp);
}

/* direct-declarator:
      declarator-id
      direct-declarator ( parameter-declaration-clause ) cv-qualifier-seq(opt)
                                            exception-specification(opt)
      direct-declaration [ constant-expression(opt) ]
      ( declarator )  */
static void
pp_cxx_direct_declarator (cxx_pretty_printer *pp, tree t)
{
  switch (TREE_CODE (t))
    {
    case VAR_DECL:
    case PARM_DECL:
    case CONST_DECL:
    case FIELD_DECL:
      if (DECL_NAME (t))
        {
          pp_cxx_space_for_pointer_operator (pp, TREE_TYPE (t));
          pp_cxx_id_expression (pp, DECL_NAME (t));
        }
      pp_cxx_abstract_declarator (pp, TREE_TYPE (t));
      break;
      
    case FUNCTION_DECL:
      pp_cxx_space_for_pointer_operator (pp, TREE_TYPE (TREE_TYPE (t)));
      pp_cxx_id_expression (pp, t);
      pp_cxx_parameter_declaration_clause (pp, t);
      
      if (DECL_NONSTATIC_MEMBER_FUNCTION_P (t))
        {
          pp_base (pp)->padding = pp_before;
          pp_cxx_cv_qualifier_seq (pp, pp_cxx_implicit_parameter_type (t));
        }

      pp_cxx_exception_specification (pp, TREE_TYPE (t));
      break;

    case TYPENAME_TYPE:
    case TEMPLATE_DECL:
    case TEMPLATE_TYPE_PARM:
    case TEMPLATE_PARM_INDEX:
      break;

    default:
      pp_c_direct_declarator (pp_c_base (pp), t);
      break;
    }
}

/* declarator:
   direct-declarator
   ptr-operator declarator  */
static void
pp_cxx_declarator (cxx_pretty_printer *pp, tree t)
{
  pp_cxx_direct_declarator (pp, t);
}

/* ctor-initializer:
      : mem-initializer-list

   mem-initializer-list:
      mem-initializer
      mem-initializer , mem-initializer-list

   mem-initializer:
      mem-initializer-id ( expression-list(opt) )

   mem-initializer-id:
      ::(opt) nested-name-specifier(opt) class-name
      identifier   */
static void
pp_cxx_ctor_initializer (cxx_pretty_printer *pp, tree t)
{
  t = TREE_OPERAND (t, 0);
  pp_cxx_whitespace (pp);
  pp_colon (pp);
  pp_cxx_whitespace (pp);
  for (; t; t = TREE_CHAIN (t))
    {
      pp_cxx_primary_expression (pp, TREE_PURPOSE (t));
      pp_cxx_call_argument_list (pp, TREE_VALUE (t));
      if (TREE_CHAIN (t))
        pp_separate_with (pp, ',');
    }
}

/* function-definition:
      decl-specifier-seq(opt) declarator ctor-initializer(opt) function-body
      decl-specifier-seq(opt) declarator function-try-block  */

void
pp_cxx_function_definition (cxx_pretty_printer *pp, tree t)
{
  tree saved_scope = pp->enclosing_scope;
  pp_cxx_decl_specifier_seq (pp, t);
  pp_cxx_declarator (pp, t);
  pp_needs_newline (pp) = true;
  pp->enclosing_scope = DECL_CONTEXT (t);
  if (DECL_SAVED_TREE (t))
    {
      tree body = DECL_SAVED_TREE (t);
      if (TREE_CODE (body) == COMPOUND_STMT
          && TREE_CODE (COMPOUND_BODY (body)) == CTOR_INITIALIZER)
        {
          body = COMPOUND_BODY (body);
          pp_cxx_ctor_initializer (pp, body);
          body = TREE_CHAIN (body);
        }
      pp_cxx_statement (pp, body);
    }
  else
    {
      pp_cxx_semicolon (pp);
      pp_needs_newline (pp) = true;
    }
  pp_flush (pp);
  pp->enclosing_scope = saved_scope;
}

/* abstract-declarator:
      ptr-operator abstract-declarator(opt)
      direct-abstract-declarator  */
static void
pp_cxx_abstract_declarator (cxx_pretty_printer *pp, tree t)
{
  if (TYPE_PTRMEM_P (t) || TYPE_PTRMEMFUNC_P (t))
    pp_cxx_right_paren (pp);
  else if (POINTER_TYPE_P (t))
    {
      if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE
          || TREE_CODE (TREE_TYPE (t)) == FUNCTION_TYPE)
        pp_cxx_right_paren (pp);
      t = TREE_TYPE (t);
    }
  pp_cxx_direct_abstract_declarator (pp, t);
}

/* direct-abstract-declarator:
      direct-abstract-declarator(opt) ( parameter-declaration-clause )
                           cv-qualifier-seq(opt) exception-specification(opt)
      direct-abstract-declarator(opt) [ constant-expression(opt) ]
      ( abstract-declarator )  */
static void
pp_cxx_direct_abstract_declarator (cxx_pretty_printer *pp, tree t)
{
  switch (TREE_CODE (t))
    {
    case REFERENCE_TYPE:
      pp_cxx_abstract_declarator (pp, t);
      break;

    case RECORD_TYPE:
      if (TYPE_PTRMEMFUNC_P (t))
        pp_cxx_direct_abstract_declarator (pp, TYPE_PTRMEMFUNC_FN_TYPE (t));
      break;

    case METHOD_TYPE:
    case FUNCTION_TYPE:
      pp_cxx_parameter_declaration_clause (pp, t);
      pp_cxx_direct_abstract_declarator (pp, TREE_TYPE (t));
      if (TREE_CODE (t) == METHOD_TYPE)
        {
          pp_base (pp)->padding = pp_before;
          pp_cxx_cv_qualifier_seq
            (pp, TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (t))));
        }
      pp_cxx_exception_specification (pp, t);
      break;

    case TYPENAME_TYPE:
    case TEMPLATE_TYPE_PARM:
    case TEMPLATE_TEMPLATE_PARM:
    case BOUND_TEMPLATE_TEMPLATE_PARM:
    case UNBOUND_CLASS_TEMPLATE:
      break;

    default:
      pp_c_direct_abstract_declarator (pp_c_base (pp), t);
      break;      
    }
}

/* type-id:
     type-specifier-seq abstract-declarator(opt) */
static void
pp_cxx_type_id (cxx_pretty_printer *pp, tree t)
{
  pp_flags saved_flags = pp_c_base (pp)->flags;
  pp_c_base (pp)->flags |= pp_c_flag_abstract;

  switch (TREE_CODE (t))
    {
    case TYPE_DECL:
    case UNION_TYPE:
    case RECORD_TYPE:
    case ENUMERAL_TYPE:
    case TYPENAME_TYPE:
    case BOUND_TEMPLATE_TEMPLATE_PARM:
    case UNBOUND_CLASS_TEMPLATE:
    case TEMPLATE_TEMPLATE_PARM:
    case TEMPLATE_TYPE_PARM:
    case TEMPLATE_PARM_INDEX:
    case TEMPLATE_DECL:
    case TYPEOF_TYPE:
    case TEMPLATE_ID_EXPR:
      /* FIXME: Should be pp_cxx_type_specifier_seq.  */
      pp_cxx_type_specifier_seq (pp, t);
      pp_cxx_declarator (pp, t);
      break;

    default:
      pp_c_type_id (pp_c_base (pp), t);
      break;
    }

  pp_c_base (pp)->flags = saved_flags;
}

/* template-argument-list:
      template-argument
      template-argument-list, template-argument

   template-argument:
      assignment-expression
      type-id
      template-name   */
static void
pp_cxx_template_argument_list (cxx_pretty_printer *pp, tree t)
{
  int i;
  if (t == NULL)
    return;
  for (i = 0; i < TREE_VEC_LENGTH (t); ++i)
    {
      tree arg = TREE_VEC_ELT (t, i);
      if (i != 0)
        pp_separate_with (pp, ',');
      if (TYPE_P (arg) || (TREE_CODE (arg) == TEMPLATE_DECL
                           && TYPE_P (DECL_TEMPLATE_RESULT (arg))))
        pp_cxx_type_id (pp, arg);
      else
        pp_cxx_expression (pp, arg);
    }
}


static void
pp_cxx_exception_declaration (cxx_pretty_printer *pp, tree t)
{
  t = DECL_STMT_DECL (t);
  pp_cxx_type_specifier_seq (pp, t);
  if (TYPE_P (t))
    pp_cxx_abstract_declarator (pp, t);
  else
    pp_cxx_declarator (pp, t);
}

/* Statements.  */

void
pp_cxx_statement (cxx_pretty_printer *pp, tree t)
{
  switch (TREE_CODE (t))
    {
    case USING_STMT:
      pp_cxx_identifier (pp, "using");
      pp_cxx_identifier (pp, "namespace");
      pp_cxx_qualified_id (pp, USING_STMT_NAMESPACE (t));
      break;

    case USING_DECL:
      pp_cxx_identifier (pp, "using");
      pp_cxx_nested_name_specifier (pp, DECL_INITIAL (t));
      pp_cxx_unqualified_id (pp, DECL_NAME (t));
      break;

    case EH_SPEC_BLOCK:
      break;

      /* try-block:
            try compound-statement handler-seq  */
    case TRY_BLOCK:
      pp_maybe_newline_and_indent (pp, 0);
      pp_cxx_identifier (pp, "try");
      pp_newline_and_indent (pp, 3);
      pp_cxx_statement (pp, TRY_STMTS (t));
      pp_newline_and_indent (pp, -3);
      if (CLEANUP_P (t))
        ;
      else
        pp_cxx_statement (pp, TRY_HANDLERS (t));
      break;

      /*
         handler-seq:
            handler handler-seq(opt)

         handler:
         catch ( exception-declaration ) compound-statement 

         exception-declaration:
            type-specifier-seq declarator
            type-specifier-seq abstract-declarator
            ...   */
    case HANDLER:
      pp_cxx_identifier (pp, "catch");
      pp_cxx_left_paren (pp);
      pp_cxx_exception_declaration (pp, HANDLER_PARMS (t));
      pp_cxx_right_paren (pp);
      pp_indentation (pp) += 3;
      pp_needs_newline (pp) = true;
      pp_cxx_statement (pp, HANDLER_BODY (t));
      pp_indentation (pp) -= 3;
      pp_needs_newline (pp) = true;
      break;

    default:
      pp_c_statement (pp_c_base (pp), t);
      break;
    }
}

/* original-namespace-definition:
      namespace identifier { namespace-body }

  As an edge case, we also handle unnamed namespace definition here.  */

static void
pp_cxx_original_namespace_definition (cxx_pretty_printer *pp, tree t)
{
  pp_cxx_identifier (pp, "namespace");
  if (DECL_NAME (t))
    pp_cxx_unqualified_id (pp, t);
  pp_cxx_whitespace (pp);
  pp_cxx_left_brace (pp);
  /* We do not print the namespace-body.  */
  pp_cxx_whitespace (pp);
  pp_cxx_right_brace (pp);
}

/* namespace-alias:
      identifier

   namespace-alias-definition:
      namespace identifier = qualified-namespace-specifier ;

   qualified-namespace-specifier:
      ::(opt) nested-name-specifier(opt) namespace-name   */

static void
pp_cxx_namespace_alias_definition (cxx_pretty_printer *pp, tree t)
{
  pp_cxx_identifier (pp, "namespace");
  pp_cxx_unqualified_id (pp, t);
  pp_cxx_whitespace (pp);
  pp_equal (pp);
  pp_cxx_whitespace (pp);
  pp_cxx_qualified_id (pp, DECL_NAMESPACE_ALIAS (t));
  pp_cxx_semicolon (pp);
}

/* simple-declaration:
      decl-specifier-seq(opt) init-declarator-list(opt)  */
static void
pp_cxx_simple_declaration (cxx_pretty_printer *pp, tree t)
{
  pp_cxx_decl_specifier_seq (pp, t);
  pp_cxx_init_declarator (pp, t);
  pp_cxx_semicolon (pp);
  pp_needs_newline (pp) = true;
}

/*
  template-parameter-list:
     template-parameter
     template-parameter-list , template-parameter  */

static inline void
pp_cxx_template_parameter_list (cxx_pretty_printer *pp, tree t)
{
  const int n = TREE_VEC_LENGTH (t);
  int i;
  for (i = 0; i < n; ++i)
    {
      if (i)
        pp_separate_with (pp, ',');
      pp_cxx_template_parameter (pp, TREE_VEC_ELT (t, i));
    }
}

/* template-parameter:
      type-parameter
      parameter-declaration

   type-parameter:
     class identifier(opt)
     class identifier(op) = type-id
     typename identifier(opt)
     typename identifier(opt) = type-id
     template < template-parameter-list > class identifier(opt)
     template < template-parameter-list > class identifier(opt) = template-name
*/
static void
pp_cxx_template_parameter (cxx_pretty_printer *pp, tree t)
{
  tree parameter =  TREE_VALUE (t);
  switch (TREE_CODE (parameter))
    {
    case TYPE_DECL:
      pp_cxx_identifier (pp, "class");
      if (DECL_NAME (parameter))
        pp_cxx_tree_identifier (pp, DECL_NAME (parameter));
      /* FIXME: Chech if we should print also default argument.  */
      break;

    case PARM_DECL:
      pp_cxx_parameter_declaration (pp, parameter);
      break;

    case TEMPLATE_DECL:
      break;

    default:
      pp_unsupported_tree (pp, t);
      break;
    }
}

/* Pretty-print a template parameter in the canonical form
   "template-parameter-<level>-<position in parameter list>".  */

void
pp_cxx_canonical_template_parameter (cxx_pretty_printer *pp, tree parm)
{
  const enum tree_code code = TREE_CODE (parm);

  /* Brings type template parameters to the canonical forms.  */
  if (code == TEMPLATE_TYPE_PARM || code == TEMPLATE_TEMPLATE_PARM
      || code == BOUND_TEMPLATE_TEMPLATE_PARM)
    parm = TEMPLATE_TYPE_PARM_INDEX (parm);
  
  pp_cxx_begin_template_argument_list (pp);
  pp_cxx_identifier (pp, "template-parameter-");
  pp_wide_integer (pp, TEMPLATE_PARM_LEVEL (parm));
  pp_minus (pp);
  pp_wide_integer (pp, TEMPLATE_PARM_IDX (parm) + 1);
  pp_cxx_end_template_argument_list (pp);
}

/*
  template-declaration:
     export(opt) template < template-parameter-list > declaration   */
static void
pp_cxx_template_declaration (cxx_pretty_printer *pp, tree t)
{
  tree tmpl = most_general_template (t);
  tree level;
  int i = 0;

  pp_maybe_newline_and_indent (pp, 0);
  for (level = DECL_TEMPLATE_PARMS (tmpl); level; level = TREE_CHAIN (level))
    {
      pp_cxx_identifier (pp, "template");
      pp_cxx_begin_template_argument_list (pp);
      pp_cxx_template_parameter_list (pp, TREE_VALUE (level));
      pp_cxx_end_template_argument_list (pp);
      pp_newline_and_indent (pp, 3);
      i += 3;
    }
  if (TREE_CODE (t) == FUNCTION_DECL && DECL_SAVED_TREE (t))
    pp_cxx_function_definition (pp, t);
  else
    pp_cxx_simple_declaration (pp, t);
}

static void
pp_cxx_explicit_specialization (cxx_pretty_printer *pp, tree t)
{
  pp_unsupported_tree (pp, t);
}

static void
pp_cxx_explicit_instantiation (cxx_pretty_printer *pp, tree t)
{
  pp_unsupported_tree (pp, t);
}

/*
    declaration:
       block-declaration
       function-definition
       template-declaration
       explicit-instantiation
       explicit-specialization
       linkage-specification
       namespace-definition

    block-declaration:
       simple-declaration
       asm-definition
       namespace-alias-definition
       using-declaration
       using-directive  */
void
pp_cxx_declaration (cxx_pretty_printer *pp, tree t)
{
  if (!DECL_LANG_SPECIFIC (t))
    pp_cxx_simple_declaration (pp, t);
  else if (DECL_USE_TEMPLATE (t))
    switch (DECL_USE_TEMPLATE (t))
      {
      case 1:
        pp_cxx_template_declaration (pp, t);
        break;
        
      case 2:
        pp_cxx_explicit_specialization (pp, t);
        break;

      case 3:
        pp_cxx_explicit_instantiation (pp, t);
        break;

      default:
        break;
      }
  else switch (TREE_CODE (t))
    {
    case VAR_DECL:
    case TYPE_DECL:
      pp_cxx_simple_declaration (pp, t);
      break;
      
    case FUNCTION_DECL:
      if (DECL_SAVED_TREE (t))
        pp_cxx_function_definition (pp, t);
      else
        pp_cxx_simple_declaration (pp, t);
      break;

    case NAMESPACE_DECL:
      if (DECL_NAMESPACE_ALIAS (t))
        pp_cxx_namespace_alias_definition (pp, t);
      else
        pp_cxx_original_namespace_definition (pp, t);
      break;

    default:
      pp_unsupported_tree (pp, t);
      break;
    }
}


typedef c_pretty_print_fn pp_fun;

void
pp_cxx_pretty_printer_init (cxx_pretty_printer *pp)
{
  pp_c_pretty_printer_init (pp_c_base (pp));
  pp_set_line_maximum_length (pp, 0);

  pp->c_base.declaration = (pp_fun) pp_cxx_declaration;
  pp->c_base.declaration_specifiers = (pp_fun) pp_cxx_decl_specifier_seq;
  pp->c_base.function_specifier = (pp_fun) pp_cxx_function_specifier;
  pp->c_base.type_specifier_seq = (pp_fun) pp_cxx_type_specifier_seq;
  pp->c_base.declarator = (pp_fun) pp_cxx_declarator;
  pp->c_base.direct_declarator = (pp_fun) pp_cxx_direct_declarator;
  pp->c_base.parameter_list = (pp_fun) pp_cxx_parameter_declaration_clause;
  pp->c_base.type_id = (pp_fun) pp_cxx_type_id;
  pp->c_base.abstract_declarator = (pp_fun) pp_cxx_abstract_declarator;
  pp->c_base.direct_abstract_declarator =
    (pp_fun) pp_cxx_direct_abstract_declarator;
  pp->c_base.simple_type_specifier = (pp_fun)pp_cxx_simple_type_specifier;

  /* pp->c_base.statement = (pp_fun) pp_cxx_statement;  */

  pp->c_base.id_expression = (pp_fun) pp_cxx_id_expression;
  pp->c_base.primary_expression = (pp_fun) pp_cxx_primary_expression;
  pp->c_base.postfix_expression = (pp_fun) pp_cxx_postfix_expression;
  pp->c_base.unary_expression = (pp_fun) pp_cxx_unary_expression;
  pp->c_base.multiplicative_expression = (pp_fun) pp_cxx_multiplicative_expression;
  pp->c_base.conditional_expression = (pp_fun) pp_cxx_conditional_expression;
  pp->c_base.assignment_expression = (pp_fun) pp_cxx_assignment_expression;
  pp->c_base.expression = (pp_fun) pp_cxx_expression;
  pp->enclosing_scope = global_namespace;
}
