m4-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

branch-1_4 forloop documentation


From: Eric Blake
Subject: branch-1_4 forloop documentation
Date: Wed, 18 Oct 2006 07:35:37 -0600
User-agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Thunderbird/1.5.0.7 Mnenhy/0.7.4.666

-----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  <address@hidden>

        * 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             address@hidden
-----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.
 
address@hidden Loops
address@hidden Loops and recursion
address@hidden Shift
address@hidden 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:
+
address@hidden Composite quote (@dots{})
address@hidden Composite dquote (@dots{})
address@hidden 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
address@hidden, 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.
address@hidden deffn
+
+An actual implementation of these three macros is distributed as
address@hidden@value{VERSION}/@/examples/@/quote.m4} in this package.  First,
+let's examine their usage:
+
address@hidden
+include(`quote.m4')
address@hidden
+-quote-dquote-dquote_elt-
address@hidden
+-quote()-dquote()-dquote_elt()-
address@hidden'-`'-
+-quote(`1')-dquote(`1')-dquote_elt(`1')-
address@hidden'-`1'-
+-quote(`1', `2')-dquote(`1', `2')-dquote_elt(`1', `2')-
address@hidden,2-`1',`2'-`1',`2'-
+define(`n', `$#')dnl
+-n(quote(`1', `2'))-n(dquote(`1', `2'))-n(dquote_elt(`1', `2'))-
address@hidden
+dquote(dquote_elt(`1', `2'))
address@hidden'',``2''
+dquote_elt(dquote(`1', `2'))
address@hidden',`2''
address@hidden 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
address@hidden 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.
+
address@hidden
+undivert(`quote.m4')dnl
address@hidden(`-1')
address@hidden quote(args) - convert args to single-quoted string
address@hidden(`quote', `ifelse(`$#', `0', `', ``$*'')')
address@hidden dquote(args) - convert args to quoted list of quoted strings
address@hidden(`dquote', ``$@@'')
address@hidden dquote_elt(args) - convert args to list of double-quoted strings
address@hidden(`dquote_elt', `ifelse(`$#', `0', `', `$#', `1', ```$1''',
address@hidden                             ```$1'',$0(shift($@@))')')
address@hidden'dnl
address@hidden example
+
address@hidden Forloop
address@hidden 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},
address@hidden), 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
address@hidden, @pxref{Incr}), and recurses.
 
-Here is the actual implementation of @code{forloop}, distributed as
+Here is an actual implementation of @code{forloop}, distributed as
 @address@hidden/@/examples/@/forloop.m4} in this package:
 
 @example
-undivert(`forloop.m4')
+undivert(`forloop.m4')dnl
 @result{}divert(`-1')
address@hidden forloop(var, from, to, stmt)
address@hidden(`forloop',
address@hidden  `pushdef(`$1', `$2')_forloop(`$1', `$2', `$3', 
`$4')popdef(`$1')')
address@hidden forloop(var, from, to, stmt) - simple version
address@hidden(`forloop', `pushdef(`$1', `$2')_forloop($@@)popdef(`$1')')
 @result{}define(`_forloop',
address@hidden  `$4`'ifelse($1, `$3', ,
address@hidden    `define(`$1', incr($1))_forloop(`$1', `$2', `$3', `$4')')')
address@hidden       `$4`'ifelse($1, `$3', `', `define(`$1', 
incr($1))$0($@@)')')
 @result{}divert`'dnl
address@hidden
 @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.
address@hidden Foreach
address@hidden 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
 
address@hidden Improved forloop
address@hidden 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 @address@hidden/@/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}.
+
address@hidden
+undivert(`forloop2.m4')dnl
address@hidden(`-1')
address@hidden forloop(var, from, to, stmt) - improved version:
address@hidden   works even if VAR is not a strict macro name
address@hidden   performs sanity check that FROM is larger than TO
address@hidden   allows complex numerical expressions in TO and FROM
address@hidden(`forloop', `ifelse(eval(`($3) >= ($2)'), `1',
address@hidden  `pushdef(`$1', eval(`$2'))_forloop(`$1',
address@hidden    eval(`$3'), `$4')popdef(`$1')')')
address@hidden(`_forloop',
address@hidden  `$3`'ifelse(indir(`$1'), `$2', `',
address@hidden    `define(`$1', incr(indir(`$1')))$0($@@)')')
address@hidden'dnl
+include(`forloop2.m4')
address@hidden
+forloop(`i', `2', `1', `no iteration occurs')
address@hidden
+forloop(`', `1', `2', ` odd iterator name')
address@hidden odd iterator name odd iterator name
+forloop(`i', `5 + 5', `0xc', ` 0x`'eval(i, `16')')
address@hidden 0xa 0xb 0xc
+forloop(`i', `a', `b', `non-numeric bounds')
address@hidden:stdin:6: bad expression in eval (bad input): (b) >= (a)
address@hidden
address@hidden 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.
+
address@hidden Improved foreach
address@hidden 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

reply via email to

[Prev in Thread] Current Thread [Next in Thread]