* lib/m4sugar/m4sugar.m4 (m4_cond): Split into...
(_m4_cond): ...this, for fewer macros per iteration.
* lib/m4sugar/foreach.m4 (_m4_cond): New implementation.
* tests/m4sugar.at (recursion): Test it.
* NEWS: Document the linear guarantee.
Signed-off-by: Eric Blake <ebb9@byu.net>
2008-08-06 Eric Blake <ebb9@byu.net>
+ Add linear m4_cond for m4 1.4.x.
+ * lib/m4sugar/m4sugar.m4 (m4_cond): Split into...
+ (_m4_cond): ...this, for fewer macros per iteration.
+ * lib/m4sugar/foreach.m4 (_m4_cond): New implementation.
+ * tests/m4sugar.at (recursion): Test it.
+ * NEWS: Document the linear guarantee.
+
Speed up diversion handling.
* lib/m4sugar/m4sugar.m4 (m4_divert, m4_divert_push)
(m4_divert_pop, m4_divert_text): Avoid dnl for fewer macro
previously had linear scaling with m4 1.6 but quadratic scaling
when using m4 1.4.x. All macros built on top of these also gain
the scaling improvements.
- m4_bpatsubsts m4_case m4_do m4_dquote_elt m4_foreach m4_join
- m4_list_cmp m4_map m4_map_sep m4_max m4_min m4_shiftn
+ m4_bpatsubsts m4_case m4_cond m4_do m4_dquote_elt m4_foreach
+ m4_join m4_list_cmp m4_map m4_map_sep m4_max m4_min m4_shiftn
** AT_KEYWORDS once again performs expansion on its argument, such that
AT_KEYWORDS([m4_if([$1], [], [default])]) no longer complains about
m4_define([_m4_case_],
[[[$$1],[$$2],[$$3],]])
+# m4_cond(TEST1, VAL1, IF-VAL1, TEST2, VAL2, IF-VAL2, ..., [DEFAULT])
+# -------------------------------------------------------------------
+# Similar to m4_if, except that each TEST is expanded when encountered.
+# If the expansion of TESTn matches the string VALn, the result is IF-VALn.
+# The result is DEFAULT if no tests passed. This macro allows
+# short-circuiting of expensive tests, where it pays to arrange quick
+# filter tests to run first.
+#
+# m4_cond already guarantees either 3*n or 3*n + 1 arguments, 1 <= n.
+# We only have to speed up _m4_cond, by building the temporary _m4_c:
+# m4_define([_m4_c], _m4_defn([m4_unquote]))_m4_c([m4_if(($1), [($2)],
+# [$3[]m4_define([_m4_c])])])_m4_c([m4_if(($4), [($5)],
+# [$6[]m4_define([_m4_c])])])..._m4_c([m4_if(($m-2), [($m-1)],
+# [$m[]m4_define([_m4_c])])])_m4_c([$m+1]_m4_popdef([_m4_c]))
+m4_define([_m4_cond],
+[m4_define([_m4_c], m4_pushdef([_m4_c])[m4_define([_m4_c],
+ _m4_defn([m4_unquote]))]_m4_for([_m4_c], [2], m4_eval([$# / 3 * 3 - 1]), [3],
+ [$0_(m4_decr(_m4_c), _m4_c, m4_incr(_m4_c))])[_m4_c(]m4_dquote(
+ [$]m4_eval([$# / 3 * 3 + 1]))[_m4_popdef([_m4_c]))])_m4_c($@)])
+
+m4_define([_m4_cond_],
+[[_m4_c([m4_if(($$1), [($$2)], [$$3[]m4_define([_m4_c])])])]])
+
# m4_bpatsubsts(STRING, RE1, SUBST1, RE2, SUBST2, ...)
# ----------------------------------------------------
# m4 equivalent of
[[m4_define([_m4_p],
m4_bpatsubst(m4_dquote(_m4_defn([_m4_p])), [$$1], [$$2]))]])
-
# m4_shiftn(N, ...)
# -----------------
# Returns ... shifted N times. Useful for recursive "varargs" constructs.
m4_define([m4_cond],
[m4_if([$#], [0], [m4_fatal([$0: cannot be called without arguments])],
[$#], [1], [$1],
- [$#], [2], [m4_fatal([$0: missing an argument])],
- [m4_if($1, [$2], [$3], [$0(m4_shift3($@))])])])
+ m4_eval([$# % 3]), [2], [m4_fatal([$0: missing an argument])],
+ [_$0($@)])])
+
+m4_define([_m4_cond],
+[m4_if(($1), [($2)], [$3],
+ [$#], [3], [],
+ [$#], [4], [$4],
+ [$0(m4_shift3($@))])])
## ---------------------------------------- ##
AT_SETUP([recursion])
-AT_KEYWORDS([m4@&t@_foreach m4@&t@_foreach_w m4@&t@_case m4@&t@_bpatsubsts
-m4@&t@_shiftn m4@&t@_do m4@&t@_dquote_elt m4@&t@_reverse m4@&t@_map
-m4@&t@_join m4@&t@_joinall m4@&t@_list_cmp m4@&t@_max m4@&t@_min])
+AT_KEYWORDS([m4@&t@_foreach m4@&t@_foreach_w m4@&t@_case m4@&t@_cond
+m4@&t@_bpatsubsts m4@&t@_shiftn m4@&t@_do m4@&t@_dquote_elt m4@&t@_reverse
+m4@&t@_map m4@&t@_join m4@&t@_joinall m4@&t@_list_cmp m4@&t@_max m4@&t@_min])
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,
m4_for([i], [1], [10000], [], [m4_define(i)])dnl
m4_undefine(1m4_for([i], [2], [10000], [], [,i]))dnl
m4_bpatsubsts([a1]m4_for([i], [1], [10000], [], [,i]), [a2], [A])
+m4_define([up], [m4_define([$1], m4_incr($1))$1])m4_define([j], 0)dnl
+m4_cond(m4_for([i], [1], [10000], [], [[up([j])], [9990], i,]) [oops]) j
m4_divert_pop(0)
]])
0
0
A
+9990 9990
]])
AT_DATA_M4SUGAR([script.4s],
0
0
A
+9990 9990
m4_exit([0])])
m4_init
m4_divert_push(0)[]dnl
m4_for([i], [1], [10000], [], [m4_define(i)])dnl
m4_undefine(1m4_for([i], [2], [10000], [], [,i]))dnl
m4_bpatsubsts([a1]m4_for([i], [1], [10000], [], [,i]), [a2], [A])
+m4_define([up], [m4_define([$1], m4_incr($1))$1])m4_define([j], 0)dnl
+m4_cond(m4_for([i], [1], [10000], [], [[up([j])], [9990], i,]) [oops])
m4_divert_pop(0)
]])
0
0
A
+9990 9990
]])
AT_CLEANUP