#include "m4.h"
static void expand_macro _((symbol *));
static void expand_token _((struct obstack *, token_type, token_data *));
int expansion_level = 0;
static int macro_call_id = 0;
void
expand_input (void)
{
token_type t;
token_data td;
while ((t = next_token (&td)) != TOKEN_EOF)
expand_token ((struct obstack *) NULL, t, &td);
}
static void
expand_token (struct obstack *obs, token_type t, token_data *td)
{
symbol *sym;
switch (t)
{
case TOKEN_EOF:
case TOKEN_MACDEF:
break;
case TOKEN_SIMPLE:
case TOKEN_STRING:
shipout_text (obs, TOKEN_DATA_TEXT (td), strlen (TOKEN_DATA_TEXT (td)));
break;
case TOKEN_WORD:
sym = lookup_symbol (TOKEN_DATA_TEXT (td), SYMBOL_LOOKUP);
if (sym == NULL || SYMBOL_TYPE (sym) == TOKEN_VOID
|| (SYMBOL_TYPE (sym) == TOKEN_FUNC
&& SYMBOL_BLIND_NO_ARGS (sym)
&& peek_input () != '('))
{
#ifdef ENABLE_CHANGEWORD
shipout_text (obs, TOKEN_DATA_ORIG_TEXT (td),
strlen (TOKEN_DATA_ORIG_TEXT (td)));
#else
shipout_text (obs, TOKEN_DATA_TEXT (td),
strlen (TOKEN_DATA_TEXT (td)));
#endif
}
else
expand_macro (sym);
break;
default:
M4ERROR ((warning_status, 0,
"INTERNAL ERROR: Bad token type in expand_token ()"));
abort ();
}
}
static boolean
expand_argument (struct obstack *obs, token_data *argp)
{
token_type t;
token_data td;
char *text;
int paren_level;
TOKEN_DATA_TYPE (argp) = TOKEN_VOID;
do
{
t = next_token (&td);
}
while (t == TOKEN_SIMPLE && isspace (*TOKEN_DATA_TEXT (&td)));
paren_level = 0;
while (1)
{
switch (t)
{
case TOKEN_SIMPLE:
text = TOKEN_DATA_TEXT (&td);
if ((*text == ',' || *text == ')') && paren_level == 0)
{
obstack_1grow (obs, '\0');
text = obstack_finish (obs);
if (TOKEN_DATA_TYPE (argp) == TOKEN_VOID)
{
TOKEN_DATA_TYPE (argp) = TOKEN_TEXT;
TOKEN_DATA_TEXT (argp) = text;
}
return (boolean) (*TOKEN_DATA_TEXT (&td) == ',');
}
if (*text == '(')
paren_level++;
else if (*text == ')')
paren_level--;
expand_token (obs, t, &td);
break;
case TOKEN_EOF:
M4ERROR ((EXIT_FAILURE, 0,
"ERROR: EOF in argument list"));
break;
case TOKEN_WORD:
case TOKEN_STRING:
expand_token (obs, t, &td);
break;
case TOKEN_MACDEF:
if (obstack_object_size (obs) == 0)
{
TOKEN_DATA_TYPE (argp) = TOKEN_FUNC;
TOKEN_DATA_FUNC (argp) = TOKEN_DATA_FUNC (&td);
TOKEN_DATA_FUNC_TRACED (argp) = TOKEN_DATA_FUNC_TRACED (&td);
}
break;
default:
M4ERROR ((warning_status, 0,
"INTERNAL ERROR: Bad token type in expand_argument ()"));
abort ();
}
t = next_token (&td);
}
}
static void
collect_arguments (symbol *sym, struct obstack *argptr,
struct obstack *arguments)
{
int ch;
token_data td;
token_data *tdp;
boolean more_args;
boolean groks_macro_args = SYMBOL_MACRO_ARGS (sym);
TOKEN_DATA_TYPE (&td) = TOKEN_TEXT;
TOKEN_DATA_TEXT (&td) = SYMBOL_NAME (sym);
tdp = (token_data *) obstack_copy (arguments, (voidstar) &td, sizeof (td));
obstack_grow (argptr, (voidstar) &tdp, sizeof (tdp));
ch = peek_input ();
if (ch == '(')
{
next_token (&td);
do
{
more_args = expand_argument (arguments, &td);
if (!groks_macro_args && TOKEN_DATA_TYPE (&td) == TOKEN_FUNC)
{
TOKEN_DATA_TYPE (&td) = TOKEN_TEXT;
TOKEN_DATA_TEXT (&td) = "";
}
tdp = (token_data *)
obstack_copy (arguments, (voidstar) &td, sizeof (td));
obstack_grow (argptr, (voidstar) &tdp, sizeof (tdp));
}
while (more_args);
}
}
void
call_macro (symbol *sym, int argc, token_data **argv,
struct obstack *expansion)
{
switch (SYMBOL_TYPE (sym))
{
case TOKEN_FUNC:
(*SYMBOL_FUNC (sym)) (expansion, argc, argv);
break;
case TOKEN_TEXT:
expand_user_macro (expansion, sym, argc, argv);
break;
default:
M4ERROR ((warning_status, 0,
"INTERNAL ERROR: Bad symbol type in call_macro ()"));
abort ();
}
}
static void
expand_macro (symbol *sym)
{
struct obstack arguments;
struct obstack argptr;
token_data **argv;
int argc;
struct obstack *expansion;
const char *expanded;
boolean traced;
int my_call_id;
expansion_level++;
if (expansion_level > nesting_limit)
M4ERROR ((EXIT_FAILURE, 0,
"ERROR: Recursion limit of %d exceeded, use -L<N> to change it",
nesting_limit));
macro_call_id++;
my_call_id = macro_call_id;
traced = (boolean) ((debug_level & DEBUG_TRACE_ALL) || SYMBOL_TRACED (sym));
obstack_init (&argptr);
obstack_init (&arguments);
if (traced && (debug_level & DEBUG_TRACE_CALL))
trace_prepre (SYMBOL_NAME (sym), my_call_id);
collect_arguments (sym, &argptr, &arguments);
argc = obstack_object_size (&argptr) / sizeof (token_data *);
argv = (token_data **) obstack_finish (&argptr);
if (traced)
trace_pre (SYMBOL_NAME (sym), my_call_id, argc, argv);
expansion = push_string_init ();
call_macro (sym, argc, argv, expansion);
expanded = push_string_finish ();
if (traced)
trace_post (SYMBOL_NAME (sym), my_call_id, argc, argv, expanded);
--expansion_level;
obstack_free (&arguments, NULL);
obstack_free (&argptr, NULL);
}