From: Eric Blake Date: Fri, 15 Aug 2008 13:06:39 +0000 (-0600) Subject: Fix m4_map regression from 2007-10-16. X-Git-Tag: v2.63~30 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=aeff96ce94c6ef4ea28fb77cf3c121e0db35cf09;p=thirdparty%2Fautoconf.git Fix m4_map regression from 2007-10-16. * lib/m4sugar/m4sugar.m4 (_m4_apply): New macro. (m4_map): Ignore empty sublists. For a list consisting of only an empty sublist, this restores 2.61 behavior of being a no-op. (m4_map_sep): Likewise, and expand separator. (m4_mapall, m4_mapall_sep): New macros, to regain 2.62 behavior. (_m4_map): Rewrite, to be common base for all four variants. * lib/m4sugar/foreach.m4 (_m4_map): Adjust to new prototype. * tests/m4sugar.at (m4@&t@_map): Add tests. * doc/autoconf.texi (Looping constructs) : Document new macros, and mention ramifications of expanded separator. * NEWS: Mention the change. Signed-off-by: Eric Blake --- diff --git a/ChangeLog b/ChangeLog index 9df0fa666..d62576b78 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2008-08-15 Eric Blake + + Fix m4_map regression from 2007-10-16. + * lib/m4sugar/m4sugar.m4 (_m4_apply): New macro. + (m4_map): Ignore empty sublists. For a list consisting of only an + empty sublist, this restores 2.61 behavior of being a no-op. + (m4_map_sep): Likewise, and expand separator. + (m4_mapall, m4_mapall_sep): New macros, to regain 2.62 behavior. + (_m4_map): Rewrite, to be common base for all four variants. + * lib/m4sugar/foreach.m4 (_m4_map): Adjust to new prototype. + * tests/m4sugar.at (m4@&t@_map): Add tests. + * doc/autoconf.texi (Looping constructs) : Document new + macros, and mention ramifications of expanded separator. + * NEWS: Mention the change. + 2008-08-14 Eric Blake Implement m4_transform_pair, to speed up AS_IF. diff --git a/NEWS b/NEWS index 301169716..96995567c 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,14 @@ GNU Autoconf NEWS - User visible changes. AC_TYPE_SIGNAL +** The macros m4_map and m4_map_sep now ignore any list elements + consisting of just empty quotes, and m4_map_sep now expands its + separator. This fixes a regression in 2.62 when these macros were + first documented, for the sake of clients expecting the semantics + that these macros had prior to that time. The new macros m4_mapall + and m4_mapall_sep, along with extra quoting of the separator, can + be used to get the semantics that m4_map_sep had in 2.62. + ** Clients of m4_expand, such as AS_HELP_STRING and AT_SETUP, can now handle properly quoted but otherwise unbalanced parentheses (for some macros, this fixes a regression in 2.62). @@ -27,10 +35,11 @@ GNU Autoconf NEWS - User visible changes. allowing the output of unbalanced parantheses in more contexts. ** The following m4sugar macros are new: - m4_joinall m4_reverse m4_set_add m4_set_add_all m4_set_contains - m4_set_contents m4_set_delete m4_set_difference m4_set_dump - m4_set_empty m4_set_foreach m4_set_intersection m4_set_list - m4_set_listc m4_set_remove m4_set_size m4_set_union + m4_joinall m4_mapall m4_mapall_sep m4_reverse m4_set_add + m4_set_add_all m4_set_contains m4_set_contents m4_set_delete + m4_set_difference m4_set_dump m4_set_empty m4_set_foreach + m4_set_intersection m4_set_list m4_set_listc m4_set_remove + m4_set_size m4_set_union ** The following m4sugar macros now accept multiple arguments, as is the case with underlying m4: diff --git a/doc/autoconf.texi b/doc/autoconf.texi index 61cccca65..2ce88f807 100644 --- a/doc/autoconf.texi +++ b/doc/autoconf.texi @@ -10822,24 +10822,49 @@ The deprecated macro @code{AC_FOREACH} is an alias of @end defmac @defmac m4_map (@var{macro}, @var{list}) +@defmacx m4_mapall (@var{macro}, @var{list}) @defmacx m4_map_sep (@var{macro}, @var{separator}, @var{list}) +@defmacx m4_mapall_sep (@var{macro}, @var{separator}, @var{list}) +@msindex{map} +@msindex{mapall} +@msindex{map_sep} +@msindex{mapall_sep} 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. +suitable for @code{m4_apply}. The macros @code{m4_map} and +@code{m4_map_sep} ignore empty argument descriptions, while +@code{m4_mapall} and @code{m4_mapall_sep} invoke @var{macro} with no +arguments. The macros @code{m4_map_sep} and @code{m4_mapall_sep} +additionally expand @var{separator} between invocations of @var{macro}. + +Note that @var{separator} is expanded, unlike in @code{m4_join}. When +separating output with commas, this means that the map result can be +used as a series of arguments, by using a single-quoted comma as +@var{separator}, or as a single string, by using a double-quoted comma. + @example m4_map([m4_count], []) @result{} m4_map([ m4_count], [[], [[1]], [[1], [2]]]) +@result{} 1 2 +m4_mapall([ m4_count], [[], + [[1]], + [[1], [2]]]) @result{} 0 1 2 m4_map_sep([m4_eval], [,], [[[1+2]], [[10], [16]]]) @result{}3,a +m4_map_sep([m4_echo], [,], [[[a]], [[b]]]) +@result{}a,b +m4_count(m4_map_sep([m4_echo], [,], [[[a]], [[b]]])) +@result{}2 +m4_map_sep([m4_echo], [[,]], [[[a]], [[b]]]) +@result{}a,b +m4_count(m4_map_sep([m4_echo], [[,]], [[[a]], [[b]]])) +@result{}1 @end example @end defmac diff --git a/lib/m4sugar/foreach.m4 b/lib/m4sugar/foreach.m4 index 53a2c7e24..91a964306 100644 --- a/lib/m4sugar/foreach.m4 +++ b/lib/m4sugar/foreach.m4 @@ -257,11 +257,16 @@ m4_define([m4_reverse], # of LIST. $1, $2... must in turn be lists, appropriate for m4_apply. # # m4_map/m4_map_sep only execute once; the speedup comes in fixing -# _m4_map. m4_foreach to the rescue. +# _m4_map. The mismatch in () is intentional, since $1 supplies the +# opening `(' (but it sure looks odd!). Build the temporary _m4_m: +# $1, [$3])$1, [$4])...$1, [$m])_m4_popdef([_m4_m]) m4_define([_m4_map], [m4_if([$#], [2], [], - [m4_foreach([_m4_elt], [m4_shift2($@)], - [m4_apply([$1], m4_defn([_m4_elt]))])])]) + [m4_define([_m4_m], m4_pushdef([_m4_m])_m4_for([_m4_m], [3], [$#], [1], + [$0_([1], _m4_m)])[_m4_popdef([_m4_m])])_m4_m($@)])]) + +m4_define([_m4_map_], +[[$$1, [$$2])]]) # m4_transform(EXPRESSION, ARG...) # -------------------------------- diff --git a/lib/m4sugar/m4sugar.m4 b/lib/m4sugar/m4sugar.m4 index a5a07b961..b33737087 100644 --- a/lib/m4sugar/m4sugar.m4 +++ b/lib/m4sugar/m4sugar.m4 @@ -684,10 +684,17 @@ m4_define([m4_wrap_lifo], # --------------------- # Invoke MACRO, with arguments provided from the quoted list of # comma-separated quoted arguments. If LIST is empty, invoke MACRO -# without arguments. +# without arguments. The expansion will not be concatenated with +# subsequent text. m4_define([m4_apply], [m4_if([$2], [], [$1], [$1($2)])[]]) +# _m4_apply(MACRO, LIST) +# ---------------------- +# Like m4_apply, except do nothing if LIST is empty. +m4_define([_m4_apply], +[m4_if([$2], [], [], [$1($2)[]])]) + # m4_count(ARGS) # -------------- @@ -803,7 +810,7 @@ m4_define([m4_quote], [[$*]]) # --------------- # 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 +# macro name in m4_mapall), 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], @@ -1002,31 +1009,63 @@ m4_define([m4_foreach_w], # m4_map(MACRO, LIST) -# ------------------- -# Invoke MACRO($1), MACRO($2) etc. where $1, $2... are the elements -# 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, ignore the second argument, and use m4_shift2 to detect the end of -# recursion. +# m4_mapall(MACRO, LIST) +# ---------------------- +# Invoke MACRO($1), MACRO($2) etc. where $1, $2... are the elements of +# LIST. $1, $2... must in turn be lists, appropriate for m4_apply. +# If LIST contains an empty sublist, m4_map skips the expansion of +# MACRO, while m4_mapall expands MACRO with no arguments. +# +# 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, ignore the second argument, and use m4_shift2 to +# detect the end of recursion. The mismatch in () is intentional; see +# _m4_map. For m4_map, an empty list behaves like an empty sublist +# and gets ignored; for m4_mapall, we must special-case the empty +# list. m4_define([m4_map], +[_m4_map([_m4_apply([$1]], [], $2)]) + +m4_define([m4_mapall], [m4_if([$2], [], [], - [_$0([$1], [], $2)])]) -m4_define([_m4_map], -[m4_if([$#], [2], [], - [m4_apply([$1], [$3])$0([$1], m4_shift2($@))])]) + [_m4_map([m4_apply([$1]], [], $2)])]) # m4_map_sep(MACRO, SEPARATOR, LIST) -# ---------------------------------- -# Invoke MACRO($1), SEPARATOR, MACRO($2), ..., MACRO($N) where $1, $2... $N -# are the elements of LIST, and are in turn lists appropriate for m4_apply. -# SEPARATOR is not further expanded. +# m4_mapall_sep(MACRO, SEPARATOR, LIST) +# ------------------------------------- +# Invoke MACRO($1), SEPARATOR, MACRO($2), ..., MACRO($N) where $1, +# $2... $N are the elements of LIST, and are in turn lists appropriate +# for m4_apply. SEPARATOR is expanded, in order to allow the creation +# of a list of arguments by using a single-quoted comma as the +# separator. For each empty sublist, m4_map_sep skips the expansion +# of MACRO and SEPARATOR, while m4_mapall_sep expands MACRO with no +# arguments. +# +# For m4_mapall_sep, merely expand the first iteration without the +# separator, then include separator as part of subsequent recursion. +# For m4_map_sep, things are trickier - we don't know if the first +# list element is an empty sublist, so we must define a self-modifying +# helper macro and use that as the separator instead. m4_define([m4_map_sep], +[m4_pushdef([m4_Sep], [m4_define([m4_Sep], _m4_defn([m4_unquote]))])]dnl +[_m4_map([_m4_apply([m4_Sep([$2])[]$1]], [], $3)m4_popdef([m4_Sep])]) + +m4_define([m4_mapall_sep], [m4_if([$3], [], [], - [m4_apply([$1], m4_car($3))_m4_map([[$2]$1], $3)])]) + [m4_apply([$1], m4_car($3))_m4_map([m4_apply([$2[]$1]], $3)])]) +# _m4_map(PREFIX, IGNORED, SUBLIST, ...) +# -------------------------------------- +# Common implementation for all four m4_map variants. The mismatch in +# the number of () is intentional. PREFIX must supply a form of +# m4_apply, the open `(', and the MACRO to be applied. Each iteration +# then appends `,', the current SUBLIST and the closing `)', then +# recurses to the next SUBLIST. IGNORED is an aid to ending recursion +# efficiently. +m4_define([_m4_map], +[m4_if([$#], [2], [], + [$1, [$3])$0([$1], m4_shift2($@))])]) # m4_transform(EXPRESSION, ARG...) # -------------------------------- diff --git a/tests/m4sugar.at b/tests/m4sugar.at index 8e9885ae4..75207e4de 100644 --- a/tests/m4sugar.at +++ b/tests/m4sugar.at @@ -757,12 +757,12 @@ autom4te: m4 failed with exit status: 1 AT_CLEANUP -## --------------- ## -## m4_map{,_sep}. ## -## --------------- ## +## --------------------- ## +## m4_map{,all}{,_sep}. ## +## --------------------- ## AT_SETUP([m4@&t@_map]) -AT_KEYWORDS([m4@&t@_apply]) +AT_KEYWORDS([m4@&t@_apply m4@&t@_map_sep m4@&t@_mapall m4@&t@_mapall_sep]) AT_KEYWORDS([m4@&t@_count]) AT_CHECK_M4SUGAR_TEXT([[dnl @@ -770,8 +770,21 @@ m4_map([m4_count], []) m4_map([ m4_count], [[], [[1]], [[1], [2]]]) +m4_mapall([ m4_count], [[], + [[1]], + [[1], [2]]]) m4_map_sep([m4_eval], [,], [[[1+2]], [[10], [16]]]) +m4_count(m4_map_sep([m4_echo], [,], [[], [[1]], [[2]]])) +m4_count(m4_mapall_sep([m4_echo], [,], [[], [[1]], [[2]]])) +m4_map_sep([m4_eval], [[,]], [[[1+2]], + [[10], [16]]]) +m4_count(m4_map_sep([m4_echo], [[,]], [[], [[1]], [[2]]])) +m4_count(m4_mapall_sep([m4_echo], [[,]], [[], [[1]], [[2]]])) +m4_map([-], [[]]) +m4_mapall([-], [[]]) +m4_map_sep([-], [:], [[]]) +m4_mapall_sep([-], [:], [[]]) m4_define([a], [m4_if([$#], [0], [oops], [$1], [a], [pass], [oops])])dnl m4_define([a1], [oops])dnl m4_define([pass1], [oops])dnl @@ -779,8 +792,18 @@ m4_map([a], [[[a]]])1 m4_map([m4_unquote([a])], [m4_dquote([a])]) ]], [[ + 1 2 0 1 2 3,a +2 +3 +3,a +1 +1 + +- + +- pass1 pass ]], [])