#include "forth.h"
/* System ops. */

PRIMITIVE ("word", Fword, Sword, NON_IMMEDIATE, ( c -- a ) )
{
  /* Push the address of a counted string representing the next word
   * in the input stream, as bounded by the delimiter char on top of
   * the stack. 
   *
   * Push 0 if EOF.
   */

  char delimiter = (char) POP;
  char *this_tb = tb + 1;
  int count = 0;
  int ch;

  /* Toss delimiters or other garbage preceding the word. */
  while ((ch = (getc (infile))) == delimiter
         || ch == '\n');

  /* Signal EOF by pushing a null address. */
  if (ch == EOF)
    {
      PUSH (0);
      return;
    }

  while (   (ch != delimiter)
         && (ch != '\n')
         && (ch != EOF)
         && (count < 127))
    {
      
      /* Store the char in the TIB and keep going. */
      *this_tb++ = ch;
      count++;
      ch = getc (infile);
    }
  
  /* Store the count ahead of the string. */
  tb[0] = (char) count;

  /* Push the address of the counted string on the stack. */
  PUSH ((unsigned long) tb);
}


PRIMITIVE ("find", Ffind, Sfind, NON_IMMEDIATE, ( a -- a||a2 f ) )
{
  /* Take the address of a counted string (`a' points to the count
   * byte, yes), and look it up in the dictionary.
   *
   * If found, leave dictionary address and a true FLAG on the stack
   * to indicate that the lookup was successful.
   *
   * If not, leave the old address and false FLAG (it's probably a
   * number, and Fnumber will look it up.)
   */
  
  char *str = (char *) POP;
  char len  = str[0];
  long *addr;

  /* Increment past the count byte before looking up the word. */
  addr = dict_lookup (++str, len);

  if (addr)
    {
      PUSH ((long) addr);
      PUSH (TRUE);
    }
  else
    {
      PUSH ((long) --str);
      PUSH (FALSE);
    }
}


PRIMITIVE ("number", Fnumber, Snumber, NON_IMMEDIATE, ( a -- n ) )
{
  /* Take the address of a counted string, replace it with the number
   * whose English representation that string is.
   *
   * Strings that do not translate to numbers get converted to bizarre
   * things, so be careful.
   */

  char *str = (char *) POP;
  char len  = str[0];

  /* Increment past the count byte. */
  str++;

  /* Make the string be null-terminated. */
  str[len] = '\0';

  /* BASE arg zero means prefix determines base, default is
   * decimal if no prefix.
   */
  PUSH (strtol (str, (char **) NULL, 0));
}


PRIMITIVE ("count", Fcount, Scount, NON_IMMEDIATE, ( a -- a+1 c ) )
{
  /* Replace the address of a counted string with the real address and
   * the count of the string.
   */

  char *addr = (char *) POP;

  PUSH ((unsigned long) (addr + 1));
  PUSH ((unsigned long) *addr);
}



PRIMITIVE ("execute", Fexecute, Sexecute, NON_IMMEDIATE, ( a -- ) )
{
  /* Takes the address on the top of the stack and executes the
   * function it points to.
   */
  funcptr *fp;

  pc = (funcptr **) sp;
  fp = (funcptr *) POP;

  /* And they're off! */
  (*fp) ();
}


PRIMITIVE ("interpret", Finterpret, Sinterpret, NON_IMMEDIATE, ( -- ? ) )
{
  /* The interpreter loop. */
  while (TRUE)
    {
      BL;
      Fword ();
      
      if (!(*sp))
        {
          /* Word left a 0 on the stack, so input reading is over. */
          POP;
          if (infile != stdin) infile = stdin;
          return;
        }
      
      Ffind ();
      
      if (POP)
        Fexecute ();
      else
        Fnumber ();
    }
}


