2008-07-29 Eric Blake <ebb9@byu.net>
+ Add linear m4_bpatsubsts for m4 1.4.x.
+ * lib/m4sugar/m4sugar.m4 (m4_bpatsubsts): Match documentation
+ about anchors, even for only one substitution.
+ * lib/m4sugar/foreach.m4 (_m4_bpatsubsts): New implementation.
+ * doc/autoconf.texi (Conditional constructs) <m4_bpatsubsts>:
+ Clarify behavior with regard to quoting.
+ * tests/m4sugar.at (recursion): Test scaling of m4_bpatsubsts.
+ (m4@&t@_bpatsubsts): New test.
+ * NEWS: Document the linear guarantee.
+
Tweak m4_do semantics.
* lib/m4sugar/m4sugar.m4 (m4_do): Don't concat final argument with
subsequent text.
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_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_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
the result of each step of the recursion remains as a quoted string.
However, it means that anchors (@samp{^} and @samp{$} in the @var{regex}
will line up with the extra quotations, and not the characters of the
-original string.
+original string. The overquoting is removed after the final
+substitution.
@end defmac
@defmac m4_case (@var{string}, @var{value-1}, @var{if-value-1}, @
m4_define([_m4_case_],
[[[$$1],[$$2],[$$3],]])
+# m4_bpatsubsts(STRING, RE1, SUBST1, RE2, SUBST2, ...)
+# ----------------------------------------------------
+# m4 equivalent of
+#
+# $_ = STRING;
+# s/RE1/SUBST1/g;
+# s/RE2/SUBST2/g;
+# ...
+#
+# m4_bpatsubsts already validated an odd number of arguments; we only
+# need to speed up _m4_bpatsubsts. To avoid nesting, we build the
+# temporary _m4_p:
+# m4_define([_m4_p], [$1])m4_define([_m4_p],
+# m4_bpatsubst(m4_dquote(_m4_defn([_m4_p])), [$2], [$3]))m4_define([_m4_p],
+# m4_bpatsubst(m4_dquote(_m4_defn([_m4_p])), [$4], [$5]))m4_define([_m4_p],...
+# m4_bpatsubst(m4_dquote(_m4_defn([_m4_p])), [$m-1], [$m]))m4_unquote(
+# _m4_defn([_m4_p]))[]_m4_popdef([_m4_p])
+m4_define([_m4_bpatsubsts],
+[m4_define([_m4_p], m4_pushdef([_m4_p])[m4_define([_m4_p],
+ [$1])]_m4_for([_m4_p], [3], [$#], [2], [$0_(m4_decr(_m4_p),
+ _m4_p)])[m4_unquote(_m4_defn([_m4_p]))[]_m4_popdef([_m4_p])])_m4_p($@)])
+
+m4_define([_m4_bpatsubsts_],
+[[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_bpatsubsts],
[m4_if([$#], 0, [m4_fatal([$0: too few arguments: $#])],
[$#], 1, [m4_fatal([$0: too few arguments: $#: $1])],
- [$#], 2, [m4_builtin([patsubst], [$1], [$2])],
+ [$#], 2, [m4_unquote(m4_builtin([patsubst], [[$1]], [$2]))],
+ [$#], 3, [m4_unquote(m4_builtin([patsubst], [[$1]], [$2], [$3]))],
[_$0($@m4_if(m4_eval($# & 1), 0, [,]))])])
m4_define([_m4_bpatsubsts],
[m4_if([$#], 2, [$1],
AT_CLEANUP
+## --------------- ##
+## m4_bpatsubsts. ##
+## --------------- ##
+
+AT_SETUP([m4@&t@_bpatsubsts])
+
+AT_CHECK_M4SUGAR_TEXT(
+[[m4_bpatsubsts([11], [^..$])
+m4_bpatsubsts([11], [\(.\)1], [\12])
+m4_bpatsubsts([11], [^..$], [], [1], [2])
+m4_bpatsubsts([11], [\(.\)1], [\12], [1], [3])
+m4_define([a], [oops])m4_define([AB], [good])dnl
+m4_bpatsubsts([abc], [a], [A], [b], [B], [c])
+m4_bpatsubsts([$1$*$@], [\$\*], [$#])
+]], [[11
+21
+22
+23
+good
+$1$#$@
+]])
+
+AT_CLEANUP
+
## ---------- ##
## M4 Loops. ##
## ---------- ##
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@_reverse])
+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])
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_list_cmp([0m4_for([i], [1], [10000], [], [,0])], [0])
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_divert_pop(0)
]])
0
0
0
+A
]])
AT_DATA_M4SUGAR([script.4s],
0
0
0
+A
m4_exit([0])])
m4_init
m4_divert_push(0)[]dnl
m4_list_cmp([0m4_for([i], [1], [10000], [], [,0])], [0])
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_divert_pop(0)
]])
0
0
0
+A
]])
AT_CLEANUP