/* Chrysalide - Outil d'analyse de fichiers binaires
 * abi.c - décodage des noms d'éléments selon l'ABI C++ Itanium
 *
 * Copyright (C) 2018 Cyrille Bagard
 *
 *  This file is part of Chrysalide.
 *
 *  Chrysalide 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  Chrysalide 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 Chrysalide.  If not, see <http://www.gnu.org/licenses/>.
 */


#include "abi.h"


#include <assert.h>
#include <ctype.h>
#include <stdlib.h>


#include <analysis/types/basic.h>
#include <analysis/types/cse.h>
#include <common/cpp.h>
#include <mangling/context-int.h>



/* Liste des opérateurs reconnus */

#define IDT_NL(s) s, sizeof(s) - 1

const itanium_operator_info itanium_demangle_operators[] = {

    { "aN", IDT_NL("&="),               2 },
    { "aS", IDT_NL("="),                2 },
    { "aa", IDT_NL("&&"),               2 },
    { "ad", IDT_NL("&"),                1 },
    { "an", IDT_NL("&"),                2 },
    { "at", IDT_NL("alignof "),         1 },
    { "az", IDT_NL("alignof "),         1 },
    { "cc", IDT_NL("const_cast"),       2 },
    { "cl", IDT_NL("()"),               2 },
    { "cm", IDT_NL(","),                2 },
    { "co", IDT_NL("~"),                1 },
    { "dV", IDT_NL("/="),               2 },
    { "da", IDT_NL("delete[] "),        1 },
    { "dc", IDT_NL("dynamic_cast"),     2 },
    { "de", IDT_NL("*"),                1 },
    { "dl", IDT_NL("delete "),          1 },
    { "ds", IDT_NL(".*"),               2 },
    { "dt", IDT_NL("."),                2 },
    { "dv", IDT_NL("/"),                2 },
    { "eO", IDT_NL("^="),               2 },
    { "eo", IDT_NL("^"),                2 },
    { "eq", IDT_NL("=="),               2 },
    { "ge", IDT_NL(">="),               2 },
    { "gs", IDT_NL("::"),               1 },
    { "gt", IDT_NL(">"),                2 },
    { "ix", IDT_NL("[]"),               2 },
    { "lS", IDT_NL("<<="),              2 },
    { "le", IDT_NL("<="),               2 },
    { "li", IDT_NL("operator\"\" "),    1 },
    { "ls", IDT_NL("<<"),               2 },
    { "lt", IDT_NL("<"),                2 },
    { "mI", IDT_NL("-="),               2 },
    { "mL", IDT_NL("*="),               2 },
    { "mi", IDT_NL("-"),                2 },
    { "ml", IDT_NL("*"),                2 },
    { "mm", IDT_NL("--"),               1 },
    { "na", IDT_NL("new[]"),            3 },
    { "ne", IDT_NL("!="),               2 },
    { "ng", IDT_NL("-"),                1 },
    { "nt", IDT_NL("!"),                1 },
    { "nw", IDT_NL("new"),              3 },
    { "oR", IDT_NL("|="),               2 },
    { "oo", IDT_NL("||"),               2 },
    { "or", IDT_NL("|"),                2 },
    { "pL", IDT_NL("+="),               2 },
    { "pl", IDT_NL("+"),                2 },
    { "pm", IDT_NL("->*"),              2 },
    { "pp", IDT_NL("++"),               1 },
    { "ps", IDT_NL("+"),                1 },
    { "pt", IDT_NL("->"),               2 },
    { "qu", IDT_NL("?"),                3 },
    { "rM", IDT_NL("%="),               2 },
    { "rS", IDT_NL(">>="),              2 },
    { "rc", IDT_NL("reinterpret_cast"), 2 },
    { "rm", IDT_NL("%"),                2 },
    { "rs", IDT_NL(">>"),               2 },
    { "sc", IDT_NL("static_cast"),      2 },
    { "st", IDT_NL("sizeof "),          1 },
    { "sz", IDT_NL("sizeof "),          1 }
};

/* Substitutions standards */

typedef struct _itanium_std_subst_info
{
    char code;                              /* Identifiant associé         */
    const char *class;                      /* Classe visée dans l'espace  */

} itanium_std_subst_info;

const itanium_std_subst_info itanium_standard_substitutions[] = {

    { 't', NULL },
    { 'a', "allocator" },
    { 'b', "basic_string" },
    { 's', "string" },
    { 'i', "istream" },
    { 'o', "ostream" },
    { 'd', "iostream" }

};


/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_encoding(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_name(GItaniumDemangling *);

/* Détermine si le composant suivant correspond à un type donné. */
static bool is_itd_unscoped_name(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_unscoped_name(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_unscoped_template_name(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_nested_name(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_prefix(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_template_prefix(GItaniumDemangling *);

/* Détermine si le composant suivant correspond à un type donné. */
static bool is_itd_unqualified_name(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_unqualified_name(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_source_name(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static bool itd_number(GItaniumDemangling *, ssize_t *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_identifier(GItaniumDemangling *, size_t);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_operator_name(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_special_name(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_call_offset(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_nv_offset(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_v_offset(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_ctor_dtor_name(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_type(GItaniumDemangling *);

/* Extrait une propriété de composant pour un contexte Itanium. */
static TypeQualifier itd_cv_qualifiers(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_builtin_type(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_function_type(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_bare_function_type(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_class_enum_type(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_array_type(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_pointer_to_member_type(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_template_param(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_template_template_param(GItaniumDemangling *);

/* Détermine si le composant suivant correspond à un type donné. */
static bool is_itd_template_args(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_template_args(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_template_arg(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_expression(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_value_to_string(GItaniumDemangling *, bool);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_expr_primary(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_data_member_prefix(GItaniumDemangling *);

/* Extrait un composant dans un contexte Itanium. */
static bool itd_seq_id(GItaniumDemangling *, char, size_t *);

/* Extrait un composant dans un contexte Itanium. */
static itanium_component *itd_substitution(GItaniumDemangling *);



#define itd_local_name(ctx) NULL



/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

itanium_component *itd_mangled_name(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */

    /**
     * La règle traitée ici est la suivante :
     *
     * <mangled-name> ::= _Z <encoding>
     *
     */

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    if (!check_input_buffer_char(ibuf, '_'))
        return NULL;

    if (!check_input_buffer_char(ibuf, 'Z'))
        return NULL;

    result = itd_encoding(context);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_encoding(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    char peek;                              /* Prochain caractère lu       */
    itanium_component *types;               /* Composant 'bare-function...'*/

    /**
     * La règle traitée ici est la suivante :
     *
     * <encoding> ::= <function name> <bare-function-type>
     *            ::= <data name>
     *            ::= <special-name>
     *
     */

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    peek = peek_input_buffer_char(ibuf);

    if (peek == 'T' || peek == 'G')
        result = itd_special_name(context);

    else
    {
        result = itd_name(context);

        if (result != NULL)
        {
            types = itd_bare_function_type(context);

            if (types != NULL)
                result = itd_make_binary(ICT_FUNCTION_ENCODING, result, types);

            /**
             * Si le chargement des types échoue, il peut y avoir deux explications :
             *    - on ne chargeait qu'un simple nom.
             *    - il y a eu une erreur de décodage pour les types.
             *
             * Le tampon aura été vidé dans le premier cas uniquement,
             * donc on laisse le contexte détecter une éventuelle erreur.
             */

        }

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_name(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    itd_state saved;                        /* Position d'analyse courante */
    itanium_component *tname;               /* Composant '...template-name'*/
    itanium_component *targs;               /* Composant 'template-args'   */

    /**
     * La règle traitée ici est la suivante :
     *
     * <name> ::= <nested-name>
     *        ::= <unscoped-name>
     *        ::= <unscoped-template-name> <template-args>
     *        ::= <local-name>
     *
     */

    g_itanium_demangling_push_state(context, &saved);

    result = itd_nested_name(context);

    if (result == NULL)
    {
        g_itanium_demangling_pop_state(context, &saved);

        tname = itd_unscoped_template_name(context);

        if (tname != NULL)
        {
            targs = itd_template_args(context);

            if (targs != NULL)
                result = itd_make_binary(ICT_TEMPLATE_NAME_ARGS, tname, targs);

            else
                itd_unref_comp(tname);

        }

    }

    /**
     * La règle <unscoped-name> doit être traitée après <unscoped-template-name>,
     * car ces deux dernières ont une base commune et la seconde peut avoir besoin
     * d'aller plus loin avec la règle <template-args>.
     *
     * On termine donc par moins gourmand si la règle la plus complexe n'a pas abouti.
     */

    if (result == NULL)
    {
        g_itanium_demangling_pop_state(context, &saved);
        result = itd_unscoped_name(context);
    }

    if (result == NULL)
    {
        g_itanium_demangling_pop_state(context, &saved);
        result = itd_local_name(context);
    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Détermine si le composant suivant correspond à un type donné.*
*                                                                             *
*  Retour      : true si le décodage va à priori réussir, false sinon.        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool is_itd_unscoped_name(GItaniumDemangling *context)
{
    bool result;                            /* Bilan à retourner           */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    char peek;                              /* Prochain caractère lu       */
    char next_peek;                         /* Caractère après le prochain */

    /**
     * La règle anticipée ici est la suivante :
     *
     * <unscoped-name> ::= <unqualified-name>
     *                 ::= St <unqualified-name>   # ::std::
     *
     */

    result = is_itd_unqualified_name(context);

    if (!result)
    {
        ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

        peek = peek_input_buffer_char(ibuf);

        if (peek == 'S')
        {
            next_peek = peek_input_buffer_next_char(ibuf);

            result = (next_peek == 't');

        }

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_unscoped_name(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    char peek;                              /* Prochain caractère lu       */
    char next_peek;                         /* Caractère après le prochain */

    /**
     * La règle traitée ici est la suivante :
     *
     * <unscoped-name> ::= <unqualified-name>
     *                 ::= St <unqualified-name>   # ::std::
     *
     */


    if (is_itd_unqualified_name(context))
        result = itd_unqualified_name(context);

    else
    {
        ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

        peek = peek_input_buffer_char(ibuf);

        if (peek == 'S')
        {
            next_peek = peek_input_buffer_next_char(ibuf);

            if (next_peek == 't')
            {
                advance_input_buffer(ibuf, 2);

                result = itd_unqualified_name(context);

                if (result != NULL)
                    result = itd_make_unary(ICT_STD_UNSCOPED_NAME, result);

            }

            else
                result = NULL;

        }

        else
            result = NULL;

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_unscoped_template_name(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    char peek;                              /* Prochain caractère lu       */

    /**
     * La règle traitée ici est la suivante :
     *
     * <unscoped-template-name> ::= <unscoped-name>
     *                          ::= <substitution>
     *
     */

    if (is_itd_unscoped_name(context))
    {
        result = itd_unscoped_name(context);

        if (result != NULL)
            g_itanium_demangling_add_substitution(context, result);

    }

    else
    {
        ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

        peek = peek_input_buffer_char(ibuf);

        if (peek == 'S')
            result = itd_substitution(context);

        else
            result = NULL;

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_nested_name(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    TypeQualifier qualifier;                /* Propriétés supplémentaires  */
    itd_state saved;                        /* Position d'analyse courante */
    itanium_component *left;                /* Première partie             */
    itanium_component *right;               /* Seconde partie              */

    /**
     * La règle traitée ici est la suivante :
     *
     * <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E
     *               ::= N [<CV-qualifiers>] <template-prefix> <template-args> E
     *
     */

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    if (!check_input_buffer_char(ibuf, 'N'))
        return NULL;

    qualifier = itd_cv_qualifiers(context);

    result = NULL;

    g_itanium_demangling_push_state(context, &saved);

    /**
     * Comme <prefix> <unqualified-name> peut aussi être <template-prefix>,
     * on commence par traiter la seconde règle.
     */

    left = itd_template_prefix(context);

    if (left != NULL)
    {
        /**
         * Quand son traitement est un succès, <template-prefix> doit toujours
         * se terminer par <template-args>.
         */

        assert(is_itd_template_args(context));

        right = itd_template_args(context);

        if (right != NULL)
        {
            if (check_input_buffer_char(ibuf, 'E'))
                result = itd_make_binary(ICT_TEMPLATE_NAME_ARGS, left, right);

            else
            {
                itd_unref_comp(left);
                itd_unref_comp(right);
            }

        }

        else
            itd_unref_comp(left);

    }

    if (result == NULL)
    {
        g_itanium_demangling_pop_state(context, &saved);

        left = itd_prefix(context);

        /**
         * Quand son traitement est un succès, <prefix> doit toujours
         * se terminer par <unqualified-name>.
         */

        assert(left == NULL || (left != NULL && is_itd_unqualified_name(context)));

        /**
         * La règle <prefix> peut être vide, donc on se doit de tenter un
         * <unqualified-name> dans tous les cas.
         */

        right = itd_unqualified_name(context);

        if (right != NULL)
        {
            if (check_input_buffer_char(ibuf, 'E'))
                result = itd_make_binary(ICT_NESTED_NAME, left, right);

            else
            {
                if (left != NULL)
                    itd_unref_comp(left);
                itd_unref_comp(right);
            }

        }

        else if (left != NULL)
            itd_unref_comp(left);

    }

    if (result != NULL)
        itd_make_qualified_type(result, qualifier);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_prefix(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    char peek;                              /* Prochain caractère lu       */
    itanium_component *targs;               /* Composant 'template-args'   */
    itd_state saved;                        /* Position d'analyse courante */
    itanium_component *further;             /* Tentative de progression    */
    itanium_component *backup;              /* Solution de repli existante */

    /**
     * La règle traitée ici est la suivante :
     *
     * <prefix> ::= <unqualified-name>
     *          ::= <prefix> <unqualified-name>
     *          ::= <template-prefix> <template-args>
     *          ::= <template-param>
     *          ::= <decltype>
     *          ::= <prefix> <data-member-prefix>
     *          ::= <substitution>
     *
     * Réorganisée, cette règle <prefix> a pour définition :
     *
     * <prefix> ::= <unqualified-name>
     *          ::= <template-param>
     *          ::= <decltype>
     *          ::= <substitution>
     *          ::= <template-prefix> <template-args>
     *
     *          ::= <prefix> <unqualified-name>
     *          ::= <prefix> <data-member-prefix>
     *
     * Il existe cependant une boucle infinie avec une règle de <prefix> :
     *
     * <template-prefix> ::= <prefix> <template unqualified-name>
     *
     * On déplie donc les règles afin de casser la boucle, quitte à gérer ici
     * une partie de la règle <template-prefix> :
     *
     * <prefix> ::= <unqualified-name>
     *          ::= <template-param>
     *          ::= <decltype>
     *          ::= <substitution>
     *
     *          ::= <unqualified-name> <template-args>
     *          ::= <template-param> <template-args>
     *          ::= <substitution> <template-args>
     *
     *          ::= <prefix> <unqualified-name>
     *          ::= <prefix> <unqualified-name> <template-args>
     *          ::= <prefix> <data-member-prefix>
     */

    result = NULL;

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    peek = peek_input_buffer_char(ibuf);

    if (peek == 'S')
        result = itd_substitution(context);

    else if (peek == 'T')
        result = itd_template_param(context);

    else if (is_itd_unqualified_name(context))
        result = itd_unqualified_name(context);

    /**
     * Si le traitement débouche sur une règle <template-args>, c'est qu'il
     * était en fait potentiellement dans une voie <template-prefix>.
     *
     * Cette voie est vérifiée après coup en analysant la suite.
     */

    if (is_itd_template_args(context))
    {
        g_itanium_demangling_push_state(context, &saved);

        /* Ajout de la substitution tirée de <template-prefix> */
        g_itanium_demangling_add_substitution(context, result);

        targs = itd_template_args(context);

        if (targs != NULL)
        {
            if (!is_itd_unqualified_name(context))
            {
                g_itanium_demangling_pop_state(context, &saved);
                itd_unref_comp(targs);
            }

            else
                result = itd_make_binary(ICT_TEMPLATE_NAME_ARGS, result, targs);

        }

        else
        {
            itd_unref_comp(result);
            result = NULL;
            goto done;
        }

    }

    if (result == NULL)
        goto done;

    g_itanium_demangling_add_substitution(context, result);

    /* Tentative de rebouclage sur la règle <prefix> */

    while (count_input_buffer_remaining(ibuf) > 0)
    {
        g_itanium_demangling_push_state(context, &saved);

        /**
         * Comme <data-member-prefix> commence par une régle <source-name> et
         * complète ensuite sa définition et comme <unqualified-name> contient
         * également cette même règle <source-name> sans l'étendre, on commence
         * par traiter la règle <data-member-prefix>.
         */

        further = itd_data_member_prefix(context);

        if (further == NULL)
        {
            g_itanium_demangling_pop_state(context, &saved);
            further = itd_unqualified_name(context);
        }

        if (further == NULL)
            break;

        backup = result;
        itd_ref_comp(backup);

        result = itd_make_binary(ICT_PREFIX_BINARY, result, further);

        /* Ajout de la substitution tirée de <[template-]prefix> */
        g_itanium_demangling_add_substitution(context, result);

        if (is_itd_template_args(context))
        {
            targs = itd_template_args(context);

            if (targs == NULL)
            {
                itd_unref_comp(backup);
                itd_unref_comp(result);
                result = NULL;
                goto done;
            }

            result = itd_make_binary(ICT_TEMPLATE_NAME_ARGS, result, targs);

            g_itanium_demangling_add_substitution(context, result);

        }

        /* Si l'ensemble ne se termine pas par la règle finale attendue, on rétropédale */
        if (!is_itd_unqualified_name(context))
        {
            itd_unref_comp(result);

            result = backup;

            g_itanium_demangling_pop_state(context, &saved);

            break;

        }

        itd_unref_comp(backup);

    }

 done:

    /**
     * Quand son traitement est un succès, <prefix> doit toujours
     * se terminer par <unqualified-name>.
     */

    if (result != NULL)
    {
        if (!is_itd_unqualified_name(context))
        {
            itd_unref_comp(result);
            result = NULL;
        }

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_template_prefix(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    bool new_subst;                         /* Ajoute d'une substitution ? */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    char peek;                              /* Prochain caractère lu       */
    itd_state saved;                        /* Position d'analyse courante */
    itanium_component *name;                /* Second d'un couple de comp. */

    /**
     * La règle traitée ici est la suivante :
     *
     * <template-prefix> ::= <template unqualified-name>
     *                   ::= <prefix> <template unqualified-name>
     *                   ::= <template-param>
     *                   ::= <substitution>
     *
     * Il existe cependant une boucle infinie avec une règle de <prefix> :
     *
     * <prefix> ::= <template-prefix> <template-args>
     *
     * On traite donc cette règle de <prefix> en dernier, quand les autres
     * options ont été épuisées.
     */

    result = NULL;

    new_subst = true;

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    peek = peek_input_buffer_char(ibuf);

    g_itanium_demangling_push_state(context, &saved);

    if (peek == 'S')
    {
        result = itd_substitution(context);
        new_subst = false;
    }

    else if (peek == 'T')
        result = itd_template_param(context);

    else if (is_itd_unqualified_name(context))
        result = itd_unqualified_name(context);

    /* Vérification : a-t-on empiété sur une règle <prefix> ? */

    if (result != NULL)
    {
        if (is_itd_template_args(context))
            goto done;

        else
        {
            new_subst = true;

            itd_unref_comp(result);
            result = NULL;

            g_itanium_demangling_pop_state(context, &saved);

        }

    }

    /* Tentative avec <prefix> <template unqualified-name> en dernier recours */

    result = itd_prefix(context);

    if (result != NULL)
    {
        /**
         * Quand son traitement est un succès, <prefix> doit toujours
         * se terminer par <unqualified-name>.
         *
         * De même, toutes les règles <template-prefix> se poursuivent avec une
         * règle <template-args> ; la procédure n'est donc un succès que dans ce cas.
         */

        assert(is_itd_unqualified_name(context));

        name = itd_unqualified_name(context);

        if (name != NULL)
            result = itd_make_binary(ICT_TPREFIX_BINARY, result, name);

        else
        {
            itd_unref_comp(result);
            result = NULL;
        }

    }

 done:

    if (result != NULL)
    {
        if (!is_itd_template_args(context))
        {
            itd_unref_comp(result);
            result = NULL;
        }

        else if (new_subst)
            g_itanium_demangling_add_substitution(context, result);

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Détermine si le composant suivant correspond à un type donné.*
*                                                                             *
*  Retour      : true si le décodage va à priori réussir, false sinon.        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool is_itd_unqualified_name(GItaniumDemangling *context)
{
    bool result;                            /* Bilan à retourner           */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    char peek;                              /* Prochain caractère lu       */

    /**
     * La règle anticipée ici est la suivante :
     *
     * <unqualified-name> ::= <operator-name>
     *                    ::= <ctor-dtor-name>
     *                    ::= <source-name>
     *
     */

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    peek = peek_input_buffer_char(ibuf);

    result = islower(peek)                  /* <operator-name> */
           || (peek == 'C' || peek == 'D')  /* <ctor-dtor-name> */
           || isdigit(peek);                /* <source-name> */

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_unqualified_name(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    char peek;                              /* Prochain caractère lu       */

    /**
     * La règle traitée ici est la suivante :
     *
     * <unqualified-name> ::= <operator-name>
     *                    ::= <ctor-dtor-name>
     *                    ::= <source-name>
     *
     */

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    peek = peek_input_buffer_char(ibuf);

    if (islower(peek))
        result = itd_operator_name(context);

    else if (peek == 'C' || peek == 'D')
        result = itd_ctor_dtor_name(context);

    else if (isdigit(peek))
        result = itd_source_name(context);

    else
        result = NULL;

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_source_name(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    ssize_t number;                         /* Taille positive             */

    /**
     * La règle traitée ici est la suivante :
     *
     * <source-name> ::= <positive length number> <identifier>
     *
     */

    if (!itd_number(context, &number))
        return NULL;

    if (number <= 0)
        return NULL;

    result = itd_identifier(context, number);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                size    = taille positive ou non lue. [OUT]                  *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Bilan de l'opération (un chifre lu au moins).                *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool itd_number(GItaniumDemangling *context, ssize_t *size)
{
    bool result;                            /* Validité à renvoyer         */
    bool negative;                          /* Taille négative ?           */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    char peek;                              /* Prochain caractère lu       */

    /**
     * La règle traitée ici est la suivante :
     *
     * <number> ::= [n] <non-negative decimal integer>
     *
     */

    result = false;

    negative = false;

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    peek = peek_input_buffer_char(ibuf);

    if (peek == 'n')
    {
        negative = true;
        advance_input_buffer(ibuf, 1);
        peek = peek_input_buffer_char(ibuf);
    }

    *size = 0;

    while (isdigit(peek))
    {
        result = true;
        *size = *size * 10 + peek - '0';
        advance_input_buffer(ibuf, 1);
        peek = peek_input_buffer_char(ibuf);
    }

    if (negative)
        *size *= -1;

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                length  = taille de l'identifiant à retrouver.               *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_identifier(GItaniumDemangling *context, size_t length)
{
    itanium_component *result;              /* Construction à retourner    */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    const char *data;                       /* Données restantes           */
    size_t remaining;                       /* Quantité d'octets           */

    /**
     * La règle traitée ici est la suivante :
     *
     * <identifier> ::= <unqualified source code identifier>
     *
     */

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    data = get_input_buffer_string(ibuf, &remaining);

    if (length > remaining)
        return NULL;

    result = itd_make_name(data, length);

    if (result != NULL)
        advance_input_buffer(ibuf, length);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_operator_name(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    char code[2];                           /* Code à venir lire           */
    itanium_component *type;                /* Type transtypé              */
    itanium_operator_info info;             /* Clef des informations       */
    itanium_operator_info *found;           /* Informations complètes      */

    /**
     * La règle traitée ici est la suivante :
     *
     * <operator-name> ::= nw    # new
     *                 ::= na    # new[]
     *                 ::= dl    # delete
     *                 ::= da    # delete[]
     *                 ::= ps    # + (unary)
     *                 ::= ng    # - (unary)
     *                 ::= ad    # & (unary)
     *                 ::= de    # * (unary)
     *                 ::= co    # ~
     *                 ::= pl    # +
     *                 ::= mi    # -
     *                 ::= ml    # *
     *                 ::= dv    # /
     *                 ::= rm    # %
     *                 ::= an    # &
     *                 ::= or    # |
     *                 ::= eo    # ^
     *                 ::= aS    # =
     *                 ::= pL    # +=
     *                 ::= mI    # -=
     *                 ::= mL    # *=
     *                 ::= dV    # /=
     *                 ::= rM    # %=
     *                 ::= aN    # &=
     *                 ::= oR    # |=
     *                 ::= eO    # ^=
     *                 ::= ls    # <<
     *                 ::= rs    # >>
     *                 ::= lS    # <<=
     *                 ::= rS    # >>=
     *                 ::= eq    # ==
     *                 ::= ne    # !=
     *                 ::= lt    # <
     *                 ::= gt    # >
     *                 ::= le    # <=
     *                 ::= ge    # >=
     *                 ::= nt    # !
     *                 ::= aa    # &&
     *                 ::= oo    # ||
     *                 ::= pp    # ++
     *                 ::= mm    # --
     *                 ::= cm    # ,
     *                 ::= pm    # ->*
     *                 ::= pt    # ->
     *                 ::= cl    # ()
     *                 ::= ix    # []
     *                 ::= qu    # ?
     *                 ::= st    # sizeof (a type)
     *                 ::= sz    # sizeof (an expression)
     *                 ::= cv <type> # (cast)
     *                 ::= v <digit> <source-name>   # vendor extended operator
     *
     */

    result = NULL;

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    if (!get_input_buffer_next_char_carefully(ibuf, &code[0]))
        goto itd_operator_name_exit;

    if (code[0] == 'v')
    {
        type = itd_type(context);

        if (type != NULL)
            result = itd_make_cast_operator(type);
        else
            result = NULL;

        goto itd_operator_name_exit;

    }

    if (!get_input_buffer_next_char_carefully(ibuf, &code[1]))
        goto itd_operator_name_exit;

    if (code[0] == 'c' && code[1] == 'v')
    {
        result = NULL;
        goto itd_operator_name_exit;
    }

    /* Recherche dans la liste des opérateurs reconnus */

    info.code = code;

    int comp_itanium_operators(const itanium_operator_info *a, const itanium_operator_info *b)
    {
        int result;                         /* Bilan à renvoyer            */

        if (a->code[0] < b->code[0])
            result = -1;
        else if (a->code[0] > b->code[0])
            result = 1;
        else
        {
            if (a->code[1] < b->code[1])
                result = -1;
            else if (a->code[1] > b->code[1])
                result = 1;
            else
                result = 0;
        }

        return result;

    }

    found = bsearch(&info, itanium_demangle_operators,
                    ARRAY_SIZE(itanium_demangle_operators),
                    sizeof(itanium_operator_info), (__compar_fn_t)comp_itanium_operators);

    if (found != NULL)
        result = itd_make_operator(found);

 itd_operator_name_exit:

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_special_name(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    char next;                              /* Caractère suivant           */
    char peek;                              /* Prochain caractère lu       */
    itanium_component *offset1;             /* Décalage extrait #1         */
    itanium_component *offset2;             /* Décalage extrait #2         */
    itanium_component *encoding;            /* Encodage suivant            */

    /**
     * La règle traitée ici est la suivante :
     *
     * <special-name> ::= TV <type>  # virtual table
     *                ::= TT <type>  # VTT structure (construction vtable index)
     *                ::= TI <type>  # typeinfo structure
     *                ::= TS <type>  # typeinfo name (null-terminated byte string)
     *                ::= Tc <call-offset> <call-offset> <base encoding>
     *                     # base is the nominal target function of thunk
     *                     # first call-offset is 'this' adjustment
     *                     # second call-offset is result adjustment
     *                ::= T <call-offset> <base encoding>
     *                     # base is the nominal target function of thunk
     */

    result = NULL;

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    if (!get_input_buffer_next_char_carefully(ibuf, &next))
        goto exit_eof;

    if (next == 'T')
    {
        peek = peek_input_buffer_char(ibuf);

        switch (peek)
        {
            case 'V':

                advance_input_buffer(ibuf, 1);

                result = itd_type(context);

                if (result != NULL)
                    result = itd_make_unary(ICT_SPECIAL_NAME_VTABLE, result);

                break;

            case 'T':

                advance_input_buffer(ibuf, 1);

                result = itd_type(context);

                if (result != NULL)
                    result = itd_make_unary(ICT_SPECIAL_NAME_VSTRUCT, result);

                break;

            case 'I':
                advance_input_buffer(ibuf, 1);
                result = itd_type(context);
                break;

            case 'S':
                advance_input_buffer(ibuf, 1);
                result = itd_type(context);
                break;

            case 'c':

                advance_input_buffer(ibuf, 1);

                offset1 = itd_call_offset(context);
                if (offset1 == NULL) break;

                offset2 = itd_call_offset(context);
                if (offset2 == NULL)
                {
                    itd_unref_comp(offset1);
                    break;
                }

                encoding = itd_encoding(context);

                if (encoding == NULL)
                {
                    itd_unref_comp(offset1);
                    itd_unref_comp(offset2);
                }

                else
                    result = itd_make_ternary(ICT_FUNCTION_COVARIANT_THUNK, encoding, offset1, offset2);

                break;

            case '\0':

                /**
                 * Si on se trouve à la fin du tampon, on n'avance pas aveuglément !
                 */

                result = NULL;
                break;

            default:

                advance_input_buffer(ibuf, 1);

                offset1 = itd_call_offset(context);
                if (offset1 == NULL) break;

                encoding = itd_encoding(context);

                if (encoding == NULL)
                    itd_unref_comp(offset1);
                else
                    result = itd_make_binary(ICT_FUNCTION_THUNK, encoding, offset1);

                break;

        }

    }

 exit_eof:

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_call_offset(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    char next;                              /* Caractère suivant           */

    /**
     * La règle traitée ici est la suivante :
     *
     * <call-offset> ::= h <nv-offset> _
     *               ::= v <v-offset> _
     */

    result = NULL;

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    if (get_input_buffer_next_char_carefully(ibuf, &next))
        goto exit_eof;

    switch (next)
    {
        case 'h':
            result = itd_nv_offset(context);
            break;

        case 'v':
            result = itd_v_offset(context);
            break;

    }

    if (result != NULL && !check_input_buffer_char(ibuf, '_'))
    {
        itd_unref_comp(result);
        result = NULL;
    }

 exit_eof:

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_nv_offset(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    ssize_t offset;                         /* Décalage obtenu             */

    /**
     * La règle traitée ici est la suivante :
     *
     * <nv-offset> ::= <offset number>
     *                     # non-virtual base override
     */

    if (!itd_number(context, &offset))
        return NULL;

    result = itd_make_offset(ICT_NON_VIRTUAL_OFFSET, offset);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_v_offset(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    ssize_t offset;                         /* Décalage obtenu #1          */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    ssize_t voffset;                        /* Décalage obtenu #2          */

    /**
     * La règle traitée ici est la suivante :
     *
     * <v-offset>  ::= <offset number> _ <virtual offset number>
     *                     # virtual base override, with vcall offset
     */

    if (!itd_number(context, &offset))
        return NULL;

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    if (!check_input_buffer_char(ibuf, '_'))
        return NULL;

    if (!itd_number(context, &voffset))
        return NULL;

    result = itd_make_binary(ICT_DOUBLE_OFFSET,
                             itd_make_offset(ICT_NON_VIRTUAL_OFFSET, offset),
                             itd_make_offset(ICT_VIRTUAL_OFFSET, voffset));

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_ctor_dtor_name(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    char next;                              /* Caractère suivant           */
    ItaniumComponentType type;              /* Type de composant           */

    /**
     * La règle traitée ici est la suivante :
     *
     * <ctor-dtor-name> ::= C1   # complete object constructor
     *                  ::= C2   # base object constructor
     *                  ::= C3   # complete object allocating constructor
     *                  ::= D0   # deleting destructor
     *                  ::= D1   # complete object destructor
     *                  ::= D2   # base object destructor
     */

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    next = peek_input_buffer_char(ibuf);

    if (next == 'C')
        type = ICT_CONSTRUCTOR;
    else if (next == 'D')
        type = ICT_DESTRUCTOR;
    else
        return NULL;

    advance_input_buffer(ibuf, 1);

    next = peek_input_buffer_char(ibuf);

    if (next != '0' && next != '1' && next != '2')
        return NULL;

    advance_input_buffer(ibuf, 1);

    result = itd_make_with_type(type);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_type(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    TypeQualifier qualifier;                /* Propriétés supplémentaires  */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    bool handled;                           /* Prise en compte effectuée ? */
    itanium_component *sub;                 /* Sous-type lié à associer    */
    itanium_component *vendor;              /* Extension propriétaire      */
    GDataType *builtin;                     /* Type construit              */
    itd_state saved;                        /* Position d'analyse courante */
    itanium_component *targs;               /* Composant 'template-args'   */
    ItaniumComponentType comp_type;         /* Type de composant final     */

    /**
     * La règle traitée ici est la suivante :
     *
     * <type> ::= <builtin-type>
     *        ::= <function-type>
     *        ::= <class-enum-type>
     *        ::= <array-type>
     *        ::= <pointer-to-member-type>
     *        ::= <template-param>
     *        ::= <template-template-param> <template-args>
     *        ::= <substitution> # See Compression below
     *        ::= <CV-qualifiers> <type>
     *        ::= P <type>   # pointer-to
     *        ::= R <type>   # reference-to
     *        ::= O <type>   # rvalue reference-to (C++0x)
     *        ::= C <type>   # complex pair (C 2000)
     *        ::= G <type>   # imaginary (C 2000)
     *        ::= U <source-name> <type> # vendor extended type qualifier
     *        ::= Dp <type>  # pack expansion (C++0x)
     *
     */

    result = NULL;

    qualifier = itd_cv_qualifiers(context);

    if (qualifier != TQF_NONE)
    {
        result = itd_type(context);

        if (result != NULL)
            result = itd_make_qualified_type(result, qualifier);

        goto itd_type_end;

    }

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    handled = false;

    switch (peek_input_buffer_char(ibuf))
    {
        case 'F':
            result = itd_function_type(context);
            handled = true;
            break;

        case 'A':
            result = itd_array_type(context);
            handled = true;
            break;

        case 'M':
            result = itd_pointer_to_member_type(context);
            handled = true;
            break;

        case 'P':

            advance_input_buffer(ibuf, 1);

            sub = itd_type(context);
            if (sub == NULL) return NULL;

            result = itd_make_unary(ICT_POINTER_TO, sub);
            handled = true;
            break;

        case 'R':

            advance_input_buffer(ibuf, 1);

            sub = itd_type(context);
            if (sub == NULL) return NULL;

            result = itd_make_unary(ICT_REFERENCE_TO, sub);
            handled = true;
            break;

        case 'O':

            advance_input_buffer(ibuf, 1);

            sub = itd_type(context);
            if (sub == NULL) return NULL;

            result = itd_make_unary(ICT_RVALUE_REFERENCE_TO, sub);
            handled = true;
            break;

        case 'C':

            advance_input_buffer(ibuf, 1);

            sub = itd_type(context);
            if (sub == NULL) return NULL;

            result = itd_make_unary(ICT_COMPLEX_PAIR, sub);
            handled = true;
            break;

        case 'G':

            advance_input_buffer(ibuf, 1);

            sub = itd_type(context);
            if (sub == NULL) return NULL;

            result = itd_make_unary(ICT_IMAGINARY, sub);
            handled = true;
            break;

        case 'U':

            advance_input_buffer(ibuf, 1);

            result = NULL;

            vendor = itd_source_name(context);

            if (vendor == NULL)
                result = NULL;

            else
            {
                builtin = g_class_enum_type_new(CET_UNKNOWN, itd_translate_component(vendor, NULL));
                result = itd_make_type(builtin);
                itd_set_type(result, ICT_VENDOR_TYPE);
                itd_unref_comp(vendor);

                sub = itd_type(context);

                if (sub != NULL)
                    itd_unref_comp(sub);

                else
                {
                    itd_unref_comp(result);
                    result = NULL;
                }

            }

            handled = true;
            break;

        case 'D':
            if (peek_input_buffer_next_char(ibuf) == 'p')
            {
                advance_input_buffer(ibuf, 2);

                result = itd_type(context);
                handled = true;

            }
            break;

        case 'T':

            /**
             * Comme on a la définition suivante :
             *
             * <template-template-param> ::= <template-param>
             *                           ::= <substitution>
             *
             * On ne sait pas laquelle de ces deux directions prendre :
             *
             * <type> ::= <template-param>
             *        ::= <template-template-param> <template-args>
             *
             * Comme <template-args> commence toujour par un I, on teste
             * le caractère courant après <template-param> et on revient
             * un poil en arrière au besoin.
             *
             * Le cas <substitution> est traité de façon similaire après.
             */

            g_itanium_demangling_push_state(context, &saved);

            result = itd_template_param(context);

            if (result != NULL)
            {
                if (peek_input_buffer_char(ibuf) == 'I')
                {
                    itd_unref_comp(result);

                    g_itanium_demangling_pop_state(context, &saved);

                    result = itd_template_template_param(context);

                    if (result != NULL)
                    {
                        targs = itd_template_args(context);

                        if (targs != NULL)
                            result = itd_make_binary(ICT_TEMPLATE_NAME_ARGS, result, targs);

                        else
                        {
                            itd_unref_comp(result);
                            result = NULL;
                        }

                    }

                }

            }

            handled = true;
            break;

    }

    if (handled) goto itd_type_end;

    g_itanium_demangling_push_state(context, &saved);

    result = itd_builtin_type(context);
    if (result != NULL) goto itd_type_end;

    g_itanium_demangling_pop_state(context, &saved);

    result = itd_class_enum_type(context);
    if (result != NULL) goto itd_type_end;

    g_itanium_demangling_pop_state(context, &saved);

    /**
     * De façon similaire au cas <template-param> traité au dessus,
     * on guette un usage de <substitution> via :
     *
     * <template-template-param> ::= <template-param>
     *                           ::= <substitution>
     *
     * La distinction se réalise via une liste d'argument, et on tranche
     * cette fois entre les deux directions suivantes :
     *
     * <type> ::= <template-template-param> <template-args>
     *        ::= <substitution> # See Compression below
     */

    g_itanium_demangling_push_state(context, &saved);

    result = itd_substitution(context);

    if (result != NULL && peek_input_buffer_char(ibuf) == 'I')
    {
        itd_unref_comp(result);

        g_itanium_demangling_pop_state(context, &saved);

        result = itd_template_template_param(context);

        if (result != NULL)
        {
            targs = itd_template_args(context);

            if (targs != NULL)
                result = itd_make_binary(ICT_TEMPLATE_NAME_ARGS, result, targs);

            else
            {
                itd_unref_comp(result);
                result = NULL;
            }

        }

    }

 itd_type_end:

    if (result != NULL)
    {
        /**
         * Les spécifications (§ 5.1.9) précisent:
         *
         *    There are two exceptions that appear to be substitution candidates
         *    from the grammar, but are explicitly excluded:
         *
         *       <builtin-type> other than vendor extended types, and
         *       function and operator names other than extern "C" functions.
         *
         * On saute donc éventuelleement certains résultats.
         *
         * Par ailleurs, il existe quelques restrictions à propos des qualificatifs :
         *
         *    For purposes of substitution, given a CV-qualified type, the base
         *    type is substitutible, and the type with all the C, V, and r qualifiers
         *    plus any vendor extended types in the same order-insensitive set is
         *    substitutible; any type with a subset of those qualifiers is not.
         *
         * Le code courant englobe tous les qualificatifs, donc il n'y a pas de
         * mesure particulière à prendre ici.
         */

        comp_type = itd_get_component_type(result);

        if (comp_type != ICT_TYPE
            && (comp_type != ICT_FUNCTION_TYPE || itd_is_external_function(result)))
        {
            g_itanium_demangling_add_substitution(context, result);
        }

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait une propriété de composant pour un contexte Itanium. *
*                                                                             *
*  Retour      : Indication extraite.                                         *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static TypeQualifier itd_cv_qualifiers(GItaniumDemangling *context)
{
    TypeQualifier result;                   /* Valeur à remonter           */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */

    /**
     * La règle traitée ici est la suivante :
     *
     * <CV-qualifiers> ::= [r] [V] [K]  # restrict (C99), volatile, const
     *
     */

    result = TQF_NONE;

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    while (1)
        switch (peek_input_buffer_char(ibuf))
        {
            case 'r':
                result = TQF_RESTRICT;
                advance_input_buffer(ibuf, 1);
                break;

            case 'V':
                result = TQF_VOLATILE;
                advance_input_buffer(ibuf, 1);
                break;

            case 'K':
                result = TQF_CONST;
                advance_input_buffer(ibuf, 1);
                break;

            default:
                goto itd_cv_qualifiers_exit;
                break;

        }

 itd_cv_qualifiers_exit:

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_builtin_type(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    size_t consumed;                        /* Nombre de consommations     */
    BaseType type;                          /* Type reconnu ou BTP_INVALID */
    GDataType *std;                         /* Espace de noms              */
    itanium_component *vendor;              /* Extension propriétaire      */
    GDataType *builtin;                     /* Type construit              */

    /**
     * La règle traitée ici est la suivante :
     *
     * <builtin-type> ::= v  # void
     *                ::= w  # wchar_t
     *                ::= b  # bool
     *                ::= c  # char
     *                ::= a  # signed char
     *                ::= h  # unsigned char
     *                ::= s  # short
     *                ::= t  # unsigned short
     *                ::= i  # int
     *                ::= j  # unsigned int
     *                ::= l  # long
     *                ::= m  # unsigned long
     *                ::= x  # long long, __int64
     *                ::= y  # unsigned long long, __int64
     *                ::= n  # __int128
     *                ::= o  # unsigned __int128
     *                ::= f  # float
     *                ::= d  # double
     *                ::= e  # long double, __float80
     *                ::= g  # __float128
     *                ::= z  # ellipsis
     *                ::= Dd # IEEE 754r decimal floating point (64 bits)
     *                ::= De # IEEE 754r decimal floating point (128 bits)
     *                ::= Df # IEEE 754r decimal floating point (32 bits)
     *                ::= Dh # IEEE 754r half-precision floating point (16 bits)
     *                ::= DF <number> _ # ISO/IEC TS 18661 binary floating point type _FloatN (N bits)
     *                ::= Di # char32_t
     *                ::= Ds # char16_t
     *                ::= Da # auto
     *                ::= Dc # decltype(auto)
     *                ::= Dn # std::nullptr_t (i.e., decltype(nullptr))
     *                ::= u <source-name> # vendor extended type
     *
     */

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    consumed = 1;

    switch (peek_input_buffer_char(ibuf))
    {
        case 'v':
            type = BTP_VOID;
            break;
        case 'w':
            type = BTP_WCHAR_T;
            break;
        case 'b':
            type = BTP_BOOL;
            break;
        case 'c':
            type = BTP_CHAR;
            break;
        case 'a':
            type = BTP_SCHAR;
            break;
        case 'h':
            type = BTP_UCHAR;
            break;
        case 's':
            type = BTP_SHORT;
            break;
        case 't':
            type = BTP_USHORT;
            break;
        case 'i':
            type = BTP_INT;
            break;
        case 'j':
            type = BTP_UINT;
            break;
        case 'l':
            type = BTP_LONG;
            break;
        case 'm':
            type = BTP_ULONG;
            break;
        case 'x':
            type = BTP_LONG_LONG;
            break;
        case 'y':
            type = BTP_ULONG_LONG;
            break;
        case 'n':
            type = BTP_INT128;
            break;
        case 'o':
            type = BTP_UINT128;
            break;
        case 'f':
            type = BTP_FLOAT;
            break;
        case 'd':
            type = BTP_DOUBLE;
            break;
        case 'e':
            type = BTP_LONG_DOUBLE;
            break;
        case 'g':
            type = BTP_FLOAT128;
            break;
        case 'z':
            type = BTP_ELLIPSIS;
            break;

        case 'D':

            consumed = 2;

            switch (peek_input_buffer_next_char(ibuf))
            {
                case 'd':
                    type = BTP_754R_64;
                    break;
                case 'e':
                    type = BTP_754R_128;
                    break;
                case 'f':
                    type = BTP_754R_32;
                    break;
                case 'h':
                    type = BTP_754R_16;
                    break;

                case 'F':
                    advance_input_buffer(ibuf, 2);

                    if (itd_number(context, (ssize_t []) { 0 }) && check_input_buffer_char(ibuf, '_'))
                        type = BTP_754R_N;
                    else
                        type = BTP_INVALID;

                    consumed = 0;
                    break;

                case 'i':
                    type = BTP_CHAR32_T;
                    break;
                case 's':
                    type = BTP_CHAR16_T;
                    break;
                case 'a':
                    type = BTP_AUTO;
                    break;
                case 'c':
                    type = BTP_DECL_AUTO;
                    break;

                case 'n':

                    std = g_class_enum_type_new(CET_NAMESPACE, strdup("std"));

                    builtin = g_class_enum_type_new(CET_CLASS, strdup("nullptr_t"));
                    g_data_type_set_namespace(builtin, std, strdup("::"));

                    result = itd_make_type(builtin);
                    itd_set_type(result, ICT_STD_SUBST);

                    goto done;
                    break;

                default:
                    type = BTP_INVALID;
                    break;

            }

            break;

        case 'u':

            advance_input_buffer(ibuf, 1);

            vendor = itd_source_name(context);

            if (vendor == NULL)
                result = NULL;
            else
            {
                builtin = g_class_enum_type_new(CET_UNKNOWN, itd_translate_component(vendor, NULL));
                result = itd_make_type(builtin);
                itd_unref_comp(vendor);
            }

            goto done;
            break;

        default:
            type = BTP_INVALID;
            break;

    }

    if (type != BTP_INVALID)
    {
        builtin = g_basic_type_new(type);
        result = itd_make_type(builtin);
        advance_input_buffer(ibuf, consumed);
    }
    else
        result = NULL;

 done:

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_function_type(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    char peek;                              /* Prochain caractère dispo.   */
    itanium_component *args;                /* Liste des arguments         */

    /**
     * La règle traitée ici est la suivante :
     *
     * <function-type> ::= F [Y] <bare-function-type> E
     *
     */

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    if (!check_input_buffer_char(ibuf, 'F'))
        return NULL;

    peek = peek_input_buffer_char(ibuf);

    if (peek == 'Y')
        advance_input_buffer(ibuf, 1);

    args = itd_bare_function_type(context);

    if (args == NULL)
        result = NULL;

    else
    {
        result = itd_make_function_type(peek == 'Y', args);

        if (!check_input_buffer_char(ibuf, 'E'))
        {
            itd_unref_comp(result);
            result = NULL;
        }

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_bare_function_type(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    itanium_component *type;                /* Nouvel élément à intégrer   */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    itd_state saved;                        /* Position d'analyse courante */

    /**
     * La règle traitée ici est la suivante :
     *
     * <bare-function-type> ::= <signature type>+
     *       # types are possible return type, then parameter types
     *
     */

    type = itd_type(context);
    if (type == NULL) return NULL;

    result = itd_append_right_to_binary(ICT_TYPES_LIST, NULL, type);

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    while (count_input_buffer_remaining(ibuf) > 0)
    {
        g_itanium_demangling_push_state(context, &saved);

        type = itd_type(context);

        if (type == NULL)
        {
            g_itanium_demangling_pop_state(context, &saved);
            break;
        }

        result = itd_append_right_to_binary(ICT_TYPES_LIST, result, type);

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_class_enum_type(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */

    /**
     * La règle traitée ici est la suivante :
     *
     * <class-enum-type> ::= <name>
     *
     */

    result = itd_name(context);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_array_type(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    ssize_t dim_number;                     /* Dimension par un nombre     */
    itanium_component *type;                /* Type du tableau             */
    itd_state saved;                        /* Position d'analyse courante */
    itanium_component *dim_expr;            /* Dimension via expression    */

    /**
     * La règle traitée ici est la suivante :
     *
     * <array-type> ::= A <positive dimension number> _ <element type>
     *              ::= A [<dimension expression>] _ <element type>
     *
     */

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    if (!check_input_buffer_char(ibuf, 'A'))
        return NULL;

    if (itd_number(context, &dim_number))
    {
        if (!check_input_buffer_char(ibuf, '_'))
            return NULL;

        type = itd_type(context);

        if (type == NULL)
            return NULL;

        result = itd_make_array_with_dim_number(dim_number, type);

    }

    else
    {
        g_itanium_demangling_push_state(context, &saved);

        dim_expr = itd_expression(context);

        if (dim_expr == NULL)
            g_itanium_demangling_pop_state(context, &saved);

        if (!check_input_buffer_char(ibuf, '_'))
        {
            if (dim_expr != NULL)
                itd_unref_comp(dim_expr);
            return NULL;
        }

        type = itd_type(context);

        if (type == NULL)
        {
            if (dim_expr != NULL)
                itd_unref_comp(dim_expr);
            return NULL;
        }

        result = itd_make_array_with_dim_expr(dim_expr, type);

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_pointer_to_member_type(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    itanium_component *class;               /* Classe d'appatenance        */
    itanium_component *member;              /* Membre représenté           */

    /**
     * La règle traitée ici est la suivante :
     *
     * <pointer-to-member-type> ::= M <class type> <member type>
     *
     */

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    if (!check_input_buffer_char(ibuf, 'M'))
        return NULL;

    class = itd_type(context);

    if (class != NULL)
    {
        member = itd_type(context);

        if (member != NULL)
            result = itd_make_pointer_to_memeber_type(class, member);

        else
        {
            itd_unref_comp(class);
            result = NULL;
        }

    }

    else
        result = NULL;

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_template_param(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    char cur;                               /* Caractère analysé           */
    size_t id;                              /* Identifiant de substitution */

    /**
     * La règle traitée ici est la suivante :
     *
     * <template-param> ::= T_  # first template parameter
     *                  ::= T <parameter-2 non-negative number> _
     *
     */

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    if (!check_input_buffer_char(ibuf, 'T'))
        return NULL;

    result = NULL;

    if (get_input_buffer_next_char_carefully(ibuf, &cur))
    {
        if (cur == '_' || isdigit(cur) || isupper(cur))
        {
            if (!itd_seq_id(context, cur, &id))
                return NULL;

            result = g_itanium_demangling_get_template_arg(context, id);

            if (result != NULL)
                result = itd_make_unary(ICT_TEMPLATE_PARAM, result);

        }

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_template_template_param(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    char peek;                              /* Prochain caractère dispo.   */

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    peek = peek_input_buffer_char(ibuf);

    if (peek == 'T')
        result = itd_template_param(context);

    else
        result = itd_substitution(context);

    return result;

}
/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Détermine si le composant suivant correspond à un type donné.*
*                                                                             *
*  Retour      : true si le décodage va à priori réussir, false sinon.        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool is_itd_template_args(GItaniumDemangling *context)
{
    bool result;                            /* Bilan à retourner           */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    char peek;                              /* Prochain caractère lu       */

    /**
     * La règle anticipée ici est la suivante :
     *
     * <template-args> ::= I <template-arg>+ E
     *
     */

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    peek = peek_input_buffer_char(ibuf);

    result = (peek == 'I');

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_template_args(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    itanium_component *arg;                 /* Nouvel argument extrait     */
    itd_state saved;                        /* Position d'analyse courante */

    /**
     * La règle traitée ici est la suivante :
     *
     * <template-args> ::= I <template-arg>+ E
     *
     */

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    if (!check_input_buffer_char(ibuf, 'I'))
        return NULL;

    arg = itd_template_arg(context);
    if (arg == NULL) return NULL;

    result = itd_merge_list_right_to_binary(ICT_TYPES_LIST, NULL, arg);

    while (1)
    {
        g_itanium_demangling_push_state(context, &saved);

        arg = itd_template_arg(context);
        if (arg == NULL)
        {
            g_itanium_demangling_pop_state(context, &saved);
            break;
        }

        result = itd_merge_list_right_to_binary(ICT_TYPES_LIST, result, arg);

    }

    if (!check_input_buffer_char(ibuf, 'E'))
    {
        itd_unref_comp(result);
        return NULL;
    }

    result = itd_make_unary(ICT_TEMPLATE_ARGS, result);

    g_itanium_demangling_add_template_args(context, result);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_template_arg(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    char peek;                              /* Prochain caractère lu       */
    itanium_component *packed;              /* Argument compressé          */

    /**
     * La règle traitée ici est la suivante :
     *
     * <template-arg> ::= <type>                     # type or template
     *                ::= X <expression> E           # expression
     *                ::= <expr-primary>             # simple expressions
     *                ::= J <template-arg>* E        # argument pack
     *
     */

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    peek = peek_input_buffer_char(ibuf);

    if (peek == 'X')
    {
        advance_input_buffer(ibuf, 1);

        result = itd_expression(context);

        if (result != NULL && !check_input_buffer_char(ibuf, 'E'))
        {
            itd_unref_comp(result);
            result = NULL;
        }

    }

    else if (peek == 'L')
        result = itd_expr_primary(context);

    else if (peek == 'J')
    {
        advance_input_buffer(ibuf, 1);

        result = NULL;

        while (peek_input_buffer_char(ibuf) != 'E')
        {
            packed = itd_template_arg(context);

            if (packed == NULL)
            {
                if (result != NULL)
                {
                    itd_unref_comp(result);
                    result = NULL;
                }

                goto packed_failed;

            }

            result = itd_merge_list_right_to_binary(ICT_TYPES_LIST, result, packed);

        }

        if (result == NULL)
            result = itd_make_with_type(ICT_PACKED_EMPTY);

        if (!check_input_buffer_char(ibuf, 'E'))
        {
            itd_unref_comp(result);
            result = NULL;
        }

    }

    else
        result = itd_type(context);

 packed_failed:

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_expression(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    char peek;                              /* Prochain caractère lu       */
    char next_peek;                         /* Caractère après le prochain */
    itanium_component *targs;               /* Composant 'template-args'   */
    ItaniumOperatorType otype;              /* Type d'opérateur            */
    const void *odata;                      /* Données associées           */
    const itanium_operator_info *simple;    /* Données d'opérateur simple  */
    int i;                                  /* Boucle de parcours          */
    itanium_component *list;                /* Liste de sous-expressions   */
    itanium_component *sub;                 /* Sous-expression chargée     */

    /**
     * La règle traitée ici est la suivante :
     *
     * <expression> ::= <unary operator-name> <expression>
     *              ::= <binary operator-name> <expression> <expression>
     *              ::= <trinary operator-name> <expression> <expression> <expression>
     *              ::= st <type>
     *              ::= <template-param>
     *              ::= sr <type> <unqualified-name>                   # dependent name
     *              ::= sr <type> <unqualified-name> <template-args>   # dependent template-id
     *              ::= <expr-primary>
     *
     */

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    peek = peek_input_buffer_char(ibuf);

    if (peek == 'T')
        result = itd_template_param(context);

    else if (peek == 'L')
        result = itd_expr_primary(context);

    else if (islower(peek))
    {
        next_peek = peek_input_buffer_next_char(ibuf);

        if (peek == 's' && next_peek == 't')
        {
            advance_input_buffer(ibuf, 2);

            result = itd_type(context);

        }

        else if (peek == 's' && next_peek == 'r')
        {
            advance_input_buffer(ibuf, 2);

            result = itd_type(context);

            if (result != NULL)
            {
                itd_unref_comp(result);

                result = itd_unqualified_name(context);

                if (result)
                {
                    peek = peek_input_buffer_char(ibuf);

                    if (peek == 'I')
                    {
                        targs = itd_template_args(context);

                        if (targs != NULL)
                            result = itd_make_binary(ICT_TEMPLATE_NAME_ARGS, result, targs);

                        else
                        {
                            itd_unref_comp(result);
                            result = NULL;
                        }

                    }

                }

            }

        }

        else
        {
            result = itd_operator_name(context);

            if (result != NULL)
            {
                odata = itd_get_operator_info(result, &otype);

                switch (otype)
                {
                    case IOT_SIMPLE:

                        simple = (const itanium_operator_info *)odata;

                        list = NULL;

                        for (i = 0; i < simple->args; i++)
                        {
                            sub = itd_expression(context);

                            if (sub == NULL)
                            {
                                if (list != NULL)
                                {
                                    itd_unref_comp(list);
                                    list = NULL;
                                }

                                break;

                            }

                            list = itd_append_right_to_binary(ICT_EXPR_LIST, list, sub);

                        }

                        if (list == NULL)
                        {
                            itd_unref_comp(result);
                            result = NULL;
                        }

                        else
                            result = itd_make_binary(ICT_OPERATED_EXPRESSION, result, list);

                        break;

                    case IOT_CAST:

                        sub = itd_expression(context);

                        if (sub == NULL)
                        {
                            itd_unref_comp(result);
                            result = NULL;
                        }

                        else
                        {
                            list = itd_append_right_to_binary(ICT_EXPR_LIST, NULL, sub);
                            result = itd_make_binary(ICT_OPERATED_EXPRESSION, result, list);
                        }

                        break;

                    default:
                        assert(false);
                        itd_unref_comp(result);
                        result = NULL;
                        break;

                }

            }

        }

    }

    else
        result = NULL;

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                hex     = prise en compte des caractères hexadécimaux ?      *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_value_to_string(GItaniumDemangling *context, bool hex)
{
    itanium_component *result;              /* Construction à retourner    */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    const char *data;                       /* Données restantes           */
    itd_state saved;                        /* Position d'analyse initiale */
    itd_state cur;                          /* Position d'analyse courante */
    char peek;                              /* Prochain caractère lu       */

    /**
     * Les règles traitées ici sont les suivantes :
     *
     * <value number>  # integer literal
     * <value float>   # floating literal
     *
     */

    result = NULL;

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    data = get_input_buffer_text_access(ibuf);

    g_itanium_demangling_push_state(context, &saved);

    while (1)
    {
        peek = peek_input_buffer_char(ibuf);

        switch (peek)
        {
            case '0' ... '9':
                advance_input_buffer(ibuf, 1);
                break;

            case 'a' ... 'f':
                if (hex)
                    advance_input_buffer(ibuf, 1);
                else
                    goto exit_iits;
                break;

            case 'E':
                goto exit_loop;
                break;

            default:
                goto exit_iits;

        }

    }

 exit_loop:

    g_itanium_demangling_push_state(context, &cur);

    if ((cur.pos - saved.pos) > 0)
        result = itd_make_name(data, cur.pos - saved.pos);

 exit_iits:

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_expr_primary(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    itd_state saved;                        /* Position d'analyse courante */
    itanium_component *type;                /* Type de valeur extrait      */
    itd_state saved_value;                  /* Position d'analyse courante */

    /**
     * La règle traitée ici est la suivante :
     *
     * <expr-primary> ::= L <type> <value number> E  # integer literal
     *                ::= L <type> <value float> E   # floating literal
     *                ::= L <mangled-name> E         # external name
     *
     */

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    if (!check_input_buffer_char(ibuf, 'L'))
        return NULL;

    g_itanium_demangling_push_state(context, &saved);

    type = itd_type(context);

    if (type != NULL)
    {
        g_itanium_demangling_push_state(context, &saved_value);

        /* Règle <type> <value number> */

        result = itd_value_to_string(context, false);

        if (result != NULL && !check_input_buffer_char(ibuf, 'E'))
        {
            itd_unref_comp(result);
            result = NULL;
        }

        /* Règle <type> <value float> */

        if (result == NULL)
        {
            g_itanium_demangling_pop_state(context, &saved_value);

            result = itd_value_to_string(context, true);

            if (result != NULL && !check_input_buffer_char(ibuf, 'E'))
            {
                itd_unref_comp(result);
                result = NULL;
            }

        }

        itd_unref_comp(type);

    }
    else
        result = NULL;

    /* Règle <mangled-name> */

    if (result == NULL)
    {
        g_itanium_demangling_pop_state(context, &saved);
        result = itd_mangled_name(context);

        if (result != NULL && !check_input_buffer_char(ibuf, 'E'))
        {
            itd_unref_comp(result);
            result = NULL;
        }

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_data_member_prefix(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */

    result = NULL;

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                cur     = caractère courant.                                 *
*                id      = identifiant lu en cas de succès. [OUT]             *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool itd_seq_id(GItaniumDemangling *context, char cur, size_t *id)
{
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */

    /**
     * La règle traitée ici est la suivante :
     *
     * <seq-id>
     *
     */

    *id = 0;

    /**
     * La fonction n'est appelée que si un début de séquence est détecté.
     * (ie, cur == '_' || isdigit(cur) || isupper(cur)).
     */

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    if (cur != '_')
    {
        do
        {
            if (isdigit(cur))
                *id = *id * 36 + cur - '0';
            else if (isupper(cur))
                *id = *id * 36 + cur - 'A' + 10;
            else
                return false;

            if (!get_input_buffer_next_char_carefully(ibuf, &cur))
                return false;

        }
        while (cur != '_');

        (*id)++;

    }

    return true;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Extrait un composant dans un contexte Itanium.               *
*                                                                             *
*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static itanium_component *itd_substitution(GItaniumDemangling *context)
{
    itanium_component *result;              /* Construction à retourner    */
    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
    char cur;                               /* Caractère analysé           */
    size_t id;                              /* Identifiant de substitution */
    size_t i;                               /* Boucle de parcours          */
    const itanium_std_subst_info *stdinfo;  /* Raccourci de confort        */
    GDataType *std;                         /* Espace de noms              */
    GDataType *type;                        /* Type complet final          */

    /**
     * La règle traitée ici est la suivante :
     *
     * <substitution> ::= S <seq-id> _
     *                ::= S_
     *                ::= St # ::std::
     *                ::= Sa # ::std::allocator
     *                ::= Sb # ::std::basic_string
     *                ::= Ss # ::std::basic_string<char, ::std::char_traits<char>, ::std::allocator<char>>
     *                ::= Si # ::std::basic_istream<char, std::char_traits<char>>
     *                ::= So # ::std::basic_ostream<char, std::char_traits<char>>
     *                ::= Sd # ::std::basic_iostream<char, std::char_traits<char>>
     *
     */

    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;

    if (!check_input_buffer_char(ibuf, 'S'))
        return NULL;

    result = NULL;

    if (!get_input_buffer_next_char_carefully(ibuf, &cur))
        goto exit_eof;

    if (cur == '_' || isdigit(cur) || isupper(cur))
    {
        if (!itd_seq_id(context, cur, &id))
            return NULL;

        result = g_itanium_demangling_get_substitution(context, id);

    }
    else
    {
        for (i = 0; i < ARRAY_SIZE(itanium_standard_substitutions); i++)
        {
            stdinfo = &itanium_standard_substitutions[i];

            if (stdinfo->code == cur)
            {
                std = g_class_enum_type_new(CET_NAMESPACE, strdup("std"));

                if (stdinfo->class == NULL)
                    type = std;

                else
                {
                    type = g_class_enum_type_new(CET_CLASS, strdup(stdinfo->class));
                    g_data_type_set_namespace(type, std, strdup("::"));
                }

                result = itd_make_type(type);
                itd_set_type(result, ICT_STD_SUBST);

                break;

            }

        }

    }

 exit_eof:

    return result;

}