/* construct.c */

#include "config.h"
#include "object.h"
#include "instr.h"
#include "construct.h"
#include "globals.h"
#include "cache.h"

int is_legal(char *name) {
  int x,len;

  len=strlen(name);
  if (!len || len>14) return 0;
  if (*name=='.') return 0;
  x=0;
  while (x<len) {
    if (!(isgraph(name[x])) || name[x]=='/' || name[x]=='#')
      return 0;
    x++;
  }
  return 1;
}

void free_gst(struct var_tab *gst) {
  struct var_tab *curr_var,*next_var;
  struct array_size *curr_array,*next_array;

  curr_var=gst;
  while (curr_var) {
    next_var=curr_var->next;
    curr_array=curr_var->array;
    while (curr_array) {
      next_array=curr_array->next;
      FREE(curr_array);
      curr_array=next_array;
    }
    FREE(curr_var->name);
    FREE(curr_var);
    curr_var=next_var;
  }
}

void free_code(struct code *the_code) {
  struct fns *next,*curr;
  unsigned int x;

  if (!the_code) return;
  next=the_code->func_list;
  while (next) {
    curr=next;
    next=curr->next;
    x=0;
    while (x<curr->num_instr) {
      clear_var(&(curr->code[x]));
      x++;
    }
    if (curr->code)
      FREE(curr->code);
    FREE(curr->funcname);
    FREE(curr);
  }
  free_gst(the_code->gst);
  FREE(the_code);
}

char *copy_string(char *s) {
  return strcpy((char *) MALLOC(strlen(s)+1),s);
}

void push(struct var *data, struct var_stack **rts) {
  struct var_stack *tmp;

  tmp=MALLOC(sizeof(struct var_stack));
  tmp->data.type=data->type;
  switch (data->type) {
    case STRING:
    case FUNC_NAME:
      tmp->data.value.string=copy_string(data->value.string);
      break;
    case INTEGER:
      tmp->data.value.integer=data->value.integer;
      break;
    case OBJECT:
      tmp->data.value.objptr=data->value.objptr;
      break;
    case ASM_INSTR:
      tmp->data.value.instruction=data->value.instruction;
      break;
    case GLOBAL_L_VALUE:
    case LOCAL_L_VALUE:
      tmp->data.value.l_value.ref=data->value.l_value.ref;
      tmp->data.value.l_value.size=data->value.l_value.size;
      break;
    case FUNC_CALL:
      tmp->data.value.func_call=data->value.func_call;
      break;
    default:
      tmp->data.value.num=data->value.num;
      break;
  }
  tmp->next=*rts;
  *rts=tmp;
}

void pushnocopy(struct var *data, struct var_stack **rts) {
  struct var_stack *tmp;

  tmp=MALLOC(sizeof(struct var_stack));
  tmp->data.type=data->type;
  switch (data->type) {
    case STRING:
    case FUNC_NAME:
      tmp->data.value.string=data->value.string;
      break;
    case INTEGER:
      tmp->data.value.integer=data->value.integer;
      break;
    case OBJECT:
      tmp->data.value.objptr=data->value.objptr;
      break;
    case ASM_INSTR:
      tmp->data.value.instruction=data->value.instruction;
      break;
    case GLOBAL_L_VALUE:
    case LOCAL_L_VALUE:
      tmp->data.value.l_value.ref=data->value.l_value.ref;
      tmp->data.value.l_value.size=data->value.l_value.size;
      break;
    case FUNC_CALL:
      tmp->data.value.func_call=data->value.func_call;
      break;
    default:
      tmp->data.value.num=data->value.num;
      break;
  }
  tmp->next=*rts;
  *rts=tmp;
}

int resolve_var(struct var *data, struct object *obj) {
  if (data->type==GLOBAL_L_VALUE) {
    if (data->value.l_value.size!=1)
      return 1;
    if (data->value.l_value.ref>=obj->parent->funcs->num_globals)
      return 1;
    data->type=obj->globals[data->value.l_value.ref].type;
    switch (data->type) {
      case INTEGER:
        data->value.integer=obj->globals[data->value.l_value.ref].value.integer;
        break;
      case STRING:
        data->value.string=copy_string(obj->globals[data->value.l_value.ref].
                                       value.string);
        break;
      case OBJECT:
        data->value.objptr=obj->globals[data->value.l_value.ref].value.objptr;
        break;
      default:
        return 1;
        break;
    }
  } else
    if (data->type==LOCAL_L_VALUE) {
      if (data->value.l_value.size!=1)
        return 1;
      if (data->value.l_value.ref>=num_locals)
        return 1;
      data->type=locals[data->value.l_value.ref].type;
      switch (data->type) {
        case INTEGER:
          data->value.integer=locals[data->value.l_value.ref].value.integer;
          break;
        case STRING:
          data->value.string=copy_string(locals[data->value.l_value.ref].
                                         value.string);
          break;
        case OBJECT:
          data->value.objptr=locals[data->value.l_value.ref].value.objptr;
          break;
        default:
          return 1;
          break;
      }
    }
  return 0;
}

int popint(struct var *data, struct var_stack **rts, struct object *obj) {
  struct var tmp;
  int size,base;
  struct var_stack *ptr;

  ptr=*rts;
  if (ptr==NULL) return 1;
  *rts=ptr->next;
  data->type=ptr->data.type;
  switch (ptr->data.type) {
    case INTEGER:
      data->value.integer=ptr->data.value.integer;
      break;
    case STRING:
    case FUNC_NAME:
      data->value.string=ptr->data.value.string;
      break;
    case OBJECT:
      data->value.objptr=ptr->data.value.objptr;
      break;
    case ASM_INSTR:
      data->value.instruction=ptr->data.value.instruction;
      break;
    case GLOBAL_L_VALUE:
    case LOCAL_L_VALUE:
      data->value.l_value.ref=ptr->data.value.l_value.ref;
      data->value.l_value.size=ptr->data.value.l_value.size;
      break;
    case FUNC_CALL:
      data->value.func_call=ptr->data.value.func_call;
      break;
    case LOCAL_REF:
    case GLOBAL_REF:
      if (popint(&tmp,rts,obj)) {
        FREE(ptr);
        return 1;
      }
      data->value.l_value.size=tmp.value.integer;
      if (popint(&tmp,rts,obj)) {
        FREE(ptr);
        return 1;
      }
      data->value.l_value.ref=tmp.value.integer;
      if (data->type==LOCAL_REF) {
        if (data->value.l_value.ref>=num_locals) {
          FREE(ptr);
          return 1;
        }
        data->type=LOCAL_L_VALUE;
      } else {
        if (data->value.l_value.ref>=obj->parent->funcs->num_globals) {
          FREE(ptr);
          return 1;
        }
        data->type=GLOBAL_L_VALUE;
      }
      break;
    default:
      data->value.num=ptr->data.value.num;
      break;
  }
  if (data->type==LOCAL_L_VALUE || data->type==GLOBAL_L_VALUE)
    if (data->value.l_value.size!=1) {
      FREE(ptr);
      return 1;
    }
  if (resolve_var(data,obj)) {
    clear_var(data);
    FREE(ptr);
    return 1;
  }
  if (data->type!=INTEGER) {
    clear_var(data);
    FREE(ptr);
    return 1;
  }
  FREE(ptr);
  return 0;
}

int pop(struct var *data, struct var_stack **rts, struct object *obj) {
  struct var tmp;
  int size,base;
  struct var_stack *ptr;

  ptr=*rts;
  if (ptr==NULL) return 1;
  *rts=ptr->next;
  data->type=ptr->data.type;
  switch (ptr->data.type) {
    case INTEGER:
      data->value.integer=ptr->data.value.integer;
      break;
    case STRING:
    case FUNC_NAME:
      data->value.string=ptr->data.value.string;
      break;
    case OBJECT:
      data->value.objptr=ptr->data.value.objptr;
      break;
    case ASM_INSTR:
      data->value.instruction=ptr->data.value.instruction;
      break;
    case GLOBAL_L_VALUE:
    case LOCAL_L_VALUE:
      data->value.l_value.ref=ptr->data.value.l_value.ref;
      data->value.l_value.size=ptr->data.value.l_value.size;
      break;
    case FUNC_CALL:
      data->value.func_call=ptr->data.value.func_call;
      break;
    case LOCAL_REF:
    case GLOBAL_REF:
      if (popint(&tmp,rts,obj)) {
        FREE(ptr);
        return 1;
      }
      data->value.l_value.size=tmp.value.integer;
      if (popint(&tmp,rts,obj)) {
        FREE(ptr);
        return 1;
      }
      data->value.l_value.ref=tmp.value.integer;
      if (data->type==LOCAL_REF) {
        if (data->value.l_value.ref>=num_locals) {
          FREE(ptr);
          return 1;
        }
        data->type=LOCAL_L_VALUE;
      } else {
        if (data->value.l_value.ref>=obj->parent->funcs->num_globals) {
          FREE(ptr);
          return 1;
        }
        data->type=GLOBAL_L_VALUE;
      }
      break;
    default:
      data->value.num=ptr->data.value.num;
      break;
  }
  if (data->type==LOCAL_L_VALUE || data->type==GLOBAL_L_VALUE)
    if (data->value.l_value.size!=1) {
      FREE(ptr);
      return 1;
    }
  FREE(ptr);
  return 0;
}

void free_stack(struct var_stack **rts) {
  struct var_stack *next;

  next=*rts;
  while (next) {
    next=(*rts)->next;
    if ((*rts)->data.type==STRING || (*rts)->data.type==FUNC_NAME)
      FREE((*rts)->data.value.string);
    FREE(*rts);
    *rts=next;
  }
}

void clear_var(struct var *data) {
  if (data->type==STRING || data->type==FUNC_NAME)
    FREE(data->value.string);
  data->type=INTEGER;
  data->value.integer=0;
}

void clear_global_var(struct object *obj, unsigned int ref) {
  struct ref_list *next,*curr;

  if (ref>=obj->parent->funcs->num_globals)
    return;
  obj->obj_state=DIRTY;
  if (obj->globals[ref].type==STRING || obj->globals[ref].type==FUNC_NAME)
    FREE(obj->globals[ref].value.string);
  if (obj->globals[ref].type==OBJECT) {
    load_data(obj->globals[ref].value.objptr);
    obj->globals[ref].value.objptr->obj_state=DIRTY;
    next=obj->globals[ref].value.objptr->refd_by;
    if (next)
      if (next->ref_obj==obj && next->ref_num==ref) {
        obj->globals[ref].value.objptr->refd_by=next->next;
        FREE(next);
      } else
        while (next)
          if (next->ref_obj==obj && next->ref_num==ref) {
            curr->next=next->next;
            FREE(next);
            next=NULL;
          } else {
            curr=next;
            next=curr->next;
          }
  }
  obj->globals[ref].type=INTEGER;
  obj->globals[ref].value.integer=0;
}

void copy_var(struct var *dest, struct var *src) {
  dest->type=src->type;
  switch (src->type) {
    case INTEGER:
      dest->value.integer=src->value.integer;
      break;
    case STRING:
    case FUNC_NAME:
      dest->value.string=copy_string(src->value.string);
      break;
    case OBJECT:
      dest->value.objptr=src->value.objptr;
      break;
    case ASM_INSTR:
      dest->value.instruction=src->value.instruction;
      break;
    case GLOBAL_L_VALUE:
    case LOCAL_L_VALUE:
      dest->value.l_value.ref=src->value.l_value.ref;
      dest->value.l_value.size=src->value.l_value.size;
      break;
    default:
      dest->value.num=src->value.num;
      break;
  }
}

struct var_stack *gen_stack(struct var_stack **rts, struct object *obj) {
  struct var_stack *tmp,*stack1;
  unsigned long arg_count;

  stack1=*rts;
  tmp=*rts;
  if (!tmp) return NULL;
  arg_count=tmp->data.value.num;
  while (arg_count && tmp->next) {
    tmp=tmp->next;
    arg_count--;
    if (resolve_var(&(tmp->data),obj))
      return NULL;
  }
  if (arg_count) return NULL;
  *rts=tmp->next;
  tmp->next=NULL;
  return stack1;
}

struct var_stack *gen_stack_noresolve(struct var_stack **rts,
                                      struct object *obj) {
  struct var_stack *tmp,*stack1;
  unsigned long arg_count;

  stack1=*rts;
  tmp=*rts;
  if (!tmp) return NULL;
  arg_count=tmp->data.value.num;
  while (arg_count && tmp->next) {
    tmp=tmp->next;
    arg_count--;
  }
  if (arg_count) return NULL;
  *rts=tmp->next;
  tmp->next=NULL;
  return stack1;
}