/*{{{}}}*/
/*{{{  #includes*/
#ifdef VARARGS
#  include <varargs.h>
#else
#  include <stdarg.h>
#endif
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#define CODEHELP_C

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

/*{{{  variables*/
public boolean new_vars_enabled=False;
private int dv_list[int_dummy_vars];
private boolean dv_init=False;
/*}}}  */

/*{{{  mstrcpy*/
public unsigned char *mstrcpy(unsigned char const * const s)
{
  unsigned char *x;

  x=kbd_malloc(ustrlen(s)+1);
  ustrcpy(x,s);

  return(x);
}
/*}}}  */
/*{{{  m_error_po*/
public void m_error_po(char const * const m)
{
  error_po();
  fputs(m,stderr);
}
/*}}}  */
/*{{{  m_exit*/
#ifdef VARARGS
   public void m_exit(format,va_alist)char const * const format;va_dcl
#else
   public void m_exit(char const * const format,...)
#endif
{ va_list ap;

  error_po();
  /*{{{  start arglist handling*/
#  ifdef VARARGS
     va_start(ap);
#  else
     va_start(ap,format);
#  endif
  /*}}}  */
  vfprintf(stderr,(char*)format,ap);
  /*{{{  end arglist handling*/
  va_end(ap);
  /*}}}  */
  kbd_exit(1);
}
/*}}}  */
/*{{{  begin_parse*/
public void begin_parse(void)
{
  if (get_single_token()!=BEGIN)
     m_exit(M_WANTBEGIN);
}
/*}}}  */
/*{{{  end_parse*/
public void end_parse(char const * const er)
{
  if (get_single_token()!=END)
     m_exit(er);
}
/*}}}  */
/*{{{  name_parse*/
public void name_parse
 ( unsigned char *str,
   char const * const er,
   boolean const op
 )
{
  switch (get_full_token())
   { case OPCODE:
     case OPERATION:
     case VARIABLE:
        if (op)
         {
     case NAME:
           ustrcpy(str,tk_string);
           return;
         }
     default:
        m_exit(er);
   }
}
/*}}}  */
/*{{{  name_to_var transform token NAME to token VARIABLE, if possible*/
public tokens name_to_var(tokens token, int size, boolean newvars)
{ if (token==NAME && newvars)
   { creat_var(tk_string,size,-1);
     for (tk_var=var_list;tk_var;tk_var=tk_var->next)
        if (!ustrcmp(tk_var->var_name,tk_string))
           break;
     token=VARIABLE;
   }
  if (token==VARIABLE && size && size!=tk_var->size)
     m_exit(M_NOVAR);
  return(token);
}
/*}}}  */
/*{{{  put variable-adress*/
public TOKEN *put_var(TOKEN *buff)
{

  switch (name_to_var(get_full_token(),1,new_vars_enabled))
   { case VARIABLE:
        *buff++ = (TOKEN) tk_var->no;
        if (tk_var->size==1)
           break;
     default:
        m_exit(M_NOVAR);
   }
  return(buff);
}
/*}}}  */
/*{{{  dummy_var*/
public int dummy_var(int no)
{
  /*{{{  maybe init it*/
  if (!dv_init)
   { int i;
     for (i=0;i<int_dummy_vars;dv_list[i++]= -1);
     dv_init=True;
   }
  /*}}}  */
  /*{{{  no dummy-vars available -> error*/
  if (no>=int_dummy_vars)
     m_exit(M_NOVAS);
  /*}}}  */
  if (dv_list[no]==-1)
   { unsigned char s[10];

     sprintf((char*)s,"(dummy%d)",no);
     return(dv_list[no]=creat_var(s,1,-1));
   }
  else
     return(dv_list[no]);
}
/*}}}  */
/*{{{  macro-argument variable handling*/
private int ocl_arg_vars[ocl_max_args];
private int ocl_loc_vars[ocl_max_args];
private int ocl_args_used=0;

/*{{{  def_ocl_args*/
public void def_ocl_args(int no)
{ while (ocl_args_used<no)
   { unsigned char vn[26];

     sprintf((char*)vn,"(arg%d)",ocl_args_used);
     ocl_arg_vars[ocl_args_used]=creat_var(vn,1,-1);
     sprintf((char*)vn,"(loc%d)",ocl_args_used);
     ocl_loc_vars[ocl_args_used]=creat_var(vn,1,-1);
     ocl_args_used++;
   }
}
/*}}}  */
/*{{{  ocl_arg_var*/
public int ocl_arg_var(int no)
{
  if (no>=ocl_args_used)
     m_exit(M_CRASH);
  return(ocl_arg_vars[no]);
}
/*}}}  */
/*{{{  ocl_loc_var*/
public int ocl_loc_var(int no)
{
  if (no>=ocl_args_used)
     m_exit(M_CRASH);
  return(ocl_loc_vars[no]);
}
/*}}}  */
/*}}}  */
/*{{{  isdummy*/
public boolean isdummy(int x)
{ int i;

  if (dv_init) for (i=0;i<int_dummy_vars;i++) if (dv_list[i]==x) return(True);
  return(False);
}
/*}}}  */
/*{{{  create a jump-command*/
public TOKEN *generate_jmp(TOKEN j, TOKEN *ad, TOKEN *dest)
{
  switch (j)
   { case M_JMP:
     case M_CALL:
     case M_JMP_TRUE:
     case M_JMP_FALSE:
        break;
     default:
        m_exit(M_NOJUMP);
   }
  *ad++ = j;
  *ad= dest-(ad+1);

  return(ad+1);
}
/*}}}  */
/*{{{  parse_term*/
/*{{{  destination save register handling*/
typedef struct { int old;int save;boolean changed;boolean chg_use;TOKEN *save_start; } DEST_DAT;

/*{{{  start_dest_handle*/
private void start_dest_handle(DEST_DAT *t,int dest,TOKEN **buff,int *dummy_base)
{
  t->save_start= *buff;
  t->old=dest;
  t->save=dummy_var((*dummy_base)++);
  t->changed=t->chg_use=!opt_mac;
  *(*buff)++=M_PUSH_INT;
  *(*buff)++=(TOKEN)dest;
  *(*buff)++=M_POP_INT;
  *(*buff)++=(TOKEN)t->save;
}
/*}}}  */
/*{{{  end_dest_handle*/
private void end_dest_handle(DEST_DAT *t)
{ if (!t->chg_use)
   /*{{{  saved target variable not used, so clear these statements*/
   { *(t->save_start)++=O_NOP;
     *(t->save_start)++=O_NOP;
     *(t->save_start)++=O_NOP;
     *(t->save_start)++=O_NOP;
   }
   /*}}}  */
}
/*}}}  */
/*{{{  set_dest*/
private void set_dest(TOKEN *buff,int x,DEST_DAT *t)
{ *buff=x;
  if (x==t->old) t->changed=True;
}
/*}}}  */
/*{{{  set_source*/
private void set_source(TOKEN *buff,int x,DEST_DAT *t)
{ if (x==t->old && t->changed)
   { *buff=t->save;
     t->chg_use=True;
   }
  else
     *buff=x;
}
/*}}}  */
/*}}}  */

/*{{{  recursion for term-parsing*/
public TOKEN *rpt(tokens token,TOKEN *buff,int dest,int dummy,boolean *ret_used,DEST_DAT *t)
{ tokens op=name_to_var(token,0,new_vars_enabled);

  check_length(buff);
  switch (op)
   { case VARIABLE:
      /*{{{  maybe PUSH new POP old*/
      { int x=tk_var->no;

        if (tk_var->size==1)
         /*{{{  handle int var*/
         { if (x!=dest)
            { *buff++=M_PUSH_INT;
              set_source(buff++,x,t);
              *buff++=M_POP_INT;
              set_dest(buff++,dest,t);
            }
         }
         /*}}}  */
        else
         /*{{{  handle int array*/
         { begin_parse();
           op=get_full_token();
           switch(op)
            { case NUMBER:
                 *buff++=M_PUSH_INT;
                 set_source(buff++,x+tk_val,t);
                 *buff++=M_POP_INT;
                 set_dest(buff++,dest,t);
                 break;
              case VARIABLE:
                 if (tk_var->size==1)
                  { *buff++=M_PUSH_INT_X;
                    *buff++=(TOKEN)x;
                    *buff++=(TOKEN)tk_var->no;
                    *buff++=M_POP_INT;
                    set_dest(buff++,dest,t);
                    break;
                  }
              default:
                 if (!(buff=rpt(op,buff,dummy_var(dummy),dummy+1,ret_used,t))) return(0);
                 *buff++=M_PUSH_INT_X;
                 *buff++=(TOKEN)x;
                 *buff++=(TOKEN)dummy_var(dummy);
                 *buff++=M_POP_INT;
                 set_dest(buff++,dest,t);
                 break;
            }
           end_parse(M_WANTEND);
         }
         /*}}}  */
        break;
      }
      /*}}}  */
     case EVAL:
      /*{{{  get value from called macro*/
      { TOKEN *call;
        TOKEN *jmp;

        /*{{{  push dummy vars*/
        { int i;

          for (i=0;i<dummy;i++)
           { *buff++=M_PUSH_INT;
             *buff++=(TOKEN)dummy_var(i);
           }
        }
        /*}}}  */
        /*{{{  call the macro*/
        buff=generate_jmp(M_CALL,call=buff,buff);
        buff=generate_jmp(M_JMP,jmp=buff,buff);
        generate_jmp(M_CALL,call,buff);
        if (!(buff=parse_macro(buff,0,ret_used))) return(0);
        *buff++=M_END_MACRO;
        generate_jmp(M_JMP,jmp,buff);
        /*}}}  */
        /*{{{  pop dummy vars*/
        { int i;

          for (i=dummy-1;i>=0;i--)
           { *buff++=M_POP_INT;
             *buff++=(TOKEN)dummy_var(i);
           }
        }
        /*}}}  */
        /*{{{  get return value*/
        *buff++=M_PUSH_INT;
        *buff++=(TOKEN)return_var;
        *buff++=M_POP_INT;
        set_dest(buff++,dest,t);
        /*}}}  */
        break;
      }
      /*}}}  */
     case PRE:
      /*{{{  do the action first*/
      {
        /*{{{  push dummy vars*/
        { int i;

          for (i=0;i<dummy;i++)
           { *buff++=M_PUSH_INT;
             *buff++=(TOKEN)dummy_var(i);
           }
        }
        /*}}}  */
        if (!(buff=parse_macro(buff,0,ret_used))) return(0);
        /*{{{  pop dummy vars*/
        { int i;

          for (i=dummy-1;i>=0;i--)
           { *buff++=M_POP_INT;
             *buff++=(TOKEN)dummy_var(i);
           }
        }
        /*}}}  */
        if (!(buff=rpt(get_full_token(),buff,dest,dummy,ret_used,t))) return(0);
        break;
      }
      /*}}}  */
     case COUNTER:
      /*{{{  convert boolean to int*/
      { TOKEN *x;

        /*{{{  set-counter dest 0*/
        *buff++ = M_SET_COUNTER;
        set_dest(buff++,dest,t);
        *buff++ = 0;
        /*}}}  */
        if (!(buff=parse_cond(buff,dummy,ret_used))) return(0);
        buff=generate_jmp(M_JMP_FALSE,x=buff,buff);
        /*{{{  set-counter dest 1*/
        *buff++ = M_SET_COUNTER;
        *buff++ = (TOKEN) dest;
        *buff++ = 1;
        /*}}}  */
        generate_jmp(M_JMP_FALSE,x,buff);
        break;
      }
      /*}}}  */
     case OPERATION:
      /*{{{  operation*/
        *buff++ = M_SET_COUNTER;
        set_dest(buff++,dest,t);
        if (tk_operation->place<0)
         { if (tk_operation->length>1) goto error_code;
           *buff++ = tk_operation->ops[0];
         }
        else
           *buff++ = O_CALL_FIX+tk_operation->place+1;
        break;
      /*}}}  */
     case OPCODE:
      /*{{{  opcode*/
      { *buff++ = M_SET_COUNTER;
        set_dest(buff++,dest,t);
        *buff++ = tk_key->num;
        break;
      }
      /*}}}  */
     case DECODE_BUFFER:
      /*{{{  decode a position to a buffer-position*/
      { int v1;
        int v2;
        tokens tok;

        if ((tok=name_to_var(get_full_token(),1,new_vars_enabled))==VARIABLE)
           v1=tk_var->no;
        else if (!(buff=rpt(tok,buff,v1=dummy_var(dummy),dummy+1,ret_used,t)))
           return(0);
        if ((tok=name_to_var(get_full_token(),1,new_vars_enabled))==VARIABLE)
           v2=tk_var->no;
        else if (!(buff=rpt(tok,buff,v2=dummy_var(dummy+1),dummy+2,ret_used,t)))
           return(0);
        *buff++=M_GET_BUFFER;
        *buff++=(TOKEN)v1;
        *buff++=(TOKEN)v2;
        *buff++=M_POP_INT;
        *buff++=(TOKEN)dest;
        break;
      }
      /*}}}  */
     case FOLD_SL:
     case FOLD_EL:
      /*{{{  get length of fold-comment-string*/
        *buff++=M_FOLD_DATA;
        *buff++=(op==FOLD_SL)?0:2;
        set_dest(buff++,dest,t);
        break;
      /*}}}  */
     case FOLD_M1:
     case FOLD_M2:
     case FOLD_M3:
     case FOLD_M4:
     case FOLD_S:
     case FOLD_E:
      /*{{{  get data from fold comment*/
        if (!(buff=rpt(get_full_token(),buff,dummy_var(dummy),dummy+1,ret_used,t))) return(0);
        *buff++=M_FOLD_DATA;
        *buff++=(op==FOLD_M1)? 4:
                (op==FOLD_M2)? 5:
                (op==FOLD_M3)? 6:
                (op==FOLD_M4)? 7:
                (op==FOLD_S) ? 1:
                               3;
        *buff++=(TOKEN)dummy_var(dummy);
        *buff++=M_POP_INT;
        set_dest(buff++,dest,t);
        break;
      /*}}}  */
     case FILETYPE:
     case STORE_CHAR:
     case READ_REPEAT:
     case SET_ENTER:
     case SET_S_ENTER:
     case STORE_MARK:
     case STORE_X:
     case STORE_Y:
      /*{{{  get data from origami*/
        *buff++ = (token==FILETYPE)    ? M_FILETYP :
                  (token==SET_ENTER)   ? M_ENTERED :
                  (token==STORE_MARK)  ? M_STORE_MARK :
                  (token==STORE_X)     ? M_POS_TO_COUNTER :
                  (token==STORE_Y)     ? M_STORE_LINE_NO :
                  (token==STORE_CHAR)  ? M_STORE_C :
                  (token==READ_REPEAT) ? M_READ_REPEAT :
                                         M_S_ENTERED;
        set_dest(buff++,dest,t);
        break;
      /*}}}  */
     case NUMBER:
      /*{{{  M_SET_COUNTER dest value*/
      { *buff++=M_SET_COUNTER;
        set_dest(buff++,dest,t);
        *buff++=(TOKEN)tk_val;
        break;
      }
      /*}}}  */
     case ADD:
     case MINUS:
     case MULT:
     case DIV:
     case MOD:
      /*{{{  list of arguments*/
      { token=get_full_token();
        if (token!=BEGIN)
         /*{{{  compiling the unary -*/
         { if (op!=MINUS)
              m_exit(M_WANTBEGIN);
           if (!(buff=rpt(token,buff,dest,dummy,ret_used,t)))
              return(0);
           *buff++=M_INV_COUNTER;
           set_dest(buff++,dest,t);
           break;
         }
         /*}}}  */
        if (!(buff=rpt(get_full_token(),buff,dest,dummy,ret_used,t))) return(0);
        token=name_to_var(get_full_token(),0,new_vars_enabled);
        do
         /*{{{  code one argument from list*/
         { check_length(buff);
           switch (token)
            { case COMMA: break;
              case VARIABLE:
                 if (tk_var->size==1)
                  /*{{{  code: op var var*/
                  { int x=tk_var->no;

                    /*{{{  code the operation*/
                    switch (op)
                     { /* MINUS uses the code for ADD too!!          */
                       case MINUS: *buff++=M_INV_COUNTER;*buff++=(TOKEN)dest;
                       case ADD:   *buff++=M_SUM_COUNTER;break;
                       case MULT:  *buff++=M_MULT;break;
                       case DIV:   *buff++=M_DIV;break;
                       case MOD:   *buff++=M_MOD;break;
                       default:    m_exit(M_CRASH);
                     }
                    /*}}}  */
                    /*{{{  code arguments*/
                    set_dest(buff++,dest,t);
                    set_source(buff++,x,t);
                    /*}}}  */
                    if (op==MINUS) { *buff++=M_INV_COUNTER;*buff++=(TOKEN)dest; }
                    break;
                  }
                  /*}}}  */
              default:
                 if (!(buff=rpt(token,buff,dummy_var(dummy),dummy+1,ret_used,t)))
                    return(0);
                 /*{{{  code the operation*/
                 switch (op)
                  { /* MINUS uses the code for ADD too!!          */
                    case MINUS:
                       *buff++=M_INV_COUNTER;*buff++=(TOKEN)dummy_var(dummy);
                    case ADD:
                       *buff++=M_SUM_COUNTER;break;
                    case MULT:
                       *buff++=M_MULT;break;
                    case DIV:
                       *buff++=M_DIV;break;
                    case MOD:
                       *buff++=M_MOD;break;
                    default:
                       m_exit(M_CRASH);
                  }
                 /*}}}  */
                 *buff++=dest;
                 *buff++=dummy_var(dummy);
            }
         }
         /*}}}  */
        while ((token=name_to_var(get_full_token(),1,new_vars_enabled))!=END);
        break;
      }
      /*}}}  */
     case MACRO:
      /*{{{  code a one Token macro as it's value'*/
        if (ustrlen(tk_string)==1)
         /*{{{  code the integer value of a given character*/
         { *buff++=M_SET_COUNTER;
           *buff++=(TOKEN)dest;
           *buff++=(TOKEN)tk_string[0];
           break;
         }
         /*}}}  */
        else
           goto error_code;
      /*}}}  */
     case NAME:
      /*{{{  code C-x statements*/
        if (tk_string[0]=='C' && tk_string[1]=='-' && tk_string[3]=='\0')
         { *buff++=M_SET_COUNTER;
           *buff++=(TOKEN)dest;
           *buff++=ctrl_c[tk_string[2]];
           break;
         }
        else
           goto error_code;
      /*}}}  */
     default:
     error_code:
        m_exit(M_NOVAR);
   }
  return(buff);
}
/*}}}  */

public TOKEN *parse_term(tokens token,TOKEN *buff,int dest,int dummy_base,boolean *ret_used)
{ DEST_DAT t;

  start_dest_handle(&t,dest,&buff,&dummy_base);
  buff=rpt(token,buff,dest,dummy_base+1,ret_used,&t);
  end_dest_handle(&t);

  return(buff);
}
/*}}}  */
/*{{{  get_message*/
public TOKEN *get_message(TOKEN *buff)
{
  tokens read=get_full_token();
  TOKEN *x=tk_macro;

  if (read!=MACRO)
   /*{{{  complex prompt ( ... ))*/
   { if (read!=BEGIN)
        m_exit(M_NOMESSAGE);
     while ((read=get_full_token())!=END)
      /*{{{  handle message entry*/
        switch (read)
         { case MACRO:
            /*{{{  copy the string*/
            { TOKEN *x;

              for (x=tk_macro;*x;*buff++ = *x++);
              break;
            }
            /*}}}  */
           case NUMBER:
            /*{{{  code the character directly*/
              *buff++=tk_val;
              break;
            /*}}}  */
           case HISTORY:
            /*{{{  put history code*/
            *buff++=M_GET_HISTORY;
            if ((*buff++=(TOKEN)name_to_history(get_full_token(),False))==(TOKEN)unknown_history)
               m_exit(M_NOHISTORY);
            break;
            /*}}}  */
           case COUNTER:
            /*{{{  put var*/
              *buff++ = M_INT_STRING;
              if (!(buff=put_var(buff))) return(0);
              break;
            /*}}}  */
           case NAME:
            /*{{{  search for the message*/
            { int msg_no;

              if ((msg_no=name2msg(tk_string))>=0)
               { *buff++=(-msg_no);
                 break;
               }
            }
            /*}}}  */
           default:
              m_exit(M_WANTEND);
         }
      /*}}}  */
   }
   /*}}}  */
  else
     while (*x) *buff++ = *x++;
  return(buff);
}
/*}}}  */
