%{


#include <malloc.h>
#include <stdio.h>
#include <string.h>


#include "demangler-int.h"
#include "itanium.h"
#include "../../common/extstr.h"



/**
 * cf. http://www.codesourcery.com/cxx-abi/abi.html#mangling
 */


/* Taille du bond pour les allocations */
#define ITANIUM_ALLOC_CLUSTER   10



/* Décodeur de nom d'éléments selon la définition Intel */
typedef struct _itanium_demangler
{
    name_demangler common;                  /* A laisser en premier        */

    char *identifier;                       /* Identifiant en construction */
    size_t id_allocated;                    /* Taille allouée en mémoire   */
    size_t id_used;                         /* Longueur de la chaîne       */

} itanium_demangler;


/* Indique si une chaîne peut être traitée par le décodeur. */
bool can_be_itanium_demangled(itanium_demangler *, const char *);

/* Procède au décodage d'une chaîne de caractères. */
GBinRoutine *demangle_itanium_routine(itanium_demangler *, const char *);



/* Réinitialise le constructeur d'identifiants. */
void reset_itanium_identifier_building(itanium_demangler *);

/* Construit à la volée un identifiant. */
void build_itanium_identifier(itanium_demangler *, char);




/* Borne la longueur d'une chaîne à lire. */
extern void set_itanium_text_length(unsigned int);





char *strmerge(char *str1, const char *sep, char *str2);
 char *strmerge(char *str1, const char *sep, char *str2)
{
    char *result;

    if (str1 == NULL) result = str2;
    else if (str2 == NULL) result = str1;
    else
    {
        result = (char *)calloc(strlen(str1) + strlen(sep) + strlen(str2) + 1, sizeof(char));

        strcpy(result, str1);
        strcat(result, sep);
        strcat(result, str2);

        free(str1);
        free(str2);

    }

    return result;

}



%}


%union {

    char car;
	char *text;
    unsigned int val;
	short/*s16_t*/ s16_val;

    void/*simple_variable*/ *simple;
    void/*complex_variable*/ *complex;
    void/*variable*/ *var;

    int/*VariableQualifier*/ qual;

}

%parse-param { itanium_demangler *demangler }
%parse-param { GBinRoutine *routine }


%token ITANIUM_SIGNATURE

%token EE NN II

%token C1 C2 C3 D1 D2 D3

%token V W B C A H S T I J L M X Y N O F D E G Z DD DE DF DH DI DS U

%token TP TR TO TC TG

%token QR QV QK

%token ST SA SB SS SI SO SD


%token NUMBER CHAR









%token LPART MPART RPART


%token POSITION POSITION_ABS IMAGE

%token RECT

%token O_BRACKET C_BRACKET

%token PATH
%token DEC_16

%token TEXT


%type <text> TEXT PATH

%type <s16_val> DEC_16




%type <text> name unscoped_name unscoped_template_name nested_name
%type <text> unqualified_name
%type <text> prefix source_name


%type <var> type

%type <qual> qualifiers

%type <simple> builtin_type

%type <complex> class_enum_type


%type <text> template_args template_arg_list template_arg
%type <text> substitution


%type <car> CHAR
%type <val> NUMBER


%{

/* De lexi.c... */
/*int yylex(YYSTYPE *, YYLTYPE *);*/
void yyrestart(FILE *);
typedef struct yy_buffer_state *YY_BUFFER_STATE;
extern YY_BUFFER_STATE yy_scan_string(const char *);
extern void yy_delete_buffer(YY_BUFFER_STATE);


/* Affiche un message d'erreur concernant l'analyse. */
/*int yyerror(const YYLTYPE *, bool, char **, unsigned char *, char *);*/

%}


%%


input:
	ITANIUM_SIGNATURE encoding
	;

encoding:
    name bare_function_type
    ;




name:
    nested_name                     { $$ = $1; g_binary_routine_set_name(routine, $1); }
    | unscoped_name                 { $$ = $1; /*g_binary_routine_set_name(routine, $1);*/ }
    | unscoped_template_name template_args  { $$ = stradd($1, $2); /* TODO : merge -> free */ }
    ;

unscoped_name:
    unqualified_name                { $$ = $1; }
    | ST unqualified_name           { $$ = strprep($2, "std::"); }
    ;

unscoped_template_name:
    unscoped_name                   { $$ = $1; }
    | substitution                  { $$ = $1; }
    ;



nested_name:
    NN prefix unqualified_name EE   { $$ = ($3 != NULL ? strmerge($2, "::", $3) : $2); }
    ;



prefix:
    /* vide */                      { $$ = NULL; }
    | prefix unqualified_name       { $$ = ($2 != NULL ? strmerge($1, "::", $2) : $1); }
    ;




unqualified_name:
    ctor_dtor_name                  { $$ = NULL; }
    | source_name                   { $$ = $1; }
    ;



source_name:
    NUMBER                          { set_itanium_text_length($1); reset_itanium_identifier_building(demangler); } 
        identifier                  { $$ = strdup(demangler->identifier); }
    ;

identifier:
    identifier CHAR                 { build_itanium_identifier(demangler, $2); }
    | CHAR                          { build_itanium_identifier(demangler, $1); }
    ;

ctor_dtor_name:
    C1                              { g_binary_routine_set_type(routine, RTT_CONSTRUCTOR); }
    | C2                            { g_binary_routine_set_type(routine, RTT_CONSTRUCTOR); }
    | C3                            { g_binary_routine_set_type(routine, RTT_CONSTRUCTOR); }
    | D1                            { g_binary_routine_set_type(routine, RTT_DESTRUCTOR); }
    | D2                            { g_binary_routine_set_type(routine, RTT_DESTRUCTOR); }
    | D3                            { g_binary_routine_set_type(routine, RTT_DESTRUCTOR); }
    ;


type:
    builtin_type                    { $$ = create_var_from_simple_one($1); }
    | class_enum_type               { $$ = create_var_from_complex_one($1); }
    | substitution                  { $$ = create_var_from_complex_one(create_class_enum_var($1)); }
    | qualifiers type               { $$ = $2; add_qualifier_to_var($2, $1); }
    | TP type                       { $$ = create_var_from_complex_one(create_encapsulated_var(ECT_POINTER, $2)); }
    | TR type                       { $$ = create_var_from_complex_one(create_encapsulated_var(ECT_REFERENCE, $2)); }
    | TO type                       { $$ = create_var_from_complex_one(create_encapsulated_var(ECT_RVALUE_REF, $2)); }
    | TC type                       { $$ = create_var_from_complex_one(create_encapsulated_var(ECT_COMPLEX, $2)); }
    | TG type                       { $$ = create_var_from_complex_one(create_encapsulated_var(ECT_IMAGINARY, $2)); }
    ;

qualifiers:
    QR                              { $$ = VQF_RESTRICT; }
    | QV                            { $$ = VQF_VOLATILE; }
    | QK                            { $$ = VQF_CONST; }
    ;



builtin_type:
    V                               { $$ = create_typed_simple_var(BTP_VOID); }
    | W                             { $$ = create_typed_simple_var(BTP_WCHAR_T); }
    | B                             { $$ = create_typed_simple_var(BTP_BOOL); }
    | C                             { $$ = create_typed_simple_var(BTP_CHAR); }
    | A                             { $$ = create_typed_simple_var(BTP_SCHAR); }
    | H                             { $$ = create_typed_simple_var(BTP_UCHAR); }
    | S                             { $$ = create_typed_simple_var(BTP_SHORT); }
    | T                             { $$ = create_typed_simple_var(BTP_USHORT); }
    | I                             { $$ = create_typed_simple_var(BTP_INT); }
    | J                             { $$ = create_typed_simple_var(BTP_UINT); }
    | L                             { $$ = create_typed_simple_var(BTP_LONG); }
    | M                             { $$ = create_typed_simple_var(BTP_ULONG); }
    | X                             { $$ = create_typed_simple_var(BTP_LONG_LONG); }
    | Y                             { $$ = create_typed_simple_var(BTP_ULONG_LONG); }
    | N                             { $$ = create_typed_simple_var(BTP_INT128); }
    | O                             { $$ = create_typed_simple_var(BTP_UINT128); }
    | F                             { $$ = create_typed_simple_var(BTP_FLOAT); }
    | D                             { $$ = create_typed_simple_var(BTP_DOUBLE); }
    | E                             { $$ = create_typed_simple_var(BTP_LONG_DOUBLE); }
    | G                             { $$ = create_typed_simple_var(BTP_FLOAT128); }
    | Z                             { $$ = create_typed_simple_var(BTP_ELLIPSIS); }
    | DD                            { $$ = create_typed_simple_var(BTP_754R_64); }
    | DE                            { $$ = create_typed_simple_var(BTP_754R_128); }
    | DF                            { $$ = create_typed_simple_var(BTP_754R_32); }
    | DH                            { $$ = create_typed_simple_var(BTP_754R_16); }
    | DI                            { $$ = create_typed_simple_var(BTP_CHAR32_T); }
    | DS                            { $$ = create_typed_simple_var(BTP_CHAR16_T); }
    | U source_name                 { $$ = create_typed_simple_var(BTP_OTHER); /* TODO */ ; free($2); }
    ;



bare_function_type:
    bare_function_type type         { g_binary_routine_add_arg(routine, $2); }
    | type                          { g_binary_routine_add_arg(routine, $1); }
    ;

class_enum_type:
    name                            { $$ = create_class_enum_var($1); }
    ;




template_args:
    II template_arg_list EE         { $$ = stradd(strprep($2, "<"), ">"); }
    ;

template_arg_list:
    template_arg_list template_arg  { $$ = strmerge($1, ", ", $2); }
    | template_arg                  { $$ = $1; }
    ;

template_arg:
    type                            { $$ = var_to_string($1); delete_var($1); }
    ;

substitution:
    ST                              { $$ = strdup("std::"); }
    | SA                            { $$ = strdup("std::allocator"); }
    | SB                            { $$ = strdup("std::basic_string"); }
    | SS                            { $$ = strdup("std::string"); }
    | SI                            { $$ = strdup("std::istream"); }
    | SO                            { $$ = strdup("std::ostream"); }
    | SD                            { $$ = strdup("std::iostream"); }
    ;







%%


/**
 * Affiche un message d'erreur concernant l'analyse.
 * @param yyloc informations concernant les coordonnées du soucis.
 * @param hunt indique le type de passe réalisée.
 * @param ucode code résultant compilé.
 * @param index indice de commande à mettre à jour.
 * @param msg indications humaines sur l'événement.
 * @return 0.
 */
int yyerror(/*const YYLTYPE *yyloc, bool hunt, char **ucode, unsigned char *index, */char *msg)
{



	fprintf(stderr, "ERREUR !\n");
	fprintf(stderr, "%s\n", msg);

	return -1;

}







/******************************************************************************
*                                                                             *
*  Paramètres  : -                                                            *
*                                                                             *
*  Description : Définit un décodeur répondant à la norme d'Intel.            *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

name_demangler *create_itanium_demangler(void)
{
    itanium_demangler *result;              /* Structure à retourner       */

    result = (itanium_demangler *)calloc(1, sizeof(itanium_demangler));

    NAME_DEMANGLER(result)->can_be_demangled = (can_be_demangled_fc)can_be_itanium_demangled;
    NAME_DEMANGLER(result)->demangle_routine = (demangle_routine_fc)demangle_itanium_routine;

    return NAME_DEMANGLER(result);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : demangler = décodeur à utiliser.                             *
*                name      = chaîne de caractères à analyser.                 *
*                                                                             *
*  Description : Indique si une chaîne peut être traitée par le décodeur.     *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool can_be_itanium_demangled(itanium_demangler *itanium, const char *name)
{
    return (strncmp(name, "_Z", 2) == 0);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : demangler = décodeur à utiliser.                             *
*                name      = chaîne de caractères à décoder.                  *
*                                                                             *
*  Description : Procède au décodage d'une chaîne de caractères.              *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GBinRoutine *demangle_itanium_routine(itanium_demangler *demangler, const char *name)
{
    GBinRoutine *result;                    /* Construction à retourner    */
	YY_BUFFER_STATE buffer;                 /* Tampon pour bison           */
	int ret;                                /* Bilan de l'appel            */

    result = g_binary_routine_new();

	buffer = yy_scan_string(name);
	ret = yyparse(demangler, result);
	yy_delete_buffer(buffer);

    if (ret != 0)
    {
        /*delete_binary_routine(result); FIXME */
        result = NULL;
    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : demangler = décodeur à mettre à jour.                        *
*                                                                             *
*  Description : Réinitialise le constructeur d'identifiants.                 *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void reset_itanium_identifier_building(itanium_demangler *demangler)
{
    demangler->id_used = 0;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : demangler = décodeur à mettre à jour.                        *
*                value     = caractère d'identifiant à mémoriser.             *
*                                                                             *
*  Description : Construit à la volée un identifiant.                         *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void build_itanium_identifier(itanium_demangler *demangler, char value)
{
    if ((demangler->id_used + 2) > demangler->id_allocated)
    {
        demangler->id_allocated += ITANIUM_ALLOC_CLUSTER;
        demangler->identifier = (char *)realloc(demangler->identifier,
                                                demangler->id_allocated * sizeof(char));
    }

    demangler->identifier[demangler->id_used++] = value;
    demangler->identifier[demangler->id_used] = 0;

}