| /* 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<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); |
| } |