* lib/m4sugar/m4sugar.m4 (m4_curry, _m4_curry): New macros.
* tests/m4sugar.at (m4@&t@_map_args): Rename...
(m4@&t@_map_args and m4@&t@_curry): ...and add currying tests.
* doc/autoconf.texi (Looping constructs) <m4_map_args>: Document
currying as a way to add parameters.
(Evaluation Macros) <m4_curry>: Document the new macro.
* NEWS: Likewise.
Signed-off-by: Eric Blake <ebb9@byu.net>
2008-10-17 Eric Blake <ebb9@byu.net>
+ Add m4_curry.
+ * lib/m4sugar/m4sugar.m4 (m4_curry, _m4_curry): New macros.
+ * tests/m4sugar.at (m4@&t@_map_args): Rename...
+ (m4@&t@_map_args and m4@&t@_curry): ...and add currying tests.
+ * doc/autoconf.texi (Looping constructs) <m4_map_args>: Document
+ currying as a way to add parameters.
+ (Evaluation Macros) <m4_curry>: Document the new macro.
+ * NEWS: Likewise.
+
Improve suggested test filtering.
* lib/m4sugar/m4sh.m4 (_AS_DETECT_SUGGESTED_PRUNE): New macro,
extracted from...
** Configure scripts now use shell functions.
** The following m4sugar macros are new:
- m4_default_quoted m4_map_args m4_map_args_pair m4_set_map
+ m4_curry m4_default_quoted m4_map_args m4_map_args_pair
+ m4_set_map
** The following documented m4sh macros are new:
AS_LINENO_PREPARE AS_ME_PREPARE AS_VAR_COPY
@defmac m4_map_args (@var{macro}, @var{arg}@dots{})
@msindex{map_args}
-Repeatedly invoke @var{macro} with each successive @var{arg} as its
+Repeatedly invoke @var{macro} with each successive @var{arg} as its only
argument. In the following example, three solutions are presented with
the same expansion; the solution using @code{m4_map_args} is the most
efficient.
m4_map_args([ m4_echo], [plain], [active])
@result{} plain active
@end example
+
+In cases where it is useful to operate on additional parameters besides
+the list elements, the macro @code{m4_curry} can be used in @var{macro}
+to supply the argument currying necessary to generate the desired
+argument list. In the following example, @code{list_add_n} is more
+efficient than @code{list_add_x}.
+
+@example
+m4_define([list], [[1], [2], [3]])dnl
+m4_define([add], [m4_eval(([$1]) + ([$2]))])dnl
+dnl list_add_n(N, ARG...)
+dnl Output a list consisting of each ARG added to N
+m4_define([list_add_n],
+[m4_shift(m4_map_args([,m4_curry([add], [$1])], m4_shift($@@)))])dnl
+list_add_n([1], list)
+@result{}2,3,4
+list_add_n([2], list)
+@result{}3,4,5
+m4_define([list_add_x],
+[m4_shift(m4_foreach([var], m4_dquote(m4_shift($@@)),
+ [,add([$1],m4_defn([var]))]))])dnl
+list_add_x([1], list)
+@result{}2,3,4
+@end example
@end defmac
@defmac m4_map_args_pair (@var{macro}, @dvar{macro-end, macro}, @
passed.
@end defmac
+@defmac m4_curry (@var{macro}, @var{arg}@dots{})
+@msindex{curry}
+This macro performs argument currying. The expansion of this macro is
+another macro name that expects exactly one argument; that argument is
+then appended to the @var{arg} list, and then @var{macro} is expanded
+with the resulting argument list.
+
+@example
+m4_curry([m4_curry], [m4_reverse], [1])([2])([3])
+@result{}3, 2, 1
+@end example
+@end defmac
+
@defmac m4_do (@var{arg}, @dots{})
@msindex{do}
This macro loops over its arguments and expands each @var{arg} in
m4_define([m4_count], [$#])
+# m4_curry(MACRO, ARG...)
+# -----------------------
+# Perform argument currying. The expansion of this macro is another
+# macro that takes exactly one argument, appends it to the end of the
+# original ARG list, then invokes MACRO. For example:
+# m4_curry([m4_curry], [m4_reverse], [1])([2])([3]) => 3, 2, 1
+# Not quite as practical as m4_incr, but you could also do:
+# m4_define([add], [m4_eval(([$1]) + ([$2]))])
+# m4_define([add_one], [m4_curry([add], [1])])
+# add_one()([2]) => 3
+m4_define([m4_curry], [$1(m4_shift($@,)_$0])
+m4_define([_m4_curry], [[$1])])
+
+
# m4_do(STRING, ...)
# ------------------
# This macro invokes all its arguments (in sequence, of course). It is
])# AT_CHECK_M4SUGAR_TEXT
-# Order of the tests:
-# - m4_warn
-#
-# - m4_require
-# uses warn/error code.
-#
-# - m4_split
-#
-# - m4_append
-#
-# - m4_join
-#
-# - m4_text_wrap
-# uses m4_split code.
-
## --------- ##
## m4_defn. ##
## --------- ##
AT_CLEANUP
-## --------------------- ##
-## m4_map_args{,_pair}. ##
-## --------------------- ##
+## ---------------------------------- ##
+## m4_map_args{,_pair} and m4_curry. ##
+## ---------------------------------- ##
-AT_SETUP([m4@&t@_map_args])
+AT_SETUP([m4@&t@_map_args and m4@&t@_curry])
AT_KEYWORDS([m4@&t@_map_args_pair m4@&t@_reverse])
+dnl First, make sure we can curry in isolation.
+AT_CHECK_M4SUGAR_TEXT(
+[[m4_curry([m4_echo])([1])
+m4_curry([m4_curry], [m4_reverse], [1])([2])([3])
+m4_define([add], [m4_eval(([$1]) + ([$2]))])dnl
+m4_define([add_one], [m4_curry([add], [1])])dnl
+add_one()([4])
+]],
+[[1
+3, 2, 1
+5
+]])
+
+dnl Now, check that we can map a list of arguments.
AT_CHECK_M4SUGAR_TEXT([[m4_define([active], [ACTIVE])dnl
m4_map_args([ m4_echo])
m4_map_args([ m4_echo], [plain], [active])
, 2, 1, 4, 3
]])
+dnl Finally, put the two concepts together, to show the real power of the API.
+AT_CHECK_M4SUGAR_TEXT(
+[[m4_define([add], [m4_eval(([$1]) + ([$2]))])dnl
+m4_define([list], [[-1], [0], [1]])dnl
+dnl list_add_n(value, arg...)
+dnl add VALUE to each ARG and output the resulting list
+m4_define([list_add_n],
+ [m4_shift(m4_map_args([,m4_curry([add], [$1])], m4_shift($@)))])
+list_add_n([1], list)
+list_add_n([2], list)
+]], [[
+0,1,2
+1,2,3
+]])
+
AT_CLEANUP