/*
* Store the definition of a single-line macro.
*/
struct SMacro {
SMacro *next;
char *name;
int casesense;
int nparam;
int in_progress;
Token *expansion;
};
/*
* Store the definition of a multi-line macro. This is also used to
* store the interiors of `%rep...%endrep' blocks, which are
* effectively self-re-invoking multi-line macros which simply
* don't have a name or bother to appear in the hash tables. %rep
* blocks are signified by having a NULL `name' field.
*
* In a MMacro describing a `%rep' block, the `in_progress' field
* isn't merely boolean, but gives the number of repeats left to
* run.
*
* The `next' field is used for storing MMacros in hash tables; the
* `next_active' field is for stacking them on istk entries.
*
* When a MMacro is being expanded, `params', `iline', `nparam',
* `paramlen', `rotate' and `unique' are local to the invocation.
*/
struct MMacro {
MMacro *next;
char *name;
int casesense;
int nparam_min, nparam_max;
int plus; /* is the last parameter greedy? */
int nolist; /* is this macro listing-inhibited? */
int in_progress;
Token *dlist; /* All defaults as one list */
Token **defaults; /* Parameter default pointers */
int ndefs; /* number of default parameters */
Line *expansion;
MMacro *next_active;
MMacro *rep_nest; /* used for nesting %rep */
Token **params; /* actual parameters */
Token *iline; /* invocation line */
int nparam, rotate, *paramlen;
unsigned long unique;
int lineno; /* Current line number on expansion */
};
/*
* The context stack is composed of a linked list of these.
*/
struct Context {
Context *next;
SMacro *localmac;
char *name;
unsigned long number;
};
/*
* This is the internal form which we break input lines up into.
* Typically stored in linked lists.
*
* Note that `type' serves a double meaning: TOK_SMAC_PARAM is not
* necessarily used as-is, but is intended to denote the number of
* the substituted parameter. So in the definition
*
* %define a(x,y) ( (x) & ~(y) )
*
* the token representing `x' will have its type changed to
* TOK_SMAC_PARAM, but the one representing `y' will be
* TOK_SMAC_PARAM+1.
*
* TOK_INTERNAL_STRING is a dirty hack: it's a single string token
* which doesn't need quotes around it. Used in the pre-include
* mechanism as an alternative to trying to find a sensible type of
* quote to use on the filename we were passed.
*/
struct Token {
Token *next;
char *text;
SMacro *mac; /* associated macro for TOK_SMAC_END */
int type;
};
enum {
TOK_WHITESPACE = 1, TOK_COMMENT, TOK_ID, TOK_PREPROC_ID, TOK_STRING,
TOK_NUMBER, TOK_SMAC_END, TOK_OTHER, TOK_SMAC_PARAM,
TOK_INTERNAL_STRING
};
/*
* Multi-line macro definitions are stored as a linked list of
* these, which is essentially a container to allow several linked
* lists of Tokens.
*
* Note that in this module, linked lists are treated as stacks
* wherever possible. For this reason, Lines are _pushed_ on to the
* `expansion' field in MMacro structures, so that the linked list,
* if walked, would give the macro lines in reverse order; this
* means that we can walk the list when expanding a macro, and thus
* push the lines on to the `expansion' field in _istk_ in reverse
* order (so that when popped back off they are in the right
* order). It may seem cockeyed, and it relies on my design having
* an even number of steps in, but it works...
*
* Some of these structures, rather than being actual lines, are
* markers delimiting the end of the expansion of a given macro.
* This is for use in the cycle-tracking and %rep-handling code.
* Such structures have `finishes' non-NULL, and `first' NULL. All
* others have `finishes' NULL, but `first' may still be NULL if
* the line is blank.
*/
struct Line {
Line *next;
MMacro *finishes;
Token *first;
};
/*
* To handle an arbitrary level of file inclusion, we maintain a
* stack (ie linked list) of these things.
*/
struct Include {
Include *next;
FILE *fp;
Cond *conds;
Line *expansion;
char *fname;
int lineno, lineinc;
MMacro *mstk; /* stack of active macros/reps */
};
/*
* Include search path. This is simply a list of strings which get
* prepended, in turn, to the name of an include file, in an
* attempt to find the file if it's not in the current directory.
*/
struct IncPath {
IncPath *next;
char *path;
};
/*
* Conditional assembly: we maintain a separate stack of these for
* each level of file inclusion. (The only reason we keep the
* stacks separate is to ensure that a stray `%endif' in a file
* included from within the true branch of a `%if' won't terminate
* it and cause confusion: instead, rightly, it'll cause an error.)
*/
struct Cond {
Cond *next;
int state;
};
|