-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
In trying to synchronize the branch and head documentation of forloop, I
decided it was better to do an overhaul on the section. In particular, I
took to heart the previous wording that 'improving the forloop macro is an
exercise for the reader', by adding some of those improvements in the
Answers section.
This patch is against the branch, but I will be applying a similar one to
head.
For now, the new foreach section is a placeholder. I still intend to
document foreach in 1.4.x (particularly since comments in autoconf's
lib/m4sugar/m4sugar.m4 discuss how m4's documentation had flaws, then
presented a quadratic instead of linear algorithm in an attempt to show
how to overcome the flaws - I intend to make the m4 documentation point
out the flaws of a quadratic algorithm, and present the lessons learned
from autoconf's foreach; fortunately, autoconf 2.60 no longer uses a
quadratic algorithm).
2006-10-18 Eric Blake <[EMAIL PROTECTED]>
* examples/forloop.m4: Simplify.
* examples/forloop2.m4: New file.
* examples/quote.m4: New file.
* doc/m4.texinfo (Loops): Rename to...
(Shift): ...this node.
(Forloop, Foreach, Improved forloop, Improved foreach): New
nodes.
- --
Life is short - so eat dessert first!
Eric Blake [EMAIL PROTECTED]
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2.1 (Cygwin)
Comment: Public key at home.comcast.net/~ericblake/eblake.gpg
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
iD8DBQFFNi2p84KuGfSFAYARAhJjAKCOxWoUyK7DEfnkaOKm1nDeiouqjQCg0Cmd
YamBE5Uu1+rMbbtJusCTfpk=
=99A6
-----END PGP SIGNATURE-----
Index: doc/m4.texinfo
===================================================================
RCS file: /sources/m4/m4/doc/m4.texinfo,v
retrieving revision 1.1.1.1.2.88
diff -u -p -r1.1.1.1.2.88 m4.texinfo
--- doc/m4.texinfo 16 Oct 2006 13:15:32 -0000 1.1.1.1.2.88
+++ doc/m4.texinfo 18 Oct 2006 13:27:16 -0000
@@ -190,7 +190,9 @@ Conditionals, loops, and recursion
* Ifdef:: Testing if a macro is defined
* Ifelse:: If-else construct, or multibranch
-* Loops:: Loops and recursion in m4
+* Shift:: Recursion in @code{m4}
+* Forloop:: Iteration by counting
+* Foreach:: Iteration by list contents
How to debug macros and input
@@ -262,6 +264,8 @@ Compatibility with other versions of @co
Correct version of some examples
* Improved exch:: Solution for @code{exch}
+* Improved forloop:: Solution for @code{forloop}
+* Improved foreach:: Solution for @code{foreach}
* Improved cleardivert:: Solution for @code{cleardivert}
* Improved fatal_error:: Solution for @code{fatal_error}
@@ -1913,7 +1917,9 @@ something a number of times, or while so
@menu
* Ifdef:: Testing if a macro is defined
* Ifelse:: If-else construct, or multibranch
-* Loops:: Loops and recursion in m4
+* Shift:: Recursion in @code{m4}
+* Forloop:: Iteration by counting
+* Foreach:: Iteration by list contents
@end menu
@node Ifdef
@@ -2042,8 +2048,8 @@ Naturally, the normal case will be sligh
examples. A common use of @code{ifelse} is in macros implementing loops
of various kinds.
[EMAIL PROTECTED] Loops
[EMAIL PROTECTED] Loops and recursion
[EMAIL PROTECTED] Shift
[EMAIL PROTECTED] Recursion in @code{m4}
@cindex recursive macros
@cindex macros, recursive
@@ -2096,7 +2102,70 @@ reverse(`foo', `bar', `gnats', `and gnus
While not a very interesting macro, it does show how simple loops can be
made with @code{shift}, @code{ifelse} and recursion. It also shows
-that @code{shift} is usually used with @samp{$@@}.
+that @code{shift} is usually used with @samp{$@@}. Sometimes, a
+recursive algorithm requires adding quotes to each element:
+
[EMAIL PROTECTED] Composite quote (@dots{})
[EMAIL PROTECTED] Composite dquote (@dots{})
[EMAIL PROTECTED] Composite dquote_elt (@dots{})
+Takes any number of arguments, and add quoting. With @code{quote}, only
+one level of quoting is added, effectively removing whitespace after
+commas and turning multiple arguments into a single string. With
[EMAIL PROTECTED], two levels of quoting are added, one around each element,
+and one around the list. And with @code{dquote_elt}, two levels of
+quoting are added around each element.
[EMAIL PROTECTED] deffn
+
+An actual implementation of these three macros is distributed as
[EMAIL PROTECTED]@value{VERSION}/@/examples/@/quote.m4} in this package. First,
+let's examine their usage:
+
[EMAIL PROTECTED]
+include(`quote.m4')
[EMAIL PROTECTED]
+-quote-dquote-dquote_elt-
[EMAIL PROTECTED]
+-quote()-dquote()-dquote_elt()-
[EMAIL PROTECTED]'-`'-
+-quote(`1')-dquote(`1')-dquote_elt(`1')-
[EMAIL PROTECTED]'-`1'-
+-quote(`1', `2')-dquote(`1', `2')-dquote_elt(`1', `2')-
[EMAIL PROTECTED],2-`1',`2'-`1',`2'-
+define(`n', `$#')dnl
+-n(quote(`1', `2'))-n(dquote(`1', `2'))-n(dquote_elt(`1', `2'))-
[EMAIL PROTECTED]
+dquote(dquote_elt(`1', `2'))
[EMAIL PROTECTED]'',``2''
+dquote_elt(dquote(`1', `2'))
[EMAIL PROTECTED]',`2''
[EMAIL PROTECTED] example
+
+The last two lines show that when given two arguments, @code{dquote}
+results in one string, while @code{dquote_elt} results in two. Now,
+examine the implementation. Note that @code{quote} and
[EMAIL PROTECTED] make decisions based on their number of arguments, so
+that when called without arguments, they result in nothing instead of an
+empty string; this is so that it is possible to distinquish between no
+arguments and an empty first argument. @code{dquote}, on the other
+hand, results in a string no matter what, since it is still possible to
+tell whether it was invoked without arguments based on the resulting
+string.
+
[EMAIL PROTECTED]
+undivert(`quote.m4')dnl
[EMAIL PROTECTED](`-1')
[EMAIL PROTECTED] quote(args) - convert args to single-quoted string
[EMAIL PROTECTED](`quote', `ifelse(`$#', `0', `', ``$*'')')
[EMAIL PROTECTED] dquote(args) - convert args to quoted list of quoted strings
[EMAIL PROTECTED](`dquote', ``$@@'')
[EMAIL PROTECTED] dquote_elt(args) - convert args to list of double-quoted
strings
[EMAIL PROTECTED](`dquote_elt', `ifelse(`$#', `0', `', `$#', `1', ```$1''',
[EMAIL PROTECTED] ```$1'',$0(shift($@@))')')
[EMAIL PROTECTED]'dnl
[EMAIL PROTECTED] example
+
[EMAIL PROTECTED] Forloop
[EMAIL PROTECTED] Iteration by counting
@cindex for loops
@cindex loops, counting
@@ -2142,35 +2211,36 @@ internal macro @code{_forloop}, and re-e
the first argument.
The macro @code{_forloop} expands the fourth argument once, and tests
-to see if it is finished. If it has not finished, it increments
-the iteration variable (using the predefined macro @code{incr},
[EMAIL PROTECTED]), and recurses.
+to see if the iterator has reached the final value. If it has not
+finished, it increments the iterator (using the predefined macro
[EMAIL PROTECTED], @pxref{Incr}), and recurses.
-Here is the actual implementation of @code{forloop}, distributed as
+Here is an actual implementation of @code{forloop}, distributed as
@[EMAIL PROTECTED]/@/examples/@/forloop.m4} in this package:
@example
-undivert(`forloop.m4')
+undivert(`forloop.m4')dnl
@result{}divert(`-1')
[EMAIL PROTECTED] forloop(var, from, to, stmt)
[EMAIL PROTECTED](`forloop',
[EMAIL PROTECTED] `pushdef(`$1', `$2')_forloop(`$1', `$2', `$3',
`$4')popdef(`$1')')
[EMAIL PROTECTED] forloop(var, from, to, stmt) - simple version
[EMAIL PROTECTED](`forloop', `pushdef(`$1', `$2')_forloop($@@)popdef(`$1')')
@result{}define(`_forloop',
[EMAIL PROTECTED] `$4`'ifelse($1, `$3', ,
[EMAIL PROTECTED] `define(`$1', incr($1))_forloop(`$1', `$2', `$3', `$4')')')
[EMAIL PROTECTED] `$4`'ifelse($1, `$3', `', `define(`$1',
incr($1))$0($@@)')')
@result{}divert`'dnl
[EMAIL PROTECTED]
@end example
-Notice the careful use of quotes. Only three macro arguments are
+Notice the careful use of quotes. Certain macro arguments are left
unquoted, each for its own reason. Try to find out @emph{why} these
-three arguments are left unquoted, and see what happens if they are
-quoted.
+arguments are left unquoted, and see what happens if they are quoted.
+(As presented, these two macros are useful but not very robust for
+general use. They lack even basic error handling of cases like start
+value less than final value, or the first argument not being a name.
+See if you can improve these macros; or @pxref{Improved forloop, ,
+Answers}).
-Now, even though these two macros are useful, they are still not robust
-enough for general use. They lack even basic error handling of cases
-like start value less than final value, and the first argument not being
-a name. Correcting these errors are left as an exercise to the reader.
[EMAIL PROTECTED] Foreach
[EMAIL PROTECTED] Iteration by list contents
+
+FIXME - Fill out this section.
@node Debugging
@chapter How to debug macros and input
@@ -3952,7 +4022,7 @@ len(format(`%-*X', `300', `1'))
@result{}300
@end example
-Using the @code{forloop} macro defined earlier (@pxref{Loops}), this
+Using the @code{forloop} macro defined earlier (@pxref{Forloop}), this
example shows how @code{format} can be used to produce tabular output.
@example
@@ -5202,6 +5272,8 @@ presented here.
@menu
* Improved exch:: Solution for @code{exch}
+* Improved forloop:: Solution for @code{forloop}
+* Improved foreach:: Solution for @code{foreach}
* Improved cleardivert:: Solution for @code{cleardivert}
* Improved fatal_error:: Solution for @code{fatal_error}
@end menu
@@ -5224,6 +5296,54 @@ macro
@result{}expansion text
@end example
[EMAIL PROTECTED] Improved forloop
[EMAIL PROTECTED] Solution for @code{forloop}
+
+The @code{forloop} macro (@pxref{Forloop}) as presented earlier can go
+into an infinite loop if given an iterator that is not parsed as a macro
+name. It does not do any sanity checking on its numeric bounds, and
+only permits decimal numbers for bounds. Here is an improved version,
+shipped as @[EMAIL PROTECTED]/@/examples/@/forloop2.m4}; this
+version also optimizes based on the fact that the starting bound does
+not need to be passed to the helper @code{_forloop}.
+
[EMAIL PROTECTED]
+undivert(`forloop2.m4')dnl
[EMAIL PROTECTED](`-1')
[EMAIL PROTECTED] forloop(var, from, to, stmt) - improved version:
[EMAIL PROTECTED] works even if VAR is not a strict macro name
[EMAIL PROTECTED] performs sanity check that FROM is larger than TO
[EMAIL PROTECTED] allows complex numerical expressions in TO and FROM
[EMAIL PROTECTED](`forloop', `ifelse(eval(`($3) >= ($2)'), `1',
[EMAIL PROTECTED] `pushdef(`$1', eval(`$2'))_forloop(`$1',
[EMAIL PROTECTED] eval(`$3'), `$4')popdef(`$1')')')
[EMAIL PROTECTED](`_forloop',
[EMAIL PROTECTED] `$3`'ifelse(indir(`$1'), `$2', `',
[EMAIL PROTECTED] `define(`$1', incr(indir(`$1')))$0($@@)')')
[EMAIL PROTECTED]'dnl
+include(`forloop2.m4')
[EMAIL PROTECTED]
+forloop(`i', `2', `1', `no iteration occurs')
[EMAIL PROTECTED]
+forloop(`', `1', `2', ` odd iterator name')
[EMAIL PROTECTED] odd iterator name odd iterator name
+forloop(`i', `5 + 5', `0xc', ` 0x`'eval(i, `16')')
[EMAIL PROTECTED] 0xa 0xb 0xc
+forloop(`i', `a', `b', `non-numeric bounds')
[EMAIL PROTECTED]:stdin:6: bad expression in eval (bad input): (b) >= (a)
[EMAIL PROTECTED]
[EMAIL PROTECTED] example
+
+Of course, it is possible to make even more improvements, such as
+adding an optional step argument, or allowing iteration through
+descending sequences. @acronym{GNU} Autoconf provides some of these
+additional bells and whistles in its @code{m4_for} macro.
+
[EMAIL PROTECTED] Improved foreach
[EMAIL PROTECTED] Solution for @code{foreach}
+
+FIXME - add content.
+
@node Improved cleardivert
@section Solution for @code{cleardivert}
Index: examples/forloop.m4
===================================================================
RCS file: /sources/m4/m4/examples/forloop.m4,v
retrieving revision 1.1.1.1.2.1
diff -u -p -r1.1.1.1.2.1 forloop.m4
--- examples/forloop.m4 3 Jul 2006 13:16:19 -0000 1.1.1.1.2.1
+++ examples/forloop.m4 18 Oct 2006 13:27:16 -0000
@@ -1,8 +1,6 @@
divert(`-1')
-# forloop(var, from, to, stmt)
-define(`forloop',
- `pushdef(`$1', `$2')_forloop(`$1', `$2', `$3', `$4')popdef(`$1')')
+# forloop(var, from, to, stmt) - simple version
+define(`forloop', `pushdef(`$1', `$2')_forloop($@)popdef(`$1')')
define(`_forloop',
- `$4`'ifelse($1, `$3', ,
- `define(`$1', incr($1))_forloop(`$1', `$2', `$3', `$4')')')
+ `$4`'ifelse($1, `$3', `', `define(`$1', incr($1))$0($@)')')
divert`'dnl
Index: examples/forloop2.m4
===================================================================
RCS file: examples/forloop2.m4
diff -N examples/forloop2.m4
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ examples/forloop2.m4 18 Oct 2006 13:27:16 -0000
@@ -0,0 +1,12 @@
+divert(`-1')
+# forloop(var, from, to, stmt) - improved version:
+# works even if VAR is not a strict macro name
+# performs sanity check that FROM is larger than TO
+# allows complex numerical expressions in TO and FROM
+define(`forloop', `ifelse(eval(`($3) >= ($2)'), `1',
+ `pushdef(`$1', eval(`$2'))_forloop(`$1',
+ eval(`$3'), `$4')popdef(`$1')')')
+define(`_forloop',
+ `$3`'ifelse(indir(`$1'), `$2', `',
+ `define(`$1', incr(indir(`$1')))$0($@)')')
+divert`'dnl
Index: examples/quote.m4
===================================================================
RCS file: examples/quote.m4
diff -N examples/quote.m4
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ examples/quote.m4 18 Oct 2006 13:27:16 -0000
@@ -0,0 +1,9 @@
+divert(`-1')
+# quote(args) - convert args to single-quoted string
+define(`quote', `ifelse(`$#', `0', `', ``$*'')')
+# dquote(args) - convert args to quoted list of quoted strings
+define(`dquote', ``$@'')
+# dquote_elt(args) - convert args to list of double-quoted strings
+define(`dquote_elt', `ifelse(`$#', `0', `', `$#', `1', ```$1''',
+ ```$1'',$0(shift($@))')')
+divert`'dnl
_______________________________________________
M4-patches mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/m4-patches