autoconf-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

faster m4_foreach, m4_set_foreach, m4_foreach_w [was: faster m4_for]


From: Eric Blake
Subject: faster m4_foreach, m4_set_foreach, m4_foreach_w [was: faster m4_for]
Date: Wed, 05 Nov 2008 20:39:09 -0700
User-agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.17) Gecko/20080914 Thunderbird/2.0.0.17 Mnenhy/0.7.5.666

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

According to Eric Blake on 11/4/2008 4:04 PM:
> m4_for now passes the two arguments [m4_define([var],], [)expr] in place of 
> the 
> old arguments [var], [expr] and keeps its original semantics (the unbalanced 
> () 
> in pre and post are intentional, but at least they are better than _m4_map, 
> where the unbalance is split across caller and callee rather than across two 
> caller arguments; I have a pending patch to clean that up next).

More like a series of patches; all with the goal of providing a clean
PRE[arg]POST interface for the various m4_foreach/m4_map family of macros,
where PRE and POST can contain matching but unbalanced (), and can inject
more than one argument per iteration to a helper macro, when compared with
the clunkier EXPR(m4_defn([VAR])) of m4_foreach which is hammering on the
m4 symbol table by constantly redefining and accessing a temporary variable.

I'm still debating about documenting these new macros, but will probably
do so in the next week because of how much speedup they provided:
m4_map_args_sep
m4_map_args_w
m4_set_map_sep

I'm going ahead and applying this series of seven patches, because of the
huge speedups it gave me.  I tested 'time autoconf -f' on coreutils with
m4 1.4.11 (things went from 15.911 to 13.175, 17% improvement), and with
m4.git branch-1.6 (things went from 13.680 to 11.566, 15% improvement);
the bulk of the speedup comes from the final patch, where I overhauled
m4_foreach_w to use fewer regular expressions and macro calls.  I was
surprised to even find a use for m4_substr (in general, because m4_substr
doesn't quote its result, it is unsafe for most uses).  Modulo version
number changes, I verified that the resulting configure file was
unchanged, just generated faster; and I ran a complete testsuite.

Eric Blake (7):
      Improve m4_for performance.
      Add m4_map_args_sep, undocumented for now.
      Unify _m4_foreach and _m4_map.
      Use _m4_foreach in more places.
      Unify m4_set_foreach and m4_set_map.
      Use m4_set_map_sep in more places.
      Add m4_map_args_w.

> From: Eric Blake <address@hidden>
> Date: Tue, 4 Nov 2008 12:35:09 -0700
> Subject: [PATCH] Improve m4_for performance.
> 
> * lib/m4sugar/m4sugar.m4 (_m4_for): Alter API to make it easier to
> avoid m4_define by some clients.
> (m4_for): Adjust caller.
> * lib/m4sugar/foreach.m4 (_m4_foreach, m4_case, m4_bmatch)
> (_m4_cond, _m4_bpatsubsts, _m4_shiftn, m4_do, m4_reverse)
> (_m4_map, m4_map_args, m4_map_args_pair, _m4_list_pad)
> (_m4_list_cmp): Likewise.

I'm applying this one unchanged.  On top of it are the six new patches:

- --
Don't work too hard, make some time for fun as well!

Eric Blake             address@hidden
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Public key at home.comcast.net/~ericblake/eblake.gpg
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iEYEARECAAYFAkkSZt0ACgkQ84KuGfSFAYCSGgCfZXBabiiLRUgcNaq9E2fVEfEA
cLkAoKIei4zoFaPIWUoiCFNnK2/HDbdt
=zb5L
-----END PGP SIGNATURE-----
>From 1329287eb793d45ae2375414c0ec2fc17b10871d Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Mon, 3 Nov 2008 17:17:38 -0700
Subject: [PATCH] Add m4_map_args_sep, undocumented for now.

* lib/m4sugar/m4sugar.m4 (m4_map_args_sep): New macro.
(_m4_map): Change API to cover more of m4_map*.
* lib/m4sugar/foreach.m4 (_m4_map): Adjust to new API.
(m4_map_args): Delete.
* tests/m4sugar.at (m4@&address@hidden and m4@&address@hidden): Enhance
test.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog              |    8 ++++++
 lib/m4sugar/foreach.m4 |   40 +++++++++---------------------
 lib/m4sugar/m4sugar.m4 |   62 +++++++++++++++++++++++++----------------------
 tests/m4sugar.at       |   13 +++++++---
 4 files changed, 62 insertions(+), 61 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 2cbc291..c8597fd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
 2008-11-04  Eric Blake  <address@hidden>
 
+       Add m4_map_args_sep, undocumented for now.
+       * lib/m4sugar/m4sugar.m4 (m4_map_args_sep): New macro.
+       (_m4_map): Change API to cover more of m4_map*.
+       * lib/m4sugar/foreach.m4 (_m4_map): Adjust to new API.
+       (m4_map_args): Delete.
+       * tests/m4sugar.at (m4@&address@hidden and m4@&address@hidden): Enhance
+       test.
+
        Improve m4_for performance.
        * lib/m4sugar/m4sugar.m4 (_m4_for): Alter API to make it easier to
        avoid m4_define by some clients.
diff --git a/lib/m4sugar/foreach.m4 b/lib/m4sugar/foreach.m4
index cb08212..29c1a28 100644
--- a/lib/m4sugar/foreach.m4
+++ b/lib/m4sugar/foreach.m4
@@ -264,38 +264,22 @@ m4_define([m4_reverse],
     [[, ]m4_dquote($], [)])[_m4_popdef([_m4_r])])_m4_r($@)])])
 
 
-# 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_map(PRE, POST, IGNORED, LIST)
+# ---------------------------------
+# Form the common basis of the m4_map macros.  For each element of
+# LIST, expand PRE[element]POST[].  The IGNORED argument makes
+# recursion easier, and must be supplied rather than implicit.
 #
-# m4_map/m4_map_sep only execute once; the speedup comes in fixing
-# _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_map{,all,_args}{,_sep} each only execute once; the speedup comes
+# in fixing _m4_map.  Build the temporary _m4_m:
+#   $1[$4]$2[]$1[$5]$2[]...$1[$m]$2[]_m4_popdef([_m4_m])
 m4_define([_m4_map],
-[m4_if([$#], [2], [],
-       [m4_pushdef([_m4_m], _m4_for([3], [$#], [1],
-   [$0_([1],], [)])[_m4_popdef([_m4_m])])_m4_m($@)])])
+[m4_if([$#], [3], [],
+       [m4_pushdef([_m4_m], _m4_for([4], [$#], [1],
+   [$0_([1], [2],], [)])[_m4_popdef([_m4_m])])_m4_m($@)])])
 
 m4_define([_m4_map_],
-[[$$1, [$$2])]])
-
-# m4_map_args(EXPRESSION, ARG...)
-# -------------------------------
-# Expand EXPRESSION([ARG]) for each argument.  More efficient than
-# m4_foreach([var], [ARG...], [EXPRESSION(m4_defn([var]))])
-#
-# Invoke the temporary macro _m4_map_args, defined as:
-#   $1([$2])[]$1([$3])[]...$1([$m])[]_m4_popdef([_m4_map_args])
-m4_define([m4_map_args],
-[m4_if([$#], [0], [m4_fatal([$0: too few arguments: $#])],
-       [$#], [1], [],
-       [m4_pushdef([_$0], _m4_for([2], [$#], [1],
-   [_$0_([1],], [)])[_m4_popdef([_$0])])_$0($@)])])
-
-m4_define([_m4_map_args_],
-[[$$1([$$2])[]]])
+[[$$1[$$3]$$2[]]])
 
 # m4_map_args_pair(EXPRESSION, [END-EXPR = EXPRESSION], ARG...)
 # -------------------------------------------------------------
diff --git a/lib/m4sugar/m4sugar.m4 b/lib/m4sugar/m4sugar.m4
index f866112..ded4edc 100644
--- a/lib/m4sugar/m4sugar.m4
+++ b/lib/m4sugar/m4sugar.m4
@@ -1079,6 +1079,17 @@ m4_define([m4_foreach_w],
 [m4_foreach([$1], m4_split(m4_normalize([$2]), [ ]), [$3])])
 
 
+# _m4_map(PRE, POST, IGNORED, LIST)
+# ---------------------------------
+# Form the common basis of the m4_map macros.  For each element of
+# LIST, expand PRE[element]POST[].  The IGNORED argument makes
+# recursion easier, and must be supplied rather than implicit.
+#
+# Please keep foreach.m4 in sync with any adjustments made here.
+m4_define([_m4_map],
+[m4_if([$#], [3], [],
+       [$1[$4]$2[]$0([$1], [$2], m4_shift3($@))])])
+
 # m4_map(MACRO, LIST)
 # m4_mapall(MACRO, LIST)
 # ----------------------
@@ -1089,19 +1100,15 @@ m4_define([m4_foreach_w],
 #
 # 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.
-#
-# Please keep foreach.m4 in sync with any adjustments made here.
+# we unbox the list, and use _m4_map for iteration.  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_map([_m4_apply([$1],], [)], [], $2)])
 
 m4_define([m4_mapall],
 [m4_if([$2], [], [],
-       [_m4_map([m4_apply([$1]], [], $2)])])
+       [_m4_map([m4_apply([$1],], [)], [], $2)])])
 
 
 # m4_map_sep(MACRO, SEPARATOR, LIST)
@@ -1123,39 +1130,24 @@ m4_define([m4_mapall],
 # 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_map([_m4_apply([m4_Sep([$2])[]$1],], [)], [], $3)m4_popdef([m4_Sep])])
 
 m4_define([m4_mapall_sep],
 [m4_if([$3], [], [], [_$0([$1], [$2], $3)])])
 
 m4_define([_m4_mapall_sep],
-[m4_apply([$1], [$3])_m4_map([m4_apply([$2[]$1]], m4_shift2($@))])
-
-# _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.
-#
-# Please keep foreach.m4 in sync with any adjustments made here.
-m4_define([_m4_map],
-[m4_if([$#], [2], [],
-       [$1, [$3])$0([$1], m4_shift2($@))])])
+[m4_apply([$1], [$3])_m4_map([m4_apply([$2[]$1],], [)], m4_shift2($@))])
 
 # m4_map_args(EXPRESSION, ARG...)
 # -------------------------------
 # Expand EXPRESSION([ARG]) for each argument.  More efficient than
-# m4_foreach([var], [ARG...], [EXPRESSION(m4_defn([var]))])
-#
-# Please keep foreach.m4 in sync with any adjustments made here.
+#   m4_foreach([var], [ARG...], [EXPRESSION(m4_defn([var]))])
+# Shorthand for m4_map_args_sep([EXPRESSION(], [)], [], ARG...).
 m4_define([m4_map_args],
 [m4_if([$#], [0], [m4_fatal([$0: too few arguments: $#])],
        [$#], [1], [],
        [$#], [2], [$1([$2])[]],
-       [$1([$2])[]$0([$1], m4_shift2($@))])])
+       [_m4_map([$1(], [)], $@)])])
 
 
 # m4_map_args_pair(EXPRESSION, [END-EXPR = EXPRESSION], ARG...)
@@ -1181,6 +1173,18 @@ m4_define([m4_map_args_pair],
        [$1([$3], [$4])[]$0([$1], [$2], m4_shift(m4_shift3($@)))])])
 
 
+# m4_map_args_sep(PRE, POST, SEP, ARG...)
+# ---------------------------------------
+# Expand PRE[ARG]POST for each argument, with SEP between arguments.
+m4_define([m4_map_args_sep],
+[m4_if([$#], [0], [m4_fatal([$0: too few arguments: $#])],
+       [$#], [1], [],
+       [$#], [2], [],
+       [$#], [3], [],
+       [$#], [4], [$1[$4]$2[]],
+       [$1[$4]$2[]_m4_map([$3[]$1], [$2], m4_shift3($@))])])
+
+
 # m4_stack_foreach(MACRO, FUNC)
 # m4_stack_foreach_lifo(MACRO, FUNC)
 # ----------------------------------
diff --git a/tests/m4sugar.at b/tests/m4sugar.at
index 70bb24b..c8b22eb 100644
--- a/tests/m4sugar.at
+++ b/tests/m4sugar.at
@@ -1091,12 +1091,13 @@ hi
 AT_CLEANUP
 
 
-## ---------------------------------- ##
-## m4_map_args{,_pair} and m4_curry.  ##
-## ---------------------------------- ##
+## --------------------------------------- ##
+## m4_map_args{,_sep,_pair} and m4_curry.  ##
+## --------------------------------------- ##
 
 AT_SETUP([m4@&address@hidden and m4@&address@hidden)
-AT_KEYWORDS([m4@&address@hidden m4@&address@hidden)
+AT_KEYWORDS([m4@&address@hidden m4@&address@hidden m4@&address@hidden
+m4@&address@hidden)
 
 dnl First, make sure we can curry in isolation.
 AT_CHECK_M4SUGAR_TEXT(
@@ -1125,6 +1126,8 @@ m4_map_args_pair([, m4_reverse], [, m4_dquote], [1])
 m4_map_args_pair([, m4_reverse], [, m4_dquote], [1], [2])
 m4_map_args_pair([, m4_reverse], [, m4_dquote], [1], [2], [3])
 m4_map_args_pair([, m4_reverse], [, m4_dquote], [1], [2], [3], [4])
+m4_map_args_sep([<], [>], [:], [1], [2], [3])
+m4_map_args_sep([m4_echo(], [)], [ ], [plain], [active])
 ]],
 [[
  plain active
@@ -1138,6 +1141,8 @@ plainACTIVE
 , 2, 1
 , 2, 1, [3]
 , 2, 1, 4, 3
+<1>:<2>:<3>
+plain active
 ]])
 
 dnl Finally, put the two concepts together, to show the real power of the API.
-- 
1.6.0.2


>From aae086a2f71b316f2f29ad6ad3d45a8c69fffe3c Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Wed, 5 Nov 2008 07:15:56 -0700
Subject: [PATCH] Unify _m4_foreach and _m4_map.

* lib/m4sugar/m4sugar.m4 (_m4_map): Delete, merged with...
(_m4_foreach): ...this.
(m4_foreach, m4_map, m4_mapall, m4_map_sep, _m4_mapall_sep)
(m4_map_args, m4_map_args_sep): Adjust callers.
* lib/m4sugar/foreach.m4 (_m4_map): Rename...
(_m4_foreach): ...to this, overwriting old definition.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog              |   10 +++++++++
 lib/m4sugar/foreach.m4 |   53 ++++++++++++++++-------------------------------
 lib/m4sugar/m4sugar.m4 |   44 ++++++++++++++++++---------------------
 3 files changed, 48 insertions(+), 59 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index c8597fd..b5a82a8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2008-11-05  Eric Blake  <address@hidden>
+
+       Unify _m4_foreach and _m4_map.
+       * lib/m4sugar/m4sugar.m4 (_m4_map): Delete, merged with...
+       (_m4_foreach): ...this.
+       (m4_foreach, m4_map, m4_mapall, m4_map_sep, _m4_mapall_sep)
+       (m4_map_args, m4_map_args_sep): Adjust callers.
+       * lib/m4sugar/foreach.m4 (_m4_map): Rename...
+       (_m4_foreach): ...to this, overwriting old definition.
+
 2008-11-04  Eric Blake  <address@hidden>
 
        Add m4_map_args_sep, undocumented for now.
diff --git a/lib/m4sugar/foreach.m4 b/lib/m4sugar/foreach.m4
index 29c1a28..f432598 100644
--- a/lib/m4sugar/foreach.m4
+++ b/lib/m4sugar/foreach.m4
@@ -79,29 +79,29 @@
 #
 # Please keep this file in sync with m4sugar.m4.
 
-# m4_foreach(VARIABLE, LIST, EXPRESSION)
-# --------------------------------------
-# Expand EXPRESSION assigning each value of the LIST to VARIABLE.
-# LIST should have the form `item_1, item_2, ..., item_n', i.e. the
-# whole list must *quoted*.  Quote members too if you don't want them
-# to be expanded.
+# _m4_foreach(PRE, POST, IGNORED, ARG...)
+# ---------------------------------------
+# Form the common basis of the m4_foreach and m4_map macros.  For each
+# ARG, expand PRE[ARG]POST[].  The IGNORED argument makes recursion
+# easier, and must be supplied rather than implicit.
 #
 # This version minimizes the number of times that $@ is evaluated by
-# using m4_for to generate a boilerplate into VARIABLE then passing $@
-# to that temporary macro.  Thus, the recursion is done in m4_for
-# without reparsing any user input, and is not quadratic.  For an idea
-# of how this works, note that m4_foreach(i,[1,2],[i]) defines i to be
-#   m4_define([$1],[$3])$2[]m4_define([$1],[$4])$2[]m4_popdef([i])
-# then calls i([i],[i],[1],[2]).
-m4_define([m4_foreach],
-[m4_if([$2], [], [], [_$0([$1], [$3], $2)])])
-
+# using m4_for to generate a boilerplate into _m4_f then passing $@ to
+# that temporary macro.  Thus, the recursion is done in m4_for without
+# reparsing any user input, and is not quadratic.  For an idea of how
+# this works, note that m4_foreach(i,[1,2],[i]) calls
+#   _m4_foreach([m4_define([i],],[)i],[],[1],[2])
+# which defines _m4_f:
+#   $1[$4]$2[]$1[$5]$2[]_m4_popdef([_m4_f])
+# then calls _m4_f([m4_define([i],],[)i],[],[1],[2]) for a net result:
+#   m4_define([i],[1])i[]m4_define([i],[2])i[]_m4_popdef([_m4_f]).
 m4_define([_m4_foreach],
-[m4_pushdef([$1], _m4_for([3], [$#], [1],
-    [$0_([1], [2],], [)])[m4_popdef([$1])])m4_indir([$1], $@)])
+[m4_if([$#], [3], [],
+       [m4_pushdef([_m4_f], _m4_for([4], [$#], [1],
+   [$0_([1], [2],], [)])[_m4_popdef([_m4_f])])_m4_f($@)])])
 
 m4_define([_m4_foreach_],
-[[m4_define([$$1], [$$3])$$2[]]])
+[[$$1[$$3]$$2[]]])
 
 # m4_case(SWITCH, VAL1, IF-VAL1, VAL2, IF-VAL2, ..., DEFAULT)
 # -----------------------------------------------------------
@@ -264,23 +264,6 @@ m4_define([m4_reverse],
     [[, ]m4_dquote($], [)])[_m4_popdef([_m4_r])])_m4_r($@)])])
 
 
-# _m4_map(PRE, POST, IGNORED, LIST)
-# ---------------------------------
-# Form the common basis of the m4_map macros.  For each element of
-# LIST, expand PRE[element]POST[].  The IGNORED argument makes
-# recursion easier, and must be supplied rather than implicit.
-#
-# m4_map{,all,_args}{,_sep} each only execute once; the speedup comes
-# in fixing _m4_map.  Build the temporary _m4_m:
-#   $1[$4]$2[]$1[$5]$2[]...$1[$m]$2[]_m4_popdef([_m4_m])
-m4_define([_m4_map],
-[m4_if([$#], [3], [],
-       [m4_pushdef([_m4_m], _m4_for([4], [$#], [1],
-   [$0_([1], [2],], [)])[_m4_popdef([_m4_m])])_m4_m($@)])])
-
-m4_define([_m4_map_],
-[[$$1[$$3]$$2[]]])
-
 # m4_map_args_pair(EXPRESSION, [END-EXPR = EXPRESSION], ARG...)
 # -------------------------------------------------------------
 # Perform a pairwise grouping of consecutive ARGs, by expanding
diff --git a/lib/m4sugar/m4sugar.m4 b/lib/m4sugar/m4sugar.m4
index ded4edc..89d26c3 100644
--- a/lib/m4sugar/m4sugar.m4
+++ b/lib/m4sugar/m4sugar.m4
@@ -1052,16 +1052,23 @@ m4_define([_m4_for],
 # more memory for expansion.  So, rather than directly compare $2 against
 # [] and use m4_car/m4_cdr for recursion, we instead unbox the list (which
 # requires swapping the argument order in the helper), insert an ignored
-# third argument, and use m4_shift3 to detect when recursion is complete.
-#
-# Please keep foreach.m4 in sync with any adjustments made here.
+# third argument, and use m4_shift3 to detect when recursion is complete,
+# at which point this looks very much like m4_map_args.
 m4_define([m4_foreach],
 [m4_if([$2], [], [],
-       [m4_pushdef([$1])_$0([$1], [$3], [], $2)m4_popdef([$1])])])
+       [m4_pushdef([$1])_$0([m4_define([$1],], [)$3], [],
+  $2)m4_popdef([$1])])])
 
+# _m4_foreach(PRE, POST, IGNORED, ARG...)
+# ---------------------------------------
+# Form the common basis of the m4_foreach and m4_map macros.  For each
+# ARG, expand PRE[ARG]POST[].  The IGNORED argument makes recursion
+# easier, and must be supplied rather than implicit.
+#
+# Please keep foreach.m4 in sync with any adjustments made here.
 m4_define([_m4_foreach],
 [m4_if([$#], [3], [],
-       [m4_define([$1], [$4])$2[]$0([$1], [$2], m4_shift3($@))])])
+       [$1[$4]$2[]$0([$1], [$2], m4_shift3($@))])])
 
 
 # m4_foreach_w(VARIABLE, LIST, EXPRESSION)
@@ -1079,17 +1086,6 @@ m4_define([m4_foreach_w],
 [m4_foreach([$1], m4_split(m4_normalize([$2]), [ ]), [$3])])
 
 
-# _m4_map(PRE, POST, IGNORED, LIST)
-# ---------------------------------
-# Form the common basis of the m4_map macros.  For each element of
-# LIST, expand PRE[element]POST[].  The IGNORED argument makes
-# recursion easier, and must be supplied rather than implicit.
-#
-# Please keep foreach.m4 in sync with any adjustments made here.
-m4_define([_m4_map],
-[m4_if([$#], [3], [],
-       [$1[$4]$2[]$0([$1], [$2], m4_shift3($@))])])
-
 # m4_map(MACRO, LIST)
 # m4_mapall(MACRO, LIST)
 # ----------------------
@@ -1100,15 +1096,15 @@ m4_define([_m4_map],
 #
 # 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_map for iteration.  For m4_map, an
-# empty list behaves like an empty sublist and gets ignored; for
+# we unbox the list, and use _m4_foreach for iteration.  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_foreach([_m4_apply([$1],], [)], [], $2)])
 
 m4_define([m4_mapall],
 [m4_if([$2], [], [],
-       [_m4_map([m4_apply([$1],], [)], [], $2)])])
+       [_m4_foreach([m4_apply([$1],], [)], [], $2)])])
 
 
 # m4_map_sep(MACRO, SEPARATOR, LIST)
@@ -1130,13 +1126,13 @@ m4_define([m4_mapall],
 # 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_foreach([_m4_apply([m4_Sep([$2])[]$1],], [)], [], $3)m4_popdef([m4_Sep])])
 
 m4_define([m4_mapall_sep],
 [m4_if([$3], [], [], [_$0([$1], [$2], $3)])])
 
 m4_define([_m4_mapall_sep],
-[m4_apply([$1], [$3])_m4_map([m4_apply([$2[]$1],], [)], m4_shift2($@))])
+[m4_apply([$1], [$3])_m4_foreach([m4_apply([$2[]$1],], [)], m4_shift2($@))])
 
 # m4_map_args(EXPRESSION, ARG...)
 # -------------------------------
@@ -1147,7 +1143,7 @@ m4_define([m4_map_args],
 [m4_if([$#], [0], [m4_fatal([$0: too few arguments: $#])],
        [$#], [1], [],
        [$#], [2], [$1([$2])[]],
-       [_m4_map([$1(], [)], $@)])])
+       [_m4_foreach([$1(], [)], $@)])])
 
 
 # m4_map_args_pair(EXPRESSION, [END-EXPR = EXPRESSION], ARG...)
@@ -1182,7 +1178,7 @@ m4_define([m4_map_args_sep],
        [$#], [2], [],
        [$#], [3], [],
        [$#], [4], [$1[$4]$2[]],
-       [$1[$4]$2[]_m4_map([$3[]$1], [$2], m4_shift3($@))])])
+       [$1[$4]$2[]_m4_foreach([$3[]$1], [$2], m4_shift3($@))])])
 
 
 # m4_stack_foreach(MACRO, FUNC)
-- 
1.6.0.2


>From 641487fb34b8bb642ece91340d9fa2a57d75d9ee Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Wed, 5 Nov 2008 11:12:42 -0700
Subject: [PATCH] Use _m4_foreach in more places.

* lib/m4sugar/foreach.m4 (m4_dquote_elt, m4_join, m4_joinall)
(_m4_minmax, m4_set_add_all): Use _m4_foreach instead of
m4_foreach.
* lib/m4sugar/m4sugar.m4 (_m4_joinall): Use m4_map_args_sep
instead of m4_foreach or m4_map_args.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog              |    7 +++++++
 lib/m4sugar/foreach.m4 |   42 +++++++++++++++++++++---------------------
 lib/m4sugar/m4sugar.m4 |   20 +++++++-------------
 3 files changed, 35 insertions(+), 34 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index b5a82a8..3be1d3a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
 2008-11-05  Eric Blake  <address@hidden>
 
+       Use _m4_foreach in more places.
+       * lib/m4sugar/foreach.m4 (m4_dquote_elt, m4_join, m4_joinall)
+       (_m4_minmax, m4_set_add_all): Use _m4_foreach instead of
+       m4_foreach.
+       * lib/m4sugar/m4sugar.m4 (_m4_joinall): Use m4_map_args_sep
+       instead of m4_foreach or m4_map_args.
+
        Unify _m4_foreach and _m4_map.
        * lib/m4sugar/m4sugar.m4 (_m4_map): Delete, merged with...
        (_m4_foreach): ...this.
diff --git a/lib/m4sugar/foreach.m4 b/lib/m4sugar/foreach.m4
index f432598..d9ebfe0 100644
--- a/lib/m4sugar/foreach.m4
+++ b/lib/m4sugar/foreach.m4
@@ -248,9 +248,9 @@ m4_define([m4_do],
 # -------------------
 # Return ARGS as an unquoted list of double-quoted arguments.
 #
-# m4_foreach to the rescue.  It's easier to shift off the leading comma.
+# _m4_foreach to the rescue.
 m4_define([m4_dquote_elt],
-[m4_shift(m4_foreach([_m4_elt], address@hidden, 
[,m4_dquote(_m4_defn([_m4_elt]))]))])
+[m4_if([$#], [0], [], [[[$1]]_m4_foreach([,m4_dquote(], [)], $@)])])
 
 # m4_reverse(ARGS)
 # ----------------
@@ -299,23 +299,23 @@ m4_define([_m4_map_args_pair_end],
 #
 # Use a self-modifying separator, since we don't know how many
 # arguments might be skipped before a separator is first printed, but
-# be careful if the separator contains $.  m4_foreach to the rescue.
+# be careful if the separator contains $.  _m4_foreach to the rescue.
 m4_define([m4_join],
 [m4_pushdef([_m4_sep], [m4_define([_m4_sep], _m4_defn([m4_echo]))])]dnl
-[m4_foreach([_m4_arg], [m4_shift($@)],
-           [m4_ifset([_m4_arg], [_m4_sep([$1])_m4_defn([_m4_arg])])])]dnl
-[_m4_popdef([_m4_sep])])
+[_m4_foreach([_$0([$1],], [)], $@)_m4_popdef([_m4_sep])])
+
+m4_define([_m4_join],
+[m4_if([$2], [], [], [_m4_sep([$1])[$2]])])
 
 # m4_joinall(SEP, ARG1, ARG2...)
 # ------------------------------
 # Produce ARG1SEPARG2...SEPARGn.  An empty ARG results in back-to-back SEP.
 # No expansion is performed on SEP or ARGs.
 #
-# A bit easier than m4_join.  m4_foreach to the rescue.
+# A bit easier than m4_join.  _m4_foreach to the rescue.
 m4_define([m4_joinall],
 [[$2]m4_if(m4_eval([$# <= 2]), [1], [],
-          [m4_foreach([_m4_arg], [m4_shift2($@)],
-                      [[$1]_m4_defn([_m4_arg])])])])
+          [_m4_foreach([$1], [], m4_shift($@))])])
 
 # m4_list_cmp(A, B)
 # -----------------
@@ -358,12 +358,12 @@ m4_define([_m4_list_cmp__],
 # Return the decimal value of the maximum (or minimum) in a series of
 # integer expressions.
 #
-# m4_foreach to the rescue; we only need to replace _m4_minmax.  Here,
+# _m4_foreach to the rescue; we only need to replace _m4_minmax.  Here,
 # we need a temporary macro to track the best answer so far, so that
 # the foreach expression is tractable.
 m4_define([_m4_minmax],
-[m4_pushdef([_m4_best], m4_eval([$2]))m4_foreach([_m4_arg], [m4_shift2($@)],
-    [m4_define([_m4_best], $1(_m4_best, _m4_defn([_m4_arg])))])]dnl
+[m4_pushdef([_m4_best], m4_eval([$2]))_m4_foreach(
+  [m4_define([_m4_best], $1(_m4_best,], [))], m4_shift($@))]dnl
 [_m4_best[]_m4_popdef([_m4_best])])
 
 # m4_set_add_all(SET, VALUE...)
@@ -371,15 +371,15 @@ m4_define([_m4_minmax],
 # Add each VALUE into SET.  This is O(n) in the number of VALUEs, and
 # can be faster than calling m4_set_add for each VALUE.
 #
-# m4_foreach to the rescue.  If no deletions have occurred, then avoid
-# the speed penalty of m4_set_add.
+# _m4_foreach to the rescue.  If no deletions have occurred, then
+# avoid the speed penalty of m4_set_add.
 m4_define([m4_set_add_all],
 [m4_if([$#], [0], [], [$#], [1], [],
        [m4_define([_m4_set_size($1)], m4_eval(m4_set_size([$1])
-         + m4_len(m4_foreach([_m4_arg], [m4_shift($@)],
-    m4_ifdef([_m4_set_cleanup($1)],
-            [[m4_set_add([$1], _m4_defn([_m4_arg]))]],
-            [[m4_ifdef([_m4_set([$1],]_m4_defn([_m4_arg])[)], [],
-                       [m4_define([_m4_set([$1],]_m4_defn([_m4_arg])[)],
-                                  [1])m4_pushdef([_m4_set([$1])],
-       _m4_defn([_m4_arg]))-])]])))))])])
+         + m4_len(_m4_foreach(m4_ifdef([_m4_set_cleanup($1)],
+  [[m4_set_add]], [[_$0]])[([$1],], [)], $@))))])])
+
+m4_define([_m4_set_add_all],
+[m4_ifdef([_m4_set([$1],$2)], [],
+         [m4_define([_m4_set([$1],$2)],
+                    [1])m4_pushdef([_m4_set([$1])], [$2])-])])
diff --git a/lib/m4sugar/m4sugar.m4 b/lib/m4sugar/m4sugar.m4
index 89d26c3..5107792 100644
--- a/lib/m4sugar/m4sugar.m4
+++ b/lib/m4sugar/m4sugar.m4
@@ -2171,20 +2171,14 @@ m4_define([_m4_joinall],
 #   m4_combine([, ], [[a], [b], [c]], [-], [1], [2], [3])
 #   => a-1, a-2, a-3, b-1, b-2, b-3, c-1, c-2, c-3
 #
-# In order to have the correct number of SEPARATORs, we use a temporary
-# variable that redefines itself after the first use.  We must use defn
-# rather than overquoting in case PREFIX or SUFFIX contains $1, but use
-# _m4_defn for speed.  Likewise, we compute the m4_shift3 only once,
-# rather than in each iteration of the outer m4_foreach.
+# This definition is a bit hairy; the thing to realize is that we want
+# to construct m4_map_args_sep([[prefix$3]], [], [[$1]], m4_shift3($@))
+# as the inner loop, using each prefix generated by the outer loop,
+# and without recalculating m4_shift3 every outer iteration.
 m4_define([m4_combine],
-[m4_if(m4_eval([$# > 3]), [1],
-       [m4_pushdef([m4_Separator], [m4_define([m4_Separator],
-                                   _m4_defn([m4_echo]))])]]dnl
-[[m4_foreach([m4_Prefix], [$2],
-   [m4_map_args([m4_Separator([$1])]m4_dquote(_m4_defn(
-        [m4_Prefix]))[[$3]m4_echo],
-               ]]m4_dquote(m4_dquote(m4_shift3($@)))[[)])]]dnl
-[[_m4_popdef([m4_Separator])])])
+[m4_if([$2], [], [], m4_eval([$# > 3]), [1],
+[m4_map_args_sep([m4_map_args_sep(m4_dquote(], [)[[$3]], [], [[$1]],]]]dnl
+[m4_dquote(m4_dquote(m4_shift3($@)))[[)], [[$1]], $2)])])
 
 
 # m4_append(MACRO-NAME, STRING, [SEPARATOR])
-- 
1.6.0.2


>From a8c4be749a992c2e9c2f0a59336f964f4cce6d4f Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Wed, 5 Nov 2008 11:47:04 -0700
Subject: [PATCH] Unify m4_set_foreach and m4_set_map.

* lib/m4sugar/m4sugar.m4 (m4_set_map_sep): New macro, undocumented
for now.
(m4_set_contents, m4_set_foreach, m4_set_list, m4_set_listc)
(m4_set_map): Adjust callers.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog              |    6 ++++++
 lib/m4sugar/m4sugar.m4 |   28 +++++++++++++++-------------
 2 files changed, 21 insertions(+), 13 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 3be1d3a..d14b179 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2008-11-05  Eric Blake  <address@hidden>
 
+       Unify m4_set_foreach and m4_set_map.
+       * lib/m4sugar/m4sugar.m4 (m4_set_map_sep): New macro, undocumented
+       for now.
+       (m4_set_contents, m4_set_foreach, m4_set_list, m4_set_listc)
+       (m4_set_map): Adjust callers.
+
        Use _m4_foreach in more places.
        * lib/m4sugar/foreach.m4 (m4_dquote_elt, m4_join, m4_joinall)
        (_m4_minmax, m4_set_add_all): Use _m4_foreach instead of
diff --git a/lib/m4sugar/m4sugar.m4 b/lib/m4sugar/m4sugar.m4
index 5107792..15fd807 100644
--- a/lib/m4sugar/m4sugar.m4
+++ b/lib/m4sugar/m4sugar.m4
@@ -2688,8 +2688,7 @@ m4_define([m4_set_contains],
 # Use _m4_popdef for speed.  The existence of _m4_set_cleanup($1)
 # determines which version of _1 helper we use.
 m4_define([m4_set_contents],
-[m4_ifdef([_m4_set_cleanup($1)], [_$0_1c], [_$0_1])([$1])_$0_2([$1],
-  [], [], [[$2]])])
+[m4_set_map_sep([$1], [], [], [[$2]])])
 
 # _m4_set_contents_1(SET)
 # _m4_set_contents_1c(SET)
@@ -2804,9 +2803,7 @@ m4_define([m4_set_empty],
 # guaranteed.  This is faster than the corresponding m4_foreach([VAR],
 #   m4_indir([m4_dquote]m4_set_listc([SET])), [ACTION])
 m4_define([m4_set_foreach],
-[m4_pushdef([$2])m4_ifdef([_m4_set_cleanup($1)],
-    [_m4_set_contents_1c], [_m4_set_contents_1])([$1])_m4_set_contents_2([$1],
-       [m4_define([$2],], [)$3[]])m4_popdef([$2])])
+[m4_pushdef([$2])m4_set_map_sep([$1], [m4_define([$2],], [)$3])])
 
 # m4_set_intersection(SET1, SET2)
 # -------------------------------
@@ -2835,12 +2832,10 @@ m4_define([m4_set_intersection],
 # containing only the empty string; with m4_set_listc, a leading comma
 # is output if there are any elements.
 m4_define([m4_set_list],
-[m4_ifdef([_m4_set_cleanup($1)], [_m4_set_contents_1c],
-         [_m4_set_contents_1])([$1])_m4_set_contents_2([$1], [], [], [,])])
+[m4_set_map_sep([$1], [], [], [,])])
 
 m4_define([m4_set_listc],
-[m4_ifdef([_m4_set_cleanup($1)], [_m4_set_contents_1c],
-         [_m4_set_contents_1])([$1])_m4_set_contents_2([$1], [,])])
+[m4_set_map_sep([$1], [,])])
 
 # m4_set_map(SET, ACTION)
 # -----------------------
@@ -2848,12 +2843,19 @@ m4_define([m4_set_listc],
 # current element.  ACTION should not recursively list SET's contents,
 # add elements to SET, nor delete any element from SET except the one
 # passed as an argument.  The order that the elements are visited in
-# is not guaranteed.  This is faster than the corresponding
+# is not guaranteed.  This is faster than either of the corresponding
 #   m4_map_args([ACTION]m4_set_listc([SET]))
+#   m4_set_foreach([SET], [VAR], [ACTION(m4_defn([VAR]))])
 m4_define([m4_set_map],
-[m4_ifdef([_m4_set_cleanup($1)],
-    [_m4_set_contents_1c], [_m4_set_contents_1])([$1])_m4_set_contents_2([$1],
-       [$2(], [)])])
+[m4_set_map_sep([$1], [$2(], [)])])
+
+# m4_set_map_sep(SET, PRE, POST, SEP)
+# -----------------------------------
+# For each element of SET, expand PRE[value]POST[], and expand SEP
+# between elements.
+m4_define([m4_set_map_sep],
+[m4_ifdef([_m4_set_cleanup($1)], [_m4_set_contents_1c],
+         [_m4_set_contents_1])([$1])_m4_set_contents_2($@)])
 
 # m4_set_remove(SET, VALUE, [IF-PRESENT], [IF-ABSENT])
 # ----------------------------------------------------
-- 
1.6.0.2


>From 4e009d522870613c74305ad49e6cc548133b6bab Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Wed, 5 Nov 2008 12:09:00 -0700
Subject: [PATCH] Use m4_set_map_sep in more places.

* lib/m4sugar/m4sugar.m4 (m4_set_difference, m4_set_intersection)
(m4_set_union): Use m4_set_map_sep rather than m4_set_foreach.
* doc/autoconf.texi (Set manipulation Macros) <m4_set_map>:
Enhance documentation.
<m4_set_foreach>: Mention faster alternative.
(Looping constructs) <m4_foreach>: Likewise.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog              |    8 ++++++++
 doc/autoconf.texi      |   14 ++++++++++----
 lib/m4sugar/m4sugar.m4 |   31 +++++++++++++++++--------------
 3 files changed, 35 insertions(+), 18 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index d14b179..73e8095 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
 2008-11-05  Eric Blake  <address@hidden>
 
+       Use m4_set_map_sep in more places.
+       * lib/m4sugar/m4sugar.m4 (m4_set_difference, m4_set_intersection)
+       (m4_set_union): Use m4_set_map_sep rather than m4_set_foreach.
+       * doc/autoconf.texi (Set manipulation Macros) <m4_set_map>:
+       Enhance documentation.
+       <m4_set_foreach>: Mention faster alternative.
+       (Looping constructs) <m4_foreach>: Likewise.
+
        Unify m4_set_foreach and m4_set_map.
        * lib/m4sugar/m4sugar.m4 (m4_set_map_sep): New macro, undocumented
        for now.
diff --git a/doc/autoconf.texi b/doc/autoconf.texi
index bc46d77..e114992 100644
--- a/doc/autoconf.texi
+++ b/doc/autoconf.texi
@@ -10921,6 +10921,9 @@ Looping constructs
 @result{}echo foo
 @result{}echo bar, baz
 @end example
+
+Note that for some forms of @var{expression}, it may be faster to use
address@hidden
 @end defmac
 
 @anchor{m4_foreach_w}
@@ -11786,7 +11789,8 @@ Set manipulation Macros
 in any way other than removing the element currently contained in
 @var{variable}.  This macro is faster than the corresponding
 @code{m4_foreach(address@hidden,
-m4_indir([m4_dquote]m4_set_listc(address@hidden)), address@hidden)}.
+m4_indir([m4_dquote]m4_set_listc(address@hidden)), address@hidden)},
+although @code{m4_set_map} might be faster still.
 
 @example
 m4_set_add_all([a]m4_for([i], [1], [5], [], [,i]))
@@ -11847,9 +11851,11 @@ Set manipulation Macros
 argument of the set element.  Behavior is unspecified if @var{action}
 recursively lists the contents of @var{set} (although listing other sets
 is acceptable), or if it modifies the set in any way other than removing
-the element passed as an argument.  This macro is faster than the
-corresponding
address@hidden(address@hidden(address@hidden))}.
+the element passed as an argument.  This macro is faster than either
+corresponding counterpart of
address@hidden(address@hidden(address@hidden))} or
address@hidden(address@hidden, [var],
address@hidden(m4_defn([var]))])}.
 @end defmac
 
 @defmac m4_set_remove (@var{set}, @var{value}, @ovar{if-present}, @
diff --git a/lib/m4sugar/m4sugar.m4 b/lib/m4sugar/m4sugar.m4
index 15fd807..5c2e1c8 100644
--- a/lib/m4sugar/m4sugar.m4
+++ b/lib/m4sugar/m4sugar.m4
@@ -2744,12 +2744,12 @@ m4_define([m4_set_delete],
 # arguments, such as for m4_join, or wrapped inside quotes for use in
 # m4_foreach.  Order of the output is not guaranteed.
 #
-# Short-circuit the idempotence relation.  Use _m4_defn for speed.
+# Short-circuit the idempotence relation.
 m4_define([m4_set_difference],
-[m4_if([$1], [$2], [],
-       [m4_set_foreach([$1], [_m4_element],
-                      [m4_set_contains([$2], _m4_defn([_m4_element]), [],
-                                       [,_m4_defn([_m4_element])])])])])
+[m4_if([$1], [$2], [], [m4_set_map_sep([$1], [_$0([$2],], [)])])])
+
+m4_define([_m4_set_difference],
+[m4_set_contains([$1], [$2], [], [,[$2]])])
 
 # m4_set_dump(SET, [SEP])
 # -----------------------
@@ -2814,13 +2814,14 @@ m4_define([m4_set_foreach],
 # m4_foreach.  Order of the output is not guaranteed.
 #
 # Iterate over the smaller set, and short-circuit the idempotence
-# relation.  Use _m4_defn for speed.
+# relation.
 m4_define([m4_set_intersection],
 [m4_if([$1], [$2], [m4_set_listc([$1])],
        m4_eval(m4_set_size([$2]) < m4_set_size([$1])), [1], [$0([$2], [$1])],
-       [m4_set_foreach([$1], [_m4_element],
-                      [m4_set_contains([$2], _m4_defn([_m4_element]),
-                                       [,_m4_defn([_m4_element])])])])])
+       [m4_set_map_sep([$1], [_$0([$2],], [)])])])
+
+m4_define([_m4_set_intersection],
+[m4_set_contains([$1], [$2], [,[$2]])])
 
 # m4_set_list(SET)
 # m4_set_listc(SET)
@@ -2901,12 +2902,14 @@ m4_define([_m4_set_size],
 # not guaranteed.
 #
 # We can rely on the fact that m4_set_listc prunes SET1, so we don't
-# need to check _m4_set([$1],element) for 0.  Use _m4_defn for speed.
-# Short-circuit the idempotence relation.
+# need to check _m4_set([$1],element) for 0.  Short-circuit the
+# idempotence relation.
 m4_define([m4_set_union],
-[m4_set_listc([$1])m4_if([$1], [$2], [], [m4_set_foreach([$2], [_m4_element],
-  [m4_ifdef([_m4_set([$1],]_m4_defn([_m4_element])[)], [],
-           [,_m4_defn([_m4_element])])])])])
+[m4_set_listc([$1])m4_if([$1], [$2], [],
+  [m4_set_map_sep([$2], [_$0([$1],], [)])])])
+
+m4_define([_m4_set_union],
+[m4_ifdef([_m4_set([$1],$2)], [], [,[$2]])])
 
 
 ## ------------------- ##
-- 
1.6.0.2


>From b07a2b3d74f02078b409f70666853a353f95dd94 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Wed, 5 Nov 2008 17:11:15 -0700
Subject: [PATCH] Add m4_map_args_w.

* lib/m4sugar/m4sugar.m4 (m4_map_args_w): New macro, undocumented
for now.
(_m4_split): Allow user control over separator.
(m4_split): Adjust caller.
(m4_foreach_w, m4_append_uniq_w, _m4_text_wrap): Rewrite to use
m4_map_args_w.
* tests/m4sugar.at (m4@&address@hidden): Augment test keywords.
(M4 loops): Test new interface.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog              |   10 ++++++++
 lib/m4sugar/m4sugar.m4 |   61 +++++++++++++++++++++++++++++++++---------------
 tests/m4sugar.at       |   10 +++++++-
 3 files changed, 61 insertions(+), 20 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 73e8095..e6686fc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,15 @@
 2008-11-05  Eric Blake  <address@hidden>
 
+       Add m4_map_args_w.
+       * lib/m4sugar/m4sugar.m4 (m4_map_args_w): New macro, undocumented
+       for now.
+       (_m4_split): Allow user control over separator.
+       (m4_split): Adjust caller.
+       (m4_foreach_w, m4_append_uniq_w, _m4_text_wrap): Rewrite to use
+       m4_map_args_w.
+       * tests/m4sugar.at (m4@&address@hidden): Augment test keywords.
+       (M4 loops): Test new interface.
+
        Use m4_set_map_sep in more places.
        * lib/m4sugar/m4sugar.m4 (m4_set_difference, m4_set_intersection)
        (m4_set_union): Use m4_set_map_sep rather than m4_set_foreach.
diff --git a/lib/m4sugar/m4sugar.m4 b/lib/m4sugar/m4sugar.m4
index 5c2e1c8..2a0d7ce 100644
--- a/lib/m4sugar/m4sugar.m4
+++ b/lib/m4sugar/m4sugar.m4
@@ -1073,8 +1073,8 @@ m4_define([_m4_foreach],
 
 # m4_foreach_w(VARIABLE, LIST, EXPRESSION)
 # ----------------------------------------
-#
-# Like m4_foreach, but the list is whitespace separated.
+# Like m4_foreach, but the list is whitespace separated.  Depending on
+# EXPRESSION, it may be more efficient to use m4_map_args_w.
 #
 # This macro is robust to active symbols:
 #    m4_foreach_w([Var], [ active
@@ -1082,8 +1082,11 @@ m4_define([_m4_foreach],
 #    ive  ], [-Var-])end
 #    => -active--b--active-end
 #
+# This used to use a slower implementation based on m4_foreach:
+#   m4_foreach([$1], m4_split(m4_normalize([$2]), [ ]), [$3])
 m4_define([m4_foreach_w],
-[m4_foreach([$1], m4_split(m4_normalize([$2]), [ ]), [$3])])
+[m4_pushdef([$1])m4_map_args_w([$2],
+  [m4_define([$1],], [)$3])m4_popdef([$1])])
 
 
 # m4_map(MACRO, LIST)
@@ -1181,6 +1184,29 @@ m4_define([m4_map_args_sep],
        [$1[$4]$2[]_m4_foreach([$3[]$1], [$2], m4_shift3($@))])])
 
 
+# m4_map_args_w(STRING, [PRE], [POST])
+# ------------------------------------
+# Perform the expansion of PRE[word]POST[] for each word in STRING
+# separated by whitespace.  More efficient than:
+#   m4_foreach_w([var], [STRING], [PRE[]m4_defn([var])POST])
+#
+# As long as we have to use m4_bpatsubst to split the string, we might
+# as well make it also apply PRE and POST; this avoids iteration
+# altogether.  But we must be careful of any \ in PRE or POST.
+# _m4_strip returns a quoted string, but that's okay, since it also
+# supplies an empty leading and trailing argument due to our
+# intentional whitespace around STRING.  We use m4_substr to strip the
+# empty elements and remove the extra layer of quoting.
+m4_define([m4_map_args_w],
+[_$0(_m4_split([ ]m4_flatten([$1])[ ], [[       ]+],
+              m4_if(m4_index([$2$3], [\]), [-1], [[$3[]$2]],
+                    [m4_bpatsubst([[$3[]$2]], [\\], [\\\\])])),
+     m4_len([[]$3]), m4_len([$2[]]))])
+
+m4_define([_m4_map_args_w],
+[m4_substr([$1], [$2], m4_eval(m4_len([$1]) - [$2] - [$3]))])
+
+
 # m4_stack_foreach(MACRO, FUNC)
 # m4_stack_foreach_lifo(MACRO, FUNC)
 # ----------------------------------
@@ -2024,7 +2050,6 @@ m4_define([m4_toupper],
 
 # m4_split(STRING, [REGEXP])
 # --------------------------
-#
 # Split STRING into an m4 list of quoted elements.  The elements are
 # quoted with [ and ].  Beginning spaces and end spaces *are kept*.
 # Use m4_strip to remove them.
@@ -2053,15 +2078,15 @@ m4_define([m4_toupper],
 # so avoid unnecessary dnl inside the definition.
 m4_define([m4_split],
 [m4_if([$1], [], [],
-       [$2], [ ], [m4_if(m4_index([$1], [ ]), [-1], [[[$1]]], [_$0($@)])],
-       [$2], [], [_$0([$1], [[  ]+])],
-       [_$0($@)])])
+       [$2], [ ], [m4_if(m4_index([$1], [ ]), [-1], [[[$1]]],
+                        [_$0([$1], [$2], [, ])])],
+       [$2], [], [_$0([$1], [[  ]+], [, ])],
+       [_$0([$1], [$2], [, ])])])
 
 m4_define([_m4_split],
 [m4_changequote([-=<{(],[)}>=-])]dnl
 [[m4_bpatsubst(-=<{(-=<{($1)}>=-)}>=-, -=<{($2)}>=-,
-              -=<{(], [)}>=-)]m4_changequote([, ])])
-
+              -=<{(]$3[)}>=-)]m4_changequote([, ])])
 
 
 # m4_flatten(STRING)
@@ -2267,8 +2292,7 @@ m4_define([_m4_append_uniq],
 #
 # Use _m4_defn for speed.
 m4_define([m4_append_uniq_w],
-[m4_foreach_w([m4_Word], [$2],
-             [_m4_append_uniq([$1], _m4_defn([m4_Word]), [ ])])])
+[m4_map_args_w([$2], [_m4_append_uniq([$1],], [, [ ])])])
 
 
 # m4_text_wrap(STRING, [PREFIX], [FIRST-PREFIX], [WIDTH])
@@ -2342,17 +2366,16 @@ dnl check if the cursor would exceed the wrap column
 dnl if so, reset cursor, and insert newline and prefix
 dnl if not, insert the separator (usually a space)
 dnl either way, insert the word
-[[m4_foreach_w([m4_Word], [$1],
-  [m4_define([m4_Cursor],
-            m4_eval(m4_Cursor + m4_qlen(_m4_defn([m4_Word]))
-                    + 1))m4_if(m4_eval(m4_Cursor > ([$4])),
-      [1], [m4_define([m4_Cursor],
-                     m4_eval(m4_Indent + m4_qlen(_m4_defn([m4_Word])) + 1))
-[$2]],
-      [m4_Separator[]])_m4_defn([m4_Word])])]],
+[[m4_map_args_w([$1], [$0_word(], [, [$2], [$4])])]],
 dnl finally, clean up the local variables
 [[_m4_popdef([m4_Separator], [m4_Cursor], [m4_Indent])]]))
 
+m4_define([_m4_text_wrap_word],
+[m4_define([m4_Cursor], m4_eval(m4_Cursor + m4_qlen([$1]) + 1))]dnl
+[m4_if(m4_eval(m4_Cursor > ([$3])),
+      [1], [m4_define([m4_Cursor], m4_eval(m4_Indent + m4_qlen([$1]) + 1))
+[$2]],
+      [m4_Separator[]])[$1]])
 
 # m4_text_box(MESSAGE, [FRAME-CHARACTER = `-'])
 # ---------------------------------------------
diff --git a/tests/m4sugar.at b/tests/m4sugar.at
index c8b22eb..5a90493 100644
--- a/tests/m4sugar.at
+++ b/tests/m4sugar.at
@@ -546,6 +546,7 @@ AT_CLEANUP
 ## ----------- ##
 
 AT_SETUP([m4@&address@hidden)
+AT_KEYWORDS([m4@&address@hidden m4@&address@hidden)
 
 AT_CHECK_M4SUGAR_TEXT(
 [[m4_define([active], [ACTIVE])dnl
@@ -876,7 +877,7 @@ AT_CLEANUP
 
 AT_SETUP([M4 loops])
 
-AT_KEYWORDS([m4@&address@hidden m4@&address@hidden m4@&address@hidden)
+AT_KEYWORDS([m4@&address@hidden m4@&address@hidden m4@&address@hidden 
m4@&address@hidden)
 
 AT_CHECK_M4SUGAR_TEXT([[dnl
 m4_define([myvar], [outer value])dnl
@@ -921,6 +922,10 @@ m4_foreach([myvar], [[a], [b, c], [d], [e
 m4_foreach_w([myvar], [a  b c, d,e f
 g], [ myvar|])
 myvar
+m4_map_args_w([a  b c, d,e f
+g], [ ], [|])
+m4_map_args_w([a b], [\1], [/])
+m4_map_args_w([a b], [/], [\1])
 dnl only one side effect expansion, prior to visiting list elements
 m4_foreach([i], [[1], [2], [3]m4_errprintn([hi])], [m4_errprintn(i)])dnl
 dnl shifting forms an important part of loops
@@ -959,6 +964,9 @@ m4_shiftn(3,1,2,3):m4_shiftn(3,1,2,3,4)
 | f|
  a| b| c,| d,e| f| g|
 outer value
+ a| b| c,| d,e| f| g|
+\1a/\1b/
+/a\1/b\1
 ::4
 :4
 ]], [[hi
-- 
1.6.0.2


reply via email to

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