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 <ebb9@byu.net>
This commit is contained in:
Eric Blake 2007-10-16 12:00:00 -06:00
parent f3b4cc104e
commit 33239cbd38
4 changed files with 193 additions and 29 deletions

View File

@ -1,5 +1,21 @@
2007-10-16 Eric Blake <ebb9@byu.net>
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 $@.

9
NEWS
View File

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

View File

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

View File

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