#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "goomsl.h"
#include "goomsl_private.h"
#include "goomsl_yacc.h"

/*#define TRACE_SCRIPT*/

 /* {{{ definition of the instructions number */
#define INSTR_SETI_VAR_INTEGER     1
#define INSTR_SETI_VAR_VAR         2
#define INSTR_SETF_VAR_FLOAT       3
#define INSTR_SETF_VAR_VAR         4
#define INSTR_NOP                  5
/* #define INSTR_JUMP              6 */
#define INSTR_SETP_VAR_PTR         7
#define INSTR_SETP_VAR_VAR         8
#define INSTR_SUBI_VAR_INTEGER     9
#define INSTR_SUBI_VAR_VAR         10
#define INSTR_SUBF_VAR_FLOAT       11
#define INSTR_SUBF_VAR_VAR         12
#define INSTR_ISLOWERF_VAR_VAR     13
#define INSTR_ISLOWERF_VAR_FLOAT   14
#define INSTR_ISLOWERI_VAR_VAR     15
#define INSTR_ISLOWERI_VAR_INTEGER 16
#define INSTR_ADDI_VAR_INTEGER     17
#define INSTR_ADDI_VAR_VAR         18
#define INSTR_ADDF_VAR_FLOAT       19
#define INSTR_ADDF_VAR_VAR         20
#define INSTR_MULI_VAR_INTEGER     21
#define INSTR_MULI_VAR_VAR         22
#define INSTR_MULF_VAR_FLOAT       23
#define INSTR_MULF_VAR_VAR         24
#define INSTR_DIVI_VAR_INTEGER     25
#define INSTR_DIVI_VAR_VAR         26
#define INSTR_DIVF_VAR_FLOAT       27
#define INSTR_DIVF_VAR_VAR         28
/* #define INSTR_JZERO             29 */
#define INSTR_ISEQUALP_VAR_VAR     30
#define INSTR_ISEQUALP_VAR_PTR     31
#define INSTR_ISEQUALI_VAR_VAR     32
#define INSTR_ISEQUALI_VAR_INTEGER 33
#define INSTR_ISEQUALF_VAR_VAR     34
#define INSTR_ISEQUALF_VAR_FLOAT   35
/* #define INSTR_CALL              36 */
/* #define INSTR_RET               37 */
/* #define INSTR_EXT_CALL          38 */
#define INSTR_NOT_VAR              39
/* #define INSTR_JNZERO            40 */
#define  INSTR_SETS_VAR_VAR        41
#define  INSTR_ISEQUALS_VAR_VAR    42
#define  INSTR_ADDS_VAR_VAR        43
#define  INSTR_SUBS_VAR_VAR        44
#define  INSTR_MULS_VAR_VAR        45
#define  INSTR_DIVS_VAR_VAR        46

 /* }}} */
/* {{{ definition of the validation error types */
static const char *VALIDATE_OK = "ok"; 
#define VALIDATE_ERROR "error while validating "
#define VALIDATE_TODO "todo"
#define VALIDATE_SYNTHAX_ERROR "synthax error"
#define VALIDATE_NO_SUCH_INT "no such integer variable"
#define VALIDATE_NO_SUCH_VAR "no such variable"
#define VALIDATE_NO_SUCH_DEST_VAR "no such destination variable"
#define VALIDATE_NO_SUCH_SRC_VAR "no such src variable"
/* }}} */

  /***********************************/
 /* PROTOTYPE OF INTERNAL FUNCTIONS */
/***********************************/

/* {{{ */
static void gsl_instr_free(Instruction *_this);
static const char *gsl_instr_validate(Instruction *_this);
static void gsl_instr_display(Instruction *_this);

static InstructionFlow *iflow_new(void);
static void iflow_add_instr(InstructionFlow *_this, Instruction *instr);
static void iflow_clean(InstructionFlow *_this);
static void iflow_free(InstructionFlow *_this);
static void iflow_execute(FastInstructionFlow *_this, GoomSL *gsl);
/* }}} */

  /************************************/
 /* DEFINITION OF INTERNAL FUNCTIONS */
/************************************/

void iflow_free(InstructionFlow *_this)
{ /* {{{ */
  goom_hash_free(_this->labels);
  free(_this->instr);
  free(_this); /*TODO: finir cette fonction */
} /* }}} */

void iflow_clean(InstructionFlow *_this)
{ /* {{{ */
  /* TODO: clean chaque instruction du flot */
  _this->number = 0;
  goom_hash_free(_this->labels);
  _this->labels = goom_hash_new();
} /* }}} */

InstructionFlow *iflow_new(void)
{ /* {{{ */
  InstructionFlow *_this = (InstructionFlow*)malloc(sizeof(InstructionFlow));
  _this->number = 0;
  _this->tabsize = 6;
  _this->instr = (Instruction**)malloc(_this->tabsize * sizeof(Instruction*));
  _this->labels = goom_hash_new();

  return _this;
} /* }}} */

void iflow_add_instr(InstructionFlow *_this, Instruction *instr)
{ /* {{{ */
  if (_this->number == _this->tabsize) {
    _this->tabsize *= 2;
    _this->instr = (Instruction**)realloc(_this->instr, _this->tabsize * sizeof(Instruction*));
  }
  _this->instr[_this->number] = instr;
  instr->address = _this->number;
  _this->number++;
} /* }}} */

void gsl_instr_set_namespace(Instruction *_this, GoomHash *ns)
{ /* {{{ */
  if (_this->cur_param <= 0) {
    fprintf(stderr, "ERROR: Line %d, No more params to instructions\n", _this->line_number);
    exit(1);
  }
  _this->vnamespace[_this->cur_param-1] = ns;
} /* }}} */

void gsl_instr_add_param(Instruction *instr, const char *param, int type)
{ /* {{{ */
  int len;
  if (instr==NULL)
    return;
  if (instr->cur_param==0)
    return;
  --instr->cur_param;
  len = strlen(param);
  instr->params[instr->cur_param] = (char*)malloc(len+1);
  strcpy(instr->params[instr->cur_param], param);
  instr->types[instr->cur_param] = type;
  if (instr->cur_param == 0) {

    const char *result = gsl_instr_validate(instr);
    if (result != VALIDATE_OK) {
      printf("ERROR: Line %d: ", instr->parent->num_lines + 1);
      gsl_instr_display(instr);
      printf("... %s\n", result);
      instr->parent->compilationOK = 0;
      exit(1);
    }

#if USE_JITC_X86
    iflow_add_instr(instr->parent->iflow, instr);
#else
    if (instr->id != INSTR_NOP)
      iflow_add_instr(instr->parent->iflow, instr);
    else
      gsl_instr_free(instr);
#endif
  }
} /* }}} */

Instruction *gsl_instr_init(GoomSL *parent, const char *name, int id, int nb_param, int line_number)
{ /* {{{ */
  Instruction *instr = (Instruction*)malloc(sizeof(Instruction));
  instr->params     = (char**)malloc(nb_param*sizeof(char*));
  instr->vnamespace = (GoomHash**)malloc(nb_param*sizeof(GoomHash*));
  instr->types = (int*)malloc(nb_param*sizeof(int));
  instr->cur_param = instr->nb_param = nb_param;
  instr->parent = parent;
  instr->id = id;
  instr->name = name;
  instr->jump_label = NULL;
  instr->line_number = line_number;
  return instr;
} /* }}} */

void gsl_instr_free(Instruction *_this)
{ /* {{{ */
  int i;
  free(_this->types);
  for (i=_this->cur_param; i<_this->nb_param; ++i)
    free(_this->params[i]);
  free(_this->params);
  free(_this);
} /* }}} */

void gsl_instr_display(Instruction *_this)
{ /* {{{ */
  int i=_this->nb_param-1;
  printf("%s", _this->name);
  while(i>=_this->cur_param) {
    printf(" %s", _this->params[i]);
    --i;
  }
} /* }}} */

  /****************************************/
 /* VALIDATION OF INSTRUCTION PARAMETERS */
/****************************************/

static const char *validate_v_v(Instruction *_this)
{ /* {{{ */
  HashValue *dest = goom_hash_get(_this->vnamespace[1], _this->params[1]);
  HashValue *src  = goom_hash_get(_this->vnamespace[0], _this->params[0]);

  if (dest == NULL) {
    return VALIDATE_NO_SUCH_DEST_VAR;
  }
  if (src == NULL) {
    return VALIDATE_NO_SUCH_SRC_VAR;
  }
  _this->data.udest.var = dest->ptr;
  _this->data.usrc.var  = src->ptr;
  return VALIDATE_OK;
} /* }}} */

static const char *validate_v_i(Instruction *_this)
{ /* {{{ */
  HashValue *dest            = goom_hash_get(_this->vnamespace[1], _this->params[1]);
  _this->data.usrc.value_int = strtol(_this->params[0],NULL,0);

  if (dest == NULL) {
    return VALIDATE_NO_SUCH_INT;
  }
  _this->data.udest.var = dest->ptr;
  return VALIDATE_OK;
} /* }}} */

static const char *validate_v_p(Instruction *_this)
{ /* {{{ */
  HashValue *dest            = goom_hash_get(_this->vnamespace[1], _this->params[1]);
  _this->data.usrc.value_ptr = strtol(_this->params[0],NULL,0);

  if (dest == NULL) {
    return VALIDATE_NO_SUCH_INT;
  }
  _this->data.udest.var = dest->ptr;
  return VALIDATE_OK;
} /* }}} */

static const char *validate_v_f(Instruction *_this)
{ /* {{{ */
  HashValue *dest            = goom_hash_get(_this->vnamespace[1], _this->params[1]);
  _this->data.usrc.value_float = atof(_this->params[0]);

  if (dest == NULL) {
    return VALIDATE_NO_SUCH_VAR;
  }
  _this->data.udest.var = dest->ptr;
  return VALIDATE_OK;
} /* }}} */

static const char *validate(Instruction *_this,
                            int vf_f_id, int vf_v_id,
                            int vi_i_id, int vi_v_id,
                            int vp_p_id, int vp_v_id,
                            int vs_v_id)
{ /* {{{ */
  if ((_this->types[1] == TYPE_FVAR) && (_this->types[0] == TYPE_FLOAT)) {
    _this->id = vf_f_id;
    return validate_v_f(_this);
  }
  else if ((_this->types[1] == TYPE_FVAR) && (_this->types[0] == TYPE_FVAR)) {
    _this->id = vf_v_id;
    return validate_v_v(_this);
  }
  else if ((_this->types[1] == TYPE_IVAR) && (_this->types[0] == TYPE_INTEGER)) {
    _this->id = vi_i_id;
    return validate_v_i(_this);
  }
  else if ((_this->types[1] == TYPE_IVAR) && (_this->types[0] == TYPE_IVAR)) {
    _this->id = vi_v_id;
    return validate_v_v(_this);
  }
  else if ((_this->types[1] == TYPE_PVAR) && (_this->types[0] == TYPE_PTR)) {
    if (vp_p_id == INSTR_NOP) return VALIDATE_ERROR;
    _this->id = vp_p_id;
    return validate_v_p(_this);
  }
  else if ((_this->types[1] == TYPE_PVAR) && (_this->types[0] == TYPE_PVAR)) {
    _this->id = vp_v_id;
    if (vp_v_id == INSTR_NOP) return VALIDATE_ERROR;
    return validate_v_v(_this);
  }
  else if ((_this->types[1] < FIRST_RESERVED) && (_this->types[1] >= 0) && (_this->types[0] == _this->types[1])) {
    _this->id = vs_v_id;
    if (vs_v_id == INSTR_NOP) return "Impossible operation to perform between two structs";
    return validate_v_v(_this);
  }
  return VALIDATE_ERROR;
} /* }}} */

const char *gsl_instr_validate(Instruction *_this)
{ /* {{{ */
  if (_this->id != INSTR_EXT_CALL) {
    int i = _this->nb_param;
    while (i>0)
    {
      i--;
      if (_this->types[i] == TYPE_VAR) {
        int type = gsl_type_of_var(_this->vnamespace[i], _this->params[i]);

        if (type == INSTR_INT)
          _this->types[i] = TYPE_IVAR;
        else if (type == INSTR_FLOAT)
          _this->types[i] = TYPE_FVAR;
        else if (type == INSTR_PTR)
          _this->types[i] = TYPE_PVAR;
        else if ((type >= 0) && (type < FIRST_RESERVED))
          _this->types[i] = type;
        else fprintf(stderr,"WARNING: Line %d, %s has no namespace\n", _this->line_number, _this->params[i]);
      }
    }
  }

  switch (_this->id) {

    /* set */ 
    case INSTR_SET:
      return validate(_this,
          INSTR_SETF_VAR_FLOAT, INSTR_SETF_VAR_VAR,
          INSTR_SETI_VAR_INTEGER, INSTR_SETI_VAR_VAR,
          INSTR_SETP_VAR_PTR, INSTR_SETP_VAR_VAR,
          INSTR_SETS_VAR_VAR);

      /* extcall */
    case INSTR_EXT_CALL:
      if (_this->types[0] == TYPE_VAR) {
        HashValue *fval = goom_hash_get(_this->parent->functions, _this->params[0]);
        if (fval) {
          _this->data.udest.external_function = (struct _ExternalFunctionStruct*)fval->ptr;
          return VALIDATE_OK;
        }
      }
      return VALIDATE_ERROR;

      /* call */
    case INSTR_CALL:
      if (_this->types[0] == TYPE_LABEL) {
        _this->jump_label = _this->params[0];
        return VALIDATE_OK;
      }
      return VALIDATE_ERROR;

      /* ret */
    case INSTR_RET:
      return VALIDATE_OK;

      /* jump */
    case INSTR_JUMP:

      if (_this->types[0] == TYPE_LABEL) {
        _this->jump_label = _this->params[0];
        return VALIDATE_OK;
      }
      return VALIDATE_ERROR;

      /* jzero / jnzero */
    case INSTR_JZERO:
    case INSTR_JNZERO:

      if (_this->types[0] == TYPE_LABEL) {
        _this->jump_label = _this->params[0];
        return VALIDATE_OK;
      }
      return VALIDATE_ERROR;

      /* label */
    case INSTR_LABEL:

      if (_this->types[0] == TYPE_LABEL) {
        _this->id = INSTR_NOP;
        _this->nop_label = _this->params[0];
        goom_hash_put_int(_this->parent->iflow->labels, _this->params[0], _this->parent->iflow->number);
        return VALIDATE_OK;
      }
      return VALIDATE_ERROR;

      /* isequal */
    case INSTR_ISEQUAL:
      return validate(_this,
          INSTR_ISEQUALF_VAR_FLOAT, INSTR_ISEQUALF_VAR_VAR,
          INSTR_ISEQUALI_VAR_INTEGER, INSTR_ISEQUALI_VAR_VAR,
          INSTR_ISEQUALP_VAR_PTR, INSTR_ISEQUALP_VAR_VAR,
          INSTR_ISEQUALS_VAR_VAR);

      /* not */
    case INSTR_NOT:
      _this->id = INSTR_NOT_VAR;
      return VALIDATE_OK;

      /* islower */
    case INSTR_ISLOWER:
      return validate(_this,
          INSTR_ISLOWERF_VAR_FLOAT, INSTR_ISLOWERF_VAR_VAR,
          INSTR_ISLOWERI_VAR_INTEGER, INSTR_ISLOWERI_VAR_VAR,
          INSTR_NOP, INSTR_NOP, INSTR_NOP);

      /* add */
    case INSTR_ADD:
      return validate(_this,
          INSTR_ADDF_VAR_FLOAT, INSTR_ADDF_VAR_VAR,
          INSTR_ADDI_VAR_INTEGER, INSTR_ADDI_VAR_VAR,
          INSTR_NOP, INSTR_NOP,
          INSTR_ADDS_VAR_VAR);

      /* mul */
    case INSTR_MUL:
      return validate(_this,
          INSTR_MULF_VAR_FLOAT, INSTR_MULF_VAR_VAR,
          INSTR_MULI_VAR_INTEGER, INSTR_MULI_VAR_VAR,
          INSTR_NOP, INSTR_NOP,
          INSTR_MULS_VAR_VAR);

      /* sub */
    case INSTR_SUB:
      return validate(_this,
          INSTR_SUBF_VAR_FLOAT, INSTR_SUBF_VAR_VAR,
          INSTR_SUBI_VAR_INTEGER, INSTR_SUBI_VAR_VAR,
          INSTR_NOP, INSTR_NOP,
          INSTR_SUBS_VAR_VAR);

      /* div */
    case INSTR_DIV:
      return validate(_this,
          INSTR_DIVF_VAR_FLOAT, INSTR_DIVF_VAR_VAR,
          INSTR_DIVI_VAR_INTEGER, INSTR_DIVI_VAR_VAR,
          INSTR_NOP,INSTR_NOP,
          INSTR_DIVS_VAR_VAR);

    default:
      return VALIDATE_TODO;
  }
  return VALIDATE_ERROR;
} /* }}} */

  /*************/
 /* EXECUTION */
/*************/
void iflow_execute(FastInstructionFlow *_this, GoomSL *gsl)
{ /* {{{ */
  int flag = 0;
  int ip = 0;
  FastInstruction *instr = _this->instr;
  int stack[0x10000];
  int stack_pointer = 0;

  stack[stack_pointer++] = -1;

  /* Quelques Macro pour rendre le code plus lisible */
#define pSRC_VAR        instr[ip].data.usrc.var
#define SRC_VAR_INT    *instr[ip].data.usrc.var_int
#define SRC_VAR_FLOAT  *instr[ip].data.usrc.var_float
#define SRC_VAR_PTR    *instr[ip].data.usrc.var_ptr

#define pDEST_VAR       instr[ip].data.udest.var
#define DEST_VAR_INT   *instr[ip].data.udest.var_int
#define DEST_VAR_FLOAT *instr[ip].data.udest.var_float
#define DEST_VAR_PTR   *instr[ip].data.udest.var_ptr

#define VALUE_INT       instr[ip].data.usrc.value_int
#define VALUE_FLOAT     instr[ip].data.usrc.value_float
#define VALUE_PTR       instr[ip].data.usrc.value_ptr

#define JUMP_OFFSET     instr[ip].data.udest.jump_offset

#define SRC_STRUCT_ID  instr[ip].data.usrc.var_int[-1]
#define DEST_STRUCT_ID instr[ip].data.udest.var_int[-1]
#define SRC_STRUCT_IBLOCK(i)  gsl->gsl_struct[SRC_STRUCT_ID]->iBlock[i]
#define SRC_STRUCT_FBLOCK(i)  gsl->gsl_struct[SRC_STRUCT_ID]->fBlock[i]
#define DEST_STRUCT_IBLOCK(i) gsl->gsl_struct[DEST_STRUCT_ID]->iBlock[i]
#define DEST_STRUCT_FBLOCK(i) gsl->gsl_struct[DEST_STRUCT_ID]->fBlock[i]
#define DEST_STRUCT_IBLOCK_VAR(i,j) \
  ((int*)((char*)pDEST_VAR   + gsl->gsl_struct[DEST_STRUCT_ID]->iBlock[i].data))[j]
#define DEST_STRUCT_FBLOCK_VAR(i,j) \
  ((float*)((char*)pDEST_VAR + gsl->gsl_struct[DEST_STRUCT_ID]->fBlock[i].data))[j]
#define SRC_STRUCT_IBLOCK_VAR(i,j) \
  ((int*)((char*)pSRC_VAR    + gsl->gsl_struct[SRC_STRUCT_ID]->iBlock[i].data))[j]
#define SRC_STRUCT_FBLOCK_VAR(i,j) \
  ((float*)((char*)pSRC_VAR  + gsl->gsl_struct[SRC_STRUCT_ID]->fBlock[i].data))[j]
#define DEST_STRUCT_SIZE      gsl->gsl_struct[DEST_STRUCT_ID]->size

  while (1)
  {
    int i;
#ifdef TRACE_SCRIPT 
    printf("execute "); gsl_instr_display(instr[ip].proto); printf("\n");
#endif
    switch (instr[ip].id) {

      /* SET.I */
      case INSTR_SETI_VAR_INTEGER:
        DEST_VAR_INT = VALUE_INT;
        ++ip; break;

      case INSTR_SETI_VAR_VAR:
        DEST_VAR_INT = SRC_VAR_INT;
        ++ip; break;

        /* SET.F */
      case INSTR_SETF_VAR_FLOAT:
        DEST_VAR_FLOAT = VALUE_FLOAT;
        ++ip; break;

      case INSTR_SETF_VAR_VAR:
        DEST_VAR_FLOAT = SRC_VAR_FLOAT;
        ++ip; break;

        /* SET.P */
      case INSTR_SETP_VAR_VAR:
        DEST_VAR_PTR = SRC_VAR_PTR;
        ++ip; break;

      case INSTR_SETP_VAR_PTR:
        DEST_VAR_PTR = VALUE_PTR;
        ++ip; break;

        /* JUMP */
      case INSTR_JUMP:
        ip += JUMP_OFFSET; break;

        /* JZERO */
      case INSTR_JZERO:
        ip += (flag ? 1 : JUMP_OFFSET); break;

      case INSTR_NOP:
        ++ip; break;

        /* ISEQUAL.P */
      case INSTR_ISEQUALP_VAR_VAR:
        flag = (DEST_VAR_PTR == SRC_VAR_PTR);
        ++ip; break;

      case INSTR_ISEQUALP_VAR_PTR:
        flag = (DEST_VAR_PTR == VALUE_PTR);
        ++ip; break;

        /* ISEQUAL.I */
      case INSTR_ISEQUALI_VAR_VAR:
        flag = (DEST_VAR_INT == SRC_VAR_INT);
        ++ip; break;

      case INSTR_ISEQUALI_VAR_INTEGER:
        flag = (DEST_VAR_INT == VALUE_INT);
        ++ip; break;

        /* ISEQUAL.F */
      case INSTR_ISEQUALF_VAR_VAR:
        flag = (DEST_VAR_FLOAT == SRC_VAR_FLOAT);
        ++ip; break;

      case INSTR_ISEQUALF_VAR_FLOAT:
        flag = (DEST_VAR_FLOAT ==  VALUE_FLOAT);
        ++ip; break;

        /* ISLOWER.I */
      case INSTR_ISLOWERI_VAR_VAR:
        flag = (DEST_VAR_INT < SRC_VAR_INT);
        ++ip; break;

      case INSTR_ISLOWERI_VAR_INTEGER:
        flag = (DEST_VAR_INT <  VALUE_INT);
        ++ip; break;

        /* ISLOWER.F */
      case INSTR_ISLOWERF_VAR_VAR:
        flag = (DEST_VAR_FLOAT < SRC_VAR_FLOAT);
        ++ip; break;

      case INSTR_ISLOWERF_VAR_FLOAT:
        flag = (DEST_VAR_FLOAT <  VALUE_FLOAT);
        ++ip; break;

        /* ADD.I */
      case INSTR_ADDI_VAR_VAR:
        DEST_VAR_INT += SRC_VAR_INT;
        ++ip; break;

      case INSTR_ADDI_VAR_INTEGER:
        DEST_VAR_INT += VALUE_INT;
        ++ip; break;

        /* ADD.F */
      case INSTR_ADDF_VAR_VAR:
        DEST_VAR_FLOAT += SRC_VAR_FLOAT;
        ++ip; break;

      case INSTR_ADDF_VAR_FLOAT:
        DEST_VAR_FLOAT += VALUE_FLOAT;
        ++ip; break;

        /* MUL.I */
      case INSTR_MULI_VAR_VAR:
        DEST_VAR_INT *= SRC_VAR_INT;
        ++ip; break;

      case INSTR_MULI_VAR_INTEGER:
        DEST_VAR_INT *= VALUE_INT;
        ++ip; break;

        /* MUL.F */
      case INSTR_MULF_VAR_FLOAT:
        DEST_VAR_FLOAT *= VALUE_FLOAT;
        ++ip; break;

      case INSTR_MULF_VAR_VAR:
        DEST_VAR_FLOAT *= SRC_VAR_FLOAT;
        ++ip; break;

        /* DIV.I */
      case INSTR_DIVI_VAR_VAR:
        DEST_VAR_INT /= SRC_VAR_INT;
        ++ip; break;

      case INSTR_DIVI_VAR_INTEGER:
        DEST_VAR_INT /= VALUE_INT;
        ++ip; break;

        /* DIV.F */
      case INSTR_DIVF_VAR_FLOAT:
        DEST_VAR_FLOAT /= VALUE_FLOAT;
        ++ip; break;

      case INSTR_DIVF_VAR_VAR:
        DEST_VAR_FLOAT /= SRC_VAR_FLOAT;
        ++ip; break;

        /* SUB.I */
      case INSTR_SUBI_VAR_VAR:
        DEST_VAR_INT -= SRC_VAR_INT;
        ++ip; break;

      case INSTR_SUBI_VAR_INTEGER:
        DEST_VAR_INT -= VALUE_INT;
        ++ip; break;

        /* SUB.F */
      case INSTR_SUBF_VAR_FLOAT:
        DEST_VAR_FLOAT -= VALUE_FLOAT;
        ++ip; break;

      case INSTR_SUBF_VAR_VAR:
        DEST_VAR_FLOAT -= SRC_VAR_FLOAT;
        ++ip; break;

        /* CALL */
      case INSTR_CALL:
        stack[stack_pointer++] = ip + 1;
        ip += JUMP_OFFSET; break;

        /* RET */
      case INSTR_RET:
        ip = stack[--stack_pointer];
        if (ip<0) return;
        break;

        /* EXT_CALL */
      case INSTR_EXT_CALL:
        instr[ip].data.udest.external_function->function(gsl, gsl->vars, instr[ip].data.udest.external_function->vars);
        ++ip; break;

        /* NOT */
      case INSTR_NOT_VAR:
        flag = !flag;
        ++ip; break;

        /* JNZERO */
      case INSTR_JNZERO:
        ip += (flag ? JUMP_OFFSET : 1); break;

      case INSTR_SETS_VAR_VAR:
        memcpy(pDEST_VAR, pSRC_VAR, DEST_STRUCT_SIZE);
        ++ip; break;

      case INSTR_ISEQUALS_VAR_VAR:
        break;

      case INSTR_ADDS_VAR_VAR:
        /* process integers */
        i=0;
        while (DEST_STRUCT_IBLOCK(i).size > 0) {
          int j=DEST_STRUCT_IBLOCK(i).size;
          while (j--) {
            DEST_STRUCT_IBLOCK_VAR(i,j) += SRC_STRUCT_IBLOCK_VAR(i,j);
          }
          ++i;
        }
        /* process floats */
        i=0;
        while (DEST_STRUCT_FBLOCK(i).size > 0) {
          int j=DEST_STRUCT_FBLOCK(i).size;
          while (j--) {
            DEST_STRUCT_FBLOCK_VAR(i,j) += SRC_STRUCT_FBLOCK_VAR(i,j);
          }
          ++i;
        }
        ++ip; break;

      case INSTR_SUBS_VAR_VAR:
        /* process integers */
        i=0;
        while (DEST_STRUCT_IBLOCK(i).size > 0) {
          int j=DEST_STRUCT_IBLOCK(i).size;
          while (j--) {
            DEST_STRUCT_IBLOCK_VAR(i,j) -= SRC_STRUCT_IBLOCK_VAR(i,j);
          }
          ++i;
        }
        /* process floats */
        i=0;
        while (DEST_STRUCT_FBLOCK(i).size > 0) {
          int j=DEST_STRUCT_FBLOCK(i).size;
          while (j--) {
            DEST_STRUCT_FBLOCK_VAR(i,j) -= SRC_STRUCT_FBLOCK_VAR(i,j);
          }
          ++i;
        }
        ++ip; break;

      case INSTR_MULS_VAR_VAR:
        /* process integers */
        i=0;
        while (DEST_STRUCT_IBLOCK(i).size > 0) {
          int j=DEST_STRUCT_IBLOCK(i).size;
          while (j--) {
            DEST_STRUCT_IBLOCK_VAR(i,j) *= SRC_STRUCT_IBLOCK_VAR(i,j);
          }
          ++i;
        }
        /* process floats */
        i=0;
        while (DEST_STRUCT_FBLOCK(i).size > 0) {
          int j=DEST_STRUCT_FBLOCK(i).size;
          while (j--) {
            DEST_STRUCT_FBLOCK_VAR(i,j) *= SRC_STRUCT_FBLOCK_VAR(i,j);
          }
          ++i;
        }
        ++ip; break;
        
      case INSTR_DIVS_VAR_VAR:
        /* process integers */
        i=0;
        while (DEST_STRUCT_IBLOCK(i).size > 0) {
          int j=DEST_STRUCT_IBLOCK(i).size;
          while (j--) {
            DEST_STRUCT_IBLOCK_VAR(i,j) /= SRC_STRUCT_IBLOCK_VAR(i,j);
          }
          ++i;
        }
        /* process floats */
        i=0;
        while (DEST_STRUCT_FBLOCK(i).size > 0) {
          int j=DEST_STRUCT_FBLOCK(i).size;
          while (j--) {
            DEST_STRUCT_FBLOCK_VAR(i,j) /= SRC_STRUCT_FBLOCK_VAR(i,j);
          }
          ++i;
        }
        ++ip; break;

      default:
        printf("NOT IMPLEMENTED : %d\n", instr[ip].id);
        ++ip;
        exit(1);
    }
  }
} /* }}} */

int gsl_malloc(GoomSL *_this, int size)
{ /* {{{ */
  if (_this->nbPtr >= _this->ptrArraySize) {
    _this->ptrArraySize *= 2;
    _this->ptrArray = (void**)realloc(_this->ptrArray, sizeof(void*) * _this->ptrArraySize);
  }
  _this->ptrArray[_this->nbPtr] = malloc(size);
  return _this->nbPtr++;
} /* }}} */

void *gsl_get_ptr(GoomSL *_this, int id)
{ /* {{{ */
  if ((id>=0)&&(id<_this->nbPtr))
    return _this->ptrArray[id];
  fprintf(stderr,"INVALID GET PTR 0x%08x\n", id);
  return NULL;
} /* }}} */

void gsl_free_ptr(GoomSL *_this, int id)
{ /* {{{ */
  if ((id>=0)&&(id<_this->nbPtr)) {
    free(_this->ptrArray[id]);
    _this->ptrArray[id] = 0;
  }
} /* }}} */

void gsl_enternamespace(const char *name)
{ /* {{{ */
  HashValue *val = goom_hash_get(currentGoomSL->functions, name);
  if (val) {
    ExternalFunctionStruct *function = (ExternalFunctionStruct*)val->ptr;
    currentGoomSL->currentNS++;
    currentGoomSL->namespaces[currentGoomSL->currentNS] = function->vars;
  }
  else {
    fprintf(stderr, "ERROR: Line %d, Could not find namespace: %s\n", currentGoomSL->num_lines, name);
    exit(1);
  }
} /* }}} */

void gsl_reenternamespace(GoomHash *nsinfo) {
  currentGoomSL->currentNS++;
  currentGoomSL->namespaces[currentGoomSL->currentNS] = nsinfo;
}

GoomHash *gsl_leavenamespace(void)
{ /* {{{ */
  currentGoomSL->currentNS--;
  return currentGoomSL->namespaces[currentGoomSL->currentNS+1];
} /* }}} */

GoomHash *gsl_find_namespace(const char *name)
{ /* {{{ */
  int i;
  for (i=currentGoomSL->currentNS;i>=0;--i) {
    if (goom_hash_get(currentGoomSL->namespaces[i], name))
      return currentGoomSL->namespaces[i];
  }
  return NULL;
} /* }}} */

void gsl_declare_task(const char *name)
{ /* {{{ */
  if (goom_hash_get(currentGoomSL->functions, name)) {
    return;
  }
  else {
    ExternalFunctionStruct *gef = (ExternalFunctionStruct*)malloc(sizeof(ExternalFunctionStruct));
    gef->function = 0;
    gef->vars = goom_hash_new();
    gef->is_extern = 0;
    goom_hash_put_ptr(currentGoomSL->functions, name, (void*)gef);
  }
} /* }}} */

void gsl_declare_external_task(const char *name)
{ /* {{{ */
  if (goom_hash_get(currentGoomSL->functions, name)) {
    fprintf(stderr, "ERROR: Line %d, Duplicate declaration of %s\n", currentGoomSL->num_lines, name);
    return;
  }
  else {
    ExternalFunctionStruct *gef = (ExternalFunctionStruct*)malloc(sizeof(ExternalFunctionStruct));
    gef->function = 0;
    gef->vars = goom_hash_new();
    gef->is_extern = 1;
    goom_hash_put_ptr(currentGoomSL->functions, name, (void*)gef);
  }
} /* }}} */

static void reset_scanner(GoomSL *gss)
{ /* {{{ */
  gss->num_lines = 0;
  gss->instr = NULL;
  iflow_clean(gss->iflow);

  /* reset variables */
  goom_hash_free(gss->vars);
  gss->vars = goom_hash_new();
  gss->currentNS = 0;
  gss->namespaces[0] = gss->vars;

  goom_hash_free(gss->structIDS);
  gss->structIDS  = goom_hash_new();
  
  while (gss->nbStructID > 0) {
    int i;
    gss->nbStructID--;
    for(i=0;i<gss->gsl_struct[gss->nbStructID]->nbFields;++i)
      free(gss->gsl_struct[gss->nbStructID]->fields[i]);
    free(gss->gsl_struct[gss->nbStructID]);
  }

  gss->compilationOK = 1;

  goom_heap_delete(gss->data_heap);
  gss->data_heap = goom_heap_new();
} /* }}} */

static void calculate_labels(InstructionFlow *iflow)
{ /* {{{ */
  int i = 0;
  while (i < iflow->number) {
    Instruction *instr = iflow->instr[i];
    if (instr->jump_label) {
      HashValue *label = goom_hash_get(iflow->labels,instr->jump_label);
      if (label) {
        instr->data.udest.jump_offset = -instr->address + label->i;
      }
      else {
        fprintf(stderr, "ERROR: Line %d, Could not find label %s\n", instr->line_number, instr->jump_label);
        instr->id = INSTR_NOP;
        instr->nop_label = 0;
        exit(1);
      }
    }
    ++i;
  }
} /* }}} */

#ifdef USE_JITC_X86
static int powerOfTwo(int i)
{
  int b;
  for (b=0;b<31;b++)
    if (i == (1<<b))
      return b;
  return 0;
}
#endif

/* Cree un flow d'instruction optimise */
static void gsl_create_fast_iflow(void)
{ /* {{{ */
  int number = currentGoomSL->iflow->number;
  int i;
#ifdef USE_JITC_X86

  /* pour compatibilite avec les MACROS servant a execution */
  int ip = 0;
  GoomSL *gsl = currentGoomSL;

  JitcX86Env *jitc;

  if (currentGoomSL->jitc != NULL)
    jitc_x86_delete(currentGoomSL->jitc);
  jitc = currentGoomSL->jitc = jitc_x86_env_new(0xffff);
  currentGoomSL->jitc_func = jitc_prepare_func(jitc);

#if 0  
#define SRC_STRUCT_ID  instr[ip].data.usrc.var_int[-1]
#define DEST_STRUCT_ID instr[ip].data.udest.var_int[-1]
#define SRC_STRUCT_IBLOCK(i)  gsl->gsl_struct[SRC_STRUCT_ID]->iBlock[i]
#define SRC_STRUCT_FBLOCK(i)  gsl->gsl_struct[SRC_STRUCT_ID]->fBlock[i]
#define DEST_STRUCT_IBLOCK(i) gsl->gsl_struct[DEST_STRUCT_ID]->iBlock[i]
#define DEST_STRUCT_FBLOCK(i) gsl->gsl_struct[DEST_STRUCT_ID]->fBlock[i]
#define DEST_STRUCT_IBLOCK_VAR(i,j) \
  ((int*)((char*)pDEST_VAR   + gsl->gsl_struct[DEST_STRUCT_ID]->iBlock[i].data))[j]
#define DEST_STRUCT_FBLOCK_VAR(i,j) \
  ((float*)((char*)pDEST_VAR + gsl->gsl_struct[DEST_STRUCT_ID]->fBlock[i].data))[j]
#define SRC_STRUCT_IBLOCK_VAR(i,j) \
  ((int*)((char*)pSRC_VAR    + gsl->gsl_struct[SRC_STRUCT_ID]->iBlock[i].data))[j]
#define SRC_STRUCT_FBLOCK_VAR(i,j) \
  ((float*)((char*)pSRC_VAR  + gsl->gsl_struct[SRC_STRUCT_ID]->fBlock[i].data))[j]
#define DEST_STRUCT_SIZE      gsl->gsl_struct[DEST_STRUCT_ID]->size
#endif

  JITC_JUMP_LABEL(jitc, "__very_end__");
  JITC_ADD_LABEL (jitc, "__very_start__");
  
  for (i=0;i<number;++i) {
    Instruction *instr = currentGoomSL->iflow->instr[i];
    switch (instr->id) {
      case INSTR_SETI_VAR_INTEGER     :
        jitc_add(jitc, "mov [$d], $d", instr->data.udest.var_int, instr->data.usrc.value_int);
        break;
      case INSTR_SETI_VAR_VAR         :
        jitc_add(jitc, "mov eax, [$d]", instr->data.usrc.var_int);
        jitc_add(jitc, "mov [$d], eax", instr->data.udest.var_int);
        break;
        /* SET.F */
      case INSTR_SETF_VAR_FLOAT       :
        jitc_add(jitc, "mov [$d], $d", instr->data.udest.var_float, *(int*)(&instr->data.usrc.value_float));
        break;
      case INSTR_SETF_VAR_VAR         :
        jitc_add(jitc, "mov eax, [$d]", instr->data.usrc.var_float);
        jitc_add(jitc, "mov [$d], eax", instr->data.udest.var_float);
        break;
      case INSTR_NOP                  :
        if (instr->nop_label != 0)
          JITC_ADD_LABEL(jitc, instr->nop_label);
        break;
      case INSTR_JUMP                 :
        JITC_JUMP_LABEL(jitc,instr->jump_label);
        break;
      case INSTR_SETP_VAR_PTR         :
        jitc_add(jitc, "mov [$d], $d", instr->data.udest.var_ptr, instr->data.usrc.value_ptr);
        break;
      case INSTR_SETP_VAR_VAR         :
        jitc_add(jitc, "mov eax, [$d]", instr->data.usrc.var_ptr);
        jitc_add(jitc, "mov [$d], eax", instr->data.udest.var_ptr);
        break;
      case INSTR_SUBI_VAR_INTEGER     :
        jitc_add(jitc, "add [$d],  $d", instr->data.udest.var_int, -instr->data.usrc.value_int);
        break;
      case INSTR_SUBI_VAR_VAR         :
        jitc_add(jitc, "mov eax, [$d]", instr->data.udest.var_int);
        jitc_add(jitc, "sub eax, [$d]", instr->data.usrc.var_int);
        jitc_add(jitc, "mov [$d], eax", instr->data.udest.var_int);
        break;
      case INSTR_SUBF_VAR_FLOAT       :
        printf("NOT IMPLEMENTED : %d\n", instr->id);
        break;
      case INSTR_SUBF_VAR_VAR         :
        printf("NOT IMPLEMENTED : %d\n", instr->id);
        break;
      case INSTR_ISLOWERF_VAR_VAR:
        printf("NOT IMPLEMENTED : %d\n", instr->id);
        break;
      case INSTR_ISLOWERF_VAR_FLOAT:
        printf("NOT IMPLEMENTED : %d\n", instr->id);
        break;
      case INSTR_ISLOWERI_VAR_VAR:
        jitc_add(jitc,"mov edx, [$d]", instr->data.udest.var_int);
        jitc_add(jitc,"sub edx, [$d]", instr->data.usrc.var_int);
        jitc_add(jitc,"shr edx, $d",   31);
        break;
      case INSTR_ISLOWERI_VAR_INTEGER:
        jitc_add(jitc,"mov edx, [$d]", instr->data.udest.var_int);
        jitc_add(jitc,"sub edx, $d", instr->data.usrc.value_int);
        jitc_add(jitc,"shr edx, $d",   31);
        break;
      case INSTR_ADDI_VAR_INTEGER:
        jitc_add(jitc, "add [$d],  $d", instr->data.udest.var_int, instr->data.usrc.value_int);
        break;
      case INSTR_ADDI_VAR_VAR:
        jitc_add(jitc, "mov eax, [$d]", instr->data.udest.var_int);
        jitc_add(jitc, "add eax, [$d]", instr->data.usrc.var_int);
        jitc_add(jitc, "mov [$d], eax", instr->data.udest.var_int);
        break;
      case INSTR_ADDF_VAR_FLOAT:
        printf("NOT IMPLEMENTED : %d\n", instr->id);
        break;
      case INSTR_ADDF_VAR_VAR:
        printf("NOT IMPLEMENTED : %d\n", instr->id);
        break;
      case INSTR_MULI_VAR_INTEGER:
        if (instr->data.usrc.value_int != 1)
        {
          int po2 = powerOfTwo(instr->data.usrc.value_int);
          if (po2) {
            /* performs (V / 2^n) by doing V >> n */
            jitc_add(jitc, "mov  eax, [$d]",  instr->data.udest.var_int);
            jitc_add(jitc, "sal  eax, $d",    po2);
            jitc_add(jitc, "mov  [$d], eax",  instr->data.udest.var_int);
          }
          else {
            jitc_add(jitc, "mov  eax, [$d]", instr->data.udest.var_int);
            jitc_add(jitc, "imul eax, $d",   instr->data.usrc.value_int);
            jitc_add(jitc, "mov  [$d], eax", instr->data.udest.var_int);
          }
        }
        break;
      case INSTR_MULI_VAR_VAR:
        jitc_add(jitc, "mov  eax,  [$d]", instr->data.udest.var_int);
        jitc_add(jitc, "imul eax,  [$d]", instr->data.usrc.var_int);
        jitc_add(jitc, "mov  [$d], eax",  instr->data.udest.var_int);
        break;
      case INSTR_MULF_VAR_FLOAT:
        printf("NOT IMPLEMENTED : %d\n", instr->id);
        break;
      case INSTR_MULF_VAR_VAR:
        printf("NOT IMPLEMENTED : %d\n", instr->id);
        break;
      case INSTR_DIVI_VAR_INTEGER:
        if ((instr->data.usrc.value_int != 1) && (instr->data.usrc.value_int != 0))
        {
          int po2 = powerOfTwo(instr->data.usrc.value_int);
          if (po2) {
            /* performs (V / 2^n) by doing V >> n */
            jitc_add(jitc, "mov  eax, [$d]",  instr->data.udest.var_int);
            jitc_add(jitc, "sar  eax, $d",    po2);
            jitc_add(jitc, "mov  [$d], eax",  instr->data.udest.var_int);
          }
          else {
            /* performs (V/n) by doing (V*(32^2/n)) */
            long   coef;
            double dcoef = (double)4294967296.0 / (double)instr->data.usrc.value_int;
            if (dcoef < 0.0) dcoef = -dcoef;
            coef   = (long)floor(dcoef);
            dcoef -= floor(dcoef);
            if (dcoef < 0.5) coef += 1;
            
            jitc_add(jitc, "mov  eax, [$d]", instr->data.udest.var_int);
            jitc_add(jitc, "mov  edx, $d",   coef);
            jitc_add(jitc, "imul edx");
            if (instr->data.usrc.value_int < 0)
              jitc_add(jitc, "neg edx");
            jitc_add(jitc, "mov [$d], edx", instr->data.udest.var_int);
          }
        }
        break;
      case INSTR_DIVI_VAR_VAR         :
        jitc_add(jitc, "mov  eax, [$d]", instr->data.udest.var_int);
        jitc_add(jitc, "cdq"); /* sign extend eax into edx */
        jitc_add(jitc, "idiv [$d]", instr->data.usrc.var_int);
        jitc_add(jitc, "mov [$d], eax", instr->data.udest.var_int);
        break;
      case INSTR_DIVF_VAR_FLOAT:
        printf("NOT IMPLEMENTED : %d\n", instr->id);
        break;
      case INSTR_DIVF_VAR_VAR:
        printf("NOT IMPLEMENTED : %d\n", instr->id);
        break;
      case INSTR_JZERO:
        jitc_add(jitc, "cmp edx, $d", 0);
        jitc_add(jitc, "je $s", instr->jump_label);
        break;
      case INSTR_ISEQUALP_VAR_VAR     :
        jitc_add(jitc, "mov eax, [$d]", instr->data.udest.var_ptr);
        jitc_add(jitc, "mov edx, $d",   0);
        jitc_add(jitc, "cmp eax, [$d]", instr->data.usrc.var_ptr);
        jitc_add(jitc, "jne $d",        1);
        jitc_add(jitc, "inc edx");
        break;
      case INSTR_ISEQUALP_VAR_PTR     :
        jitc_add(jitc, "mov eax, [$d]", instr->data.udest.var_ptr);
        jitc_add(jitc, "mov edx, $d",   0);
        jitc_add(jitc, "cmp eax, $d",   instr->data.usrc.value_ptr);
        jitc_add(jitc, "jne $d",        1);
        jitc_add(jitc, "inc edx");
        break;
      case INSTR_ISEQUALI_VAR_VAR     :
        jitc_add(jitc, "mov eax, [$d]", instr->data.udest.var_int);
        jitc_add(jitc, "mov edx, $d",   0);
        jitc_add(jitc, "cmp eax, [$d]", instr->data.usrc.var_int);
        jitc_add(jitc, "jne $d",        1);
        jitc_add(jitc, "inc edx");
        break;
      case INSTR_ISEQUALI_VAR_INTEGER :
        jitc_add(jitc, "mov eax, [$d]", instr->data.udest.var_int);
        jitc_add(jitc, "mov edx, $d",   0);
        jitc_add(jitc, "cmp eax, $d", instr->data.usrc.value_int);
        jitc_add(jitc, "jne $d",        1);
        jitc_add(jitc, "inc edx");
        break;
      case INSTR_ISEQUALF_VAR_VAR     :
        printf("NOT IMPLEMENTED : %d\n", instr->id);
        break;
      case INSTR_ISEQUALF_VAR_FLOAT   :
        printf("NOT IMPLEMENTED : %d\n", instr->id);
        break;
      case INSTR_CALL:
        jitc_add(jitc, "call $s", instr->jump_label);
        break;
      case INSTR_RET:
        jitc_add(jitc, "ret");
        break;
      case INSTR_EXT_CALL:
        jitc_add(jitc, "mov eax, [$d]", &(instr->data.udest.external_function->vars));
        jitc_add(jitc, "push eax");
        jitc_add(jitc, "mov edx, [$d]", &(currentGoomSL->vars));
        jitc_add(jitc, "push edx");
        jitc_add(jitc, "mov eax, [$d]", &(currentGoomSL));
        jitc_add(jitc, "push eax");

        jitc_add(jitc, "mov eax, [$d]",  &(instr->data.udest.external_function));
        jitc_add(jitc, "mov eax, [eax]");
        jitc_add(jitc, "call [eax]");
        jitc_add(jitc, "add esp, $d", 12);
        break;
      case INSTR_NOT_VAR:
        jitc_add(jitc, "mov eax, edx");
        jitc_add(jitc, "mov edx, $d", 1);
        jitc_add(jitc, "sub edx, eax");
        break;
      case INSTR_JNZERO:
        jitc_add(jitc, "cmp edx, $d", 0);
        jitc_add(jitc, "jne $s", instr->jump_label);
        break;
      case INSTR_SETS_VAR_VAR:
        {
          int loop = DEST_STRUCT_SIZE / sizeof(int);
          int dst  = (int)pDEST_VAR;
          int src  = (int)pSRC_VAR;
        
          while (loop--) {
            jitc_add(jitc,"mov eax, [$d]", src);
            jitc_add(jitc,"mov [$d], eax", dst);
            src += 4;
            dst += 4;
          }
        }
        break;
      case INSTR_ISEQUALS_VAR_VAR:
        break;
      case INSTR_ADDS_VAR_VAR:
        {
        /* process integers */
        int i=0;
        while (DEST_STRUCT_IBLOCK(i).size > 0) {
          int j=DEST_STRUCT_IBLOCK(i).size;
          while (j--) { /* TODO interlace 2 */
            jitc_add(jitc, "mov eax, [$d]", &DEST_STRUCT_IBLOCK_VAR(i,j));
            jitc_add(jitc, "add eax, [$d]", &SRC_STRUCT_IBLOCK_VAR(i,j));
            jitc_add(jitc, "mov [$d], eax", &DEST_STRUCT_IBLOCK_VAR(i,j));
          }
          ++i;
        }
        /* process floats */
        i=0;
        while (DEST_STRUCT_FBLOCK(i).size > 0) {
          int j=DEST_STRUCT_FBLOCK(i).size;
          while (j--) {
            /* DEST_STRUCT_FBLOCK_VAR(i,j) += SRC_STRUCT_FBLOCK_VAR(i,j); */
            /* TODO */
          }
          ++i;
        }
        break;
        }
      case INSTR_SUBS_VAR_VAR:
        {
        /* process integers */
        int i=0;
        while (DEST_STRUCT_IBLOCK(i).size > 0) {
          int j=DEST_STRUCT_IBLOCK(i).size;
          while (j--) {
            jitc_add(jitc, "mov eax, [$d]", &DEST_STRUCT_IBLOCK_VAR(i,j));
            jitc_add(jitc, "sub eax, [$d]", &SRC_STRUCT_IBLOCK_VAR(i,j));
            jitc_add(jitc, "mov [$d], eax", &DEST_STRUCT_IBLOCK_VAR(i,j));
          }
          ++i;
        }
        break;
        }
      case INSTR_MULS_VAR_VAR:
        {
        /* process integers */
        int i=0;
        while (DEST_STRUCT_IBLOCK(i).size > 0) {
          int j=DEST_STRUCT_IBLOCK(i).size;
          while (j--) {
            jitc_add(jitc, "mov  eax,  [$d]", &DEST_STRUCT_IBLOCK_VAR(i,j));
            jitc_add(jitc, "imul eax,  [$d]", &SRC_STRUCT_IBLOCK_VAR(i,j));
            jitc_add(jitc, "mov  [$d], eax",  &DEST_STRUCT_IBLOCK_VAR(i,j));
          }
          ++i;
        }
        break;
        }
      case INSTR_DIVS_VAR_VAR:
        {
        /* process integers */
        int i=0;
        while (DEST_STRUCT_IBLOCK(i).size > 0) {
          int j=DEST_STRUCT_IBLOCK(i).size;
          while (j--) {
            jitc_add(jitc, "mov  eax,  [$d]", &DEST_STRUCT_IBLOCK_VAR(i,j));
            jitc_add(jitc, "cdq");
            jitc_add(jitc, "idiv [$d]",       &SRC_STRUCT_IBLOCK_VAR(i,j));
            jitc_add(jitc, "mov  [$d], eax",  &DEST_STRUCT_IBLOCK_VAR(i,j));
          }
          ++i;
        }
        break;
        }
    }
  }

  JITC_ADD_LABEL (jitc, "__very_end__");
  jitc_add(jitc, "call $s", "__very_start__");
  jitc_add(jitc, "mov eax, $d", 0);
  jitc_validate_func(jitc);
#else
  InstructionFlow     *iflow     = currentGoomSL->iflow;
  FastInstructionFlow *fastiflow = (FastInstructionFlow*)malloc(sizeof(FastInstructionFlow));
  fastiflow->mallocedInstr = calloc(number*16, sizeof(FastInstruction));
  /* fastiflow->instr = (FastInstruction*)(((int)fastiflow->mallocedInstr) + 16 - (((int)fastiflow->mallocedInstr)%16)); */
  fastiflow->instr = (FastInstruction*)fastiflow->mallocedInstr;
  fastiflow->number = number;
  for(i=0;i<number;++i) {
    fastiflow->instr[i].id    = iflow->instr[i]->id;
    fastiflow->instr[i].data  = iflow->instr[i]->data;
    fastiflow->instr[i].proto = iflow->instr[i];
  }
  currentGoomSL->fastiflow = fastiflow;
#endif
} /* }}} */

void yy_scan_string(const char *str);
void yyparse(void);

GoomHash *gsl_globals(GoomSL *_this)
{
  return _this->vars;
}


/**
 * Some native external functions
 */
static void ext_charAt(GoomSL *gsl, GoomHash *global, GoomHash *local)
{
  char *string = GSL_LOCAL_PTR(gsl, local, "value");
  int   index  = GSL_LOCAL_INT(gsl, local, "index");
  GSL_GLOBAL_INT(gsl, "charAt") = 0;
  if (string == NULL) {
    return;
  }
  if (index < strlen(string))
    GSL_GLOBAL_INT(gsl, "charAt") = string[index];
}

static void ext_i2f(GoomSL *gsl, GoomHash *global, GoomHash *local)
{
  int i = GSL_LOCAL_INT(gsl, local, "value");
  GSL_GLOBAL_FLOAT(gsl, "i2f") = i;
}

static void ext_f2i(GoomSL *gsl, GoomHash *global, GoomHash *local)
{
  float f = GSL_LOCAL_FLOAT(gsl, local, "value");
  GSL_GLOBAL_INT(gsl, "f2i") = f;
}

/**
 *
 */
void gsl_compile(GoomSL *_currentGoomSL, const char *script)
{ /* {{{ */
  char *script_and_externals;
  static const char *sBinds =
    "external <charAt: string value, int index> : int\n"
    "external <f2i: float value> : int\n"
    "external <i2f: int value> : float\n";

#ifdef VERBOSE
  printf("\n=== Starting Compilation ===\n");
#endif

  script_and_externals = malloc(strlen(script) + strlen(sBinds) + 2);
  strcpy(script_and_externals, sBinds);
  strcat(script_and_externals, script);

  /* 0- reset */
  currentGoomSL = _currentGoomSL;
  reset_scanner(currentGoomSL);

  /* 1- create the syntaxic tree */
  yy_scan_string(script_and_externals);
  yyparse();

  /* 2- generate code */
  gsl_commit_compilation();

  /* 3- resolve symbols */
  calculate_labels(currentGoomSL->iflow);

  /* 4- optimize code */
  gsl_create_fast_iflow();

  /* 5- bind a few internal functions */
  gsl_bind_function(currentGoomSL, "charAt", ext_charAt);
  gsl_bind_function(currentGoomSL, "f2i", ext_f2i);
  gsl_bind_function(currentGoomSL, "i2f", ext_i2f);
  free(script_and_externals);
  
#ifdef VERBOSE
  printf("=== Compilation done. # of lines: %d. # of instr: %d ===\n", currentGoomSL->num_lines, currentGoomSL->iflow->number);
#endif
} /* }}} */

void gsl_execute(GoomSL *scanner)
{ /* {{{ */
  if (scanner->compilationOK) {
#if USE_JITC_X86
    scanner->jitc_func();
#else
    iflow_execute(scanner->fastiflow, scanner);
#endif
  }
} /* }}} */

GoomSL *gsl_new(void)
{ /* {{{ */
  GoomSL *gss = (GoomSL*)malloc(sizeof(GoomSL));

  gss->iflow = iflow_new();
  gss->vars  = goom_hash_new();
  gss->functions = goom_hash_new();
  gss->nbStructID  = 0;
  gss->structIDS   = goom_hash_new();
  gss->gsl_struct_size = 32;
  gss->gsl_struct = (GSL_Struct**)malloc(gss->gsl_struct_size * sizeof(GSL_Struct*));
  gss->currentNS = 0;
  gss->namespaces[0] = gss->vars;
  gss->data_heap = goom_heap_new();

  reset_scanner(gss);

  gss->compilationOK = 0;
  gss->nbPtr=0;
  gss->ptrArraySize=256;
  gss->ptrArray = (void**)malloc(gss->ptrArraySize * sizeof(void*));
#ifdef USE_JITC_X86
  gss->jitc = NULL;
#endif
  return gss;
} /* }}} */

void gsl_bind_function(GoomSL *gss, const char *fname, GoomSL_ExternalFunction func)
{ /* {{{ */
  HashValue *val = goom_hash_get(gss->functions, fname);
  if (val) {
    ExternalFunctionStruct *gef = (ExternalFunctionStruct*)val->ptr;
    gef->function = func;
  }
  else fprintf(stderr, "Unable to bind function %s\n", fname);
} /* }}} */

int gsl_is_compiled(GoomSL *gss)
{ /* {{{ */
  return gss->compilationOK;
} /* }}} */

void gsl_free(GoomSL *gss)
{ /* {{{ */
  if (gss->iflow)
    iflow_free(gss->iflow);
  if (gss->vars)
    goom_hash_free(gss->vars);
  if (gss->functions)
    goom_hash_free(gss->functions);

  free(gss->gsl_struct);
  free(gss->ptrArray);

  if (gss->data_heap)
    goom_heap_delete(gss->data_heap);
  if (gss->structIDS)
    goom_hash_free(gss->structIDS);

  free(gss);
} /* }}} */


static int gsl_nb_import;
static char gsl_already_imported[256][256];

char *gsl_init_buffer(const char *fname)
{
    char *fbuffer;
    fbuffer = (char*)malloc(512);
    fbuffer[0]=0;
    gsl_nb_import = 0;
    if (fname)
      gsl_append_file_to_buffer(fname,&fbuffer);
    return fbuffer;
}

static char *gsl_read_file(const char *fname)
{
  FILE *f;
  char *buffer;
  int fsize;
  f = fopen(fname,"rt");
  if (!f) {
    fprintf(stderr, "ERROR: Could not load file %s\n", fname);
    exit(1);
  }
  fseek(f,0,SEEK_END);
  fsize = ftell(f);
  rewind(f);
  buffer = (char*)malloc(fsize+512);
  if (fread(buffer,1,fsize,f) != fsize) {
    fprintf(stderr, "ERROR: Could not read file %s\n", fname);
    exit(1);
  }
  fclose(f);
  buffer[fsize]=0;
  return buffer;
}

void gsl_append_file_to_buffer(const char *fname, char **buffer)
{
    char *fbuffer;
    int size,fsize,i=0;
    char reset_msg[256];
    
    /* look if the file have not been already imported */
    for (i=0;i<gsl_nb_import;++i) {
      if (strcmp(gsl_already_imported[i], fname) == 0)
        return;
    }
    
    /* add fname to the already imported files. */
    strlcpy(gsl_already_imported[gsl_nb_import], fname, sizeof(gsl_already_imported[0]));

    /* load the file */
    fbuffer = gsl_read_file(fname);
    fsize = strlen(fbuffer);
        
    /* look for #import */
    while (fbuffer[i]) {
      if ((fbuffer[i]=='#') && (fbuffer[i+1]=='i')) {
        char impName[256];
        int j;
        while (fbuffer[i] && (fbuffer[i]!=' '))
          i++;
        i++;
        j=0;
        while (fbuffer[i] && (fbuffer[i]!='\n'))
          impName[j++] = fbuffer[i++];
        impName[j++] = 0;
        gsl_append_file_to_buffer(impName, buffer);
      }
      i++;
    }
    
    sprintf(reset_msg, "\n#FILE %s#\n#RST_LINE#\n", fname);
    strcat(*buffer, reset_msg);
    size=strlen(*buffer);
    *buffer = (char*)realloc(*buffer, size+fsize+256);
    strcat((*buffer)+size, fbuffer);
    free(fbuffer);
}


