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) <m4_map>: Document new
macros, and mention ramifications of expanded separator.
* NEWS: Mention the change.

Signed-off-by: Eric Blake <ebb9@byu.net>
This commit is contained in:
Eric Blake 2008-08-15 07:06:39 -06:00
parent 903b58ef95
commit aeff96ce94
6 changed files with 150 additions and 34 deletions

View File

@ -1,3 +1,18 @@
2008-08-15 Eric Blake <ebb9@byu.net>
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) <m4_map>: Document new
macros, and mention ramifications of expanded separator.
* NEWS: Mention the change.
2008-08-14 Eric Blake <ebb9@byu.net>
Implement m4_transform_pair, to speed up AS_IF.

17
NEWS
View File

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

View File

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

View File

@ -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...)
# --------------------------------

View File

@ -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.
# 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.
# 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_if([$3], [], [],
[m4_apply([$1], m4_car($3))_m4_map([[$2]$1], $3)])])
[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([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...)
# --------------------------------

View File

@ -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
]], [])