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
This commit is contained in:
Neil Booth 2002-06-09 20:04:17 +00:00 committed by Neil Booth
parent 25f2e17641
commit 1ce676a061
4 changed files with 322 additions and 95 deletions

View File

@ -1,3 +1,21 @@
2002-06-09 Neil Booth <neil@daikokuya.demon.co.uk>
* 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 <marekm@amelek.gda.pl>
* config/avr/avr.c (avr_mcu_types): Update for new devices.

View File

@ -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 *));

View File

@ -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

View File

@ -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 *) &macro->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);