From: Eric Blake Date: Tue, 16 Oct 2007 18:00:00 +0000 (-0600) Subject: Fix m4_map, and add some more utility macros. X-Git-Tag: v2.62~212^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=33239cbd38632ce58d1a2ba3944c665ca9374287;p=thirdparty%2Fautoconf.git Fix m4_map, and add some more utility macros. * lib/m4sugar/m4sugar.m4 (m4_apply, m4_count, m4_dquote_elt) (m4_echo, m4_make_list): New documented macros. (_m4_quote, _m4_shift2): New helper macros. (m4_map): Change semantics to allow calling macro without arguments. (m4_map_sep): Likewise. Also change semantics to quote separator, to match m4_join and m4_append. (m4_version_unletter): Fix use of m4_map. * doc/autoconf.texi (Evaluation Macros): Document m4_apply, m4_count, m4_dquote_elt, m4_echo, m4_make_list. (Text processing Macros): Mention m4_dquote as a faster alternative to joining with commas. (Looping constructs): Document m4_map, m4_map_sep. * NEWS: Mention new macros. Signed-off-by: Eric Blake --- diff --git a/ChangeLog b/ChangeLog index 930f3baf..f5b770d9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,21 @@ 2007-10-16 Eric Blake + Fix m4_map, and add some more utility macros. + * lib/m4sugar/m4sugar.m4 (m4_apply, m4_count, m4_dquote_elt) + (m4_echo, m4_make_list): New documented macros. + (_m4_quote, _m4_shift2): New helper macros. + (m4_map): Change semantics to allow calling macro without + arguments. + (m4_map_sep): Likewise. Also change semantics to quote separator, + to match m4_join and m4_append. + (m4_version_unletter): Fix use of m4_map. + * doc/autoconf.texi (Evaluation Macros): Document m4_apply, + m4_count, m4_dquote_elt, m4_echo, m4_make_list. + (Text processing Macros): Mention m4_dquote as a faster + alternative to joining with commas. + (Looping constructs): Document m4_map, m4_map_sep. + * NEWS: Mention new macros. + A few more m4sugar improvements, to benefit libtool. * lib/m4sugar/m4sugar.m4 (m4_bpatsubsts, _m4_shiftn): Reduce size of expansion by avoiding extra uses of $@. diff --git a/NEWS b/NEWS index e4239065..25095f20 100644 --- a/NEWS +++ b/NEWS @@ -93,8 +93,8 @@ GNU Autoconf NEWS - User visible changes. should be analyzed to make sure they will still work with the new documented behavior. - m4_cmp m4_list_cmp m4_join m4_sign m4_text_box m4_text_wrap - m4_version_compare + m4_cmp m4_list_cmp m4_join m4_map m4_map_sep m4_sign + m4_text_box m4_text_wrap m4_version_compare - Packages using the undocumented m4sugar macro m4_PACKAGE_VERSION should consider using the new AC_AUTOCONF_VERSION instead. @@ -120,8 +120,9 @@ GNU Autoconf NEWS - User visible changes. be used to take action depending on whether anything was appended. ** The following m4sugar macros are new: - m4_cond m4_expand m4_ignore m4_max m4_min m4_newline - m4_shift2 m4_shift3 m4_unquote + m4_apply m4_cond m4_count m4_dquote_elt m4_echo m4_expand + m4_ignore m4_make_list m4_max m4_min m4_newline m4_shift2 + m4_shift3 m4_unquote ** Warnings are now generated by default when an installer invokes 'configure' with an unknown --enable-* or --with-* option. diff --git a/doc/autoconf.texi b/doc/autoconf.texi index 19ad1102..369b5556 100644 --- a/doc/autoconf.texi +++ b/doc/autoconf.texi @@ -10611,16 +10611,16 @@ comma-separated quoted @var{list}, or the empty string if @var{list} had only one element. Generally, when using quoted lists of quoted elements, @code{m4_cdr} should be called without any extra quotes. -For example, this is an implementation of @code{m4_map}; note how each -iteration of the helper macro @code{_m4_map} checks for the end of -recursion, then merely applies the first argument to the first element -of the list, then recurses with the rest of the list. +For example, this is a simple implementation of @code{m4_map}; note how +each iteration checks for the end of recursion, then merely applies the +first argument to the first element of the list, then recurses with the +rest of the list. (The actual implementation in M4sugar is a bit more +involved, to gain some speed and share code with @code{m4_map_sep}). @example -m4_define([m4_map], [m4_if([$2], [[]], [], [_$0($@@)])])dnl -m4_define([_m4_map], [m4_ifval([$2], - [$1(m4_unquote(m4_car($2)))[]$0([$1], m4_cdr($2))])])dnl -m4_map([ m4_eval], [[1],[1+1]]) -@result{} 1 2 +m4_define([m4_map], [m4_ifval([$2], + [m4_apply([$1], m4_car($2))[]$0([$1], m4_cdr($2))])])dnl +m4_map([ m4_eval], [[[1]], [[1+1]], [[10],[16]]]) +@result{} 1 2 a @end example @end defmac @@ -10660,7 +10660,27 @@ The deprecated macro @code{AC_FOREACH} is an alias of @code{m4_foreach_w}. @end defmac -@c TODO document m4_map, m4_map_sep +@defmac m4_map (@var{macro}, @var{list}) +@defmacx m4_map_sep (@var{macro}, @var{separator}, @var{list}) +Loop over the comma separated quoted list of argument descriptions in +@var{list}, and invoke @var{macro} with the arguments. An argument +description is in turn a comma-separated quoted list of quoted elements, +suitable for @code{m4_apply}, making it possible to invoke @var{macro} +without arguments if an argument description is empty. +@code{m4_map_sep} additionally outputs @var{separator} between macro +invocations, with no additional expansion of the separator. +@example +m4_map([m4_count], []) +@result{} +m4_map([ m4_count], [[], + [[1]], + [[1], [2]]]) +@result{} 0 1 2 +m4_map_sep([m4_eval], [,], [[[1+2]], + [[10], [16]]]) +@result{}3,a +@end example +@end defmac @defmac m4_shiftn (@var{count}, @dots{}) @defmacx m4_shift2 (@dots{}) @@ -10683,6 +10703,29 @@ for two and three shifts, respectively. The following macros give some control over the order of the evaluation by adding or removing levels of quotes. +@defmac m4_apply (@var{macro}, @var{list}) +@msindex apply +Apply the elements of the quoted, comma-separated @var{list} as the +arguments to @var{macro}. If @var{list} is empty, invoke @var{macro} +without arguments. +@example +m4_apply([m4_count], []) +@result{}0 +m4_apply([m4_count], [[]]) +@result{}1 +m4_apply([m4_count], [[1], [2]]) +@result{}2 +m4_apply([m4_join], [[|], [1], [2]]) +@result{}1|2 +@end example +@end defmac + +@defmac m4_count (@var{arg1}, @dots{}) +@msindex{count} +This macro returns the decimal count of the number of arguments it was +passed. +@end defmac + @defmac m4_do (@var{arg1}, @dots{}) @msindex{do} This macro loops over its arguments and expands each @var{arg} in @@ -10697,6 +10740,19 @@ Conveniently, if there is just one @var{arg}, this effectively adds a level of quoting. @end defmac +@defmac m4_dquote_elt (@var{arg1}, @dots{}) +@msindex{dquote_elt} +Return the arguments as a series of double-quoted arguments. Whereas +@code{m4_dquote} returns a single argument, @code{m4_dquote_elt} returns +as many arguments as it was passed. +@end defmac + +@defmac m4_echo (@var{arg1}, @dots{}) +@msindex{echo} +Return the arguments, with the same level of quoting. Other than +discarding whitespace after unquoted commas, this macro is a no-op. +@end defmac + @defmac m4_expand (@var{arg}) @msindex{expand} Return the expansion of @var{arg} as a quoted string. Whereas @@ -10740,6 +10796,29 @@ Note that for earlier versions of Autoconf, the macro @code{__gnu__} can serve the same purpose, although it is less readable. @end defmac +@defmac m4_make_list (@var{arg1}, @dots{}) +@msindex{make_list} +This macro exists to aid debugging of M4sugar algorithms. Its net +effect is similar to @code{m4_dquote}---it produces a quoted list of +quoted arguments, for each @var{arg}. The difference is that this +version uses a comma-newline separator instead of just comma, to improve +readability of the list; with the result that it is less efficient than +@code{m4_dquote}. +@example +m4_define([zero],[0])m4_define([one],[1])m4_define([two],[2])dnl +m4_dquote(zero, [one], [[two]]) +@result{}[0],[one],[[two]] +m4_make_list(zero, [one], [[two]]) +@result{}[0], +@result{}[one], +@result{}[[two]] +m4_foreach([number], m4_dquote(zero, [one], [[two]]), [ number]) +@result{} 0 1 two +m4_foreach([number], m4_make_list(zero, [one], [[two]]), [ number]) +@result{} 0 1 two +@end example +@end defmac + @c m4_noquote is too dangerous to document - it invokes macros that @c probably rely on @samp{[]} nested quoting for proper operation. The @c user should generally prefer m4_unquote instead. @@ -10876,6 +10955,10 @@ m4_define([active], [ACTIVE])dnl m4_join([|], [one], [], [active], [two]) @result{}one|active|two @end example + +Note that if all you intend to do is join @var{args} with commas between +them, to form a quoted list suitable for @code{m4_foreach}, it is more +efficient to use @code{m4_dquote}. @end defmac @defmac m4_newline diff --git a/lib/m4sugar/m4sugar.m4 b/lib/m4sugar/m4sugar.m4 index e63d470e..459dc23b 100644 --- a/lib/m4sugar/m4sugar.m4 +++ b/lib/m4sugar/m4sugar.m4 @@ -450,23 +450,27 @@ m4_define([m4_cond], # m4_map(MACRO, LIST) # ------------------- # Invoke MACRO($1), MACRO($2) etc. where $1, $2... are the elements -# of LIST (which can be lists themselves, for multiple arguments MACROs). +# of LIST. $1, $2... must in turn be lists, appropriate for m4_apply. +# +# Since LIST may be quite large, we want to minimize how often it appears +# in the expansion. Rather than use m4_car/m4_cdr iteration, we unbox the +# list, and use _m4_shift2 to detect the end of recursion. m4_define([m4_map], -[m4_if([$2], [[]], [], - [_m4_map([$1], [$2])])]) +[m4_if([$2], [], [], + [_$0([$1], $2)])]) m4_define([_m4_map], [m4_if([$#], [1], [], - [$1(m4_unquote(m4_car($2)))[]_m4_map([$1]_m4_cdr($2))])]) + [m4_apply([$1], [$2])$0([$1]_m4_shift2($@))])]) # m4_map_sep(MACRO, SEPARATOR, LIST) # ---------------------------------- # Invoke MACRO($1), SEPARATOR, MACRO($2), ..., MACRO($N) where $1, $2... $N -# are the elements of LIST (which can be lists themselves, for multiple -# arguments MACROs). +# are the elements of LIST, and are in turn lists appropriate for m4_apply. +# SEPARATOR is not further expanded. m4_define([m4_map_sep], -[m4_if([$3], [[]], [], - [$1(m4_unquote(m4_car($3)))[]_m4_map([$2[]$1]_m4_cdr($3))])]) +[m4_if([$3], [], [], + [m4_apply([$1], m4_car($3))m4_map([[$2]$1]_m4_cdr($3))])]) ## ---------------------------------------- ## @@ -602,12 +606,16 @@ m4_define([_m4_shiftn], m4_define([m4_shift2], [m4_shift(m4_shift($@))]) m4_define([m4_shift3], [m4_shift(m4_shift(m4_shift($@)))]) +# _m4_shift2(...) # _m4_shift3(...) # --------------- -# Like m4_shift3, except include a leading comma unless there were exactly -# three arguments. Why? Because in recursion, it is nice to distinguish -# between 1 element left and 0 elements left, based on how many arguments -# this shift expands to. +# Like m4_shift2 or m4_shift3, except include a leading comma unless shifting +# consumes all arguments. Why? Because in recursion, it is nice to +# distinguish between 1 element left and 0 elements left, based on how many +# arguments this shift expands to. +m4_define([_m4_shift2], +[m4_if([$#], [2], [], + [, m4_shift(m4_shift($@))])]) m4_define([_m4_shift3], [m4_if([$#], [3], [], [, m4_shift(m4_shift(m4_shift($@)))])]) @@ -630,6 +638,22 @@ m4_define([m4_undefine], ## 7. Quoting manipulation. ## ## ------------------------- ## + +# m4_apply(MACRO, LIST) +# --------------------- +# Invoke MACRO, with arguments provided from the quoted list of +# comma-separated quoted arguments. If LIST is empty, invoke MACRO +# without arguments. +m4_define([m4_apply], +[m4_if([$2], [], [$1], [$1($2)])[]]) + + +# m4_count(ARGS) +# -------------- +# Return a count of how many ARGS are present. +m4_define([m4_count], [$#]) + + # m4_do(STRING, ...) # ------------------ # This macro invokes all its arguments (in sequence, of course). It is @@ -647,6 +671,22 @@ m4_define([m4_do], m4_define([m4_dquote], [[$@]]) +# m4_dquote_elt(ARGS) +# ------------------- +# Return ARGS as an unquoted list of double-quoted arguments. +m4_define([m4_dquote_elt], +[m4_if([$#], [0], [], + [$#], [1], [[[$1]]], + [[[$1]],$0(m4_shift($@))])]) + + +# m4_echo(ARGS) +# ------------- +# Return the ARGS, with the same level of quoting. Whitespace after +# unquoted commas are consumed. +m4_define([m4_echo], [$@]) + + # m4_expand(ARG) # -------------- # Return the expansion of ARG as a single string. Unlike m4_quote($1), this @@ -684,6 +724,18 @@ m4_define([_m4_expand], m4_define([m4_ignore]) +# m4_make_list(ARGS) +# ------------------ +# Similar to m4_dquote, this creates a quoted list of quoted ARGS. This +# version is less efficient than m4_dquote, but separates each argument +# with a comma and newline, rather than just comma, for readability. +# When developing an m4sugar algorithm, you could temporarily use +# m4_pushdef([m4_dquote],m4_defn([m4_make_list])) +# around your code to make debugging easier. +m4_define([m4_make_list], [m4_join([, +], m4_dquote_elt($@))]) + + # m4_noquote(STRING) # ------------------ # Return the result of ignoring all quotes in STRING and invoking the @@ -700,7 +752,7 @@ m4_define([m4_noquote], # m4_quote(ARGS) # -------------- # Return ARGS as a single argument. Any whitespace after unquoted commas -# is stripped. +# is stripped. There is always output, even when there were no arguments. # # It is important to realize the difference between `m4_quote(exp)' and # `[exp]': in the first case you obtain the quoted *result* of the @@ -709,6 +761,17 @@ m4_define([m4_noquote], m4_define([m4_quote], [[$*]]) +# _m4_quote(ARGS) +# --------------- +# Like m4_quote, except that when there are no arguments, there is no +# output. For conditional scenarios (such as passing _m4_quote as the +# macro name in m4_map), this feature can be used to distinguish between +# one argument of the empty string vs. no arguments. However, in the +# normal case with arguments present, this is less efficient than m4_quote. +m4_define([_m4_quote], +[m4_if([$#], [0], [], [[$*]])]) + + # m4_unquote(ARGS) # ---------------- # Remove one layer of quotes from each ARG, performing one level of @@ -1731,7 +1794,7 @@ m4_define([m4_normalize], # m4_join(SEP, ARG1, ARG2...) # --------------------------- # Produce ARG1SEPARG2...SEPARGn. Avoid back-to-back SEP when a given ARG -# is the empty string. +# is the empty string. No expansion is performed on SEP or ARGs. # # Since the number of arguments to join can be arbitrarily long, we # want to avoid having more than one $@ in the macro definition; @@ -2034,7 +2097,8 @@ m4_define([m4_sign], # used by m4_version_compare, but since [0r36:a] is less readable than 10, # we provide a wrapper for human use. m4_define([m4_version_unletter], -[m4_map_sep([m4_eval], [.], _$0([$1]))]) +[m4_map_sep([m4_eval], [.], + m4_dquote(m4_dquote_elt(m4_unquote(_$0([$1])))))]) m4_define([_m4_version_unletter], [m4_translit(m4_bpatsubst([[[$1]]], ]dnl m4_dquote(m4_dquote(m4_defn([m4_cr_Letters])))[[+],