* lib/m4sugar/m4sugar.m4 (m4_reverse): New macro.
(m4_list_cmp): Rewrite to give linear behavior with M4 1.6 on an
m4_reverse'd list.
* lib/m4sugar/foreach.m4 (m4_reverse): Add the M4 1.4.x
counterpart.
* tests/m4sugar.at (recursion): Test it.
* doc/autoconf.texi (Evaluation Macros) <m4_reverse>: Document
it.
(Text processing Macros) <m4_append>: Cross-reference to m4_set.
* NEWS: Mention new macro.
Signed-off-by: Eric Blake <ebb9@byu.net>
+2008-07-29 Eric Blake <ebb9@byu.net>
+
+ Add m4_reverse, and improve m4_list_cmp.
+ * lib/m4sugar/m4sugar.m4 (m4_reverse): New macro.
+ (m4_list_cmp): Rewrite to give linear behavior with M4 1.6 on an
+ m4_reverse'd list.
+ * lib/m4sugar/foreach.m4 (m4_reverse): Add the M4 1.4.x
+ counterpart.
+ * tests/m4sugar.at (recursion): Test it.
+ * doc/autoconf.texi (Evaluation Macros) <m4_reverse>: Document
+ it.
+ (Text processing Macros) <m4_append>: Cross-reference to m4_set.
+ * NEWS: Mention new macro.
+
2008-07-28 Eric Blake <ebb9@byu.net>
Avoid _m4_shiftn for m4 1.6 speedup.
allowing the output of unbalanced parantheses in more contexts.
** The following m4sugar macros are new:
- m4_joinall m4_set_add m4_set_add_all m4_set_contains
+ 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
although it loses whitespace after unquoted commas in the process.
@end defmac
+@defmac m4_reverse (@var{arg}, @dots{})
+@msindex{reverse}
+Outputs each argument with the same level of quoting, but in reverse
+order, and with space following each comma for readability.
+
+@example
+m4_define([active], [ACT,IVE])
+@result{}
+m4_reverse(active, [active])
+@result{}active, IVE, ACT
+@end example
+@end defmac
+
@defmac m4_unquote (@var{arg}, @dots{})
@msindex{unquote}
This macro was introduced in Autoconf 2.62. Expand each argument,
string, depending on the quality of the underlying M4 implementation,
while @code{m4_append_uniq} has an inherent quadratic scaling factor.
If an algorithm can tolerate duplicates in the final string, use the
-former for speed.
+former for speed. If duplicates must be avoided, consider using
+@code{m4_set_add} instead (@pxref{Set manipulation Macros}).
@example
m4_define([active], [ACTIVE])dnl
m4_define([m4_dquote_elt],
[m4_shift(m4_foreach([_m4_elt], [$@], [,m4_dquote(_m4_defn([_m4_elt]))]))])
+# m4_reverse(ARGS)
+# ----------------
+# Output ARGS in reverse order.
+#
+# Invoke _m4_r($@) with the temporary _m4_r built as
+# [$m], [$m-1], ..., [$2], [$1]_m4_popdef([_m4_r])
+m4_define([m4_reverse],
+[m4_if([$#], [0], [], [$#], [1], [[$1]],
+[m4_define([_m4_r], m4_dquote([$$#])m4_pushdef([_m4_r],
+ m4_decr([$#]))_m4_for([_m4_r], [1], [-1],
+ [[, ]m4_dquote([$]_m4_r)])[_m4_popdef([_m4_r])])_m4_r($@)])])
+
+
# m4_map(MACRO, LIST)
# -------------------
# Invoke MACRO($1), MACRO($2) etc. where $1, $2... are the elements
[m4_if([$#], [0], [], [[$*]])])
+# m4_reverse(ARGS)
+# ----------------
+# Output ARGS in reverse order.
+m4_define([m4_reverse],
+[m4_if([$#], [0], [], [$#], [1], [[$1]],
+ [$0(m4_shift($@)), [$1]])])
+
+
# m4_unquote(ARGS)
# ----------------
# Remove one layer of quotes from each ARG, performing one level of
# expansion includes the name of the macro to invoke on the tail, either
# m4_ignore or m4_unquote. This is particularly useful when comparing
# long lists, since less text is being expanded for deciding when to end
-# recursion.
+# recursion. The recursion is between a pair of macros that alternate
+# which list is trimmed by one element; this is more efficient than
+# calling m4_cdr on both lists from a single macro.
m4_define([m4_list_cmp],
-[m4_if([$1$2], [], 0,
- [$1], [], [$0(0, [$2])],
- [$2], [], [$0([$1], 0)],
- [$1], [$2], 0,
- [_$0(m4_cmp(m4_car($1), m4_car($2)))([$0(m4_cdr($1), m4_cdr($2))])])])
+[m4_if([$1], [$2], [0], [_m4_list_cmp_1([$1], $2)])])
+
m4_define([_m4_list_cmp],
-[m4_if([$1], 0, [m4_unquote], [$1m4_ignore])])
+[m4_if([$1], [], [0m4_ignore], [$2], [0], [m4_unquote], [$2m4_ignore])])
+
+m4_define([_m4_list_cmp_1],
+[_m4_list_cmp_2([$2], [m4_shift2($@)], $1)])
+
+m4_define([_m4_list_cmp_2],
+[_m4_list_cmp([$1$3], m4_cmp([$3+0], [$1+0]))(
+ [_m4_list_cmp_1(m4_dquote(m4_shift3($@)), $2)])])
# m4_max(EXPR, ...)
# m4_min(EXPR, ...)
AT_SETUP([recursion])
AT_KEYWORDS([m4@&t@_foreach m4@&t@_foreach_w m4@&t@_shiftn m4@&t@_dquote_elt
-m4@&t@_join m4@&t@_joinall m4@&t@_list_cmp m4@&t@_max m4@&t@_min])
+m4@&t@_join m4@&t@_joinall m4@&t@_list_cmp m4@&t@_max m4@&t@_min
+m4@&t@_reverse])
dnl This test completes in a reasonable time if m4_foreach is linear,
dnl but thrashes if it is quadratic. If we are testing with m4 1.4.x,
[,i]))m4_for([i], [2], [10000], [], [,i]))
m4_case([10000]m4_for([i], [1], [10000], [], [,i]),[end])
m4_list_cmp(m4_dquote(1m4_for([i], [2], [10000], [], [,i])),
- m4_dquote(1m4_for([i], [2], [10000], [], [,i]), [0]))
+ m4_dquote(m4_reverse(10000m4_for([i], [9999], [1], [], [,i])), [0]))
m4_for([i], [1], [10000], [], [m4_define(i)])dnl
m4_undefine(1m4_for([i], [2], [10000], [], [,i]))dnl
m4_divert_pop(0)
[,i]))m4_for([i], [2], [10000], [], [,i]))
m4_case([10000]m4_for([i], [1], [10000], [], [,i]),[end])
m4_list_cmp(m4_dquote(1m4_for([i], [2], [10000], [], [,i])),
- m4_dquote(1m4_for([i], [2], [10000], [], [,i]), [0]))
+ m4_dquote(m4_reverse(10000m4_for([i], [9999], [1], [], [,i])), [0]))
m4_for([i], [1], [10000], [], [m4_define(i)])dnl
m4_undefine(1m4_for([i], [2], [10000], [], [,i]))dnl
m4_divert_pop(0)