home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.cs.arizona.edu
/
ftp.cs.arizona.edu.tar
/
ftp.cs.arizona.edu
/
icon
/
historic
/
v92.tgz
/
v92.tar
/
v92
/
src
/
preproc
/
macro.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-03-22
|
18KB
|
661 lines
/*
* This file contains various functions for dealing with macros.
*/
#include "::preproc:preproc.h"
#include "::preproc:ptoken.h"
#include "::preproc:pproto.h"
/*
* Prototypes for static functions.
*/
hidden struct macro **m_find Params((char *mname));
hidden int eq_id_lst Params((struct id_lst *lst1,
struct id_lst *lst2));
hidden int eq_tok_lst Params((struct tok_lst *lst1,
struct tok_lst *lst2));
hidden int parm_indx Params((char *id, struct macro *m));
hidden novalue cpy_str Params((char *ldelim, char *image,
char *rdelim, struct str_buf *sbuf));
hidden struct token *stringize Params((struct token *trigger,
struct mac_expand *me));
hidden struct paste_lsts *paste_parse Params((struct token *t,
struct mac_expand *me));
hidden int *cpy_image Params((struct token *t, int *s));
#define MacTblSz 149
#define MHash(x) (((unsigned int) x) % MacTblSz)
static struct macro *m_table[MacTblSz]; /* hash table of macros */
int max_recurse;
/*
* Some string to put in the string table:
*/
static char *line_mac = "__LINE__";
static char *file_mac = "__FILE__";
static char *date_mac = "__DATE__";
static char *time_mac = "__TIME__";
static char *rcrs_mac = "__RCRS__";
static char *defined = "defined";
/*
* m_find - return return location of pointer to where macro belongs in
* macro table. If the macro is not in the table, the pointer at the
* location is NULL.
*/
static struct macro **m_find(mname)
char *mname;
{
struct macro **mpp;
for (mpp = &m_table[MHash(mname)]; *mpp != NULL && (*mpp)->mname != mname;
mpp = &(*mpp)->next)
;
return mpp;
}
/*
* eq_id_lst - check to see if two identifier lists contain the same identifiers
* in the same order.
*/
static int eq_id_lst(lst1, lst2)
struct id_lst *lst1;
struct id_lst *lst2;
{
if (lst1 == lst2)
return 1;
if (lst1 == NULL || lst2 == NULL)
return 0;
if (lst1->id != lst2->id)
return 0;
return eq_id_lst(lst1->next, lst2->next);
}
/*
* eq_tok_lst - check to see if 2 token lists contain the same tokens
* in the same order. All white space tokens are considered equal.
*/
static int eq_tok_lst(lst1, lst2)
struct tok_lst *lst1;
struct tok_lst *lst2;
{
if (lst1 == lst2)
return 1;
if (lst1 == NULL || lst2 == NULL)
return 0;
if (lst1->t->tok_id != lst2->t->tok_id)
return 0;
if (lst1->t->tok_id != WhiteSpace && lst1->t->tok_id != PpDirEnd &&
lst1->t->image != lst2->t->image)
return 0;
return eq_tok_lst(lst1->next, lst2->next);
}
/*
* init_macro - initialize this module, setting up standard macros.
*/
novalue init_macro()
{
int i;
struct macro **mpp;
struct token *t;
char *s;
char time_str[26];
static char *time_buf;
static char *date_buf;
static short first_time = 1;
if (first_time) {
first_time = 0;
/*
* Add names of standard macros to sting table.
*/
line_mac = spec_str(line_mac);
file_mac = spec_str(file_mac);
date_mac = spec_str(date_mac);
time_mac = spec_str(time_mac);
rcrs_mac = spec_str(rcrs_mac);
defined = spec_str(defined);
}
else {
/*
* Free macro definitions from the file processed.
*/
for (i = 0; i < MacTblSz; ++i)
free_m_lst(m_table[i]);
}
for (i = 0; i < MacTblSz; ++i)
m_table[i] = NULL;
/*
* __LINE__ and __FILE__ are macros that require special processing
* when they are processed. Indicate that.
*/
mpp = m_find(line_mac);
*mpp = new_macro(line_mac, SpecMac, 0, NULL, NULL);
mpp = m_find(file_mac);
*mpp = new_macro(file_mac, SpecMac, 0, NULL, NULL);
/*
* __TIME__ and __DATE__ must be initialized to the current time and
* date.
*/
getctime(time_str);
date_buf = (char *)alloc((unsigned int)12);
time_buf = (char *)alloc((unsigned int)9);
s = date_buf;
for (i = 4; i <= 10; ++i)
*s++ = time_str[i];
for (i = 20; i <= 23; ++i)
*s++ = time_str[i];
*s = '\0';
s = time_buf;
for (i = 11; i <= 18; ++i)
*s++ = time_str[i];
*s = '\0';
date_buf = spec_str(date_buf);
time_buf = spec_str(time_buf);
t = new_token(StrLit, date_buf, "", 0);
mpp = m_find(date_mac);
*mpp = new_macro(date_mac, FixedMac, 0, NULL, new_t_lst(t));
t = new_token(StrLit, time_buf, "", 0);
mpp = m_find(time_mac);
*mpp = new_macro(time_mac, FixedMac, 0, NULL, new_t_lst(t));
/*
* __RCRS__ is a special macro to indicate the allowance of
* recursive macros. It is not ANSI-standard. Initialize it
* to "1".
*/
mpp = m_find(rcrs_mac);
*mpp = new_macro(rcrs_mac, NoArgs, 0, NULL, new_t_lst(copy_t(one_tok)));
max_recurse = 1;
}
/*
* m_install - install a macro.
*/
novalue m_install(mname, category, multi_line, prmlst, body)
struct token *mname; /* name of macro */
int multi_line; /* flag indicating if this is a multi-line macro */
int category; /* # parms, or NoArgs if it is object-like macro */
struct id_lst *prmlst; /* parameter list */
struct tok_lst *body; /* replacement list */
{
struct macro **mpp;
char *s;
if (mname->image == defined)
errt1(mname, "'defined' may not be the subject of #define");
/*
* The special macro __RCRS__ may only be defined as a single integer
* token and must be an object-like macro.
*/
if (mname->image == rcrs_mac) {
if (body == NULL || body->t->tok_id != PpNumber || body->next != NULL)
errt1(mname, "__RCRS__ must be a decimal integer");
if (category != NoArgs)
errt1(mname, "__RSCS__ may have no arguments");
max_recurse = 0;
for (s = body->t->image; *s != '\0'; ++s) {
if (*s >= '0' && *s <= '9')
max_recurse = max_recurse * 10 + (*s - '0');
else
errt1(mname, "__RCRS__ must be a decimal integer");
}
}
mpp = m_find(mname->image);
if (*mpp == NULL)
*mpp = new_macro(mname->image, category, multi_line, prmlst, body);
else {
/*
* The macro is already defined. Make sure it is identical (up to
* white space) to this definition.
*/
if (!((*mpp)->category == category && eq_id_lst((*mpp)->prmlst, prmlst) &&
eq_tok_lst((*mpp)->body, body)))
errt2(mname, "invalid redefinition of macro ", mname->image);
free_id_lst(prmlst);
free_t_lst(body);
}
}
/*
* m_delete - delete a macro.
*/
novalue m_delete(mname)
struct token *mname;
{
struct macro **mpp, *mp;
if (mname->image == defined)
errt1(mname, "'defined' may not be the subject of #undef");
/*
* Undefining __RCRS__ allows unlimited macro recursion (non-ANSI
* standard feature.
*/
if (mname->image == rcrs_mac)
max_recurse = -1;
/*
* Make sure undefining this macro is allowed, and free storage
* associate with it.
*/
mpp = m_find(mname->image);
if (*mpp != NULL) {
mp = *mpp;
if (mp->category == FixedMac || mp->category == SpecMac)
errt2(mname, mname->image, " may not be the subject of #undef");
*mpp = mp->next;
free_m(mp);
}
}
/*
* m_lookup - lookup a macro name. Return pointer to macro, if it is defined;
* return NULL, if it is not. This routine sets the definition for macros
* whose definitions various from place to place.
*/
struct macro *m_lookup(id)
struct token *id;
{
struct macro *m;
static char buf[20];
m = *m_find(id->image);
if (m != NULL && m->category == SpecMac)
if (m->mname == line_mac) { /* __LINE___ */
sprintf(buf, "%d", id->line);
m->body = new_t_lst(new_token(PpNumber, buf, id->fname,
id->line));
}
else if (m->mname == file_mac) /* __FILE__ */
m->body = new_t_lst(new_token(StrLit, id->fname, id->fname,
id->line));
return m;
}
/*
* parm_indx - see if a name is a paramter to the given macro.
*/
static int parm_indx(id, m)
char *id;
struct macro *m;
{
struct id_lst *idlst;
int i;
for (i = 0, idlst = m->prmlst; i < m->category; i++, idlst = idlst->next)
if (id == idlst->id)
return i;
return -1;
}
/*
* cpy_str - copy a string into a string buffer, adding delimiters.
*/
static novalue cpy_str(ldelim, image, rdelim, sbuf)
char *ldelim;
char *image;
char *rdelim;
struct str_buf *sbuf;
{
register char *s;
for (s = ldelim; *s != '\0'; ++s)
AppChar(*sbuf, *s);
for (s = image; *s != '\0'; ++s) {
if (*s == '\\' || *s == '"')
AppChar(*sbuf, '\\');
AppChar(*sbuf, *s);
}
for (s = rdelim; *s != '\0'; ++s)
AppChar(*sbuf, *s);
}
/*
* stringize - create a stringized version of a token.
*/
static struct token *stringize(trigger, me)
struct token *trigger;
struct mac_expand *me;
{
register struct token *t;
struct tok_lst *arg;
struct str_buf *sbuf;
char *s;
int indx;
/*
* Get the next token from the macro body. It must be a macro parameter;
* retrieve the raw tokens for the corresponding argument.
*/
if (me->rest_bdy == NULL)
errt1(trigger, "the # operator must have an argument");
t = me->rest_bdy->t;
me->rest_bdy = me->rest_bdy->next;
if (t->tok_id == Identifier)
indx = parm_indx(t->image, me->m);
else
indx = -1;
if (indx == -1)
errt1(t, "the # operator may only be applied to a macro argument");
arg = me->args[indx];
/*
* Copy the images for the argument tokens into a string buffer. Note
* that the images of string and character literals lack quotes; these
* must be escaped in the stringized value.
*/
sbuf = get_sbuf();
while (arg != NULL) {
t = arg->t;
if (t->tok_id == WhiteSpace)
AppChar(*sbuf, ' ');
else if (t->tok_id == StrLit)
cpy_str("\\\"", t->image, "\\\"", sbuf);
else if (t->tok_id == LStrLit)
cpy_str("L\\\"", t->image, "\\\"", sbuf);
else if (t->tok_id == CharConst)
cpy_str("'", t->image, "'", sbuf);
else if (t->tok_id == LCharConst)
cpy_str("L'", t->image, "'", sbuf);
else
for (s = t->image; *s != '\0'; ++s)
AppChar(*sbuf, *s);
arg = arg->next;
}
/*
* Created the token for the stringized argument.
*/
t = new_token(StrLit, str_install(sbuf), trigger->fname, trigger->line);
t->flag |= trigger->flag & LineChk;
rel_sbuf(sbuf);
return t;
}
/*
* paste_parse - parse an expression involving token pasting operators (and
* stringizing operators). Return a list of token lists. Each token list
* is from a token pasting operand, with operands that are macro parameters
* replaced by their corresponding argument (this is why a list of tokens
* is needed for each operand). Any needed stringizing is done as the list
* is created.
*/
static struct paste_lsts *paste_parse(t, me)
struct token *t;
struct mac_expand *me;
{
struct token *t1;
struct token *trigger = NULL;
struct tok_lst *lst;
struct paste_lsts *plst;
int indx;
if (me->rest_bdy == NULL || me->rest_bdy->t->tok_id != PpPaste)
plst = NULL; /* we have reached the end of the pasting expression */
else {
/*
* The next token is a pasting operator. Copy it an move on to the
* operand.
*/
trigger = copy_t(me->rest_bdy->t);
me->rest_bdy = me->rest_bdy->next;
if (me->rest_bdy == NULL)
errt1(t, "the ## operator must not appear at the end of a macro");
t1 = me->rest_bdy->t;
me->rest_bdy = me->rest_bdy->next;
/*
* See if the operand is a stringizing operation.
*/
if (t1->tok_id == '#')
t1 = stringize(t1, me);
else
t1 = copy_t(t1);
plst = paste_parse(t1, me); /* get any further token pasting */
}
/*
* If the operand is a macro parameter, replace it by the corresponding
* argument, otherwise make the operand into a 1-element token list.
*/
indx = -1;
if (t->tok_id == Identifier)
indx = parm_indx(t->image, me->m);
if (indx == -1)
lst = new_t_lst(t);
else {
lst = me->args[indx];
free_t(t);
}
/*
* Ignore emtpy arguments when constructing the pasting list.
*/
if (lst == NULL)
return plst;
else
return new_plsts(trigger, lst, plst);
}
/*
* cpy_image - copy the image of a token into a character buffer adding
* delimiters if it is a string or character literal.
*/
static int *cpy_image(t, s)
struct token *t;
int *s; /* the string buffer can contain EOF */
{
register char *s1;
switch (t->tok_id) {
case StrLit:
*s++ = '"';
break;
case LStrLit:
*s++ = 'L';
*s++ = '"';
break;
case CharConst:
*s++ = '\'';
break;
case LCharConst:
*s++ = 'L';
*s++ = '\'';
break;
}
s1 = t->image;
while (*s1 != '\0')
*s++ = *s1++;
switch (t->tok_id) {
case StrLit:
case LStrLit:
*s++ = '"';
break;
case CharConst:
case LCharConst:
*s++ = '\'';
break;
}
return s;
}
/*
* paste - return the next token from a source which pastes tokens. The
* source may represent a series of token pasting operators.
*/
struct token *paste()
{
struct token *t;
struct token *t1;
struct token *trigger;
struct paste_lsts *plst;
union src_ref ref;
int i;
int *s;
plst = src_stack->u.plsts;
/*
* If the next token of the current list is not the one to be pasted,
* just return it.
*/
t = copy_t(plst->tlst->t);
plst->tlst = plst->tlst->next;
if (plst->tlst != NULL)
return t;
/*
* We have the last token from the current list. If there is another
* list, this token must be pasted to the first token of that list.
* Make the next list the current one and get its first token.
*/
trigger = plst->trigger;
plst = plst->next;
free_plsts(src_stack->u.plsts);
src_stack->u.plsts = plst;
if (plst == NULL) {
pop_src();
return t;
}
t1 = next_tok();
/*
* Paste tokens by creating a character source with the images of the
* two tokens concatenated.
*/
ref.cs = new_cs(trigger->fname, NULL,
(int)strlen(t->image) + (int)strlen(t1->image) + 7);
push_src(CharSrc, &ref);
s = cpy_image(t, ref.cs->char_buf);
s = cpy_image(t1, s);
*s = EOF;
/*
* Treat all characters of the new source as if they come from the
* location of the token pasting.
*/
for (i = 0; i < (s - ref.cs->char_buf + 1); ++i)
*(ref.cs->line_buf) = trigger->line;
ref.cs->last_char = s;
ref.cs->dir_state = Reset;
first_char = ref.cs->char_buf;
next_char = first_char;
last_char = ref.cs->last_char;
return next_tok(); /* first token from pasted images */
}
/*
* mac_tok - return the next token from a source which is a macro.
*/
struct token *mac_tok()
{
struct mac_expand *me;
register struct token *t, *t1;
struct paste_lsts *plst;
union src_ref ref;
int line_check;
int indx;
int line;
char *fname;
me = src_stack->u.me; /* macro, current position, and arguments */
/*
* Get the next token from the macro body.
*/
if (me->rest_bdy == NULL)
return NULL;
t = me->rest_bdy->t;
me->rest_bdy = me->rest_bdy->next;
/*
* If this token is a stringizing operator, try stringizing the next
* token.
*/
if (t->tok_id == '#')
t = stringize(t, me);
else
t = copy_t(t);
if (me->rest_bdy != NULL && me->rest_bdy->t->tok_id == PpPaste) {
/*
* We have found token pasting. If there is a series of such operators,
* make them all into one token pasting source and push it on
* the source stack.
*/
if (t->flag & LineChk) {
line_check = 1;
line = t->line;
fname = t->fname;
}
else
line_check = 0;
plst = paste_parse(t, me);
if (plst != NULL) {
ref.plsts = plst;
push_src(PasteLsts, &ref);
}
t1 = next_tok();
if (line_check && !(t1->flag & LineChk)) {
t1->flag |= LineChk;
t1->line = line;
t1->fname = fname;
}
return t1;
}
else if (t->tok_id == Identifier &&
(indx = parm_indx(t->image, me->m)) != -1) {
/*
* We have found a parameter. Push a token source for the corresponding
* argument, that is, replace the parameter with its definition.
*/
ref.tlst = me->exp_args[indx];
push_src(TokLst, &ref);
if (t->flag & LineChk) {
line = t->line;
fname = t->fname;
t1 = next_tok();
if (!(t1->flag & LineChk)) {
/*
* The parameter name token is significant with respect to
* outputting #line directives but the first argument token
* is not. Pretend the argument has the same line number as the
* parameter name.
*/
t1->flag |= LineChk;
t1->line = line;
t1->fname = fname;
}
free_t(t);
return t1;
}
else {
free_t(t);
return next_tok();
}
}
else {
/*
* This is an ordinary token, nothing further is needed here.
*/
return t;
}
}