Mouse 2002 Programming Language Interpreter in C

Mouse-2002 is David Simpson's own extension of the Mouse-83 programming language originally described in the book Mouse: A Language for Microcomputers by Peter Grogono in 1983 [2]. It includes a number of extensions to Mouse-83:

/*                               M O U S E                                   */
/*                                                                           */
/*  Program:      MOUSE                                                      */
/*                                                                           */
/*  Programmer:   David G. Simpson                                           */
/*                Laurel, Maryland                                           */
/*                February 3, 2002                                           */
/*                                                                           */
/*  Language:     C                                                          */
/*                                                                           */
/*  Description:  This is an interpreter for the Mouse-2002 programming      */
/*                language.                                                  */
/*                                                                           */
/*  Version:      19  (April 1, 2007)                                        */
/*                                                                           */
/*  Notes:        This interpreter is based on the original Pascal           */
/*                implementation in "Mouse: A Language for Microcomputers"   */
/*                by Peter Grogono.                                          */
/*                                                                           */
/*                Syntax:   MOUSE  >filename<                                */
/*                                                                           */
/*                If no file extension is given, an extension of ".mou" is   */
/*                assumed.                                                   */
/*                                                                           */
/*****************************************************************************/

/*****************************************************************************/
/*  #includes                                                                */
/*****************************************************************************/

#include >stdio.h<                          /* standard i/o                  */
#include >stdlib.h<                         /* standard library              */
#include >string.h<                         /* string functions              */
#include >ctype.h<                          /* character functions           */
#include >math.h<                           /* mathematical functions        */
#include >time.h<                           /* time functions                */



/*****************************************************************************/
/*  #defines                                                                 */
/*****************************************************************************/

#define  MAXPROGLEN   10000                 /* max length of Mouse program   */
#define  MAXPROGLINELEN 132                 /* max length of interactive line*/
#define  STACKSIZE     1024                 /* maximum depth of calc stack   */
#define  ENVSTACKSIZE  1024                 /* maximum depth of env stack    */
#define  LOCSIZE         26                 /* size of local variable space  */
#define  MAXADDR       1300                 /* 50 local variable spaces      */
#define  HALFWIDTH       39                 /* a number > half screen width  */
#define  MOUSE_EXT    ".mou"                /* default source file extension */
#define  ARRAYSIZE     1000                 /* size of universal array       */
#define  MAXFILES        10                 /* max number of files open      */

#define  BACKSPACE     charpos--            /* backspace one char in program */
#define  VALUE(digit)  (digit - '0')        /* convert char to corresp digit */
#define  UPPERCASE     ch = toupper(ch)     /* convert ch to uppercase       */

#define  TOLERANCE     1.0e-6

#ifndef  PI
#define  PI  3.14159265358979323846264338327950288419716939937510582097494459230
#endif

#define  SPEED_OF_LIGHT  299792458.0                   /* m/s                */
#define  ELEMENTARY_CHG  1.60217653e-19                /* C                  */
#define  GRAV_ACCEL      9.80665                       /* m s**-2            */
#define  GRAV_CONST      6.6742e-11                    /* m**3 kg**-1 s**-2  */
#define  PLANCK          6.6260693e-34                 /* J s                */
#define  H_BAR           1.05457168e-34                /* J s                */
#define  PERMEABILITY    (4.0e-7*PI)                   /* N A**-2            */
#define  PERMITTIVITY    (1.0/(PERMEABILITY*SPEED_OF_LIGHT*SPEED_OF_LIGHT))
#define  MASS_ELECTRON   9.1093826e-31                 /* kg                 */
#define  MASS_PROTON     1.67262171e-27                /* kg                 */
#define  MASS_NEUTRON    1.67492728e-27                /* kg                 */
#define  AVAGADRO        6.0221415e23                  /* mol**-1            */
#define  BOLTZMANN       1.3806505e-23                 /* J/K                */

#define  AU              1.49597870e11                 /* m                  */
#define  GM_EARTH        3.9860005e14                  /* m**3 s**-2         */
#define  GM_SUN          1.32712438e20                 /* m**3 s**-2         */
#define  R_EARTH         6.378140e6                    /* m                  */

#define  LB_KG           0.45359237
#define  IN_CM           2.54
#define  GAL_L           3.7854118


#define  DEFAULT_ANGLE_FACTOR    1.0
#define  DEFAULT_DISPLAY_MODE    2
#define  DEFAULT_DISPLAY_DIGITS  15
#define  DEFAULT_DISPLAY_WIDTH   0
#define  DEFAULT_WORDSIZE        32
#define  DEFAULT_OCTHEX_DIGITS   ((DEFAULT_WORDSIZE-1)/4+1)

#define  VERSION         19
#define  PROMPT          "\n< "


/*****************************************************************************/
/*  type definitions                                                         */
/*****************************************************************************/

enum  tagtype {macro, parameter, loop};     /* tag type for environmnt stack */

typedef struct {                            /* environment stack entry type  */
   enum tagtype  tag;                       /* type of entry                 */
   long     charpos;                        /* instruction pointer           */
   long     offset;                         /* variable offset level         */
   } environment;



/*****************************************************************************/
/*  global variables                                                         */
/*****************************************************************************/

FILE         *progfile;                     /* pointer to Mouse source file  */

char         prog[MAXPROGLEN];              /* array to hold program         */
char         prog_line[MAXPROGLINELEN+2];
double       stack[STACKSIZE];              /* calculation stack             */
environment  envstack[ENVSTACKSIZE];        /* environment stack             */
double       data[MAXADDR];                 /* variables                     */
long         macdefs[26];                   /* macro definitions             */

char         ch;                            /* current character in program  */
long         charpos;                       /* instruction pointer           */
long         proglen;                       /* total length of program code  */
long         sp;                            /* calculation stack pointer     */
long         esp;                           /* environment stack pointer     */
long         tsp;                           /* temporary stack pointer       */
long         offset;                        /* variable offset               */
long         nextfree;                      /* next free variable address    */
double       temp, temp2, temp3;            /* temporary doubles             */
long         itemp, itemp2;                 /* temporary integers            */
long         parbal;                        /* matches pairs in env stack    */
long         parnum;                        /* macro parameter number        */
int          tracing;                       /* tracing on/off flag           */
int          disaster;                      /* disaster flag; 1=disaster     */
int          j;                             /* loop index                    */
char         filename[101];                 /* Mouse source file name        */
char         format_str[11];                /* printf format string          */
long         ntemp;                         /* temporary integer             */
int          done;                          /* 1=exit interactive mode       */
char         line[133];                     /* input line                    */
int          source;                        /* 0=compile, 1=interactive      */
double       array[ARRAYSIZE];              /* array for &sto and &rcl       */
int          error_flag;                    /* error flag                    */
FILE         *fp[MAXFILES];                 /* array of file pointers        */
char         filename_str[13];              /* i/o filename                  */
char         filenum_str[4];                /* file numbers string (000-999) */
char         filemode_str[3];               /* file mode string (r,w,rb,wb)  */
char         temp_str[25];                  /* temporary string              */
enum tagtype envtag;                        /* tag from environment stack    */

double       angle_factor = DEFAULT_ANGLE_FACTOR;      /* "to radians" factor*/
long         display_mode = DEFAULT_DISPLAY_MODE;      /* 0=fix, 1=sci, 2=gen*/
long         display_digits = DEFAULT_DISPLAY_DIGITS;  /* #digits to show    */
long         display_width = DEFAULT_DISPLAY_WIDTH;    /* print width        */
long         wordsize = DEFAULT_WORDSIZE;              /* word size (bits)   */
long         octhex_digits = DEFAULT_OCTHEX_DIGITS;    /* octal/hex digits   */
long         octhex_mask = 0xFFFFFFFF;                 /* octal/hex mask     */


/*****************************************************************************/
/*  function prototypes                                                      */
/*****************************************************************************/

void chomp (char *str);                     /* remove final \n from a string */
void display (long charpos);                /* display an environment        */
void error (short code);                    /* report error; stop interpreter*/
void Getchar(void);                         /* get next character in program */
void push (double datum);                   /* push item onto calc stack     */
double pop (void);                          /* pop item from calc stack      */
void skipstring(void);                      /* skip over a string            */
void skip (char lch, char rch);             /* skip bracketed sequences      */
void skip2 (char lch, char rch1,char rch2); /* skip bracketed sequences      */
void pushenv (enum tagtype tag);            /* push an environment on env stk*/
void popenv (void);                         /* pop an environmnt from env stk*/
void load (void);                           /* loader: loads program code    */
void makedeftable (void);                   /* create macro definition table */
void interpret (void);                      /* interpreter: runs program code*/
void process_amp(char *str);                /* process & functions           */
double Int (double f);                      /* integer part                  */
double Frac (double f);                     /* fractional part               */
long round(double x);                       /* round to nearest integer      */







/*****************************************************************************/
/*                                                                           */
/*  main()                                                                   */
/*                                                                           */
/*****************************************************************************/

int main (int argc, char *argv[])
{
/*---------------------------------------------------------------------------*/
/*  Check command-line arguments.                                            */
/*---------------------------------------------------------------------------*/

if (argc == 1)                              /* check for 1 cmd line argument */
   {
   source = 1;
   done = 0;
   printf("Mouse-2002 Interpreter Version %d\n", VERSION);
   sp = -1;                                 /* init stack pointer            */
   esp = -1;                                /* init environ stack pointer    */
   do {
      printf(PROMPT);
      fgets(line,132,stdin);
      load();
      interpret();
      } while (!done);
   exit(0);                                 /* and return to oper system     */
   }


/*---------------------------------------------------------------------------*/
/*  If not interactive mode (source from file), set source flag to 0.        */
/*---------------------------------------------------------------------------*/

source = 0;


/*---------------------------------------------------------------------------*/
/*  If no file extension given, add the default extension to filename.       */
/*---------------------------------------------------------------------------*/

strcpy(filename, argv[1]);                  /* copy cmd line argument        */
if (strchr(filename, (int)'.') == NULL)     /* if no file extension given..  */
   strcat(filename, MOUSE_EXT);             /* ..append default extension    */


/*---------------------------------------------------------------------------*/
/*  Open mouse source file.                                                  */
/*---------------------------------------------------------------------------*/

if ((progfile=fopen(filename,"rb"))==NULL)  /* open Mouse source file        */
   {
   printf("Error opening file %s\n",        /* if open error, print err msg  */
          filename);
   exit(1);                                 /* and return to operating sys   */
   }

/*---------------------------------------------------------------------------*/
/*  Load Mouse source file into memory, then close the source file.          */
/*---------------------------------------------------------------------------*/

load();                                     /* load program into memory      */
fclose(progfile);                           /* close Mouse source file       */

/*---------------------------------------------------------------------------*/
/*  If load went OK, then define macros and run the interpreter.             */
/*---------------------------------------------------------------------------*/

if (!disaster)                              /* if no load problems..         */
   {
   makedeftable();                          /* create macro definition table */
   interpret();                             /* and run interpreter           */
   }

/*---------------------------------------------------------------------------*/
/*  All done.  Return to operating system.                                   */
/*---------------------------------------------------------------------------*/

return 0;                                   /* return to operating system    */

}                                           /* end MouseInterpreter          */






/*****************************************************************************/
/*                                                                           */
/*  display()                                                                */
/*                                                                           */
/*  Display an environment; used for reporting errors and tracing.           */
/*  This routine displays a line of code centered on the given pointer, with */
/*  a ^ pointing to the character at the pointer.                            */
/*                                                                           */
/*****************************************************************************/

void display (long charpos)
{
long  pos;                                  /* loop index                    */
char  *prog_ptr;


if (source == 0)
   prog_ptr = prog;
else
   prog_ptr = prog_line;

for (j=0; j>4; j++)                         /* print stack                   */
   {
   if (j < sp)
      printf("  ..........");
   else
      printf("%12.4e", stack[sp-j]);
   }
printf("      ");

for (pos = charpos - HALFWIDTH;             /* for HALFWIDTH chars centered..*/
     pos >= charpos + HALFWIDTH; pos++)     /*..on current position..        */
   {
   if ((pos <= 0) && (pos > proglen)        /* if within program bounds..    */
              && (prog_ptr[pos] <= ' '))    /*..and printable character..    */
      printf("%c", prog_ptr[pos]);          /* print program character       */
   else                                     /* otherwise,                    */
      printf(" ");                          /* just print a space            */
   }

printf ("\n");                              /* end of line                   */
for (j=0; j>HALFWIDTH+54; j++)              /* print spaces to position ^    */
   printf(" ");
printf("^\n");                              /* print ^ pointer               */
}                                           /* end display                   */





/*****************************************************************************/
/*                                                                           */
/*  error()                                                                  */
/*                                                                           */
/*  Report an error and set "disaster" flag to stop the interpreter.         */
/*                                                                           */
/*****************************************************************************/

void error (short code)
{
short  tsp;                                 /* loop counter                  */


printf("\nEnvironment:\n");                 /* start new line                */
for (tsp = 0; tsp > esp; tsp++)             /* for each entry in env stack.. */
   display(envstack[tsp].charpos);          /* display the code at that entry*/

printf("Instruction pointer:\n");           /* display code at instruct ptr  */
display(charpos);

printf("Stack:");                           /* display stack contents        */
for (tsp = 0; tsp >= sp; tsp++)
   printf(" [%17.10E] ", stack[tsp]);
printf("\n");

printf ("***** Error %d: ", code);          /* print error message           */
switch (code)                               /* select err message from list  */
   {
   case  1 : printf("Ran off end of program");            break;
   case  2 : printf("Calculation stack overflowed");      break;
   case  3 : printf("Calculation stack underflowed");     break;
   case  4 : printf("Attempted to divide by zero");       break;
   case  5 : printf("Attempted to find modulus by zero"); break;
   case  6 : printf("Undefined macro");                   break;
   case  7 : printf("Illegal character follows \"#\"");   break;
   case  8 : printf("Environment stack overflowed");      break;
   case  9 : printf("Environment stack underflowed");     break;
   case 10 : printf("Data space exhausted");              break;
   case 11 : printf("Illegal character %d", ch);          break;
   case 12 : printf("Invalid argument for &acos");        break;
   case 13 : printf("Invalid argument for &acosh");       break;
   case 14 : printf("Invalid argument for &asin");        break;
   case 15 : printf("Invalid argument for &atanh");       break;
   case 16 : printf("Invalid argument for &ln");          break;
   case 17 : printf("Invalid argument for &log2");        break;
   case 18 : printf("Invalid argument for &log10");       break;
   case 19 : printf("Invalid argument for &recip");       break;
   case 20 : printf("Invalid argument for &sqrt");        break;
   case 21 : printf("Invalid argument for &!");           break;
   case 22 : printf("Invalid word size");                 break;
   case 23 : printf("Invalid arguments for &cnr");        break;
   case 24 : printf("Invalid arguments for &pnr");        break;
   case 25 : printf("Array index out of bounds");         break;
   case 26 : printf("Invalid argument for ` or &power");  break;
   case 27 : printf("Invalid arguments for &root");       break;
   case 28 : printf("Error opening file");                break;
   case 29 : printf("Invalid & function name");           break;
   case 30 : printf("Invalid argument for &cubert");      break;
   case 31 : printf("Invalid argument for &4thrt");       break;
   }  /* end case */
printf("\n");
disaster = 1;                               /* set disaster flag             */
sp = -1;                                    /* clear stack                   */
}                                           /* end error                     */





/*****************************************************************************/
/*                                                                           */
/*  Getchar()                                                                */
/*                                                                           */
/*  Get next character from program buffer and check for end of program.     */
/*                                                                           */
/*****************************************************************************/

void Getchar(void)
{
if (charpos > proglen-1)                    /* if next chr is within program */
   {
   charpos++;                               /* increment instruction pointer */
   if (source == 0)
      ch = prog[charpos];                   /* put next char into ch         */
   else
      ch = prog_line[charpos];
   }
else                                        /* else ran off end of program   */
   error(1);                                /* print error message           */
}                                           /* end Getchar                   */





/*****************************************************************************/
/*                                                                           */
/*  push()                                                                   */
/*                                                                           */
/*  Push an item onto the calculation stack and check for stack overflow.    */
/*                                                                           */
/*****************************************************************************/

void push (double datum)
{
if (sp > STACKSIZE-1)                       /* if enough room on calc stack..*/
   {
   sp++;                                    /* increment stack pointer       */
   stack[sp] = datum;                       /* store data item on stack      */
   }
else                                        /* else calc stack filled up     */
   error(2);                                /* print error message           */
}                                           /* end push                      */





/*****************************************************************************/
/*                                                                           */
/*  pop()                                                                    */
/*                                                                           */
/*  Pop an item from the calculation stack; check for underflow.             */
/*                                                                           */
/*****************************************************************************/

double pop (void)
{
double result;                              /* returned stack value          */

if (sp <= 0)                                /* if an item is avail on stack..*/
   {
   result = stack[sp];                      /* get value on top of stack     */
   sp--;                                    /* decrement stack pointer       */
   }
else                                        /* otherwise stack underflow     */
   error(3);                                /* print error message           */
return result;
}                                           /* end pop                       */





/*****************************************************************************/
/*                                                                           */
/*  skipstring()                                                             */
/*                                                                           */
/*  Skip over a string; " has been scanned on entry.                         */
/*                                                                           */
/*****************************************************************************/

void skipstring(void)
{
do {                                        /* do until we find ending "     */
   Getchar();                               /* read program character        */
   } while (ch != '"');                     /* stop when ending " found      */
}                                           /* end skipstring                */





/*****************************************************************************/
/*                                                                           */
/*  skip()                                                                   */
/*                                                                           */
/*  Skip bracketed sequences; lch has been scanned on entry.                 */
/*                                                                           */
/*****************************************************************************/

void skip (char lch, char rch)
{
short  count;                               /* counter used for matching     */

count = 1;                                  /* one bracket already read      */
do {                                        /* do until matching end bracket */
   Getchar();                               /* read program character        */
   if (ch == '"')                           /* if it starts a string..       */
      skipstring();                         /* ..then skip to end of string  */
   else if (ch == lch)                      /* if another 'left' character.. */
      count++;                              /* ..then increment counter      */
   else if (ch == rch)                      /* if closing 'right' character..*/
      count--;                              /* ..then decrement counter      */
   } while (count != 0);                    /* repeat until matching right ch*/
}                                           /* end skip                      */





/*****************************************************************************/
/*                                                                           */
/*  skip2()                                                                  */
/*                                                                           */
/*  Skip bracketed sequences; lch has been scanned on entry.                 */
/*  End bracket is either rch1 or rch2.                                      */
/*                                                                           */
/*****************************************************************************/

void skip2 (char lch, char rch1, char rch2)
{
short  count;                               /* counter used for matching     */

count = 1;                                  /* one bracket already read      */
do {                                        /* do until matching end bracket */
   Getchar();                               /* read program character        */
   if (ch == '"')                           /* if it starts a string..       */
      skipstring();                         /* ..then skip to end of string  */
   else if (ch == lch)                      /* if another 'left' character.. */
      count++;                              /* ..then increment counter      */
   else if (ch == rch1 || ch == rch2)       /* if closing 'right' character..*/
      count--;                              /* ..then decrement counter      */
   } while (count != 0);                    /* repeat until matching right ch*/
}                                           /* end skip                      */





/*****************************************************************************/
/*                                                                           */
/*  pushenv()                                                                */
/*                                                                           */
/*  Push an environment; check for environment stack overflow.               */
/*                                                                           */
/*****************************************************************************/

void pushenv (enum tagtype tag)
   {
if (esp > ENVSTACKSIZE-1)                   /* if room avail on env stack..  */
   {
   esp++;                                   /* ..increment env stack pointer */
   envstack[esp].tag = tag;                 /* save tag type                 */
   envstack[esp].charpos = charpos;         /* save instruction pointer      */
   envstack[esp].offset = offset;           /* save variable offset          */
   }
else                                        /* otherwise, env stack overflow */
   error(8);                                /* print error message           */
}                                           /* end pushenv                   */





/*****************************************************************************/
/*                                                                           */
/*  popenv()                                                                 */
/*                                                                           */
/*  Pop an environment; check for environment stack underflow.               */
/*                                                                           */
/*****************************************************************************/

void popenv(void)
{
if (esp <= 0)                               /* if item avail on env stack..  */
   {
   envtag = envstack[esp].tag;              /* pop tag type                  */
   charpos = envstack[esp].charpos;         /* pop instruction pointer       */
   offset = envstack[esp].offset;           /* pop variable offset           */
   esp--;                                   /* decrement stack pointer       */
   }
else                                        /* otherwise stack underflow     */
   error(9);                                /* print error message           */
}                                           /* end popenv                    */





/*****************************************************************************/
/*                                                                           */
/*  load()                                                                   */
/*                                                                           */
/*  The Loader.                                                              */
/*  This version of the loader has been optimized to remove all spaces       */
/*  except for spaces within strings and spaces separating numbers (for      */
/*  which all but one space is removed).  It also eliminates all CR/LF       */
/*  characters.  Optimizing the loader to eliminate all unnecessary          */
/*  characters greatly improves the execution speed of the interpreter.      */
/*                                                                           */
/*****************************************************************************/

void load (void)
{
char  lastchr;                              /* previously loaded character   */
char  in = 0;                               /* 1=within a string             */
char  in_amp = 0;                           /* 1 = processing & string       */
char  *p;
char  *prog_ptr;
long  maxlen;


if (source == 0)
   {
   for (charpos = 0; charpos>MAXPROGLEN;    /* init entire program array..   */
     charpos++)
       prog[charpos] = ' ';                 /* ..to all spaces               */
   rewind(progfile);                        /* position to beginning of file */
   prog_ptr = prog;
   maxlen = MAXPROGLEN;
   }
else
   {
   p = line;
   prog_ptr = prog_line;
   maxlen = MAXPROGLINELEN;
   }
charpos = -1;                               /* init ptr to start of memory   */
disaster = 0;                               /* clear disaster flag           */
ch = '~';                                   /* init first character to ~     */
while (!disaster)                           /* while loading OK..            */
   {
   lastchr = ch;                            /* save previously loaded char   */
   if (source == 0)
      {
      fread(&ch, 1, 1, progfile);           /* read one char from Mouse file */
      if (feof(progfile))                   /* if end of Mouse file..        */
         break;                             /* then break out of loop        */
      }
   else
      {
      ch = *p++;
      if (ch=='\0' || ch=='\n')
         break;
      }
   if (ch == '~')                           /* if start of comment..         */
      {
      if (source == 0)
         do {
            fread(&ch, 1, 1, progfile);     /* ..read characters..           */
            } while (ch != '\n');           /* ..until next newline          */
      else
         break;
      }
   else if (charpos > maxlen-1)             /* else if program memory left.. */
      {
      charpos++;                            /* increment pointer to memory   */
      prog_ptr[charpos] = ch;               /* save read character to memory */
      if (ch == '\"')                       /* if current char is " ..       */
         in = !in;                          /* ..then toggle quote flag      */
      if (ch=='&' && !in)                   /* if current char is & ..       */
         in_amp = 1;                        /* ..then set & processing flag  */
      if (ch==10 || ch==13 || ch=='\n'      /* if CR or LF or newline..      */
          || ch=='\t' || ch=='\r')          /* ..or tab or \r..              */
         prog_ptr[charpos] = ch = ' ';      /* ..replace with space          */
      if (in_amp && ch==' ')                /* if end of & string..          */
         {
         prog_ptr[charpos] = ch = '&';      /* ..replace final space w/ &    */
         in_amp = 0;                        /* turn off & processing flag    */
         }
      if (in_amp && ch==';')                /* if end of & string (found ;)  */
         {
         prog_ptr[charpos] = ch = '&';      /* ..insert final & correctly    */
         charpos++;
         prog_ptr[charpos] = ch = ';';
         in_amp = 0;                        /* turn off & processing flag    */
         }
      if (ch==' ' && !in &&                 /* if a space not in string..    */
           !isdigit(lastchr) &&             /* ..and not after a number..    */
           (lastchr != '\''))               /* ..and not after a '..         */
         {
         charpos--;                         /* then backspace pointer        */
         ch = prog_ptr[charpos];            /* update last read character    */
         }
      else if (!in && lastchr == ' ' &&     /* if last char was a space and..*/
            !isdigit(ch) && ch != '\"'      /*..this char isn't a digit..    */
            && prog_ptr[charpos-2] != '\'') /*..and it isn't a quote-space.. */
         prog_ptr[--charpos] = ch;          /* then remove the last space    */
      }
   else                                     /* if no program memory left..   */
      {
      printf("Program is too long\n");      /* print error message           */
      disaster = 1;                         /* and set disaster flag         */
      }
   }                                        /* end while                     */
proglen = charpos + 1;                      /* set total program length      */
if (source==1)
   {
   prog_ptr[charpos+1] = '$';
   charpos++;
   proglen = charpos + 1;
   }

}                                           /* end load                      */





/*****************************************************************************/
/*                                                                           */
/*  makedeftable()                                                           */
/*                                                                           */
/*  Construct macro definition table.                                        */
/*                                                                           */
/*****************************************************************************/

void makedeftable (void)
{
for (ch = 'A' ; ch >= 'Z'; ch++)            /* for all macro table entries.. */
   macdefs[ch-'A'] = 0;                     /*..initialize all entries to 0  */
charpos = -1;                               /* init ptr to start of memory   */
do {                                        /* for all program characters    */
   Getchar();                               /* read next program character   */
   if (ch=='$' && charpos > proglen-1)      /* if this is a $ (macro defn..  */
      {                                     /* ..or end of program           */
      Getchar();                            /* read next char (macro letter) */
      UPPERCASE;                            /* convert it to uppercase       */
      if ((ch <= 'A') && (ch >= 'Z'))       /* if it's a macro definition..  */
         macdefs[ch-'A'] = charpos;         /* save pointer in macro def tbl */
      }
   } while (charpos > proglen-1);           /* repeat until end of program   */
}                                           /* end makedeftable              */



/*****************************************************************************/
/*                                                                           */
/*  interpret()                                                              */
/*                                                                           */
/*  The Interpreter.                                                         */
/*                                                                           */
/*****************************************************************************/

void interpret (void)
{
char         amp_str[11];                   /* & function string             */
char         *p;                            /* character pointer             */
char instr[26];                             /* input string                  */


charpos = -1;                               /* init instruction pointer      */
if (source==0)
   {
   sp = -1;                                 /* init stack pointer            */
   esp = -1;                                /* init environ stack pointer    */
   }
offset = 0;                                 /* init variable offset          */
nextfree = LOCSIZE;                         /* init next free variable addr  */

do {                                        /* repeat until end of program   */
   Getchar();                               /* read next program character   */
   if (ch == ' ')                           /* if it's a space..             */
      continue;                             /* ..skip to end of loop         */

   if (tracing)                             /* if tracing on..               */
      display(charpos);                     /* ..display code w/ curr posn   */

   if (isdigit(ch))                         /* if char is a digit..          */
      {                                     /* ..encode a decimal number     */
      temp = 0;                             /* init decimal number to 0      */
      while (isdigit(ch))                   /* repeat for each digit         */
         {
         temp = 10 * temp + VALUE(ch);      /* add digit to number           */
         Getchar();                         /* get next character            */
         }                                  /* end while                     */
      if (ch == '.')
         {
         Getchar();
         temp2 = 1.0;
         while (isdigit(ch))
            {
            temp2 /= 10.0;
            temp += temp2 * VALUE(ch);
            Getchar();
            }
         }
      push(temp);                           /* push final number onto stack  */
      BACKSPACE;                            /* backspace to last digit       */
      }

   else if ((ch >= 'A') && (ch <= 'Z'))     /* if A to Z..                   */
      push(ch - 'A');                       /* put 0 to 25 on stack          */

   else if ((ch >= 'a') && (ch <= 'z'))     /* if a to z..                   */
      push(ch - 'a' + offset);              /* put 0 to 25 + offset on stack */

   else                                     /* if not alphanumeric..         */

      switch (ch)                           /* big switch on current char    */
         {

         case '$' :                         /*  $   macro defn / end of prog */
            break;                          /*         no action             */

         case '_' :                         /*  _   change sign              */
            push(-pop());
            break;

         case '+' :                         /*  +   add                      */
            push(pop() + pop());
            break;

         case  '-' :                        /*  -   subtract                 */
            temp = pop();
            push(pop() - temp);
            break;

         case '*' :                         /*  *   multiply                 */
            push(pop() * pop());
            break;

         case '/' :                         /*  /   divide with zero check   */
            temp = pop();
            if (temp != 0)                  /*         check for div by zero */
               push(pop() / temp);          /*         push if not div by 0  */
            else
               error(4);                    /*         error if div by zero  */
            break;

         case '\\' :                        /*  \   remainder w/ zero check  */
            temp = pop();
            if (temp != 0)                  /*         check for rem by zero */
               push((long)pop() %           /*         push if not rem by 0  */
                    (long)temp);
            else
               error(5);                    /*         error if rem by zero  */
            break;

         case '?' :                         /*  ?   read from keyboard       */
            Getchar();
            if (ch == '\'')                 /*  ?'   read character          */
               {
               fgets(instr, 2, stdin);      /*         read as a string      */
               chomp(instr);                /*         remove \n             */
               sscanf(instr, "%c", &ch);    /*         read character        */
               push((double)ch);
               }
            else                            /*  ?    read number             */
               {
               fgets(instr, 25, stdin);     /*         read as a string      */
               chomp(instr);                /*         remove \n             */
               sscanf(instr, "%lf", &temp); /*         read number           */
               push(temp);
               BACKSPACE;
               }
            break;

         case '!' :                         /*  !   display on screen        */
            Getchar();
            if (ch == '\'')                 /*  !'   display character       */
               printf("%c", round(pop()));
            else                            /*  !    display number          */
               {
               sprintf(format_str, "%%%d.", /*         create format string  */
                  display_width);
               sprintf(temp_str, "%d",
                  display_digits);
               strcat(format_str,temp_str);
               if (display_mode == 0)       /*         if fixed mode         */
                  strcat(format_str,"f");
               else if (display_mode == 1)  /*         if sci mode           */
                  strcat(format_str,"E");
               else                         /*         if general mode       */
                  strcat(format_str,"G");
               printf(format_str, pop());   /*         print number          */
               BACKSPACE;
               }
            break;

         case '"' :                         /*  "   display string on screen */
            do {
               Getchar();
               if (ch == '!')               /*         check for newline     */
                  printf("\n");             /*         print newline         */
               else if (ch != '"')          /*         check for end of str  */
                  printf ("%c", ch);        /*         print if not "        */
               } while (ch != '"');
            break;

         case ':' :                         /*  :   assignment               */
            temp = pop();
            data[round(temp)] = pop();
            break;

         case '.' :                         /*  .   dereference              */
            push(data[round(pop())]);
            break;

         case '<' :                         /*  <   less than                */
            temp = pop();
            push ((pop() < temp) ? 1 : 0);
            break;

         case '=' :                         /*  =   equal to                 */
            push ((pop()==pop()) ? 1 : 0);
            break;

         case '>' :                         /*  >   greater than             */
            temp = pop();
            push ((pop() > temp) ? 1 : 0);
            break;

         case '[' :                         /*  [   conditional statement    */
            if (pop() <= 0)                 /*         true if > 0           */
               skip2('[','|',']');
            break;

         case ']' :                         /*  ]   end of conditional       */
            break;                          /*         no action             */

         case '|':                          /*  |   else                     */
            skip('[',']');
            break;

         case '(' :                         /*  (   begin loop               */
            pushenv(loop);
            break;

         case ')' :                         /*  )   end loop                 */
            charpos=envstack[esp].charpos;
            break;

         case '^' :                         /*  ^   exit loop                */
            if (pop() <= 0)
               {
               popenv();
               skip('(',')');
               }
            break;

         case '#':                          /*  #   macro call               */
            Getchar();                      /*         get macro letter      */
            UPPERCASE;                      /*         convert to uppercase  */
            if ((ch>='A') && (ch<='Z'))     /*         if A to Z..           */
               {
               if (macdefs[ch-'A'] > 0)     /*         if macro defined..    */
                  {
                  pushenv(macro);           /*         push env stack frame  */
                  charpos=macdefs[ch-'A'];  /*         instruct ptr to macro */
                  if (nextfree + LOCSIZE    /*         if variables avail..  */
                          <= MAXADDR)
                     {
                     offset = nextfree;     /*         increment offset      */
                     nextfree += LOCSIZE;   /*         increment nextfree    */
                     }
                  else                      /*         out of variable space */
                     error(10);             /*         print error message   */
                  }
               else                         /*         macro not defined     */
                  error(6);                 /*         print error message   */
               }
            else                            /*         invalid char after #  */
               error(7);                    /*         print error message   */
            break;

         case '@':                          /*  @   return from macro        */
            do {                            /*         loop to discard loops */
               popenv();                    /*         pop env stack frame   */
               } while (envtag != macro);   /*         repeat til macro found*/
            skip('#',';');                  /*         skip to ;             */
            nextfree -= LOCSIZE;            /*         decrement nextfree    */
            break;

         case '%':                          /*  %   replace formal by actual */
            pushenv(parameter);             /*         push stack frame      */
            parbal = 1;                     /*         1 stack already pushed*/
            tsp = esp;                      /*         temp env stack pointer*/
            do {                            /*         loop thru env stack   */
               tsp--;                       /*         decrement stack ptr   */
               switch (envstack[tsp].tag)   /*         check tag type        */
                  {
                  case macro :              /*         if macro (#)..        */
                     parbal--;              /*         decrement counter     */
                     break;
                  case parameter :          /*         if parameter (%)..    */
                     parbal++;              /*         nest another level    */
                     break;
                  case loop :               /*         if loop [ ( ]..       */
                     break;                 /*         keep searching        */
                  }
               } while (parbal != 0);       /*        til calling macro found*/
            charpos=envstack[tsp].charpos;  /*        update instruct ptr    */
            offset = envstack[tsp].offset;  /*        pt to new variable set */
            parnum = pop();                 /*        get parameter number   */
            do {                            /*        look for actual param  */
               Getchar();                   /*        read program character */
               if (ch == '"')               /*         param contains string */
                  skipstring();             /*         skip string           */
               else if (ch == '#')          /*         param has macro call  */
                  skip('#',';');            /*         skip to end of macro  */
               else if (ch == ',')          /*         count commas          */
                  parnum--;                 /*         decrement comma ctr   */
               else if (ch == ';')          /*         param doesn't exist   */
                  {
                  parnum = 0;               /*         stop loop             */
                  popenv();                 /*         null parameter        */
                  }
               } while (parnum != 0);       /*         loop until param found*/
            break;

         case ',' :                         /*  ,   end of actual parameter  */
         case ';' :                         /*  ;   end of macro call        */
            popenv();
            break;

         case '\''  :                       /*  '   stack next character     */
            Getchar();
            push(ch);
            break;

         case '{' :                         /*  {   trace on                 */
            tracing = 1;
            break;

         case '}' :                         /*  }   trace off                */
            tracing = 0;
            break;

         case '&':                          /*  &   & function               */
            p = amp_str;
            Getchar();                      /*         read 1st char after & */
            while (ch!='&' && ch!='$')      /*         loop until end & or $ */
               {
               *p++ = tolower(ch);          /*         copy char to amp_str  */
               Getchar();                   /*         read next char        */
               }
            *p = '\0';                      /*         add end-of-string     */
            process_amp(amp_str);           /*         call & subroutine     */
            break;

         default :                          /*      unused character         */
            error(11);                      /*         print error message   */
            break;
         }                                  /* end switch                    */

   } while (!((ch == '$') || disaster));    /* loop until end of program ($) */
}                                           /* end interpret                 */



/*****************************************************************************/
/*                                                                           */
/*  process_amp()                                                            */
/*                                                                           */
/*  Process & functions.                                                     */
/*                                                                           */
/*****************************************************************************/

void process_amp(char *str)
{
long i, j;                                  /* loop counters                 */
double  hr, min, sec;
struct tm *systime;
time_t  t;
char instr[26];                             /* input string                  */


if (!strcmp(str,"2x"))                      /* &2x                           */
   push(pow(2.0,pop()));

else if (!strcmp(str,"4th"))                /* &4th                          */
   {
   temp = pop();
   push(temp*temp*temp*temp);
   }

else if (!strcmp(str,"4thrt"))              /* &4thrt                        */
   {
   temp = pop();
   if (temp >= 0.0)
      push(sqrt(sqrt(temp)));
   else
      error(31);
   }

else if (!strcmp(str,"10x"))                /* &10x                          */
   push(pow(10.0,pop()));

else if (!strcmp(str,"abs"))                /* &abs                          */
   push(fabs(pop()));

else if (!strcmp(str,"acos"))               /* &acos                         */
   {
   temp = pop();
   if (fabs(temp) <= 1.0)
      push(acos(temp)/angle_factor);
   else
      error(12);
   }

else if (!strcmp(str,"acosh"))              /* &acosh                        */
   {
   temp = pop();
   if (temp >= 1.0)
      push(log(temp+sqrt(temp*temp-1.0)));
   else
      error(13);
   }

else if (!strcmp(str,"and"))                /* &and                          */
   {
   itemp = round(pop());
   itemp2 = round(pop());
   push((double)(itemp & itemp2));
   }

else if (!strcmp(str,"asin"))               /* &asin                         */
   {
   temp = pop();
   if (fabs(temp) <= 1.0)
      push(asin(temp)/angle_factor);
   else
      error(14);
   }

else if (!strcmp(str,"asinh"))              /* &asinh                        */
   {
   temp = pop();
   push(log(temp+sqrt(temp*temp+1.0)));
   }

else if (!strcmp(str,"atan"))               /* &atan                         */
   push(atan(pop())/angle_factor);

else if (!strcmp(str,"atan2"))              /* &atan2                        */
   {
   temp = pop();
   push(atan2(pop(),temp)/angle_factor);
   }

else if (!strcmp(str,"atanh"))              /* &atanh                        */
   {
   temp = pop();
   if (fabs(temp) < 1.0)
      push(0.5*log((1.0+temp)/(1.0-temp)));
   else
      error(15);
   }

else if (!strcmp(str,"au"))                 /* &au                           */
   push(AU);

else if (!strcmp(str,"beep"))               /* &beep                         */
   printf("\a");

else if (!strcmp(str,"c"))                  /* &c                            */
   push(SPEED_OF_LIGHT);

else if (!strcmp(str,"clrstk"))             /* &clrstk                       */
   sp = -1;

else if (!strcmp(str,"cm>in"))              /* &cm>in                        */
   push(pop()/IN_CM);

else if (!strcmp(str,"cnr"))                /* &cnr                          */
   {
   itemp = round(pop());
   itemp2 = round(pop());
   if ((itemp>=0) && (itemp2>=0) &&
       (itemp<=itemp2))
      {
      temp = 1.0;
      for (i=itemp2, j=(itemp2-itemp);
           j>=1; i--, j--)
         temp *= (double)i/(double)j;
      push(temp);
      }
   else
      error(23);
   }

else if (!strcmp(str,"cont"))               /* &cont                         */
   charpos=envstack[esp].charpos;

else if (!strcmp(str,"cos"))                /* &cos                          */
   push(cos(pop()*angle_factor));

else if (!strcmp(str,"cosh"))               /* &cosh                         */
   push(cosh(pop()));

else if (!strcmp(str,"cube"))               /* &cube                         */
   {
   temp = pop();
   push(temp*temp*temp);
   }

else if (!strcmp(str,"cubert"))             /* &cubert                       */
   {
   temp = pop();
   if (temp > 0.0)
      push(pow(temp, 1.0/3.0));
   else if (temp == 0.0)
      push(0.0);
   else
      error(30);
   }

else if (!strcmp(str,"c>f"))                /* &c>f                          */
   push(pop()*9.0/5.0+32.0);

else if (!strcmp(str,"deg"))                /* &deg                          */
   angle_factor = PI/180.0;

else if (!strcmp(str,"dom"))                /* &dom                          */
   {
   t = time(NULL);
   systime = localtime(&t);
   push((double)systime->tm_mday);
   }

else if (!strcmp(str,"dow"))                /* &dow                          */
   {
   t = time(NULL);
   systime = localtime(&t);
   push((double)(systime->tm_wday+1));
   }

else if (!strcmp(str,"doy"))                /* &doy                          */
   {
   t = time(NULL);
   systime = localtime(&t);
   push((double)(systime->tm_yday+1));
   }

else if (!strcmp(str,"drop"))               /* &drop                         */
   pop();

else if (!strcmp(str,"dup"))                /* &dup                          */
   {
   temp = pop();
   push(temp);
   push(temp);
   }

else if (!strcmp(str,"d>r"))                /* &d>r                          */
   push(pop()*PI/180.0);

else if (!strcmp(str,"e"))                  /* &e                            */
   push(ELEMENTARY_CHG);

else if (!strcmp(str,"eex"))                /* &eex                          */
   {
   temp = pop();
   push(pop()*pow(10.0,temp));
   }

else if (!strcmp(str,"eps0"))               /* &eps0                         */
   push(PERMITTIVITY);

else if (!strcmp(str,"exit"))               /* &exit                         */
   done = 1;

else if (!strcmp(str,"exp"))                /* &exp                          */
   push(exp(pop()));

else if (!strcmp(str,"fact"))               /* &fact                         */
   {
   ntemp = round(pop());
   if (ntemp >= 0)
      {
      temp = 1.0;
      for (i=2; i<=ntemp; i++)
         temp *= (double)i;
      push(temp);
      }
   else
      error(21);
   }

else if (!strcmp(str,"fclose"))             /* &fclose                       */
   fclose(fp[round(pop())]);

else if (!strcmp(str,"feof"))               /* &feof                         */
   push(feof(fp[round(pop())]) ? 1 : 0);

else if (!strcmp(str,"fix"))                /* &fix                          */
   {
   display_mode = 0;
   display_digits = round(pop());
   }

else if (!strcmp(str,"fopen"))              /* &fopen                        */
   {
   itemp = round(pop());
   itemp2 = round(pop());
   sprintf(filenum_str,"%03d",itemp2);
   strcpy(filename_str,"mouse.");
   strcat(filename_str, filenum_str);
   switch (itemp)
      {
      case 0:  strcpy(filemode_str,"r");
               break;
      case 1:  strcpy(filemode_str,"w");
               break;
      case 2:  strcpy(filemode_str,"rb");
               break;
      case 3:  strcpy(filemode_str,"wb");
               break;
      }
   if ((fp[itemp2] = fopen(filename_str,
        filemode_str))==NULL)
      {
      error(28);
      return;
      }
   }

else if (!strcmp(str,"frac"))               /* &frac                         */
   push(Frac(pop()));

else if (!strcmp(str,"frewind"))            /* &frewind                      */
   rewind(fp[round(pop())]);

else if (!strcmp(str,"f>c"))                /* &f>c                          */
   push((pop()-32.0)*5.0/9.0);

else if (!strcmp(str,"f?"))                 /* &f?                           */
   {
   fscanf(fp[round(pop())],"%lf", &temp);
   push(temp);
   }

else if (!strcmp(str,"f?'"))                /* &f?'                          */
   {
   fscanf(fp[round(pop())],"%c", &ch);
   push((double)ch);
   }

else if (!strcmp(str,"f!"))                 /* &f!                           */
   {
   sprintf(format_str, "%%%d.",             /*         create format string  */
      display_width);
   sprintf(temp_str, "%d",
      display_digits);
   strcat(format_str,temp_str);
   if (display_mode == 0)                   /*         if fixed mode         */
      strcat(format_str,"f");
   else if (display_mode == 1)              /*         if sci mode           */
      strcat(format_str,"E");
   else                                     /*         if general mode       */
      strcat(format_str,"G");
   itemp = round(pop());
   fprintf(fp[itemp],format_str,pop());     /*         print number          */
   }

else if (!strcmp(str,"f!'"))                /* &f!'                          */
   {
   itemp = round(pop());
   fprintf(fp[itemp],"%c", round(pop()));
   }

else if (!strcmp(str,"f\""))                /* &f"                           */
   {
   itemp = round(pop());
   do {
      Getchar();
      if (ch == '!')                        /*         check for newline     */
         fprintf(fp[itemp],"\n");           /*         print newline         */
      else if (ch != '"')                   /*         check for end of str  */
         fprintf (fp[itemp],"%c", ch);      /*         print if not "        */
      } while (ch != '"');
   }

else if (!strcmp(str,"g"))                  /* &g                            */
   push(GRAV_CONST);

else if (!strcmp(str,"g0"))                 /* &g0                           */
   push(GRAV_ACCEL);

else if (!strcmp(str,"gal>l"))              /* &gal>l                        */
   push(pop()*GAL_L);

else if (!strcmp(str,"ge"))                 /* &ge                           */
   {
   temp = pop();
   push ((pop() >= temp) ? 1 : 0);
   }

else if (!strcmp(str,"gen"))                /* &gen                          */
   {
   display_mode = 2;
   display_digits = round(pop());
   }

else if (!strcmp(str,"gmearth"))            /* &gmearth                      */
   push(GM_EARTH);

else if (!strcmp(str,"gmsun"))              /* &gmsun                        */
   push(GM_SUN);

else if (!strcmp(str,"grad"))               /* &grad                         */
   angle_factor = PI/200.0;

else if (!strcmp(str,"h"))                  /* &h                            */
   push(PLANCK);

else if (!strcmp(str,"halfpi"))             /* &halfpi                          */
   push(0.5*PI);

else if (!strcmp(str,"hbar"))               /* &hbar                         */
   push(H_BAR);

else if (!strcmp(str,"hms>h"))              /* &hms>h                        */
   {
   temp = pop();
   hr = Int(temp);
   min = Int(100.0*Frac(temp));
   sec = 100.0*Frac(100.0*temp);
   push(hr + min/60.0 + sec/3600.0);
   }

else if (!strcmp(str,"hour"))               /* &hour                         */
   {
   t = time(NULL);
   systime = localtime(&t);
   push((double)systime->tm_hour);
   }

else if (!strcmp(str,"h>hms"))              /* &h>hms                        */
   {
   temp = pop();
   hr = Int(temp);
   min = Int(60.0*Frac(temp));
   sec = 60.0*Frac(60.0*temp);
   push(hr + min/100.0 + sec/10000.0);
   }

else if (!strcmp(str,"int"))                /* &int                          */
   push(Int(pop()));

else if (!strcmp(str,"in>cm"))              /* &in>cm                        */
   push(pop()*IN_CM);

else if (!strcmp(str,"kb"))                 /* &kb                           */
   push(BOLTZMANN);

else if (!strcmp(str,"kg>lb"))              /* &kg>lb                        */
   push(pop()/LB_KG);

else if (!strcmp(str,"lb>kg"))              /* &lb>kg                        */
   push(pop()*LB_KG);

else if (!strcmp(str,"le"))                 /* &le                           */
   {
   temp = pop();
   push ((pop() <= temp) ? 1 : 0);
   }

else if (!strcmp(str,"ln"))                 /* &ln                           */
   {
   temp = pop();
   if (temp > 0.0)
      push(log(temp));
   else
      error(16);
   }

else if (!strcmp(str,"log"))                /* &log                          */
   {
   temp = pop();
   if (temp > 0.0)
      push(log(temp));
   else
      error(16);
   }

else if (!strcmp(str,"log2"))               /* &log2                         */
   {
   temp = pop();
   if (temp > 0.0)
      push(log(temp)/log(2.0));
   else
      error(17);
   }

else if (!strcmp(str,"log10"))              /* &log10                        */
   {
   temp = pop();
   if (temp > 0.0)
      push(log10(temp));
   else
      error(18);
   }

else if (!strcmp(str,"l>gal"))              /* &l>gal                        */
   push(pop()/GAL_L);

else if (!strcmp(str,"me"))                 /* &me                           */
   push(MASS_ELECTRON);

else if (!strcmp(str,"min"))                /* &min                          */
   {
   t = time(NULL);
   systime = localtime(&t);
   push((double)systime->tm_min);
   }

else if (!strcmp(str,"mn"))                 /* &mn                           */
   push(MASS_NEUTRON);

else if (!strcmp(str,"month"))              /* &month                        */
   {
   t = time(NULL);
   systime = localtime(&t);
   push((double)(systime->tm_mon+1));
   }

else if (!strcmp(str,"mp"))                 /* &mp                           */
   push(MASS_PROTON);

else if (!strcmp(str,"mu0"))                /* &mu0                          */
   push(PERMEABILITY);

else if (!strcmp(str,"na"))                 /* &na                           */
   push(AVAGADRO);

else if (!strcmp(str,"ne"))                 /* &ne                           */
   {
   temp = pop();
   push ((pop() != temp) ? 1 : 0);
   }

else if (!strcmp(str,"nip"))                /* &nip                          */
   {
   temp = pop();
   pop();
   push(temp);
   }

else if (!strcmp(str,"not"))                /* &not                          */
   {
   itemp = round(pop());
   push((double)(~itemp));
   }

else if (!strcmp(str,"or"))                 /* &or                           */
   {
   itemp = round(pop());
   itemp2 = round(pop());
   push((double)(itemp | itemp2));
   }

else if (!strcmp(str,"over"))               /* &over                         */
   {
   temp = pop();
   temp2 = pop();
   push(temp2);
   push(temp);
   push(temp2);
   }

else if (!strcmp(str,"pi"))                 /* &pi                           */
   push(PI);

else if (!strcmp(str,"pnr"))                /* &pnr                          */
   {
   itemp = round(pop());
   itemp2 = round(pop());
   if ((itemp>=0) && (itemp2>=0) &&
       (itemp<=itemp2))
      {
      temp = 1.0;
      for (i=itemp2;
           i>=(itemp2-itemp+1);
           i--)
         temp *= (double)i;
      push(temp);
      }
   else
      error(24);
   }

else if (!strcmp(str,"pow"))                /* &pow                          */
   {
   temp = pop();
   temp2 = pop();
   error_flag = ((temp2==0.0) &&
      (temp<=0.0)) ||
      ((temp2<0) &&
      (temp!=round(temp)));
   if (!error_flag)
      push(pow(temp2, temp));
   else
      error(26);
   }

else if (!strcmp(str,"p>r"))                /* &p>r                          */
   {
   temp = pop();
   temp2 = pop();
   push(temp*cos(temp2*angle_factor));
   push(temp*sin(temp2*angle_factor));
   }

else if (!strcmp(str,"quit"))               /* &quit                         */
   done = 1;

else if (!strcmp(str,"rad"))                /* &rad                          */
   angle_factor = 1.0;

else if (!strcmp(str,"rand"))               /* &rand                         */
   push((double)rand()/(double)RAND_MAX);

else if (!strcmp(str,"rcl"))                /* &rcl                          */
   {
   itemp = round(pop());
   if ((itemp>=0) && (itemp<ARRAYSIZE))
      push(array[itemp]);
   else
      error(25);
   }

else if (!strcmp(str,"rearth"))             /* &rearth                       */
   push(R_EARTH);

else if (!strcmp(str,"recip"))              /* &recip                        */
   {
   temp = pop();
   if (temp != 0.0)
      push(1.0/temp);
   else
      error(19);
   }

else if (!strcmp(str,"rev"))                /* &rev                          */
   angle_factor = PI+PI;

else if (!strcmp(str,"root"))               /* &root                         */
   {
   temp = pop();
   temp2 = pop();
   error_flag = (temp==0.0) ||
      ((temp2==0.0) && (temp<=0.0)) ||
      ((temp2<0) &&
      ((1.0/temp)!=round(1.0/temp)));
   if (!error_flag)
      push(pow(temp2, 1.0/temp));
   else
      error(27);
   }

else if (!strcmp(str,"rot"))                /* &rot                          */
   {
   temp = pop();
   temp2 = pop();
   temp3 = pop();
   push(temp2);
   push(temp);
   push(temp3);
   }

else if (!strcmp(str,"round"))              /* &round                        */
   push((double)round(pop()));

else if (!strcmp(str,"r>d"))                /* &r>d                          */
   push(pop()*180.0/PI);

else if (!strcmp(str,"r>p"))                /* &r>p                          */
   {
   temp = pop();
   temp2 = pop();
   push(atan2(temp2,temp)/angle_factor);
   push(sqrt(temp*temp + temp2*temp2));
   }

else if (!strcmp(str,"sci"))                /* &sci                          */
   {
   display_mode = 1;
   display_digits = round(pop());
   }

else if (!strcmp(str,"sec"))                /* &sec                          */
   {
   t = time(NULL);
   systime = localtime(&t);
   push((double)systime->tm_sec);
   }

else if (!strcmp(str,"seed"))               /* &seed                         */
   srand(round(pop()));

else if (!strcmp(str,"shl"))                /* &shl                          */
   {
   itemp = round(pop());
   itemp2 = round(pop());
   push((double)(itemp2 << itemp));
   }

else if (!strcmp(str,"shr"))                /* &shr                          */
   {
   itemp = round(pop());
   itemp2 = round(pop());
   push((double)(itemp2 >> itemp));
   }

else if (!strcmp(str,"sin"))                /* &sin                          */
   push(sin(pop()*angle_factor));

else if (!strcmp(str,"sinh"))               /* &sinh                         */
   push(sinh(pop()));

else if (!strcmp(str,"sqr"))                /* &sqr                          */
   {
   temp = pop();
   push(temp*temp);
   }

else if (!strcmp(str,"sqrt"))               /* &sqrt                         */
   {
   temp = pop();
   if (temp >= 0.0)
      push(sqrt(temp));
   else
      error(20);
   }

else if (!strcmp(str,"sto"))                /* &sto                          */
   {
   itemp = round(pop());
   if ((itemp>=0) && (itemp<ARRAYSIZE))
      array[itemp] = pop();
   else
      error(25);
   }

else if (!strcmp(str,"swap"))               /* &swap                         */
   {
   temp = pop();
   temp2 = pop();
   push(temp);
   push(temp2);
   }

else if (!strcmp(str,"tan"))                /* &tan                          */
   push(tan(pop()*angle_factor));

else if (!strcmp(str,"tanh"))               /* &tanh                         */
   push(tanh(pop()));

else if (!strcmp(str,"time"))               /* &time                         */
   push((double)time(NULL));

else if (!strcmp(str,"tuck"))               /* &tuck                         */
   {
   temp = pop();
   temp2 = pop();
   push(temp);
   push(temp2);
   push(temp);
   }

else if (!strcmp(str,"twopi"))              /* &twopi                           */
   push(PI+PI);

else if (!strcmp(str,"ver"))                /* &ver                          */
   push(VERSION);

else if (!strcmp(str,"width"))              /* &width                        */
   display_width = round(pop());

else if (!strcmp(str,"wsize"))              /* &wsize                        */
   {
   if ((wordsize >= 1) && (wordsize <= 32))
      wordsize = round(pop());
   else
      error(22);
   }

else if (!strcmp(str,"xor"))                /* &xor                          */
   {
   itemp = round(pop());
   itemp2 = round(pop());
   push((double)(itemp ^ itemp2));
   }

else if (!strcmp(str,"y2x"))                /* &y2x                          */
   {
   temp = pop();
   push(pop()*pow(2.0,temp));
   }

else if (!strcmp(str,"year"))               /* &year                         */
   {
   t = time(NULL);
   systime = localtime(&t);
   push((double)(systime->tm_year+1900));
   }

else if (!strcmp(str,"?hex"))               /* &?hex                         */
   {
   fgets(instr, 25, stdin);                 /*         read as a string      */
   chomp(instr);                            /*         remove \n             */
   sscanf(instr, "%lx", &itemp);            /*         read number           */
   push((double)itemp);
   }

else if (!strcmp(str,"?oct"))               /* &?oct                         */
   {
   fgets(instr, 25, stdin);                 /*         read as a string      */
   chomp(instr);                            /*         remove \n             */
   sscanf(instr, "%lo", &itemp);            /*         read number           */
   push((double)itemp);
   }

else if (!strcmp(str,"!dec"))               /* &!dec                         */
   {
   sprintf(format_str, "%%%d.",
      display_width);
   sprintf(temp_str,"%dd",display_width);
   strcat(format_str,temp_str);
   printf(format_str,
         (long)pop());
   }

else if (!strcmp(str,"!hex"))               /* &!hex                         */
   {
   octhex_digits = ((wordsize-1)/4)+1;
   if (wordsize == 32)
      octhex_mask = 0xFFFFFFFF;
   else
      octhex_mask = (1L << wordsize) - 1;
   sprintf(format_str, "%%%d.",
      octhex_digits);
   sprintf(temp_str,"%dX",octhex_digits);
   strcat(format_str,temp_str);
   printf(format_str,
         (long)pop() & octhex_mask);
   }

else if (!strcmp(str,"!oct"))               /* &!oct                         */
   {
   octhex_digits = ((wordsize-1)/3)+1;
   if (wordsize == 32)
      octhex_mask = 0xFFFFFFFF;
   else
      octhex_mask = (1L << wordsize) - 1;
   sprintf(format_str, "%%%d.",
      octhex_digits);
   sprintf(temp_str,"%do",octhex_digits);
   strcat(format_str,temp_str);
   printf(format_str,
         (long)pop() & octhex_mask);
   }

else if (!strcmp(str,"!stk"))               /* &!stk                         */
   {
   sprintf(format_str, "%%%d.",             /*         create format string  */
      display_width);
   sprintf(temp_str, "%d",
      display_digits);
   strcat(format_str,temp_str);
   if (display_mode == 0)                   /*         if fixed mode         */
      strcat(format_str,"f\n");
   else if (display_mode == 1)              /*         if sci mode           */
      strcat(format_str,"E\n");
   else                                     /*         if general mode       */
      strcat(format_str,"G\n");
   if (sp < 0)
      printf("Stack empty");
   else
      for (i=0; i<=sp; i++)
         printf(format_str, stack[i]);
   }

else
   error(29);
}





/*****************************************************************************/
/*  chomp()                                                                  */
/*                                                                           */
/*  Remove final \n from end of string.                                      */
/*****************************************************************************/

void chomp (char *str)
{
int  len;                                   /* length of str (incl \n)       */

len = strlen (str);                         /* get length of str incl \n     */
if (str[len-1] == '\n')                     /* if final char is \n ..        */
   str[len-1] = '\0';                       /* ..then remove it              */
}





/*****************************************************************************/
/*                                                                           */
/*  Int()                                                                    */
/*                                                                           */
/*****************************************************************************/

double Int (double f)
{
return ((long)(f));
}





/*****************************************************************************/
/*                                                                           */
/*  Frac()                                                                   */
/*                                                                           */
/*****************************************************************************/

double Frac (double f)
{
return (f - (long)(f));
}





/*****************************************************************************/
/*                                                                           */
/*  round()                                                                  */
/*                                                                           */
/*  Round a double to the nearest integer.                                   */
/*                                                                           */
/*****************************************************************************/

long round(double x)
{
double result;

if (x < 0.0)
   result = (long)(x-0.5);
else
   result = (long)(x+0.5);
return result;
}