From 1ce676a061d91b434a62d90d7a2f5d8fdf07e8af Mon Sep 17 00:00:00 2001 From: Neil Booth Date: Sun, 9 Jun 2002 20:04:17 +0000 Subject: [PATCH] cpphash.h (_cpp_push_text_context): Update. * cpphash.h (_cpp_push_text_context): Update. (_cpp_arguments_ok): New. * cppmacro.c (_cpp_arguments_ok): New, split out from... (collect_args): ...here. (_cpp_push_text_context): Change inputs. * cpptrad.c (struct fun_macro, maybe_start_funlike, save_argument, replace_args_and_push): New. (lex_identifier, _cpp_lex_identifier_trad, scan_parameters): Don't use IS macros directly. (scan_out_logical_line): Handle function-like macro argument collection. (push_replacement_text): Update. (replacement_length): Remove. (_cpp_create_trad_definition): Don't skip whitespace before checking for '('. From-SVN: r54412 --- gcc/ChangeLog | 18 +++ gcc/cpphash.h | 5 +- gcc/cppmacro.c | 98 +++++++++------- gcc/cpptrad.c | 296 ++++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 322 insertions(+), 95 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 79b875c127d3..4888b7988e08 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,21 @@ +2002-06-09 Neil Booth + + * cpphash.h (_cpp_push_text_context): Update. + (_cpp_arguments_ok): New. + * cppmacro.c (_cpp_arguments_ok): New, split out from... + (collect_args): ...here. + (_cpp_push_text_context): Change inputs. + * cpptrad.c (struct fun_macro, maybe_start_funlike, save_argument, + replace_args_and_push): New. + (lex_identifier, _cpp_lex_identifier_trad, scan_parameters): + Don't use IS macros directly. + (scan_out_logical_line): Handle function-like macro argument + collection. + (push_replacement_text): Update. + (replacement_length): Remove. + (_cpp_create_trad_definition): Don't skip whitespace before + checking for '('. + 2002-06-09 Marek Michalkiewicz * config/avr/avr.c (avr_mcu_types): Update for new devices. diff --git a/gcc/cpphash.h b/gcc/cpphash.h index 9a4915daf723..6baf211958d6 100644 --- a/gcc/cpphash.h +++ b/gcc/cpphash.h @@ -438,9 +438,12 @@ extern void _cpp_free_definition PARAMS ((cpp_hashnode *)); extern bool _cpp_create_definition PARAMS ((cpp_reader *, cpp_hashnode *)); extern void _cpp_pop_context PARAMS ((cpp_reader *)); extern void _cpp_push_text_context PARAMS ((cpp_reader *, cpp_hashnode *, - const uchar *, const uchar*)); + const uchar *, size_t)); extern bool _cpp_save_parameter PARAMS ((cpp_reader *, cpp_macro *, cpp_hashnode *)); +extern bool _cpp_arguments_ok PARAMS ((cpp_reader *, cpp_macro *, + const cpp_hashnode *, + unsigned int)); /* In cpphash.c */ extern void _cpp_init_hashtable PARAMS ((cpp_reader *, hash_table *)); diff --git a/gcc/cppmacro.c b/gcc/cppmacro.c index 5e954d457e0a..d5179487637b 100644 --- a/gcc/cppmacro.c +++ b/gcc/cppmacro.c @@ -451,6 +451,52 @@ paste_all_tokens (pfile, lhs) push_token_context (pfile, NULL, lhs, 1); } +/* Returns TRUE if the number of arguments ARGC supplied in an + invocation of the MACRO referenced by NODE is valid. An empty + invocation to a macro with no parameters should pass ARGC as zero. + + Note that MACRO cannot necessarily be deduced from NODE, in case + NODE was redefined whilst collecting arguments. */ +bool +_cpp_arguments_ok (pfile, macro, node, argc) + cpp_reader *pfile; + cpp_macro *macro; + const cpp_hashnode *node; + unsigned int argc; +{ + if (argc == macro->paramc) + return true; + + if (argc < macro->paramc) + { + /* As an extension, a rest argument is allowed to not appear in + the invocation at all. + e.g. #define debug(format, args...) something + debug("string"); + + This is exactly the same as if there had been an empty rest + argument - debug("string", ). */ + + if (argc + 1 == macro->paramc && macro->variadic) + { + if (CPP_PEDANTIC (pfile) && ! macro->syshdr) + cpp_error (pfile, DL_PEDWARN, + "ISO C99 requires rest arguments to be used"); + return true; + } + + cpp_error (pfile, DL_ERROR, + "macro \"%s\" requires %u arguments, but only %u given", + NODE_NAME (node), macro->paramc, argc); + } + else + cpp_error (pfile, DL_ERROR, + "macro \"%s\" passed %u arguments, but takes just %u", + NODE_NAME (node), argc, macro->paramc); + + return false; +} + /* Reads and returns the arguments to a function-like macro invocation. Assumes the opening parenthesis has been processed. If there is an error, emits an appropriate diagnostic and returns @@ -466,7 +512,6 @@ collect_args (pfile, node) macro_arg *args, *arg; const cpp_token *token; unsigned int argc; - bool error = false; macro = node->value.macro; if (macro->paramc) @@ -561,47 +606,17 @@ collect_args (pfile, node) cpp_error (pfile, DL_ERROR, "unterminated argument list invoking macro \"%s\"", NODE_NAME (node)); - error = true; } - else if (argc < macro->paramc) + else { - /* As an extension, a rest argument is allowed to not appear in - the invocation at all. - e.g. #define debug(format, args...) something - debug("string"); - - This is exactly the same as if there had been an empty rest - argument - debug("string", ). */ - - if (argc + 1 == macro->paramc && macro->variadic) - { - if (CPP_PEDANTIC (pfile) && ! macro->syshdr) - cpp_error (pfile, DL_PEDWARN, - "ISO C99 requires rest arguments to be used"); - } - else - { - cpp_error (pfile, DL_ERROR, - "macro \"%s\" requires %u arguments, but only %u given", - NODE_NAME (node), macro->paramc, argc); - error = true; - } - } - else if (argc > macro->paramc) - { - /* Empty argument to a macro taking no arguments is OK. */ - if (argc != 1 || arg->count) - { - cpp_error (pfile, DL_ERROR, - "macro \"%s\" passed %u arguments, but takes just %u", - NODE_NAME (node), argc, macro->paramc); - error = true; - } + /* A single empty argument is counted as no argument. */ + if (argc == 1 && macro->paramc == 0 && args[0].count == 0) + argc = 0; + if (_cpp_arguments_ok (pfile, macro, node, argc)) + return base_buff; } - if (!error) - return base_buff; - + /* An error occurred. */ _cpp_release_buff (pfile, base_buff); return NULL; } @@ -919,10 +934,11 @@ push_token_context (pfile, macro, first, count) /* Push a traditional macro's replacement text. */ void -_cpp_push_text_context (pfile, macro, start, end) +_cpp_push_text_context (pfile, macro, start, len) cpp_reader *pfile; cpp_hashnode *macro; - const uchar *start, *end; + const uchar *start; + size_t len; { cpp_context *context = next_context (pfile); @@ -930,7 +946,7 @@ _cpp_push_text_context (pfile, macro, start, end) context->macro = macro; context->buff = NULL; CUR (context) = start; - RLIMIT (context) = end; + RLIMIT (context) = start + len; } /* Expand an argument ARG before replacing parameters in a diff --git a/gcc/cpptrad.c b/gcc/cpptrad.c index 3886b4522c7e..7ba9d924ef03 100644 --- a/gcc/cpptrad.c +++ b/gcc/cpptrad.c @@ -44,6 +44,29 @@ struct block #define BLOCK_HEADER_LEN offsetof (struct block, text) #define BLOCK_LEN(TEXT_LEN) CPP_ALIGN (BLOCK_HEADER_LEN + TEXT_LEN) +/* Structure holding information about a function-like macro + invocation. */ +struct fun_macro +{ + /* Memory buffer holding the trad_arg array. */ + _cpp_buff *buff; + + /* An array of size the number of macro parameters + 1, containing + the offsets of the start of each macro argument in the output + buffer. The argument continues until the character before the + start of the next one. */ + size_t *args; + + /* The hashnode of the macro. */ + cpp_hashnode *node; + + /* The offset of the macro name in the output buffer. */ + size_t offset; + + /* Zero-based index of argument being currently lexed. */ + unsigned int argc; +}; + /* Lexing TODO: Handle -C, maybe -CC, and space in escaped newlines. Stop cpplex.c from recognizing comments and directives during its lexing pass. Get rid of line_base usage - seems pointless? Do we @@ -62,7 +85,10 @@ static void push_replacement_text PARAMS ((cpp_reader *, cpp_hashnode *)); static bool scan_parameters PARAMS ((cpp_reader *, cpp_macro *)); static void save_replacement_text PARAMS ((cpp_reader *, cpp_macro *, unsigned int)); -static unsigned int replacement_length PARAMS ((cpp_macro *)); +static void maybe_start_funlike PARAMS ((cpp_reader *, cpp_hashnode *, + const uchar *, struct fun_macro *)); +static void save_argument PARAMS ((struct fun_macro *, size_t)); +static void replace_args_and_push PARAMS ((cpp_reader *, struct fun_macro *)); /* Ensures we have N bytes' space in the output buffer, and reallocates it if not. */ @@ -205,10 +231,10 @@ lex_identifier (pfile, cur) { do *out++ = *cur++; - while (ISIDNUM (*cur)); + while (is_numchar (*cur)); cur = skip_escaped_newlines (pfile, cur); } - while (ISIDNUM (*cur)); + while (is_numchar (*cur)); CUR (pfile->context) = cur; len = out - pfile->trad_out_cur; @@ -226,7 +252,7 @@ _cpp_lex_identifier_trad (pfile) { const uchar *cur = skip_whitespace (pfile, CUR (pfile->context)); - if (!ISIDST (*cur)) + if (!is_idstart (*cur)) { CUR (pfile->context) = cur; return NULL; @@ -309,12 +335,45 @@ _cpp_read_logical_line_trad (pfile) return true; } +/* Set up state for finding the opening '(' of a function-like + macro. */ +static void +maybe_start_funlike (pfile, node, start, macro) + cpp_reader *pfile; + cpp_hashnode *node; + const uchar *start; + struct fun_macro *macro; +{ + unsigned int n = node->value.macro->paramc + 1; + + if (macro->buff) + _cpp_release_buff (pfile, macro->buff); + macro->buff = _cpp_get_buff (pfile, n * sizeof (size_t)); + macro->args = (size_t *) BUFF_FRONT (macro->buff); + macro->node = node; + macro->offset = start - pfile->trad_out_base; + macro->argc = 0; + + pfile->state.parsing_args = 1; +} + +/* Save the OFFSET of the start of the next argument to MACRO. */ +static void +save_argument (macro, offset) + struct fun_macro *macro; + size_t offset; +{ + macro->argc++; + if (macro->argc <= macro->node->value.macro->paramc) + macro->args[macro->argc] = offset; +} + /* Copies the next logical line in the current buffer to the output buffer. The output is guaranteed to terminate with a NUL character. If MACRO is non-NULL, then we are scanning the replacement list of - MACRO, and we call save_replacement_text every time we meet an + MACRO, and we call save_replacement_text() every time we meet an argument. */ static void scan_out_logical_line (pfile, macro) @@ -323,9 +382,11 @@ scan_out_logical_line (pfile, macro) { cpp_context *context; const uchar *cur; - unsigned int c, quote = 0; + unsigned int c, paren_depth, quote = 0; uchar *out; + struct fun_macro fmacro; + fmacro.buff = NULL; new_context: context = pfile->context; cur = CUR (context); @@ -357,16 +418,22 @@ scan_out_logical_line (pfile, macro) cur--; if (!pfile->buffer->from_stage3) cpp_error (pfile, DL_PEDWARN, "no newline at end of file"); + if (pfile->state.parsing_args == 2) + cpp_error (pfile, DL_ERROR, + "unterminated argument list invoking macro \"%s\"", + NODE_NAME (fmacro.node)); pfile->line++; - goto finish_output; + goto done; case '\r': case '\n': cur = handle_newline (pfile, cur - 1); - out[-1] = '\0'; - finish_output: - CUR (context) = cur; - pfile->trad_out_cur = out - 1; - return; + if (pfile->state.parsing_args == 2) + { + /* Newlines in arguments become a space. */ + out[-1] = ' '; + continue; + } + goto done; case '"': case '\'': @@ -418,16 +485,25 @@ scan_out_logical_line (pfile, macro) pfile->trad_out_cur = --out; node = lex_identifier (pfile, cur - 1); - if (node->type == NT_MACRO && !pfile->state.prevent_expansion) + if (node->type == NT_MACRO + && pfile->state.parsing_args != 2 + && !pfile->state.prevent_expansion) { - /* Remove the macro name from the output. */ - pfile->trad_out_cur = out; - push_replacement_text (pfile, node); - goto new_context; + if (node->value.macro->fun_like) + maybe_start_funlike (pfile, node, out, &fmacro); + else + { + /* Remove the object-like macro's name from the + output, and push its replacement text. */ + pfile->trad_out_cur = out; + push_replacement_text (pfile, node); + goto new_context; + } } else if (macro && node->arg_index) { - /* Remove the macro name from the output. */ + /* Found a parameter in the replacement text of a + #define. Remove its name from the output. */ pfile->trad_out_cur = out; save_replacement_text (pfile, macro, node->arg_index); } @@ -437,15 +513,91 @@ scan_out_logical_line (pfile, macro) } break; + case '(': + if (quote == 0) + { + paren_depth++; + if (pfile->state.parsing_args == 1) + { + const uchar *p = pfile->trad_out_base + fmacro.offset; + + /* Invoke a prior function-like macro if there is only + white space in-between. */ + while (is_numchar (*p)) + p++; + while (is_space (*p)) + p++; + + if (p == out - 1) + { + pfile->state.parsing_args = 2; + paren_depth = 1; + out = pfile->trad_out_base + fmacro.offset; + fmacro.args[0] = fmacro.offset; + } + else + pfile->state.parsing_args = 0; + } + } + break; + + case ',': + if (quote == 0 && pfile->state.parsing_args == 2 && paren_depth == 1) + save_argument (&fmacro, out - pfile->trad_out_base); + break; + + case ')': + if (quote == 0) + { + paren_depth--; + if (pfile->state.parsing_args == 2 && paren_depth == 0) + { + cpp_macro *m = fmacro.node->value.macro; + + pfile->state.parsing_args = 0; + save_argument (&fmacro, out - pfile->trad_out_base); + + /* A single whitespace argument is no argument. */ + if (fmacro.argc == 1 && m->paramc == 0) + { + const uchar *p = pfile->trad_out_base; + p += fmacro.args[0]; + while (is_space (*p)) + p++; + if (p == pfile->trad_out_base + fmacro.args[1]) + fmacro.argc = 0; + } + + if (_cpp_arguments_ok (pfile, m, fmacro.node, fmacro.argc)) + { + /* Remove the macro's invocation from the + output, and push its replacement text. */ + pfile->trad_out_cur = (pfile->trad_out_base + + fmacro.offset); + CUR (context) = cur; + replace_args_and_push (pfile, &fmacro); + goto new_context; + } + } + } + break; + default: break; } } + + done: + out[-1] = '\0'; + CUR (context) = cur; + pfile->trad_out_cur = out - 1; + if (fmacro.buff) + _cpp_release_buff (pfile, fmacro.buff); } /* Push a context holding the replacement text of the macro NODE on - the context stack. Doesn't yet handle special built-ins or - function-like macros. */ + the context stack. NODE is either object-like, or a function-like + macro with no arguments. */ static void push_replacement_text (pfile, node) cpp_reader *pfile; @@ -453,9 +605,70 @@ push_replacement_text (pfile, node) { cpp_macro *macro = node->value.macro; - _cpp_push_text_context (pfile, node, - macro->exp.text, - macro->exp.text + macro->count); + _cpp_push_text_context (pfile, node, macro->exp.text, macro->count); +} + +/* Push a context holding the replacement text of the macro NODE on + the context stack. NODE is either object-like, or a function-like + macro with no arguments. */ +static void +replace_args_and_push (pfile, fmacro) + cpp_reader *pfile; + struct fun_macro *fmacro; +{ + cpp_macro *macro = fmacro->node->value.macro; + + if (macro->paramc == 0) + push_replacement_text (pfile, fmacro->node); + else + { + const uchar *exp; + uchar *p; + _cpp_buff *buff; + size_t len = 0; + + /* Calculate the length of the argument-replaced text. */ + for (exp = macro->exp.text;;) + { + struct block *b = (struct block *) exp; + + len += b->text_len; + if (b->arg_index == 0) + break; + len += (fmacro->args[b->arg_index] + - fmacro->args[b->arg_index - 1] - 1); + exp += BLOCK_LEN (b->text_len); + } + + /* Allocate room for the expansion plus NUL. */ + buff = _cpp_get_buff (pfile, len + 1); + + /* Copy the expansion and replace arguments. */ + p = BUFF_FRONT (buff); + for (exp = macro->exp.text;;) + { + struct block *b = (struct block *) exp; + size_t arglen; + + memcpy (p, b->text, b->text_len); + p += b->text_len; + if (b->arg_index == 0) + break; + arglen = (fmacro->args[b->arg_index] + - fmacro->args[b->arg_index - 1] - 1); + memcpy (p, pfile->trad_out_base + fmacro->args[b->arg_index - 1], + arglen); + p += arglen; + exp += BLOCK_LEN (b->text_len); + } + + /* NUL-terminate. */ + *p = '\0'; + _cpp_push_text_context (pfile, fmacro->node, BUFF_FRONT (buff), len); + + /* So we free buffer allocation when macro is left. */ + pfile->context->buff = buff; + } } /* Read and record the parameters, if any, of a function-like macro @@ -476,7 +689,7 @@ scan_parameters (pfile, macro) { cur = skip_whitespace (pfile, cur); - if (ISIDST (*cur)) + if (is_idstart (*cur)) { ok = false; if (_cpp_save_parameter (pfile, macro, lex_identifier (pfile, cur))) @@ -500,25 +713,6 @@ scan_parameters (pfile, macro) return ok; } -/* Calculate the length of the replacement text of MACRO. */ -static unsigned int -replacement_length (macro) - cpp_macro *macro; -{ - unsigned int result = 0; - const uchar *exp = macro->exp.text; - - for (;;) - { - struct block *block = (struct block *) exp; - - result += block->text_len; - if (block->arg_index == 0) - return result; - exp += BLOCK_LEN (block->text_len); - } -} - /* Save the text from pfile->trad_out_base to pfile->trad_out_cur as the replacement text for the current macro, followed by argument ARG_INDEX, with zero indicating the end of the replacement @@ -568,10 +762,7 @@ save_replacement_text (pfile, macro, arg_index) in the replacement list, excluding the parameter names, and save this in macro->count, else store the total bytes in the replacement text so far (including block headers). */ - if (arg_index == 0) - macro->count = replacement_length (macro); - else - macro->count += blen; + macro->count += blen; } } @@ -585,13 +776,11 @@ _cpp_create_trad_definition (pfile, macro) const uchar *cur; uchar *limit; - /* Skip leading whitespace now. */ - CUR (pfile->context) = skip_whitespace (pfile, CUR (pfile->context)); - /* Is this a function-like macro? */ if (* CUR (pfile->context) == '(') { - /* Setting macro to NULL indicates an error occurred. */ + /* Setting macro to NULL indicates an error occurred, and + prevents unnecessary work in scan_out_logical_line. */ if (!scan_parameters (pfile, macro)) macro = NULL; else @@ -601,10 +790,11 @@ _cpp_create_trad_definition (pfile, macro) BUFF_FRONT (pfile->a_buff) = (uchar *) ¯o->params[macro->paramc]; macro->fun_like = 1; } - - CUR (pfile->context) = skip_whitespace (pfile, CUR (pfile->context)); } + /* Skip leading whitespace in the replacement text. */ + CUR (pfile->context) = skip_whitespace (pfile, CUR (pfile->context)); + pfile->trad_out_cur = pfile->trad_out_base; pfile->state.prevent_expansion++; scan_out_logical_line (pfile, macro);