/* syntax.c  syntax module for vasm */
/* (c) in 2002 by Volker Barthelmann */

#include "vasm.h"

/* The syntax module parses the input (read_next_line), handles
   assembly-directives (section, data-storage etc.) and parses
   mnemonics. Assembly instructions are split up in mnemonic name,
   qualifiers and operands. new_inst returns a matching instruction,
   if one exists.
   Routines for creating sections and adding atoms to sections will
   be provided by the main module.
*/

char *syntax_copyright="vasm test syntax module (c) 2002 Volker Barthelmann";

hashtable *dirhash;

char commentchar=';';
char *defsectname = NULL;
char *defsecttype = NULL;


char *skip(char *s)
{
  while (isspace((unsigned char )*s))
    s++;
  return s;
}

/* check for end of line, issue error, if not */
void eol(char *s)
{
  s = skip(s);
  if (*s!='\0' && *s!=commentchar)
    syntax_error(6);
}

char *skip_operand(char *s)
{
  int par_cnt=0;
  while(1){
    if(*s=='(') par_cnt++;
    if(*s==')'){
      if(par_cnt>0)
        par_cnt--;
      else
        syntax_error(3);
    }
    if(!*s||*s==commentchar||(*s==','&&par_cnt==0))
      break;
    s++;
  }
  if(par_cnt!=0)
    syntax_error(4);
  return s;
}

static void prident(char *p,int len)
{
  int olen=len;
  while(len--)
    putchar(*p++);
  printf("(len=%d)",olen);
}

static void handle_section(char *s)
{
  char *name,*attr;
  if(*s!='\"'){
    name=s;
    if(!ISIDSTART(*s)){
      syntax_error(10);
      return;
    }
    while(ISIDCHAR(*s))
      s++;
    name=cnvstr(name,s-name);
    s=skip(s);
  }else{
    s++;
    name=s;
    while(*s&&*s!='\"')
      s++;
    name=cnvstr(name,s-name);
    s=skip(s+1);
  }
  if(*s==','){
    s=skip(s+1);
    attr=s;
    if(*s!='\"')
      syntax_error(7);
    else
      s++;
    attr=s;
    while(*s&&*s!='\"')
      s++;    
    attr=cnvstr(attr,s-attr);
    s=skip(s+1);
  }else
    attr="";
  new_section(name,attr,1);
  switch_section(name,attr);
  eol(s);
}

static char *string(char *s,dblock **result)
{
  dblock *db=new_dblock();
  taddr size;
  char *p,esc;
  if(*s!='\"')
    ierror(0);
  else
    s++;
  p=s;
  size=0;
  while(*s&&*s!='\"'){
    if(*s=='\\')
      s=escape(s,&esc);
    else
      s++;
    size++;
  }
  db->size=size;
  db->data=mymalloc(db->size);
  s=p;
  p=db->data;
  while(*s&&*s!='\"'){
    if(*s=='\\')
      s=escape(s,p++);
    else
      *p++=*s++;
  }
  *result=db;
  if(*s=!'\"')
    syntax_error(7);
  else
    s++;
  return s;
}

static void handle_data(char *s,int size,int noalign,int zeroterm)
{
  expr *tree;
  dblock *db;
  do{
    char *opstart=s;
    operand *op;
    if(size==8&&*s=='\"'){
      s=string(s,&db);
      add_atom(0,new_data_atom(db,1));
    }else{
      op=new_operand();
      s=skip_operand(s);
      if(!parse_operand(opstart,s-opstart,op,DATA_OPERAND(size))){
        syntax_error(8);
      }else{
        atom *a=new_datadef_atom(size,op);
        if(noalign)
          a->align=1;
        add_atom(0,a);
      }
    }
    s=skip(s);
    if(*s==','){
      s=skip(s+1);
    }else if(*s)
      syntax_error(9);
  }while(*s);
  if(zeroterm){
    if(size!=8)
      ierror(0);
    db=new_dblock();
    db->size=1;
    db->data=mymalloc(1);
    *db->data=0;
    add_atom(0,new_data_atom(db,1));
  }    
  eol(s);
}

static void handle_global(char *s)
{
  symbol *sym;
  char *name;
  name=s;
  if(!(name=parse_identifier(&s))){
    syntax_error(10);
    return;
  }
  sym=new_import(name);
  sym->flags|=EXPORT;
  myfree(name);
  eol(s);
}

static void handle_align(char *s)
{
  taddr align=parse_constexpr(&s);
  atom *a=new_space_atom(number_expr(0),1,0);
  a->align=1<<align;
  add_atom(0,a);
  eol(s);
}

static void handle_space(char *s)
{
  expr *space,*fill=0;
  space=parse_expr_tmplab(&s);
  s=skip(s);
  if(*s==','){
    s=skip(s+1);
    fill=parse_expr_tmplab(&s);
  }
  add_atom(0,new_space_atom(space,1,fill));
  eol(s);  
}

static void handle_8bit(char *s){ handle_data(s,8,0,0); }
static void handle_16bit(char *s){ handle_data(s,16,0,0); }
static void handle_32bit(char *s){ handle_data(s,32,0,0); }
static void handle_16bit_noalign(char *s){ handle_data(s,16,1,0); }
static void handle_32bit_noalign(char *s){ handle_data(s,32,1,0); }
static void handle_string(char *s){ handle_data(s,8,0,1); }
static void handle_texts(char *s){ handle_section(".text,\"acrx4\"");eol(s);}
static void handle_datas(char *s){ handle_section(".data,\"adrw4\"");eol(s);}
static void handle_sdatas(char *s){ handle_section(".sdata,\"adrw4\"");eol(s);}
static void handle_sdata2s(char *s){ handle_section(".sdata2,\"adr4\"");eol(s);}
static void handle_rodatas(char *s){ handle_section(".rodata,\"adr4\"");eol(s);}
static void handle_bsss(char *s){ handle_section(".bss,\"aurw4\"");eol(s);}
static void handle_sbsss(char *s){ handle_section(".bss,\"aurw4\"");eol(s);}

struct {
  char *name;
  void (*func)(char *);
} directives[]={
  "section",handle_section,
  "dcb",handle_8bit,
  "dc.b",handle_8bit,
  "dcw",handle_16bit_noalign,
  "dc.w",handle_16bit_noalign,
  "dcl",handle_32bit_noalign,
  "dc.l",handle_32bit_noalign,
  "public",handle_global,
  "xdef",handle_global,
  "xref",handle_global,  
  "text",handle_texts,
  "cseg",handle_texts,
  "data",handle_datas,
  "bss",handle_bsss,  
  "align",handle_align,
  "ds.b",handle_space,
};

int dir_cnt=sizeof(directives)/sizeof(directives[0]);

/* Handles assembly directives; returns non-zero if the line
   was a directive. */
static int handle_directive(char *line)
{
  char *s,*name;
  hashdata data;
  s=skip(line);
  if(!ISIDSTART(*s))
    return 0;
  name=s;
  while(ISIDCHAR(*s))
    s++;
  if(!find_namelen(dirhash,name,s-name,&data))
    return 0;
  directives[data.idx].func(skip(s));
  return 1;
}

/* Very simple, very incomplete. */
void parse(void)
{
  char *s,*line,*inst,*ext[MAX_QUALIFIERS?MAX_QUALIFIERS:1],*op[MAX_OPERANDS];
  int inst_len,ext_len[MAX_QUALIFIERS?MAX_QUALIFIERS:1],op_len[MAX_OPERANDS];
  int i,ext_cnt,op_cnt,par_cnt;
  instruction *ip;
  while(line=read_next_line()){
    s=line;

    if(isalnum((unsigned char)*s)){
      /* Handle labels at beginning of line */
      char *labname,*equ;
      symbol *label;
      while(*s&&!isspace((unsigned char)*s)&&*s!=':')
        s++;
      labname=cnvstr(line,s-line);
      s=skip(s+1);
      if(!strncmp(s,"equ",3)&&isspace((unsigned char)s[3])){
        s=skip(s+3);
        label=new_abs(labname,parse_expr(&s));
      }else{
        label=new_labsym(0,labname);
        add_atom(0,new_label_atom(label));
      }
      free(labname);
    }

    s=parse_cpu_special(s);

    if(handle_directive(s))
      continue;

    /* skip spaces */
    s=skip(s);
    if(!*s)
      continue;

    /* read mnemonic name */
    inst=s;
    if(!ISIDSTART(*s)){
      syntax_error(10);
      continue;
    }
#if MAX_QUALIFIERS==0
    while(*s&&!isspace((unsigned char)*s))
      s++;
#else
    while(*s&&*s!='.'&&!isspace((unsigned char)*s))
      s++;
#endif
    inst_len=s-inst;

    /* read qualifiers */
    ext_cnt=0;
    while(*s=='.'&&ext_cnt<MAX_QUALIFIERS){
      s++;
      ext[ext_cnt]=s;
      while(*s&&*s!='.'&&!isspace((unsigned char)*s))
        s++;
      ext_len[ext_cnt]=s-ext[ext_cnt];
      if(ext_len[ext_cnt]<=0)
        syntax_error(1);
      else
        ext_cnt++;
    }

    if(!isspace((unsigned char)*s)) syntax_error(2);
    
    /* read operands, terminated by comma (unless in parentheses)  */
    s=skip(s);
    op_cnt=0;
    while(*s&&op_cnt<MAX_OPERANDS){
      op[op_cnt]=s;
      s=skip_operand(s);
      op_len[op_cnt]=s-op[op_cnt];
      if(op_len[op_cnt]<=0)
        syntax_error(5);
      else
        op_cnt++;
      s=skip(s);
      if(*s!=','){
        break;
      }else{
        s=skip(s+1);
      }
    }      
    s=skip(s);
    if(*s!=0) syntax_error(6);
    ip=new_inst(inst,inst_len,op_cnt,op,op_len);
#if MAX_QUALIFIERS>0
    if(ip){
      for(i=0;i<ext_cnt;i++)
        ip->qualifiers[i]=cnvstr(ext[i],ext_len[i]);
      for(;i<MAX_QUALIFIERS;i++)
        ip->qualifiers[i]=0;
    }
#endif
    if(ip){
      add_atom(0,new_inst_atom(ip));
    }else
      ;
  }
}

char *const_prefix(char *s,int *base)
{
  if(isdigit((unsigned char)*s)){
    *base=10;
    return s;
  }
  if(*s=='$'){
    *base=16;
    return s+1;
  }
  if(*s=='%'){
    *base=2;
    return s+1;
  }
  *base=0;
  return s;
}

char *const_suffix(char *start,char *end)
{
  return end;
}

char *get_local_label(char **start)
{
  return NULL;
}

int init_syntax()
{
  size_t i;
  hashdata data;
  dirhash=new_hashtable(0x200); /*FIXME: */
  for(i=0;i<dir_cnt;i++){
    data.idx=i;
    add_hashentry(dirhash,directives[i].name,data);
  }
  
  return 1;
}

int syntax_args(char *p)
{
  return 0;
}
