/********** 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|}" 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 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