#define SCANNER
/*{{{  #includes*/
#include <ctype.h>
#include <termcap.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include <local/bool.h>

#include "../common/keys.h"
#include "keybind.h"
/*}}}  */

/*{{{  type nod to avoid*/
typedef struct NOD {
  char *name;
  struct NOD *next;
} nod;
/*}}}  */
/*{{{  variable declarations*/
#define f_s_depth 4
private nod *already_read=NULL;
private int f_s_ptr=0,
            input=EOF,
            current_line_no=0,
            inp_stack[f_s_depth],
            po_stack[f_s_depth];
private FILE *file_stack[f_s_depth];
private char *name_stack[f_s_depth];
public char   tk_char;
public ALIAS  *tk_alias;
public KEYNAME *tk_key;
public OP     *tk_operation;
public VARS   *tk_var;
public char   tk_string[name_lg];
public TOKEN  tk_macro[macro_lg];
public ALIAS  *func_key[64];
public OP     *new_op[64];
public VARS   *vars[128];
public char   *source;
/*}}}  */

/*{{{  list all keywords/operations*/
#ifdef STD_C
public void sc_list_keys(void)
#else
public void sc_list_keys()
#endif
{
  /*{{{  all operations known in origami*/
  { KEYNAME *key=bindings;
    while (key->num != O_NOP) printf("%s\n",(key++)->name);
  }
  /*}}}  */
  /*{{{  all keywords known from keybind*/
  { keywords *key=keytab;
    while (key->value != ERROR) printf("%s\n",(key++)->name);
  }
  /*}}}  */
}
/*}}}  */
/*{{{  create an operation entry*/
#ifdef STD_C
public void creat_op(char *name,bool mode,int lg,TOKEN *code,int place)
#else
public creat_op(name,mode,lg,code,place)
char *name;int lg,place; TOKEN *code; bool mode;
#endif

/*{{{  comment*/
/* arguments are:
  name - name of the macro/function
  mode - the code is defined (forward)
  lg   - number of tokens
  code - the tokenlist
  place- the number of the used origami-macro-buffer or 0 for defmac
*/
/*}}}  */
{
  OP **act=new_op;

  while (*act!=NULL) act++;
  *act=(OP*)malloc(sizeof(OP));
  (*act)->op_name=(char*)malloc(strlen(name)+1);
  strcpy((*act)->op_name,name);
  (*act)->length=lg;
  (*act)->def=mode;
  (*act)->place=place;
  (*act)->ops=malloc((1+lg)*sizeof(int));
  memcpy((*act)->ops,code,lg*sizeof(int));
  /*{{{  verbose-handling*/
  if (verbose) {
    if (place)
      fprintf(stderr,F_BELONGS,name);
    else
      fprintf(stderr,F_OPERATION,name,lg);
  }
  /*}}}  */
}
/*}}}  */
/*{{{  create an variable entry*/
#ifdef STD_C
public int creat_var(char *name)
#else
public int creat_var(name) char *name;
#endif
{
  VARS **act=vars;

  while (*act!=NULL) act++;
  *act=(VARS*)malloc(sizeof(VARS));
  (*act)->var_name=(char*)malloc(strlen(name)+1);
  strcpy((*act)->var_name,name);
  return((*act)->no=int_no++);
}
/*}}}  */
/*{{{  create an alias entry*/
#ifdef STD_C
public void creat_alias(char *name, char *code, int lg)
#else
public creat_alias(name,code,lg) char *name,*code; int lg;
#endif
{
  ALIAS **act= func_key;

  while (*act!=NULL) act++;
  *act=(ALIAS*)malloc(sizeof(ALIAS));
  (*act)->key_name=(char*)malloc(strlen(name)+1);
  strcpy((*act)->key_name,name);
  (*act)->code=(char*)malloc(lg+1);
  memcpy((*act)->code,code,lg);
  (*act)->length=lg;
}
/*}}}  */
/*{{{  init alias list*/
/*{{{  void init_default_alias()*/
#ifdef STD_C
private void init_default_alias(void)
#else
private void init_default_alias()
#endif
{
  creat_alias("esc","\033",1);
  creat_alias("tab","\t",1);
  creat_alias("return","\r",1);
  os_default_alias();
}
/*}}}  */
/*{{{  void init_termcap_alias()*/
os_termcap_data

#ifdef STD_C
  private void init_termcap_alias(void)
#else
  private void init_termcap_alias()
#endif
{
  os_termcap_alias();
}
/*}}}  */
/*{{{  void init_alias()*/
public void init_alias()
{
  init_default_alias();
  init_termcap_alias();
}
/*}}}  */
/*}}}  */
/*{{{  error_po()*/
#ifdef STD_C
public void error_po(void)
#else
public void error_po()
#endif
{
  int i=0;

  for (;i<=f_s_ptr;i++)
    fprintf(stderr,F_ERRORPO,
                   (i?name_stack[i]:source),
                   (i==f_s_ptr?current_line_no:po_stack[i]));
}
/*}}}  */
/*{{{  read a character from input, count lines*/
#ifdef STD_C
public int read_in(void)
#else
public int read_in()
#endif
{
  input=getc(in);
  if (input=='\n') current_line_no++;
  return(input);
}
/*}}}  */
/*{{{  get a token from input file*/
#ifdef STD_C
private tokens get_single_token(bool code)
#else
private tokens get_single_token(code) bool code;
#endif
{
  /*{{{  skip comments, spaces  ...*/
  while (input=='\n' || input==' ' || input==';' || input==begin_std_c || input==end_std_c)
  {
    if (input==';' || input==begin_std_c || input==end_std_c)
    {
      while (input!='\n' && input!=EOF) read_in();
    }
    read_in();
  }
  /*}}}  */

  switch (input) {
    /*{{{  single char tokens*/
    case EOF: {read_in();return(ENDFILE);}
    case '(': {read_in();return(BEGIN);}
    case ')': {read_in();return(END);}
    case ',': {read_in();return(COMMA);}
    /*}}}  */
    default :
    if (code) {
      /*{{{  code-parser*/
      switch (input)
      {
        /*{{{  Meta-handling*/
        case 'M':
        {
          read_in();
          if (input!='-')
          {
            error_po();
            fprintf(stderr,M_NOMETA);
            return(ERROR);
          }
          read_in();
          return(META);
        }
        /*}}}  */
        /*{{{  Control-handling*/
        case 'C':
        {
          /*{{{  read -" and error-check*/
          read_in();
          if (input!='-')
          {
            error_po();
            fprintf(stderr,M_NOCNTRL);
            return(ERROR);
          }
          /*}}}  */
          read_in();
          if (input=='?') tk_char=127;
          else tk_char=toupper(input)-'A'+1;
          read_in();
          return(CHAR);
        }
        /*}}}  */
        /*{{{  Char-handling*/
        case '"':
        {
          tk_char=read_in();
          if (input<' ' || input>127)
          {
            error_po();
            fprintf(stderr,M_NOCHAR);
            return(ERROR);
          }
          read_in();
          return(CHAR);
          break;
        }
        /*}}}  */
        /*{{{  Alias-handling*/
        case '$':
        {
          char *s=tk_string;
          ALIAS **a=func_key;
        
          read_in();
          /*{{{  read aliasname*/
          while (input!=' ' && input!='\n'
          && input!='=' && input!=EOF && input!=')')
          {
            *s++=input;
            read_in();
          }
          *s='\0';
          /*}}}  */
          while (*a!=NULL &&
          strcmp((*a)->key_name,tk_string)) a++;
          if (*a!=NULL) { tk_alias = *a; return(DOLLAR); }
          error_po();
          fprintf(stderr,M_NOALIAS);
          return(ERROR);
        }
        /*}}}  */
        /*{{{  default=error*/
        default :
        {
          error_po();
          fprintf(stderr,M_NOCODE);
          return(ERROR);
        }
        /*}}}  */
      }
      /*}}}  */
    } else {
      /*{{{  macro-parser*/
      switch (input) {
        /*{{{  string-handling*/
        case '"':
        {
          TOKEN *t=tk_macro;
        
          read_in();
          do {
            /*{{{  Test auf < ' ' oder EOF*/
            if (input<' ') {
              error_po();
              fprintf(stderr,M_FALSEEND);
              return(ERROR);
            }
            /*}}}  */
            *t++ = (TOKEN) input;
            read_in();
          } while (   input!=' ' && input!='\n'
                   && input!=')' && input!=EOF && input!='"');
          *t= (TOKEN) 0;
          return(MACRO);
          break;
        }
        /*}}}  */
        /*{{{  opcode or keyword handling*/
        default : {
          char *s=tk_string;
          keywords *key=keytab;
          OP **a=new_op;
          VARS **v=vars;
        
          while (input!=' ' && input!='\n' && input!=')' && input!=','
                 && input!='=' && input!=EOF && input!='(')
          {
            *s++=input;read_in();
          }
          *s='\0';
          /*{{{  search as keyword*/
          while (key->name != NULL && strcmp(key->name,tk_string)) key++;
          if (key->value!=ERROR) return(key->value);
          /*}}}  */
          /*{{{  search as opcode*/
          tk_key=bindings;
          while(tk_key->num!=O_NOP && strcmp(tk_key->name,tk_string)) tk_key++;
          if (tk_key->num!=O_NOP) return(OPCODE);
          /*}}}  */
          /*{{{  search as operation*/
          while (*a!=NULL &&
          strcmp((*a)->op_name,tk_string)) a++;
          if (*a!=NULL) { tk_operation = *a; return(OPERATION); }
          /*}}}  */
          /*{{{  search as variable*/
          while (*v!=NULL &&
          strcmp((*v)->var_name,tk_string)) v++;
          if (*v!=NULL) { tk_var = *v; return(VARIABLE); }
          /*}}}  */
          return(NAME);
        }
        /*}}}  */
      }
      /*}}}  */
    }
  }
  return(ERROR);
}
/*}}}  */
/*{{{  get a token*/
#ifdef STD_C
public tokens get_token(bool code)
#else
public tokens get_token(code) bool code;
#endif
{
  tokens t=get_single_token(code);

  switch (t) {
    /*{{{  NODUP*/
    case NODUP: {
      nod *x=malloc(sizeof(nod));
    
      /*{{{  enough memory for storing the current file as read ?*/
      if (x==NULL) {
        fprintf(stderr,M_NOMEMORY);
        return(ERROR);
      }
      /*}}}  */
      /*{{{  store current filename*/
      x->next=already_read;
      already_read=x;
      if ((x->name=malloc(strlen(f_s_ptr?name_stack[f_s_ptr]:source)+1))==NULL) {
        fprintf(stderr,M_NOMEMORY);
        return(ERROR);
      }
      strcpy(x->name,f_s_ptr?name_stack[f_s_ptr]:source);
      if (verbose) fprintf(stderr,F_NODUP,x->name);
      /*}}}  */
      return(get_token(code));
      break;
    }
    /*}}}  */
    /*{{{  ENDFILE -> goto upper level, if any*/
    case ENDFILE:
    if (f_s_ptr) {
      fclose(in);
      free(name_stack[f_s_ptr]);
      in=file_stack[--f_s_ptr];
      current_line_no=po_stack[f_s_ptr];
      input=inp_stack[f_s_ptr];
      return(get_token(code));
    } else
      return(ENDFILE);
    /*}}}  */
    /*{{{  INCLUDE -> go to lower level*/
    case INCLUDE: {
      nod *x=already_read;
    
      /*{{{  check if correct include*/
      if (get_single_token(FALSE)!=NAME) {
        error_po();
        fprintf(stderr,M_NOFNAME);
        return(ERROR);
      }
      if (f_s_ptr==f_s_depth) {
        fprintf(stderr,M_DEPTH);
        return(ERROR);
      }
      /*}}}  */
      /*{{{  check for multiple read of the file*/
      while (x!=NULL)
        if (strcmp(x->name,tk_string))
          x=x->next;
        else {
          if (verbose) fprintf(stderr,F_DUP,tk_string);
          return(get_token(code));
        }
      /*}}}  */
      /*{{{  store file-info*/
      po_stack[f_s_ptr]=current_line_no;
      inp_stack[f_s_ptr]=input;
      file_stack[f_s_ptr++]=in;
      name_stack[f_s_ptr]=malloc(strlen(tk_string)+1);
      strcpy(name_stack[f_s_ptr],tk_string);
      /*}}}  */
      /*{{{  try to open*/
      if ((in=fopen(tk_string,"r"))==NULL) {
        fprintf(stderr,F_INCLUDE,tk_string);
        f_s_ptr--;
        return(ERROR);
      }
      /*}}}  */
      /*{{{  start reading*/
      read_in();
      current_line_no=0;
      return(get_token(code));
      /*}}}  */
    }
    /*}}}  */
    default: return(t);
  }
}
/*}}}  */
