* lib/m4sugar/m4sugar.m4 (m4_for): Use fewer macros.
(_m4_for): Take additional parameter, for fewer m4_indir calls.
* lib/m4sugar/foreach.m4 (_m4_foreach, _m4_shiftn, m4_do)
(m4_reverse, _m4_list_pad, _m4_list_cmp): Adjust all callers.
* doc/autoconf.texi (Looping constructs) <m4_for>: Document subtle
semantic change caused by the optimization.
* tests/m4sugar.at (M4 loops): Test the new semantics.
Signed-off-by: Eric Blake <ebb9@byu.net>
2008-07-29 Eric Blake <ebb9@byu.net>
+ Optimize m4_for.
+ * lib/m4sugar/m4sugar.m4 (m4_for): Use fewer macros.
+ (_m4_for): Take additional parameter, for fewer m4_indir calls.
+ * lib/m4sugar/foreach.m4 (_m4_foreach, _m4_shiftn, m4_do)
+ (m4_reverse, _m4_list_pad, _m4_list_cmp): Adjust all callers.
+ * doc/autoconf.texi (Looping constructs) <m4_for>: Document subtle
+ semantic change caused by the optimization.
+ * tests/m4sugar.at (M4 loops): Test the new semantics.
+
One more m4_list_cmp tweak.
* lib/m4sugar/m4sugar.m4 (_m4_list_cmp_1): Don't defer shift.
* lib/m4sugar/foreach.m4 (m4_list_cmp): Fix comment.
expand @var{expression} with the numeric value assigned to @var{var}.
If @var{step} is omitted, it defaults to @samp{1} or @samp{-1} depending
on the order of the limits. If given, @var{step} has to match this
-order.
+order. The number of iterations is determined independently from
+definition of @var{var}; iteration cannot be short-circuited or
+lengthened by modifying @var{var} from within @var{expression}.
@end defmac
@defmac m4_foreach (@var{var}, @var{list}, @var{expression})
[m4_if([$2], [], [], [_$0([$1], [$3], $2)])])
m4_define([_m4_foreach],
-[m4_define([$1], m4_pushdef([$1], [3])_m4_for([$1], [$#], [1],
- [$0_([1], [2], m4_indir([$1]))])[m4_popdef([$1])])m4_indir([$1], $@)])
+[m4_define([$1], m4_pushdef([$1])_m4_for([$1], [3], [$#], [1],
+ [$0_([1], [2], _m4_defn([$1]))])[m4_popdef([$1])])m4_indir([$1], $@)])
m4_define([_m4_foreach_],
[[m4_define([$$1], [$$3])$$2[]]])
# ,[$5],[$6],...,[$m]_m4_popdef([_m4_s])
# before calling m4_shift(_m4_s($@)).
m4_define([_m4_shiftn],
-[m4_define([_m4_s], m4_pushdef([_m4_s],
- m4_incr(m4_incr([$1])))_m4_for([_m4_s], [$#], [1],
- [[,]m4_dquote([$]_m4_s)])[_m4_popdef([_m4_s])])m4_shift(_m4_s($@))])
+[m4_define([_m4_s],
+ m4_pushdef([_m4_s])_m4_for([_m4_s], m4_eval([$1 + 2]), [$#], [1],
+ [[,]m4_dquote([$]_m4_s)])[_m4_popdef([_m4_s])])m4_shift(_m4_s($@))])
# m4_do(STRING, ...)
# ------------------
# Here, we use the temporary macro _m4_do, defined as
# $1$2...$n[]_m4_popdef([_m4_do])
m4_define([m4_do],
-[m4_define([_$0], m4_pushdef([_$0], [1])_m4_for([_$0], [$#], [1],
- [$][_$0])[[]_m4_popdef([_$0])])_$0($@)])
+[m4_define([_$0], m4_pushdef([_$0])_m4_for([_$0], [1], [$#], [1],
+ [$_$0])[[]_m4_popdef([_$0])])_$0($@)])
# m4_dquote_elt(ARGS)
# -------------------
# [$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_define([_m4_r], m4_dquote([$$#])m4_pushdef([_m4_r])_m4_for([_m4_r],
+ m4_decr([$#]), [1], [-1],
[[, ]m4_dquote([$]_m4_r)])[_m4_popdef([_m4_r])])_m4_r($@)])])
# then calls _m4_cmp([1+0], [0], [1], [2+0])
m4_define([m4_list_cmp],
[m4_if([$1], [$2], 0,
- [_$0($1+0_m4_list_pad(m4_count($1), m4_count($2)),
- $2+0_m4_list_pad(m4_count($2), m4_count($1)))])])
+ [m4_pushdef([_m4_size])_$0($1+0_m4_list_pad(m4_count($1), m4_count($2)),
+ $2+0_m4_list_pad(m4_count($2), m4_count($1)))])])
m4_define([_m4_list_pad],
-[m4_if(m4_eval($1 < $2), [1], [m4_for([], [$1 + 1], [$2], [], [,0])])])
+[m4_if(m4_eval($1 < $2), [1],
+ [_m4_for([_m4_size], m4_incr([$1]), [$2], [1], [,0])])])
m4_define([_m4_list_cmp],
-[m4_pushdef([_m4_size], m4_eval([$# >> 1]))]dnl
-[m4_define([_m4_cmp], m4_pushdef([_m4_cmp], [1])[m4_if(]_m4_for([_m4_cmp],
- _m4_size, [1], [$0_(_m4_cmp, m4_eval(_m4_cmp + _m4_size))])[
+[m4_define([_m4_size], m4_eval([$# >> 1]))]dnl
+[m4_define([_m4_cmp], m4_pushdef([_m4_cmp])[m4_if(]_m4_for([_m4_cmp],
+ [1], _m4_size, [1], [$0_(_m4_cmp, m4_eval(_m4_cmp + _m4_size))])[
[0]_m4_popdef([_m4_cmp], [_m4_size]))])_m4_cmp($@)])
m4_define([_m4_list_cmp_],
# m4_for(VARIABLE, FIRST, LAST, [STEP = +/-1], EXPRESSION)
# --------------------------------------------------------
# Expand EXPRESSION defining VARIABLE to FROM, FROM + 1, ..., TO with
-# increments of STEP.
-# Both limits are included, and bounds are checked for consistency.
-# The algorithm is robust to indirect VARIABLE names, and uses _m4_defn
-# where possible for speed.
+# increments of STEP. Both limits are included, and bounds are
+# checked for consistency. The algorithm is robust to indirect
+# VARIABLE names. Changing VARIABLE inside EXPRESSION will not impact
+# the number of iterations.
+#
+# Uses _m4_defn for speed, and avoid dnl in the macro body.
m4_define([m4_for],
-[m4_pushdef([$1], m4_eval([$2]))dnl
-m4_cond([m4_eval(([$3]) > _m4_defn([$1]))], 1,
-[m4_pushdef([_m4_step], m4_eval(m4_default([$4], 1)))dnl
-m4_assert(_m4_step > 0)dnl
-_m4_for([$1], m4_eval((([$3]) - _m4_defn([$1]))
- / _m4_step * _m4_step + _m4_defn([$1])),
- _m4_step, [$5])],
- [m4_eval(([$3]) < _m4_defn([$1]))], 1,
-[m4_pushdef([_m4_step], m4_eval(m4_default([$4], -1)))dnl
-m4_assert(_m4_step < 0)dnl
-_m4_for([$1], m4_eval((_m4_defn([$1]) - ([$3]))
- / -(_m4_step) * _m4_step + _m4_defn([$1])),
- _m4_step, [$5])],
- [m4_pushdef([_m4_step])dnl
-$5])[]dnl
-m4_popdef([_m4_step])dnl
-m4_popdef([$1])])
-
-
-# _m4_for(VARIABLE, LAST, STEP, EXPRESSION)
-# -----------------------------------------
-# Core of the loop, no consistency checks, all arguments are plain numbers.
+[m4_pushdef([$1], m4_eval([$2]))]dnl
+[m4_cond([m4_eval(([$3]) > ([$2]))], 1,
+ [m4_pushdef([_m4_step], m4_eval(m4_default([$4],
+ 1)))m4_assert(_m4_step > 0)_$0([$1], _m4_defn([$1]),
+ m4_eval((([$3]) - ([$2])) / _m4_step * _m4_step + ([$2])),
+ _m4_step, [$5])],
+ [m4_eval(([$3]) < ([$2]))], 1,
+ [m4_pushdef([_m4_step], m4_eval(m4_default([$4],
+ -1)))m4_assert(_m4_step < 0)_$0([$1], _m4_defn([$1]),
+ m4_eval((([$2]) - ([$3])) / -(_m4_step) * _m4_step + ([$2])),
+ _m4_step, [$5])],
+ [m4_pushdef([_m4_step])$5])[]]dnl
+[m4_popdef([_m4_step], [$1])])
+
+
+# _m4_for(VARIABLE, COUNT, LAST, STEP, EXPRESSION)
+# ------------------------------------------------
+# Core of the loop, no consistency checks, all arguments are plain
+# numbers. Define VARIABLE to COUNT, expand EXPRESSION, then alter
+# COUNT by STEP and iterate if COUNT is not LAST.
m4_define([_m4_for],
-[$4[]dnl
-m4_if(m4_defn([$1]), [$2], [],
- [m4_define([$1], m4_eval(m4_defn([$1])+[$3]))$0($@)])])
+[m4_define([$1], [$2])$5[]m4_if([$2], [$3], [],
+ [$0([$1], m4_eval([$2 + $4]), [$3], [$4], [$5])])])
# Implementing `foreach' loops in m4 is much more tricky than it may
m4_for([myvar], 8, 16, 3 * 2, [ myvar])
m4_for([myvar], 8, 16, -3 * -2, [ myvar])
m4_for([myvar], [2<<2], [2<<3], [-3 * (-2)], [ myvar])
+dnl Modifying var does not affect the number of iterations
+m4_for([myvar], 1, 5, , [ myvar[]m4_define([myvar], 5)])
dnl Make sure we can do nameless iteration
m4_for(, 1, 10, , -)
dnl foreach tests
8 14
8 14
8 14
+ 1 2 3 4 5
----------
a| b, c| d| e
| f|