#include #include #include #include "global.h" char *magic_words[] = { "__STDC__", /* these two are particuarly magic -- */ "defined", /* leave them where they are */ "__DATE__", "__TIME__", "__FILE__", "__LINE__", "__INCLUDE_LEVEL__", }; int N_MWORDS = nelems(magic_words); /* magic_token() -- create a Token to be returned by expand_magic() below */ static TokenP magic_token(type, text, l, T0) int type; char *text; long l; TokenP T0; { TokenP T = alloc_token(); int len; register char *s, *t; T->pre_ws = strdup(T0->pre_ws); if (type != STR_CON) T->txt = strdup(T0->txt); else { len = strlen(T0->txt); T->txt = mallok(len + 3); strcpy(T->txt + 1, T0->txt); T->txt[0] = T->txt[len + 1] = '"'; T->txt[len + 2] = '\0'; } T->type = type; if (type == NUMBER) T->val = l; if (type == ID) T->flags |= BLUEPAINT; return T; } /* expand_magic() -- expand a magic preprocessor constant */ static TokenP expand_magic(T) TokenP T; { static char buf[20]; int i; TokenP T1, tL; Token tLH; for (i = 0; i < N_MWORDS; i++) if (streq(T->txt, magic_words[i])) break; switch (i) { case 0: /* __STDC__ */ return magic_token(NUMBER, "1", 1L, T); case 1: /* defined */ tL = &tLH; tL->next = NULL; buf[0] = '0'; buf[1] = '\0'; T1 = token(); if (T1->type == STOP) { push_tlist(T1); error("defined() has no parameter"); goto nope; } tL = tL->next = T1; if (T1->type == ID) { if (lookup(T1->txt, T1->hashval)) buf[0] = '1'; } else if (T1->type == LPAREN) { T1 = token(); tL = tL->next = T1; if (T1->type == STOP) { error("unterminated defined() macro"); goto nope; } else if (T1->type == ID) { if (lookup(T1->txt, T1->hashval)) buf[0] = '1'; T1 = token(); tL = tL->next = T1; if (T1->type != RPAREN) { error("missing `)' in defined()"); goto nope; } } else { error("parameter \"%s\" to defined() is not an identifier", T1->txt); goto nope; } } else { error("parameter \"%s\" to defined() is not an identifier", T1->txt); goto nope; } free_tlist(tLH.next); return magic_token(NUMBER, buf, (long)(*buf == '1'), T); nope: push_tlist(tLH.next); return magic_token(NUMBER, "0", 0L, T); case 2: /* __DATE__ */ return magic_token(STR_CON, date_string, 0L, T); case 3: /* __TIME__ */ return magic_token(STR_CON, time_string, 0L, T); case 4: /* __FILE__ */ return magic_token(STR_CON, cur_file, 0L, T); case 5: /* __LINE__ */ sprintf(buf, "%lu", this_line); return magic_token(NUMBER, buf, this_line, T); case 6: /* __INCLUDE_LEVEL__ */ sprintf(buf, "%lu", include_level); return magic_token(NUMBER, buf, include_level, T); default: bugchk("unknown magic word %s", T->txt); } } /* get_args() -- read in the actual arguments of a macro expansion. |mname| is the name of the macro being expanded; |nargs| is the number of arguments to read. */ static TokenP *get_args(mname, nargs) char *mname; int nargs; { TokenP *args; int cur_arg, par_level = 0; TokenP T, L; Token head; args = (TokenP *) mallok((nargs ? nargs : 1) * sizeof (TokenP)); for (cur_arg = 0; cur_arg < nargs || cur_arg == 0; cur_arg++) args[cur_arg] = NULL; cur_arg = 0; L = &head; L->next = NULL; change_mode(SLURP, 0); for (;;) { T = token(); switch (T->type) { case STOP: push_tlist(T); error("unterminated macro \"%s\"", mname); goto out_loop; case EOL: free_token(T); continue; case LPAREN: par_level++; break; case RPAREN: if (--par_level < 0) { free_token(T); goto out_loop; } break; case COMMA: if (par_level == 0) { free_token(T); args[cur_arg++] = head.next; L = &head; L->next = NULL; continue; } break; } if (cur_arg < nargs) L = L->next = T; } out_loop: change_mode(0, SLURP); if (head.next || cur_arg > 0) args[cur_arg++] = head.next; if (cur_arg != nargs) error("macro \"%s\" declared with %d arg%s, used with %d", mname, nargs, (nargs == 1 ? "" : "s"), cur_arg); return args; } /* stringize() -- create a string literal representing the token list |T| */ static TokenP stringize(T) TokenP T; { char *buf; register char *s, *t; size_t buflen, i; ptrdiff_t dp; TokenP tt, t0; s = buf = mallok(buflen = 80); *s++ = '"'; for (tt = T; tt; tt = tt->next) { i = 2 * strlen(tt->txt) + 2; if (tt != T) ; i += strlen(tt->pre_ws); s = grow(&buf, &buflen, s, i); if (tt != T) for (t = tt->pre_ws; *t; t++) *s++ = *t; for (t = tt->txt; *t; t++) { if (*t == '\\' || *t == '"') *s++ = '\\'; *s++ = *t; } } *s++ = '"'; *s = '\0'; t0 = alloc_token(); t0->type = STR_CON; t0->txt = buf; t0->pre_ws = strdup(T->pre_ws); return t0; } /* expand() -- expand the macro definition |M| of the Token |T|. Returns a list of Token's representing the expanded text. WARNING: _Always_ check the BLUEPAINT flag before calling expand(), or you'll end up with either the wrong answer or an infinite loop, or both. At the moment this is not a concern, since only exp_token() calls expand(). */ void expand(T, M) TokenP T; Macro *M; { Token head1, head2, mhead; TokenP t1, pt1, t2 = &head2, pt2 = &head1, *args, *exp_args, *str_args; int n; head1.flags = head2.flags = mhead.flags = 0; head1.next = &head2; head2.next = NULL; mhead.next = M->m_text; if (M->flags & MARKED) { T->flags |= BLUEPAINT; push_tlist(T); return; } if (M->flags & MAGIC) { push_tlist(expand_magic(T)); return; } if (M->flags & HASARGS) { t1 = token(); if (t1->type != LPAREN) { push_tlist(t1); T->flags |= BLUEPAINT; push_tlist(T); return; } args = get_args(T->txt, M->nargs); exp_args = mallok(M->nargs * sizeof (TokenP)); str_args = mallok(M->nargs * sizeof (TokenP)); for (n = 0; n < M->nargs; n++) if (args[n]) { exp_args[n] = expand_tlist(copy_tlist(args[n])); str_args[n] = stringize(args[n]); } else exp_args[n] = str_args[n] = NULL; } else args = exp_args = str_args = NULL; M->flags |= MARKED; t2->next = NULL; for (t1 = M->m_text, pt1 = &mhead; t1; t1 = t1->next, pt1 = pt1->next) { TokenP t3; if (t1->type == MACRO_ARG) { t3 = copy_tlist( (t1->flags & STRINGIZE_ME ? str_args : t1->flags & CONCAT_NEXT || pt1->flags & CONCAT_NEXT ? args : exp_args )[t1->val] ); } else { t3 = copy_token(t1); t3->flags &= ~(STRINGIZE_ME | CONCAT_NEXT | TRAIL_SPC); } if (pt1->flags & CONCAT_NEXT) { TokenP t4; t4 = merge_tokens(t2, t3); pt2->next = t4; t4->next = t3->next; t2->next = t3->next = NULL; free_token(t2); free_token(t3); t2 = t4; } else t2->next = t3; while (t2->next) { t2 = t2->next; pt2 = pt2->next; } } /* prepend the leading whitespace from T to the first token of the expanded text, if any */ if (head2.next) { if (head2.next->pre_ws) free(head2.next->pre_ws); head2.next->pre_ws = T->pre_ws; } /* add a trailing space */ t2->flags |= TRAIL_SPC; push_tlist(mk_unmarker(T)); push_tlist(head2.next); } /* expand_tlist() -- call expand() on each of a list of Token's, returning the resulting list of Token's */ TokenP expand_tlist(TL) TokenP TL; { Token head; TokenP t1, t2 = &head; Macro *M; head.next = NULL; push_tlist(mk_stopper()); push_tlist(TL); for (t1 = token(); t1->type != STOP; t1 = token()) { if (t1->type == ID && !(t1->flags & BLUEPAINT) && (M = lookup(t1->txt, t1->hashval))) { expand(t1, M, NULL); free_token(t1); } else { t2->next = t1; t2 = t2->next; } } /* t1 should now hold a STOP token */ free_token(t1); return head.next; }