/* ---------------------------------------------------------------------- */
/*                   Copyright (C) 1991 by Natrlich!                     */
/*                      This file is copyrighted!                         */
/*                Refer to the documentation for details.                 */
/* ---------------------------------------------------------------------- */
#define DEBUG     0
#define TOKDEBUG  0            /* following set 4 Atari 8MHz */
#define FINPUT_IO 1            /* yields 15% speed increase  */
#define QINPUT_IO 1            /* yields 20% speed increase  */
#define USEINLINE 0
#define QSTRING   0            /* 1 == doesn't do much       */
#define QWHITE    1
#define QCOMMENT  1
#define QIDENT    1
#define QIDENT2   0            /* 1 == worsens performance ! */
#define QLABEL    0            /* 1 == doesn't do anything   */
#define QMACRO    0            /* 1 == same or worse         */
#define QDIRECT   0            /* 1 == doesn't do anything   */

/* --------------------------------------------------------- */
/*    SHIT!! FLEX can't digest characters >= 128             */
/*    Now I have to write my own lexer...                    */
/* --------------------------------------------------------- */
#include "defines.h"
#include "nasm.h"
#include "y_tab.h"
#include <stdio.h>
#if OS == TOS
#include <osbind.h>
#endif
#include <setjmp.h>

#include "debug.h"
#include "code.h"
#include NMALLOC_H
#if VERSION && FINPUT_IO
extern buffer huge   *bp;
# include "inputfst.h"
#endif

#define LINESTART       0
#define INSTRUCTION     1
#define AFTERINSTR      2
#define AFTERELSE       3
#define AFTEROPTION     4
#define AFTERFLOAT      5
#define AFTERINCLUDE    6
#define COMMENT         7

#define E_GARBAGE    garbage        /* historic */
#define E_LABELFORM  labelform
#define E_HEXLEN     hexlen
#define E_INTSIZE    intsize

static char
   igarbage[] =  "Garbage in the instruction field",
   garbage[]  =  "Totally uncomprehendable garbage",
   labelform[] = "Label starts off with wrong character",
   hexlen[] =  "Hex value exceeds 16 Bit (not that good on a 64K machine)",
   intsize[] = "Integer value >= 65536, how would that fit into 2 bytes ?",
   mpara_incomplete[] = 
                "Incomplete macro parameter (form %[$]<[0-9]+|(<label>))";

/* --------------------------------------------------------- */
/* The ultra-clever (har har) way of deciding whether we are */
/* facing something usable as an identifier character.       */
/* There is a method to this madness:                        */
/*   Bit7 . Bit6 . Bit5 . Bit4 . Bit3 . Bit2 . Bit1 . Bit0   */
/*   -----+------+------+------+------+------+------+-----   */
/*   Lower|      |      | Foo  | A-Z  |Float | Hex  | No     */
/*   -----+------+------+------+------+------+------+-----   */
/*    $80   $40    $20    $10    $08    $04    $02    $01    */
/*                                                           */
/*    A question remains, are we losing with this table ??   */
/* --------------------------------------------------------- */
#define is_ident( c)    (is_what[ (c)])
#define is_lower( c)    ((signed char) is_what[ (c)] < 0)
#define is_hex( c)      (is_what[ (c)] & M_HEX)
#define is_digit( c)    (is_what[ (c)] == DIGIT)
#define is_letter( c)   (is_what[ (c)] & M_LETTER)
#define is_float( c)    (is_what[ (c)] & M_FLOAT)

#define tis_ident( c)   (p_table[ (c)])
#define tis_lower( c)   ((signed char) p_table[ (c)] < 0)
#define tis_hex( c)     (p_table[ (c)] & M_HEX)
#define tis_digit( c)   (p_table[ (c)] == DIGIT)
#define tis_letter( c)  (p_table[ (c)] & M_LETTER)
#define tis_float( c)   (p_table[ (c)] & M_FLOAT)

#define M_LOWER   0x80
#define M_FOO     0x10
#define M_LETTER  0x08
#define M_FLOAT   0x04
#define M_HEX     0x02
#define M_DIGIT   0x01

#define DIGIT     (M_DIGIT  | M_HEX | M_FLOAT)
#define L         (M_LETTER | M_LOWER)           /* lower  */
#define A          M_LETTER                      /* upper  */
#define H         (M_LETTER | M_HEX)             /* up&hex */
#define E         (M_LETTER | M_HEX | M_FLOAT)
#define P                             M_FLOAT
#define X          M_FOO
#define N          DIGIT


byte is_what[] =                          /* xlat:  is' watt !?!? */
{
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $00 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $10 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,P,0, /* $20 */
   N,N,N,N,  N,N,N,N,  N,N,X,0,  0,0,0,X, /* $30 */

   X,H,H,H,  H,E,H,A,  A,A,A,A,  A,A,A,A, /* $40 */
   A,A,A,A,  A,A,A,A,  A,A,A,0,  0,0,0,X, /* $50 */
   0,L,L,L,  L,L,L,L,  L,L,L,L,  L,L,L,L, /* $60 */
   L,L,L,L,  L,L,L,L,  L,L,L,0,  0,0,0,0, /* $70 */

   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $80 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $90 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $A0 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $B0 */

   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $C0 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $D0 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $E0 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0  /* $F0 */
};

#undef F
#undef N
#undef L
#undef A
#undef H
#undef E
#undef P
#undef X


#define is_white( c)    (white[ (c)])
#define skip_white(c)   while( white[ c]) c = uinput()
#define tis_white( c)   (p_table[ (c)])
#define tskip_white(c)  while( p_table[ c]) c = uinput()

static char white[] =         /* ain't no saying whether this actually */
{                             /* helps improve the speed               */
   0,0,0,0,  0,0,0,0,  0,1,0,0,  0,1,0,0, /* $00 */
   0,0,0,0,  0,0,0,0,  0,0,1,0,  0,0,0,0, /* $10 */   /* $1A for MSDOS */
   1,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $20 */   /* whatever that */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $30 */   /* might be      */

   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $40 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $50 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $60 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $70 */

   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $80 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $90 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $A0 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $B0 */

   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $C0 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $D0 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $E0 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0  /* $F0 */
};

#define A   0x01   /* 'X'  */
#define B   0x02   /* 'Y'  */

#define C   0x03   /* '#'  */

#define D   0x04   /* '('  */
#define E   0x05   /* ')'  */
#define F   0x06   /* ','  */
#define G   0x07   /* '='  */
#define H   0x08   /* '+'  */
#define I   0x09   /* '-'  */
#define J   0x0A   /* '!'  */
#define K   0x0B   /* '&'  */
#define L   0x0C   /* '*'  */
#define M   0x0D   /* '/'  */
#define N   0x0E   /* '\\' */
#define O   0x0F   /* '^'  */
#define P   0x10   /* '['  */
#define Q   0x11   /* ']'  */
#define IREGS  0x02

static byte af1_toks[] =
{
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $00 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $10 */
   0,J,0,C,  0,0,K,0,  D,E,L,H,  F,I,0,M, /* $20 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,G,0,0, /* $30 */

   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $40 */
   0,0,0,0,  0,0,0,0,  A,B,0,P,  N,Q,O,0, /* $50 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $60 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $70 */

   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $80 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $90 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $A0 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $B0 */

   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $C0 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $D0 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $E0 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0  /* $F0 */
};

static byte af2_toks[] =
{
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $00 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $10 */
   0,J,0,0,  0,0,K,0,  D,E,L,H,  F,I,0,M, /* $20 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,G,0,0, /* $30 */

   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $40 */
   0,0,0,0,  0,0,0,0,  0,0,0,P,  N,Q,O,0, /* $50 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $60 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $70 */

   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $80 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $90 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $A0 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $B0 */

   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $C0 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $D0 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0, /* $E0 */
   0,0,0,0,  0,0,0,0,  0,0,0,0,  0,0,0,0  /* $F0 */
};
#undef A
#undef B
#undef C
#undef D
#undef E
#undef F
#undef G
#undef H
#undef I
#undef J
#undef K
#undef L
#undef M
#undef N
#undef O
#undef P
#undef Q


#if DEBUG        /* Bugs ? Yeah SURE we gottem.. All sizes and shapes! */
#define dreturn( val)   \
{                       \
   int   foo = (val);   \
   prtname( foo);       \
   LEAVE();             \
   return( foo);        \
}

#define xreturn( val)   \
{                       \
   int   foo = (val);   \
                        \
   LEAVE();             \
   return( foo);        \
}

#define fdreturn( val)  \
{                       \
   int   foo = (val);   \
                        \
   c = uinput();        \
   prtname( foo);       \
   LEAVE();             \
   return( foo);        \
}

#else
#define dreturn   return
#define xreturn   return
#define fdreturn( val)  \
{                       \
   c = uinput();        \
   return( val);        \
}
#endif

#define comparators()                                 \
   if( c_ == '<')                                     \
      if( (c_ = uinput()) == '>')                     \
      {                                               \
         c = c_;                                      \
         fdreturn( T_NEQ);                            \
      }                                               \
      else                                            \
         if( (c = c_) == '=')                         \
         {                                            \
            fdreturn( T_LEQ);                         \
         }                                            \
         else                                         \
            dreturn( '<');                            \
                                                      \
   if( c_ == '>')                                     \
   {                                                  \
      if( (c = uinput()) == '=')                      \
      {                                               \
         fdreturn( T_GEQ);                            \
      }                                               \
      dreturn( '>');                                  \
   }

extern lword yylval;
extern       freshflag;

static char  miss_char[] = "Missing character after \' (quote)",
             yytext[1024];
int          state = LINESTART,
             wasstrlen,
#if __NSTDC__
             (*gettoken)( void);
#else
             (*gettoken)();
#endif

byte         c;
#if VERSION
static byte  tmp;
#endif
jmp_buf      syn_panic;

#if DEBUG
int          syndebug;
#endif


int   nilexer()
{
   yylval = 0;
   return(0);
}


int   yylex()
{
   ENTER("yylex");
#if TOKDEBUG
   {
      int   i = (*gettoken)();

      prtname( i);
      xreturn( i);
   }
#else
# if OS == TOS && WITHNOISY
   {
      extern int  noisytok, _in_macro;

      if( noisytok)
      {
         static unsigned char fx[] =
         {
            2, 0, 3, 0, 9, 8, 7, 0xFD, 0xFF, 10,
            7, 0xFF, 0xFF, 0
         };
         int          tok = (*gettoken)();


         fx[1] = tok & 0x3F;
         fx[3] = _in_macro;
         Dosound( fx);
         xreturn( tok);
      }
   }
# endif
   xreturn((*gettoken)());
#endif
}


int   lexer()
{
   register byte  c_ = c,
                  *p_table = (byte *) white;

   ENTER( "lexer");
   if( freshflag)                /* Have we set syn_panic already ??    */
   {
      freshflag = 0;             /* No clr flag                         */
#if DEBUG
      SAVESTATE( &syndebug);
#endif
      if( setjmp( syn_panic))    /* Do the setjmp ONCE                  */
      {
#if DEBUG
         SETBACK( syndebug);
#endif
         MESS("\007\t\tsyntax panic occurred");
         while( c_ != '\n')       /* If a longjmp() happens: ignore line */
            c_ = uinput();
        state = LINESTART;       /* reset state */
      }
   }


   if( tis_white( c_))             /* TOS files have white before LF */
   {
#if VERSION && FINPUT_IO && QINPUT_IO && QWHITE
      register byte huge   *pt  = bp->p;
      register byte        *tab = _uptable;
      register lword       rem  = bp->remain;

      while( tis_white( c_ = quinput( rem, pt, tab)));
      if( bp)
      {
         bp->remain = rem;
         bp->p      = pt;
      }
#else
      while( tis_white( c_ = uinput()));
#endif
      if( state == LINESTART)
         state = INSTRUCTION;
   }

   if( c_ == ';' || state == COMMENT)
   {
#if VERSION && FINPUT_IO && QINPUT_IO && QCOMMENT
      register byte huge   *pt = bp->p;
      register byte       *tab = _uptable;
      register lword       rem = bp->remain;

      while( (c_ = quinput( rem, pt, tab)) != '\n');
      if( bp)
      {
         bp->remain = rem;
         bp->p      = pt;
      }
#else
      while( (c_ = uinput()) != '\n');
#endif
   }

   if( c_ == '\n')
   {
      state = LINESTART;
      c = c_;
      fdreturn( T_EOL);
   }

#if USEINLINE
   switch( state)
   {
      case LINESTART    :
         IMESS("[LINESTART] c == %d", (lword) c_, 2);
         state = INSTRUCTION;
         if( is_ident( c = c_))
         {
            xreturn( label_suckin());
         }
         else
            junk_suckin( E_GARBAGE);


      case INSTRUCTION  :
         IMESS("[INSTRUCTION c=='%c']", (lword) c_, 1);
         if( c_ == '.')
         {
            c = c_;
            state = AFTERELSE;
            xreturn( directive_suckin());
         }
         if( is_ident( c = c_))
         {
            state = AFTERINSTR;
            xreturn( macro_suckin()); /* macros and instructions too */
         }
         state = AFTERELSE;
         if( c_ == '=')
            fdreturn( c_);
         if( c_ == '*')
         {
            while( tis_white( c_ = uinput()));
            if( c_ == '=')
               fdreturn( T_ORG);
            c_ = '*';
         }
         yytext[0] = c = c_;
         yytext[1] = 0;
         lsyntax( igarbage);


      case AFTERINSTR   :
         MESS("[AFTERINSTR]");
         switch( c_)
         {
            case '$' :  xreturn( hex_suckin());
            case '.' :  xreturn( directive_suckin());
            case '%' :  xreturn( para_suckin());
            case '\'':
            {
               if( tis_white( c = input()))
                  lsyntax(miss_char);
               yylval = (lword) c;
               fdreturn( T_CHAR);
            }
         }
         if( is_digit( c = c_))
            xreturn( int_suckin());
         {
            register int   a;

            if( a = af1_toks[ c_])
            {
               if( is_ident( c = uinput()) && a <= IREGS)
                  xreturn( ident2_suckin( c_));
               dreturn( c_);
            }
         }
         if( is_ident( c_))
            xreturn( ident_suckin());
         comparators();
         junk_suckin( garbage);


      case AFTERELSE    :
         MESS("[AFTERELSE]");
         switch( c_)
         {
            case '%' :  xreturn( para_suckin());
            case '"' :  xreturn( string_suckin());
            case '$' :  xreturn( hex_suckin());
            case '.' :  xreturn( directive_suckin());
            case '\'':  if( tiswhite( c = input()))
                           lsyntax(miss_char);
                        yylval = (lword) c;
                        fdreturn( T_CHAR);
         }
         if( is_digit( c = c_))
            xreturn( int_suckin());
         if( af2_toks[ c_])
            fdreturn( c_);
         if( is_ident(c_))
            xreturn( ident_suckin());
         comparators();
         junk_suckin( garbage);


      case AFTEROPTION  :
         MESS("[AFTEROPTION]");
         if( c_ == ',')
            fdreturn( c_);
         c = c_;
         xreturn( option_suckin());


      case AFTERINCLUDE :
         MESS("[AFTERINCLUDE]");
         if( c_ == '#')
            fdreturn( c_);
         c = c_;
         xreturn( file_suckin());

      case AFTERFLOAT   :
         MESS("[AFTERFLOAT]");
         if(  c_ == ',')
            fdreturn( c_);
         c = c_;
         xreturn( float_suckin());
   }
   nierror("problems with the lexer state");
}
#else
   switch( state)
   {
      case LINESTART   :
         IMESS("[LINESTART] c == %d", (lword) c, 2);
         state = INSTRUCTION;
         if( is_ident(c = c_))
         {
            xreturn( label_suckin());
         }
         else
            junk_suckin( E_GARBAGE);

      case INSTRUCTION :
         IMESS("[INSTRUCTION c=='%c']", (lword) c_, 1);
         if( (c = c_) == '.')
         {
            state = AFTERELSE;
            xreturn( directive_suckin());
         }
         if( is_ident( c))
         {
            state = AFTERINSTR;
            xreturn( macro_suckin()); /* macros and instructions too */
         }
         MESS("**HIT THIS***");
         state = AFTERELSE;
         if( c_ == '=')
            fdreturn( c_);
         if( c_ == '*')
         {
            while( tis_white( c_ = uinput()));
            if( c_ == '=')
               fdreturn( T_ORG);
            c_ = '*';
         }
         yytext[0] = c = c_;
         yytext[1] = 0;
         lsyntax( igarbage);


      case AFTERINSTR  :
      {
         register int   a;

         MESS("[AFTERINSTR]");
         if( a = af1_toks[ c_])
         {
            if( is_ident( c = uinput()) && a <= IREGS)
               xreturn( ident2_suckin( c_));
            dreturn( c_);
         }
         comparators();
         c = c_;
         xreturn( get_itoken());
      }

      case AFTERELSE   :
      {
         MESS("[AFTERELSE]");
         if( af2_toks[ c_])
            fdreturn( c_);
         comparators();
         c = c_;
         xreturn( get_etoken());
      }

      case AFTEROPTION:
         MESS("[AFTEROPTION]");
         if( c_ == ',')
            fdreturn( c_);
         c = c_;
         xreturn( option_suckin());

      case AFTERFLOAT:
         MESS("[AFTERFLOAT]");
         if(  c_ == ',')
            fdreturn( c_);
         c = c_;
         xreturn( float_suckin());

      case AFTERINCLUDE:
         MESS("[AFTERINCLUDE]");
         if( c_ == '#')
            fdreturn( c_);
         c = c_;
         xreturn( file_suckin());
   }
   nierror("problems with the lexer state");
#if __NSTDC__ && VERY_PRETTY
   return(0);
#endif
}

/* ------------------------------------------------------------- */
/*           Stands for: Get after instruction token.            */
/* ------------------------------------------------------------- */
get_itoken()
{
   ENTER("get_itoken");
   switch( c)
   {
      case '$' :  xreturn( hex_suckin());
      case '.' :  xreturn( directive_suckin());
      case '%' :  xreturn( para_suckin());
      case '\'':  if( is_white( c = input()))
                     lsyntax(miss_char);
                  yylval = (lword) c;
                  fdreturn( T_CHAR);
   }
   if( is_digit(c))
      xreturn( int_suckin());
   if( is_ident( c))
      xreturn( ident_suckin());
   junk_suckin( garbage);
#if __NSTDC__ && VERY_PRETTY
   return(0);
#endif
}

/* ------------------------------------------------------------- */
/* Stands for: Get after macro call token.                       */
/* ------------------------------------------------------------- */
get_etoken()
{
   ENTER("get_etoken");
   IMESS("c = '%c'", (lword) c, 1);
   switch( c)
   {
      case '%' :  xreturn( para_suckin());
      case '"' :  xreturn( string_suckin());
      case '$' :  xreturn( hex_suckin());
      case '.' :  xreturn( directive_suckin());
      case '\'':  if( is_white( c = input()))
                     lsyntax(miss_char);
                  yylval = (lword) c;
                  fdreturn( T_CHAR);
   }
   if( is_digit(c))
      xreturn( int_suckin());
   if( is_ident(c))
      xreturn( ident_suckin());
   junk_suckin( garbage);
#if __NSTDC__ && VERY_PRETTY
   return(0);
#endif
}
#endif

/* ------------------------------------------------------------- */
/* Stands for: Get after macro call token.                       */
/* ------------------------------------------------------------- */
para_suckin()
{
   ENTER("para_suckin");

   switch( c = uinput())
   {
      case '(' :
         MESS("way1a");
         while( is_white( c = uinput()));
         if( is_ident( c))
         {
            MESS("way1b");
            ident_suckin();
            skip_white( c);
            if( c == ')')
               fdreturn( T_MLPARA)
         }
         break;

      case '$' :
         MESS("way2a");
         if( is_digit( c = uinput()))
         {
            int_suckin();
            dreturn( T_MSPARA);
         }
         if( c == '(')
         {
            MESS("way2b");
            while( is_white( c = uinput()));
            if( is_ident( c))
            {
               MESS("way2c");
               ident_suckin();
               skip_white( c);
               if( c == ')')
                  fdreturn( T_MLSPARA)
            }
         }
         break;

      default :
         IMESS("way3 c = '%c'", (lword) c, 1);
         if( is_digit( c))
         {
            int_suckin();
            dreturn( T_MPARA);
         }
   }
   lsyntax( mpara_incomplete);
}


ident_suckin()
{
   register byte  *p = (byte *) yytext,
                  c_ = c,
                  *p_table = is_what;
   register int   i = 1;

   ENTER("ident_suckin");
#if VERSION && FINPUT_IO && QINPUT_IO && QIDENT
   {
      register byte huge   *pt  = bp->p;
      register byte        *tab = _uptable;
      register lword       rem  = bp->remain;

      do
      {
         *p++ = c_;
         i++;
      }
      while( tis_ident( c_ = quinput( rem, pt, tab)));
      if( bp)
      {
         bp->remain = rem;
         bp->p      = pt;
      }
   }
#else
   do
   {
      *p++ = c_;                             /* convert to upper & save */
      i++;
   }
   while( tis_ident( c_ = uinput()));
#endif

   *p = 0;
   c = c_;
   if( state == AFTERINSTR)
      if( ! yytext[1] && yytext[0] == 'A')
         xreturn( T_ACCU);
   yy_txt2val( i);
   dreturn( T_IDENT);
}


ident2_suckin( extra)
char  extra;
{
   register byte  *p = (byte *) yytext,
                  c_ = c,
                  *p_table = is_what;
   register int   i = 2;

   ENTER("ident2_suckin");
   *p++ = extra;

#if VERSION && FINPUT_IO && QINPUT_IO && QIDENT2
   {
      register byte huge   *pt  = bp->p;
      register byte        *tab = _uptable;
      register lword       rem  = bp->remain;

      do
      {
         *p++ = c_;
         i++;
      }
      while( tis_ident( c_ = quinput( rem, pt, tab)));
      if( bp)
      {
         bp->remain = rem;
         bp->p      = pt;
      }
   }
#else
   do
   {
      *p++ = c_;                             /* convert to upper & save */
      i++;
   }
   while( tis_ident(c_ = uinput()));
#endif
   *p = 0;
   c = c_;
   if( state == AFTERINSTR)
      if( ! yytext[1] && yytext[0] == 'A')
         xreturn( T_ACCU);
   yy_txt2val( i);
   dreturn( T_IDENT);
}


label_suckin()
{
   register char  *p = yytext,
                  c_ = c,
                  *p_table = (char *) is_what;
   register int   i = 1;

   ENTER("label_suckin");
   if( c_ == '.' || tis_digit( c_))
   {
      c = c_;
      lsyntax( E_LABELFORM);
   }

#if VERSION && FINPUT_IO && QINPUT_IO && QLABEL
   {
      register byte huge   *pt  = bp->p;
      register byte        *tab = _uptable;
      register lword       rem  = bp->remain;

      while( tis_ident( c_))
      {
         *p++ = c_;
         i++;
         c_   = quinput( rem, pt, tab);
      }
      if( bp)
      {
         bp->remain = rem;
         bp->p      = pt;
      }
   }
#else
   while( tis_ident(c_))                     /* Suck in macro call or   */
   {                                         /* instruction             */
      *p++ = c_;                             /* convert to upper & save */
      i++;
      c_   = uinput();                       /* get fresh input         */
   }
#endif

   if( p[-1] == ':' && i > 2)       /* kill trailing : for labels */
   {
      --i;
      --p;
   }
   *p = 0;
   c = c_;
   yy_txt2val( i);
   dreturn( T_LABEL);
}


void junk_suckin( error)
char  *error;
{
   register byte  *p = (byte *) yytext;

   ENTER("junk_suckin");
   while( c > ' ' && c != ';' && c != ',') /* Errors.. so we don't care */
   {                                        /* about speed anyway.       */
      *p++ = c;
      c    = uinput();
   }
   *p = 0;
   ALEAVE();
   lsyntax( error);
}


string_suckin()
{
   register byte       *p = (byte *) yytext + 2;
   register unsigned   i = 0;

   ENTER("string_suckin");
#if VERSION && FINPUT_IO && QINPUT_IO && QSTRING
   {
      register byte huge   *pt = bp->p;
      register lword       rem = bp->remain;

      while( (*p++ = qinput( rem, pt)) != '\"')
         i++;
      if( bp)
      {
         bp->remain = rem;
         bp->p      = pt;
      }
   }
#else
   while( (*p++ = input()) != '\"')
      i++;
#endif
   p[-1] = 0;
   dbyte( yytext, wasstrlen = i);
   i += 3;           /* 2 for i, 1 for strlen */
   yy_txt2val( i);
   fdreturn( T_STRING);
}

char  *make_string( s)
register char  *s;
{
   register char  *q;
   register int   i;
   char           *t;
   
   ENTER("make_string");
   {
      register char  *src = s;
      for( src = s, i = 0; *src++; i++);
   }
   q  = t = str_alloc( i + 3);
   dbyte( q, i);
   q += 2;
   
   while( i > 4)
   {
      *q++ = *s++;
      *q++ = *s++;
      *q++ = *s++;
      *q++ = *s++;
      i -= 4;
   }
   do
      *q++ = *s++;
   while( --i);
   
   LEAVE();
   return( t);
}

file_suckin()
{
   register byte  *p = (byte *) yytext,
                   c_ = (c >= 'A' && c <= 'Z') ? c + 32 : c;
   register int   i  = 1;

   ENTER("file_suckin");
   do
   {
      *p++ = c_;
      i++;
      if ( (c_ = input()) == '/' || c_ == '>')
         c_ = '\\';
   }
   while( is_ident( c_) || c_ == '\\');
   *p = 0;
   c = c_;
   yy_txt2val( i);
   dreturn( T_FILE);
}

float_suckin()
{
   char     *p    = yytext;
   int      eflag = 1,
            pflag = 1,
            jflag = 0,
            kflag = 1;

   ENTER("float_suckin");
   while( (is_float(c) || ((c == '+' || c == '-') && (jflag || kflag)))
            && (c != '.' || (pflag && eflag))
            && (c != 'E' || eflag))
   {
      if( (c == '+') || (c == '-'))
         kflag = 0;
      if( c == '.')
         pflag = 0;
      jflag = 0;
      if( c == 'E')
      {
         pflag = eflag = 0;
         jflag = 1;
      }
      *p++ = c;
      c = uinput();
   }
   *p = 0;
   dropfloat( yytext);
   dreturn( T_XFLOAT);
}

/* ---------------------------------------------------------- */
/*       To make this more generic, it would be good to       */
/*       parametrize (sp?) this macro a bit more              */
/* ---------------------------------------------------------- */
#define nextdigit( _recurs)                                             \
   if( tis_digit( c_ = uinput()))                                       \
   {                                                                    \
      tmp  = val + val;                                                 \
      val  = (tmp << 2) + tmp;                                          \
      val += c_ - '0';                                                  \
      _recurs                                                           \
   }

#define finaldigit()                                                    \
   if( tis_digit( c_ = uinput()))                                       \
   {                                                                    \
      if( val >= 6553 && (val > 6553 || c_ >= '6')) /* This looks */    \
      {                                             /* worse than */    \
         c = c_;                                    /* it is      */    \
synerr:                                                                 \
         lsyntax( E_INTSIZE);                                           \
      }                                                                 \
      tmp  = val + val;                                                 \
      val  = (tmp << 2) + tmp;                                          \
      yylval = (lword) (val + c_ - '0');                                \
      if( tis_digit( c = uinput()))                                     \
         goto synerr;                                                   \
      IMESS("Int number is %ld", yylval, 4);                            \
      xreturn( T_NUMBER);                                               \
   }

/* ---------------------------------------------------------- */
/* Suck in a int value. Most probably fuinputting the number  */
/* into yytext and converting it later with nextdigit would   */
/* be faster. remains to be tested.                           */
/* ---------------------------------------------------------- */
int_suckin()
{
   register word     val = c - '0', tmp;
   register byte     c_,
                    *p_table = is_what;

   ENTER("int_suckin");
   nextdigit( nextdigit( nextdigit( finaldigit()))); /* can you dig it ? */
   c = c_;
   yylval = (lword) val;
   IMESS("Int number is %ld", yylval, 4);
   xreturn( T_NUMBER);
}

/* ---------------------------------------------------------- */
/* Suck in a hex value, we assume that, the '$' has been      */
/* removed before.                                            */
/* ---------------------------------------------------------- */
hex_suckin()
{
   register byte        *p  = (byte *) yytext,
                        c_,
                        *p_table = is_what;
   register unsigned    val = 0,
                        i   = 4;

   ENTER("hex_suckin");
   do
      if( tis_hex(c_ = uinput()))
      {
         *p++ = c_;
         val  <<= 4;
         val  += (c_ >= 'A')  ? c_ - 'A' + 10  : c_ - '0';
      }
      else
      {
         c = c_;
         if( i == 4)
            lsyntax("That hex number looks mighty strange");
         yylval = (lword) val;
         IMESS("calced hexnumber = %x", (lword) val, 2);
         dreturn( T_NUMBER);
      }
   while( i--);
   c = c_;
   lsyntax(E_HEXLEN);
}


#include "md_suck.c"

/* ---------------------------------------------------------- */
/* If a lsyntax error occurs, we'll give a nice message and   */
/* then go back to the business at hand. Tokenizing.          */
/* This is not supposed to find EOF errors, that's for the    */
/* I/O routines.                                              */
/* ---------------------------------------------------------- */
void lsyntax( err)
char  *err;
{
   if( yytext[0])
      nserror( err, yytext);
   else
      nterror( err, (int) c);
   longjmp( syn_panic, 1);
}

/* ---------------------------------------------------------- */
/*           Mallocer for identifier and other strings        */
/*                                                            */
/*  (Keeping the fingers crossed that this is faster than a)  */
/*                       (simple malloc)                      */
/* ---------------------------------------------------------- */
typedef struct _str_m
{
   lword                free;
   char huge            *space;
   struct _str_m huge   *before;
} str_m;

static str_m huge *st_h;

char   *str_alloc( size)
int   size;
{
   register str_m huge  *p;
   register char  huge  *q;
#if STATISTICS
   extern   word     _a_char, _m_char;
   extern  lword     _z_char, _s_char;
#endif

   ENTER("str_alloc");
#if STATISTICS
   _a_char++;
   _z_char += size;
#endif
#if DEBUG
   IMESS("Requested size $%lX", (lword) (size_t) size, 4);
   if( st_h)
   {
      IMESS("st_h == $%lX", (lword) st_h, 4);
      IMESS("st_h->free : $%lX", (lword) st_h->free, 4);
      IMESS("st_h->space: $%lX", (lword) st_h->space, 4);
   }
#endif
   if( (p = st_h) && p->free >= (lword) size)
   {
letsdoit:
      p->free  -= (lword) size;
      q         = p->space;
      p->space += (lword) size;
      IMESS("sending $%lX as address", (lword) q, 4);
      IMESS("st_h->space: $%lX", (lword) st_h->space, 4);
      LEAVE();
      return( q);
   }
#if STATISTICS
   _m_char++;
   _s_char = sizeof(str_m) + STRMAX;
#endif
   MESS("New big block to allocate");
   p         = (str_m huge *) nmalloc( (long) sizeof(str_m) + STRMAX);
   p->free   = (lword) STRMAX;
   p->space  = (char huge *) p + sizeof( str_m);
   p->before = st_h;
   st_h      = p;
   if( size > STRMAX)
      nierror("An unusally large string forces NASM65 to crash");
   goto letsdoit;
}


void yy_txt2val( i)
register int   i;
{
   register char  *src = yytext,
                  *q;

#if VERSION
   register str_m huge  *p = st_h;

   ENTER("yy_txt2val");
   if( p->free >= (lword) i)
   {
      p->free  -= (lword) i;
      q         = p->space;
      p->space += (lword) i;
   }
   else
      q = str_alloc( i);
#else
   ENTER("yy_txt2val");

   q = str_alloc( i);
#endif
   yylval = (lword) q;

   while( i > 4)
   {
      *q++ = *src++;
      *q++ = *src++;
      *q++ = *src++;
      *q++ = *src++;
      i -= 4;
   }
   do
      *q++ = *src++;
   while( --i);
   IMESS("st_h->space: $%lX", (lword) st_h->space, 4);
   LEAVE();
}

/* ------------------------------------------------------------
   Words to keep in mind:

                        "Little green man
                           about four foot
                         maybe he want
                           to kick some butt!"   (Vai)

   ------------------------------------------------------------ */
