/
Crimson2/alias/
Crimson2/area.tmp/
Crimson2/area.tmp/AnomalySpaceDock/
Crimson2/area.tmp/AnomalyStation/
Crimson2/area.tmp/AntHill/
Crimson2/area.tmp/ArcticTerrarium/
Crimson2/area.tmp/BuilderCity/
Crimson2/area.tmp/Dungeon/
Crimson2/area.tmp/MiningDock/
Crimson2/area.tmp/PipeSystem/
Crimson2/area.tmp/RattArea/
Crimson2/area.tmp/RobotFactory/
Crimson2/area.tmp/SilverDale/
Crimson2/area.tmp/StarshipFearless/
Crimson2/area.tmp/StationConduits/
Crimson2/area.tmp/TerrariumAlpha/
Crimson2/area.tmp/TerrariumBeta/
Crimson2/area.tmp/TestArea/
Crimson2/area.tmp/Void/
Crimson2/area/
Crimson2/area/AnomalySpaceDock/
Crimson2/area/AnomalyStation/
Crimson2/area/MiningDock/
Crimson2/area/PipeSystem/
Crimson2/area/SilverDale/
Crimson2/area/StationConduits/
Crimson2/area/Void/
Crimson2/board/
Crimson2/clone/
Crimson2/lib/
Crimson2/mole/
Crimson2/mole/mole_src/HELP/
Crimson2/player/
Crimson2/util/
Crimson2/wldedit/
Crimson2/wldedit/res/
/* compile.c
 * Written by B. Cameron Lesiuk, 1995
 * Written for use with Crimson2 MUD (written/copyright Ryan Haksi 1995).
 * This source is proprietary. Use of this code without permission from 
 * Ryan Haksi or Cam Lesiuk is strictly prohibited. 
 * 
 * (wi961@freenet.victoria.bc.ca)
 */  

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "crimson2.h"
#include "macro.h"
#include "log.h"
#include "mem.h"
#include "str.h"
#include "ini.h"
#include "send.h"
#include "extra.h"
#include "thing.h"
#include "index.h"
#include "code.h"
#include "codestuf.h"
#include "interp.h"
#include "function.h"
#include "compile.h"

/** Define Statments **/

/* compile-time options */

/* Compile defaults */
#define CSTACK_SIZE      (1<<12)  /* size of stack */
#define CSTACK_FRAME     (CSTACK_SIZE>>1) /* # stack frames */
#define CCODE_SIZE       (1<<12) /* size of compiled code buffer */

/* These statements are the return codes for CompileReadNext (CRN) */
#define CRN_EOF      0x01
#define CRN_OPERATOR 0x02
#define CRN_COMMENT  0x03
#define CRN_COMMA    0x04
#define CRN_PERIOD   0x05
#define CRN_OSBRAC   0x06 /* [ bracket set  (Square bracket) */
#define CRN_CSBRAC   0x07
#define CRN_OBRAC    0x08 /* { brace set (bracket) */
#define CRN_CBRAC    0x09
#define CRN_ORBRAC   0x0A /* ( parenthesis set (Round bracket) */
#define CRN_CRBRAC   0x0B
#define CRN_STRING   0x0C /* string */
#define CRN_SEMI     0x0D /* semicolon */
#define CRN_NUMBER   0x0E /* numerical constant */
#define CRN_LABEL    0x0F /* Label - this is further divided, but not in CRN */
/* CRN_LABEL is interpreted into the following: */
#define CRN_RESWRD   0x10 /* reserved word (if, while, int) */
#define CRN_FUNCT    0x11 /* function */
#define CRN_VAR      0x12 /* variable */
#define CRN_UNDEF    0x13 /* undefined label */

/* Compile uses these flags in cValidMask to determine which ops are valid */
/* NOTE: the value must correspond to the CRN return codes defined above */
#define CV_EOF       (1<<CRN_EOF)
#define CV_OPERATOR  (1<<CRN_OPERATOR)
#define CV_COMMENT   (1<<CRN_COMMENT)
#define CV_COMMA     (1<<CRN_COMMA)
#define CV_PERIOD    (1<<CRN_PERIOD)
#define CV_OSBRAC    (1<<CRN_OSBRAC)
#define CV_CSBRAC    (1<<CRN_CSBRAC)
#define CV_OBRAC     (1<<CRN_OBRAC)
#define CV_CBRAC     (1<<CRN_CBRAC)
#define CV_ORBRAC    (1<<CRN_ORBRAC)
#define CV_CRBRAC    (1<<CRN_CRBRAC)
#define CV_STRING    (1<<CRN_STRING)
#define CV_SEMI      (1<<CRN_SEMI)
#define CV_NUMBER    (1<<CRN_NUMBER)
#define CV_LABEL     (1<<CRN_LABEL)
#define CV_RESWRD    (1<<CRN_RESWRD)
#define CV_FUNCT     (1<<CRN_FUNCT)
#define CV_VAR       (1<<CRN_VAR)
#define CV_UNDEF     (1<<CRN_UNDEF)

/* return codes for CompileReadNext */
#define CRN_RC_OK     0
#define CRN_RC_EOF    1
#define CRN_RC_SYNTAX 2

/* Compile State machine- major state codes */
#define CS_END        0xFFFFF000 /* end of program (looking for EOF) */
#define CS_START      0x00000100 /* start of program (looking for { ) */
#define CS_MAIN       0x00010000 /* Main State Machine Loop */
#define CS_VAR        0x00018000 /* Variable declarations */
#define CS_IF         0x00020000 /* Reserved word "if" expr handler */
#define CS_WHILE      0x00030000 /* Reserved word "while" expr handler */
#define CS_STOP       0x00040000 /* Reserved word "stop" expr handler */
#define CS_GET_EXP    0x00050000 /* Get Expression state machine loop */
#define CS_FUNCTION   0x00060000 /* handles function calls etc. */
#define CS_GET_NEXT   0x00070000 /* gets next Var or Function */

/* Compile state machine - expression type flag values 
 * Confused? Here, let me explain:
 * In the following example:
 *          a=b+c;
 * `a' is an lvalue. lvalues must be variables. `1=b+c' is invalid!!!
 * `b+c' is an rvalue. rvalues can be variables, functions, constants, etc.
 * `a=b+c' is an expression. It can also be used as an rvalue.
 *
 * In the following example:
 *          function(a,b+c);
 * `a' is a rvalue. Functions must be passed rvalues. Don't worry though,
 *     just about everything can be used as an rvalue.
 * `b+c' is also an rvalue.
 * `function' is a function call. It's also an rvalue.
 *
 * Got it?
 */
#define CEFLAG_EXP    1<<0 /* expression */
#define CEFLAG_RVALUE 1<<1 /* rvalue */
#define CEFLAG_LVALUE 1<<2 /* lvalue */
#define CEFLAG_FUNCT  1<<3 /* function */
/* Compile state machine - data type flag values */
#define CDFLAG_UNDEF  (1<<CDT_UNDEF)
#define CDFLAG_NULL   (1<<CDT_NULL)
#define CDFLAG_STR    (1<<CDT_STR)
#define CDFLAG_INT    (1<<CDT_INT)
#define CDFLAG_THING  (1<<CDT_THING)
#define CDFLAG_EXTRA  (1<<CDT_EXTRA)
#define CDFLAG_EXIT   (1<<CDT_EXIT)

/* Structures and unions */
typedef union CompileUnion {
  UWORD cuPointer;    /* pointer to a variable, function, string, etc */
  SBYTE cuSByte;      /* signed byte */
  WORD  cuSWord;      /* signed word */
  LWORD cuSLWord;     /* 32-bit signed data */
  ULWORD cuUnsigned;  /* 32-bit unsigned data */
  ULWORD cuState;     /* state for state machine */
  ULWORD cuValid;     /* valid flags for state machine */
  UWORD cuFunction;   /* function being called */
  UWORD cuVar;        /* variable being referenced */
  BYTE cuParamType;   /* parameter type for functions */
  ULWORD cuVarFlag;   /* Data Type flag */
  ULWORD cuExpFlag;   /* Expression Type Flag */
  UWORD cuOffset;     /* Offset - a saved location in program */
  void *cuPtr;        /* pointer data - used for things like COP_PUSHPT */
} COMPILEUNION;

typedef struct CompileData { /* this type is used for the stack, for */
  BYTE cInstruction;         /* parameter passing, etc. etc. etc.    */
  COMPILEUNION cData;
} COMPILEDATA;

typedef struct CharMark { /* used by CompileReadNext */
  BYTE *SCMstr;
  WORD SCMoperation;
} CHARMARK;

typedef struct CompileVarTableType { /* variable table */
  BYTE cText[CVAR_NAME_LENGTH]; /* variable name */
  WORD cType;     /* variable type */
  WORD cDomain;   /* variable domain */
  ULWORD cOffset;   /* location in variable table specified by cDomain */
} COMPILEVARTABLETYPE;

typedef struct CompileReservedWord { /* table entry for reserved words */
  BYTE cText[10]; /* reserved word name */
} COMPILERESERVEDWORD;

/* Globals - used everywhere in compile.c */
WORD cComp;   /* pointer to where we are in the compiled code */
BYTE *cCompBuf;/* compile buffer */
LWORD cCompBufSizeByte;
BYTE *cSrc;    /* pointer to where we are in the soure (text) code */
BYTE *cSrcTop; /* pointer to top of soure (text) code */
BYTE *cLastSrc;/* pointer to the last place we were in the source code */
BYTE *cNextSrc;/* pointer to the next place in source code - if we know it. */
COMPILEDATA *cStack; /* stack used by all compile routines */
COMPILEDATA *cStackP; /* pointer to current stack - used to access stack */
LWORD cStackSizeByte; /* current stack size in bytes */
WORD cSP;      /* current stack pointer */
WORD cSF;      /* current stack frame */
WORD *cSFP;    /* stack frame base position pointer*/
LWORD cSFPSizeByte; /* current stack frame size in bytes */
THING *cThing;  /* thing to report stuff (like errors) to */
WORD cRc;       /* return code - use everywhere */
UWORD cCurVar;   /* current variable we're at in the variable table */
COMPILEVARTABLETYPE cVarTable[CMAX_VAR]; /* variable table */
LWORD cReqStackSize;    /* size of required Interp.c stack required. */ 
LWORD cReqStackSizeMax; /* Max value of cReqStackSize */
LWORD cReqParameter;   /* Max parameters required by Interp */

/* Macros - used everywhere in compile.c */
#define COMPILE_GETLASTSRC()  \
do { \
  if ((cRc=CompileGetLastSrc())) \
    return (cRc); \
} while(0)

#define COMPILE_ADDBYTE(byte) \
do { \
  if ((cRc=CompileAddByte(byte))) \
    return (cRc); \
} while(0)

#define COMPILE_STRING(ptr)   \
do { \
  if ((cRc=CompileString(ptr))) \
    return (cRc); \
} while(0)

#define COMPILE_PUSH(ptr)  \
do { \
  if ((cRc=CompilePush(ptr))) \
    return (cRc); \
} while(0)

#define COMPILE_PULL(ptr)  \
do { \
  if ((cRc=CompilePull(ptr))) \
    return (cRc); \
} while(0)

#define COMPILE_UPSTACK()  \
do { \
  if ((cRc=CompileUpStack())) \
    return (cRc); \
} while(0)

#define COMPILE_DOWNSTACK()  \
do { \
  if ((cRc=CompileDownStack())) \
    return (cRc); \
} while(0)

#define COMPILE_COMPILE(ptr)  \
do { \
  if ((cRc=CompileCompile(ptr))) \
    return (cRc); \
} while(0)

#define COMPILE_COMPILESTACK()  \
do { \
  if ((cRc=CompileCompileStack())) \
    return (cRc); \
} while(0)

#define COMPILE_COMPILECOMMENT(ptr)  \
do { \
  if ((cRc=CompileComment(ptr))) \
    return (cRc); \
} while(0)

#define COMPILE_ADDPOINTER(ptr)  \
do { \
  if ((cRc=CompileAddPointer(ptr))) \
    return (cRc); \
} while(0)

/* This routine is called by the compiler if a warning occurs. This routine */
/* should pass the warning message etc. on to the appropriate place.        */
void CompileWarningReport(BYTE *cError) {
  if (cThing) {
    SendThing("^a",cThing);
    SendThing(cError,cThing);
  }
}

/* This routine is called by the compiler if an error occurs. This routine */
/* should pass the error message etc. on to the appropriate place.         */
void CompileErrorReport(BYTE *cError) {
  BYTE temp,temp2,buf[40];
  BYTE *cP,*cSP,*cNP; /* pointer, start pointer, Next pointer */
  ULWORD cLine;

  if (cThing) {
    /* First, count lines, and find "line before" error */
    cLine=1; 
    for (cP=cSP=cNP=cSrcTop;(*cP)&&(cP!=cSrc);cP++) {
      if ((*cP)=='\n') {
        cLine++;
        cSP=cNP;
        cNP=cP+1;
        }
      }
    /* Increment past error to "line after" error */
    if (*cP)
      cP++;
    for (;(*cP)&&((*cP)!='\n');cP++);
    if (*cP)
      cP++;
    for (;(*cP)&&((*cP)!='\n');cP++);

    temp=*cSrc;
    temp2=*cP;
    *cSrc=*cP=0;
    sprintf(buf,"^wError at line %li:\n",cLine);
    SendThing(buf,cThing);
    SendThing("^a",cThing);
    SendThing(cSP,cThing);
    SendThing(" ^r<<*ERROR*\n^a",cThing);
    *cSrc=temp;
    SendThing(cSrc,cThing);
    SendThing("\n",cThing);
    *cP=temp2;
    SendThing("^w",cThing);
    SendThing(cError,cThing);
    SendThing("^a",cThing);
  }
}

/* This procedure sets cSrc to a new point and keeps track of cLastSrc */
void CompileSetSrc(BYTE *newpoint) {
  cLastSrc=cSrc;
  cSrc=newpoint;
  cNextSrc=NULL;
}

WORD CompileGetLastSrc() { /* retrieves last cSrc pointer */
  if (cLastSrc==NULL) { 
    /* If this happens, the state machine is screwed up. FIX IT! */
    /* We are only allowed to go 'back' one step!                */
    CompileErrorReport("internal error (compile.c): cLastSrc is NULL. This should not happen.\nRecord this message and your program, and notify your Sys Admin.\n");
    return(COMPILE_INTERNAL_ERROR);
  }
  else {
    cNextSrc=cSrc;
    cSrc=cLastSrc;
    cLastSrc=NULL;
    return(0);
  }
}

WORD CompileGetNextSrc() { /* oposite of CompileGetLastSrc */
  if (cNextSrc==NULL) {
    /* If this happens, the state machine is screwed up. FIX IT! */
    /* We are only allowed to go 'back' one step!                */
    CompileErrorReport("internal error (compile.c): cNextSrc is NULL. This should not happen.\nRecord this message and your program, and notify your Sys Admin.\n");
    return(COMPILE_INTERNAL_ERROR);
  }
  else {
    cLastSrc=cSrc;
    cSrc=cNextSrc;
    cNextSrc=NULL;
    return(0);
  }
}

/** Stack Functions **/

WORD CompileInitStack() {
  cSP=0; /* zero'th stack position in the... */ 
  cSF=0; /* zero'th stack frame, which starts at... */
  cSFP[cSF]=0; /* zero'th position in the stack */
  cStackP=&cStack[cSFP[cSF]]; /* set up pointer to this stack frame */
  return(0);
}

WORD CompileUpStack() { /* go up one stack frame */
  cSF++;
  REALLOC("WARNING! compile.c stack frame overflow... resizing\n",
    cSFP,WORD,(cSF+1),cSFPSizeByte);
  cSFP[cSF]=cSFP[cSF-1]+cSP; /* set new stack frame */
  cSP=0; /* zero'th position in new stack frame */
  cStackP=&cStack[cSFP[cSF]]; /* set up pointer to this stack frame */
  return(0);
}

WORD CompileDownStack() { /* go down one stack frame */
  BYTE error[300];
  if (cSP) { /* send WARNING message, not error, that stack is non-empty. */
             /* HOPEFULLY, if this code is modified correctly and runs    */
             /* smoothly, this condition will never happen. HOWEVER, the  */
             /* whole idea behind a stack frame is that each section of   */
             /* code gets its own stack, and can't screw up other peoples */
             /* stacks. (peoples? well, I guess we could consider states  */
             /* of a state machine as people. :)                          */
    if (cSP==1) {
      sprintf(error,"internal warning (compile.c): %i datum on stack lost with CompileDownStack().\nThis may cause your program to compile incorrectly. Notify your Sys Admin.\n",cSP);
    }
    else {
      sprintf(error,"internal warning (compile.c): %i data on stack lost with CompileDownStack().\nThis may cause your program to compile incorrectly. Notify your Sys Admin.\n",cSP);
    }
    CompileWarningReport(error);
  }
  if (cSF<=0) {
    CompileErrorReport("compile error: stack frame underflow\n");
    return(COMPILE_INTERNAL_ERROR);
  }
  cSP=cSFP[cSF]-cSFP[cSF-1];
  cSF--;
  cStackP=&cStack[cSFP[cSF]]; /* set up pointer to this stack frame */
  return(0);
}

WORD CompilePush(COMPILEDATA *data) {
  REALLOC("WARNING! compile.c stack overflow... resizing\n",
    cStack,COMPILEDATA,(cSP+cSFP[cSF]+1),cStackSizeByte);
  /* if we've just REALLOC'ed, our cStackP pointer is no longer valid. 
   * Instead of checking for a changed cStack, I'm just going to 
   * ALWAYS re-load cStackP from cStack */
  cStackP=&cStack[cSFP[cSF]]; /* set up pointer to this stack frame */

  cStackP[cSP].cInstruction = data->cInstruction; /* copy instruction */
  cStackP[cSP].cData.cuUnsigned=data->cData.cuUnsigned;
    /* copy data. ALL the data should be copied using the Unsigned */
  cSP++;
  return(0);
}

WORD CompilePull(COMPILEDATA *data) {
  if (!cSP) {
    CompileErrorReport("compile error: stack underflow\n");
    return(COMPILE_INTERNAL_ERROR);
  }
  cSP--;
  data->cInstruction=cStackP[cSP].cInstruction;
  data->cData.cuUnsigned=cStackP[cSP].cData.cuUnsigned;
  return(0);
}







/** Compile Functions **/

WORD CompileAddByte(BYTE data) {
  REALLOC("WARNING! compile.c buffer overflow... resizing\n",
    cCompBuf,BYTE,(cComp+1),cCompBufSizeByte);
  cCompBuf[cComp++]=data;
  return(0);
}

/* This procedure adds a pointer to the compiled code */
WORD CompileAddPointer(void *ptr) {
  ULWORD i;
  REALLOC("WARNING! compile.c buffer overflow... resizing\n",
    cCompBuf,BYTE,(cComp+CODE_PT_SIZE+1),cCompBufSizeByte);
  /* the following doesn't work on Solaris for some reason */
  /*
  *((void**)(&(cCompBuf[cComp])))=(void *)ptr;
  */

  /* alternate */
  for (i=0;i<CODE_PT_SIZE;i++)
    cCompBuf[cComp+i]=((((unsigned long)(ptr))>>(8*i))&0x0FF);
  cComp+=CODE_PT_SIZE;
  return(0);
}

/* this compiles, as a comment, the string passed to it */
WORD CompileComment(BYTE *comment) { /* compiles a comment into code */
  BYTE *pointer;

  if (BIT(codeSet,CODE_SAVECOMMENT)) {
    COMPILE_ADDBYTE(COP_COMMENT);
    pointer=comment;
    pointer--;
    do {
      pointer++;
      COMPILE_ADDBYTE(*pointer);
    }
    while(*pointer);
  } else {
    CompileWarningReport("compile warning: comments are turf'd.\n");
  }
  return(0);
} /* CompileComment */

/* this function appends a string onto the compiled program */
WORD CompileString(BYTE *string) { /* compiles a string into code */
  BYTE *pointer;

  pointer=string;
  pointer--;
  do {
    pointer++;
    COMPILE_ADDBYTE(*pointer);
  }
  while(*pointer);
  return(0);
} /* CompileString */

/* this fuction takes `data' and adds it on to the compiled program. */
WORD CompileCompile(COMPILEDATA *instruction) {
  LWORD temp;
  static LWORD cLastPush;

  /* optimize space useage by evaluating the generic COP_PUSHV and replacing */
  /* it with PUSHV1 (1-byte), PUSHV2 (2-bytes), or PUSHV4 (4-bytes).         */
  if (instruction->cInstruction==COP_PUSHV) {
    temp=instruction->cData.cuSLWord;
    if ((-128<=temp)&&(temp<=127)) {
      instruction->cInstruction=COP_PUSHV1;
      instruction->cData.cuSByte=temp;
    }
    else if ((-32768<=temp)&&(temp<=32767)) {
      instruction->cInstruction=COP_PUSHV2;
      instruction->cData.cuSWord=temp;
    }
    else {
      instruction->cInstruction=COP_PUSHV4;
      instruction->cData.cuSLWord=temp;
    }
  } /* if instruction of variety PUSHV */

  switch (instruction->cInstruction) {
      /* These operations decrement by 1 the stack pointer */
    case COP_EQUSR:
    case COP_EQUSL:
    case COP_EQUOR:
    case COP_EQUXOR:
    case COP_EQUAND:
    case COP_EQUSUB:
    case COP_EQUADD:
    case COP_EQUMOD:
    case COP_EQUDIV:
    case COP_EQUMULT:
    case COP_EQU:
    case COP_BOR:
    case COP_BAND:
    case COP_OR:
    case COP_XOR:
    case COP_AND:
    case COP_BEQU:
    case COP_BNEQU:
    case COP_GEQ:
    case COP_GR:
    case COP_LEQ:
    case COP_LS:
    case COP_SR:
    case COP_SL:
    case COP_SUB:
    case COP_ADD:
    case COP_MOD:
    case COP_DIV:
    case COP_MULT:
    case COP_POP:
      cReqStackSize--;

      /* These operations don't affect the stack pointer */
    case COP_BSUB:
    case COP_BADD:
    case COP_ASUB:
    case COP_AADD:
    case COP_COMP:
    case COP_NOT:
    case COP_NEG:
    case COP_TERM:
      COMPILE_ADDBYTE(instruction->cInstruction);
      break;

      /* These operations affect the stack in different ways */
    case COP_EXEC:
    case COP_EXECR:
      cReqStackSize-=cLastPush;
      if (cLastPush>cReqParameter) /* get max # parameters required */
        cReqParameter=cLastPush;
      COMPILE_ADDBYTE(instruction->cInstruction);
      COMPILE_ADDBYTE((instruction->cData.cuPointer&0x00ff));
      COMPILE_ADDBYTE((instruction->cData.cuPointer&0xff00)>>8);
      break;
    case COP_PUSHL:
    case COP_PUSHG:
    case COP_PUSHP:
      cReqStackSize++;
      COMPILE_ADDBYTE(instruction->cInstruction);
      COMPILE_ADDBYTE((instruction->cData.cuPointer&0x00ff));
      COMPILE_ADDBYTE((instruction->cData.cuPointer&0xff00)>>8);
      break;
    case COP_IFZ:
    case COP_WHILEZ:
      cReqStackSize--;
    case COP_GOTO:
    case COP_COMMENT:
      COMPILE_ADDBYTE(instruction->cInstruction);
      COMPILE_ADDBYTE((instruction->cData.cuPointer&0x00ff));
      COMPILE_ADDBYTE((instruction->cData.cuPointer&0xff00)>>8);
      break;
    case COP_PUSHV1:
      cLastPush=(LWORD)(instruction->cData.cuSByte);
      cReqStackSize++;
      COMPILE_ADDBYTE(instruction->cInstruction);
      COMPILE_ADDBYTE(instruction->cData.cuSByte&0xff);
      break;
    case COP_PUSHV2:
      cLastPush=(LWORD)(instruction->cData.cuSWord);
      cReqStackSize++;
      COMPILE_ADDBYTE(instruction->cInstruction);
      COMPILE_ADDBYTE(instruction->cData.cuSWord&0x00ff);
      COMPILE_ADDBYTE((instruction->cData.cuSWord&0xff00)>>8);
      break;
    case COP_PUSHV:
    case COP_PUSHV4:
      cLastPush=(LWORD)(instruction->cData.cuSLWord);
      cReqStackSize++;
      COMPILE_ADDBYTE(instruction->cInstruction);
      COMPILE_ADDBYTE(instruction->cData.cuSLWord&0x000000ff);
      COMPILE_ADDBYTE((instruction->cData.cuSLWord&0x0000ff00)>>8);
      COMPILE_ADDBYTE((instruction->cData.cuSLWord&0x00ff0000)>>16);
      COMPILE_ADDBYTE((instruction->cData.cuSLWord&0xff000000)>>24);
      break;
    case COP_PUSHPT:
      cReqStackSize++;
      COMPILE_ADDBYTE(instruction->cInstruction);
      COMPILE_ADDPOINTER(instruction->cData.cuPtr);
      break;
    case COP_NULL:
    default:
      CompileWarningReport("internal warning (compile.c): compiler passed illegal instruction.\nThis may cause your program to compile incorrectly. Notify your Sys Admin.\n");
      break;
  }
  if (cReqStackSize>cReqStackSizeMax)
    cReqStackSizeMax=cReqStackSize;
  return(0);
}

/* This pulls the top instruction off the stack and compiles it */
WORD CompileCompileStack() {
  COMPILEDATA instruction; 

  COMPILE_PULL(&instruction);
  COMPILE_COMPILE(&instruction);
  return(0);
}


LWORD CompileStrToNum(BYTE *string) { /* converts string to LWORD */
  BYTE *pointer;
  LWORD num;

  num=0;
  pointer=string;
  while(*pointer) {
    num=(num*10)+(*pointer-'0');
    pointer++;
    }
  return(num);
}

/** Parser Functions **/

/* CompileReadNext ()
** *c_label   - returns: text of label, comment, string, whatever
** *c_operation - 1 WORD code as defined by the #define statements
**                starting with CRN_
** returns COMPILE_CRN_RC_OK if operation ok, errorcode otherwise: 
**      (*label=errorcode)
**  CRN_RC_EOF - EOF reached unexpectedly
**  CRN_RC_SYNTAX - syntactical error
**
** This procedure reads the next 'bit' of code which is of importance
** to the compiler. It doesn't read very far, and it's up to the compiler
** to keep track of what is valid and when. What I mean by the next 'bit'
** is this:
**
**   if you fed this routine the following line, repeatedly, until EOLN,
**
**      a = c * (d-e) /o  hi mom o/ + some_funct(g,h)+strlen("hi there");
**                    ^^         ^^
**                    pretend these are real comment markers :)
**       
**   you would get:
**      c_label      c_operation
**      a            CRN_LABEL<<8
**      NULL         CRN_OPERATOR<<8 + COP_EQU
**      c            CRN_LABEL<<8
**      NULL         CRN_OPERATOR<<8 + COP_MULT
**      NULL         CRN_ORBRAC<<8
**      d            CRN_LABEL<<8
**      NULL         CRN_OPERATOR<<8 + COP_SUB
**      e            CRN_LABEL<<8
**      NULL         CRN_CRBRAC<<8
**      hi mom       CRN_COMMENT<<8
**      NULL         CRN_OPERATOR<<8 + COP_ADD
**      some_funct   CRN_LABEL<<8
**      NULL         CRN_ORBRAC<<8
**      g            CRN_LABEL<<8
**      NULL         CRN_COMMA<<8
**      h            CRN_LABEL<<8
**      NULL         CRN_CRBRAC<<8
**      NULL         CRN_OPERATOR<<8 + COP_ADD
**      strlen       CRN_LABEL<<8
**      NULL         CRN_ORBRAC<<8
**      hi there     CRN_STRING<<8
**      NULL         CRN_CRBRAC<<8
**      NULL         CRN_SEMI<<8
**      NULL         CRN_EOF<<8   (assuming this is the end of the file )
*/
WORD CompileReadNext(BYTE *c_label, WORD *c_operation) {

  BYTE *p_label; /* pointers to these things. */
  BYTE *pointer;
  WORD SCM_offset;
  static CHARMARK SCM[]= { /* make sure you put triple-char, then double, then single */
    { ">>=",(CRN_OPERATOR<<8) + COP_EQUSR     },
    { "<<=",(CRN_OPERATOR<<8) + COP_EQUSL     },
    { "|=" ,(CRN_OPERATOR<<8) + COP_EQUOR     },
    { "^=" ,(CRN_OPERATOR<<8) + COP_EQUXOR    },
    { "&=" ,(CRN_OPERATOR<<8) + COP_EQUAND    },
    { "-=" ,(CRN_OPERATOR<<8) + COP_EQUSUB    },
    { "+=" ,(CRN_OPERATOR<<8) + COP_EQUADD    },
    { "%=" ,(CRN_OPERATOR<<8) + COP_EQUMOD    },
    { "/=" ,(CRN_OPERATOR<<8) + COP_EQUDIV    },
    { "*=" ,(CRN_OPERATOR<<8) + COP_EQUMULT   },
    { "&&" ,(CRN_OPERATOR<<8) + COP_BAND      },
    { "||" ,(CRN_OPERATOR<<8) + COP_BOR       },
    { "!=" ,(CRN_OPERATOR<<8) + COP_BNEQU     },
    { "==" ,(CRN_OPERATOR<<8) + COP_BEQU      },
    { "<=" ,(CRN_OPERATOR<<8) + COP_LEQ       },
    { ">=" ,(CRN_OPERATOR<<8) + COP_GEQ       },
    { "<<" ,(CRN_OPERATOR<<8) + COP_SL        },
    { ">>" ,(CRN_OPERATOR<<8) + COP_SR        },
    { "--" ,(CRN_OPERATOR<<8) + COP_ASUB      },
    { "++" ,(CRN_OPERATOR<<8) + COP_AADD      },
    { "`"  ,(CRN_OPERATOR<<8) + COP_COMP      },
    { "<"  ,(CRN_OPERATOR<<8) + COP_LS        },
    { ">"  ,(CRN_OPERATOR<<8) + COP_GR        },
    { "!"  ,(CRN_OPERATOR<<8) + COP_NOT       },
    { "="  ,(CRN_OPERATOR<<8) + COP_EQU       },
    { "+"  ,(CRN_OPERATOR<<8) + COP_ADD       },
    { "-"  ,(CRN_OPERATOR<<8) + COP_SUB       },
    { "*"  ,(CRN_OPERATOR<<8) + COP_MULT      },
    { "/"  ,(CRN_OPERATOR<<8) + COP_DIV       },
    { "%"  ,(CRN_OPERATOR<<8) + COP_MOD       },
    { "&"  ,(CRN_OPERATOR<<8) + COP_AND       },
    { "|"  ,(CRN_OPERATOR<<8) + COP_OR        },
    { "\\"  ,(CRN_OPERATOR<<8) + COP_XOR      },
    { "{"  ,CRN_OBRAC<<8                   }, /* grammar marks */
    { "}"  ,CRN_CBRAC<<8                   },
    { "("  ,CRN_ORBRAC<<8                  },
    { ")"  ,CRN_CRBRAC<<8                  },
    { ";"  ,CRN_SEMI<<8                    },
    { ","  ,CRN_COMMA<<8                   },
    { "["  ,CRN_OSBRAC<<8                  },
    { "]"  ,CRN_CSBRAC<<8                  },
    { NULL ,0                              },
  };

  pointer=cSrc; /* start at beginning of program */
  *c_label=0; /* null-out all our return codes */
  *c_operation=0;

  /* first, lets find first non-space, non-LF, non-etc char*/
  while(((*pointer == ' ')||(*pointer < 32))&&(*pointer !=0))
    pointer++;

  if (*pointer == 0) /* check for EOF */ {
    *c_operation=CRN_EOF<<8;
    CompileSetSrc(pointer);
    return(CRN_RC_OK);
  }

  if ((*pointer == '/') && (*(pointer+1)=='/')) { /* we've a '/'/' comment! */
    pointer=pointer+2; /* advance pointer to comment 'guts' */
    *c_operation=CRN_COMMENT<<8; /* define as comment */
    p_label=c_label;  /* start our 'c_label' filling! */
    while ((*pointer!='\n')&&(*pointer!='\r')&&(*pointer!=10)&&
          (*pointer!=0)) {
      *p_label=*pointer; /* fill up label with comment */
      pointer++; /* advance pointer & p_label */
      p_label++;
    }
    /* at this point, whatever we reached, it's 'end of comment' */
    *p_label=0; /* mark as end of string */
    CompileSetSrc(pointer); /* send back 'the rest */
    return(CRN_RC_OK);
  }

  if ((*pointer == '/') && (*(pointer+1)=='*')) { /* we've a '/'*' comment! */
    pointer=pointer+2; /* advance pointer to comment text */
    *c_operation=CRN_COMMENT<<8; /* define as comment */
    p_label=c_label;  /* start our 'label' filling! */
    while (((*pointer!='*')||(*(pointer+1)!='/'))&&
          (*pointer!=0)) {
      *p_label=*pointer; /* fill up label with comment */
      pointer++; /* advance pointer & p_label */
      p_label++;
    }
    /* at this point, if EOF, error!  */
    if (*pointer == 0) {
      sprintf(c_label,"'/*' comment missing matching '*/' before EOF");
      CompileSetSrc(pointer);
      return(CRN_RC_EOF);
    }
    /* ok, we've found the end marker */
    pointer=pointer+2; /* advance pointer to outside comment */
    *p_label=0; /* mark as end of string */
    CompileSetSrc(pointer); /* send back 'the rest */
    return(CRN_RC_OK);
  }
        
  if (*pointer == '"') { /* check if this is a quoted string */ 
    *c_operation=CRN_STRING<<8; /* define as string */
    p_label=c_label; /* start our 'label' filling! */
    pointer++; /* skip past open " */
    while ((*pointer !='"')&&(*pointer != 0)) { 
      /* copy text into label - quote string so anything goes */
      if ((*pointer=='\\')&&(*(pointer+1)!=0)) { /* escape sequence */
        pointer++;
        if (*pointer=='a') *p_label='\a';
        else if (*pointer=='b') *p_label='\b';
        else if (*pointer=='f') *p_label='\f';
        else if (*pointer=='n') *p_label='\n';
        else if (*pointer=='r') *p_label='\r';
        else if (*pointer=='t') *p_label='\t';
        else if (*pointer=='v') *p_label='\v';
        else *p_label=*pointer;
      } else *p_label=*pointer;
      p_label++;
      pointer++;
    }
    if (*pointer==0) { /* an EOF? Error! */
      sprintf(c_label,"Unexpected EOF! You are missing a final \" to your quote!");
      CompileSetSrc(pointer);
      return(CRN_RC_EOF);
    }
    *p_label=0; /* terminate c_label */
    pointer++; /* advance pointer past ending " */
    CompileSetSrc(pointer);
    return(CRN_RC_OK);
  }

  if (*(pointer+1)!=0) {/* we don't want to go over - segmentation faults :) */
    if ((*pointer=='\'')&&(*(pointer+2)=='\'')) { /* ' quoted number  */
      *c_operation=CRN_NUMBER<<8; /* define as number */
      p_label=c_label; /* start our 'label' filling! */
      pointer++;
      *(p_label++)=((*pointer)/100)+'0';
      *(p_label++)=((*pointer)/10)-(((*pointer)/100)*10)+'0';
      *(p_label++)=(*pointer)-(((*pointer)/10)*10)+'0';
      *(p_label++)=0; /* terminate string */
      pointer+=2;
      CompileSetSrc(pointer);
      return(CRN_RC_OK);
    }
  }


  /* now, the following while-loop is just to save code space and     */
  /* whip through all the cases where we would return after reading a */
  /* simple char string. This is what the big structure def'n is for. */
  SCM_offset=0; /* start at the beginning */
  while (SCM[SCM_offset].SCMstr!=NULL) {
    if ((strlen(SCM[SCM_offset].SCMstr)==3)&& /* first give triple a crack */
        (*pointer==SCM[SCM_offset].SCMstr[0])&&
        (*(pointer+1)==SCM[SCM_offset].SCMstr[1])&&
        (*(pointer+2)==SCM[SCM_offset].SCMstr[2])) {
      *c_operation=SCM[SCM_offset].SCMoperation;
      CompileSetSrc(pointer+3);
      strcpy(c_label,SCM[SCM_offset].SCMstr);
      return(CRN_RC_OK);
    } 
    else if ((strlen(SCM[SCM_offset].SCMstr)==2)&& /* then doubles */
             (*pointer==SCM[SCM_offset].SCMstr[0])&&
             (*(pointer+1)==SCM[SCM_offset].SCMstr[1])) {
      *c_operation=SCM[SCM_offset].SCMoperation;
      CompileSetSrc(pointer+2);
      strcpy(c_label,SCM[SCM_offset].SCMstr);
      return(CRN_RC_OK);
    }
    else if ((strlen(SCM[SCM_offset].SCMstr)==1)&& /* finally singles */
             (*pointer==SCM[SCM_offset].SCMstr[0])) {
      *c_operation=SCM[SCM_offset].SCMoperation;
      CompileSetSrc(pointer+1);
      strcpy(c_label,SCM[SCM_offset].SCMstr);
      return(CRN_RC_OK);
    }
    SCM_offset++;
  }

  if ((*pointer>='0')&&(*pointer<='9')) { /* we've got a number */
    *c_operation=CRN_NUMBER<<8; /* define as number */
    p_label=c_label; /* initialize p_label to start of c_label */
    while((*pointer>='0')&&(*pointer<='9')) {
      *p_label=*pointer; /* copy number into 'label' */
      pointer++; /* advance pointers */
      p_label++;
    }
    *p_label=0; /* terminate label */
    CompileSetSrc(pointer);
    return(CRN_RC_OK);
  }

  if (((*pointer>='a')&&(*pointer<='z'))|| /* we're lowercase alpha */
     ((*pointer>='A')&&(*pointer<='Z'))|| /* we're uppercase alpha */
     (*pointer == '_')) { /* and of course, the underscore is valid */
    /* ok, for us to have made it this far, we've got to be looking */
    /* at an alphabetic label. Let's read it into c_label now.      */

    /* Start copying label, and boogey along until we hit a non-label*/
    /* character (eg: 'int a' or 'a = 2;' ) or a '(' (eg: printf("hi");)*/
    *c_operation=CRN_LABEL<<8; /* define as label */
    p_label=c_label; /* initialize p_label to start of c_label */
    while(((*pointer>='a')&&(*pointer<='z'))|| /* we're lowercase alpha */
         ((*pointer>='A')&&(*pointer<='Z'))|| /* we're uppercase alpha */
         ((*pointer>='0')&&(*pointer<='9'))|| /* numbers just can't be the first character in a label */
         (*pointer == '_')) { /* and of course, the underscore is valid */
      *p_label=*pointer; /* copy label into 'label' */
      pointer++; /* advance pointer & p_label */
      p_label++;
    } /* while */
    *p_label=0; /* terminate label */
    CompileSetSrc(pointer);
    return(CRN_RC_OK);
  } 
  else { /* we've got an invalid/unrecognized character! */
    sprintf(c_label,"I don't recognize this character: %c",*pointer);
    CompileSetSrc(pointer+1);
    return(CRN_RC_SYNTAX);
  } 
} /* CompileReadNext */



/* Compile
 * The guts of this whole thing.
 *
 * Compile is a great big huge gigantic enormous ugly disgusting state machine.
 * I hope you like it :)
 *
 * ccSrc     = pointer to a STR containing source code to compile.
 * ccComp    = pointer to a pointer to a STR (which will be created by Compile)
 *             containing compiled binary code. NOTE: ccComp will contain, 
 *             upon return, a pointer to the STR.
 * ccThing   = the THING to send informational messages to (eg compile errors).
 *
 */
WORD Compile(STR *ccSrc,STR **ccComp,THING *ccThing) {
  ULWORD cValid; /* mask as to what operation is currently valid */
  ULWORD cState;  /* current state for state machine */
  BYTE cLabel[1000]; /* Label storage - use with CompileReadNext */
  UWORD cOperation; /* operation - use with CompileReadNext */
  UWORD cLastOperation; /* used to save last CompileReadNext operation */
  BYTE cError[200]; /* for error messages */
  BYTE cError2[200]; /* for error messages */
  UWORD counter; /* used here and there as a local counter */
  UWORD cReserved; /* offset to Reserved word in cResWord */
  UWORD cFunction; /* offset to function in fTable */
  UWORD cVar;      /* offset to variable in cVarTable */
  COMPILEDATA cTemp; /* data variable used for communicating with stacks etc. */
  UWORD cCurLocal; /* current local variable we're at */
  UWORD cTotalLocal,cTotalGlobal,cTotalPrivate; /* variable totals */

  /* flags used by compile state machine */
  ULWORD cVarFlag; /* variable type flag */
  ULWORD cExpFlag; /* Expression Type flag */
  ULWORD cNumParam; /* # parameters we've had in this function call */

  *ccComp=NULL; /* if we return w/ an error, return NULL */
  cComp=0;
  cSrc=cSrcTop=cLastSrc=ccSrc->sText;  /* setup source code for global access */
  cNextSrc=NULL;
  cThing=ccThing; /* set up Thing so that we have someplace to dump errors */
  cNumParam=0;
  cReqStackSize=cReqStackSizeMax=0; /* reset our interp stack size */
  cReqParameter=0;                  /* reset our interp parameter list size */

  if ((cRc=CompileInitStack())) /* initialize (empty) stack */
    return(cRc);

  /* set up start of code */
  strcpy(cCompBuf,CODE_PROG_HEADER);
  cComp=CODE_PROG_HEADER_LENGTH;

  *cVarTable[cSystemVariable].cText=0; /* initialize (empty) variable table */
  cVarTable[cSystemVariable].cType=CDT_NULL;
  cVarTable[cSystemVariable].cDomain=CDOMAIN_UNDEF;
  cVarTable[cSystemVariable].cOffset=0;
  cCurVar=cSystemVariable+1;
  cCurLocal=cSystemVariable+1; /* locals reside along side system vars. :) */

  cState=CS_START; /* State 1 is the starting state. State 0 exits */
  cValid=CV_COMMENT|CV_VAR|CV_CBRAC|CV_FUNCT|CV_RESWRD|CV_ORBRAC|
         CV_OPERATOR|CV_OBRAC|CV_EOF|CV_COMMENT|CV_SEMI;

  /** start of Compile state machine **/
  while (1) {
    if (cNextSrc) { /* we've already parsed the next block - just skip ahead */
      cRc=CompileGetNextSrc();
      cOperation=cLastOperation;
      if (cRc)
        return(cRc);
    } else {
      cRc=CompileReadNext(cLabel,&cOperation);
      cLastOperation=cOperation;
      if (cRc != CRN_RC_OK) { /* check for error codes from CompileReadNext */
        switch(cRc) {
          case CRN_RC_EOF:
            sprintf(cError,"parse error (EOF): %s\n",cLabel);
            break;
          case CRN_RC_SYNTAX:
            sprintf(cError,"parse error (SYNTAX): %s\n",cLabel);
            break;
          default:
            sprintf(cError,"parse error (unknown): %s\n",cLabel);
            break;
        }
        CompileErrorReport(cError);
        return(COMPILE_SYNTAX_ERROR);
      }
    }

    if (((cOperation&0xFF00)>>8)==CRN_LABEL) { /* chng label to fn,var,etc. */
      /* first, check for reserved word */
      counter=0;
      while((*cResWord[counter].cText!=0)&&
            (strcmp(cResWord[counter].cText,cLabel))) {
        counter++;
      }
      if (*cResWord[counter].cText!=0) { /* we've found it */
        cOperation=(CRN_RESWRD<<8)+(cOperation&0x00FF);
        cReserved=counter;
      }
      else { /* not reserved, must be a function, variable, or undefined. */
        /* first, check if it's a function */
        counter=0;
        while((fTable[counter].fText!=NULL)&& /*NOTE: Function calls are all*/
          (STRICMP(fTable[counter].fText,cLabel))) { /* NOT case dependent!!*/
          counter++;
  }
        if (fTable[counter].fText !=NULL) { /* we've found it */
          cOperation=(CRN_FUNCT<<8)+(cOperation&0x00FF);
          cFunction=counter;
        }
        else { /* not function, must be variable or undefined */
          counter=0;
          while((counter<cCurVar)&&
                (strcmp(cVarTable[counter].cText,cLabel))) {
            counter++;
          }
          if (counter<cCurVar) {  /* found it */
            cOperation=(CRN_VAR<<8)+(cOperation&0x00FF);
            cVar=counter;
          }
          else { /* not variable, must be undefined */
            cOperation=(CRN_UNDEF<<8)+(cOperation&0x00FF);
          } /* variable check */
        } /* function check */
      } /* reserved check */
    } /* if (cOoperation==CRN_LABEL) */

    cState=(cState&0xFFFFFF00)+((cOperation&0xFF00)>>8);

    if (BIT(codeSet,CODE_DEBUGCOMPILE)) {
      sprintf(cError,"^a  0x%08lX : %04X `%s'\n",cState,cOperation,cLabel);
      printf(cError);
      if (cThing) SendThing(cError,cThing);
    }

    /* Now, before we enter the state machine, we do a preliminary error */
    /* check. This looks at what is valid at the current time. It allows */
    /* the current state to filter out certain types of stuff, thus      */
    /* saving gobs of error checking in the state machine itself. It     */
    /* results in error codes not being quite so specific, but hey, too  */
    /* bad. If you want, you can code ALL the error codes, one by one,   */
    /* and someday have pretty error codes for every possible exception, */
    /* but I hope you've got a computer with lots of memory!             */
    /* (well, I guess you do - if you've got that much time to kill, you */
    /* must have money to spend on a big computer)                       */
    if (!(cValid&(1<<((cOperation&0xFF00)>>8)))) { /* invalid at this time */
      /* define type of error (borrow cLabel as temporary buffer) */
      switch((cOperation&0xFF00)>>8) {
        case CRN_EOF:
          sprintf(cError2,"EOF");
          break;
        case CRN_OPERATOR:
          sprintf(cError2,"operator");
          break;
        case CRN_COMMENT:
          sprintf(cError2,"comment `%s'",cLabel);
          break;
        case CRN_COMMA:
          sprintf(cError2,"comma (,)");
          break;
        case CRN_STRING:
          sprintf(cError2,"string constant \"%s\"",cLabel);
          break;
        case CRN_SEMI:
          sprintf(cError2,"semicolon (;)");
          break;
        case CRN_NUMBER:
          sprintf(cError2,"use of constant `%s'",cLabel);
          break;
        case CRN_RESWRD:
          sprintf(cError2,"use of reserved word `%s'",cLabel);
          break;
        case CRN_VAR:
          sprintf(cError2,"reference to variable `%s'",cLabel);
          break;
        case CRN_FUNCT:
          sprintf(cError2,"call to function `%s'",cLabel);
          break;
        case CRN_UNDEF:
          sprintf(cError2,"undefined label `%s' ",cLabel);
          break;
        case CRN_OBRAC:
          sprintf(cError2,"brace `{'");
          break;
        case CRN_CBRAC:
          sprintf(cError2,"brace `}'");
          break;
        case CRN_OSBRAC:
          sprintf(cError2,"bracket `['");
          break;
        case CRN_CSBRAC:
          sprintf(cError2,"bracket `]'");
          break;
        case CRN_ORBRAC:
          sprintf(cError2,"parenthesis `('");
          break;
        case CRN_CRBRAC:
          sprintf(cError2,"parenthesis `)'");
          break;
        case CRN_LABEL:
          sprintf(cError2,"unrecognized label `%s'",cLabel);
          break;
        default:
          sprintf(cError2,"this operation");
          break;
      }
      sprintf(cError,"compile error: %s is invalid here.\n",cError2);
      CompileErrorReport(cError);
      return(COMPILE_SYNTAX_ERROR);
    }

    /* ok, here it is - the state machine, in the form of a switch */
    switch (cState) {

/*  EEEEEE  NNN  NN  DDDDD
 *  EE      NNNN NN  DD  DD
 *  EEEE    NN NNNN  DD  DD
 *  EE      NN  NNN  DD  DD
 *  EEEEEE  NN   NN  DDDDD   */

      /* CS_END: end of program */
      case CS_END+CRN_EOF:  /* end of program */
        COMPILE_PULL(&cTemp); /* set `next block' for code section */
        cCompBuf[cTemp.cData.cuPointer]=(BYTE)(cComp&0x00ff);
        cCompBuf[cTemp.cData.cuPointer+1]=(BYTE)((cComp&0xff00)>>8);
        /* The following WACK of code takes the variable table cVarTable,
         * and translates it into it's component `compiled' parts. It starts
         * by tallying the Local, Global, and Private vars. This way, we know
         * how many of each we're dealing with. Then, it does what is required
         * for each domain's variables.
         */
        cTotalLocal=cTotalGlobal=cTotalPrivate=0;
        for(counter=cSystemVariable+1;counter<cCurVar;counter++) {
          if (cVarTable[counter].cDomain==CDOMAIN_LOCAL) {
            cTotalLocal++;
          } else if (cVarTable[counter].cDomain==CDOMAIN_GLOBAL) {
            cTotalGlobal++;
          } else if (cVarTable[counter].cDomain==CDOMAIN_PRIVATE) {
            cTotalPrivate++;
          } else {
            sprintf(cError,"compile error: unrecognized domain for `%s'. This shouldn't happen.\nRecord your program and this message, and contact your Sys Admin.\n",cVarTable[counter].cText);
            CompileErrorReport(cError);
            return(COMPILE_INTERNAL_ERROR);
          }
        } /* tally domain totals */
        /* uncomment these two lines if you want a status line
         * like this: "Variables: 6 local, 1 global, 0 private" printed. */
/*        sprintf(cError,"^aVariables: ^c%i ^alocal, ^c%i ^aglobal, ^c%i ^aprivate.\n",
                cTotalLocal,cTotalGlobal,cTotalPrivate);
        CompileWarningReport(cError);*/
        /* Translate local variables into compiled code */
        if (cTotalLocal) {
          COMPILE_ADDBYTE(CODE_BLK_VAR);  /* create VAR block */
          cTemp.cInstruction=COP_NULL; /* save location */
          cTemp.cData.cuPointer=cComp; 
          COMPILE_PUSH(&cTemp);
          COMPILE_ADDBYTE(0); /* 'next block' pointer (alloc space) */
          COMPILE_ADDBYTE(0); /* 'next block' pointer (alloc space) */
          for(counter=cSystemVariable+1;counter<cCurVar;counter++) { /* types */
            if (cVarTable[counter].cDomain==CDOMAIN_LOCAL) {
              COMPILE_ADDBYTE(cVarTable[counter].cType);
            }
          }
          COMPILE_PULL(&cTemp); /* set `next block' for code section */
          cCompBuf[cTemp.cData.cuPointer]=(BYTE)(cComp&0x00ff);
          cCompBuf[cTemp.cData.cuPointer+1]=(BYTE)((cComp&0xff00)>>8);
          COMPILE_ADDBYTE(CODE_BLK_VAR_NAME); /* create VAR_NAME blk */
          cTemp.cInstruction=COP_NULL; /* save location */
          cTemp.cData.cuPointer=cComp; 
          COMPILE_PUSH(&cTemp);
          COMPILE_ADDBYTE(0); /* 'next block' pointer (alloc space) */
          COMPILE_ADDBYTE(0); /* 'next block' pointer (alloc space) */
          for(counter=cSystemVariable+1;counter<cCurVar;counter++) { /* types */
            if (cVarTable[counter].cDomain==CDOMAIN_LOCAL) {
              COMPILE_STRING(cVarTable[counter].cText);
            }
          }
          COMPILE_PULL(&cTemp); /* set `next block' for code section */
          cCompBuf[cTemp.cData.cuPointer]=(BYTE)(cComp&0x00ff);
          cCompBuf[cTemp.cData.cuPointer+1]=(BYTE)((cComp&0xff00)>>8);
        }
        if (cTotalGlobal) {
          CompileErrorReport("compile error: global variables unsupported.\n");
          return(COMPILE_INTERNAL_ERROR);
        /******* CONVERT VARIABLE TABLE INTO GLOBAL*****/
        /******* ADD GLOBAL VARIABLE SECTIONS TO CODE ******/
        }
        if (cTotalPrivate) {
          CompileErrorReport("compile error: private variables unsupported.\n");
          return(COMPILE_INTERNAL_ERROR);
        /******* CONVERT VARIABLE TABLE INTO PRIVATE*****/
        /******* ADD PRIVATE VARIABLE SECTIONS TO CODE ******/
        }
        COMPILE_ADDBYTE(CODE_BLK_NULL);
        *ccComp=StrCreate(cCompBuf, cComp-1, HASH);
        InterpStackAlloc(cReqStackSizeMax,cReqParameter,cTotalLocal);
        return(COMPILE_OK);
        break;

/*  SSSS  TTTTTT   AAAA    RRRRR   TTTTTT
 * SS       TT    AA  AA   RR  RR    TT
 *  SSS     TT    AAAAAA   RRRR      TT
 *    SS    TT    AA  AA   RR RR     TT
 * SSSS     TT    AA  AA   RR  RR    TT */

      /* CS_START: start of program */
      case CS_START+CRN_SEMI: 
      case CS_START+CRN_COMMENT: 
      case CS_START+CRN_VAR: 
      case CS_START+CRN_CBRAC: 
      case CS_START+CRN_FUNCT: 
      case CS_START+CRN_RESWRD: 
      case CS_START+CRN_ORBRAC: 
      case CS_START+CRN_OPERATOR: 
      case CS_START+CRN_OBRAC: 
        COMPILE_GETLASTSRC(); /* Whoah! backup! */
        COMPILE_ADDBYTE(CODE_BLK_CODE); /* code block header */
        cTemp.cInstruction=COP_NULL;
        cTemp.cData.cuOffset=cComp;
        COMPILE_PUSH(&cTemp); /* push marker for 'next blk' offset */
        COMPILE_ADDBYTE(0); /* 'next block' pointer (alloc space) */
        COMPILE_ADDBYTE(0); /* 'next block' pointer (alloc space) */
        cTemp.cInstruction=COP_NULL; /* push cValid and cState for */
        cTemp.cData.cuValid=CV_EOF|CV_COMMENT;  /* when we find closing `}'   */
        COMPILE_PUSH(&cTemp);
        cTemp.cData.cuState=CS_END;
        COMPILE_PUSH(&cTemp);
        COMPILE_UPSTACK();
        cState=CS_MAIN; /* goto normal handler */
        cValid=CV_SEMI|CV_FUNCT|CV_VAR|CV_OPERATOR|CV_ORBRAC|CV_OBRAC|
               CV_RESWRD|CV_COMMENT;
        break;
      case CS_START+CRN_EOF:
        sprintf(cError,"compile error: nothing to compile\n");
        CompileErrorReport(cError);
        return(COMPILE_SYNTAX_ERROR);
        break;

/* VV     VV   AAAA   RRRRR   
 *  VV   VV   AA  AA  RR  RR 
 *   VV VV    AAAAAA  RRRR
 *    VVV     AA  AA  RR RR 
 *     V      AA  AA  RR  RR */

      /* CS_VAR: allocate & specify variables */
      case CS_VAR+CRN_RESWRD: /* domain reader */
        if (cCurVar>=CMAX_VAR) {
          sprintf(cError,"compile error: variable table full. You've got too many variables!\n");
          CompileErrorReport(cError);
          return(COMPILE_INTERNAL_ERROR);
        }
        if ((cReserved==CRES_INT)||
            (cReserved==CRES_STR)||
            (cReserved==CRES_EXIT)||
            (cReserved==CRES_EXTRA)||
            (cReserved==CRES_THING)) {
          cVarTable[cCurVar].cDomain=CDOMAIN_LOCAL; /* default to local */
          cVarTable[cCurVar].cOffset=cCurLocal; 
          cCurLocal++;
          COMPILE_GETLASTSRC(); /* backup so our `type' can be read */
        } else if(cReserved==CRES_LOCAL) {
          cVarTable[cCurVar].cDomain=CDOMAIN_LOCAL;
          cVarTable[cCurVar].cOffset=cCurLocal; 
          cCurLocal++;
        } else if(cReserved==CRES_GLOBAL) {
          cVarTable[cCurVar].cDomain=CDOMAIN_GLOBAL; 
          cVarTable[cCurVar].cOffset=0; /*****CHANGE*****/
        } else if(cReserved==CRES_PRIVATE) {
          cVarTable[cCurVar].cDomain=CDOMAIN_PRIVATE;
          cVarTable[cCurVar].cOffset=0; /*****CHANGE*****/
        } else {
          sprintf(cError,"compile error: unrecognized variable domain `%s'. This should not happen.\nRecord your program and this message, and contact your Sys Admin.\n",cLabel);
          CompileErrorReport(cError);
          return(COMPILE_INTERNAL_ERROR);
        }
        cValid=CV_COMMENT|CV_RESWRD;
        cState=CS_VAR+0x100;
        break;
      case CS_VAR+0x100+CRN_RESWRD: /* type reader */
        if (cReserved==CRES_INT) {
          cVarTable[cCurVar].cType=CDT_INT;
        } else if (cReserved==CRES_STR) {
          cVarTable[cCurVar].cType=CDT_STR;
        } else if (cReserved==CRES_THING) {
          cVarTable[cCurVar].cType=CDT_THING;
        } else if (cReserved==CRES_EXTRA) {
          cVarTable[cCurVar].cType=CDT_EXTRA;
        } else if (cReserved==CRES_EXIT) {
          cVarTable[cCurVar].cType=CDT_EXIT;
        } else {
          sprintf(cError,"compile error: unrecognized variable type `%s'. This should not happen.\nRecord your program and this message, and contact your Sys Admin.\n",cLabel);
          CompileErrorReport(cError);
          return(COMPILE_INTERNAL_ERROR);
        }
        cValid=CV_COMMENT|CV_UNDEF|CV_VAR;
        cState=CS_VAR+0x200;
        break;
      case CS_VAR+0x200+CRN_UNDEF: /* we know type,domain. Define the sucker */
        if (strlen(cLabel)>CVAR_NAME_LENGTH) {
          sprintf(cError,"compile error: variable name `%s' exceeds length limit of %i chars.\n",cLabel,CVAR_NAME_LENGTH);
          CompileErrorReport(cError);
          return(COMPILE_SYNTAX_ERROR);
        }
        strcpy(cVarTable[cCurVar].cText,cLabel);
        cValid=CV_COMMENT|CV_COMMA|CV_SEMI;
        cState=CS_VAR+0x300;
        cCurVar++;
        break;
      case CS_VAR+0x200+CRN_VAR:
        sprintf(cError,"compile error: variable `%s' is already defined!\n",cLabel);
        CompileErrorReport(cError);
        return(COMPILE_SYNTAX_ERROR);
        break;
      case CS_VAR+0x300+CRN_COMMA: /* define another of the same */
        if (cCurVar>=CMAX_VAR) {
          sprintf(cError,"compile error: variable table full. You've got too many variables!\n");
          CompileErrorReport(cError);
          return(COMPILE_INTERNAL_ERROR);
        }
        cVarTable[cCurVar].cType=cVarTable[cCurVar-1].cType;
        cVarTable[cCurVar].cDomain=cVarTable[cCurVar-1].cDomain;
        if (cVarTable[cCurVar].cDomain==CDOMAIN_LOCAL) {
          cVarTable[cCurVar].cOffset=cCurLocal; 
          cCurLocal++;
        } else if (cVarTable[cCurVar].cDomain==CDOMAIN_GLOBAL) {
          cVarTable[cCurVar].cOffset=0; /*****CHANGE*****/
           /* ADD VARIABLE TO GLOBAL TABLE */
        } else if (cVarTable[cCurVar].cDomain==CDOMAIN_PRIVATE) {
          cVarTable[cCurVar].cOffset=0; /*****CHANGE*****/
           /* ADD VARIABLE TO PRIVATE TABLE */
        } else {
          sprintf(cError,"compile error: unrecognized variable domain. This should not happen.\nRecord your program and this message, and contact your Sys Admin.\n");
          CompileErrorReport(cError);
          return(COMPILE_INTERNAL_ERROR);
        }
        cValid=CV_COMMENT|CV_UNDEF|CV_VAR;
        cState=CS_VAR+0x200; /* go back and define another */
        break;
      case CS_VAR+0x300+CRN_SEMI:
        COMPILE_DOWNSTACK();
        COMPILE_PULL(&cTemp); /* return to calling state */
        cState=cTemp.cData.cuState;
        COMPILE_PULL(&cTemp);
        cValid=cTemp.cData.cuValid;
        break;

/* MMM    MMM   AAAA   IIIIII  NNN   NN
 * MMMM  MMMM  AA  AA    II    NNNN  NN
 * MM MMMM MM  AAAAAA    II    NN NN NN
 * MM  MM  MM  AA  AA    II    NN  NNNN
 * MM      MM  AA  AA  IIIIII  NN   NNN  */

      /* CS_MAIN: main loop - process a 'statement'. */
      case CS_MAIN+CRN_OBRAC: /* we've encountered a { - compound statement */
        cTemp.cInstruction=COP_NULL;
        cTemp.cData.cuValid=CV_SEMI|CV_FUNCT|CV_VAR|CV_OPERATOR|CV_ORBRAC|
                            CV_OBRAC|CV_RESWRD|CV_COMMENT|CV_CBRAC;
        COMPILE_PUSH(&cTemp);
        cTemp.cData.cuState=CS_MAIN+0x200; /* brace handler */
        COMPILE_PUSH(&cTemp);
        COMPILE_UPSTACK();
        cState=CS_MAIN; /* call statement handler */
        cValid=CV_COMMENT|CV_VAR|CV_FUNCT|CV_RESWRD|CV_ORBRAC|CV_OPERATOR|
               CV_OBRAC|CV_SEMI|CV_CBRAC;
        /* NOTE: THIS IS THE ONLY PLACE ALLOWD TO CALL CS_MAIN WITH CV_CBRAC */
        break;
      case CS_MAIN+CRN_CBRAC: /* closing char - empty { } */
        /* THIS SHOULD ONLY HAPPEN IMMEDIATELY FOLLOWING CS_MAIN+CRN_OBRAC */
        /* IN THE EVENT THAT THE CODER ENTERED " { } "                     */
        COMPILE_DOWNSTACK();
        COMPILE_PULL(&cTemp);
        COMPILE_PULL(&cTemp);
        COMPILE_DOWNSTACK();
        COMPILE_PULL(&cTemp); /* return to calling state */
        cState=cTemp.cData.cuState;
        COMPILE_PULL(&cTemp);
        cValid=cTemp.cData.cuValid;
        break;
      case CS_MAIN+CRN_SEMI: /* null statement */
        COMPILE_DOWNSTACK();
        COMPILE_PULL(&cTemp); /* return to calling state */
        cState=cTemp.cData.cuState;
        COMPILE_PULL(&cTemp);
        cValid=cTemp.cData.cuValid;
        break;
      case CS_MAIN+CRN_FUNCT: /* expression statement */
      case CS_MAIN+CRN_VAR:
      case CS_MAIN+CRN_OPERATOR:
      case CS_MAIN+CRN_ORBRAC:
        COMPILE_GETLASTSRC(); /* whoah! backup! */
        cTemp.cInstruction=COP_NULL;
        cTemp.cData.cuValid=CV_SEMI|CV_COMMENT;
        COMPILE_PUSH(&cTemp);
        cTemp.cData.cuState=CS_MAIN+0x100; /* goto wrapup after this */
        COMPILE_PUSH(&cTemp);
        cState=CS_GET_EXP;  /* Ok: we're just throwing the whole line at   */
                            /* CS_GET_EXP. It's going to return us cVarFlag*/
                            /* and cExpFlag. We then let our wrapup state  */
                            /* compare what we get back to what we expected*/
                            /* (ie what we just put on the stack) and let  */
                            /* it handle any errors. The CS_GET_EXP should */
                            /* be able to handle anything with the exprsn. */
        cValid=CV_COMMENT|CV_ORBRAC|CV_STRING|CV_NUMBER|CV_FUNCT|CV_VAR|
               CV_OPERATOR;
        COMPILE_UPSTACK();
        break;
      case CS_MAIN+CRN_RESWRD:
        if ((cReserved==CRES_INT)||
            (cReserved==CRES_STR)||
            (cReserved==CRES_THING)||
            (cReserved==CRES_EXIT)||
            (cReserved==CRES_EXTRA)||
            (cReserved==CRES_LOCAL)||
            (cReserved==CRES_GLOBAL)||
            (cReserved==CRES_PRIVATE)) {
          COMPILE_GETLASTSRC();
          cState=CS_VAR; /* goto variable handler - let it return to caller. */
          cValid=CV_RESWRD|CV_COMMENT;
        } else if (cReserved==CRES_IF) {
          COMPILE_GETLASTSRC();
          cState=CS_IF; /* goto if handler - let it return to caller. */
          cValid=CV_RESWRD|CV_COMMENT;
        } else if (cReserved==CRES_WHILE) {
          COMPILE_GETLASTSRC();
          cState=CS_WHILE; /* goto if handler - let it return to caller. */
          cValid=CV_RESWRD|CV_COMMENT;
        } else if (cReserved==CRES_ELSE) {
          sprintf(cError,"compile error: reserved word `%s' is invalid here.\n",cLabel);
          CompileErrorReport(cError);
          return(COMPILE_SYNTAX_ERROR);
        } else if((cReserved==CRES_STOP)||
                  (cReserved==CRES_RETURN)) {
          COMPILE_GETLASTSRC();
          cState=CS_STOP; /* goto if handler - let it return to caller. */
          cValid=CV_RESWRD|CV_COMMENT;
        } else if ((cReserved==CRES_CHAR)||
                   (cReserved==CRES_CONST)||
                   (cReserved==CRES_DOUBLE)||
                   (cReserved==CRES_FLOAT)||
                   (cReserved==CRES_LONG)||
                   (cReserved==CRES_PUBLIC)||
                   (cReserved==CRES_SHORT)||
                   (cReserved==CRES_SIGNED)||
                   (cReserved==CRES_STRUCT)||
                   (cReserved==CRES_TYPEDEF)||
                   (cReserved==CRES_UNION)||
                   (cReserved==CRES_UNSIGNED)||
                   (cReserved==CRES_CONTINUE)||
                   (cReserved==CRES_VOID)) {
          sprintf(cError,"compile error: reserved word `%s' not supported.\n",cLabel);
          CompileErrorReport(cError);
          sprintf(cError,"Only `str', `int', `thing', `local', `global', and `private' are valid\n");
          CompileWarningReport(cError);
          sprintf(cError,"variable types.\n");
          CompileWarningReport(cError);
          return(COMPILE_SYNTAX_ERROR);
        } else {
          sprintf(cError,"compile error: reserved word `%s' not supported\n",cLabel);
          CompileErrorReport(cError);
          return(COMPILE_INTERNAL_ERROR);
        }
        break;
      case CS_MAIN+0x100+CRN_SEMI: /* this is our CS_MAIN expr wrapup state */
        if ((!(cExpFlag & CEFLAG_FUNCT))&&(!(cExpFlag&CEFLAG_EXP))) {
          sprintf(cError,"compile error: this is not a valid assignment or function call.\n");
          CompileErrorReport(cError);
          return(COMPILE_SYNTAX_ERROR);
        }
        /* Everything came back ok! */
        /* Since this is the MAIN loop, and both a function and an     */
        /* expression will leave a value on the stack, compile a pop.  */
        cTemp.cInstruction=COP_POP;
        COMPILE_COMPILE(&cTemp);
        COMPILE_DOWNSTACK();
        COMPILE_PULL(&cTemp); /* return to calling state */
        cState=cTemp.cData.cuState;
        COMPILE_PULL(&cTemp);
        cValid=cTemp.cData.cuValid;
        break;
      case CS_MAIN+0x200+CRN_CBRAC: /* close brace `}' */
        COMPILE_DOWNSTACK(); /* return to caller */
        COMPILE_PULL(&cTemp);
        cState=cTemp.cData.cuState;
        COMPILE_PULL(&cTemp);
        cValid=cTemp.cData.cuValid;
        break;
      case CS_MAIN+0x200+CRN_SEMI: /* more of this compound statement! */
      case CS_MAIN+0x200+CRN_FUNCT:
      case CS_MAIN+0x200+CRN_VAR:
      case CS_MAIN+0x200+CRN_OPERATOR:
      case CS_MAIN+0x200+CRN_ORBRAC:
      case CS_MAIN+0x200+CRN_OBRAC:
      case CS_MAIN+0x200+CRN_RESWRD: 
        COMPILE_GETLASTSRC(); /* there's more in this {}! */
                              /* backup & do it again.    */
        cTemp.cInstruction=COP_NULL;
        cTemp.cData.cuValid=CV_SEMI|CV_FUNCT|CV_VAR|CV_OPERATOR|CV_ORBRAC|
                            CV_OBRAC|CV_RESWRD|CV_COMMENT|CV_CBRAC;
        COMPILE_PUSH(&cTemp);
        cTemp.cData.cuState=CS_MAIN+0x200;
        COMPILE_PUSH(&cTemp);
        COMPILE_UPSTACK();
        cValid=CV_SEMI|CV_FUNCT|CV_VAR|CV_OPERATOR|CV_ORBRAC|CV_OBRAC|
               CV_RESWRD|CV_COMMENT;
        cState=CS_MAIN;
        break;

/* IIIIII FFFFFF
 *   II   FF
 *   II   FFFF
 *   II   FF
 * IIIIII FF         */

      case CS_IF+CRN_RESWRD:
        cValid=CV_ORBRAC|CV_COMMENT; /* now, expect `(' */
        cState=CS_IF+0x100;
        break;
      case CS_IF+0x100+CRN_ORBRAC:
        cTemp.cInstruction=COP_NULL;
        cTemp.cData.cuValid=CV_COMMENT|CV_CRBRAC; 
        COMPILE_PUSH(&cTemp);
        cTemp.cData.cuState=CS_IF+0x200;
        COMPILE_PUSH(&cTemp);
        COMPILE_UPSTACK();
        cValid=CV_COMMENT|CV_ORBRAC|CV_STRING|CV_NUMBER|CV_FUNCT|CV_VAR
               |CV_OPERATOR|CV_CRBRAC;
        cState=CS_GET_EXP; /* get expression to evaluate */
        break;
      case CS_IF+0x200+CRN_CRBRAC:
        if (!(cExpFlag&CEFLAG_RVALUE)) { /* nothing to evaluate */
          CompileErrorReport("compile error: empty `if' (nothing to evaluate)\n");
          return(COMPILE_SYNTAX_ERROR);
        }
        if (!(cVarFlag&CDFLAG_INT)) { /* no int to evaluate! */
          CompileErrorReport("compile error: `if' must have integer expression!\n");
          return(COMPILE_SYNTAX_ERROR);
        }
        cTemp.cInstruction=COP_NULL; /* save `if' location to adj. offset jmp */
        cTemp.cData.cuOffset=cComp;
        COMPILE_PUSH(&cTemp);
        cTemp.cInstruction=COP_IFZ;
        cTemp.cData.cuPointer=0;
        COMPILE_COMPILE(&cTemp);
        /* we don't know what we're returning to, nor do we care! */
        /* Unless, of course, it's an `else'.                     */
        cTemp.cData.cuValid=CV_EOF|CV_OPERATOR|CV_COMMENT|CV_COMMA|CV_PERIOD|
                            CV_OSBRAC|CV_CSBRAC|CV_OBRAC|CV_CBRAC|CV_ORBRAC|
                            CV_CRBRAC|CV_STRING|CV_SEMI|CV_NUMBER|CV_RESWRD|
                            CV_FUNCT|CV_VAR|CV_UNDEF;
        COMPILE_PUSH(&cTemp);
        cTemp.cData.cuState=CS_IF+0x300;
        COMPILE_PUSH(&cTemp);
        COMPILE_UPSTACK();
        cState=CS_MAIN; /* compile statement */
        cValid=CV_SEMI|CV_FUNCT|CV_VAR|CV_OPERATOR|CV_ORBRAC|CV_OBRAC|
               CV_RESWRD|CV_COMMENT;
        break;
      case CS_IF+0x300+CRN_EOF:
      case CS_IF+0x300+CRN_OPERATOR:
      case CS_IF+0x300+CRN_COMMENT:
      case CS_IF+0x300+CRN_COMMA:
      case CS_IF+0x300+CRN_PERIOD:
      case CS_IF+0x300+CRN_OSBRAC:
      case CS_IF+0x300+CRN_CSBRAC:
      case CS_IF+0x300+CRN_OBRAC:
      case CS_IF+0x300+CRN_CBRAC:
      case CS_IF+0x300+CRN_ORBRAC:
      case CS_IF+0x300+CRN_CRBRAC:
      case CS_IF+0x300+CRN_STRING:
      case CS_IF+0x300+CRN_SEMI:
      case CS_IF+0x300+CRN_NUMBER:
      case CS_IF+0x300+CRN_FUNCT:
      case CS_IF+0x300+CRN_UNDEF:
      case CS_IF+0x300+CRN_VAR:
      case CS_IF+0x400+CRN_EOF:      /* yes, overlapping states. But look at it.*/
      case CS_IF+0x400+CRN_OPERATOR: /* This is saving a fair amount of code.   */
      case CS_IF+0x400+CRN_COMMENT:
      case CS_IF+0x400+CRN_COMMA:
      case CS_IF+0x400+CRN_PERIOD:
      case CS_IF+0x400+CRN_OSBRAC:
      case CS_IF+0x400+CRN_CSBRAC:
      case CS_IF+0x400+CRN_OBRAC:
      case CS_IF+0x400+CRN_CBRAC:
      case CS_IF+0x400+CRN_ORBRAC:
      case CS_IF+0x400+CRN_CRBRAC:
      case CS_IF+0x400+CRN_STRING:
      case CS_IF+0x400+CRN_SEMI:
      case CS_IF+0x400+CRN_NUMBER:
      case CS_IF+0x400+CRN_FUNCT:
      case CS_IF+0x400+CRN_UNDEF:
      case CS_IF+0x400+CRN_VAR:
      case CS_IF+0x400+CRN_RESWRD: /* second time - no check for an 'else' */
        COMPILE_PULL(&cTemp);
        cCompBuf[cTemp.cData.cuPointer+1]=(BYTE)(cComp&0x00ff); /* adj if jmp */
        cCompBuf[cTemp.cData.cuPointer+2]=(BYTE)((cComp&0xff00)>>8);
        /* boogey on back to home base - ie return to caller */
        COMPILE_GETLASTSRC();
        COMPILE_DOWNSTACK();
        COMPILE_PULL(&cTemp); /* return to calling state */
        cState=cTemp.cData.cuState;
        COMPILE_PULL(&cTemp);
        cValid=cTemp.cData.cuValid;
        break;
      case CS_IF+0x300+CRN_RESWRD:
        if (cReserved==CRES_ELSE) { /* there's an else! */
          COMPILE_PULL(&cTemp); /* mark zero  */
          cCompBuf[cTemp.cData.cuPointer+1]=(BYTE)((cComp+3)&0x00ff); /* adj if jmp */
          cCompBuf[cTemp.cData.cuPointer+2]=(BYTE)(((cComp+3)&0xff00)>>8);
          cTemp.cInstruction=COP_NULL; /* save `goto' location to adj. offset jmp */
          cTemp.cData.cuOffset=cComp;
          COMPILE_PUSH(&cTemp);
          cTemp.cInstruction=COP_GOTO;
          cTemp.cData.cuPointer=0;
          COMPILE_COMPILE(&cTemp);
          /* we don't know what we're returning to, nor do we care! */
          /* Unless, of course, it's an `else'.                     */
          cTemp.cData.cuValid=CV_EOF|CV_OPERATOR|CV_COMMENT|CV_COMMA|CV_PERIOD|
                              CV_OSBRAC|CV_CSBRAC|CV_OBRAC|CV_CBRAC|CV_ORBRAC|
                              CV_CRBRAC|CV_STRING|CV_SEMI|CV_NUMBER|CV_RESWRD|
                              CV_FUNCT|CV_VAR|CV_UNDEF;
          COMPILE_PUSH(&cTemp);
          cTemp.cData.cuState=CS_IF+0x400;
          COMPILE_PUSH(&cTemp);
          COMPILE_UPSTACK();
          cState=CS_MAIN; /* compile statement */
          cValid=CV_SEMI|CV_FUNCT|CV_VAR|CV_OPERATOR|CV_ORBRAC|CV_OBRAC|
                 CV_RESWRD|CV_COMMENT;
          break;
        } else {
          COMPILE_PULL(&cTemp);
          cCompBuf[cTemp.cData.cuPointer+1]=(BYTE)(cComp&0x00ff); /* adj if jmp */
          cCompBuf[cTemp.cData.cuPointer+2]=(BYTE)((cComp&0xff00)>>8);
          /* boogey on back to home base - ie return to caller */
          COMPILE_GETLASTSRC();
          COMPILE_DOWNSTACK();
          COMPILE_PULL(&cTemp); /* return to calling state */
          cState=cTemp.cData.cuState;
          COMPILE_PULL(&cTemp);
          cValid=cTemp.cData.cuValid;
        }
        break;

/* WW     WW  HH   HH  IIIIII  LL      EEEEEE
 * WW WWW WW  HH   HH    II    LL      EE
 * WWWW WWWW  HHHHHHH    II    LL      EEEE
 * WWW   WWW  HH   HH    II    LL      EE
 * WW     WW  HH   HH  IIIIII  LLLLLL  EEEEEE          */

      case CS_WHILE+CRN_RESWRD:
        cValid=CV_ORBRAC|CV_COMMENT; /* now, expect `(' */
        cState=CS_WHILE+0x100;
        break;
      case CS_WHILE+0x100+CRN_ORBRAC:
        cTemp.cInstruction=COP_GOTO; /* save `goto' location to adj. goto */
        cTemp.cData.cuOffset=cComp;
        COMPILE_PUSH(&cTemp);
        cTemp.cInstruction=COP_NULL;
        cTemp.cData.cuValid=CV_COMMENT|CV_CRBRAC; 
        COMPILE_PUSH(&cTemp);
        cTemp.cData.cuState=CS_WHILE+0x200;
        COMPILE_PUSH(&cTemp);
        COMPILE_UPSTACK();
        cValid=CV_COMMENT|CV_ORBRAC|CV_STRING|CV_NUMBER|CV_FUNCT|CV_VAR
               |CV_OPERATOR|CV_CRBRAC;
        cState=CS_GET_EXP; /* get expression to evaluate */
        break;
      case CS_WHILE+0x200+CRN_CRBRAC:
        if (!(cExpFlag&CEFLAG_RVALUE)) { /* nothing to evaluate */
          CompileErrorReport("compile error: empty `while' (nothing to evaluate)\n");
          return(COMPILE_SYNTAX_ERROR);
        }
        if (!(cVarFlag&CDFLAG_INT)) { /* no int to evaluate! */
          CompileErrorReport("compile error: `if' must have integer expression!\n");
          return(COMPILE_SYNTAX_ERROR);
        }
        cTemp.cInstruction=COP_NULL; /* save `while' location to adj. goto */
        cTemp.cData.cuOffset=cComp;
        COMPILE_PUSH(&cTemp);
        cTemp.cInstruction=COP_WHILEZ;
        cTemp.cData.cuPointer=0;
        COMPILE_COMPILE(&cTemp);
        /* we don't know what we're returning to, nor do we care! */
        /* Unless, of course, it's an `else'.                     */
        cTemp.cData.cuValid=CV_EOF|CV_OPERATOR|CV_COMMENT|CV_COMMA|CV_PERIOD|
                            CV_OSBRAC|CV_CSBRAC|CV_OBRAC|CV_CBRAC|CV_ORBRAC|
                            CV_CRBRAC|CV_STRING|CV_SEMI|CV_NUMBER|CV_RESWRD|
                            CV_FUNCT|CV_VAR|CV_UNDEF;
        COMPILE_PUSH(&cTemp);
        cTemp.cData.cuState=CS_WHILE+0x300;
        COMPILE_PUSH(&cTemp);
        COMPILE_UPSTACK();
        cState=CS_MAIN; /* compile statement */
        cValid=CV_SEMI|CV_FUNCT|CV_VAR|CV_OPERATOR|CV_ORBRAC|CV_OBRAC|
               CV_RESWRD|CV_COMMENT;
        break;
      case CS_WHILE+0x300+CRN_EOF:
      case CS_WHILE+0x300+CRN_OPERATOR:
      case CS_WHILE+0x300+CRN_COMMENT:
      case CS_WHILE+0x300+CRN_COMMA:
      case CS_WHILE+0x300+CRN_PERIOD:
      case CS_WHILE+0x300+CRN_OSBRAC:
      case CS_WHILE+0x300+CRN_CSBRAC:
      case CS_WHILE+0x300+CRN_OBRAC:
      case CS_WHILE+0x300+CRN_CBRAC:
      case CS_WHILE+0x300+CRN_ORBRAC:
      case CS_WHILE+0x300+CRN_CRBRAC:
      case CS_WHILE+0x300+CRN_STRING:
      case CS_WHILE+0x300+CRN_SEMI:
      case CS_WHILE+0x300+CRN_NUMBER:
      case CS_WHILE+0x300+CRN_FUNCT:
      case CS_WHILE+0x300+CRN_UNDEF:
      case CS_WHILE+0x300+CRN_RESWRD:
      case CS_WHILE+0x300+CRN_VAR:
        COMPILE_PULL(&cTemp); /* mark zero  */
        cCompBuf[cTemp.cData.cuPointer+1]=(BYTE)((cComp+3)&0x00ff); /* adj while jmp */
        cCompBuf[cTemp.cData.cuPointer+2]=(BYTE)(((cComp+3)&0xff00)>>8);
        COMPILE_COMPILESTACK();
        /* boogey on back to home base - ie return to caller */
        COMPILE_GETLASTSRC();
        COMPILE_DOWNSTACK();
        COMPILE_PULL(&cTemp); /* return to calling state */
        cState=cTemp.cData.cuState;
        COMPILE_PULL(&cTemp);
        cValid=cTemp.cData.cuValid;
        break;

/* EEEEEE  XX    XX  IIIIII  TTTTTT
 * EE       XX  XX     II      TT 
 * EEEE      XXXX      II      TT 
 * EE       XX  XX     II      TT 
 * EEEEEE  XX    XX  IIIIII    TT      */

      case CS_STOP+CRN_RESWRD:
        cValid=CV_SEMI|CV_COMMENT; /* now, expect `;' */
        cState=CS_STOP+0x100;
        cTemp.cInstruction=COP_TERM;
        COMPILE_COMPILE(&cTemp);
        break;
      case CS_STOP+0x100+CRN_SEMI:
        COMPILE_DOWNSTACK();
        COMPILE_PULL(&cTemp); /* return to calling state */
        cState=cTemp.cData.cuState;
        COMPILE_PULL(&cTemp);
        cValid=cTemp.cData.cuValid;
        break;

/*  GGGGG  EEEEEE  TTTTTT         EEEEEE  XX    XX  PPPPP
 * GG      EE        TT           EE       XX  XX   PP  PP
 * GG GGG  EEEE      TT           EEEE      XXXX    PPPPP
 * GG  GG  EE        TT           EE       XX  XX   PP
 *  GGGGG  EEEEEE    TT   ______  EEEEEE  XX    XX  PP      */

      /* just a note before we get into this.                          */
      /* Get_Exp returns, in cExpFlag and cVarFlag, the type of        */
      /* expression read in. cExpFlag indicates: is it a valid lvalue? */
      /* rvalue? Is it a full equation (ie a=b+c as opposed to b+c)?   */
      /* Is it a valid function call?                                  */
      /* cVarFlag indicates the type of variables involved.            */
      case CS_GET_EXP+CRN_SEMI: /* null expression */
        cTemp.cInstruction=COP_PUSHV;
        cTemp.cData.cuSLWord=0;
        COMPILE_GETLASTSRC(); /* whoah! backup and return ; */
        COMPILE_DOWNSTACK();
        COMPILE_PULL(&cTemp); /* return to calling state */
        cState=cTemp.cData.cuState;
        COMPILE_PULL(&cTemp);
        cValid=cTemp.cData.cuValid;
        break;
      case CS_GET_EXP+CRN_CRBRAC:
        cNumParam=0; /* we got a SomeFunct() type call - no params */
        COMPILE_GETLASTSRC(); /* whoah! backup and return ) */
        cVarFlag=cExpFlag=0;
        COMPILE_DOWNSTACK();
        COMPILE_PULL(&cTemp); /* return to calling state */
        cState=cTemp.cData.cuState;
        COMPILE_PULL(&cTemp);
        cValid=cTemp.cData.cuValid;
        break;
      case CS_GET_EXP+CRN_FUNCT:
      case CS_GET_EXP+CRN_VAR:
      case CS_GET_EXP+CRN_STRING:
      case CS_GET_EXP+CRN_NUMBER:
      case CS_GET_EXP+CRN_ORBRAC:
      case CS_GET_EXP+CRN_OPERATOR: /* for -, !, ++, --, ~ */
        COMPILE_GETLASTSRC(); /* backup! */
        cTemp.cInstruction=COP_NULL; /* start with "all possibilities" */
        cTemp.cData.cuExpFlag=CEFLAG_FUNCT|CEFLAG_LVALUE|CEFLAG_RVALUE|
                              CEFLAG_EXP; 
        COMPILE_PUSH(&cTemp); /* and slowly whittle them away */
        cTemp.cData.cuVarFlag=CDFLAG_STR|CDFLAG_INT|CDFLAG_THING|CDFLAG_NULL|
                              CDFLAG_EXTRA|CDFLAG_EXIT;
        COMPILE_PUSH(&cTemp);
        cTemp.cData.cuValid=CV_COMMENT|CV_OPERATOR|CV_COMMA|CV_SEMI|CV_CRBRAC;
        COMPILE_PUSH(&cTemp);
        cTemp.cData.cuState=CS_GET_EXP+0x100;
        COMPILE_PUSH(&cTemp);
        COMPILE_UPSTACK();
        cValid=CV_COMMENT|CV_FUNCT|CV_NUMBER|CV_ORBRAC|CV_VAR|CV_STRING|CV_OPERATOR;
        cState=CS_GET_NEXT;
        break;
      case CS_GET_EXP+0x100+CRN_SEMI:
      case CS_GET_EXP+0x100+CRN_CRBRAC:
      case CS_GET_EXP+0x100+CRN_COMMA:
        COMPILE_PULL(&cTemp); /* pull cVarFlag */
        cVarFlag=cVarFlag & cTemp.cData.cuVarFlag;
        COMPILE_PULL(&cTemp); /* pull cExpFlag */
        cExpFlag=cExpFlag & cTemp.cData.cuExpFlag;
        if (!(cVarFlag)) { /* wrong data type */
          sprintf(cError,"compile error: incompatible data types.\n");
          CompileErrorReport(cError);
          return(COMPILE_SYNTAX_ERROR);
        }
        /* compile anything left on the stack */
        while(cSP) {
          COMPILE_COMPILESTACK();
          } 
        /* backup and exit */
        COMPILE_GETLASTSRC(); 
        COMPILE_DOWNSTACK();
        COMPILE_PULL(&cTemp); /* pull cState */
        cState=cTemp.cData.cuState;
        COMPILE_PULL(&cTemp); /* pull cValid */
        cValid=cTemp.cData.cuValid;
        break;
      case CS_GET_EXP+0x100+CRN_OPERATOR:
        COMPILE_PULL(&cTemp); /* pull cVarFlag */
        cVarFlag=cVarFlag & cTemp.cData.cuVarFlag;
        COMPILE_PULL(&cTemp); /* pull cExpFlag */
        cExpFlag=cExpFlag & cTemp.cData.cuExpFlag; /* compare data types */
        if (!(cVarFlag)) { /* wrong data type */
          sprintf(cError,"compile error: incompatible data types.\n");
          CompileErrorReport(cError);
          return(COMPILE_SYNTAX_ERROR);
        }
        if ((cOperation&0xff)<=COP_EQU) { /* equate operation */
          if (!(cExpFlag&CEFLAG_LVALUE)) { /* invalid lvalue */
            sprintf(cError,"compile error: invalid lvalue in expression <lvalue>=<rvalue>\n");
            CompileErrorReport(cError);
            return(COMPILE_SYNTAX_ERROR);
          }
          if (((cOperation&0xff)!=COP_EQU)&& /* check for non-`=' and non-ints!*/
              (!(cVarFlag&CDFLAG_INT))) {
            sprintf(cError,"compile error: can only use this operator on `int' types.\n");
            CompileErrorReport(cError);
            return(COMPILE_SYNTAX_ERROR);
          }
          cTemp.cInstruction=cOperation&0xff;
          COMPILE_PUSH(&cTemp);
          /* just call CS_GET_EXP, make sure it's a valid rvalue, & exit */
          cTemp.cInstruction=COP_NULL;
          cTemp.cData.cuExpFlag=cExpFlag;
          COMPILE_PUSH(&cTemp);
          cTemp.cData.cuVarFlag=cVarFlag;
          COMPILE_PUSH(&cTemp);
          cTemp.cData.cuValid=CV_COMMENT|CV_SEMI|CV_COMMA|CV_CRBRAC;
          COMPILE_PUSH(&cTemp);
          cTemp.cData.cuState=CS_GET_EXP+0x200;
          COMPILE_PUSH(&cTemp);
          /* and call CS_GET_EXP */
          COMPILE_UPSTACK();
          cValid=CV_COMMENT|CV_ORBRAC|CV_STRING|CV_NUMBER|CV_FUNCT|CV_VAR|CV_OPERATOR;
          cState=CS_GET_EXP;
        } /* if (equate) */
        else if (((cOperation&0xff)==COP_ASUB)||
                 ((cOperation&0xff)==COP_AADD)) { /* ++ or -- */
          if (!(cVarFlag&CDFLAG_INT)) { /* invalid op with this type */
            sprintf(cError,"compile error: can only use this operator on `int' types.\n");
            CompileErrorReport(cError);
            return(COMPILE_SYNTAX_ERROR);
          }
          if (!(cExpFlag&CEFLAG_LVALUE)) { /* no lvalue! AAAAA */
            sprintf(cError,"compile error: modifiable lvalue (ie a variable) is needed to use ++ or --\n");
            CompileErrorReport(cError);
            return(COMPILE_SYNTAX_ERROR);
          }
          cExpFlag=(cExpFlag|CEFLAG_LVALUE|CEFLAG_FUNCT)
                  -(CEFLAG_LVALUE|CEFLAG_FUNCT);
          cExpFlag|=CEFLAG_EXP;
          cTemp.cInstruction=cOperation&0xFF;
          COMPILE_COMPILE(&cTemp); /* compile unary operator */
          cTemp.cInstruction=COP_NULL; /* and push back cExpFlag & cVarFlag */
          cTemp.cData.cuExpFlag=cExpFlag;
          COMPILE_PUSH(&cTemp);
          cTemp.cData.cuVarFlag=cVarFlag;
          COMPILE_PUSH(&cTemp);
        } /* -- or ++ operators */
        else if ( (((cOperation&0xff)==COP_BEQU)||
                   ((cOperation&0xff)==COP_BNEQU))&& 
                  (!(cVarFlag&CDFLAG_INT)) )  { /* == or != w/ non-int */
          /* this is a special case because we have to turn a non-int  */
          /* expression such as `a==b' (assuming a & b are non-ints)   */
          /* into an int expression so it can be evaluated properly!   */
          cTemp.cInstruction=cOperation&0xff;
          COMPILE_PUSH(&cTemp);
          /* just call CS_GET_EXP, make sure it's a valid rvalue, modify cVarFlags, & exit */
          cTemp.cInstruction=COP_NULL;
          cTemp.cData.cuExpFlag=cExpFlag;
          COMPILE_PUSH(&cTemp);
          cTemp.cData.cuVarFlag=cVarFlag;
          COMPILE_PUSH(&cTemp);
          cTemp.cData.cuValid=CV_COMMENT|CV_SEMI|CV_COMMA|CV_CRBRAC;
          COMPILE_PUSH(&cTemp);
          cTemp.cData.cuState=CS_GET_EXP+0x300;
          COMPILE_PUSH(&cTemp);
          /* and call CS_GET_EXP */
          COMPILE_UPSTACK();
          cValid=CV_COMMENT|CV_ORBRAC|CV_STRING|CV_NUMBER|CV_FUNCT|CV_VAR|CV_OPERATOR;
          cState=CS_GET_EXP;
        } /* == or != w/ non-int */
        else { /* everything else (non-equate operation) */
          if (!(cVarFlag&CDFLAG_INT)) { /* invalid op with this type */
            sprintf(cError,"compile error: can only use this operator on `int' types.\n");
            CompileErrorReport(cError);
            return(COMPILE_SYNTAX_ERROR);
          }
          /* adjust cExpFlag - it can't be these & have an operator! */
          cExpFlag=(cExpFlag|CEFLAG_LVALUE|CEFLAG_FUNCT|CEFLAG_EXP)
                  -(CEFLAG_LVALUE|CEFLAG_FUNCT|CEFLAG_EXP);
          /* push the instruction */
          cTemp.cInstruction=0xff; 
          while((cTemp.cInstruction > (cOperation&0xff)) && cSP) {
            COMPILE_PULL(&cTemp);
            if (cTemp.cInstruction > (cOperation&0xff)) {
              COMPILE_COMPILE(&cTemp);
            } /* if */
            else {
              COMPILE_PUSH(&cTemp);
            } /* else */
          } /* while */
          cTemp.cInstruction=cOperation&0xff;
          COMPILE_PUSH(&cTemp);
          cTemp.cData.cuExpFlag=cExpFlag; /* save everything & get next thing */
          COMPILE_PUSH(&cTemp);
          cTemp.cData.cuVarFlag=cVarFlag;
          COMPILE_PUSH(&cTemp);  
          cTemp.cData.cuValid=CV_COMMENT|CV_OPERATOR|CV_COMMA|CV_SEMI|CV_CRBRAC;
          COMPILE_PUSH(&cTemp);
          cTemp.cData.cuState=CS_GET_EXP+0x100;
          COMPILE_PUSH(&cTemp);
          COMPILE_UPSTACK();
          cValid=CV_COMMENT|CV_FUNCT|CV_NUMBER|CV_ORBRAC|CV_VAR|CV_STRING|CV_OPERATOR;
          cState=CS_GET_NEXT;
        } /* else non-equate operator */
        break;
      case CS_GET_EXP+0x200+CRN_SEMI:   /* this state handles expressions */
      case CS_GET_EXP+0x200+CRN_CRBRAC:
      case CS_GET_EXP+0x200+CRN_COMMA:
        COMPILE_PULL(&cTemp); /* pull cVarFlag */
        cVarFlag=cVarFlag & cTemp.cData.cuVarFlag;
        if (!(cVarFlag)) { /* wrong data type */
          sprintf(cError,"compile error: assignment involves incompatible data types.\n");
          CompileErrorReport(cError);
          return(COMPILE_SYNTAX_ERROR);
        }
        COMPILE_PULL(&cTemp); /* pull cExpFlag */
        if (!(cExpFlag&CEFLAG_RVALUE)) { /* invalid rvalue */
          sprintf(cError,"compile error: invalid rvalue in expression <lvalue>=<rvalue>\n");
          CompileErrorReport(cError);
          return(COMPILE_SYNTAX_ERROR);
        }
        /* note: we KNOW we have an expression, thus the CEFLAG_EXP etc above */
        cExpFlag=(cExpFlag&cTemp.cData.cuExpFlag)|CEFLAG_EXP;
        /* compile anything left on the stack */
        while(cSP) {
          COMPILE_COMPILESTACK();
        } 
        /* backup and exit */
        COMPILE_GETLASTSRC(); 
        COMPILE_DOWNSTACK();
        COMPILE_PULL(&cTemp); /* pull cState */
        cState=cTemp.cData.cuState;
        COMPILE_PULL(&cTemp); /* pull cValid */
        cValid=cTemp.cData.cuValid;
        break;
      case CS_GET_EXP+0x300+CRN_SEMI:   /* this state handles pointer == & != */
      case CS_GET_EXP+0x300+CRN_CRBRAC:
      case CS_GET_EXP+0x300+CRN_COMMA:
        COMPILE_PULL(&cTemp); /* pull cVarFlag */
        cVarFlag=cVarFlag & cTemp.cData.cuVarFlag;
        if (!(cVarFlag)) { /* wrong data type */
          sprintf(cError,"compile error: equality involves incompatible data types.\n");
          CompileErrorReport(cError);
          return(COMPILE_SYNTAX_ERROR);
        }
        COMPILE_PULL(&cTemp); /* pull cExpFlag */
        if (!(cExpFlag&CEFLAG_RVALUE)) { /* invalid rvalue */
          sprintf(cError,"compile error: invalid rvalue in expression <lvalue>=<rvalue>\n");
          CompileErrorReport(cError);
          return(COMPILE_SYNTAX_ERROR);
        }
        cExpFlag=((cExpFlag&cTemp.cData.cuExpFlag)|
                  (CEFLAG_EXP|CEFLAG_FUNCT|CEFLAG_LVALUE))
                 -(CEFLAG_EXP|CEFLAG_FUNCT|CEFLAG_LVALUE);
        cVarFlag=CDFLAG_INT; /* mark our equate function as a proper int */
        /* compile anything left on the stack */
        while(cSP) {
          COMPILE_COMPILESTACK();
        } 
        /* backup and exit */
        COMPILE_GETLASTSRC(); 
        COMPILE_DOWNSTACK();
        COMPILE_PULL(&cTemp); /* pull cState */
        cState=cTemp.cData.cuState;
        COMPILE_PULL(&cTemp); /* pull cValid */
        cValid=cTemp.cData.cuValid;
        break;

/* FFFFFF  UU   UU  NNN   NN   CCCC   TTTTTT  IIIIII   OOOO   NNN   NN
 * FF      UU   UU  NNNN  NN  CC  CC    TT      II    OO  OO  NNNN  NN
 * FFFF    UU   UU  NN NN NN  CC        TT      II    OO  OO  NN NN NN
 * FF      UU   UU  NN  NNNN  CC  CC    TT      II    OO  OO  NN  NNNN
 * FF       UUUUU   NN   NNN   CCCC     TT    IIIIII   OOOO   NN   NNN */

      case CS_FUNCTION+CRN_FUNCT:
        cTemp.cInstruction=COP_EXECR;  /* push function on stack */
        cTemp.cData.cuFunction=cFunction;
        COMPILE_PUSH(&cTemp);
        cTemp.cInstruction=COP_NULL; /* push this fn's return type */
        cTemp.cData.cuParamType=fTable[cFunction].fDataType;
        COMPILE_PUSH(&cTemp);
        cTemp.cData.cuUnsigned=cNumParam; /* push last fn's # params */
        COMPILE_PUSH(&cTemp);
        cNumParam=1; /* assume 1 param for this procedure - we are reset
                         to 0 if we have a ) as our first entry to our
                         expression! */
        counter=0;                     /* and push param types on stack */
        while(fTable[cFunction].fParamType[counter]!=CDT_NULL)
          counter++;
        /* you see, we want to put the parameters on the stack in reverse */
        /* order (it's a stack - get it? - FILO ring a bell?)             */
        if (!counter) { /* ie NO parameters */
          cTemp.cData.cuValid=CV_CRBRAC|CV_COMMENT|CV_COMMA;
          COMPILE_PUSH(&cTemp);
          cTemp.cData.cuState=CS_FUNCTION+0x400; /* empty ) handler */
          COMPILE_PUSH(&cTemp);
        }
        else { /* put parameter types on stack */
          counter--;
          cTemp.cData.cuParamType=fTable[cFunction].fParamType[counter];
          COMPILE_PUSH(&cTemp);
          cTemp.cData.cuValid=CV_CRBRAC|CV_COMMENT|CV_COMMA; /* first, our ) handler */
          COMPILE_PUSH(&cTemp);
          cTemp.cData.cuState=CS_FUNCTION+0x300; /* ) handler */
          COMPILE_PUSH(&cTemp);
          while(counter) { /* here's where we put them on the stack! */
            counter--;
            cTemp.cData.cuParamType=fTable[cFunction].fParamType[counter];
            COMPILE_PUSH(&cTemp);
            cTemp.cData.cuValid=CV_COMMENT|CV_COMMA|CV_CRBRAC;
            COMPILE_PUSH(&cTemp);
            cTemp.cData.cuState=CS_FUNCTION+0x200; /* , handler */
            COMPILE_PUSH(&cTemp);
          } /* while(counter) */
        } /* else */
  COMPILE_UPSTACK();
        cValid=CV_ORBRAC|CV_COMMENT; /* read ( */
        cState=CS_FUNCTION+0x100;
        break;
      case CS_FUNCTION+0x100+CRN_ORBRAC:
        /* the stack has been set up and everything, so we just have to */
        /* call CS_GET_EXP. This state is just to make sure there's a ( */
        cValid=CV_COMMENT|CV_ORBRAC|CV_STRING|CV_NUMBER|CV_FUNCT|CV_VAR|
               CV_CRBRAC|CV_OPERATOR;
        /* NOTE: we allow a CV_CRBRAC because we might have somefunct() */
        /*       but this should not be normal practice with CS_GET_EXP.*/
        cState=CS_GET_EXP;
        break;
      case CS_FUNCTION+0x200+CRN_COMMA:
        COMPILE_PULL(&cTemp);
        if ((!(cVarFlag&(1<<cTemp.cData.cuParamType)))||
          (!(cExpFlag&CEFLAG_RVALUE))) {
          sprintf(cError,"compile error: data type of parameter to function is incorrect.\n");
          CompileErrorReport(cError);
          return(COMPILE_SYNTAX_ERROR);
        }
        cNumParam++; /* increment our # parameters in this function call */
        COMPILE_UPSTACK();
        cValid=CV_COMMENT|CV_ORBRAC|CV_STRING|CV_NUMBER|CV_FUNCT|CV_VAR|CV_OPERATOR;
        cState=CS_GET_EXP;
        break;
      case CS_FUNCTION+0x200+CRN_CRBRAC:
        /* unless our param type is CDT_ETC, too few parameters to function */
        COMPILE_PULL(&cTemp);
        if (cTemp.cData.cuParamType==CDT_ETC) { 
          /* we've finally run out of parameters with a CDT_ETC param type. */
          /* let's pack up our bags and go home. */
          COMPILE_GETLASTSRC(); 
          COMPILE_PULL(&cTemp); /* return to calling state */
          cState=cTemp.cData.cuState;
          COMPILE_PULL(&cTemp);
          cValid=cTemp.cData.cuValid;
          break;
        }
        sprintf(cError,"compile error: Bill, while I agree that, in time, our band will be\n           most triumphant, there's too few parameters passed to this function.\n");
        CompileErrorReport(cError);
        return(COMPILE_SYNTAX_ERROR);
        break;
      case CS_FUNCTION+0x300+CRN_COMMA:
        COMPILE_PULL(&cTemp);
        if (cTemp.cData.cuParamType==CDT_ETC) { /* anything goes... */
          /* anything goes - as many variables, of any type */
          /* push our CDT_ETC back on the stack - for next time, and
           * see if there's "one more variable" - which will land us
           * back here!                                                */
          COMPILE_PUSH(&cTemp);
          cTemp.cData.cuValid=CV_CRBRAC|CV_COMMENT|CV_COMMA; /* first, our ) handler */
          COMPILE_PUSH(&cTemp);
          cTemp.cData.cuState=CS_FUNCTION+0x300; /* ) handler */
          COMPILE_PUSH(&cTemp);
          cNumParam++; /* increment our # parameters in this function call */
          COMPILE_UPSTACK();
          cValid=CV_COMMENT|CV_ORBRAC|CV_STRING|CV_NUMBER|CV_FUNCT|CV_VAR|CV_OPERATOR;
          cState=CS_GET_EXP;
          break;
        }
        /* too many parameters to function */
        sprintf(cError,"compile error: But Ted, there's too many parameters passed to this function!\n");
        CompileErrorReport(cError);
        return(COMPILE_SYNTAX_ERROR);
        break;
      case CS_FUNCTION+0x300+CRN_CRBRAC:
        COMPILE_PULL(&cTemp);
        if (cTemp.cData.cuParamType!=CDT_ETC) /* type is specified  */
           if ((!(cVarFlag&(1<<cTemp.cData.cuParamType)))||
              (!(cExpFlag&CEFLAG_RVALUE))) {
            sprintf(cError,"compile error: data type of parameter to function is incorrect.\n");
            CompileErrorReport(cError);
            return(COMPILE_SYNTAX_ERROR);
          }
        cTemp.cInstruction=COP_PUSHV; /* load in our # params specifier */
        cTemp.cData.cuSLWord=cNumParam;
        COMPILE_COMPILE(&cTemp);
        COMPILE_PULL(&cTemp); /* retrieve our last fn's cNumParam value */
        cNumParam=cTemp.cData.cuUnsigned; 
        COMPILE_PULL(&cTemp); /* get our return data type */
        cVarFlag=1<<cTemp.cData.cuParamType;
        cExpFlag=CEFLAG_RVALUE|CEFLAG_FUNCT; /* while we're setting flags */
        COMPILE_COMPILESTACK(); /* compile our COP_EXEC */
        COMPILE_DOWNSTACK();
        COMPILE_PULL(&cTemp); /* return to calling state */
        cState=cTemp.cData.cuState;
        COMPILE_PULL(&cTemp);
        cValid=cTemp.cData.cuValid;
        break;
      case CS_FUNCTION+0x400+CRN_CRBRAC: /* no-parameter ) handler */
        if (cVarFlag | cExpFlag) {
          sprintf(cError,"compile error: this function expects no parameters!\n");
          CompileErrorReport(cError);
          return(COMPILE_SYNTAX_ERROR);
        }
        cTemp.cInstruction=COP_PUSHV; /* load in our # params specifier */
        cTemp.cData.cuSLWord=cNumParam;
        COMPILE_COMPILE(&cTemp);
        COMPILE_PULL(&cTemp); /* retrieve our last fn's cNumParam value */
        cNumParam=cTemp.cData.cuUnsigned; 
        COMPILE_PULL(&cTemp); /* get our return data type */
        cVarFlag=1<<cTemp.cData.cuParamType;
        cExpFlag=CEFLAG_RVALUE|CEFLAG_FUNCT; /* while we're setting flags */
        COMPILE_COMPILESTACK(); /* compile our COP_EXEC */
        COMPILE_DOWNSTACK();
        COMPILE_PULL(&cTemp); /* return to calling state */
        cState=cTemp.cData.cuState;
        COMPILE_PULL(&cTemp);
        cValid=cTemp.cData.cuValid;
        break;
      case CS_FUNCTION+0x400+CRN_COMMA: 
        /* this state is just to print out a pleasant error message          */ 
        sprintf(cError,"compile error: this function expects no parameters!\n");
        CompileErrorReport(cError);
        return(COMPILE_SYNTAX_ERROR);
        break;

/*  GGGGG  EEEEEE  TTTTTT         NNN   NN  EEEEEE  XX    XX  TTTTTT
 * GG      EE        TT           NNNN  NN  EE       XX  XX     TT
 * GG GGG  EEEE      TT           NN NN NN  EEEE      XXXX      TT
 * GG  GG  EE        TT           NN  NNNN  EE       XX  XX     TT
 *  GGGGG  EEEEEE    TT   ______  NN   NNN  EEEEEE  XX    XX    TT   */

      case CS_GET_NEXT+CRN_FUNCT:
        COMPILE_GETLASTSRC(); /* whoah! backup! */
        cState=CS_FUNCTION; /* pass function over to function handler and */
                            /* let IT return to our previous state.       */
        cValid=CV_FUNCT;
        break;
      case CS_GET_NEXT+CRN_NUMBER:
        cTemp.cInstruction=COP_PUSHV;
        cTemp.cData.cuSLWord=CompileStrToNum(cLabel);
        COMPILE_COMPILE(&cTemp);
        cVarFlag=CDFLAG_INT;
        cExpFlag=CEFLAG_RVALUE;
        COMPILE_DOWNSTACK(); /* and return to our caller */
        COMPILE_PULL(&cTemp);
        cState=cTemp.cData.cuState;
        COMPILE_PULL(&cTemp);
        cValid=cTemp.cData.cuValid;
        break;
      case CS_GET_NEXT+CRN_STRING:
        cTemp.cInstruction=COP_PUSHPT;
        cTemp.cData.cuPtr=STRCREATE(cLabel);
        COMPILE_COMPILE(&cTemp);
        cVarFlag=CDFLAG_STR;
        cExpFlag=CEFLAG_RVALUE;
        COMPILE_DOWNSTACK(); /* and return to our caller */
        COMPILE_PULL(&cTemp);
        cState=cTemp.cData.cuState;
        COMPILE_PULL(&cTemp);
        cValid=cTemp.cData.cuValid;
        break;
      case CS_GET_NEXT+CRN_ORBRAC:
        cValid=CV_COMMENT|CV_ORBRAC|CV_STRING|CV_NUMBER|CV_FUNCT|CV_VAR|
               CV_CRBRAC|CV_OPERATOR;
        cState=CS_GET_EXP; /* let's get the expression in this bracket */
        cTemp.cInstruction=COP_NULL;
        cTemp.cData.cuValid=CV_CRBRAC|CV_COMMENT; /* and return to ) handler */
        COMPILE_PUSH(&cTemp);
        cTemp.cData.cuState=CS_GET_NEXT+0x100;
        COMPILE_PUSH(&cTemp);
        COMPILE_UPSTACK();
        break;
      case CS_GET_NEXT+CRN_VAR:
        if (cVarTable[cVar].cDomain==CDOMAIN_LOCAL) {
          cTemp.cInstruction=COP_PUSHL;
        } else if (cVarTable[cVar].cDomain==CDOMAIN_GLOBAL) {
          cTemp.cInstruction=COP_PUSHG;
        } else if (cVarTable[cVar].cDomain==CDOMAIN_PRIVATE) {
          cTemp.cInstruction=COP_PUSHP;
        } else {
          sprintf(cError,"internal error (compile.c): Unknown domain for `%s'.\nThis shouldn't happen. Notify your Sys Admin.\n",cVarTable[cVar].cText);
          CompileErrorReport(cError);
          return(COMPILE_INTERNAL_ERROR);
        }
        cTemp.cData.cuVar=cVarTable[cVar].cOffset;
        COMPILE_COMPILE(&cTemp);
        if (cVarTable[cVar].cType==CDT_INT) {
          cVarFlag=CDFLAG_INT;
        } else if (cVarTable[cVar].cType==CDT_STR) {
          cVarFlag=CDFLAG_STR;
        } else if (cVarTable[cVar].cType==CDT_THING) {
          cVarFlag=CDFLAG_THING;
        } else if (cVarTable[cVar].cType==CDT_EXTRA) {
          cVarFlag=CDFLAG_EXTRA;
        } else if (cVarTable[cVar].cType==CDT_EXIT) {
          cVarFlag=CDFLAG_EXIT;
        } else {
          sprintf(cError,"internal error (compile.c): Unknown variable type for `%s'.\nThis shouldn't happen. Notify your Sys Admin.\n",cVarTable[cVar].cText);
          CompileErrorReport(cError);
          return(COMPILE_INTERNAL_ERROR);
        }
        if ((cVarTable[cVar].cDomain==CDOMAIN_LOCAL)&&
            (cVarTable[cVar].cOffset<cSystemVariableStatic)) {
          cExpFlag=CEFLAG_RVALUE;
        } else {
          cExpFlag=CEFLAG_RVALUE|CEFLAG_LVALUE;
        }
        COMPILE_DOWNSTACK(); /* and return to our caller */
        COMPILE_PULL(&cTemp);
        cState=cTemp.cData.cuState;
        COMPILE_PULL(&cTemp);
        cValid=cTemp.cData.cuValid;
        break;
      case CS_GET_NEXT+CRN_OPERATOR: /* for `, !, \, ++, -- */
        if ((cOperation&0xff)==COP_SUB) { /* conversion of `-' */
          cOperation=((cOperation&0xff00)+COP_NEG);
        }
        if ((cOperation&0xff)==COP_ASUB) { /* conversion of `--' */
          cOperation=((cOperation&0xff00)+COP_BSUB);
        }
        if ((cOperation&0xff)==COP_AADD) { /* conversion of `++' */
          cOperation=((cOperation&0xff00)+COP_BADD);
        }

        if (((cOperation&0xff)!=COP_COMP)&&
            ((cOperation&0xff)!=COP_BSUB)&&
            ((cOperation&0xff)!=COP_BADD)&&
            ((cOperation&0xff)!=COP_NOT)&&
            ((cOperation&0xff)!=COP_NEG)) { /* non-unary operation */
          CompileErrorReport("compile error: unexpected operator.\n");
          return(COMPILE_SYNTAX_ERROR);
        }
        cTemp.cInstruction=cOperation&0xff;  /* save instruction */
        COMPILE_PUSH(&cTemp);
        cTemp.cInstruction=COP_NULL; 
        cTemp.cData.cuValid=CV_COMMENT|CV_OPERATOR|CV_COMMA|CV_SEMI|CV_CRBRAC;
        COMPILE_PUSH(&cTemp);
        cTemp.cData.cuState=CS_GET_NEXT+0x200;
        COMPILE_PUSH(&cTemp);
        COMPILE_UPSTACK();
        cValid=CV_COMMENT|CV_FUNCT|CV_NUMBER|CV_ORBRAC|CV_VAR|CV_STRING|CV_OPERATOR;
        cState=CS_GET_NEXT;
        break;
      case CS_GET_NEXT+0x100+CRN_CRBRAC:
        /* cVarFlag is set. We just adjust cExpFlag to reflect ()'s and exit */
        cExpFlag=(cExpFlag|CEFLAG_LVALUE|CEFLAG_FUNCT)
                -(CEFLAG_LVALUE|CEFLAG_FUNCT);
        /* ie: since there's ()'s around it, it can't be funct, lvalue, exp.*/
        COMPILE_DOWNSTACK(); /* and return to our caller */
        COMPILE_PULL(&cTemp);
        cState=cTemp.cData.cuState;
        COMPILE_PULL(&cTemp);
        cValid=cTemp.cData.cuValid;
        break;
      case CS_GET_NEXT+0x200+CRN_OPERATOR: /* process uniary operator */
      case CS_GET_NEXT+0x200+CRN_SEMI:
      case CS_GET_NEXT+0x200+CRN_COMMA:
      case CS_GET_NEXT+0x200+CRN_CRBRAC:
        COMPILE_GETLASTSRC(); /* whoah! backup! */
        COMPILE_PULL(&cTemp); /* get instruction */
        if ((cTemp.cInstruction==COP_BSUB)||
            (cTemp.cInstruction==COP_BADD)) { /* we need an lvalue for these */
          if (!(cExpFlag&CEFLAG_LVALUE)) { /* no lvalue! AAAAA */
            sprintf(cError,"compile error: modifiable lvalue (ie a variable) is needed to use ++ or --\n");
            CompileErrorReport(cError);
            return(COMPILE_SYNTAX_ERROR);
          }
        }
        /* modify cExpFlag - it can't be any of these: */
        cExpFlag=(cExpFlag|CEFLAG_EXP|CEFLAG_LVALUE|CEFLAG_FUNCT)
                -(CEFLAG_EXP|CEFLAG_LVALUE|CEFLAG_FUNCT);
        cExpFlag|=CEFLAG_EXP;
        COMPILE_COMPILE(&cTemp); /* compile unary operator */
        COMPILE_DOWNSTACK(); /* and return to our caller */
        COMPILE_PULL(&cTemp);
        cState=cTemp.cData.cuState;
        COMPILE_PULL(&cTemp);
        cValid=cTemp.cData.cuValid;
        break;



      /* default: this state handles comments, or should never occur */
      default: 
        if ((cState & 0xFF)==CRN_COMMENT) { /* catch-all comment routine */
          COMPILE_COMPILECOMMENT(cLabel);
          cState=cState&0xFFFFFF00;
        }
        else {
          sprintf(cError,"internal error (compile.c): Unknown state 0x%08lX\nRecord this message and your program, and notify your Sys Admin.\n",cState);
          CompileErrorReport(cError);
          return(COMPILE_INTERNAL_ERROR);
        }
        break;
    } /* State machine switch */
  } /* State machine while(1) loop */
} /* Compile */

/* CompileInit
 * Initializes all structures required by Compile.c 
 */
void CompileInit() {
  ULWORD i;
  ULWORD cOffset;

  /* allocate our stack */
  cStackSizeByte=CSTACK_SIZE; /* default stack size */
  MALLOC((void *)cStack,BYTE,cStackSizeByte);

  /* allocate our stack frame */
  cSFPSizeByte=CSTACK_FRAME; /* default stack frame size */
  MALLOC((void *)cSFP,BYTE,cSFPSizeByte);

  /* allocate our compile buffer (where the binary code is stored) */
  cCompBufSizeByte=CCODE_SIZE; /* default buffer size */
  MALLOC((void *)cCompBuf,BYTE,cCompBufSizeByte);

  /* generate our global variable tables */
  cOffset=0;
  for (i=0;i<=cSystemVariable;i++) {
    if (cSysVar[i].cText!=NULL) {
      strcpy(cVarTable[cOffset].cText,cSysVar[i].cText);
      cVarTable[cOffset].cType=cSysVar[i].cType;
      cVarTable[cOffset].cDomain=CDOMAIN_LOCAL;
      cVarTable[cOffset].cOffset=cOffset;
      cOffset++;
    }
  }
}