/********** gpm.c *****************************************************/

/*
 * Variable A is the most-recently got character from the input stream.
 *
 */


/*
 * 940310 Changed method of making initial stack.	AdG
 */

#define GPM_C_

#include "gpmdefs.h"
#include "gpm.h"
#include "gpmmain.h"
#include "gpmmonitor.h"
#include "gpmbuiltin.h"
#include "gpmutil.h"

int CurrentChar;

int W;
int ClosedPtr = 0;
int OpenPtr = 0;
int EnvPtr = -1;		/* Environment on an empty stack. */
int QuoteLevel = 1;

int OutputPtr = OutputStream;
int InputPtr = InputStream;

int *Stack;			/* MUST be public */
int StackPtr = 0;		/* Top of an empty stack. */
int StackSize;

/*
 * This is the initial stack contents. 
 *
 * Each builtin function (DEF, ...) needs a token. A token should be
 * a small negative integer, ie. #define TOKEN_DEF (-1). Besides the
 * token, the builting needs to be added to the array builtins. This
 * is an array of builtin_t's, which contain the name of the builtin
 * function (ie. "DEF"), its token, and two (as yet unused) fields,
 * f, which is a function to call when the builting macro is called,
 * and def, which is a text string equal to the definition. A single
 * builtin should have *either* f or def defined, but not both.
 *
 * If a builtin has a definition, token should be 0. That is, legal
 * builtins are:
 *
 * {"string",TOKEN,function,NULL}	or
 * {"string",0,NULL,"def"}
 *
 * Where the second is equivalent to having "{DEF|string|<def>}" before
 * any input from files.
 *
 */

typedef struct
{
	char *name;
	int token;
	void (*f)();
	char *def;
} builtin_t;

#define TOKEN_DEF	(-1)
#define TOKEN_VAL	(-2)
#define TOKEN_UPDATE	(-3)
#define TOKEN_BIN	(-4)
#define TOKEN_DEC	(-5)
#define TOKEN_BAR	(-6)

#ifdef DEBUG_MACROS
#define TOKEN_MSG	(-7)
#define TOKEN_DUMP	(-9)
#define TOKEN_EXIT	(-10)
#endif

#ifdef TUPLE_MACROS
#define TOKEN_SUB	(-11)
#define TOKEN_LEN	(-12)
#endif

#define TOKEN_IFDEF	(-13)

builtin_t builtins[]=
{
	{"DEF",		TOKEN_DEF,	do_def,		NULL},
	{"VAL",		TOKEN_VAL,	do_val,		NULL},
	{"UPDATE",	TOKEN_UPDATE,	do_update,	NULL},
#ifdef DEBUG_MACROS
	{"MSG",		TOKEN_MSG,	do_msg,		NULL},
	{"EXIT",	TOKEN_EXIT,	do_exit,	NULL},
	{"DUMP",	TOKEN_DUMP,	do_dump,	NULL},
#endif
#ifdef TUPLE_MACROS
	{"SUB",		TOKEN_SUB,	do_sub,		NULL},
	{"LEN",		TOKEN_LEN,	do_len,		NULL},
#endif
	{"IFDEF",	TOKEN_IFDEF,	do_ifdef,	NULL},
	{"BIN",		TOKEN_BIN,	do_bin,		NULL},
	{"DEC",		TOKEN_DEC,	do_dec,		NULL},
	{"BAR",		TOKEN_BAR,	do_bar,		NULL}
} ;

#define NBUILTINS (sizeof(builtins)/sizeof(builtin_t))


/*
 * Static error messages needed in this module.
 */

static char *CloserMissing="Missing one or more %c.\n";

/*
 * Stack operations. Only push is implemented, because that's
 * the only operation gpm uses as such --- although NextCh
 * may hide a pop operation (something to check out).
 */

void Push(int item)
{
	if (StackPtr < StackSize)
		Stack[StackPtr++] = item;
	else
	{
		fprintf(stderr,"Stack overflow. ");
		PrintErrorLine();
		exit(1);
	}
}

void LoadC(int item)
{
	if (OutputPtr == OutputStream) putchar(item);
	else Push(item);
}

void LoadS(char *s)
{
	if (s==NULL) return;

	while(*s)
	{
		LoadC((int)*s++);
	}
}

void Load()
{
	LoadC(CurrentChar);
}







int iFind(where)
int where;
{
	int EnvFollower=EnvPtr;

	while(1)
	{
		int r;
		for (r = 0; r <= Stack[where] - 1; r++)
		{
			if (Stack[where + r] != Stack[EnvFollower + r + 1])
			{
				EnvFollower=Stack[EnvFollower];
				if (EnvFollower < 0)  return -1;
				break;
			}
		}
		if (r>=Stack[where])
		{
			return EnvFollower + 1 + Stack[where];
		}
	}
}

void Find(int where)
{
	int i;

	W = where;
	i = iFind(where);
	if (i < 0)	Monitor(MacroNotFound);
	else 		W = i;
}

void gpm()
{
  int	r;

Start:	
  	NextCh();
  if (CurrentChar == QuoteOpen) 
  {
    QuoteLevel++;
    goto Q2;
  }
  else if (CurrentChar == ApplyOpen) goto Fn;
  else if (CurrentChar == ItemSep) goto NextItem;
  else if (CurrentChar == ApplyClose) goto Apply;
  else if (CurrentChar == ArgStart) goto LoadArg;
  else if (CurrentChar == Marker) goto EndFn;
#ifdef QENDS
  else if (CurrentChar == QuoteClose) goto Exit;
#endif
  else if (CurrentChar == EOF) goto Exit;

Copy:	Load();
	if (QuoteLevel == 1) goto Start;
Q2:	NextCh();			/* replace with QuotedString */
	if (CurrentChar == QuoteOpen) {
		QuoteLevel++;
		goto Copy;
	}
        if (CurrentChar == EOF) goto Exit;
	if (CurrentChar != QuoteClose)
		goto Copy;
	QuoteLevel--;
	if (QuoteLevel == 1) goto Start;
	else goto Copy;

Fn:	Stack[StackPtr + 0] = OutputPtr;
	Stack[StackPtr + 1] = F;
	Stack[StackPtr + 2] = 0;
	Stack[StackPtr + 3] = 0;
	F = StackPtr + 1;
	OutputPtr = StackPtr + 3;
	StackPtr = StackPtr + 4;
	goto Start;

NextItem:
	if (OutputPtr == 0) goto Copy;
	Stack[OutputPtr] = StackPtr - OutputPtr - Stack[OutputPtr];
	Stack[StackPtr] = 0;
	OutputPtr = StackPtr;
	StackPtr = StackPtr + 1;
	goto Start;

Apply:	if (P > F) {
		Monitor1();
		goto Copy;
	}
	if (OutputPtr == OutputStream)
		goto Copy;
	{
		int tH = OutputPtr;
		int tF = F;
		int tS = StackPtr;
		int tP = P;
		F = Stack[tF];
		P = tF;
		OutputPtr = Stack[tF - 1];
		StackPtr = tS + 1;
		Stack[tH] = tS - tH;
		Stack[tS] = Marker;
		Stack[tF - 1] = tS - tF + 2;
		Stack[tF] = tP;
		Stack[tF + 1] = InputPtr;
	}
	if (OutputPtr != OutputStream)
		Stack[OutputPtr] = Stack[OutputPtr] + Stack[P - 1];
	Find(P + 2);

/*
 * Now that we've found the definition matchine the call, look
 * at what kind of associated string we've got. If the string
 * has a negative length, then it should be one of the tokens.
 *
 */

#ifdef DEBUG
	fprintf(stderr,"Found a macro.\n");
#endif

	if (Stack[W]<0)
	{
/*
 * The builtins have lengths smaller than zero. I suppose
 * definitions with lengths > 1<<31 are also counted as negative,
 * but that's not exactly relevant.
 */

/*
 * Search the list (?) of builtins for a builtin matching
 * the token found on the stack. (The length os a builtin is
 * really its token.)
 */

		for (r=0; r<NBUILTINS; r++)
		{
			if (builtins[r].token==Stack[W])
			{
				if (builtins[r].f!=NULL)
					builtins[r].f();
				goto EndFn;
				break;
			}
		}

/*
 * This is only reached if the builtin is not found in the
 * list --- probably an internal error.
 */

		Monitor(NotABuiltin);
	}

/*
 * If the length is positive, it's a normal definition,
 * so copy it to the stack by setting the InputPtr to
 * the beginning of the definition (W + 1).
 */


	InputPtr = W + 1;
	goto Start;

LoadArg:if (P == 0) {
		if (OutputPtr == OutputStream)
			goto Copy;
		else {
			Monitor2();
			goto Copy;
		}
	}
	NextCh();
	W = P + 2;
	if (Num(A) < 0)
		Monitor3();
	for (r = 0; r <= Num(A) - 1; r++) {
		W = W + Stack[W];
		if (Stack[W] == Marker)
		{
			Monitor4();
			break;
		}
	}
#ifdef EMPTYARG
        if (Stack[W]!= Marker)
#endif
/*
 * W now points to the place on the stack where the value of 
 * argument A is kept; copy that to the output area.
 */
	for (r = 1; r <= Stack[W] - 1; r++) {
		A = Stack[W + r];
		Load();
	}
	goto Start;

EndFn:	if (F > P) {
		Monitor5();
		goto Apply;
	}
	Stack[StackPtr] = EnvPtr;
	A = StackPtr;
	while (Stack[A] >= P - 1 + Stack[P - 1]) {
		int tA = A;
		A = Stack[A];
		Stack[tA] = Stack[tA] - Stack[P - 1];
	}
	W = Stack[A];
	while (W > P - 1) {
		W = Stack[W];
	}
	Stack[A] = W;
	EnvPtr = Stack[StackPtr];
	if (H != 0) {
		if (H > P)
			H = H - Stack[P - 1];
		else	
			Stack[H] = Stack[H] - Stack[P - 1];
	}
	InputPtr = Stack[P + 1];
	StackPtr = StackPtr - Stack[P - 1];
	CurrentChar = P - 1;
	W = P - 1 + Stack[P - 1];
	P = Stack[P];
	while (A != StackPtr) {
		Stack[A] = Stack[W];
		A = A + 1;
		W = W + 1;
	}
	goto Start;

Exit:	if ((InputPtr != 0) && (H != 0))
		Monitor8();
	if (QuoteLevel>1) fprintf(stderr,CloserMissing,QuoteClose);
	if (OutputPtr!=OutputStream) fprintf(stderr,CloserMissing,ApplyClose);
	return;

}


/*
 * Initialisation. This involves creating the stack and stuff,
 * and creating all the builtins on the stack. Each builtin is
 * added by the function AddBuiltin, gpmInit does the other
 * stuff like allocating memory.
 */

void AddBuiltin(builtin_t *b)
{
	char *s;

#ifdef DEBUG
	fprintf(stderr,"EnvPtr=%d\n",EnvPtr);
	fprintf(stderr,"Name=%s\n",b->name);
	fprintf(stderr,"Length=%d\n",strlen(b->name));
	fprintf(stderr,"Token=%d\n",b->token);
#endif

	Push(EnvPtr);			/* Add a link to previous builtin. */
	EnvPtr=StackPtr-1;		/* Update environment chain. */
	Push(strlen(b->name)+1);	/* Length+token */

	s=b->name;			/* Save the name of the builtin. */
	while(*s) Push(*s++);

	Push(b->token);			/* And its token. */
}

void gpmInit(argc,argv,envp)
int argc;
char *argv[],**envp;		/* envp not used ! */
{
  int c;
  char *stack_size=(char *)NULL;

  for (c=1; c<argc; c++)
  {
    if (argv[c]!=NULL)
    {
      if (argv[c][0]=='-')
      {
        switch(argv[c][1])
        {
            case '-' : break;
            case 'S' : stack_size=argv[c+1]; argv[c]=argv[c+1]=NULL;
                         if (stack_size==NULL)
                       {
                         fprintf(stderr,"Bad stack size\n"); exit(1);
                       }
                       c++; break;
            default  : Usage(); exit(1);
        }
      }
    }
  }

/*
 * Now that we have the parameters for the stack (how big is it, etc.)
 * try to allocate it and die if that isn't possible.
 */

  if (stack_size==NULL) StackSize=DefStackSize;
  else StackSize=atoi(stack_size);

  if (StackSize < MinStackSize)
  {
    fprintf(stderr,"Stack size must be at least %d\n",MinStackSize);
    exit(1);
  }
  if ((Stack=(int *)malloc(StackSize*sizeof(int)))==NULL)
  {
    fprintf(stderr,"Can't get memory for stack size %d\n",StackSize);
    exit(1);
  }


/*
 * Now go through the existing list of builtins and add each one
 * on to the stack. The function AddBuiltin does all the work
 * to make the stack right.
 */

#ifdef DEBUG
	fprintf(stderr,"Number of builtins=%d\n",
		sizeof(builtins)/sizeof(builtin_t));
#endif

	for (c=0; c<NBUILTINS; c++)
		AddBuiltin(&builtins[c]);

#ifdef DEBUG
	do_dump();
#endif
}

