autoconf-patches
[Top][All Lists]
Advanced

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

m4_argn


From: Eric Blake
Subject: m4_argn
Date: Wed, 22 Apr 2009 16:56:37 +0000 (UTC)
User-agent: Loom/3.14 (http://gmane.org/)

I noticed a recent patch in the bison testsuite that attempted to extract $10:
http://lists.gnu.org/archive/html/bison-patches/2009-04/msg00083.html

> +# Sigh.  Some M4's can't reference arg 10 directly.
> +m4_pushdef([arg10], m4_car(m4_shiftn(9, $@)))

This is O(n) extraction, since m4_shiftn is O(n); extracting m elements then 
gives O(m*n) complexity for quadratic scaling.

However, in reality m4sugar requires GNU m4, which in turn guarantees access to 
$10 unless you have enabled traditional mode (in a future m4 version, 
POSIXLY_CORRECT will enable traditional mode, but that breaks enough else in 
m4sugar that I need to fix m4/m4.m4 to detect whether 'm4 -g' works, and if so, 
teach autom4te to use it.  But that's a separate patch.)  So I proposed adding 
m4_argn in that thread.  Plus, this has the benefit of O(1) argument 
extraction, or O(m) complexity when extracting m arguments.

A future version of m4 will support ${10} as the Posix-sanctioned extension for 
referencing the tenth argument, and issue a warning about $10 (since it 
conflicts with Posix); but when that future event happens, I will either add a 
debugmode flag in m4, or make m4_init in m4sugar handle the difference in 
syntax (similar to how we already handle whether new enough m4 is able to warn 
on popdef(undefined)).

I will be pushing this soon.



From: Eric Blake <address@hidden>
Date: Wed, 22 Apr 2009 10:43:51 -0600
Subject: [PATCH] Add m4_argn.

* lib/m4sugar/m4sugar.m4 (m4_argn): New macro.
* NEWS: Document it.
* doc/autoconf.texi (Looping constructs) <m4_argn>: Likewise.
<m4_car, m4_cdr>: Improve documentation.
* tests/m4sugar.at (m4 lists): New test.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog              |    9 ++++++++
 NEWS                   |    3 +-
 doc/autoconf.texi      |   27 ++++++++++++++++--------
 lib/m4sugar/m4sugar.m4 |   29 ++++++++++++++++++++------
 tests/m4sugar.at       |   52 ++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 103 insertions(+), 17 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 943a6cf..a8a108d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2009-04-22  Eric Blake  <address@hidden>
+
+       Add m4_argn.
+       * lib/m4sugar/m4sugar.m4 (m4_argn): New macro.
+       * NEWS: Document it.
+       * doc/autoconf.texi (Looping constructs) <m4_argn>: Likewise.
+       <m4_car, m4_cdr>: Improve documentation.
+       * tests/m4sugar.at (m4 lists): New test.
+
 2009-04-21  Eric Blake  <address@hidden>

        Shuffle maintainer-specific rules.
diff --git a/NEWS b/NEWS
index 60a6e20..107a10c 100644
--- a/NEWS
+++ b/NEWS
@@ -19,7 +19,8 @@ GNU Autoconf NEWS - User visible changes.
 ** The autotest macro AT_CHECK_NOESCAPE is now documented.

 ** The following documented m4sugar macros are new:
-   m4_default_nblank  m4_default_nblank_quoted  m4_ifblank  m4_ifnblank
+   m4_argn  m4_default_nblank  m4_default_nblank_quoted  m4_ifblank
+   m4_ifnblank

 * Major changes in Autoconf 2.63b (2009-03-31) [beta]
   Released by Eric Blake, based on git versions 2.63.*.
diff --git a/doc/autoconf.texi b/doc/autoconf.texi
index e6fd489..10fc283 100644
--- a/doc/autoconf.texi
+++ b/doc/autoconf.texi
@@ -11144,26 +11144,35 @@ Looping constructs
 @result{}123
 @end example

address@hidden m4_car (@var{list})
address@hidden m4_argn (@var{n}, @address@hidden)
address@hidden
+Extracts argument @var{n} (larger than 0) from the remaining arguments.
+If there are too few arguments, the empty string is used.  For any
address@hidden besides 1, this is more efficient than the similar
address@hidden(m4_shiftn(address@hidden, [], address@hidden@dots{}]))}.
address@hidden defmac
+
address@hidden m4_car (@address@hidden)
 @msindex{car}
-Expands to the quoted first element of the comma-separated quoted
address@hidden  Often used with @code{m4_cdr} to recursively iterate
+Expands to the quoted first @var{arg}.  Can be used with @code{m4_cdr}
+to recursively iterate
 through a list.  Generally, when using quoted lists of quoted elements,
 @code{m4_car} should be called without any extra quotes.
 @end defmac

address@hidden m4_cdr (@var{list})
address@hidden m4_cdr (@address@hidden)
 @msindex{cdr}
-Expands to a quoted list of all but the first element of the
-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.
+Expands to a quoted list of all but the first @var{arg}, or the empty
+string if there was only one argument.  Generally, when using quoted
+lists of quoted elements, @code{m4_cdr} should be called without any
+extra quotes.

 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 repeats 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}).
+involved, to gain some speed and share code with @code{m4_map_sep}, and
+also to avoid expanding side effects in @samp{$2} twice).
 @example
 m4_define([m4_map], [m4_ifval([$2],
   [m4_apply([$1], m4_car($2))[]$0([$1], m4_cdr($2))])])dnl
diff --git a/lib/m4sugar/m4sugar.m4 b/lib/m4sugar/m4sugar.m4
index 420fd99..f5a1f1c 100644
--- a/lib/m4sugar/m4sugar.m4
+++ b/lib/m4sugar/m4sugar.m4
@@ -454,20 +454,35 @@ m4_define([m4_bmatch],
        [m4_if(m4_bregexp([$1], [$2]), -1, [$0([$1], m4_shift3($@))],
              [$3])])])

+# m4_argn(N, ARGS...)
+# -------------------
+# Extract argument N (greater than 0) from ARGS.  Example:
+#   m4_define([b], [B])
+#   m4_argn([2], [a], [b], [c]) => b
+#
+# Rather than using m4_car(m4_shiftn([$1], $@)), we exploit the fact that
+# GNU m4 can directly reference any argument, through an indirect macro.
+m4_define([m4_argn],
+[m4_assert([0 < $1])]dnl
+[m4_pushdef([_$0], [_m4_popdef([_$0])]m4_dquote([$]m4_incr([$1])))_$0($@)])

-# m4_car(LIST)
-# m4_cdr(LIST)
-# ------------
-# Manipulate m4 lists.
+
+# m4_car(ARGS...)
+# m4_cdr(ARGS...)
+# ---------------
+# Manipulate m4 lists.  m4_car returns the first argument.  m4_cdr
+# bundles all but the first argument into a quoted list.  These two
+# macros are generally used with list arguments, with quoting removed
+# to break the list into multiple m4 ARGS.
 m4_define([m4_car], [[$1]])
 m4_define([m4_cdr],
 [m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
        [$#], 1, [],
        [m4_dquote(m4_shift($@))])])

-# _m4_cdr(LIST)
-# -------------
-# Like m4_cdr, except include a leading comma unless only one element
+# _m4_cdr(ARGS...)
+# ----------------
+# Like m4_cdr, except include a leading comma unless only one argument
 # remains.  Why?  Because comparing a large list against [] is more
 # expensive in expansion time than comparing the number of arguments; so
 # _m4_cdr can be used to reduce the number of arguments when it is time
diff --git a/tests/m4sugar.at b/tests/m4sugar.at
index 2dd81dc..bf56c4d 100644
--- a/tests/m4sugar.at
+++ b/tests/m4sugar.at
@@ -787,6 +787,58 @@ AT_CLEANUP


 ## ---------- ##
+## m4 lists.  ##
+## ---------- ##
+
+AT_SETUP([m4 lists])
+
+AT_KEYWORDS([m4@&address@hidden m4@&address@hidden m4@&address@hidden 
_m4@&t_cdr])
+
+AT_CHECK_M4SUGAR_TEXT([[dnl
+m4_define([a], [A])m4_define([b], [B])m4_define([c], [C])
+m4_argn([1], [a], [b], [c])
+m4_argn([2], [a], [b], [c])
+m4_argn([3], [a], [b], [c])
+m4_argn([4], [a], [b], [c])
+m4_car([a], [b], [c])
+m4_cdr([a], [b], [c])
+m4_cdr([a], [b])
+m4_cdr([a])
+_m4_cdr([a], [b], [c])
+_m4_cdr([a], [b])
+_m4_cdr([a])
+m4_if(m4_cdr([], []), [[]], [good], [bad])
+m4_if(m4_cdr([]), [], [good], [bad])
+]], [[
+a
+b
+c
+
+a
+[b],[c]
+[b]
+
+, [b],[c]
+, [b]
+
+good
+good
+]])
+
+AT_DATA_M4SUGAR([script.4s],
+[[m4_init
+m4_argn([0], [a], [b], [c])
+]])
+AT_CHECK_M4SUGAR([-o-], [1], [],
+[[script.4s:2: error: assert failed: 0 < 0
+script.4s:2: the top level
+autom4te: m4 failed with exit status: 1
+]])
+
+AT_CLEANUP
+
+
+## ---------- ##
 ## m4_split.  ##
 ## ---------- ##

-- 
1.6.1.2







reply via email to

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