[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: branch-1_4 foreach documentation
From: |
Eric Blake |
Subject: |
Re: branch-1_4 foreach documentation |
Date: |
Sat, 21 Oct 2006 09:18:26 -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
According to Eric Blake on 10/20/2006 8:53 PM:
> Patch to head to follow.
>
> 2006-10-20 Eric Blake <address@hidden>
>
> * doc/m4.texinfo: Trailing '@comment' doesn't format nicely in
> TeX, so recognize '@w{ }' instead. Likewise, @code{_name} at the
> end of a TeX line splits incorrectly.
Ported as follows:
2006-10-21 Eric Blake <address@hidden>
* tests/generate.awk (normalize): Update recognition of trailing
spaces in tests.
* doc/m4.texinfo: Minor formatting fixes from branch.
(Foreach, Improved foreach): Merge from branch.
* examples/foreach.m4: Merge from branch.
* examples/foreachq.m4: New file from branch.
* examples/foreach2.m4: Likewise.
* examples/foreachq2.m4: Likewise.
- --
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
iD8DBQFFOjpB84KuGfSFAYARAm0oAKCPx9xEndefapqwxO6JBBF1jO2P0QCgsk5f
4IWlquV2b3UzCUhBkTf1Owk=
=ZWjo
-----END PGP SIGNATURE-----
Index: doc/m4.texinfo
===================================================================
RCS file: /sources/m4/m4/doc/m4.texinfo,v
retrieving revision 1.69
diff -u -p -r1.69 m4.texinfo
--- doc/m4.texinfo 21 Oct 2006 12:49:57 -0000 1.69
+++ doc/m4.texinfo 21 Oct 2006 15:17:34 -0000
@@ -100,7 +100,7 @@ files @address@hidden/@/AUTHORS
distribution.
@ifclear beta
-This is release @value{VERSION}. It is now considered stable: future
+This is release @value{VERSION}. It is now considered stable: future
releases on this branch are only meant to fix bugs, increase speed, or
improve documentation.
@end ifclear
@@ -1203,7 +1203,7 @@ eval(`1')
@end example
There is also a command line option (@option{--prefix-builtins}, or
address@hidden, @pxref{Operation modes, , Invoking m4})) that renames all
address@hidden, @pxref{Operation modes, , Invoking m4}) that renames all
builtin macros with a prefix of @samp{m4_} at startup. The option has
no effect whatsoever on user defined macros. For example, with this option,
one has to write @code{m4_dnl} and even @code{m4_m4exit}. It also has
@@ -1882,7 +1882,7 @@ define(`string', `The macro dnl is very
')
@result{}
string
address@hidden macro @comment
address@hidden address@hidden }
defn(`string')
@result{}The macro dnl is very useful
@result{}
@@ -2597,7 +2597,7 @@ $ @kbd{m4 -I examples}
include(`forloop.m4')
@result{}
forloop(`i', `1', `8', `i ')
address@hidden 2 3 4 5 6 7 8 @comment
address@hidden 2 3 4 5 6 7 address@hidden }
@end example
For-loops can be nested, like:
@@ -2619,12 +2619,12 @@ forloop(`i', `1', `4', `forloop(`j', `1'
The implementation of the @code{forloop} macro is fairly
straightforward. The @code{forloop} macro itself is simply a wrapper,
which saves the previous definition of the first argument, calls the
-internal macro @code{_forloop}, and re-establishes the saved definition of
-the first argument.
+internal macro @address@hidden, and re-establishes the saved
+definition of the first argument.
-The macro @code{_forloop} expands the fourth argument once, and tests
-to see if the iterator has reached the final value. If it has not
-finished, it increments the iterator (using the predefined macro
+The macro @address@hidden expands the fourth argument once, and
+tests to see if the iterator has reached the final value. If it has
+not finished, it increments the iterator (using the predefined macro
@code{incr}, @pxref{Incr}), and recurses.
Here is an actual implementation of @code{forloop}, distributed as
@@ -2658,63 +2658,184 @@ macros; or @pxref{Improved forloop, , An
@cindex loops, list iteration
@cindex iterating over lists
Here is an example of a loop macro that implements list iteration.
address@hidden FIXME - this section still needs some work done
@deffn Composite foreach (@var{iterator}, @var{paren-list}, @var{text})
address@hidden Composite foreachq (@var{iterator}, @var{quote-list}, @var{text})
Takes the name in @var{iterator}, which must be a valid macro name, and
-successively assign it each value from @var{paren-list}.
address@hidden is a comma-separated list of elements surrounded by
-parentheses. For each assignment to @var{iterator}, append @var{text}
-to the expansion of @code{foreach}. @var{text} may refer to
+successively assign it each value from @var{paren-list} or
address@hidden In @code{foreach}, @var{paren-list} is a
+comma-separated list of elements contained in parentheses. In
address@hidden, @var{quote-list} is a comma-separated list of elements
+contained in a quoted string. For each assignment to @var{iterator},
+append @var{text} to the overall expansion. @var{text} may refer to
@var{iterator}. Any definition of @var{iterator} prior to this
invocation is restored.
@end deffn
-As an example, this displays each word in a list inside of a sentence.
+As an example, this displays each word in a list inside of a sentence,
+using an implementation of @code{foreach} distributed as
address@hidden@value{VERSION}/@/examples/@/foreach.m4}, and @code{foreachq}
+in @address@hidden/@/examples/@/foreachq.m4}.
address@hidden FIXME - include(`foreach.m4')
address@hidden ignore
address@hidden examples
@example
-foreach(`x', `(foo, bar, foobar)', `Word was: x
-')
+$ @kbd{m4 -I examples}
+include(`foreach.m4')
address@hidden
+foreach(`x', (foo, bar, foobar), `Word was: x
+')dnl
address@hidden was: foo
address@hidden was: bar
address@hidden was: foobar
+include(`foreachq.m4')
address@hidden
+foreachq(`x', `foo, bar, foobar', `Word was: x
+')dnl
@result{}Word was: foo
@result{}Word was: bar
@result{}Word was: foobar
@end example
+It is possible to be more complex; each element of the @var{paren-list}
+or @var{quote-list} can itself be a list, to pass as further arguments
+to a helper macro. This example generates a shell case statement:
+
address@hidden examples
address@hidden
+$ @kbd{m4 -I examples}
+include(`foreach.m4')
address@hidden
+define(`_case', ` $1)
+ $2=" $1";;
+')dnl
+define(`_cat', `$1$2')dnl
+case $`'1 in
address@hidden $1 in
+foreach(`x', `(`(`a', `vara')', `(`b', `varb')', `(`c', `varc')')',
+ `_cat(`_case', x)')dnl
address@hidden a)
address@hidden vara=" a";;
address@hidden b)
address@hidden varb=" b";;
address@hidden c)
address@hidden varc=" c";;
+esac
address@hidden
address@hidden example
+
The implementation of the @code{foreach} macro is a bit more involved;
-it is a wrapper around two helper macros. First, @code{_arg1} is needed
-to grab the first element of a list. Second, @code{_foreach} implements
-the recursion, successively walking through the original list.
-
-Here is an actual implementation of @code{forloop}, followed by a
-demonstration of using it to filter out a list of symbols that contain
address@hidden
-
address@hidden FIXME - include(foreach.m4),include(quote.m4)
address@hidden
-define(`foreach', `pushdef(`$1')_foreach($@@)popdef(`$1')')dnl
-define(`_arg1', ``$1'')dnl
-define(`_foreach',
- `ifelse($2, `()', ,
- `define(`$1',
- `_arg1$2')$3`'_foreach(`$1', `(shift$2)',
- `$3')')')dnl
-define(`dquote', ``$@@'')
address@hidden
-foreach(`macro', (dquote(m4symbols)),
- `regexp(macro, `.*if.*', ``\&',')')
address@hidden,ifelse,shift,
address@hidden example
-
-The example had to use a helper @code{quote} to ensure that the output
-from @code{m4symbols} was double quoted; without it, the macro would have
-gone into an infinite loop thanks to macros being reinvoked during the
-rescanning. Choosing @samp{()} as the list delimiters made this
-example rather awkward in terms of proper quoting. (A different
-implementation can be acheived by changing @var{list} from a
-parenthesized list to a quoted list. Try reimplementing @code{foreach}
-with these semantics yourself; or @pxref{Improved foreach, , Answers}).
+it is a wrapper around two helper macros. First, @address@hidden is
+needed to grab the first element of a list. Second,
address@hidden@w{_foreach}} implements the recursion, successively walking
+through the original list. Here is a simple implementation of
address@hidden:
+
address@hidden examples
address@hidden
+$ @kbd{m4 -I examples}
+undivert(`foreach.m4')dnl
address@hidden(`-1')
address@hidden foreach(x, (item_1, item_2, ..., item_n), stmt)
address@hidden parenthesized list, simple version
address@hidden(`foreach', `pushdef(`$1')_foreach($@@)popdef(`$1')')
address@hidden(`_arg1', `$1')
address@hidden(`_foreach', `ifelse(`$2', `()', `',
address@hidden `define(`$1', _arg1$2)$3`'$0(`$1', (shift$2), `$3')')')
address@hidden'dnl
address@hidden example
+
+Unfortunately, that implementation is not robust to macro names as list
+elements. Each iteration of @address@hidden is stripping another
+layer of quotes, leading to erratic results if list elements are not
+already fully expanded. The first cut at implementing @code{foreachq}
+takes this into account. Also, when using quoted elements in a
address@hidden, the overall list must be quoted. A @var{quote-list}
+has the nice property of requiring fewer characters to create a list
+containing the same quoted elements. To see the difference between the
+two macros, we attempt to pass double-quoted macro names in a list,
+expecting the macro name on output after one layer of quotes is removed
+during list iteration and the final layer removed during the final
+rescan:
+
address@hidden examples
address@hidden
+$ @kbd{m4 -I examples}
+define(`a', `1')define(`b', `2')define(`c', `3')
address@hidden
+include(`foreach.m4')
address@hidden
+include(`foreachq.m4')
address@hidden
+foreach(`x', `(``a'', ``(b'', ``c)'')', `x
+')
address@hidden
address@hidden(2)1
address@hidden
address@hidden, x
address@hidden)
+foreachq(`x', ```a'', ``(b'', ``c)''', `x
+')dnl
address@hidden
address@hidden(b
address@hidden)
address@hidden example
+
+Obviously, @code{foreachq} did a better job; here is its implementation:
+
address@hidden examples
address@hidden
+$ @kbd{m4 -I examples}
+undivert(`foreachq.m4')dnl
address@hidden(`quote.m4')dnl
address@hidden(`-1')
address@hidden foreachq(x, `item_1, item_2, ..., item_n', stmt)
address@hidden quoted list, simple version
address@hidden(`foreachq', `pushdef(`$1')_foreachq($@@)popdef(`$1')')
address@hidden(`_arg1', `$1')
address@hidden(`_foreachq', `ifelse(quote($2), `', `',
address@hidden `define(`$1', `_arg1($2)')$3`'$0(`$1', `shift($2)', `$3')')')
address@hidden'dnl
address@hidden example
+
+Notice that @address@hidden had to use the helper macro
address@hidden defined earlier (@pxref{Shift}), to ensure that the
+embedded @code{ifelse} call does not go haywire if a list element
+contains a comma. Unfortunately, this implementation of @code{foreachq}
+has its own severe flaw. Whereas the @code{foreach} implementation was
+linear, this macro is quadratic in the number of list elements, and is
+much more likely to trip up the limit set by the command line option
address@hidden (or @option{-L}, @pxref{Limits control, ,
+Invoking m4}). (It is possible to have robust iteration with linear
+behavior for either list style. See if you can learn from the best
+elements of both of these implementations to create robust macros; or
address@hidden foreach, , Answers}).
+
+With a robust @code{foreach} implementation, it is possible to create a
+filter on a list of defined symbols. This next example will find all
+symbols that contain @samp{if}. Notice the use of @code{dquote} and
address@hidden to ensure that the list of macro names is properly
+quoted; without these, the iteration would be invoking various macros
+with catastrophic effects. This example also shows a trick for
+generating the correct number of commas in the resulting output.
+
address@hidden examples
address@hidden
+$ @kbd{m4 -I examples}
+include(`quote.m4')include(`foreachq.m4')
address@hidden
+pushdef(`sep', ``, '')
address@hidden
+pushdef(`cleanup', `popdef(`sep', `cleanup')')
address@hidden
+pushdef(`sep', `define(`cleanup',
+ `popdef(`cleanup')')popdef(`sep')')
address@hidden
+foreachq(`macro', dquote(dquote_elt(m4symbols)),
+ `regexp(macro, `.*if.*', `sep`\&'')')
address@hidden, ifelse, shift
+cleanup
address@hidden
address@hidden example
@node Debugging
@chapter How to debug macros and input
@@ -4727,7 +4848,7 @@ patsubst(`GNUs not Unix', `\w*', `(\&)')
patsubst(`GNUs not Unix', `\w+', `(\&)')
@result{}(GNUs) (not) (Unix)
patsubst(`GNUs not Unix', `[A-Z][a-z]+')
address@hidden not @comment
address@hidden address@hidden }
@end example
Here is a slightly more realistic example, which capitalizes individual
@@ -5398,16 +5519,16 @@ foo
@result{}6
@end example
-The @code{__program__} macro behaves like @samp{$0} in shell
+The @address@hidden macro behaves like @samp{$0} in shell
terminology. If you invoke @code{m4} through an absolute path or a link
with a different spelling, rather than by relying on a @env{PATH} search
-for plain @samp{m4}, it will affect how @code{__program__} expands. The
-intent is that you can use it to produce error messages with the same
-formatting that @code{m4} produces internally. It can also be used
+for plain @samp{m4}, it will affect how @address@hidden expands.
+The intent is that you can use it to produce error messages with the
+same formatting that @code{m4} produces internally. It can also be used
within @code{syscmd} (@pxref{Syscmd}) to pick the same version of
@code{m4} that is currently running, rather than whatever version of
address@hidden happens to be first in @env{PATH}. It was first introduced
-in @acronym{GNU} M4 1.4.6.
address@hidden happens to be first in @env{PATH}. It was first introduced in
address@hidden M4 1.4.6.
@node M4exit
@section Exiting from @code{m4}
@@ -5734,8 +5855,8 @@ Macros can be called indirectly through
@item
The name of the current input file and the current input line number are
-accessible through the builtins @code{__file__} and @code{__line__}
-(@pxref{Errprint}).
+accessible through the builtins @address@hidden and
address@hidden@w{__line__}} (@pxref{Errprint}).
@item
The generation of sync lines can be controlled through @code{syncoutput}
@@ -5832,19 +5953,20 @@ and without @env{POSIXLY_CORRECT} set, t
@item
@findex __gnu__
GNU @code{m4} without @samp{-G} option will define the macro
address@hidden to expand to the empty string.
address@hidden@w{__gnu__}} to expand to the empty string.
@item
@findex unix
@findex __unix__
-On UNIX systems, GNU @code{m4} without the @option{-G} option will define
-the macro @code{__unix__}, otherwise the macro @code{unix}. Both will
-expand to the empty string.
+On UNIX systems, GNU @code{m4} without the @option{-G} option will
+define the macro @address@hidden, otherwise the macro @code{unix}.
+Both will expand to the empty string.
@item
@findex __windows__
On Windows systems, GNU @code{m4} without the @option{-G} option will
-define the macro @code{__windows__}, which expands to the empty string.
+define the macro @address@hidden, which expands to the empty
+string.
@end itemize
@node Experiments
@@ -5925,7 +6047,7 @@ name. It does not do any sanity checkin
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}.
+not need to be passed to the helper @address@hidden
@comment examples
@comment status: 1
@@ -5965,81 +6087,206 @@ additional bells and whistles in its @co
@node Improved foreach
@section Solution for @code{foreach}
-The @code{foreach} macro (@pxref{Foreach}) as presented required the
-user to use parentheses to delineate the list. This approach is
-quadratic, because the entire list is propagated through each recursion,
-with additional invocations of the shift macro added on each iteration:
+The @code{foreach} and @code{foreachq} macros (@pxref{Foreach}) as
+presented earlier each have flaws. First, we will examine and fix the
+quadratic behavior of @code{foreachq}:
address@hidden FIXME - include(foreach.m4),include(quote.m4)
address@hidden options: -d-V
address@hidden examples
@example
-define(`foreach', `pushdef(`$1')_foreach($@@)popdef(`$1')')dnl
-define(`_arg1', ``$1'')dnl
-define(`_foreach',
- `ifelse($2, `()', ,
- `define(`$1',
- `_arg1$2')$3`'_foreach(`$1', `(shift$2)',
- `$3')')')dnl
-define(`dquote', ``$@@'')
+$ @kbd{m4 -I examples}
+include(`foreachq.m4')
@result{}
-foreach(`macro', (dquote(m4symbols)), `regexp(macro, `shift', `\&')')
address@hidden
-traceon(`shift')
+traceon(`shift')debugmode(`aq')
@result{}
-foreach(`a', `(1,2,3)', `a
-')
+foreachq(`x', ``1', `2', `3', `4'', `x
+')dnl
@result{}1
address@hidden: -2- shift
address@hidden: -2- shift
address@hidden: -3- shift(`1', `2', `3', `4')
address@hidden: -2- shift(`1', `2', `3', `4')
@result{}2
address@hidden: -3- shift
address@hidden: -2- shift
address@hidden: -3- shift
address@hidden: -2- shift
address@hidden: -4- shift(`1', `2', `3', `4')
address@hidden: -3- shift(`2', `3', `4')
address@hidden: -3- shift(`1', `2', `3', `4')
address@hidden: -2- shift(`2', `3', `4')
@result{}3
address@hidden: -4- shift
address@hidden: -3- shift
address@hidden: -2- shift
address@hidden: -5- shift(`1', `2', `3', `4')
address@hidden: -4- shift(`2', `3', `4')
address@hidden: -3- shift(`3', `4')
address@hidden: -4- shift(`1', `2', `3', `4')
address@hidden: -3- shift(`2', `3', `4')
address@hidden: -2- shift(`3', `4')
address@hidden
address@hidden: -6- shift(`1', `2', `3', `4')
address@hidden: -5- shift(`2', `3', `4')
address@hidden: -4- shift(`3', `4')
address@hidden: -3- shift(`4')
address@hidden example
+
+Each successive iteration was adding more quoted @code{shift}
+invocations, and the entire list contents were passing through every
+iteration. In general, when recursing, it is a good idea to make the
+recursion use fewer arguments, rather than adding additional quoted
+uses of @code{shift}. By doing so, @code{m4} uses less memory, invokes
+fewer macros, is less likely to run into machine limits, and most
+importantly, performs faster. The fixed version of @code{foreachq} can
+be found in @address@hidden/@/examples/@/foreachq2.m4}:
+
address@hidden examples
address@hidden
+$ @kbd{m4 -I examples}
+include(`foreachq2.m4')
@result{}
+undivert(`foreachq2.m4')dnl
address@hidden(`quote.m4')dnl
address@hidden(`-1')
address@hidden foreachq(x, `item_1, item_2, ..., item_n', stmt)
address@hidden quoted list, improved version
address@hidden(`foreachq', `pushdef(`$1')_foreachq($@@)popdef(`$1')')
address@hidden(`_arg1q', ``$1'')
address@hidden(`_rest', `ifelse(`$#', `1', `', `dquote(shift($@@))')')
address@hidden(`_foreachq', `ifelse(`$2', `', `',
address@hidden `define(`$1', _arg1q($2))$3`'$0(`$1', _rest($2), `$3')')')
address@hidden'dnl
+traceon(`shift')debugmode(`aq')
address@hidden
+foreachq(`x', ``1', `2', `3', `4'', `x
+')dnl
address@hidden
address@hidden: -3- shift(`1', `2', `3', `4')
address@hidden
address@hidden: -3- shift(`2', `3', `4')
address@hidden
address@hidden: -3- shift(`3', `4')
address@hidden
@end example
-An alternative implementation takes a quoted list, with semantics that
-the list is expanded once before iteration, and has the benefit that
-each iteration operates on a shorter list, giving linear performance on
-long lists. Another way of viewing these semantics is that the
-outermost quotes delineates the list, then each element of the list must
-be quoted as though the list delimiters were not present. Notice the
-difference when iterating over @code{m4symbols}; we must use
address@hidden instead of @code{dquote} to get the necessary quoting.
+Note that the fixed version calls unquoted helper macros in
address@hidden@w{_foreachq}} to trim elements immediately; those helper macros
+in turn must re-supply the layer of quotes lost in the macro invocation.
+Contrast the use of @address@hidden, which quotes the first list
+element, with @address@hidden of the earlier implementation that
+returned the first list element directly.
+
+For a different approach, the improved version of @code{foreach},
+available in @address@hidden/@/examples/@/foreach2.m4}, simply
+overquotes the arguments to @address@hidden to begin with, using
address@hidden Then @address@hidden can just use
address@hidden@w{_arg1}} to remove the extra layer of quoting that was added up
+front:
address@hidden FIXME - include(foreach.m4),include(quote.m4)
address@hidden options: -d-V
address@hidden examples
@example
-define(`foreach', `pushdef(`$1')_foreach($@@)popdef(`$1')')dnl
-define(`_arg1', ``$1'')dnl
-define(`quote', `ifelse(`$#', `0', `', ``$*'')')dnl
-define(`dquote', ``$@@'')dnl
-define(`dquote_elt', `ifelse(`$#', `0', `', `$#', `1', ```$1''',
- ```$1'',dquote_elt(shift($@@))')')dnl
-define(`_rest', `ifelse(`$#', `1', , `dquote(shift($@@))')')dnl
-define(`_foreach',
- `ifelse(quote($2), , ,
- `define(`$1',
- `_arg1($2)')$3`'_foreach(`$1', _rest($2),
- `$3')')')dnl
-foreach(`macro', `dquote_elt(m4symbols)',
- `regexp(macro, `shift', `\&')')
address@hidden
-traceon(`shift')
+$ @kbd{m4 -I examples}
+include(`foreach2.m4')
@result{}
-foreach(`a', ``1',`2',`3'', `a
-')
+undivert(`foreach2.m4')dnl
address@hidden(`quote.m4')dnl
address@hidden(`-1')
address@hidden foreach(x, (item_1, item_2, ..., item_n), stmt)
address@hidden parenthesized list, improved version
address@hidden(`foreach', `pushdef(`$1')_foreach(`$1',
address@hidden (dquote(dquote_elt$2)), `$3')popdef(`$1')')
address@hidden(`_arg1', `$1')
address@hidden(`_foreach', `ifelse(`$2', `(`')', `',
address@hidden `define(`$1', _arg1$2)$3`'$0(`$1', (dquote(shift$2)), `$3')')')
address@hidden'dnl
+traceon(`shift')debugmode(`aq')
address@hidden
+foreach(`x', `(`1', `2', `3', `4')', `x
+')dnl
address@hidden: -4- shift(`1', `2', `3', `4')
address@hidden: -4- shift(`2', `3', `4')
address@hidden: -4- shift(`3', `4')
@result{}1
address@hidden: -3- shift
address@hidden: -3- shift(``1'', ``2'', ``3'', ``4'')
@result{}2
address@hidden: -3- shift
address@hidden: -3- shift(``2'', ``3'', ``4'')
@result{}3
address@hidden: -3- shift(``3'', ``4'')
address@hidden
address@hidden: -3- shift(``4'')
address@hidden example
+
+In summary, recursion over list elements is trickier than it appeared at
+first glance, but provides a powerful idiom within @code{m4} processing.
+As a final demonstration, both list styles are now able to handle
+several scenarios that would wreak havoc on the original
+implementations. This points out one other difference between the two
+list styles. @code{foreach} evaluates unquoted list elements only once,
+in preparation for calling @address@hidden But @code{foreachq}
+evaluates unquoted list elements twice while visiting the first list
+element, once in @address@hidden and once in @address@hidden When
+deciding which list style to use, one must take into account whether
+repeating the side effects of unquoted list elements will have any
+detrimental effects.
+
address@hidden examples
address@hidden
+$ @kbd{m4 -I examples}
+include(`foreach2.m4')
address@hidden
+include(`foreachq2.m4')
@result{}
+dnl 0-element list:
+foreach(`x', `', `<x>') / foreachq(`x', `', `<x>')
address@hidden /@w{ }
+dnl 1-element list of empty element
+foreach(`x', `()', `<x>') / foreachq(`x', ``'', `<x>')
address@hidden<> / <>
+dnl 2-element list of empty elements
+foreach(`x', `(`',`')', `<x>') / foreachq(`x', ``',`'', `<x>')
address@hidden<><> / <><>
+dnl 1-element list of a comma
+foreach(`x', `(`,')', `<x>') / foreachq(`x', ``,'', `<x>')
address@hidden<,> / <,>
+dnl 2-element list of unbalanced parentheses
+foreach(`x', `(`(', `)')', `<x>') / foreachq(`x', ``(', `)'', `<x>')
address@hidden<(><)> / <(><)>
+define(`active', `ACT, IVE')
address@hidden
+traceon(`active')
address@hidden
+dnl list of unquoted macros; expansion occurs before recursion
+foreach(`x', `(active, active)', `<x>
+')dnl
address@hidden: -4- active -> `ACT, IVE'
address@hidden: -4- active -> `ACT, IVE'
address@hidden<ACT>
address@hidden<IVE>
address@hidden<ACT>
address@hidden<IVE>
+foreachq(`x', `active, active', `<x>
+')dnl
address@hidden: -3- active -> `ACT, IVE'
address@hidden: -3- active -> `ACT, IVE'
address@hidden<ACT>
address@hidden: -3- active -> `ACT, IVE'
address@hidden: -3- active -> `ACT, IVE'
address@hidden<IVE>
address@hidden<ACT>
address@hidden<IVE>
+dnl list of quoted macros; expansion occurs during recursion
+foreach(`x', `(`active', `active')', `<x>
+')dnl
address@hidden: -1- active -> `ACT, IVE'
address@hidden<ACT, IVE>
address@hidden: -1- active -> `ACT, IVE'
address@hidden<ACT, IVE>
+foreachq(`x', ``active', `active'', `<x>
+')dnl
address@hidden: -1- active -> `ACT, IVE'
address@hidden<ACT, IVE>
address@hidden: -1- active -> `ACT, IVE'
address@hidden<ACT, IVE>
+dnl list of double-quoted macro names; no expansion
+foreach(`x', `(``active'', ``active'')', `<x>
+')dnl
address@hidden<active>
address@hidden<active>
+foreachq(`x', ```active'', ``active''', `<x>
+')dnl
address@hidden<active>
address@hidden<active>
@end example
@node Improved cleardivert
@@ -6080,12 +6327,13 @@ undivert
@section Solution for @code{fatal_error}
The @code{fatal_error} macro (@pxref{M4exit}) is not robust to versions
-of @acronym{GNU} M4 earlier than 1.4.8, where invoking @code{__file__}
-(@pxref{Location}) inside @code{m4wrap} would result in an empty string,
-and @code{__line__} resulted in @samp{0} even though all files start at
-line 1. Furthermore, versions earlier than 1.4.6 did not support the
address@hidden macro. If you want @code{fatal_error} to work across
-the entire 1.4.x release series, a better implementation would be:
+of @acronym{GNU} M4 earlier than 1.4.8, where invoking
address@hidden@w{__file__}} (@pxref{Location}) inside @code{m4wrap} would result
+in an empty string, and @address@hidden resulted in @samp{0} even
+though all files start at line 1. Furthermore, versions earlier than
+1.4.6 did not support the @address@hidden macro. If you want
address@hidden to work across the entire 1.4.x release series, a
+better implementation would be:
@comment status: 1
@example
Index: examples/foreach.m4
===================================================================
RCS file: /sources/m4/m4/examples/foreach.m4,v
retrieving revision 1.2
diff -u -p -r1.2 foreach.m4
--- examples/foreach.m4 24 Nov 2000 01:31:33 -0000 1.2
+++ examples/foreach.m4 21 Oct 2006 15:17:34 -0000
@@ -1,29 +1,8 @@
-divert(-1)
+divert(`-1')
# foreach(x, (item_1, item_2, ..., item_n), stmt)
-define(`foreach', `pushdef(`$1', `')_foreach($@)popdef(`$1')')
-define(`_arg1', ``$1'')
-define(`_foreach',
- `ifelse($2, `()', ,
- `define(`$1', `_arg1$2')$3`'_foreach(`$1', `(shift$2)',
`$3')')')
-
-# traceon(`define', `foreach', `_foreach', `ifelse')
-
-define(a, 1)
-define(b, 2)
-define(c, 3)
-divert
-foreach(`x', `(foo, bar, foobar)', `Word was: x
-')
-
-# Quote torture from Akim Demaille <address@hidden>
-foreach(`x', `(`a', `(b', `c)')', `Word was: x
-')
-
-# Something more complex, from Pierre Gaumond <address@hidden>.
-define(`case', ` $1)
- $2=" -$1";;
-')dnl
-define(`_cat', `$1$2')dnl
-`case' "$1" in
-foreach(`x', ((a, vara), (b, varb), (c, varc)), `_cat(`case', x)')dnl
-esac
+# parenthesized list, simple version
+define(`foreach', `pushdef(`$1')_foreach($@)popdef(`$1')')
+define(`_arg1', `$1')
+define(`_foreach', `ifelse(`$2', `()', `',
+ `define(`$1', _arg1$2)$3`'$0(`$1', (shift$2), `$3')')')
+divert`'dnl
Index: examples/foreach2.m4
===================================================================
RCS file: examples/foreach2.m4
diff -N examples/foreach2.m4
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ examples/foreach2.m4 21 Oct 2006 15:17:34 -0000
@@ -0,0 +1,10 @@
+include(`quote.m4')dnl
+divert(`-1')
+# foreach(x, (item_1, item_2, ..., item_n), stmt)
+# parenthesized list, improved version
+define(`foreach', `pushdef(`$1')_foreach(`$1',
+ (dquote(dquote_elt$2)), `$3')popdef(`$1')')
+define(`_arg1', `$1')
+define(`_foreach', `ifelse(`$2', `(`')', `',
+ `define(`$1', _arg1$2)$3`'$0(`$1', (dquote(shift$2)), `$3')')')
+divert`'dnl
Index: examples/foreachq.m4
===================================================================
RCS file: examples/foreachq.m4
diff -N examples/foreachq.m4
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ examples/foreachq.m4 21 Oct 2006 15:17:34 -0000
@@ -0,0 +1,9 @@
+include(`quote.m4')dnl
+divert(`-1')
+# foreachq(x, `item_1, item_2, ..., item_n', stmt)
+# quoted list, simple version
+define(`foreachq', `pushdef(`$1')_foreachq($@)popdef(`$1')')
+define(`_arg1', `$1')
+define(`_foreachq', `ifelse(quote($2), `', `',
+ `define(`$1', `_arg1($2)')$3`'$0(`$1', `shift($2)', `$3')')')
+divert`'dnl
Index: examples/foreachq2.m4
===================================================================
RCS file: examples/foreachq2.m4
diff -N examples/foreachq2.m4
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ examples/foreachq2.m4 21 Oct 2006 15:17:34 -0000
@@ -0,0 +1,10 @@
+include(`quote.m4')dnl
+divert(`-1')
+# foreachq(x, `item_1, item_2, ..., item_n', stmt)
+# quoted list, improved version
+define(`foreachq', `pushdef(`$1')_foreachq($@)popdef(`$1')')
+define(`_arg1q', ``$1'')
+define(`_rest', `ifelse(`$#', `1', `', `dquote(shift($@))')')
+define(`_foreachq', `ifelse(`$2', `', `',
+ `define(`$1', _arg1q($2))$3`'$0(`$1', _rest($2), `$3')')')
+divert`'dnl
Index: tests/generate.awk
===================================================================
RCS file: /sources/m4/m4/tests/generate.awk,v
retrieving revision 1.22
diff -u -p -r1.22 generate.awk
--- tests/generate.awk 19 Oct 2006 16:19:20 -0000 1.22
+++ tests/generate.awk 21 Oct 2006 15:17:34 -0000
@@ -141,7 +141,7 @@ function normalize(contents, i, lines
gsub ("@}", "}", line);
gsub ("@@", "@", line);
gsub ("@tabchar{}", "\t", line);
- gsub ("@comment.*", "@\\&t@", line);
+ gsub ("@w{ }", " @\\&t@", line);
gsub ("m4_", "address@hidden&address@hidden", line);
gsub ("stdin", "input.m4", line);