diff options
author | Eric Blake <ebb9@byu.net> | 2007-12-06 13:33:40 -0700 |
---|---|---|
committer | Eric Blake <ebb9@byu.net> | 2008-04-14 22:05:40 -0600 |
commit | 6b67f52903f191b8e0b9c295182a8e6504fb4f96 (patch) | |
tree | 84e6f9b82e7dc8419cff128c7f886bf37e2603c8 | |
parent | fb04a26fa6ac38cbe5517372b1a984af0c851876 (diff) | |
download | m4-6b67f52903f191b8e0b9c295182a8e6504fb4f96.tar.gz |
Stage 21: $@ concatenates builtins, m4wrap takes builtins.
* src/m4.h (append_macro): New prototype.
(push_macro, push_wrapup_init, arg_print, func_print): Alter
prototypes.
* src/input.c (INPUT_MACRO): Delete, covered by INPUT_CHAIN.
(INPUT_EOF): New input block type, for efficiency.
(struct input_block): Remove u.func member.
(input_eof): New input sentinel.
(append_macro): New function.
(push_macro, push_wrapup_init): Add parameter.
(push_token): Support builtin tokens.
(init_macro_token): Always use chain for builtin tokens.
(push_string_init, pop_input, pop_wrapup, input_print)
(peek_input, next_char, next_char_1, input_init): Adjust callers.
* src/macro.c (arg_equal, wrap_args): Handle builtin tokens.
(arg_print): Add parameter.
(collect_arguments, arg_type, arg_text): Adjust callers.
* src/builtin.c (m4_m4wrap): Handle builtin tokens.
(func_print): Add parameter.
(m4_defn): Allow pushing builtin alongside other text.
(m4_errprint): Adjust caller.
* src/debug.c (trace_pre): Likewise.
* doc/m4.texinfo (Defn, Ifelse, Debug Levels): Update tests to new
behavior.
(M4wrap): New test.
(cherry picked from commit 32d4bf4d447e0e252c809897b795b57b3bfd74de)
Signed-off-by: Eric Blake <ebb9@byu.net>
-rw-r--r-- | .cvsignore | 2 | ||||
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | ChangeLog | 35 | ||||
-rw-r--r-- | doc/m4.texinfo | 84 | ||||
-rw-r--r-- | src/builtin.c | 26 | ||||
-rw-r--r-- | src/debug.c | 2 | ||||
-rw-r--r-- | src/input.c | 203 | ||||
-rw-r--r-- | src/m4.h | 12 | ||||
-rw-r--r-- | src/macro.c | 86 |
9 files changed, 253 insertions, 199 deletions
@@ -17,6 +17,7 @@ configure.lineno COPYING depcomp gendocs.sh +GNUmakefile gnupload INSTALL install-sh @@ -30,4 +31,3 @@ stamp-h stamp-h1 stamp-h.in tests -GNUmakefile @@ -21,6 +21,7 @@ COPYING CVS depcomp gendocs.sh +GNUmakefile gnupload INSTALL install-sh @@ -34,4 +35,3 @@ stamp-h stamp-h1 stamp-h.in tests -GNUmakefile @@ -1,3 +1,38 @@ +2008-04-14 Eric Blake <ebb9@byu.net> + + Stage 21: $@ concatenates builtins, m4wrap takes builtins. + Create a new input block type, which always fails with CHAR_EOF, + so that remaining input routines no longer have to check for NULL + input block. Improve arg_print to handle builtin tokens when + printing to a known chain, rather than always flattening builtins, + allowing m4wrap and $@ references to handle embedded builtins. + Memory impact: none. + Speed impact: noticeable improvement, from fewer conditionals. + * src/m4.h (append_macro): New prototype. + (push_macro, push_wrapup_init, arg_print, func_print): Alter + prototypes. + * src/input.c (INPUT_MACRO): Delete, covered by INPUT_CHAIN. + (INPUT_EOF): New input block type, for efficiency. + (struct input_block): Remove u.func member. + (input_eof): New input sentinel. + (append_macro): New function. + (push_macro, push_wrapup_init): Add parameter. + (push_token): Support builtin tokens. + (init_macro_token): Always use chain for builtin tokens. + (push_string_init, pop_input, pop_wrapup, input_print) + (peek_input, next_char, next_char_1, input_init): Adjust callers. + * src/macro.c (arg_equal, wrap_args): Handle builtin tokens. + (arg_print): Add parameter. + (collect_arguments, arg_type, arg_text): Adjust callers. + * src/builtin.c (m4_m4wrap): Handle builtin tokens. + (func_print): Add parameter. + (m4_defn): Allow pushing builtin alongside other text. + (m4_errprint): Adjust caller. + * src/debug.c (trace_pre): Likewise. + * doc/m4.texinfo (Defn, Ifelse, Debug Levels): Update tests to new + behavior. + (M4wrap): New test. + 2008-04-11 Eric Blake <ebb9@byu.net> Ensure --program-prefix doesn't regress. diff --git a/doc/m4.texinfo b/doc/m4.texinfo index 4a523c6c..52fc77b7 100644 --- a/doc/m4.texinfo +++ b/doc/m4.texinfo @@ -2281,10 +2281,11 @@ bar @result{}0 @end example -Also note that @code{defn} with multiple arguments can only join text -macros, not builtins. Likewise, when collecting macro arguments, a -builtin token is preserved only when it occurs in isolation. A future -version of @acronym{GNU} M4 may lift these restrictions. +Also note that as of M4 1.6, @code{defn} with multiple arguments can +join text with builtin tokens. However, when collecting macro +arguments, a builtin token is preserved only when it occurs in +isolation. A future version of @acronym{GNU} M4 may lift this +restriction. @example $ @kbd{m4 -d} @@ -2293,13 +2294,12 @@ define(`a', `A')define(`AA', `b') traceon(`defn', `define') @result{} defn(`a', `divnum', `a') -@error{}m4:stdin:3: Warning: defn: cannot concatenate builtin `divnum' -@error{}m4trace: -1- defn(`a', `divnum', `a') -> ``A'`A'' +@error{}m4trace: -1- defn(`a', `divnum', `a') -> ``A'<divnum>`A'' @result{}AA define(`mydivnum', defn(`divnum', `divnum'))mydivnum -@error{}m4:stdin:4: Warning: defn: cannot concatenate builtin `divnum' -@error{}m4:stdin:4: Warning: defn: cannot concatenate builtin `divnum' -@error{}m4trace: -2- defn(`divnum', `divnum') +@error{}m4trace: -2- defn(`divnum', `divnum') -> `<divnum><divnum>' +@error{}m4:stdin:4: Warning: define: cannot concatenate builtin `divnum' +@error{}m4:stdin:4: Warning: define: cannot concatenate builtin `divnum' @error{}m4trace: -1- define(`mydivnum', `') @result{} traceoff(`defn', `define') @@ -2317,10 +2317,10 @@ define(`mydivnum', `a'defn(`divnum'))mydivnum define(`q', ``$@@'') @result{} define(`foo', q(`a', defn(`divnum')))foo -@error{}m4:stdin:10: Warning: define: cannot quote builtin -@result{}a, +@error{}m4:stdin:10: Warning: define: cannot concatenate builtins +@result{}foo ifdef(`foo', `yes', `no') -@result{}yes +@result{}no @end example @node Pushdef @@ -2860,8 +2860,8 @@ ifelse(`-01234567890123456789', `-'e(long)`-', `yes', `no') @result{}no @end example -@comment It would be nice to pass builtin tokens through m4wrap, as well -@comment as allowing concatenation of builtins in ifelse and user macros. +@comment It would be nice to allow concatenation of builtins without +@comment using $@ handling. @example define(`e', `$@@')define(`q', ``$@@'')define(`u', `$*') @result{} @@ -2881,33 +2881,25 @@ cmp(`q(defn(`defn'))', `q(`<defn>')')-fixme cmp(`q(defn(`defn'))', ``'')-fixme @error{}m4:stdin:7: Warning: ifelse: cannot quote builtin @result{}no-fixme -cmp(`q(`1', `2', defn(`defn'))', `q(`1', `2', defn(`d'))')-fixme -@error{}m4:stdin:8: Warning: ifelse: cannot quote builtin -@error{}m4:stdin:8: Warning: ifelse: cannot quote builtin -@result{}yes-fixme -cmp(`q(`1', `2', defn(`defn'))', `q(`1', `2', `<defn>')')-fixme -@error{}m4:stdin:9: Warning: ifelse: cannot quote builtin -@result{}no-fixme -cmp(`q(`1', `2', defn(`defn'))', ```1',`2',<defn>'')-fixme -@error{}m4:stdin:10: Warning: ifelse: cannot quote builtin -@result{}no-fixme -cmp(`q(`1', `2', defn(`defn'))', ```1',`2',`''')-fixme -@error{}m4:stdin:11: Warning: ifelse: cannot quote builtin -@result{}yes-fixme +cmp(`q(`1', `2', defn(`defn'))', `q(`1', `2', defn(`d'))') +@result{}yes +cmp(`q(`1', `2', defn(`defn'))', `q(`1', `2', `<defn>')') +@result{}no +cmp(`q(`1', `2', defn(`defn'))', ```1',`2',<defn>'') +@result{}no +cmp(`q(`1', `2', defn(`defn'))', ```1',`2',`''') +@result{}no define(`cat', `$1`'ifelse(`$#', `1', `', `$0(shift($@@))')') @result{} -cat(`define(`foo',', defn(`divnum'), `)foo')-fixme -@error{}m4:stdin:13: Warning: ifelse: cannot quote builtin -@result{}-fixme -cat(e(`define(`bar',', defn(`divnum'), `)bar'))-fixme -@error{}m4:stdin:14: Warning: ifelse: cannot quote builtin -@result{}-fixme -m4wrap(`u('q(`cat(`define(`baz','', defn(`divnum'), ``)baz')')`)-fixme +cat(`define(`foo',', defn(`divnum'), `)foo') +@result{}0 +cat(e(`define(`bar',', defn(`divnum'), `)bar')) +@result{}0 +m4wrap(`u('q(`cat(`define(`baz','', defn(`divnum'), ``)baz')')`) ') -@error{}m4:stdin:15: Warning: m4wrap: cannot quote builtin @result{} ^D -@result{}-fixme +@result{}0 @end example @end ignore @@ -3811,7 +3803,7 @@ echo(`1', `long string') @error{}m4trace: -1- echo(`1', `long s...') -> ``1',`l...' @result{}1,long string indir(`echo', defn(`changequote')) -@error{}m4trace: -2- defn(`change...') +@error{}m4trace: -2- defn(`change...') -> `<changequote>' @error{}m4trace: -1- indir(`echo', <changequote>) -> ``<changequote>'' @result{} @end example @@ -4723,6 +4715,24 @@ m4wrap(`m4wrap(`)')len(abc') @error{}m4:stdin:1: len: end of file in argument list @end example +As of M4 1.6, @code{m4wrap} transparently handles builtin tokens +generated by @code{defn} (@pxref{Defn}). However, for portability, it +is better to defer the evaluation of @code{defn} along with the rest of +the wrapped text, as is done for @code{foo} in the example below, rather +than computing the builtin token up front, as is done for @code{bar}. + +@example +m4wrap(`define(`foo', defn(`divnum'))foo +') +@result{} +m4wrap(`define(`bar', ')m4wrap(defn(`divnum'))m4wrap(`)bar +') +@result{} +^D +@result{}0 +@result{}0 +@end example + @node File Inclusion @chapter File inclusion diff --git a/src/builtin.c b/src/builtin.c index e9856a8b..07d2ce09 100644 --- a/src/builtin.c +++ b/src/builtin.c @@ -123,7 +123,7 @@ builtin_tab[] = { "indir", true, true, true, m4_indir }, { "len", false, false, true, m4_len }, { "m4exit", false, false, false, m4_m4exit }, - { "m4wrap", false, false, true, m4_m4wrap }, + { "m4wrap", false, true, true, m4_m4wrap }, { "maketemp", false, false, true, m4_maketemp }, { "mkstemp", false, false, true, m4_mkstemp }, { "patsubst", true, false, true, m4_patsubst }, @@ -204,19 +204,25 @@ find_builtin_by_name (const char *name) /*------------------------------------------------------------------. | Print a representation of FUNC to OBS. If FLATTEN, output QUOTES | -| around an empty string instead. | +| around an empty string instead; else if CHAIN, append the builtin | +| to the chain; otherwise print the name of FUNC. | `------------------------------------------------------------------*/ void func_print (struct obstack *obs, const builtin *func, bool flatten, - const string_pair *quotes) + token_chain **chain, const string_pair *quotes) { assert (func); - if (flatten && quotes) + if (flatten) { - obstack_grow (obs, quotes->str1, quotes->len1); - obstack_grow (obs, quotes->str2, quotes->len2); + if (quotes) + { + obstack_grow (obs, quotes->str1, quotes->len1); + obstack_grow (obs, quotes->str2, quotes->len2); + } } - else if (!flatten) + else if (chain) + append_macro (obs, func->func, NULL, chain); + else { obstack_1grow (obs, '<'); obstack_grow (obs, func->name, strlen (func->name)); @@ -1022,10 +1028,8 @@ m4_defn (struct obstack *obs, int argc, macro_arguments *argv) m4_warn (0, me, _("builtin `%s' requested by frozen file not found"), ARG (i)); - else if (argc != 2) - m4_warn (0, me, _("cannot concatenate builtin `%s'"), ARG (i)); else - push_macro (b); + push_macro (obs, b); break; default: @@ -1548,7 +1552,7 @@ m4_errprint (struct obstack *obs, int argc, macro_arguments *argv) if (bad_argc (ARG (0), argc, 1, -1)) return; - arg_print (obs, argv, 1, NULL, true, " ", NULL, false); + arg_print (obs, argv, 1, NULL, true, NULL, " ", NULL, false); debug_flush_files (); len = obstack_object_size (obs); /* The close_stdin module makes it safe to skip checking the return diff --git a/src/debug.c b/src/debug.c index 46e1306f..fde6c499 100644 --- a/src/debug.c +++ b/src/debug.c @@ -368,7 +368,7 @@ trace_pre (const char *name, int id, macro_arguments *argv) trace_format ("("); arg_print (&trace, argv, 1, (debug_level & DEBUG_TRACE_QUOTE) ? &curr_quote : NULL, - false, ", ", &len, true); + false, NULL, ", ", &len, true); trace_format (")"); } diff --git a/src/input.c b/src/input.c index 86db7041..3a913b71 100644 --- a/src/input.c +++ b/src/input.c @@ -72,10 +72,10 @@ /* Type of an input block. */ enum input_type { - INPUT_STRING, /* String resulting from macro expansion. */ - INPUT_FILE, /* File from command line or include. */ - INPUT_MACRO, /* Builtin resulting from defn. */ - INPUT_CHAIN /* FIFO chain of separate strings and $@ refs. */ + INPUT_STRING, /* String resulting from macro expansion. */ + INPUT_FILE, /* File from command line or include. */ + INPUT_CHAIN, /* FIFO chain of separate strings, builtins, and $@ refs. */ + INPUT_EOF /* Placeholder at bottom of input stack. */ }; typedef enum input_type input_type; @@ -103,7 +103,6 @@ struct input_block bool_bitfield advance : 1; /* Track previous start_of_input_line. */ } u_f; /* INPUT_FILE */ - builtin_func *func; /* INPUT_MACRO */ struct { token_chain *chain; /* Current link in chain. */ @@ -136,15 +135,19 @@ static struct obstack *current_input; /* Bottom of token_stack, for obstack_free. */ static void *token_bottom; -/* Pointer to top of current_input. */ +/* Pointer to top of current_input, never NULL. */ static input_block *isp; -/* Pointer to top of wrapup_stack. */ +/* Pointer to top of wrapup_stack, never NULL. */ static input_block *wsp; -/* Aux. for handling split push_string (). */ +/* Auxiliary for handling split push_string (), NULL if not pushing + text for rescanning. */ static input_block *next; +/* Marker at the end of the input stack. */ +static input_block input_eof = { NULL, INPUT_EOF, "", 0 }; + /* Flag for next_char () to increment current_line. */ static bool start_of_input_line; @@ -265,33 +268,50 @@ push_file (FILE *fp, const char *title, bool close) isp = i; } -/*-----------------------------------------------------------------. -| push_macro () pushes the builtin macro FUNC on the input stack. | -| If next is non-NULL, this push invalidates a call to | -| push_string_init (), whose storage is consequently released. | -`-----------------------------------------------------------------*/ - +/*------------------------------------------------------------------. +| Given an obstack OBS, capture any unfinished text as a link, then | +| append the builtin FUNC as the next link in the chain that starts | +| at *START and ends at *END. START may be NULL if *END is | +| non-NULL. | +`------------------------------------------------------------------*/ void -push_macro (builtin_func *func) +append_macro (struct obstack *obs, builtin_func *func, token_chain **start, + token_chain **end) { - input_block *i; - - if (next != NULL) - { - obstack_free (current_input, next); - next = NULL; - } + token_chain *chain; assert (func); - i = (input_block *) obstack_alloc (current_input, sizeof *i); - i->type = INPUT_MACRO; - i->file = current_file; - i->line = current_line; - input_change = true; + make_text_link (obs, start, end); + chain = (token_chain *) obstack_alloc (obs, sizeof *chain); + if (*end) + (*end)->next = chain; + else + *start = chain; + *end = chain; + chain->next = NULL; + chain->type = CHAIN_FUNC; + chain->quote_age = 0; + chain->u.func = func; +} - i->u.func = func; - i->prev = isp; - isp = i; +/*------------------------------------------------------------------. +| push_macro () pushes the builtin FUNC onto the obstack OBS, which | +| is either the input or wrapup stack. | +`------------------------------------------------------------------*/ + +void +push_macro (struct obstack *obs, builtin_func *func) +{ + input_block *block = (obs == current_input ? next : wsp); + assert (block); + if (block->type == INPUT_STRING) + { + block->type = INPUT_CHAIN; + block->u.u_c.chain = block->u.u_c.end = NULL; + } + else + assert (block->type == INPUT_CHAIN); + append_macro (obs, func, &block->u.u_c.chain, &block->u.u_c.end); } /*--------------------------------------------------------------. @@ -304,7 +324,7 @@ push_string_init (void) { /* Free any memory occupied by completely parsed strings. */ assert (next == NULL); - while (isp && pop_input (false)); + while (pop_input (false)); /* Reserve the next location on the obstack. */ next = (input_block *) obstack_alloc (current_input, sizeof *next); @@ -361,7 +381,18 @@ push_token (token_data *token, int level, bool inuse) return false; } } - else if (TOKEN_DATA_TYPE (token) != TOKEN_FUNC) + else if (TOKEN_DATA_TYPE (token) == TOKEN_FUNC) + { + if (next->type == INPUT_STRING) + { + next->type = INPUT_CHAIN; + next->u.u_c.chain = next->u.u_c.end = NULL; + } + append_macro (current_input, TOKEN_DATA_FUNC (token), &next->u.u_c.chain, + &next->u.u_c.end); + return false; + } + else { /* For composite tokens, if argv is already in use, creating additional references for long text segments is more @@ -407,23 +438,15 @@ push_token (token_data *token, int level, bool inuse) adjust_refcount (level, true); inuse = true; } - else if (TOKEN_DATA_TYPE (token) == TOKEN_FUNC) - { - chain = (token_chain *) obstack_alloc (current_input, sizeof *chain); - if (next->u.u_c.end) - next->u.u_c.end->next = chain; - else - next->u.u_c.chain = chain; - next->u.u_c.end = chain; - chain->next = NULL; - chain->type = CHAIN_FUNC; - chain->quote_age = 0; - chain->u.func = TOKEN_DATA_FUNC (token); - } while (src_chain) { - /* TODO support func concatenation. */ - assert (src_chain->type != CHAIN_FUNC); + if (src_chain->type == CHAIN_FUNC) + { + append_macro (current_input, src_chain->u.func, &next->u.u_c.chain, + &next->u.u_c.end); + src_chain = src_chain->next; + continue; + } if (level == -1) { /* Nothing to copy, since link already lives on obstack. */ @@ -526,13 +549,13 @@ push_string_finish (void) `--------------------------------------------------------------*/ struct obstack * -push_wrapup_init (void) +push_wrapup_init (token_chain ***end) { input_block *i; token_chain *chain; assert (obstack_object_size (wrapup_stack) == 0); - if (wsp) + if (wsp != &input_eof) { i = wsp; assert (i->type == INPUT_CHAIN && i->u.u_c.end @@ -559,6 +582,7 @@ push_wrapup_init (void) chain->quote_age = 0; chain->u.u_l.file = current_file; chain->u.u_l.line = current_line; + *end = &i->u.u_c.end; return wrapup_stack; } @@ -596,12 +620,6 @@ pop_input (bool cleanup) return false; break; - case INPUT_MACRO: - assert (!isp->u.func || !cleanup); - if (isp->u.func) - return false; - break; - case INPUT_CHAIN: chain = isp->u.u_c.chain; assert (!chain || !cleanup); @@ -658,6 +676,9 @@ pop_input (bool cleanup) output_current_line = -1; break; + case INPUT_EOF: + return false; + default: assert (!"pop_input"); abort (); @@ -684,7 +705,7 @@ pop_wrapup (void) obstack_free (current_input, NULL); free (current_input); - if (wsp == NULL) + if (wsp == &input_eof) { /* End of the program. Free all memory even though we are about to exit, since it makes leak detection easier. */ @@ -703,7 +724,7 @@ pop_wrapup (void) obstack_init (wrapup_stack); isp = wsp; - wsp = NULL; + wsp = &input_eof; input_change = true; return true; @@ -730,9 +751,6 @@ input_print (struct obstack *obs, const input_block *input) obstack_grow (obs, input->file, strlen (input->file)); obstack_1grow (obs, '>'); break; - case INPUT_MACRO: - func_print (obs, find_builtin_by_addr (input->u.func), false, NULL); - break; case INPUT_CHAIN: chain = input->u.u_c.chain; while (chain) @@ -746,14 +764,14 @@ input_print (struct obstack *obs, const input_block *input) break; case CHAIN_FUNC: func_print (obs, find_builtin_by_addr (chain->u.func), false, - NULL); + NULL, NULL); break; case CHAIN_ARGV: assert (!chain->u.u_a.comma); if (arg_print (obs, chain->u.u_a.argv, chain->u.u_a.index, quote_cache (NULL, chain->quote_age, chain->u.u_a.quotes), - chain->u.u_a.flatten, NULL, &maxlen, false)) + chain->u.u_a.flatten, NULL, NULL, &maxlen, false)) return; break; default: @@ -789,9 +807,7 @@ peek_input (bool allow_argv) while (1) { - if (block == NULL) - return CHAR_EOF; - + assert (block); switch (block->type) { case INPUT_STRING: @@ -809,11 +825,6 @@ peek_input (bool allow_argv) block->u.u_f.end = true; break; - case INPUT_MACRO: - if (block->u.func) - return CHAR_MACRO; - break; - case INPUT_CHAIN: chain = block->u.u_c.chain; while (chain) @@ -863,6 +874,9 @@ peek_input (bool allow_argv) } break; + case INPUT_EOF: + return CHAR_EOF; + default: assert (!"peek_input"); abort (); @@ -887,7 +901,7 @@ peek_input (bool allow_argv) `-------------------------------------------------------------------*/ #define next_char(AQ, AA) \ - (isp && isp->type == INPUT_STRING && isp->u.u_s.len && !input_change \ + (isp->type == INPUT_STRING && isp->u.u_s.len && !input_change \ ? (isp->u.u_s.len--, to_uchar (*isp->u.u_s.str++)) \ : next_char_1 (AQ, AA)) @@ -899,13 +913,7 @@ next_char_1 (bool allow_quote, bool allow_argv) while (1) { - if (isp == NULL) - { - current_file = ""; - current_line = 0; - return CHAR_EOF; - } - + assert (isp); if (input_change) { current_file = isp->file; @@ -940,11 +948,6 @@ next_char_1 (bool allow_quote, bool allow_argv) } break; - case INPUT_MACRO: - if (isp->u.func) - return CHAR_MACRO; - break; - case INPUT_CHAIN: chain = isp->u.u_c.chain; while (chain) @@ -1013,6 +1016,9 @@ next_char_1 (bool allow_quote, bool allow_argv) } break; + case INPUT_EOF: + return CHAR_EOF; + default: assert (!"next_char_1"); abort (); @@ -1064,28 +1070,15 @@ init_macro_token (token_data *td) { token_chain *chain; - if (isp->type == INPUT_MACRO) + assert (isp->type == INPUT_CHAIN); + chain = isp->u.u_c.chain; + assert (!chain->quote_age && chain->type == CHAIN_FUNC && chain->u.func); + if (td) { - assert (isp->u.func); - if (td) - { - TOKEN_DATA_TYPE (td) = TOKEN_FUNC; - TOKEN_DATA_FUNC (td) = isp->u.func; - } - isp->u.func = NULL; - } - else - { - assert (isp->type == INPUT_CHAIN); - chain = isp->u.u_c.chain; - assert (!chain->quote_age && chain->type == CHAIN_FUNC && chain->u.func); - if (td) - { - TOKEN_DATA_TYPE (td) = TOKEN_FUNC; - TOKEN_DATA_FUNC (td) = chain->u.func; - } - chain->u.func = NULL; + TOKEN_DATA_TYPE (td) = TOKEN_FUNC; + TOKEN_DATA_FUNC (td) = chain->u.func; } + chain->u.func = NULL; } /*-------------------------------------------------------------------. @@ -1282,8 +1275,8 @@ input_init (void) obstack_init (&token_stack); token_bottom = obstack_finish (&token_stack); - isp = NULL; - wsp = NULL; + isp = &input_eof; + wsp = &input_eof; next = NULL; start_of_input_line = false; @@ -393,11 +393,13 @@ void skip_line (const char *); /* push back input */ void make_text_link (struct obstack *, token_chain **, token_chain **); void push_file (FILE *, const char *, bool); -void push_macro (builtin_func *); +void append_macro (struct obstack *, builtin_func *, token_chain **, + token_chain **); +void push_macro (struct obstack *, builtin_func *); struct obstack *push_string_init (void); bool push_token (token_data *, int, bool); const input_block *push_string_finish (void); -struct obstack *push_wrapup_init (void); +struct obstack *push_wrapup_init (token_chain ***); void push_wrapup_finish (void); bool pop_wrapup (void); void input_print (struct obstack *, const input_block *); @@ -509,7 +511,8 @@ size_t arg_len (macro_arguments *, unsigned int); builtin_func *arg_func (macro_arguments *, unsigned int); struct obstack *arg_scratch (void); bool arg_print (struct obstack *, macro_arguments *, unsigned int, - const string_pair *, bool, const char *, size_t *, bool); + const string_pair *, bool, token_chain **, const char *, + size_t *, bool); macro_arguments *make_argv_ref (macro_arguments *, const char *, size_t, bool, bool); void push_arg (struct obstack *, macro_arguments *, unsigned int); @@ -569,7 +572,8 @@ const char *ntoa (int32_t, int); const builtin *find_builtin_by_addr (builtin_func *); const builtin *find_builtin_by_name (const char *); -void func_print (struct obstack *, const builtin *, bool, const string_pair *); +void func_print (struct obstack *, const builtin *, bool, token_chain **, + const string_pair *); /* File: path.c --- path search for include files. */ diff --git a/src/macro.c b/src/macro.c index 6a6a90c9..c4356444 100644 --- a/src/macro.c +++ b/src/macro.c @@ -582,8 +582,7 @@ collect_arguments (symbol *sym, struct obstack *arguments, argv->wrapper = args.wrapper; argv->has_ref = args.has_ref; argv->has_func = args.has_func; - /* TODO allow funcs without crippling quote age. */ - if (args.quote_age != quote_age () || args.has_func) + if (args.quote_age != quote_age ()) argv->quote_age = 0; argv->arraylen = args.arraylen; return argv; @@ -910,8 +909,6 @@ arg_type (macro_arguments *argv, unsigned int index) type = TOKEN_TEXT; if (type != TOKEN_TEXT) assert (argv->has_func); - /* TODO support TOKEN_COMP meaning concatenation of builtins. */ - assert (type != TOKEN_COMP); return type; } @@ -936,7 +933,6 @@ arg_text (macro_arguments *argv, unsigned int index) case TOKEN_TEXT: return TOKEN_DATA_TEXT (token); case TOKEN_COMP: - /* TODO - concatenate functions. */ chain = token->u.u_c.chain; obs = arg_scratch (); while (chain) @@ -952,7 +948,7 @@ arg_text (macro_arguments *argv, unsigned int index) quote_cache (NULL, chain->quote_age, chain->u.u_a.quotes), argv->flatten || chain->u.u_a.flatten, NULL, NULL, - false); + NULL, false); break; default: assert (!"arg_text"); @@ -983,6 +979,7 @@ arg_equal (macro_arguments *argv, unsigned int indexa, unsigned int indexb) token_chain tmpb; token_chain *ca = &tmpa; token_chain *cb = &tmpb; + token_chain *chain; struct obstack *obs = arg_scratch (); /* Quick tests. */ @@ -1041,30 +1038,34 @@ arg_equal (macro_arguments *argv, unsigned int indexa, unsigned int indexb) { if (ca->type == CHAIN_ARGV) { - tmpa.next = ca->next; + tmpa.next = NULL; tmpa.type = CHAIN_STR; - /* TODO support $@ with funcs. */ - assert (!ca->u.u_a.has_func || argv->flatten || ca->u.u_a.flatten); + tmpa.u.u_s.str = NULL; + tmpa.u.u_s.len = 0; + chain = &tmpa; arg_print (obs, ca->u.u_a.argv, ca->u.u_a.index, quote_cache (NULL, ca->quote_age, ca->u.u_a.quotes), - argv->flatten || ca->u.u_a.flatten, NULL, NULL, false); - tmpa.u.u_s.len = obstack_object_size (obs); - tmpa.u.u_s.str = (char *) obstack_finish (obs); - ca = &tmpa; + argv->flatten || ca->u.u_a.flatten, &chain, NULL, NULL, + false); + assert (obstack_object_size (obs) == 0 && chain != &tmpa); + chain->next = ca->next; + ca = tmpa.next; continue; } if (cb->type == CHAIN_ARGV) { - tmpb.next = cb->next; + tmpb.next = NULL; tmpb.type = CHAIN_STR; - /* TODO support $@ with funcs. */ - assert (!cb->u.u_a.has_func || argv->flatten || cb->u.u_a.flatten); + tmpb.u.u_s.str = NULL; + tmpb.u.u_s.len = 0; + chain = &tmpb; arg_print (obs, cb->u.u_a.argv, cb->u.u_a.index, quote_cache (NULL, cb->quote_age, cb->u.u_a.quotes), - argv->flatten || cb->u.u_a.flatten, NULL, NULL, false); - tmpb.u.u_s.len = obstack_object_size (obs); - tmpb.u.u_s.str = (char *) obstack_finish (obs); - cb = &tmpb; + argv->flatten || cb->u.u_a.flatten, &chain, NULL, NULL, + false); + assert (obstack_object_size (obs) == 0 && chain != &tmpb); + chain->next = cb->next; + cb = tmpb.next; continue; } if (ca->type == CHAIN_FUNC) @@ -1223,18 +1224,21 @@ arg_scratch (void) /* Dump a representation of ARGV to the obstack OBS, starting with argument INDEX. If QUOTES is non-NULL, each argument is displayed - with those quotes. If FLATTEN, builtins are ignored. Separate - arguments with SEP, which defaults to a comma. If MAX_LEN is - non-NULL, truncate the output after *MAX_LEN bytes are output and - return true; otherwise, return false, and reduce *MAX_LEN by the - number of bytes output. If QUOTE_EACH, the truncation length is - reset for each argument, quotes do not count against length, and - all arguments are printed; otherwise, quotes count against the - length and trailing arguments may be discarded. */ + with those quotes. If FLATTEN, builtins are converted to empty + quotes; if CHAINP, *CHAINP is updated with macro tokens; otherwise, + builtins are represented by their name. Separate arguments with + SEP, which defaults to a comma. If MAX_LEN is non-NULL, truncate + the output after *MAX_LEN bytes are output and return true; + otherwise, return false, and reduce *MAX_LEN by the number of bytes + output. If QUOTE_EACH, the truncation length is reset for each + argument, quotes do not count against length, and all arguments are + printed; otherwise, quotes count against the length and trailing + arguments may be discarded. MAX_LEN and CHAINP may not both be + specified. */ bool arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index, - const string_pair *quotes, bool flatten, const char *sep, - size_t *max_len, bool quote_each) + const string_pair *quotes, bool flatten, token_chain **chainp, + const char *sep, size_t *max_len, bool quote_each) { size_t len = max_len ? *max_len : INT_MAX; unsigned int i; @@ -1245,6 +1249,8 @@ arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index, size_t sep_len; size_t *plen = quote_each ? NULL : &len; + if (chainp) + assert (!max_len && *chainp); if (!sep) sep = ","; sep_len = strlen (sep); @@ -1287,13 +1293,13 @@ arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index, break; case CHAIN_FUNC: func_print (obs, find_builtin_by_addr (chain->u.func), - flatten, quotes); + flatten, chainp, quotes); break; case CHAIN_ARGV: if (arg_print (obs, chain->u.u_a.argv, chain->u.u_a.index, quote_cache (NULL, chain->quote_age, chain->u.u_a.quotes), - flatten, NULL, &len, false)) + flatten, chainp, NULL, &len, false)) done = true; break; default: @@ -1310,7 +1316,7 @@ arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index, break; case TOKEN_FUNC: func_print (obs, find_builtin_by_addr (TOKEN_DATA_FUNC (token)), - flatten, quotes); + flatten, chainp, quotes); break; default: assert (!"arg_print"); @@ -1319,6 +1325,8 @@ arg_print (struct obstack *obs, macro_arguments *argv, unsigned int index, } if (max_len) *max_len = len; + else if (chainp) + make_text_link (obs, NULL, chainp); return false; } @@ -1524,11 +1532,12 @@ wrap_args (macro_arguments *argv) struct obstack *obs; token_data *token; token_chain *chain; + token_chain **end; if ((argv->argc == 2 || no_gnu_extensions) && arg_empty (argv, 1)) return; - obs = push_wrapup_init (); + obs = push_wrapup_init (&end); for (i = 1; i < (no_gnu_extensions ? 2 : argv->argc); i++) { if (i != 1) @@ -1540,8 +1549,8 @@ wrap_args (macro_arguments *argv) obstack_grow (obs, TOKEN_DATA_TEXT (token), TOKEN_DATA_LEN (token)); break; case TOKEN_FUNC: - /* TODO allow builtins through m4wrap. */ - assert (false); + append_macro (obs, TOKEN_DATA_FUNC (token), NULL, end); + break; case TOKEN_COMP: chain = token->u.u_c.chain; while (chain) @@ -1552,14 +1561,13 @@ wrap_args (macro_arguments *argv) obstack_grow (obs, chain->u.u_s.str, chain->u.u_s.len); break; case CHAIN_FUNC: - /* TODO allow builtins through m4wrap. */ - assert (false); + append_macro (obs, chain->u.func, NULL, end); break; case CHAIN_ARGV: arg_print (obs, chain->u.u_a.argv, chain->u.u_a.index, quote_cache (NULL, chain->quote_age, chain->u.u_a.quotes), - chain->u.u_a.flatten, NULL, NULL, false); + chain->u.u_a.flatten, end, NULL, NULL, false); break; default: assert (!"wrap_args"); |