/* GNU m4 -- A simple macro processor Copyright (C) 1989, 90, 91, 92, 93, 94 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* This file contains the functions, that performs the basic argument parsing and macro expansion. */ #include "m4.h" static void expand_macro _((symbol *)); static void expand_token _((struct obstack *, token_type, token_data *)); /* Current recursion level in expand_macro (). */ int expansion_level = 0; /* The number of the current call of expand_macro (). */ static int macro_call_id = 0; /*----------------------------------------------------------------------. | This function read all input, and expands each token, one at a time. | `----------------------------------------------------------------------*/ void expand_input (void) { token_type t; token_data td; while ((t = next_token (&td)) != TOKEN_EOF) expand_token ((struct obstack *) NULL, t, &td); } /*------------------------------------------------------------------------. | Expand one token, according to its type. Potential macro names | | (TOKEN_WORD) are looked up in the symbol table, to see if they have a | | macro definition. If they have, they are expanded as macros, otherwise | | the text are just copied to the output. | `------------------------------------------------------------------------*/ static void expand_token (struct obstack *obs, token_type t, token_data *td) { symbol *sym; switch (t) { /* TOKSW */ 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 (); } } /*-------------------------------------------------------------------------. | This function parses one argument to a macro call. It expects the first | | left parenthesis, or the separating comma to have been read by the | | caller. It skips leading whitespace, and reads and expands tokens, | | until it finds a comma or an right parenthesis at the same level of | | parentheses. It returns a flag indicating whether the argument read are | | the last for the active macro call. The argument are build on the | | obstack OBS, indirectly through expand_token (). | `-------------------------------------------------------------------------*/ 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; /* Skip leading white space. */ do { t = next_token (&td); } while (t == TOKEN_SIMPLE && isspace (*TOKEN_DATA_TEXT (&td))); paren_level = 0; while (1) { switch (t) { /* TOKSW */ case TOKEN_SIMPLE: text = TOKEN_DATA_TEXT (&td); if ((*text == ',' || *text == ')') && paren_level == 0) { /* The argument MUST be finished, whether we want it or not. */ 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); } } /*-------------------------------------------------------------------------. | Collect all the arguments to a call of the macro SYM. The arguments are | | stored on the obstack ARGUMENTS and a table of pointers to the arguments | | on the obstack ARGPTR. | `-------------------------------------------------------------------------*/ static void collect_arguments (symbol *sym, struct obstack *argptr, struct obstack *arguments) { int ch; /* lookahead for ( */ 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); /* gobble parenthesis */ 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); } } /*------------------------------------------------------------------------. | The actual call of a macro is handled by call_macro (). call_macro () | | is passed a symbol SYM, whose type is used to call either a builtin | | function, or the user macro expansion function expand_user_macro () | | (lives in builtin.c). There are ARGC arguments to the call, stored in | | the ARGV table. The expansion is left on the obstack EXPANSION. Macro | | tracing is also handled here. | `------------------------------------------------------------------------*/ 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 (); } } /*-------------------------------------------------------------------------. | The macro expansion is handled by expand_macro (). It parses the | | arguments, using collect_arguments (), and builds a table of pointers to | | the arguments. The arguments themselves are stored on a local obstack. | | Expand_macro () uses call_macro () to do the call of the macro. | | | | Expand_macro () is potentially recursive, since it calls expand_argument | | (), which might call expand_token (), which might call expand_macro (). | `-------------------------------------------------------------------------*/ 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 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); }