/*      SC      A Spreadsheet Calculator
 *              Lexical analyser
 *
 *              original by James Gosling, September 1982
 *              modifications by Mark Weiser and Bruce Israel,
 *                      University of Maryland
 *
 *              More mods Robert Bond, 12/86
 *              Major mods to run on VMS and AMIGA, 1/17/87
 *
 */



#include "sc.h"
#include <ctype.h>
#include "y_tab.h"

extern char *malloc();
extern int ttgetc();
extern void yyerror(),move(),printw(),clrtoeol(),clrtobot();

struct key
{
  char *key;
  int val;
};

struct key experres[] =
{
#include "experres.h"
  0, 0};

struct key statres[] =
{
#include "statres.h"
  0, 0};

/*
 * This converts a floating point number of the form
 * [s]ddd[.d*][esd*]  where s can be a + or - and e is E or e.
 * to floating point.
 * p is advanced.
 */

char *
strtof (p, res)
     register char *p;
     double *res;
{
  double acc;
  int sign;
  double fpos;
  int exp;
  int exps;

  acc = 0.0;
  sign = 1;
  exp = 0;
  exps = 1;
  if (*p == '+')
    p++;
  else if (*p == '-')
    {
      p++;
      sign = -1;
    }
  while (isdigit (*p))
    {
      acc = acc * 10.0 + (double) (*p - '0');
      p++;
    }
  if (*p == 'e' || *p == 'E')
    {
      p++;
      if (*p == '+')
        p++;
      else if (*p == '-')
        {
          p++;
          exps = -1;
        }
      while (isdigit (*p))
        {
          exp = exp * 10 + (*p - '0');
          p++;
        }
    }
  if (*p == '.')
    {
      fpos = 1.0 / 10.0;
      p++;
      while (isdigit (*p))
        {
          acc += (*p - '0') * fpos;
          fpos *= 1.0 / 10.0;
          p++;
        }
    }
  if (*p == 'e' || *p == 'E')
    {
      exp = 0;
      exps = 1;
      p++;
      if (*p == '+')
        p++;
      else if (*p == '-')
        {
          p++;
          exps = -1;
        }
      while (isdigit (*p))
        {
          exp = exp * 10 + (*p - '0');
          p++;
        }
    }
  if (exp)
    {
      if (exps > 0)
        while (exp--)
          acc *= 10.0;
      else
        while (exp--)
          acc *= 1.0 / 10.0;
    }
  if (sign > 0)
    *res = acc;
  else
    *res = -acc;

  return (p);
}

int yylex ()
{
  register char *p = line + linelim;
  int ret = -1;
  while (isspace (*p))
    p++;
  if (*p == 0)
    ret = -1;
  else if (isalpha (*p))
    {
      char *tokenst = p;
      register tokenl;
      register struct key *tbl;
      while (isalpha (*p))
        p++;
      if (p - tokenst <= 2)
        {                       /* a COL is 1 or 2 char alpha */
          register col;
          ret = COL;
          col = ((tokenst[0] & 0137) - 'A');
          if (p == tokenst + 2)
            col = (col + 1) * 26 + ((tokenst[1] & 0137) - 'A');
          yylval.ival = col;
        }
      else
        {
          ret = WORD;
          tokenl = p - tokenst;
          for (tbl = linelim ? experres : statres; tbl->key; tbl++)
            if (((tbl->key[0] ^ tokenst[0]) & 0137) == 0
                && tbl->key[tokenl] == 0)
              {
                register i = 1;
                while (i < tokenl && ((tokenst[i] ^ tbl->key[i]) & 0137) == 0)
                  i++;
                if (i >= tokenl)
                  {
                    ret = tbl->val;
                    break;
                  }
              }
          if (ret == WORD)
            {
              linelim = p - line;
#ifdef ENGLISH
              yyerror ("Unintelligible word");
#endif
#ifdef GERMAN
              yyerror ("Unverstaendliches Wort");
#endif
            }
        }
    }
  else if ((*p == '.') || isdigit (*p))
    {
      register long v = 0;
      char *nstart = p;
      if (*p != '.')
        {
          do
            v = v * 10 + (*p - '0');
          while (isdigit (*++p));
        }
      if (*p == '.' || *p == 'e' || *p == 'E')
        {
          ret = FNUMBER;
          p = strtof (nstart, &yylval.fval);
        }
      else
        {
          if ((int) v != v)
            {
              ret = FNUMBER;
              yylval.fval = v;
            }
          else
            {

              ret = NUMBER;
              yylval.ival = v;
            }
        }
    }
  else if (*p == '"')
    {
      /* This storage is never freed.  Oh well.  -MDW */
      char *ptr;
      ptr = p + 1;
      while (*ptr && *ptr++ != '"') ;
      ptr = (char *) malloc ((unsigned) (ptr - p));
      yylval.sval = ptr;
      p += 1;
      while (*p && *p != '"')
        *ptr++ = *p++;
      *ptr = 0;
      if (*p)
        p += 1;
      ret = STRING;
    }
  else if (*p == '[')
    {
      while (*p && *p != ']')
        p++;
      if (*p)
        p++;
      linelim = p - line;
      return yylex ();
    }
  else
    ret = *p++;
  linelim = p - line;
  return ret;
}

#define N_KEY 26

struct key_map
{
  char *k_str;
  char k_val;
  char k_index;
};

struct key_map km[N_KEY];

void initkbd()
{
  /* cursor set mode */
  km[0].k_str = "\033OD";
  km[0].k_val = ctl ('b');
  km[1].k_str = "\033OC";
  km[1].k_val = ctl ('f');
  km[2].k_str = "\033OA";
  km[2].k_val = ctl ('p');
  km[3].k_str = "\033OB";
  km[3].k_val = ctl ('n');
  /* cursor reset mode */
  km[4].k_str = "\033[D";
  km[4].k_val = ctl ('b');
  km[5].k_str = "\033[C";
  km[5].k_val = ctl ('f');
  km[6].k_str = "\033[A";
  km[6].k_val = ctl ('p');
  km[7].k_str = "\033[B";
  km[7].k_val = ctl ('n');
  /* CSI arrows */
  km[8].k_str = "\233D";
  km[8].k_val = ctl ('b');
  km[9].k_str = "\233C";
  km[9].k_val = ctl ('f');
  km[10].k_str = "\233A";
  km[10].k_val = ctl ('p');
  km[11].k_str = "\233B";
  km[11].k_val = ctl ('n');
  /* application keypad mode */
  km[12].k_str = "\033Op";
  km[12].k_val = '0';
  km[13].k_str = "\033Oq";
  km[13].k_val = '1';
  km[14].k_str = "\033Or";
  km[14].k_val = '2';
  km[15].k_str = "\033Os";
  km[15].k_val = '3';
  km[16].k_str = "\033Ot";
  km[16].k_val = '4';
  km[17].k_str = "\033Ou";
  km[17].k_val = '5';
  km[18].k_str = "\033Ov";
  km[18].k_val = '6';
  km[19].k_str = "\033Ow";
  km[19].k_val = '7';
  km[20].k_str = "\033Ox";
  km[20].k_val = '8';
  km[21].k_str = "\033Oy";
  km[21].k_val = '9';
  km[22].k_str = "\033Om";
  km[22].k_val = '-';
  km[23].k_str = "\033Ol";
  km[23].k_val = ',';
  km[24].k_str = "\033On";
  km[24].k_val = '.';
  km[25].k_str = "\033OM";
  km[25].k_val = ctl ('m');
}

char nmgetch()
{
  register int c;
  register struct key_map *kp;
  register struct key_map *biggest;
  register int i;
  int almost;

  static char dumpbuf[10];
  static char *dumpindex;

  void timeout ();

  if (dumpindex && *dumpindex)
    return (*dumpindex++);

  c = ttgetc ();
  biggest = 0;
  almost = 0;

  for (kp = &km[0]; kp < &km[N_KEY]; kp++)
    {
      if (!kp->k_str)
        continue;
      if (c == (kp->k_str[kp->k_index] & 0xFF))
        {
          almost = 1;
          kp->k_index++;
          if (kp->k_str[kp->k_index] == 0)
            {
              c = kp->k_val;
              for (kp = &km[0]; kp < &km[N_KEY]; kp++)
                kp->k_index = 0;
              return (c);
            }
        }
      if (!biggest && kp->k_index)
        biggest = kp;
      else if (kp->k_index && biggest->k_index < kp->k_index)
        biggest = kp;
    }

  if (almost)
    return (nmgetch ());

  if (biggest)
    {
      for (i = 0; i < biggest->k_index; i++)
        dumpbuf[i] = biggest->k_str[i];
      dumpbuf[i++] = c;
      dumpbuf[i] = 0;
      dumpindex = &dumpbuf[1];
      for (kp = &km[0]; kp < &km[N_KEY]; kp++)
        kp->k_index = 0;
      return (dumpbuf[0]);
    }
  return (c);
}

int dbline;

void debug (fmt, a, b, c)
{
  move (2 + (dbline++ % 22), 80 - 60);
  printw (fmt, a, b, c);
  clrtoeol ();
}

void help()
{
  move (2, 0);
  clrtobot ();
  dbline = 0;
#ifdef ENGLISH
  debug("                 Cursor cmds:");
  debug("  ^n j next row       ^p k prev. row      ^g erase cmd");
  debug("  ^f l fwd col        ^b h back col       ^r redraw screen");
  debug("     0 first col         $ end col");
  debug("                 Cell cmds:");
  debug(" \" < > enter label       = enter value     x clear cell");
  debug("     c copy cell         m mark cell      ^t line 1 on/off");
  debug("    ^a type value       ^e type expr.     ^v type vbl name");
  debug("                 Row, Column cmds:");
  debug(" ar ac dup     ir ic insert  sr sc show  vr vc value only");
  debug(" dr dc delete  zr zc hide    pr pc pull      f format");
  debug("                 File cmds:");
  debug("     G get database      M merge database  T write tbl fmt");
  debug("     P put database      W write listing");
  debug("                 Misc. cmds:");
  debug(" ^c, q quit              / copy region    pm pull (merge)");
  debug("                 Expression Operators");
  debug("  +-*/ arithmetic     ?e:e conditional   & | booleans");
  debug(" < = > relations     <= >= relations      != relations");
  debug("       @sum(v1:v2)         @avg(v1:v2)       @prod(v1:v2)");
#endif
#ifdef GERMAN
  debug("                 Cursor Befehle:");
  debug(" ^n j naechste Zeile  ^p k vorige Zeile   ^g Stornieren");
  debug(" ^f l naechste Spalte ^b h vorige Spalte  ^r Anzeige neu");
  debug("    0 erste Spalte      $ letzte Spalte");
  debug("                Zellen Befehle:");
  debug("\" < > Namen eingeben  = Wert eingeben   x Zelle loeschen");
  debug(" c Zelle kopieren m Zelle markieren ^t Zeile 1 an/aus");
  debug("^a Schreibe Wert ^e Schreibe Namen ^v Schreibe Zellennamen");
  debug("                Zeilen, Spalten Befehle:");
  debug("ar ac dup.  ir ic einfuegen sr sc zeige   vr vc Nur Wert");
  debug("dr dc loesch. zr zc verstecke pr pc pull      f Format");
  debug("                Datei Befehle:");
  debug("    G Lade Datei   M Datei hinzufuegen T Schreibe tab fmt");
  debug("    P Speichere Datei   W Schreibe Listing");
  debug("                Gemischte Befehle:");
  debug(" ^c, q Beenden  / Region kopieren   pm Region hinzufuegen");
  debug("                 Operatoren fuer Ausdruecke");
  debug("  +-*/ Arithmetisch   ?e:e Bedingung     & | Logisch");
  debug(" < = > Bedingungen   <= >= Bedingungen    != Bedingungen");
  debug("       @sum(v1:v2)         @avg(v1:v2)       @prod(v1:v2)");
#endif
}
