| /* GNU m4 -- A simple macro processor |
| |
| Copyright (C) 1989-1994, 2006-2007, 2009-2014, 2016 Free Software |
| Foundation, Inc. |
| |
| This file is part of GNU M4. |
| |
| GNU M4 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 3 of the License, or |
| (at your option) any later version. |
| |
| GNU M4 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, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| /* 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 *, int); |
| |
| /* 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; |
| |
| /* The shared stack of collected arguments for macro calls; as each |
| argument is collected, it is finished and its location stored in |
| argv_stack. Normally, this stack can be used simultaneously by |
| multiple macro calls; the exception is when an outer macro has |
| generated some text, then calls a nested macro, in which case the |
| nested macro must use a local stack to leave the unfinished text |
| alone. Too bad obstack.h does not provide an easy way to reopen a |
| finished object for further growth, but in practice this does not |
| hurt us too much. */ |
| static struct obstack argc_stack; |
| |
| /* The shared stack of pointers to collected arguments for macro |
| calls. This object is never finished; we exploit the fact that |
| obstack_blank_fast is documented to take a negative size to reduce |
| the size again. */ |
| static struct obstack argv_stack; |
| |
| /*----------------------------------------------------------------------. |
| | This function read all input, and expands each token, one at a time. | |
| `----------------------------------------------------------------------*/ |
| |
| void |
| expand_input (void) |
| { |
| token_type t; |
| token_data td; |
| int line; |
| |
| obstack_init (&argc_stack); |
| obstack_init (&argv_stack); |
| |
| while ((t = next_token (&td, &line)) != TOKEN_EOF) |
| expand_token ((struct obstack *) NULL, t, &td, line); |
| |
| obstack_free (&argc_stack, NULL); |
| obstack_free (&argv_stack, NULL); |
| } |
| |
| |
| /*----------------------------------------------------------------. |
| | 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 is just copied to the output. | |
| `----------------------------------------------------------------*/ |
| |
| static void |
| expand_token (struct obstack *obs, token_type t, token_data *td, int line) |
| { |
| symbol *sym; |
| |
| switch (t) |
| { /* TOKSW */ |
| case TOKEN_EOF: |
| case TOKEN_MACDEF: |
| break; |
| |
| case TOKEN_OPEN: |
| case TOKEN_COMMA: |
| case TOKEN_CLOSE: |
| case TOKEN_SIMPLE: |
| case TOKEN_STRING: |
| shipout_text (obs, TOKEN_DATA_TEXT (td), strlen (TOKEN_DATA_TEXT (td)), |
| line); |
| 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_token () != TOKEN_OPEN)) |
| { |
| #ifdef ENABLE_CHANGEWORD |
| shipout_text (obs, TOKEN_DATA_ORIG_TEXT (td), |
| strlen (TOKEN_DATA_ORIG_TEXT (td)), line); |
| #else |
| shipout_text (obs, TOKEN_DATA_TEXT (td), |
| strlen (TOKEN_DATA_TEXT (td)), line); |
| #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 is the last for the active macro call. The argument | |
| | is built on the obstack OBS, indirectly through expand_token (). | |
| `-------------------------------------------------------------------*/ |
| |
| static bool |
| expand_argument (struct obstack *obs, token_data *argp) |
| { |
| token_type t; |
| token_data td; |
| char *text; |
| int paren_level; |
| const char *file = current_file; |
| int line = current_line; |
| |
| TOKEN_DATA_TYPE (argp) = TOKEN_VOID; |
| |
| /* Skip leading white space. */ |
| do |
| { |
| t = next_token (&td, NULL); |
| } |
| while (t == TOKEN_SIMPLE && isspace (to_uchar (*TOKEN_DATA_TEXT (&td)))); |
| |
| paren_level = 0; |
| |
| while (1) |
| { |
| |
| switch (t) |
| { /* TOKSW */ |
| case TOKEN_COMMA: |
| case TOKEN_CLOSE: |
| if (paren_level == 0) |
| { |
| /* The argument MUST be finished, whether we want it or not. */ |
| obstack_1grow (obs, '\0'); |
| text = (char *) obstack_finish (obs); |
| |
| if (TOKEN_DATA_TYPE (argp) == TOKEN_VOID) |
| { |
| TOKEN_DATA_TYPE (argp) = TOKEN_TEXT; |
| TOKEN_DATA_TEXT (argp) = text; |
| } |
| return t == TOKEN_COMMA; |
| } |
| /* fallthru */ |
| case TOKEN_OPEN: |
| case TOKEN_SIMPLE: |
| text = TOKEN_DATA_TEXT (&td); |
| |
| if (*text == '(') |
| paren_level++; |
| else if (*text == ')') |
| paren_level--; |
| expand_token (obs, t, &td, line); |
| break; |
| |
| case TOKEN_EOF: |
| /* current_file changed to "" if we see TOKEN_EOF, use the |
| previous value we stored earlier. */ |
| M4ERROR_AT_LINE ((EXIT_FAILURE, 0, file, line, |
| "ERROR: end of file in argument list")); |
| break; |
| |
| case TOKEN_WORD: |
| case TOKEN_STRING: |
| expand_token (obs, t, &td, line); |
| break; |
| |
| case TOKEN_MACDEF: |
| if (obstack_object_size (obs) == 0) |
| { |
| TOKEN_DATA_TYPE (argp) = TOKEN_FUNC; |
| TOKEN_DATA_FUNC (argp) = TOKEN_DATA_FUNC (&td); |
| } |
| break; |
| |
| default: |
| M4ERROR ((warning_status, 0, |
| "INTERNAL ERROR: bad token type in expand_argument ()")); |
| abort (); |
| } |
| |
| t = next_token (&td, NULL); |
| } |
| } |
| |
| /*-------------------------------------------------------------. |
| | 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) |
| { |
| token_data td; |
| token_data *tdp; |
| bool more_args; |
| bool 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, &td, sizeof td); |
| obstack_ptr_grow (argptr, tdp); |
| |
| if (peek_token () == TOKEN_OPEN) |
| { |
| next_token (&td, NULL); /* 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) = (char *) ""; |
| } |
| tdp = (token_data *) obstack_copy (arguments, &td, sizeof td); |
| obstack_ptr_grow (argptr, 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; |
| |
| case TOKEN_VOID: |
| 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; /* Alternate obstack if argc_stack is busy. */ |
| unsigned argv_base; /* Size of argv_stack on entry. */ |
| bool use_argc_stack = true; /* Whether argc_stack is safe. */ |
| token_data **argv; |
| int argc; |
| struct obstack *expansion; |
| const char *expanded; |
| bool traced; |
| int my_call_id; |
| |
| /* Report errors at the location where the open parenthesis (if any) |
| was found, but after expansion, restore global state back to the |
| location of the close parenthesis. This is safe since we |
| guarantee that macro expansion does not alter the state of |
| current_file/current_line (dnl, include, and sinclude are special |
| cased in the input engine to ensure this fact). */ |
| const char *loc_open_file = current_file; |
| int loc_open_line = current_line; |
| const char *loc_close_file; |
| int loc_close_line; |
| |
| SYMBOL_PENDING_EXPANSIONS (sym)++; |
| expansion_level++; |
| if (nesting_limit > 0 && expansion_level > nesting_limit) |
| M4ERROR ((EXIT_FAILURE, 0, |
| "recursion limit of %d exceeded, use -L<N> to change it", |
| nesting_limit)); |
| |
| macro_call_id++; |
| my_call_id = macro_call_id; |
| |
| traced = (debug_level & DEBUG_TRACE_ALL) || SYMBOL_TRACED (sym); |
| |
| argv_base = obstack_object_size (&argv_stack); |
| if (obstack_object_size (&argc_stack) > 0) |
| { |
| /* We cannot use argc_stack if this is a nested invocation, and an |
| outer invocation has an unfinished argument being |
| collected. */ |
| obstack_init (&arguments); |
| use_argc_stack = false; |
| } |
| |
| if (traced && (debug_level & DEBUG_TRACE_CALL)) |
| trace_prepre (SYMBOL_NAME (sym), my_call_id); |
| |
| collect_arguments (sym, &argv_stack, |
| use_argc_stack ? &argc_stack : &arguments); |
| |
| argc = ((obstack_object_size (&argv_stack) - argv_base) |
| / sizeof (token_data *)); |
| argv = (token_data **) ((char *) obstack_base (&argv_stack) + argv_base); |
| |
| loc_close_file = current_file; |
| loc_close_line = current_line; |
| current_file = loc_open_file; |
| current_line = loc_open_line; |
| |
| 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, expanded); |
| |
| current_file = loc_close_file; |
| current_line = loc_close_line; |
| |
| --expansion_level; |
| --SYMBOL_PENDING_EXPANSIONS (sym); |
| |
| if (SYMBOL_DELETED (sym)) |
| free_symbol (sym); |
| |
| if (use_argc_stack) |
| obstack_free (&argc_stack, argv[0]); |
| else |
| obstack_free (&arguments, NULL); |
| obstack_blank_fast (&argv_stack, -argc * sizeof (token_data *)); |
| } |