home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
GEMini Atari
/
GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso
/
files
/
program
/
cpp102
/
macro.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-10-23
|
8KB
|
351 lines
#include <stddef.h>
#include <string.h>
#include <time.h>
#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;
}