%{

#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#include "coder.h"
#include "d2c_tok.h"


extern int yylex();

extern void free_flex_memory(void);


/* Affiche un message d'erreur suite à l'analyse en échec. */
static int d2c_error(rented_coder *, char *);

/* Affiche des indications sur l'utilisation du programme. */
static void show_usage(const char *);


%}


%code requires {

/* Pour la définition des expressions conditionnelles... */
#include "coder.h"

#include "conv.h"
#include "helpers.h"

struct action_tmp
{
    CondActionType action;
    const char *details;
};


#define register_named_field_in_coder(c, n, l)              \
    ({                                                      \
        encoding_spec *__spec;                              \
        coding_bits *__bits;                                \
        __spec = get_current_encoding_spec(c);              \
        __bits = get_bits_in_encoding_spec(__spec);         \
        register_named_field_in_bits(__bits, n, l);         \
    })

#define register_bit_in_coder(c, v)                         \
    ({                                                      \
        encoding_spec *__spec;                              \
        coding_bits *__bits;                                \
        __spec = get_current_encoding_spec(c);              \
        __bits = get_bits_in_encoding_spec(__spec);         \
        register_bit_in_bits(__bits, v);                    \
    })

#define count_coder_bits(c)                                 \
    ({                                                      \
        encoding_spec *__spec;                              \
        coding_bits *__bits;                                \
        __spec = get_current_encoding_spec(c);              \
        __bits = get_bits_in_encoding_spec(__spec);         \
        count_coded_bits(__bits);                           \
    })

#define register_syntax_item_in_coder(c, n, i)              \
    ({                                                      \
        encoding_spec *__spec;                              \
        asm_syntax *__syntax;                               \
        __spec = get_current_encoding_spec(c);              \
        __syntax = get_syntax_in_encoding_spec(__spec);     \
        register_syntax_item(__syntax, n, i);               \
    })

#define register_conversion_in_coder(c, f)                  \
    ({                                                      \
        encoding_spec *__spec;                              \
        conv_list *__list;                                  \
        __spec = get_current_encoding_spec(c);              \
        __list = get_conversions_in_encoding_spec(__spec);  \
        register_conversion(__list, f);                     \
    })

#define register_hook_in_coder(c, t, f)                     \
    ({                                                      \
        encoding_spec *__spec;                              \
        instr_hooks *__hooks;;                              \
        __spec = get_current_encoding_spec(c);              \
        __hooks = get_hooks_in_encoding_spec(__spec);       \
        register_hook_function(__hooks, t, f);              \
    })

#define add_conditional_rule_to_coder(c, e, a)              \
    ({                                                      \
        encoding_spec *__spec;                              \
        decoding_rules *__rules;                            \
        __spec = get_current_encoding_spec(c);              \
        __rules = get_rules_in_encoding_spec(__spec);       \
        register_conditional_rule(__rules, e, a);           \
    })

}

%union {

    char *string;                           /* Chaîne de caractères #1     */
    const char *cstring;                    /* Chaîne de caractères #2     */


    int integer;

    conv_func *subst;                       /* Fonction de conversion      */
    arg_list_t *args;                       /* Liste d'arguments           */

    arg_expr_t *arg;                        /* Argument multi-usages       */
    ConvUnaryOperation un_op;               /* Opération unaire            */
    ConvBinaryOperation bin_op;             /* Opération bianire           */


    cond_expr *expr;                        /* Expression de déclenchement */
    rule_action raction;                    /* Action et éléments associés */

}

%parse-param { rented_coder *coder }


%token COPYRIGHT
%token TITLE
%token INS_NAME INS_DETAILS

%token ENCODING
%token TYPE NUMBER
%token ENC_START ENC_END

%token WORD HALF NAME SIZE BIT

%token SYNTAX OPERAND_NAME OPERAND_INTERNAL OPERAND_VISIBLE

%token CONV EQ OP COMMA CP NOT EOR COLON

%token HOOKS

%token RULES IF EXPR_START EQUAL BINVAL IMMVAL EXPR_END AND THEN SEE CALL UNPREDICTABLE


%type <string> COPYRIGHT INS_NAME
%type <cstring> INS_DETAILS

%type <string> TYPE
%type <integer> NUMBER

%type <string> NAME
%type <integer> SIZE BIT

%type <string> OPERAND_NAME OPERAND_INTERNAL OPERAND_VISIBLE

%type <subst> substitution

%type <args> arg_list
%type <arg> arg_expr arg_composed
%type <un_op> arg_expr_un_op
%type <bin_op> arg_expr_bin_op
%type <string> arg_field

%type <expr> rule_cond
%type <string> BINVAL IMMVAL
%type <raction> action


%%


input : name encodings { if (!dump_all_routines_using_coder(coder)) YYABORT; }

name : COPYRIGHT TITLE INS_NAME             { save_notes_for_coder(coder, $1, $3, NULL); }
     | COPYRIGHT TITLE INS_NAME INS_DETAILS { save_notes_for_coder(coder, $1, $3, $4); }




encodings : /* empty */
          | encoding encodings

encoding : ENCODING TYPE NUMBER content { push_encoding_spec(coder, $2, $3); }


content : /* empty */
        | bitfield content
        | syntax content
        | conversions content
        | hooks content
        | rules content


bitfield : HALF bits {
                       if (count_coder_bits(coder) != 16)
                       {
                         fprintf(stderr, "Unexpected word size: %u vs 16\n", count_coder_bits(coder));
                         YYABORT;
                       }
                     }
         | WORD bits {
                       if (count_coder_bits(coder) != 32)
                       {
                         fprintf(stderr, "Unexpected word size: %u vs 32\n", count_coder_bits(coder));
                         YYABORT;
                       }
                     }

bits : /* empty */
     | NAME SIZE bits   { register_named_field_in_coder(coder, $1, $2); }
     | BIT bits         { register_bit_in_coder(coder, $1); }


syntax : SYNTAX operands

operands : /* empty */
         | operands OPERAND_NAME     { register_syntax_item_in_coder(coder, $2, SIT_KEYWORD); }
         | operands OPERAND_INTERNAL { register_syntax_item_in_coder(coder, $2, SIT_INT_OPERAND); }
         | operands OPERAND_VISIBLE  { register_syntax_item_in_coder(coder, $2, SIT_EXT_OPERAND); }


conversions : CONV substitutions

substitutions : /* empty */
              | substitutions substitution { register_conversion_in_coder(coder, $2); }

substitution : NAME EQ arg_expr            { $$ = make_conv_from_expr($1, $3); }
             | NAME EQ NAME OP arg_list CP { $$ = make_conv_from_func($1, $3, $5); }


arg_list : arg_expr                { $$ = build_arg_list($1); }
         | arg_list COMMA arg_expr { $$ = extend_arg_list($1, $3); }

arg_expr : NAME                                     { $$ = build_arg_expr_from_name($1); }
          | NUMBER                                  { $$ = build_arg_expr_from_number($1); }
          | arg_composed                            { $$ = $1; }
          | OP arg_expr CP                          { $$ = $2; }
          | arg_expr_un_op arg_expr                 { $$ = build_unary_arg_expr($2, $1); }
          | OP arg_expr arg_expr_bin_op arg_expr CP { $$ = build_binary_arg_expr($2, $4, $3); }

arg_expr_un_op : NOT { $$ = CUO_NOT; }

arg_expr_bin_op : EOR { $$ = CBO_EOR; }

arg_composed : arg_field COLON arg_field    { $$ = build_composed_arg_expr($1, $3); }
             | arg_composed COLON arg_field { $$ = extend_composed_arg_expr($1, $3); }

arg_field : NAME   { $$ = $1; }
          | BINVAL { $$ = $1; }


hooks : HOOKS hookings

hookings : /* empty */
         | hookings hooking

hooking : NAME EQ NAME { register_hook_in_coder(coder, $1, $3); }


rules : RULES rules_list

rules_list : /* empty */
           | rules_list rule

rule : IF EXPR_START rule_cond EXPR_END THEN action { add_conditional_rule_to_coder(coder, $3, &$6); }
     | action                                       { add_conditional_rule_to_coder(coder, NULL, &$1); }

rule_cond : NAME EQUAL BINVAL   { $$ = build_simple_cond_expression($1, CCT_EQUAL, $3); }
          | NAME EQUAL IMMVAL   { $$ = build_simple_cond_expression($1, CCT_EQUAL, $3); }
          | EXPR_START rule_cond EXPR_END AND EXPR_START rule_cond EXPR_END
                                { $$ = build_composed_cond_expression($2, COT_AND, $6); }

action : SEE INS_DETAILS          { $$.type = CAT_SEE; $$.details = make_callable($2, false); }
       | UNPREDICTABLE            { $$.type = CAT_UNPREDICTABLE; }
       | CALL NAME OP arg_list CP { $$.type = CAT_CALL; $$.callee = $2; $$.args = $4; }


%%


/******************************************************************************
*                                                                             *
*  Paramètres  : coder = codeur impliqué dans le processus.                   *
*                msg   = message d'erreur.                                    *
*                                                                             *
*  Description : Affiche un message d'erreur suite à l'analyse en échec.      *
*                                                                             *
*  Retour      : 0                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static int d2c_error(rented_coder *coder, char *msg)
{
	printf("yyerror line %d: %s\n", d2c_get_lineno(), msg);

	return 0;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : argv0 = nombre du programme exécuté.                         *
*                                                                             *
*  Description : Affiche des indications sur l'utilisation du programme.      *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void show_usage(const char *argv0)
{
    printf("\n");

    printf("Usage: %s [options] < file\n", argv0);

    printf("\n");

    printf("Options:\n");

    printf("\n");

    printf("\t-h | --help\t\t\tDisplay this messsage.\n");
    printf("\t-d | --dir <string>\t\tSpecify the main output directory.\n");
    printf("\t-a | --arch <string>\t\tDefine the archicture to handle.\n");
    printf("\t-H | --header <string>\t\tSet the base of the #ifndef / #define game.\n");
    printf("\t-e | --encoding <string>\tDefine encoding prefixes for files.\n");
    printf("\t-m | --macro <string>\t\tRegister some conversion functions.\n");

    printf("\n");

}


/******************************************************************************
*                                                                             *
*  Paramètres  : argc = nombre d'arguments dans la ligne de commande.         *
*                argv = arguments de la ligne de commande.                    *
*                                                                             *
*  Description : Point d'entrée du programme.                                 *
*                                                                             *
*  Retour      : EXIT_SUCCESS si le prgm s'est déroulé sans encombres.        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

int main(int argc, char **argv)
{
    int result;                             /* Bilan à retourner           */
    rented_coder *coder;                    /* Codeur à briffer & employer */           
    int index;                              /* Indice de fichier à traiter */
    bool need_help;                         /* Affichage de l'aide ?       */
    bool has_error;                         /* Erreur dans la ligne de cmd.*/
    int ret;                                /* Bilan d'une lecture d'arg.  */
    char *sep;                              /* Caratère '=' en coupure     */

    static struct option long_options[] = {

        { "help",       no_argument,        NULL,   'h' },
        { "input",      required_argument,  NULL,   'i' },
        { "dir",        required_argument,  NULL,   'd' },
        { "arch",       required_argument,  NULL,   'a' },
        { "header",     required_argument,  NULL,   'H' },
        { "encoding",   required_argument,  NULL,   'e' },
        { "macro",      required_argument,  NULL,   'M' },
        { NULL,         0,                  NULL,   0   }

    };

    result = EXIT_SUCCESS;

    coder = create_coder();

    index = 0;

    need_help = false;
    has_error = false;

    while (!has_error)
    {
        ret = getopt_long(argc, argv, "hi:d:a:H:e:M:", long_options, &index);
        if (ret == -1) break;

        switch (ret)
        {
            case 'h':
                need_help = true;
                break;

            case 'i':
                set_coder_input_file(coder, optarg);
                break;

            case 'd':
                set_coder_output_directory(coder, optarg);
                break;

            case 'a':
                set_coder_arch(coder, optarg);
                break;

            case 'H':
                set_coder_header_base(coder, optarg);
                break;

            case 'e':

                sep = strchr(optarg, '=');
                has_error = (sep == NULL);

                if (!has_error)
                {
                    *sep = '\0';
                    register_encoding(get_coder_pre_proc(coder), optarg, sep + 1);
                }

                break;

            case 'M':

                sep = strchr(optarg, '=');
                has_error = (sep == NULL);

                if (!has_error)
                {
                    *sep = '\0';
                    define_macro(get_coder_pre_proc(coder), optarg, sep + 1);
                }

                break;

        }

    }

    if (need_help || has_error || !do_basic_checks_with_coder(coder) || optind != argc)
    {
        show_usage(argv[0]);
        result = (need_help ? EXIT_SUCCESS : EXIT_FAILURE);
        goto exit;
    }

	result = yyparse(coder);

 exit:

    free_flex_memory();

    delete_coder(coder);

    return result;

}