/*
 * llex.c -- lexical analysis routines.
 */

#include "link.h"
#include "tproto.h"
#include "tglobals.h"
#include "opcode.h"

int nlflag = 0;		/* newline last seen */

#define tonum(c)	(isdigit(c) ? (c - '0') : ((c & 037) + 9))

/*
 * getopc - get an opcode from infile, return the opcode number (via
 *  binary search of opcode table), and point id at the name of the opcode.
 */
int getopc(id)
char **id;
   {
   register char *s;
   register struct opentry *p;
   register int test;
   word indx;
   int low, high, cmp;

   indx = getstr();
   if (indx == -1)
      return EOF;
   s = &lsspace[indx];
   low = 0;
   high = NOPCODES;
   do {
      test = (low + high) / 2;
      p = &optable[test];
      if ((cmp = strcmp(p->op_name, s)) < 0)
         low = test + 1;
      else if (cmp > 0)
         high = test;
      else {
         *id = p->op_name;
         return (p->op_code);
         }
      } while (low < high);
   *id = s;
   return 0;
   }

/*
 * getid - get an identifier from infile, put it in the identifier
 *  table, and return a index to it.
 */
word getid()
   {
   word indx;

   indx = getstr();
   if (indx == -1)
      return EOF;
   return putident((int)strlen(&lsspace[indx])+1, 1);
   }

/*
 * getstr - get an identifier from infile and return an index to it.
 */
word getstr()
   {
   register int c;
   register word indx;

   indx = lsfree;
   while ((c = getc(infile)) == ' ' || c == '\t') ;
   if (c == EOF)
      return -1;
   while (c != ' ' && c != '\t' && c != '\n' && c != ',' && c != EOF) {
      if (indx >= stsize)
         lsspace = (char *)trealloc(lsspace, NULL, &stsize, 1, 1,
            "string space");
      lsspace[indx++] = c;
      c = getc(infile);
      }
   if (indx >= stsize)
      lsspace = (char *)trealloc(lsspace, NULL, &stsize, 1, 1,
         "string space");
   lsspace[indx] = '\0';
   nlflag = (c == '\n');
   return lsfree;
   }

/*
 * getrest - get the rest of the line from infile, put it in the identifier
 *  table, and return its index in the string space.
 */
word getrest()
   {
   register int c;
   register word indx;

   indx = lsfree;
   while ((c = getc(infile)) != '\n' && c != EOF) {
      if (indx >= stsize)
         lsspace = (char *)trealloc(lsspace, NULL, &stsize, 1, 1,
            "string space");
      lsspace[indx++] = c;
      }
   if (indx >= stsize)
      lsspace = (char *)trealloc(lsspace, NULL, &stsize, 1, 1,
         "string space");
   lsspace[indx++] = '\0';
   nlflag = (c == '\n');
   return putident((int)(indx - lsfree), 1);
   }

/*
 * getdec - get a decimal integer from infile, and return it.
 */
int getdec()
   {
   register int c, n;
   int sign = 1, rv;

   n = 0;
   while ((c = getc(infile)) == ' ' || c == '\t') ;
   if (c == EOF)
      return 0;
   if (c == '-') {
      sign = -1;
      c = getc(infile);
      }
   while (c >= '0' && c <= '9') {
      n = n * 10 + (c - '0');
      c = getc(infile);
      }
   nlflag = (c == '\n');
   rv = n * sign;
   return rv;					/* some compilers ... */
   }

/*
 * getoct - get an octal number from infile, and return it.
 */
int getoct()
   {
   register int c, n;

   n = 0;
   while ((c = getc(infile)) == ' ' || c == '\t') ;
   if (c == EOF)
      return 0;
   while (c >= '0' && c <= '7') {
      n = (n << 3) | (c - '0');
      c = getc(infile);
      }
   nlflag = (c == '\n');
   return n;
   }

/*
 *  Get integer, but if it's too large for a long, put the string via wp
 *   and return -1.
 */
long getint(j,wp)
   int j;
   word *wp;
   {
   register int c;
   int over = 0;
   register word indx;
   double result = 0;
   long lresult = 0;
   double radix;

   ++j;   /* incase we need to add a '\0' and make it into a string */
   if (lsfree + j >= stsize)
      lsspace = (char *)trealloc(lsspace, NULL, &stsize, 1, j, "string space");
   indx = lsfree;

   while ((c = getc(infile)) >= '0' && c <= '9') {
      lsspace[indx++] = c;
      result = result * 10 + (c - '0');
      lresult = lresult * 10 + (c - '0');
      if (result <= MinLong || result >= MaxLong) {
         over = 1;			/* flag overflow */
         result = 0;			/* reset to avoid fp exception */
         }
      }
   if (c == 'r' || c == 'R') {
      lsspace[indx++] = c;
      radix = result;
      lresult = 0;
      result = 0;
      while ((c = getc(infile)) != 0) {
         lsspace[indx++] = c;
         if (isdigit(c) || isalpha(c))
            c = tonum(c);
         else
            break;
         result = result * radix + c;
         lresult = lresult * radix + c;
         if (result <= MinLong || result >= MaxLong) {
            over = 1;			/* flag overflow */
            result = 0;			/* reset to avoid fp exception */
            }
         }
      }
   nlflag = (c == '\n');
   if (!over)
      return lresult;			/* integer is small enough */
   else {				/* integer is too large */
      lsspace[indx++] = '\0';
      *wp = putident((int)(indx - lsfree), 1); /* convert integer to string */
      return -1;			/* indicate integer is too big */
      }
   }

/*
 * getreal - get an Icon real number from infile, and return it.
 */
double getreal()
   {
   double n;
   register int c, d, e;
   int esign;
   register char *s, *ep;
   char cbuf[128];

   s = cbuf;
   d = 0;
   while ((c = getc(infile)) == '0')
      ;
   while (c >= '0' && c <= '9') {
      *s++ = c;
      d++;
      c = getc(infile);
      }
   if (c == '.') {
      if (s == cbuf)
         *s++ = '0';
      *s++ = c;
      while ((c = getc(infile)) >= '0' && c <= '9')
         *s++ = c;
      }
   ep = s;
   if (c == 'e' || c == 'E') {
      *s++ = c;
      if ((c = getc(infile)) == '+' || c == '-') {
         esign = (c == '-');
         *s++ = c;
         c = getc(infile);
         }
      else
         esign = 0;
      e = 0;
      while (c >= '0' && c <= '9') {
         e = e * 10 + c - '0';
         *s++ = c;
         c = getc(infile);
         }
      if (esign) e = -e;
      e += d - 1;
      if (abs(e) >= LogHuge)
         *ep = '\0';
      }
   *s = '\0';
   n = atof(cbuf);
   nlflag = (c == '\n');
   return n;
   }

/*
 * getlab - get a label ("L" followed by a number) from infile,
 *  and return the number.
 */

int getlab()
   {
   register int c;

   while ((c = getc(infile)) != 'L' && c != EOF && c != '\n') ;
   if (c == 'L')
      return getdec();
   nlflag = (c == '\n');
   return 0;
   }

/*
 * getstrlit - get a string literal from infile, as a string
 *  of octal bytes, and return its index into the string table.
 */
word getstrlit(l)
register int l;
   {
   register word indx;

   if (lsfree + l >= stsize)
      lsspace = (char *)trealloc(lsspace, NULL, &stsize, 1, l, "string space");
   indx = lsfree;
   while (!nlflag && l--)
      lsspace[indx++] = getoct();
   if (indx >= stsize)
      lsspace = (char *)trealloc(lsspace, NULL, &stsize, 1, 1,
         "string space");
   lsspace[indx++] = '\0';
   return putident((int)(indx-lsfree), 1);
   }

/*
 * newline - skip to next line.
 */
void newline()
   {
   register int c;

   if (!nlflag) {
      while ((c = getc(infile)) != '\n' && c != EOF) ;
      }
   nlflag = 0;
   }
