bug-bison
[Top][All Lists]
Advanced

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

Re: m4_map


From: Eric Blake
Subject: Re: m4_map
Date: Fri, 15 Aug 2008 07:29:06 -0600
User-agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.16) Gecko/20080708 Thunderbird/2.0.0.16 Mnenhy/0.7.5.666

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

According to Ralf Wildenhues on 8/14/2008 12:43 PM:
> * Eric Blake wrote on Thu, Aug 14, 2008 at 03:11:44PM CEST:
>> So, to solve things, I'm thinking of the following change:
>>
>> m4_map, m4_map_sep no longer invoke func on an empty argument sublist.
>>
>> Meanwhile, I will add two new functions m4_mapall and m4_mapall_sep,
> 
> As far as I have understood the problem space (which is quite a bit from
> fully, mind you), your proposal makes sense to me, if the bison
> developers don't have issues with it.

Here's the patch that I'm playing with on the autoconf side.  I've already
verified that with this patch, autoconf's m4sugar.m4 was able to pass the
bison testsuite with two minor tweaks (bison still has one use of m4_fst
that autoconf no longer supplies, and bison needs to be taught how to find
m4sugar/foreach.m4 for linear rather than quadratic scaling when run atop
m4 1.4.x).  I'll follow up to the bison list with two patches to finish
the re-sync with autoconf's m4sugar improvements.

>  I understand correctly that
> except for Autoconf and Bison, practically nobody uses these macros
> yet?

Yes, that seems to be the case.  And even Autoconf's one existing usage
(besides the testsuite) in m4_version_unletter did not need any tweaking
for the fixed semantics.

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

iEYEARECAAYFAkilhKEACgkQ84KuGfSFAYCNhwCgjmHC333G8iB+duI/HV21ysTT
/D4AoLbtRZor0p67nwDNKB0lYcnIoyFm
=89kJ
-----END PGP SIGNATURE-----
>From aeff96ce94c6ef4ea28fb77cf3c121e0db35cf09 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Fri, 15 Aug 2008 07:06:39 -0600
Subject: [PATCH] Fix m4_map regression from 2007-10-16.

* lib/m4sugar/m4sugar.m4 (_m4_apply): New macro.
(m4_map): Ignore empty sublists.  For a list consisting of only an
empty sublist, this restores 2.61 behavior of being a no-op.
(m4_map_sep): Likewise, and expand separator.
(m4_mapall, m4_mapall_sep): New macros, to regain 2.62 behavior.
(_m4_map): Rewrite, to be common base for all four variants.
* lib/m4sugar/foreach.m4 (_m4_map): Adjust to new prototype.
* tests/m4sugar.at (m4@&address@hidden): Add tests.
* doc/autoconf.texi (Looping constructs) <m4_map>: Document new
macros, and mention ramifications of expanded separator.
* NEWS: Mention the change.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog              |   15 +++++++++
 NEWS                   |   17 ++++++++--
 doc/autoconf.texi      |   33 ++++++++++++++++++--
 lib/m4sugar/foreach.m4 |   11 +++++--
 lib/m4sugar/m4sugar.m4 |   77 ++++++++++++++++++++++++++++++++++++------------
 tests/m4sugar.at       |   31 +++++++++++++++++--
 6 files changed, 150 insertions(+), 34 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 9df0fa6..d62576b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2008-08-15  Eric Blake  <address@hidden>
+
+       Fix m4_map regression from 2007-10-16.
+       * lib/m4sugar/m4sugar.m4 (_m4_apply): New macro.
+       (m4_map): Ignore empty sublists.  For a list consisting of only an
+       empty sublist, this restores 2.61 behavior of being a no-op.
+       (m4_map_sep): Likewise, and expand separator.
+       (m4_mapall, m4_mapall_sep): New macros, to regain 2.62 behavior.
+       (_m4_map): Rewrite, to be common base for all four variants.
+       * lib/m4sugar/foreach.m4 (_m4_map): Adjust to new prototype.
+       * tests/m4sugar.at (m4@&address@hidden): Add tests.
+       * doc/autoconf.texi (Looping constructs) <m4_map>: Document new
+       macros, and mention ramifications of expanded separator.
+       * NEWS: Mention the change.
+
 2008-08-14  Eric Blake  <address@hidden>
 
        Implement m4_transform_pair, to speed up AS_IF.
diff --git a/NEWS b/NEWS
index 3011697..9699556 100644
--- a/NEWS
+++ b/NEWS
@@ -19,6 +19,14 @@ GNU Autoconf NEWS - User visible changes.
 
      AC_TYPE_SIGNAL
 
+** The macros m4_map and m4_map_sep now ignore any list elements
+   consisting of just empty quotes, and m4_map_sep now expands its
+   separator.  This fixes a regression in 2.62 when these macros were
+   first documented, for the sake of clients expecting the semantics
+   that these macros had prior to that time.  The new macros m4_mapall
+   and m4_mapall_sep, along with extra quoting of the separator, can
+   be used to get the semantics that m4_map_sep had in 2.62.
+
 ** Clients of m4_expand, such as AS_HELP_STRING and AT_SETUP, can now
    handle properly quoted but otherwise unbalanced parentheses (for
    some macros, this fixes a regression in 2.62).
@@ -27,10 +35,11 @@ GNU Autoconf NEWS - User visible changes.
    allowing the output of unbalanced parantheses in more contexts.
 
 ** The following m4sugar macros are new:
-   m4_joinall  m4_reverse  m4_set_add  m4_set_add_all  m4_set_contains
-   m4_set_contents  m4_set_delete  m4_set_difference  m4_set_dump
-   m4_set_empty  m4_set_foreach  m4_set_intersection  m4_set_list
-   m4_set_listc  m4_set_remove  m4_set_size  m4_set_union
+   m4_joinall  m4_mapall  m4_mapall_sep  m4_reverse  m4_set_add
+   m4_set_add_all  m4_set_contains  m4_set_contents  m4_set_delete
+   m4_set_difference  m4_set_dump  m4_set_empty  m4_set_foreach
+   m4_set_intersection  m4_set_list  m4_set_listc  m4_set_remove
+   m4_set_size  m4_set_union
 
 ** The following m4sugar macros now accept multiple arguments, as is the
    case with underlying m4:
diff --git a/doc/autoconf.texi b/doc/autoconf.texi
index 61cccca..2ce88f8 100644
--- a/doc/autoconf.texi
+++ b/doc/autoconf.texi
@@ -10822,24 +10822,49 @@ The deprecated macro @code{AC_FOREACH} is an alias of
 @end defmac
 
 @defmac m4_map (@var{macro}, @var{list})
address@hidden m4_mapall (@var{macro}, @var{list})
 @defmacx m4_map_sep (@var{macro}, @var{separator}, @var{list})
address@hidden m4_mapall_sep (@var{macro}, @var{separator}, @var{list})
address@hidden
address@hidden
address@hidden
address@hidden
 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.
address@hidden additionally outputs @var{separator} between macro
-invocations, with no additional expansion of the separator.
+suitable for @code{m4_apply}.  The macros @code{m4_map} and
address@hidden ignore empty argument descriptions, while
address@hidden and @code{m4_mapall_sep} invoke @var{macro} with no
+arguments.  The macros @code{m4_map_sep} and @code{m4_mapall_sep}
+additionally expand @var{separator} between invocations of @var{macro}.
+
+Note that @var{separator} is expanded, unlike in @code{m4_join}.  When
+separating output with commas, this means that the map result can be
+used as a series of arguments, by using a single-quoted comma as
address@hidden, or as a single string, by using a double-quoted comma.
+
 @example
 m4_map([m4_count], [])
 @result{}
 m4_map([ m4_count], [[],
                      [[1]],
                      [[1], [2]]])
address@hidden 1 2
+m4_mapall([ m4_count], [[],
+                        [[1]],
+                        [[1], [2]]])
 @result{} 0 1 2
 m4_map_sep([m4_eval], [,], [[[1+2]],
                             [[10], [16]]])
 @result{}3,a
+m4_map_sep([m4_echo], [,], [[[a]], [[b]]])
address@hidden,b
+m4_count(m4_map_sep([m4_echo], [,], [[[a]], [[b]]]))
address@hidden
+m4_map_sep([m4_echo], [[,]], [[[a]], [[b]]])
address@hidden,b
+m4_count(m4_map_sep([m4_echo], [[,]], [[[a]], [[b]]]))
address@hidden
 @end example
 @end defmac
 
diff --git a/lib/m4sugar/foreach.m4 b/lib/m4sugar/foreach.m4
index 53a2c7e..91a9643 100644
--- a/lib/m4sugar/foreach.m4
+++ b/lib/m4sugar/foreach.m4
@@ -257,11 +257,16 @@ m4_define([m4_reverse],
 # of LIST.  $1, $2... must in turn be lists, appropriate for m4_apply.
 #
 # m4_map/m4_map_sep only execute once; the speedup comes in fixing
-# _m4_map.  m4_foreach to the rescue.
+# _m4_map.  The mismatch in () is intentional, since $1 supplies the
+# opening `(' (but it sure looks odd!).  Build the temporary _m4_m:
+#   $1, [$3])$1, [$4])...$1, [$m])_m4_popdef([_m4_m])
 m4_define([_m4_map],
 [m4_if([$#], [2], [],
-       [m4_foreach([_m4_elt], [m4_shift2($@)],
-                  [m4_apply([$1], m4_defn([_m4_elt]))])])])
+       [m4_define([_m4_m], m4_pushdef([_m4_m])_m4_for([_m4_m], [3], [$#], [1],
+   [$0_([1], _m4_m)])[_m4_popdef([_m4_m])])_m4_m($@)])])
+
+m4_define([_m4_map_],
+[[$$1, [$$2])]])
 
 # m4_transform(EXPRESSION, ARG...)
 # --------------------------------
diff --git a/lib/m4sugar/m4sugar.m4 b/lib/m4sugar/m4sugar.m4
index a5a07b9..b337370 100644
--- a/lib/m4sugar/m4sugar.m4
+++ b/lib/m4sugar/m4sugar.m4
@@ -684,10 +684,17 @@ m4_define([m4_wrap_lifo],
 # ---------------------
 # Invoke MACRO, with arguments provided from the quoted list of
 # comma-separated quoted arguments.  If LIST is empty, invoke MACRO
-# without arguments.
+# without arguments.  The expansion will not be concatenated with
+# subsequent text.
 m4_define([m4_apply],
 [m4_if([$2], [], [$1], [$1($2)])[]])
 
+# _m4_apply(MACRO, LIST)
+# ----------------------
+# Like m4_apply, except do nothing if LIST is empty.
+m4_define([_m4_apply],
+[m4_if([$2], [], [], [$1($2)[]])])
+
 
 # m4_count(ARGS)
 # --------------
@@ -803,7 +810,7 @@ m4_define([m4_quote],  [[$*]])
 # ---------------
 # Like m4_quote, except that when there are no arguments, there is no
 # output.  For conditional scenarios (such as passing _m4_quote as the
-# macro name in m4_map), this feature can be used to distinguish between
+# macro name in m4_mapall), this feature can be used to distinguish between
 # one argument of the empty string vs. no arguments.  However, in the
 # normal case with arguments present, this is less efficient than m4_quote.
 m4_define([_m4_quote],
@@ -1002,31 +1009,63 @@ m4_define([m4_foreach_w],
 
 
 # m4_map(MACRO, LIST)
-# -------------------
-# Invoke MACRO($1), MACRO($2) etc. where $1, $2... are the elements
-# of LIST.  $1, $2... must in turn be lists, appropriate for m4_apply.
-#
-# 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.
+# m4_mapall(MACRO, LIST)
+# ----------------------
+# Invoke MACRO($1), MACRO($2) etc. where $1, $2... are the elements of
+# LIST.  $1, $2... must in turn be lists, appropriate for m4_apply.
+# If LIST contains an empty sublist, m4_map skips the expansion of
+# MACRO, while m4_mapall expands MACRO with no arguments.
+#
+# Since LIST may be quite large, we want to minimize how often it
+# appears in the expansion.  Rather than use m4_car/m4_cdr iteration,
+# we unbox the list, ignore the second argument, and use m4_shift2 to
+# detect the end of recursion.  The mismatch in () is intentional; see
+# _m4_map.  For m4_map, an empty list behaves like an empty sublist
+# and gets ignored; for m4_mapall, we must special-case the empty
+# list.
 m4_define([m4_map],
+[_m4_map([_m4_apply([$1]], [], $2)])
+
+m4_define([m4_mapall],
 [m4_if([$2], [], [],
-       [_$0([$1], [], $2)])])
-m4_define([_m4_map],
-[m4_if([$#], [2], [],
-       [m4_apply([$1], [$3])$0([$1], m4_shift2($@))])])
+       [_m4_map([m4_apply([$1]], [], $2)])])
 
 
 # m4_map_sep(MACRO, SEPARATOR, LIST)
-# ----------------------------------
-# Invoke MACRO($1), SEPARATOR, MACRO($2), ..., MACRO($N) where $1, $2... $N
-# are the elements of LIST, and are in turn lists appropriate for m4_apply.
-# SEPARATOR is not further expanded.
+# m4_mapall_sep(MACRO, SEPARATOR, LIST)
+# -------------------------------------
+# Invoke MACRO($1), SEPARATOR, MACRO($2), ..., MACRO($N) where $1,
+# $2... $N are the elements of LIST, and are in turn lists appropriate
+# for m4_apply.  SEPARATOR is expanded, in order to allow the creation
+# of a list of arguments by using a single-quoted comma as the
+# separator.  For each empty sublist, m4_map_sep skips the expansion
+# of MACRO and SEPARATOR, while m4_mapall_sep expands MACRO with no
+# arguments.
+#
+# For m4_mapall_sep, merely expand the first iteration without the
+# separator, then include separator as part of subsequent recursion.
+# For m4_map_sep, things are trickier - we don't know if the first
+# list element is an empty sublist, so we must define a self-modifying
+# helper macro and use that as the separator instead.
 m4_define([m4_map_sep],
+[m4_pushdef([m4_Sep], [m4_define([m4_Sep], _m4_defn([m4_unquote]))])]dnl
+[_m4_map([_m4_apply([m4_Sep([$2])[]$1]], [], $3)m4_popdef([m4_Sep])])
+
+m4_define([m4_mapall_sep],
 [m4_if([$3], [], [],
-       [m4_apply([$1], m4_car($3))_m4_map([[$2]$1], $3)])])
+       [m4_apply([$1], m4_car($3))_m4_map([m4_apply([$2[]$1]], $3)])])
 
+# _m4_map(PREFIX, IGNORED, SUBLIST, ...)
+# --------------------------------------
+# Common implementation for all four m4_map variants.  The mismatch in
+# the number of () is intentional.  PREFIX must supply a form of
+# m4_apply, the open `(', and the MACRO to be applied.  Each iteration
+# then appends `,', the current SUBLIST and the closing `)', then
+# recurses to the next SUBLIST.  IGNORED is an aid to ending recursion
+# efficiently.
+m4_define([_m4_map],
+[m4_if([$#], [2], [],
+       [$1, [$3])$0([$1], m4_shift2($@))])])
 
 # m4_transform(EXPRESSION, ARG...)
 # --------------------------------
diff --git a/tests/m4sugar.at b/tests/m4sugar.at
index 8e9885a..75207e4 100644
--- a/tests/m4sugar.at
+++ b/tests/m4sugar.at
@@ -757,12 +757,12 @@ autom4te: m4 failed with exit status: 1
 AT_CLEANUP
 
 
-## --------------- ##
-## m4_map{,_sep}.  ##
-## --------------- ##
+## --------------------- ##
+## m4_map{,all}{,_sep}.  ##
+## --------------------- ##
 
 AT_SETUP([m4@&address@hidden)
-AT_KEYWORDS([m4@&address@hidden)
+AT_KEYWORDS([m4@&address@hidden m4@&address@hidden m4@&address@hidden 
m4@&address@hidden)
 AT_KEYWORDS([m4@&address@hidden)
 
 AT_CHECK_M4SUGAR_TEXT([[dnl
@@ -770,8 +770,21 @@ m4_map([m4_count], [])
 m4_map([ m4_count], [[],
                      [[1]],
                      [[1], [2]]])
+m4_mapall([ m4_count], [[],
+                        [[1]],
+                        [[1], [2]]])
 m4_map_sep([m4_eval], [,], [[[1+2]],
                             [[10], [16]]])
+m4_count(m4_map_sep([m4_echo], [,], [[], [[1]], [[2]]]))
+m4_count(m4_mapall_sep([m4_echo], [,], [[], [[1]], [[2]]]))
+m4_map_sep([m4_eval], [[,]], [[[1+2]],
+                              [[10], [16]]])
+m4_count(m4_map_sep([m4_echo], [[,]], [[], [[1]], [[2]]]))
+m4_count(m4_mapall_sep([m4_echo], [[,]], [[], [[1]], [[2]]]))
+m4_map([-], [[]])
+m4_mapall([-], [[]])
+m4_map_sep([-], [:], [[]])
+m4_mapall_sep([-], [:], [[]])
 m4_define([a], [m4_if([$#], [0], [oops], [$1], [a], [pass], [oops])])dnl
 m4_define([a1], [oops])dnl
 m4_define([pass1], [oops])dnl
@@ -779,8 +792,18 @@ m4_map([a], [[[a]]])1
 m4_map([m4_unquote([a])], [m4_dquote([a])])
 ]],
 [[
+ 1 2
  0 1 2
 3,a
+2
+3
+3,a
+1
+1
+
+-
+
+-
 pass1
 pass
 ]], [])
-- 
1.5.6.4


reply via email to

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