/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1986-2011 AT&T Intellectual Property *
* and is licensed under the *
* Eclipse Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* http://www.eclipse.org/org/documents/epl-v10.html *
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
* Glenn Fowler
* AT&T Research
*
* preprocessor macro call
*/
#include "pplib.h"
#include <ctype.h>
/*
* call a macro by pushing its value on the input stream
* only the macro token itself has been consumed
* -1 returned if macro disabled
* 0 returned if tok==0 and sym->mac->value to be copied to output by caller
* 1 returned if value pushed on input
*/
int
ppcall(register struct ppsymbol* sym, int tok)
{
register int c;
register char* p;
register char* q;
register struct ppmacro* mac;
int n;
int m;
int ret;
int old_hidden;
int last_line;
long old_state;
char* last_file;
char* old_next;
char* old_token;
struct ppmacstk* mp;
struct ppinstk* old_in;
struct ppinstk* kp;
struct pptuple* tp;
ret = -1;
sym->flags |= SYM_NOTICED;
if (mac = sym->macro)
{
count(macro);
if ((sym->flags & SYM_PREDICATE) && (pp.state & (CONDITIONAL|WARN)) == (CONDITIONAL|WARN))
error(1, "%s: macro definition overrides assertion: use #%s ...", sym->name, sym->name);
if (sym->flags & SYM_DISABLED)
#if COMPATIBLE
if ((pp.state & (COMPATIBILITY|TRANSITION)) != COMPATIBILITY || !mac->arity)
#endif
{
pp.mode |= MARKMACRO;
#if COMPATIBLE
if ((pp.state & (COMPATIBILITY|STRICT)) == (COMPATIBILITY|STRICT))
error(1, "%s: macro recursion inhibited", sym->name);
#endif
goto disable;
}
if ((sym->flags & SYM_PREDEFINED) && !(pp.mode & (HOSTED|INACTIVE)))
{
#if COMPATIBLE
if (*sym->name != '_' && !(pp.state & COMPATIBILITY))
#else
if (*sym->name != '_')
#endif
{
if (pp.state & STRICT)
{
error(1, "%s: obsolete predefined symbol expansion disabled", sym->name);
goto disable;
}
error(1, "%s: obsolete predefined symbol expanded%s", sym->name, (pp.state & DIRECTIVE) ? "" : " outside of directive");
}
else if (!(pp.state & DIRECTIVE) && mac->value && (ppisdig(*mac->value) || *mac->value == '#'))
error(1, "%s: predefined symbol expanded outside of directive", sym->name);
}
debug((-5, "macro %s = %s", sym->name, mac->value));
if (pp.macref)
(*pp.macref)(sym, error_info.file, error_info.line, (pp.state & CONDITIONAL) ? REF_IF : REF_NORMAL, 0L);
if (tp = mac->tuple)
{
old_state = pp.state;
pp.state |= DEFINITION|NOSPACE;
old_token = pp.token;
n = 2 * MAXTOKEN;
pp.token = p = oldof(0, char, 0, n);
q = p + MAXTOKEN;
*pp.token++ = ' ';
old_hidden = pp.hidden;
while (c = pplex())
{
if (c == '\n')
{
pp.hidden++;
pp.state |= HIDDEN|NEWLINE;
old_state |= HIDDEN|NEWLINE;
error_info.line++;
}
else if (c == '#')
{
ungetchr(c);
break;
}
else
{
for (;;)
{
if (streq(pp.token, tp->token))
{
if (!(tp = tp->match))
break;
if (!tp->nomatch)
{
free(p);
pp.state = old_state;
pp.token = old_token;
PUSH_TUPLE(sym, tp->token);
ret = 1;
goto disable;
}
}
else if (!(tp = tp->nomatch))
break;
}
if (!tp)
{
pp.token = pp.toknxt;
break;
}
}
if ((pp.token = pp.toknxt) > q)
{
c = pp.token - p;
p = newof(p, char, n += MAXTOKEN, 0);
q = p + n - MAXTOKEN;
pp.token = p + c;
}
*pp.token++ = ' ';
}
if (pp.token > p && *(pp.token - 1) == ' ')
pp.token--;
if (pp.hidden != old_hidden)
*pp.token++ = '\n';
else
*pp.token++ = ' ';
*pp.token = 0;
pp.state = old_state;
pp.token = old_token;
if (*p)
PUSH_RESCAN(p);
else
free(p);
if (!mac->value)
goto disable;
}
if (sym->flags & SYM_FUNCTION)
{
/*
* a quick and dirty '(' peek to avoid possibly
* inappropriate ungetchr()'s below
*/
for (p = pp.in->nextchr; isspace(*p); p++);
if ((c = *p) != '(' && c != '/' && c != 0 && c != MARK)
goto disable;
old_next = (c == MARK) ? pp.in->nextchr : NiL;
old_token = pp.token;
mp = pp.macp->next;
if ((pp.token = (char*)&mp->arg[mac->arity + 1]) > pp.maxmac)
error(3, "%s: too many nested function-like macros", sym->name);
old_hidden = pp.hidden;
old_state = pp.state;
pp.state |= DEFINITION|FILEPOP|NOSPACE;
while ((c = pplex()) == '\n')
{
pp.hidden++;
pp.state |= HIDDEN|NEWLINE;
old_state |= HIDDEN|NEWLINE;
error_info.line++;
}
if (c != '(')
{
pp.state = old_state;
if (old_next)
pp.in->nextchr = old_next;
else
{
if (c)
{
p = pp.toknxt;
while (p > pp.token)
ungetchr(*--p);
#if COMPATIBLE
if ((pp.state & (COMPATIBILITY|STRICT)) == (COMPATIBILITY|STRICT))
error(1, "%s: macro arguments omitted", sym->name);
#endif
if (c == T_ID && !(pp.state & HIDDEN))
ungetchr(' ');
}
if (pp.hidden != old_hidden)
{
ungetchr('\n');
error_info.line--;
if (pp.hidden && !--pp.hidden)
pp.state &= ~HIDDEN;
}
}
pp.token = old_token;
goto disable;
}
pp.state = old_state;
/*
* arg[i][-1] is an extra char for each actual i
* for a possible ungetchr('"') during IN_QUOTE
* arg[i][-1]==0 if arg i need not be expanded
* arg[0][-2] holds the actual arg count
*/
c = 0;
m = 0;
n = 0;
mp = pp.macp->next;
p = pp.token = (char*)&mp->arg[mac->arity + 1];
pp.state |= COLLECTING|NOEXPAND;
pp.state &= ~FILEPOP;
sym->flags |= SYM_ACTIVE;
old_in = pp.in;
last_line = error_info.line;
last_file = error_info.file;
mp->line = error_info.line;
#if MACKEYARGS
if (pp.option & KEYARGS)
{
for (c = 0; c < mac->arity; c++)
mp->arg[c] = mac->args.key[c].value + 1;
mp->arg[0]++;
}
else
#endif
{
*++p = ' ';
mp->arg[0] = ++p;
}
#if MACKEYARGS
keyarg:
if (pp.option & KEYARGS)
{
pp.state |= NOSPACE;
switch (pplex())
{
case T_ID:
break;
case ')': /* no actual key args */
if (!(pp.state & NOEXPAND))
pp.state |= NOEXPAND;
for (c = 0; c < mac->arity; c++)
mp->arg[c][-1] = 0;
c = 0;
goto endactuals;
default:
error(3, "%s: invalid keyword macro argument", pp.token);
break;
}
for (c = 0; c < mac->arity; c++)
if (streq(pp.token, mac->args.key[c].name)) break;
if (c >= mac->arity)
error(2, "%s: invalid macro argument keyword", pp.token);
if (pplex() != '=')
error(2, "= expected in keyword macro argument");
pp.state &= ~NOSPACE;
if (!c)
p++;
pp.token = mp->arg[c] = ++p;
}
#endif
for (;;)
{
if ((pp.mactop = pp.token = p) >= pp.maxmac)
error(3, "%s: too many nested function-like macros", sym->name);
switch (pplex())
{
case '(':
n++;
break;
case ')':
if (!n--)
{
if (p > mp->arg[c] && *(p - 1) == ' ')
p--;
if (p > mp->arg[c] && *(p - 1) == '\\')
{
for (q = mp->arg[c]; q < p; q++)
if (*q == '\\')
q++;
if (q > p)
*p++ = '\\';
}
#if MACKEYARGS
*p = 0;
m++;
#endif
goto endactuals;
}
break;
case ',':
if (!n && (m++, (c < mac->arity - 1 || !(sym->flags & SYM_VARIADIC))))
{
if (p > mp->arg[c] && *(p - 1) == ' ')
p--;
*p++ = 0;
if (!(pp.state & NOEXPAND))
pp.state |= NOEXPAND;
else
mp->arg[c][-1] = 0;
#if MACKEYARGS
if (pp.option & KEYARGS)
{
pp.token = p + 1;
goto keyarg;
}
#endif
{
if ((pp.state & STRICT) && p == mp->arg[c])
error(1, "%s: macro call argument %d is null", sym->name, c + 1);
if (c < mac->arity)
c++;
*p++ = ' ';
}
pp.toknxt = mp->arg[c] = p;
}
break;
case 0:
if (pp.in == old_in)
kp = 0;
else
for (kp = pp.in; kp && kp != old_in; kp = kp->prev);
if (!kp)
{
error(
#if COMPATIBLE
(pp.state & COMPATIBILITY) ? 3 :
#endif
2, "%s: %s in macro argument list", sym->name, pptokchr(0));
goto endactuals;
}
continue;
case '\n':
pp.state |= HIDDEN;
error_info.line++;
pp.hidden++;
/*FALLTHROUGH*/
case ' ':
if (p > mp->arg[c] && *(p - 1) != ' ') *p++ = ' ';
continue;
}
p = pp.toknxt;
if (error_info.line != last_line)
{
SETLINE(p, error_info.line);
last_line = error_info.line;
}
if (error_info.file != last_file)
{
SETFILE(p, error_info.file);
last_file = error_info.file;
}
}
endactuals:
if (pp.state & NOEXPAND)
mp->arg[c][-1] = 0;
pp.token = old_token;
if (pp.in != old_in)
{
for (kp = pp.in; kp && kp != old_in; kp = kp->prev);
if (kp)
error(2, "%s: macro call starts and ends in different files", sym->name);
}
pp.state &= ~(COLLECTING|FILEPOP|NOEXPAND);
sym->flags &= ~SYM_ACTIVE;
#if MACKEYARGS
if (!(pp.option & KEYARGS))
#endif
{
if (p > mp->arg[0] && ++m || (sym->flags & SYM_VARIADIC))
c++;
if (c != (n = mac->arity) && (c > 0 || n > 1) && !(sym->flags & SYM_EMPTY))
{
if (!(sym->flags & SYM_VARIADIC))
error(1, "%s: %d actual argument%s expected", sym->name, n, n == 1 ? "" : "s");
else if (c < --n)
error(1, "%s: at least %d actual argument%s expected", sym->name, n, n == 1 ? "" : "s");
#if COMPATIBLE
if (!c && (pp.state & (COMPATIBILITY|STRICT)) == (COMPATIBILITY|STRICT))
goto disable;
#endif
}
if (!c)
++c;
while (c < mac->arity)
mp->arg[c++] = (char*)"\0" + 1;
}
mp->arg[0][-2] = m;
*p++ = 0;
nextframe(mp, p);
count(function);
}
if (!tok && (sym->flags & SYM_NOEXPAND))
{
if (sym->flags & SYM_FUNCTION)
popframe(mp);
ret = !mac->size;
}
else if (!(pp.state & HEADER) || (pp.option & HEADEREXPANDALL) || pp.in->type != IN_COPY)
{
if (sym->flags & SYM_MULTILINE)
PUSH_MULTILINE(sym);
else
PUSH_MACRO(sym);
ret = 1;
}
}
disable:
if (ret < 0 && sym->hidden && !(pp.mode & EXPOSE) && !(pp.state & HEADER) && (pp.in->type == IN_FILE || pp.in->type == IN_MACRO || pp.in->type == IN_EXPAND))
{
struct ppinstk* inp;
for (inp = pp.in; inp->type != IN_FILE && inp->prev; inp = inp->prev);
sfsprintf(pp.hidebuf, MAXTOKEN, "_%d_%s_hIDe", inp->index, sym->name);
PUSH_STRING(pp.hidebuf);
ret = 1;
}
pp.state &= ~NEWLINE;
pp.in->flags |= IN_tokens;
count(token);
return ret;
}