home *** CD-ROM | disk | FTP | other *** search
- /*
- * 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;
- }
- }
-