autoconf-patches
[Top][All Lists]
Advanced

[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







reply via email to

[Prev in Thread] Current Thread [Next in Thread]