[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Fix m4_join
From: |
Eric Blake |
Subject: |
Re: Fix m4_join |
Date: |
Tue, 16 Oct 2007 18:21:35 +0000 (UTC) |
User-agent: |
Loom/3.14 (http://gmane.org/) |
Eric Blake <ebb9 <at> byu.net> writes:
> Maybe I should write up a new macro,
> m4_make_list(ARGS), which takes multiple arguments, and does just that.
>
> And while I'm at it, I think it is time that I implement a few other macros:
>
> m4_echo(ARGS) - expands to its arguments, each one quoted. Useful for
> situations, like m4_map, where you have to provide a macro name, but don't
> need to do any action.
>
> m4_dquote_elt(ARGS) (see dquote_elt in the m4 manual) - expands to an
> unquoted list of each argument double-quoted.
>
> m4_count(ARGS) - expands to the count of the number of arguments
Here's the patch for these macros, along with m4_apply, and semantics changes
to m4_map{,_sep} to make it reliably distinguish between an empty list of
arguments to apply, vs. a list containing a macro application with 0 arguments,
vs. a list containing an argument list that contains an empty argument.
I'm not sure m4_map will be used much (partly because it requires so much
quoting!). But I did find a use for it in my recent changes to
m4_version_unletter.
From: Eric Blake <address@hidden>
Date: Tue, 16 Oct 2007 12:00:00 -0600
Subject: [PATCH] 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 <address@hidden>
---
ChangeLog | 16 +++++++
NEWS | 9 ++--
doc/autoconf.texi | 103 +++++++++++++++++++++++++++++++++++++++++++-----
lib/m4sugar/m4sugar.m4 | 94 ++++++++++++++++++++++++++++++++++++-------
4 files changed, 193 insertions(+), 29 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 930f3ba..f5b770d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,21 @@
2007-10-16 Eric Blake <address@hidden>
+ 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 address@hidden
diff --git a/NEWS b/NEWS
index e423906..25095f2 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 19ad110..369b555 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]])
address@hidden 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]]])
address@hidden 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
address@hidden TODO document m4_map, m4_map_sep
address@hidden m4_map (@var{macro}, @var{list})
address@hidden m4_map_sep (@var{macro}, @var{separator}, @var{list})
+Loop over the comma separated quoted list of argument descriptions in
address@hidden, 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.
address@hidden additionally outputs @var{separator} between macro
+invocations, with no additional expansion of the separator.
address@hidden
+m4_map([m4_count], [])
address@hidden
+m4_map([ m4_count], [[],
+ [[1]],
+ [[1], [2]]])
address@hidden 0 1 2
+m4_map_sep([m4_eval], [,], [[[1+2]],
+ [[10], [16]]])
address@hidden,a
address@hidden example
address@hidden 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.
address@hidden m4_apply (@var{macro}, @var{list})
address@hidden 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.
address@hidden
+m4_apply([m4_count], [])
address@hidden
+m4_apply([m4_count], [[]])
address@hidden
+m4_apply([m4_count], [[1], [2]])
address@hidden
+m4_apply([m4_join], [[|], [1], [2]])
address@hidden|2
address@hidden example
address@hidden defmac
+
address@hidden m4_count (@var{arg1}, @dots{})
address@hidden
+This macro returns the decimal count of the number of arguments it was
+passed.
address@hidden 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
address@hidden m4_dquote_elt (@var{arg1}, @dots{})
address@hidden
+Return the arguments as a series of double-quoted arguments. Whereas
address@hidden returns a single argument, @code{m4_dquote_elt} returns
+as many arguments as it was passed.
address@hidden defmac
+
address@hidden m4_echo (@var{arg1}, @dots{})
address@hidden
+Return the arguments, with the same level of quoting. Other than
+discarding whitespace after unquoted commas, this macro is a no-op.
address@hidden 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
address@hidden m4_make_list (@var{arg1}, @dots{})
address@hidden
+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
address@hidden
address@hidden
+m4_define([zero],[0])m4_define([one],[1])m4_define([two],[2])dnl
+m4_dquote(zero, [one], [[two]])
address@hidden,[one],[[two]]
+m4_make_list(zero, [one], [[two]])
address@hidden,
address@hidden,
address@hidden
+m4_foreach([number], m4_dquote(zero, [one], [[two]]), [ number])
address@hidden 0 1 two
+m4_foreach([number], m4_make_list(zero, [one], [[two]]), [ number])
address@hidden 0 1 two
address@hidden example
address@hidden 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 e63d470..459dc23 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], address@hidden)
+# 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], address@hidden)
+
+
# 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])))[[+],
--
1.5.3.2
- Fix m4_join, Eric Blake, 2007/10/13
- Re: Fix m4_join, Ralf Wildenhues, 2007/10/14
- Re: Fix m4_join, Eric Blake, 2007/10/14
- Re: Fix m4_join, Ralf Wildenhues, 2007/10/15
- Re: Fix m4_join, Eric Blake, 2007/10/15
- Re: Fix m4_join, Ralf Wildenhues, 2007/10/15
- Re: Fix m4_join, Eric Blake, 2007/10/15
- Re: Fix m4_join, Ralf Wildenhues, 2007/10/16
- Re: Fix m4_join,
Eric Blake <=