/* UPE library - code tree operations
 * (c)2000 Stepan Roh
 * see license.txt for copying
 */
 
#include "upecc_tree.h"
#include "upecc_ids.h"
#include "upecc.h"

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

/* byte_size from source */
int upecc_byte_size = UPECC_DEF_BYTE_SIZE;
/* numbers_len from source */
int upecc_num_len = UPECC_DEF_NUM_LEN;
/* global id table */
id_table_t *global_id_table = NULL;
/* ip register */
char *upecc_ip_reg = NULL;
/* generated processor name */
char *upecc_proc_name = "upe_proc";
/* instructions tree node */
tree_node_t *upecc_instructions = NULL;

/* creates tree node */
tree_node_t *create_tree (void) {
  tree_node_t *t;
  
  if (!(t = malloc (sizeof (tree_node_t)))) return NULL;
  
  t->sons = t->next = NULL;
  t->temp_vars_num = 0;
  
  return t;
}

/* destroys whole tree */
void destroy_tree (tree_node_t *tree) {
  tree_node_t *t;
  
  while (tree->sons) {
    t = tree->sons->next;
    destroy_tree (tree->sons);
    tree->sons = t;
  }
  
  switch (tree->type) {
    case NODE_NUM :
      upe_donenum (tree->x.num);
      break;
    case NODE_VAR :
    case NODE_FN :
      free (tree->x.id);
      break;
    case NODE_VAR_DECL :
      free (tree->x.var_decl.id);
      break;
    case NODE_INSTRUCTION :
      destroy_id_table (tree->x.id_table);
      break;
    case NODE_NUM_QUOTE :
      upe_donenum (tree->x.num_quote.num);
      break;
    case NODE_NUM_BIT_RANGE :
      break;
  }
        
  free (tree);
}

/* evaluates tree immediately
 * returns zero on success, non-zero on error
 */
int eval_tree (tree_node_t *tree, upe_number_t **result) {

  assert (tree); assert (result);
  
  switch (tree->type) {
    case NODE_NUM : *result = upe_dupnum (tree->x.num); break;
    case NODE_FN : {
      tree_node_t *n; int snum;
      id_item_t *i; upe_number_t **args, *tmp, *res; int j, ret;
      
      i = (id_item_t *)lookup_id_table (global_id_table, tree->x.id);
      if (!i || (i->type != ID_BUILTIN_FN) || !i->x.bifn.builtin_fn) return -1;
      n = tree->sons; snum = 0;
      while (n) {
        snum++;
        n = n->next;
      }
      args = NULL;
      if (snum) {
        if (!(args = malloc (sizeof (upe_number_t *) * snum))) return -2;
      }
      n = tree->sons;
      for (j = 0; j < snum; j++) {
        ret = eval_tree (n, &res);
        if (ret) return ret;
        args[j] = res;
        n = n->next;
      }
      if (!(tmp = i->x.bifn.builtin_fn (snum, args))) return -2;
      *result = tmp;
      for (j = 0; j < snum; j++) upe_donenum (args[j]);
      free (args);
      break; }
    case NODE_VAR : {
      id_item_t *i;
      
      i = (id_item_t *)lookup_id_table (global_id_table, tree->x.id);
      if (!i || (i->type != ID_CONST)) return -1;
      *result = upe_dupnum (i->x.num);
      break; }
    default :
      return -1;
  }
  
  return 0;
}

/* free_info fn for id_item_t */
void destroy_id_item (void *item) {

  switch (((id_item_t *)item)->type) {
    case ID_VAR : break;
    case ID_FN :
      destroy_tree (((id_item_t *)item)->x.fn.args);
      destroy_tree (((id_item_t *)item)->x.fn.code);
      destroy_id_table (((id_item_t *)item)->x.fn.id_table);
      break;
    case ID_EXT_FN : break;
    case ID_BUILTIN_FN : break;
    case ID_CONST :
      upe_donenum (((id_item_t *)item)->x.num);
      break;
    case ID_REG : break;
    case ID_INSTR_ALIAS : break;
  }
  
  free (item);
}

/* builtin functions */

upe_number_t *fn_sum_add (int num, upe_number_t **vals) {
  upe_number_t *n; int i;
  
  if (!(n = upe_initnum (upecc_num_len, 0, UPE_NO_SIGN))) return NULL;
  upe_zeronum (n);
  
  for (i = 0; i < num; i++) upe_addnum (n, vals[i], NULL);
  
  return n;
}

upe_number_t *fn_sum_sub (int num, upe_number_t **vals) {
  upe_number_t *n; int i;
  
  if (!(n = upe_initnum (upecc_num_len, 0, UPE_NO_SIGN))) return NULL;
  upe_zeronum (n);
  
  if (num) {
    upe_copynum (n, vals[0]);
    for (i = 1; i < num; i++) upe_subnum (n, vals[i], NULL);
  }
  
  return n;
}

upe_number_t *fn_sum_and (int num, upe_number_t **vals) {
  upe_number_t *n; int i;
  
  if (!(n = upe_initnum (upecc_num_len, 0, UPE_NO_SIGN))) return NULL;
  upe_zeronum (n);
  
  if (num) {
    upe_copynum (n, vals[0]);
    for (i = 1; i < num; i++) upe_andnum (n, vals[i]);
  }
  
  return n;
}

upe_number_t *fn_sum_or (int num, upe_number_t **vals) {
  upe_number_t *n; int i;
  
  if (!(n = upe_initnum (upecc_num_len, 0, UPE_NO_SIGN))) return NULL;
  upe_zeronum (n);
  
  for (i = 0; i < num; i++) upe_ornum (n, vals[i]);
  
  return n;
}

upe_number_t *fn_sum_bitand (int num, upe_number_t **vals) {
  upe_number_t *n; int i;
  
  if (!(n = upe_initnum (upecc_num_len, 0, UPE_NO_SIGN))) return NULL;
  upe_zeronum (n);
  
  if (num) {
    upe_copynum (n, vals[0]);
    for (i = 1; i < num; i++) upe_bitandnum (n, vals[i]);
  }
  
  return n;
}

upe_number_t *fn_sum_bitor (int num, upe_number_t **vals) {
  upe_number_t *n; int i;
  
  if (!(n = upe_initnum (upecc_num_len, 0, UPE_NO_SIGN))) return NULL;
  upe_zeronum (n);
  
  for (i = 0; i < num; i++) upe_bitornum (n, vals[i]);
  
  return n;
}

upe_number_t *fn_sum_bitxor (int num, upe_number_t **vals) {
  upe_number_t *n; int i;
  
  if (!(n = upe_initnum (upecc_num_len, 0, UPE_NO_SIGN))) return NULL;
  upe_zeronum (n);
  
  if (num) {
    upe_copynum (n, vals[0]);
    for (i = 1; i < num; i++) upe_bitxornum (n, vals[i]);
  }
  
  return n;
}

upe_number_t *fn_eq (int num, upe_number_t **vals) {
  upe_number_t *n;
  
  assert (num == 2);
  
  if (!(n = upe_initnum (upecc_num_len, 0, UPE_NO_SIGN))) return NULL;
  upe_zeronum (n);
  
  upe_setnum (n, upe_eqnum (vals[0], vals[1]));
  
  return n;
}

upe_number_t *fn_ne (int num, upe_number_t **vals) {
  upe_number_t *n;
  
  assert (num == 2);
  
  if (!(n = upe_initnum (upecc_num_len, 0, UPE_NO_SIGN))) return NULL;
  upe_zeronum (n);
  
  upe_setnum (n, upe_nenum (vals[0], vals[1]));
  
  return n;
}

upe_number_t *fn_lt (int num, upe_number_t **vals) {
  upe_number_t *n;
  
  assert (num == 2);
  
  if (!(n = upe_initnum (upecc_num_len, 0, UPE_NO_SIGN))) return NULL;
  upe_zeronum (n);
  
  upe_setnum (n, upe_ltnum (vals[0], vals[1]));
  
  return n;
}

upe_number_t *fn_le (int num, upe_number_t **vals) {
  upe_number_t *n;
  
  assert (num == 2);
  
  if (!(n = upe_initnum (upecc_num_len, 0, UPE_NO_SIGN))) return NULL;
  upe_zeronum (n);
  
  upe_setnum (n, upe_lenum (vals[0], vals[1]));
  
  return n;
}

upe_number_t *fn_gt (int num, upe_number_t **vals) {
  upe_number_t *n;
  
  assert (num == 2);
  
  if (!(n = upe_initnum (upecc_num_len, 0, UPE_NO_SIGN))) return NULL;
  upe_zeronum (n);
  
  upe_setnum (n, upe_gtnum (vals[0], vals[1]));
  
  return n;
}

upe_number_t *fn_ge (int num, upe_number_t **vals) {
  upe_number_t *n;
  
  assert (num == 2);
  
  if (!(n = upe_initnum (upecc_num_len, 0, UPE_NO_SIGN))) return NULL;
  upe_zeronum (n);
  
  upe_setnum (n, upe_genum (vals[0], vals[1]));
  
  return n;
}

upe_number_t *fn_neg (int num, upe_number_t **vals) {
  upe_number_t *n;
  
  assert (num == 1);
  
  if (!(n = upe_initnum (upecc_num_len, 0, UPE_NO_SIGN))) return NULL;
  upe_zeronum (n);
  upe_copynum (n, vals[0]);
  upe_negnum (n);
  
  return n;
}

upe_number_t *fn_bitneg (int num, upe_number_t **vals) {
  upe_number_t *n;
  
  assert (num == 1);
  
  if (!(n = upe_initnum (upecc_num_len, 0, UPE_NO_SIGN))) return NULL;
  upe_zeronum (n);
  upe_copynum (n, vals[0]);
  upe_bitnegnum (n);
  
  return n;
}

upe_number_t *fn_cond_expr (int num, upe_number_t **vals) {
  upe_number_t *n;
  
  assert (num == 3);
  
  if (!(n = upe_initnum (upecc_num_len, 0, UPE_NO_SIGN))) return NULL;
  upe_zeronum (n);
  if (upe_isnonzero (vals[0]))
    upe_copynum (n, vals[1]);
  else
    upe_copynum (n, vals[2]);
  
  return n;
}

upe_number_t *fn_bit_range (int num, upe_number_t **vals) {
  upe_number_t *n;
  
  assert (num == 3);

  if (!(n = upe_dupnum_range (vals[0], upe_getnum (vals[1]), upe_getnum (vals[2])))) return NULL;

  return n;
}

upe_number_t *fn_shl (int num, upe_number_t **vals) {
  upe_number_t *n;
  
  assert (num == 2);
  
  if (!(n = upe_initnum (upecc_num_len, 0, UPE_NO_SIGN))) return NULL;
  upe_zeronum (n);
  upe_copynum (n, vals[0]);
  upe_shlnum (n, upe_getnum (vals[1]));
  
  return n;
}

upe_number_t *fn_shr (int num, upe_number_t **vals) {
  upe_number_t *n;
  
  assert (num == 2);
  
  if (!(n = upe_initnum (upecc_num_len, 0, UPE_NO_SIGN))) return NULL;
  upe_zeronum (n);
  upe_copynum (n, vals[0]);
  upe_shrnum (n, upe_getnum (vals[1]));
  
  return n;
}
