* 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 <ebb9@byu.net>
+2009-04-22 Eric Blake <ebb9@byu.net>
+
+ 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-22 Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
Improve description of AC_PROG_CC_C89 and AC_PROG_CC_C99.
** 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.*.
@result{}123
@end example
-@defmac m4_car (@var{list})
+@defmac m4_argn (@var{n}, @ovar{arg}@dots{})
+@msindex{argn}
+Extracts argument @var{n} (larger than 0) from the remaining arguments.
+If there are too few arguments, the empty string is used. For any
+@var{n} besides 1, this is more efficient than the similar
+@samp{m4_car(m4_shiftn([@var{n}], [], [@var{arg}@dots{}]))}.
+@end defmac
+
+@defmac m4_car (@var{arg}@dots{})
@msindex{car}
-Expands to the quoted first element of the comma-separated quoted
-@var{list}. 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
-@defmac m4_cdr (@var{list})
+@defmac m4_cdr (@var{arg}@dots{})
@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
[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
AT_CLEANUP
+## ---------- ##
+## m4 lists. ##
+## ---------- ##
+
+AT_SETUP([m4 lists])
+
+AT_KEYWORDS([m4@&t@_car m4@&t@_cdr m4@&t@_argn _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. ##
## ---------- ##