This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GNU M4 source repository".
http://git.sv.gnu.org/gitweb/?p=m4.git;a=commitdiff;h=7319157ccd7cd65f72c0a456c3091252a13f558a The branch, branch-1_4 has been updated via 7319157ccd7cd65f72c0a456c3091252a13f558a (commit) from 950807234e3ffd376ad51b2a21cd276dd8b4e59c (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 7319157ccd7cd65f72c0a456c3091252a13f558a Author: Eric Blake <[EMAIL PROTECTED]> Date: Thu Nov 1 09:28:46 2007 -0600 Stage 15: return argv refs back to collect_arguments. * src/m4.h (enum token_type): Add TOKEN_ARGV. (struct token_chain): Add skip_last member to argv link. (next_token): Update prototype. * src/input.c (CHAR_ARGV): New placeholder input character. (peek_input): Add parameter, to pass $@ at once. (next_char_1, append_quote_token): Handle $@ inside quotes. (init_argv_token): New function. (push_token, match_input, next_token, peek_token, lex_debug): Update callers. * src/macro.c (expand_input, collect_arguments): Likewise. (expand_argument): Handle incoming $@ token. (arg_adjust_refcount, arg_token, arg_text, make_argv_ref_token): Handle nested $@ refs. * src/symtab.c (symtab_debug): Update caller. * examples/null.m4: Document more tests that are needed. Add tests for NUL with divert, patsubst, and regexp. * examples/null.out: Update for new tests. * doc/m4.texinfo (Syntax): Add test for m4exit and NUL. * checks/get-them (AWK): Give a default value. * checks/check-them: Allow tests to invoke child processes with same include path. Perform message normalization on stderr. (cherry picked from commit 1fecefc8b990254aa667a01d12c6c7a2d716df06) Signed-off-by: Eric Blake <[EMAIL PROTECTED]> ----------------------------------------------------------------------- Summary of changes: ChangeLog | 31 +++++++++ checks/check-them | 10 ++- checks/get-them | 4 +- doc/m4.texinfo | 8 ++- examples/null.m4 | Bin 3671 -> 5764 bytes examples/null.out | Bin 338 -> 400 bytes src/input.c | 177 +++++++++++++++++++++++++++++++++++++++++------------ src/m4.h | 7 ++- src/macro.c | 112 +++++++++++++++++++++++++-------- src/symtab.c | 2 +- 10 files changed, 275 insertions(+), 76 deletions(-) diff --git a/ChangeLog b/ChangeLog index c455abc..0baa3c1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,34 @@ +2008-02-16 Eric Blake <[EMAIL PROTECTED]> + + Stage 15: return argv refs back to collect_arguments. + Collect an entire $@ reference at once rather than one argument at + a time, outside of quotes (but inside quotes, $@ is still + flattened for now). The skip_last field allows concatenation of + $@ with other text when collecting arguments. + Memory impact: noticeable improvement, due to better reuse of [EMAIL PROTECTED] + Speed impact: noticeable improvement, due to less parsing. + * src/m4.h (enum token_type): Add TOKEN_ARGV. + (struct token_chain): Add skip_last member to argv link. + (next_token): Update prototype. + * src/input.c (CHAR_ARGV): New placeholder input character. + (peek_input): Add parameter, to pass $@ at once. + (next_char_1, append_quote_token): Handle $@ inside quotes. + (init_argv_token): New function. + (push_token, match_input, next_token, peek_token, lex_debug): + Update callers. + * src/macro.c (expand_input, collect_arguments): Likewise. + (expand_argument): Handle incoming $@ token. + (arg_adjust_refcount, arg_token, arg_text, make_argv_ref_token): + Handle nested $@ refs. + * src/symtab.c (symtab_debug): Update caller. + * examples/null.m4: Document more tests that are needed. Add + tests for NUL with divert, patsubst, and regexp. + * examples/null.out: Update for new tests. + * doc/m4.texinfo (Syntax): Add test for m4exit and NUL. + * checks/get-them (AWK): Give a default value. + * checks/check-them: Allow tests to invoke child processes with + same include path. Perform message normalization on stderr. + 2008-02-15 Eric Blake <[EMAIL PROTECTED]> Use fastmaps for better regex performance. diff --git a/checks/check-them b/checks/check-them index daa1b00..9fca39b 100755 --- a/checks/check-them +++ b/checks/check-them @@ -1,6 +1,6 @@ #!/bin/sh # Check GNU m4 against examples from the manual source. -# Copyright (C) 1992, 2006, 2007 Free Software Foundation, Inc. +# Copyright (C) 1992, 2006, 2007, 2008 Free Software Foundation, Inc. # Sanity check what we are testing m4 --version @@ -68,7 +68,7 @@ do echo "Checking $file" options=`sed -ne '3s/^dnl @ extra options: //p;3q' "$file"` sed -e '/^dnl @/d' -e '/^\^D$/q' "$file" \ - | LC_MESSAGES=C m4 -d -I "$examples" $options - >$out 2>$err + | LC_MESSAGES=C M4PATH=$examples m4 -d $options - >$out 2>$err stat=$? xstat=`sed -ne '2s/^dnl @ expected status: //p;2q' "$file"` @@ -96,9 +96,11 @@ do xerrfile=`sed -n 's/^dnl @ expected error: //p' "$file"` if test -z "$xerrfile" ; then - sed -e '/^dnl @error{}/!d' -e 's///' -e "s|^m4:|$m4:|" "$file" > $xerr + sed '/^dnl @error{}/!d; s///; '"s|^m4:|$m4:|; s|\.\./examples|$examples|" \ + "$file" > $xerr else - cp "$examples/$xerrfile" $xerr + sed "s|^m4:|$m4:|; s|\.\./examples|$examples|" \ + "$examples/$xerrfile" > $xerr fi # For the benefit of mingw, normalize \r\n line endings diff --git a/checks/get-them b/checks/get-them index e034962..803f413 100755 --- a/checks/get-them +++ b/checks/get-them @@ -1,11 +1,13 @@ #!/bin/sh # -*- AWK -*- # Extract all examples from the manual source. -# Copyright (C) 1992, 2005, 2006, 2007 Free Software Foundation, Inc. +# Copyright (C) 1992, 2005, 2006, 2007, 2008 Free Software Foundation, +# Inc. # This script is for use with GNU awk. FILE=${1-/dev/null} +: ${AWK=awk} $AWK ' diff --git a/doc/m4.texinfo b/doc/m4.texinfo index 69cfb62..32cb0a9 100644 --- a/doc/m4.texinfo +++ b/doc/m4.texinfo @@ -933,7 +933,13 @@ exception of the @sc{nul} character (the zero byte @samp{'\0'}). @comment xout: null.out @comment xerr: null.err @example -include(`null.m4')dnl +define(`m4exit')include(`null.m4')dnl [EMAIL PROTECTED] example + [EMAIL PROTECTED] status: 2 [EMAIL PROTECTED] +include(`null.m4') [EMAIL PROTECTED] This file tests m4 behavior on NUL bytes. @end example @end ignore diff --git a/examples/null.m4 b/examples/null.m4 index 904a6ef..2632522 100644 Binary files a/examples/null.m4 and b/examples/null.m4 differ diff --git a/examples/null.out b/examples/null.out index 6e8a114..c42e03c 100644 Binary files a/examples/null.out and b/examples/null.out differ diff --git a/src/input.c b/src/input.c index a0de36f..e320c72 100644 --- a/src/input.c +++ b/src/input.c @@ -154,6 +154,7 @@ static bool input_change; #define CHAR_EOF 256 /* Character return on EOF. */ #define CHAR_MACRO 257 /* Character return for MACRO token. */ #define CHAR_QUOTE 258 /* Character return for quoted string. */ +#define CHAR_ARGV 259 /* Character return for $@ reference. */ /* Quote chars. */ string_pair curr_quote; @@ -446,7 +447,7 @@ push_token (token_data *token, int level, bool inuse) next->u.u_c.end = chain; if (chain->type == CHAIN_ARGV) { - assert (!chain->u.u_a.comma); + assert (!chain->u.u_a.comma && !chain->u.u_a.skip_last); inuse |= arg_adjust_refcount (chain->u.u_a.argv, true); } else if (chain->type == CHAIN_STR && chain->u.u_s.level >= 0) @@ -712,17 +713,18 @@ input_print (struct obstack *obs, const input_block *input) } -/*-----------------------------------------------------------------. -| Low level input is done a character at a time. The function | -| peek_input () is used to look at the next character in the input | -| stream. At any given time, it reads from the input_block on the | -| top of the current input stack. The return value is an unsigned | -| char, or CHAR_EOF if there is no more input, or CHAR_MACRO if a | -| builtin token occurs next. | -`-----------------------------------------------------------------*/ +/*------------------------------------------------------------------. +| Low level input is done a character at a time. The function | +| peek_input () is used to look at the next character in the input | +| stream. At any given time, it reads from the input_block on the | +| top of the current input stack. The return value is an unsigned | +| char, CHAR_EOF if there is no more input, CHAR_MACRO if a builtin | +| token occurs next, or CHAR_ARGV if ALLOW_ARGV and the input is | +| visiting an argv reference with the correct quoting. | +`------------------------------------------------------------------*/ static int -peek_input (void) +peek_input (bool allow_argv) { int ch; input_block *block = isp; @@ -757,6 +759,7 @@ peek_input (void) chain = block->u.u_c.chain; while (chain) { + unsigned int argc; switch (chain->type) { case CHAIN_STR: @@ -764,11 +767,17 @@ peek_input (void) return to_uchar (*chain->u.u_s.str); break; case CHAIN_ARGV: - /* TODO - pass multiple arguments to macro.c at once. */ - if (chain->u.u_a.index == arg_argc (chain->u.u_a.argv)) + argc = arg_argc (chain->u.u_a.argv); + if (chain->u.u_a.index == argc) break; if (chain->u.u_a.comma) return ','; + /* Only return a reference if the quoting is correct + and the reference has more than one argument + left. */ + if (allow_argv && chain->quote_age == current_quote_age + && chain->u.u_a.quotes && chain->u.u_a.index + 1 < argc) + return CHAR_ARGV; /* Rather than directly parse argv here, we push another input block containing the next unparsed argument from argv. */ @@ -778,7 +787,7 @@ peek_input (void) chain->u.u_a.index++; chain->u.u_a.comma = true; push_string_finish (); - return peek_input (); + return peek_input (allow_argv); default: assert (!"peek_input"); abort (); @@ -871,9 +880,7 @@ next_char_1 (bool allow_quote) chain = isp->u.u_c.chain; while (chain) { - /* TODO also support returning $@ as CHAR_QUOTE. */ - if (allow_quote && chain->quote_age == current_quote_age - && chain->type == CHAIN_STR) + if (allow_quote && chain->quote_age == current_quote_age) return CHAR_QUOTE; switch (chain->type) { @@ -889,7 +896,6 @@ next_char_1 (bool allow_quote) adjust_refcount (chain->u.u_s.level, false); break; case CHAIN_ARGV: - /* TODO - pass multiple arguments to macro.c at once. */ if (chain->u.u_a.index == arg_argc (chain->u.u_a.argv)) { arg_adjust_refcount (chain->u.u_a.argv, false); @@ -956,7 +962,6 @@ skip_line (const char *name) if (file != current_file || line != current_line) input_change = true; } - /*-------------------------------------------------------------------. | When a MACRO token is seen, next_token () uses init_macro_token () | @@ -983,20 +988,30 @@ append_quote_token (struct obstack *obs, token_data *td) token_chain *src_chain = isp->u.u_c.chain; token_chain *chain; - assert (isp->type == INPUT_CHAIN && obs && current_quote_age - && src_chain->type == CHAIN_STR && src_chain->u.u_s.level >= 0); + assert (isp->type == INPUT_CHAIN && obs && current_quote_age); isp->u.u_c.chain = src_chain->next; /* Speed consideration - for short enough tokens, the speed and memory overhead of parsing another INPUT_CHAIN link outweighs the time to inline the token text. */ - if (src_chain->u.u_s.len <= INPUT_INLINE_THRESHOLD) + if (src_chain->type == CHAIN_STR + && src_chain->u.u_s.len <= INPUT_INLINE_THRESHOLD) { + assert (src_chain->u.u_s.level >= 0); obstack_grow (obs, src_chain->u.u_s.str, src_chain->u.u_s.len); adjust_refcount (src_chain->u.u_s.level, false); return; } + /* TODO preserve $@ through a quoted context. */ + if (src_chain->type == CHAIN_ARGV) + { + arg_print (obs, src_chain->u.u_a.argv, src_chain->u.u_a.index, + src_chain->u.u_a.quotes, NULL); + arg_adjust_refcount (src_chain->u.u_a.argv, false); + return; + } + if (TOKEN_DATA_TYPE (td) == TOKEN_VOID) { TOKEN_DATA_TYPE (td) = TOKEN_COMP; @@ -1013,6 +1028,65 @@ append_quote_token (struct obstack *obs, token_data *td) chain->next = NULL; } + +/*-------------------------------------------------------------------. +| When an ARGV token is seen, convert TD to point to it via a | +| composite token. Use OBS for any additional allocations needed to | +| store the token chain. | +`-------------------------------------------------------------------*/ +static void +init_argv_token (struct obstack *obs, token_data *td) +{ + token_chain *src_chain; + token_chain *chain; + int ch = next_char (true); + + assert (ch == CHAR_QUOTE && TOKEN_DATA_TYPE (td) == TOKEN_VOID + && isp->type == INPUT_CHAIN && isp->u.u_c.chain->type == CHAIN_ARGV + && obs && obstack_object_size (obs) == 0); + + src_chain = isp->u.u_c.chain; + isp->u.u_c.chain = src_chain->next; + TOKEN_DATA_TYPE (td) = TOKEN_COMP; + /* Clone the link, since the input will be discarded soon. */ + chain = (token_chain *) obstack_copy (obs, src_chain, sizeof *chain); + td->u.u_c.chain = td->u.u_c.end = chain; + chain->next = NULL; + + /* If the next character is not ',' or ')', then unlink the last + argument from argv and schedule it for reparsing. This way, + expand_argument never has to deal with concatenation of argv with + arbitrary text. Note that the implementation of safe_quotes + ensures peek_input won't return CHAR_ARGV if the user is perverse + enough to mix comment delimiters with argument separators: + + define(n,`$#')define(echo,$*)changecom(`,,',`)')n(echo(a,`,b`)'',c)) + => 2 (not 3) + + Therefore, we do not have to worry about calling MATCH, and thus + do not have to worry about pop_input being called and + invalidating the argv reference. + + When the $@ ref is used unchanged, we completely bypass the + decrement of the argv refcount in next_char_1, since the ref is + still live via the current collect_arguments. However, when the + last element of the $@ ref is reparsed, we must increase the argv + refcount here, to compensate for the fact that it will be + decreased once the final element is parsed. */ + assert (*curr_comm.str1 != ',' && *curr_comm.str1 != ')' + && *curr_comm.str1 != *curr_quote.str1); + ch = peek_input (false); + if (ch != ',' && ch != ')') + { + isp->u.u_c.chain = src_chain; + src_chain->u.u_a.index = arg_argc (chain->u.u_a.argv) - 1; + src_chain->u.u_a.comma = true; + chain->u.u_a.skip_last = true; + arg_adjust_refcount (chain->u.u_a.argv, true); + } +} + + /*------------------------------------------------------------------. | This function is for matching a string against a prefix of the | | input stream. If the string S matches the input and CONSUME is | @@ -1029,7 +1103,7 @@ match_input (const char *s, bool consume) const char *t; bool result = false; - ch = peek_input (); + ch = peek_input (false); if (ch != to_uchar (*s)) return false; /* fail */ @@ -1041,7 +1115,7 @@ match_input (const char *s, bool consume) } next_char (false); - for (n = 1, t = s++; (ch = peek_input ()) == to_uchar (*s++); ) + for (n = 1, t = s++; (ch = peek_input (false)) == to_uchar (*s++); ) { next_char (false); n++; @@ -1320,18 +1394,20 @@ safe_quotes (void) /*--------------------------------------------------------------------. -| Parse a single token from the input stream, set TD to its | -| contents, and return its type. A token is TOKEN_EOF if the | +| Parse a single token from the input stream, set TD to its | +| contents, and return its type. A token is TOKEN_EOF if the | | input_stack is empty; TOKEN_STRING for a quoted string or comment; | -| TOKEN_WORD for something that is a potential macro name; and | +| TOKEN_WORD for something that is a potential macro name; and | | TOKEN_SIMPLE for any single character that is not a part of any of | | the previous types. If LINE is not NULL, set *LINE to the line | | where the token starts. If OBS is not NULL, expand TOKEN_STRING | | directly into OBS rather than in token_stack temporary storage | -| area, and TD could be a TOKEN_COMP instead of the usual | -| TOKEN_TEXT. Report errors (unterminated comments or strings) on | -| behalf of CALLER, if non-NULL. | -| | +| area, and TD could be a TOKEN_COMP instead of the usual | +| TOKEN_TEXT. If ALLOW_ARGV, OBS must be non-NULL, and an entire | +| series of arguments can be returned as TOKEN_ARGV when a $@ | +| reference is encountered. Report errors (unterminated comments or | +| strings) on behalf of CALLER, if non-NULL. | +| | | Next_token () returns the token type, and passes back a pointer to | | the token data through TD. Non-string token text is collected on | | the obstack token_stack, which never contains more than one token | @@ -1340,7 +1416,8 @@ safe_quotes (void) `--------------------------------------------------------------------*/ token_type -next_token (token_data *td, int *line, struct obstack *obs, const char *caller) +next_token (token_data *td, int *line, struct obstack *obs, bool allow_argv, + const char *caller) { int ch; int quote_level; @@ -1362,7 +1439,7 @@ next_token (token_data *td, int *line, struct obstack *obs, const char *caller) /* Can't consume character until after CHAR_MACRO is handled. */ TOKEN_DATA_TYPE (td) = TOKEN_VOID; - ch = peek_input (); + ch = peek_input (allow_argv && current_quote_age); if (ch == CHAR_EOF) { #ifdef DEBUG_INPUT @@ -1381,6 +1458,17 @@ next_token (token_data *td, int *line, struct obstack *obs, const char *caller) #endif /* DEBUG_INPUT */ return TOKEN_MACDEF; } + if (ch == CHAR_ARGV) + { + init_argv_token (obs, td); +#ifdef DEBUG_INPUT + xfprintf (stderr, "next_token -> ARGV (%d args)\n", + (arg_argc (td->u.u_c.chain->u.u_a.argv) + - td->u.u_c.chain->u.u_a.index + - (td->u.u_c.chain->u.u_a.skip_last ? 1 : 0))); +#endif + return TOKEN_ARGV; + } next_char (false); /* Consume character we already peeked at. */ file = current_file; @@ -1409,7 +1497,8 @@ next_token (token_data *td, int *line, struct obstack *obs, const char *caller) else if (default_word_regexp && (isalpha (ch) || ch == '_')) { obstack_1grow (&token_stack, ch); - while ((ch = peek_input ()) < CHAR_EOF && (isalnum (ch) || ch == '_')) + while ((ch = peek_input (false)) < CHAR_EOF + && (isalnum (ch) || ch == '_')) { obstack_1grow (&token_stack, ch); next_char (false); @@ -1424,7 +1513,7 @@ next_token (token_data *td, int *line, struct obstack *obs, const char *caller) obstack_1grow (&token_stack, ch); while (1) { - ch = peek_input (); + ch = peek_input (false); if (ch >= CHAR_EOF) break; obstack_1grow (&token_stack, ch); @@ -1547,9 +1636,19 @@ next_token (token_data *td, int *line, struct obstack *obs, const char *caller) token_type_string (type)); while (chain) { - assert (chain->type == CHAIN_STR); - xfprintf (stderr, "%s", chain->u.u_s.str); - len += chain->u.u_s.len; + switch (chain->type) + { + case CHAIN_STR: + xfprintf (stderr, "%s", chain->u.u_s.str); + len += chain->u.u_s.len; + break; + case CHAIN_ARGV: + xfprintf (stderr, "[EMAIL PROTECTED]"); + break; + default: + assert (!"next_token"); + abort (); + } links++; chain = chain->next; } @@ -1569,7 +1668,7 @@ token_type peek_token (void) { token_type result; - int ch = peek_input (); + int ch = peek_input (false); if (ch == CHAR_EOF) { @@ -1684,7 +1783,7 @@ lex_debug (void) token_type t; token_data td; - while ((t = next_token (&td, NULL, NULL, "<debug>")) != TOKEN_EOF) + while ((t = next_token (&td, NULL, NULL, false, "<debug>")) != TOKEN_EOF) print_token ("lex", t, &td); } #endif /* DEBUG_INPUT */ diff --git a/src/m4.h b/src/m4.h index 0f11366..7df29b8 100644 --- a/src/m4.h +++ b/src/m4.h @@ -266,7 +266,8 @@ enum token_type TOKEN_COMMA, /* Active character `,', TOKEN_TEXT. */ TOKEN_CLOSE, /* Active character `)', TOKEN_TEXT. */ TOKEN_SIMPLE, /* Any other single character, TOKEN_TEXT. */ - TOKEN_MACDEF /* A macro's definition (see "defn"), TOKEN_FUNC. */ + TOKEN_MACDEF, /* A macro's definition (see "defn"), TOKEN_FUNC. */ + TOKEN_ARGV /* A series of parameters, TOKEN_COMP. */ }; /* The data for a token, a macro argument, and a macro definition. */ @@ -309,6 +310,7 @@ struct token_chain unsigned int index; /* Argument index within argv. */ bool_bitfield flatten : 1; /* True to treat builtins as text. */ bool_bitfield comma : 1; /* True when `,' is next input. */ + bool_bitfield skip_last : 1; /* True if last argument omitted. */ const string_pair *quotes; /* NULL for $*, quotes for [EMAIL PROTECTED] */ } u_a; @@ -373,7 +375,8 @@ typedef enum token_data_type token_data_type; void input_init (void); token_type peek_token (void); -token_type next_token (token_data *, int *, struct obstack *, const char *); +token_type next_token (token_data *, int *, struct obstack *, bool, + const char *); void skip_line (const char *); /* push back input */ diff --git a/src/macro.c b/src/macro.c index d686b73..8b85cf6 100644 --- a/src/macro.c +++ b/src/macro.c @@ -216,7 +216,7 @@ expand_input (void) TOKEN_DATA_ORIG_TEXT (&empty_token) = ""; #endif - while ((t = next_token (&td, &line, NULL, NULL)) != TOKEN_EOF) + while ((t = next_token (&td, &line, NULL, false, NULL)) != TOKEN_EOF) expand_token (NULL, t, &td, line, true); for (i = 0; i < stacks_count; i++) @@ -364,7 +364,7 @@ expand_argument (struct obstack *obs, token_data *argp, const char *caller) /* Skip leading white space. */ do { - t = next_token (&td, NULL, obs, caller); + t = next_token (&td, NULL, obs, true, caller); } while (t == TOKEN_SIMPLE && isspace (to_uchar (*TOKEN_DATA_TEXT (&td)))); @@ -455,6 +455,20 @@ expand_argument (struct obstack *obs, token_data *argp, const char *caller) } break; + case TOKEN_ARGV: + assert (paren_level == 0 && TOKEN_DATA_TYPE (argp) == TOKEN_VOID + && obstack_object_size (obs) == 0 + && td.u.u_c.chain == td.u.u_c.end + && td.u.u_c.chain->type == CHAIN_ARGV); + TOKEN_DATA_TYPE (argp) = TOKEN_COMP; + argp->u.u_c.chain = argp->u.u_c.end = td.u.u_c.chain; + t = next_token (&td, NULL, NULL, false, caller); + if (argp->u.u_c.chain->u.u_a.skip_last) + assert (t == TOKEN_COMMA); + else + assert (t == TOKEN_COMMA || t == TOKEN_CLOSE); + return t == TOKEN_COMMA; + default: assert (!"expand_argument"); abort (); @@ -462,7 +476,7 @@ expand_argument (struct obstack *obs, token_data *argp, const char *caller) if (TOKEN_DATA_TYPE (argp) != TOKEN_VOID || obstack_object_size (obs)) first = false; - t = next_token (&td, NULL, obs, caller); + t = next_token (&td, NULL, obs, first, caller); } } @@ -496,7 +510,8 @@ collect_arguments (symbol *sym, struct obstack *arguments, if (peek_token () == TOKEN_OPEN) { - next_token (&td, NULL, NULL, SYMBOL_NAME (sym)); /* gobble parenthesis */ + /* gobble parenthesis */ + next_token (&td, NULL, NULL, false, SYMBOL_NAME (sym)); do { tdp = (token_data *) obstack_alloc (arguments, sizeof *tdp); @@ -519,12 +534,22 @@ collect_arguments (symbol *sym, struct obstack *arguments, && TOKEN_DATA_QUOTE_AGE (tdp) != args.quote_age) args.quote_age = 0; else if (TOKEN_DATA_TYPE (tdp) == TOKEN_COMP) - args.has_ref = true; + { + args.has_ref = true; + if (tdp->u.u_c.chain->type == CHAIN_ARGV) + { + args.argc += (tdp->u.u_c.chain->u.u_a.argv->argc + - tdp->u.u_c.chain->u.u_a.index + - tdp->u.u_c.chain->u.u_a.skip_last - 1); + args.wrapper = true; + } + } } while (more_args); } argv = (macro_arguments *) obstack_finish (argv_stack); argv->argc = args.argc; + argv->wrapper = args.wrapper; argv->has_ref = args.has_ref; if (args.quote_age != quote_age ()) argv->quote_age = 0; @@ -734,9 +759,20 @@ arg_adjust_refcount (macro_arguments *argv, bool increase) chain = argv->array[i]->u.u_c.chain; while (chain) { - assert (chain->type == CHAIN_STR); - if (chain->u.u_s.level >= 0) - adjust_refcount (chain->u.u_s.level, increase); + switch (chain->type) + { + case CHAIN_STR: + if (chain->u.u_s.level >= 0) + adjust_refcount (chain->u.u_s.level, increase); + break; + case CHAIN_ARGV: + assert (chain->u.u_a.argv->inuse); + arg_adjust_refcount (chain->u.u_a.argv, increase); + break; + default: + assert (!"arg_adjust_refcount"); + abort (); + } chain = chain->next; } } @@ -766,12 +802,14 @@ arg_token (macro_arguments *argv, unsigned int index, int *level) for (i = 0; i < argv->arraylen; i++) { token = argv->array[i]; - if (TOKEN_DATA_TYPE (token) == TOKEN_COMP) + if (TOKEN_DATA_TYPE (token) == TOKEN_COMP + && token->u.u_c.chain->type == CHAIN_ARGV) { token_chain *chain = token->u.u_c.chain; /* TODO - for now we support only a single-length $@ chain. */ - assert (!chain->next && chain->type == CHAIN_ARGV); - if (index < chain->u.u_a.argv->argc - (chain->u.u_a.index - 1)) + assert (!chain->next); + if (index <= (chain->u.u_a.argv->argc - chain->u.u_a.index + - chain->u.u_a.skip_last)) { token = arg_token (chain->u.u_a.argv, chain->u.u_a.index - 1 + index, level); @@ -780,7 +818,8 @@ arg_token (macro_arguments *argv, unsigned int index, int *level) token = &empty_token; break; } - index -= chain->u.u_a.argv->argc - chain->u.u_a.index; + index -= (chain->u.u_a.argv->argc - chain->u.u_a.index + - chain->u.u_a.skip_last); } else if (--index == 0) break; @@ -793,18 +832,24 @@ arg_token (macro_arguments *argv, unsigned int index, int *level) static void arg_mark (macro_arguments *argv) { + unsigned int i; + token_chain *chain; + if (argv->inuse) return; argv->inuse = true; if (argv->wrapper) - { - /* TODO for now we support only a single-length $@ chain. */ - assert (argv->arraylen == 1 - && TOKEN_DATA_TYPE (argv->array[0]) == TOKEN_COMP - && !argv->array[0]->u.u_c.chain->next - && argv->array[0]->u.u_c.chain->type == CHAIN_ARGV); - argv->array[0]->u.u_c.chain->u.u_a.argv->inuse = true; - } + for (i = 0; i < argv->arraylen; i++) + if (TOKEN_DATA_TYPE (argv->array[i]) == TOKEN_COMP) + { + chain = argv->array[i]->u.u_c.chain; + while (chain) + { + if (chain->type == CHAIN_ARGV && !chain->u.u_a.argv->inuse) + arg_mark (chain->u.u_a.argv); + chain = chain->next; + } + } } /* Given ARGV, return how many arguments it refers to. */ @@ -854,14 +899,24 @@ arg_text (macro_arguments *argv, unsigned int index) case TOKEN_TEXT: return TOKEN_DATA_TEXT (token); case TOKEN_COMP: - /* TODO - concatenate multiple arguments? For now, we assume - all elements are text. */ + /* TODO - concatenate functions. */ chain = token->u.u_c.chain; obs = arg_scratch (); while (chain) { - assert (chain->type == CHAIN_STR); - obstack_grow (obs, chain->u.u_s.str, chain->u.u_s.len); + switch (chain->type) + { + case CHAIN_STR: + obstack_grow (obs, chain->u.u_s.str, chain->u.u_s.len); + break; + case CHAIN_ARGV: + arg_print (obs, chain->u.u_a.argv, chain->u.u_a.index, + chain->u.u_a.quotes, NULL); + break; + default: + assert (!"arg_text"); + abort (); + } chain = chain->next; } obstack_1grow (obs, '\0'); @@ -1122,13 +1177,13 @@ make_argv_ref_token (token_data *token, struct obstack *obs, int level, token_chain *chain; assert (obstack_object_size (obs) == 0); - if (argv->wrapper) + if (argv->wrapper && argv->arraylen == 1) { /* TODO for now we support only a single-length $@ chain. */ - assert (argv->arraylen == 1 - && TOKEN_DATA_TYPE (argv->array[0]) == TOKEN_COMP); + assert (TOKEN_DATA_TYPE (argv->array[0]) == TOKEN_COMP); chain = argv->array[0]->u.u_c.chain; - assert (!chain->next && chain->type == CHAIN_ARGV); + assert (!chain->next && chain->type == CHAIN_ARGV + && !chain->u.u_a.skip_last); argv = chain->u.u_a.argv; index += chain->u.u_a.index - 1; } @@ -1145,6 +1200,7 @@ make_argv_ref_token (token_data *token, struct obstack *obs, int level, chain->u.u_a.index = index; chain->u.u_a.flatten = flatten; chain->u.u_a.comma = false; + chain->u.u_a.skip_last = false; if (quotes) { /* Clone the quotes into the obstack, since a subsequent diff --git a/src/symtab.c b/src/symtab.c index 277a79f..dac49d7 100644 --- a/src/symtab.c +++ b/src/symtab.c @@ -350,7 +350,7 @@ symtab_debug (void) int delete; static int i; - while (next_token (&td, NULL, NULL, "<debug>") == TOKEN_WORD) + while (next_token (&td, NULL, NULL, false, "<debug>") == TOKEN_WORD) { text = TOKEN_DATA_TEXT (&td); if (*text == '_') hooks/post-receive -- GNU M4 source repository
