/*{{{}}}*/
/*{{{  #includes*/
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#define PARSEASM_C
#define CODE_LG_ARRAY

#include "keybind.h"
#include <h/codelg.h>
#include <lib/ori_add_lib.h>
/*}}}  */

/*{{{  typedef ocl_label*/
typedef struct ocl_label
 { char *name;
   TOKEN *val;
   struct ocl_label *next;
 } ocl_label;
/*}}}  */
/*{{{  variables*/
ocl_label *used_labels;
ocl_label *def_labels;
TOKEN *start_buff;
/*}}}  */

/*{{{  create_label*/
private void create_label(ocl_label **labels,TOKEN *buff,unsigned char *str)
{ ocl_label *x;

  x=kbd_malloc(sizeof(ocl_label)+ustrlen(str)+1);
  x->next= *labels;
  *labels=x;
  x->val=buff;
  strcpy(x->name=(char*)(x+1),(char*)str);
}
/*}}}  */
/*{{{  get_ind_arg*/
private boolean get_ind_arg(TOKEN **buff_p)
{ tokens token;
  int fak;
  int add=0;

  /*{{{  maybe get offset*/
  while ((token=get_full_token())==ADD)
   { int add_fak=1;

     switch (get_full_token())
      { case MINUS:
           if (get_full_token()!=NUMBER)
        default:
              goto error_int_arg;
           add_fak= -1;
        case NUMBER:
           add+=add_fak*tk_val;
           break;

      }
   }
  /*}}}  */
  fak=1;
  switch (token)
   { case MINUS:
        if (get_full_token()!=NUMBER)
           goto error_int_arg;
        fak= -1;
     case NUMBER:
        *(*buff_p)++=tk_val*fak+add;
        return(False);
     case NAME:
      { int x;

        if ((x=name2msg(tk_string))>=0)
         { *(*buff_p)++=x*fak+add;
           return(False);
         }
        goto error_int_arg;
      }
     case BEGIN:
      { unsigned char hide_var[NAME_LG];

        /*{{{  get name for hidden variable*/
        if (get_full_token()!=NAME) goto error_int_arg;
        hide_var[0]='(';
        ustrcpy(hide_var+1,tk_string);
        if (get_full_token()!=END) goto error_int_arg;
        strcat((char*)hide_var,")");
        ustrcpy(tk_string,hide_var);
        /*}}}  */
        /*{{{  look for this hidden variable*/
        { VARS_LIST const *x;

          for (x=var_list;;)
             if (!x)
                goto error_int_arg;
             else if (x->var_name && !ustrcmp(x->var_name,hide_var))
                break;
             else
                x=x->next;
          tk_var=x;
        }
        /*}}}  */
      }
     case VARIABLE:
        *(*buff_p)++=tk_var->no+add;
        return(False);
     default:
     error_int_arg:
        m_exit(M_NOVAR);
   }
}
/*}}}  */
/*{{{  get_char_arg*/
private TOKEN *get_char_arg(TOKEN *buff)
{
  switch (get_full_token())
   { case NUMBER:
        *buff++=tk_val;
        return(buff);
     case MACRO:
        if (ustrlen(tk_string)!=1)
           goto char_arg_error;
        *buff++=tk_string[0];
        return(buff);
     default:
     char_arg_error:
        m_exit(M_NOADDCOUNTER);
   }
}
/*}}}  */
/*{{{  get_add_arg*/
private TOKEN *get_add_arg(TOKEN *buff)
{ tokens token;
  int add=0;

  /*{{{  maybe get offset*/
  while ((token=get_full_token())==ADD)
   { int fak=1;

     switch (get_full_token())
      { case MINUS:
           if (get_full_token()!=NUMBER)
        default:
              goto ad_arg_error;
           fak= -1;
        case NUMBER:
           add+=fak*tk_val;
           break;

      }

   }
  /*}}}  */
  switch (token)
   {
     /*{{{  NUMBER use offset directly*/
     case NUMBER:
        *buff++=tk_val;
        return(buff);
     /*}}}  */
     /*{{{  NAME   use label, maybe mark for backpatching*/
     case NAME:
      { ocl_label *x;

        for (x=def_labels;x && strcmp(x->name,(char*)tk_string);x=x->next);
        if (x)
         { *buff=x->val-buff-1+add;
         }
        else
         { create_label(&used_labels,buff,tk_string);
           *buff=start_buff-buff+1+add;
         }
        return(buff+1);
      }
     /*}}}  */
     default:
     ad_arg_error:
        m_exit(M_NOADDCOUNTER);
   }
}
/*}}}  */
/*{{{  parse_asm*/
public TOKEN *parse_asm(TOKEN *buff,unsigned char *name)
{ tokens token;

  used_labels=0;
  def_labels=0;
  start_buff=buff;
  if (verbose_level>0) fprintf(stderr,M_ASM_1,name);
  begin_parse();
  do
   /*{{{  handle a statement*/
   { check_length(buff);
     token=get_full_token();
     switch (token)
      {
        /*{{{  insert opcode*/
        case OPCODE:
           *buff++=tk_key->num;
           continue;
        /*}}}  */
        /*{{{  single TOKEN macro*/
        case OPERATION:
           if (tk_operation->length!=1) goto asm_error;
           *buff++ = *(tk_operation->ops);
           continue;
        /*}}}  */
        /*{{{  eval, call parse_macro as subroutine call*/
        case EVAL:
         { TOKEN *call;
           TOKEN *jmp;
           boolean foo;

           buff=generate_jmp(M_CALL,call=buff,buff);
           buff=generate_jmp(M_JMP,jmp=buff,buff);
           generate_jmp(M_CALL,call,buff);
           if (!(buff=opt_parse_macro(call=buff,0,&foo,False,!demand,True))) return(0);
           *buff++=M_END_MACRO;
           generate_jmp(M_JMP,jmp,buff);
           break;
        }
        /*}}}  */
        /*{{{  number*/
        case NUMBER:
           *buff++=tk_val;
           break;
        /*}}}  */
        /*{{{  string*/
        case MACRO:
         { TOKEN *x;

           for (x=tk_macro;*x;*buff++ = *x++);
           break;
         }
        /*}}}  */
        /*{{{  NAME -> ocl-asm-statement or label*/
        case NAME:
         { KEYNAME const *a;

           if ((a=name2asm(tk_string)))
            /*{{{  asm-command*/
            { *buff++=a->num;
              switch (cmd_type[a->num-O_NOP])
               { case COM_II:
                    if (get_ind_arg(&buff)) return(0);
                 case COM_I:
                    if (get_ind_arg(&buff)) return(0);
                 case COM:
                    break;
                 case COM_C:
                    if (!(buff=get_char_arg(buff))) return(0);
                    break;
                 case COM_A:
                    if (!(buff=get_add_arg(buff))) return(0);
                    break;
                 case COM_IIP:
                    if (get_ind_arg(&buff)) return(0);
                 case COM_IP:
                    if (get_ind_arg(&buff)) return(0);
                 case COM_P:
                    if (!(buff=get_message(buff))) return(0);
                    break;
               }
              continue;
            }
            /*}}}  */
           else if (tk_string[0]=='.')
            /*{{{  label*/
            { ocl_label *x,*y;

              /*{{{  already defined?*/
              for
               ( x=def_labels;
                 x && strcmp(x->name,(char*)tk_string+1);
                 x=x->next
               );
              if (x)
                 if (x->val!=buff)
                  /*{{{  error, duplicate definition*/
                    m_exit(M_DUPDEF);
                  /*}}}  */
                 else
                  /*{{{  same adress used, so ok*/
                    break;
                  /*}}}  */
              /*}}}  */
              /*{{{  backpatch*/
              for (y=0,x=used_labels;x;)
                 if (strcmp(x->name,(char*)tk_string+1))
                  { y=x;
                    x=x->next;
                  }
                 else
                  { *(x->val)+=buff-start_buff-2;
                    if (y)
                     { y->next=x->next;
                       kbd_free(x);
                       x=y->next;
                     }
                    else
                     { used_labels=x->next;
                       kbd_free(x);
                       x=used_labels;
                     }
                  }
              /*}}}  */
              /*{{{  define new*/
              create_label(&def_labels,buff,tk_string+1);
              /*}}}  */
              break;
            }
            /*}}}  */
           else
              goto asm_error;
         }
        /*}}}  */
        /*{{{  ) -> ready*/
        case END:
           break;
        /*}}}  */
        /*{{{  default: error*/
        default:
        asm_error:
           m_exit(M_NOMSTRING);
        /*}}}  */
      }
   }
   /*}}}  */
  while (token!=END);
  if (used_labels)
   { error_po();
     fprintf(stderr,F_ASM_LM,used_labels->name);
     return(0);
   }
  while (def_labels)
   { ocl_label *x;

     x=def_labels;
     def_labels=x->next;
     kbd_free(x);
   }
  return(buff);
}
/*}}}  */
