This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GNU M4 source repository".
http://git.sv.gnu.org/gitweb/?p=m4.git;a=commitdiff;h=6b1c5a2cbd84a5eb48fe352b7bc8c0568a020d62 The branch, master has been updated via 6b1c5a2cbd84a5eb48fe352b7bc8c0568a020d62 (commit) from a2df6b461c098df5a505d79d119538b3a294e301 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 6b1c5a2cbd84a5eb48fe352b7bc8c0568a020d62 Author: Eric Blake <[EMAIL PROTECTED]> Date: Sat Mar 15 20:52:36 2008 -0600 Document join, in order to fix bug in m4wrap example. * examples/join.m4: New file. * examples/wraplifo2.m4: Likewise. * Makefile.am (EXTRA_DIST): Add new files. * doc/m4.texinfo (Improved m4wrap): New node. (Defn, Location): Enhance tests. (Shift): Document the composit macro join. (Incompatibilities): Move documentation of LIFO vs. FIFO... (M4wrap): ...here, to match improved example. Signed-off-by: Eric Blake <[EMAIL PROTECTED]> ----------------------------------------------------------------------- Summary of changes: ChangeLog | 12 ++ Makefile.am | 6 +- doc/m4.texinfo | 280 +++++++++++++++++++++++++++++++++++++++++++++++++ examples/join.m4 | 15 +++ examples/wraplifo2.m4 | 9 ++ 5 files changed, 321 insertions(+), 1 deletions(-) create mode 100644 examples/join.m4 create mode 100644 examples/wraplifo2.m4 diff --git a/ChangeLog b/ChangeLog index 8ac919e..6e602e1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2008-03-15 Eric Blake <[EMAIL PROTECTED]> + + Document join, in order to fix bug in m4wrap example. + * examples/join.m4: New file. + * examples/wraplifo2.m4: Likewise. + * Makefile.am (EXTRA_DIST): Add new files. + * doc/m4.texinfo (Improved m4wrap): New node. + (Defn, Location): Enhance tests. + (Shift): Document the composit macro join. + (Incompatibilities): Move documentation of LIFO vs. FIFO... + (M4wrap): ...here, to match improved example. + 2008-03-13 Eric Blake <[EMAIL PROTECTED]> Stage 19c: allow builtin tokens in more macros. diff --git a/Makefile.am b/Makefile.am index 2ef7261..1f53c7c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -329,6 +329,7 @@ dist_pkgdata_DATA = \ examples/incl.m4 \ examples/include.m4 \ examples/indir.m4 \ + examples/join.m4 \ examples/loop.m4 \ examples/misc.m4 \ examples/multiquotes.m4 \ @@ -342,7 +343,10 @@ dist_pkgdata_DATA = \ examples/translit.m4 \ examples/undivert.incl \ examples/undivert.m4 \ - examples/wrap.m4 + examples/wrap.m4 \ + examples/wrapfifo.m4 \ + examples/wraplifo.m4 \ + examples/wraplifo2.m4 ## ----------- ## ## Test suite. ## diff --git a/doc/m4.texinfo b/doc/m4.texinfo index 6d024b3..175d923 100644 --- a/doc/m4.texinfo +++ b/doc/m4.texinfo @@ -288,6 +288,7 @@ Correct version of some examples * Improved exch:: Solution for @code{exch} * Improved forloop:: Solution for @code{forloop} * Improved foreach:: Solution for @code{foreach} +* Improved m4wrap:: Solution for @code{m4wrap} * Improved cleardivert:: Solution for @code{cleardivert} * Improved capitalize:: Solution for @code{capitalize} * Improved fatal_error:: Solution for @code{fatal_error} @@ -2337,6 +2338,11 @@ dumpdef(`') m4symbols(defn(`divnum')) @error{}m4:stdin:19: Warning: m4symbols: invalid macro name ignored @result{} +define(`foo', `define(`$1', $2)')dnl +foo(`bar', defn(`divnum')) [EMAIL PROTECTED] +bar [EMAIL PROTECTED] @end example A warning is issued if @var{name} is undefined. Also, at present, @@ -3065,6 +3071,8 @@ shift(`foo', `bar', `baz') An example of the use of @code{shift} is this macro: [EMAIL PROTECTED] reversing arguments [EMAIL PROTECTED] arguments, reversing @deffn Composite reverse (@dots{}) Takes any number of arguments, and reverses their order. @end deffn @@ -3142,6 +3150,113 @@ example2(`feeling rather indecisive today') @result{}default answer: 4 @end example [EMAIL PROTECTED] joining arguments [EMAIL PROTECTED] arguments, joining [EMAIL PROTECTED] concatenating arguments +Another common task that requires iteration is joining a list of +arguments into a single string. + [EMAIL PROTECTED] Composite join (@ovar{separator}, @[EMAIL PROTECTED]) [EMAIL PROTECTED] Composite joinall (@ovar{separator}, @[EMAIL PROTECTED]) +Generate a single-quoted string, consisting of each @var{arg} separated +by @var{separator}. While @code{joinall} always outputs a [EMAIL PROTECTED] between arguments, @code{join} avoids the [EMAIL PROTECTED] for an empty @var{arg}. [EMAIL PROTECTED] deffn + +Here are some examples of its usage, based on the implementation [EMAIL PROTECTED]@value{VERSION}/@/examples/@/join.m4} distributed in this +package: + [EMAIL PROTECTED] examples [EMAIL PROTECTED] +$ @kbd{m4 -I examples} +include(`join.m4') [EMAIL PROTECTED] +join,join(`-'),join(`-', `'),join(`-', `', `') [EMAIL PROTECTED],,, +joinall,joinall(`-'),joinall(`-', `'),joinall(`-', `', `') [EMAIL PROTECTED],,,- +join(`-', `1') [EMAIL PROTECTED] +join(`-', `1', `2', `3') [EMAIL PROTECTED] +join(`', `1', `2', `3') [EMAIL PROTECTED] +join(`-', `', `1', `', `', `2', `') [EMAIL PROTECTED] +joinall(`-', `', `1', `', `', `2', `') [EMAIL PROTECTED] +join(`,', `1', `2', `3') [EMAIL PROTECTED],2,3 +define(`nargs', `$#')dnl +nargs(join(`,', `1', `2', `3')) [EMAIL PROTECTED] [EMAIL PROTECTED] example + +Examining the implementation shows some interesting points about several +m4 programming idioms. + [EMAIL PROTECTED] examples [EMAIL PROTECTED] +$ @kbd{m4 -I examples} +undivert(`join.m4')dnl [EMAIL PROTECTED](`-1') [EMAIL PROTECTED] join(sep, args) - join each non-empty ARG into a single [EMAIL PROTECTED] string, with each element separated by SEP [EMAIL PROTECTED](`join', [EMAIL PROTECTED](`$#', `2', ``$2'', [EMAIL PROTECTED] `ifelse(`$2', `', `', ``$2'_')$0(`$1', shift(shift($@@)))')') [EMAIL PROTECTED](`_join', [EMAIL PROTECTED](`$#$2', `2', `', [EMAIL PROTECTED] `ifelse(`$2', `', `', ``$1$2'')$0(`$1', shift(shift($@@)))')') [EMAIL PROTECTED] joinall(sep, args) - join each ARG, including empty ones, [EMAIL PROTECTED] into a single string, with each element separated by SEP [EMAIL PROTECTED](`joinall', ``$2'_$0(`$1', shift($@@))') [EMAIL PROTECTED](`_joinall', [EMAIL PROTECTED](`$#', `2', `', ``$1$3'$0(`$1', shift(shift($@@)))')') [EMAIL PROTECTED]'dnl [EMAIL PROTECTED] example + +First, notice that this implementation creates helper macros [EMAIL PROTECTED] and @code{_joinall}. This division of labor makes it +easier to output the correct number of @var{separator} instances: [EMAIL PROTECTED] and @code{joinall} are responsible for the first argument, +without a separator, while @code{_join} and @code{_joinall} are +responsible for all remaining arguments, always outputting a separator +when outputting an argument. + +Next, observe how @code{join} decides to iterate to itself, because the +first @var{arg} was empty, or to output the argument and swap over to [EMAIL PROTECTED] If the argument is non-empty, then the nested [EMAIL PROTECTED] results in an unquoted @samp{_}, which is concatenated +with the @samp{$0} to form the next macro name to invoke. The [EMAIL PROTECTED] implementation is simpler since it does not have to +suppress empty @var{arg}; it always executes once then defers to [EMAIL PROTECTED] + +Another important idiom is the idea that @var{separator} is reused for +each iteration. Each iteration has one less argument, but rather than +discarding @samp{$1} by iterating with @code{$0(shift($@@))}, the macro +discards @samp{$2} by using @code{$0(`$1', shift(shift($@@)))}. + +Next, notice that it is possible to compare more than one condition in a +single @code{ifelse} test. The test of @samp{$#$2} against @samp{2} +allows @code{_join} to iterate for two separate reasons---either there +are still more than two arguments, or there are exactly two arguments +but the last argument is not empty. + +Finally, notice that these macros require exactly two arguments to +terminate recursion, but that they still correctly result in empty +output when given no @var{args} (i.e., zero or one macro argument). On +the first pass when there are too few arguments, the @code{shift} +results in no output, but leaves an empty string to serve as the +required second argument for the second pass. Put another way, [EMAIL PROTECTED]', shift($@@)} is not the same as @samp{$@@}, since only the +former guarantees at least two arguments. + [EMAIL PROTECTED] quote manipulation [EMAIL PROTECTED] manipulating quotes Sometimes, a recursive algorithm requires adding quotes to each element, or treating multiple arguments as a single element: @@ -3208,6 +3323,37 @@ undivert(`quote.m4')dnl @result{}divert`'dnl @end example +It is worth pointing out that @samp{quote(@var{args})} is more efficient +than @samp{joinall(`,', @var{args})} for producing the same output. + [EMAIL PROTECTED] nine arguments, more than [EMAIL PROTECTED] more than nine arguments [EMAIL PROTECTED] arguments, more than nine +One more useful macro based on @code{shift} allows portably selecting +an arbitrary argument (usually greater than the ninth argument), without +relying on the @acronym{GNU} extension of multi-digit arguments +(@pxref{Arguments}). + [EMAIL PROTECTED] Composite argn (@var{n}, @dots{}) +Expands to argument @var{n} out of the remaining arguments. @var{n} +must be a positive number. Usually invoked as [EMAIL PROTECTED]([EMAIL PROTECTED]',$@@)}. [EMAIL PROTECTED] deffn + +It is implemented as: + [EMAIL PROTECTED] +define(`argn', `ifelse(`$1', 1, ``$2'', + `argn(decr(`$1'), shift(shift($@@)))')') [EMAIL PROTECTED] +argn(`1', `a') [EMAIL PROTECTED] +define(`foo', `argn(`11', $@@)') [EMAIL PROTECTED] +foo(`a', `b', `c', `d', `e', `f', `g', `h', `i', `j', `k', `l') [EMAIL PROTECTED] [EMAIL PROTECTED] example + @node Forloop @section Iteration by counting @@ -5088,6 +5234,64 @@ which the saved text is reread is undefined. If @code{m4wrap} is not used recursively, the saved pieces of text are reread in the opposite order in which they were saved (LIFO---last in, first out). +It is possible to emulate @acronym{POSIX} behavior even +with older versions of @acronym{GNU} M4 by including the file [EMAIL PROTECTED]@value{VERSION}/@/examples/@/wrapfifo.m4} from the +distribution: + [EMAIL PROTECTED] examples [EMAIL PROTECTED] +$ @kbd{m4 -I examples} +undivert(`wrapfifo.m4')dnl [EMAIL PROTECTED] Redefine m4wrap to have FIFO semantics. [EMAIL PROTECTED](`_m4wrap_level', `0')dnl [EMAIL PROTECTED](`m4wrap', [EMAIL PROTECTED](`m4wrap'_m4wrap_level, [EMAIL PROTECTED] `define(`m4wrap'_m4wrap_level, [EMAIL PROTECTED] defn(`m4wrap'_m4wrap_level)`$1')', [EMAIL PROTECTED] `builtin(`m4wrap', `define(`_m4wrap_level', [EMAIL PROTECTED] incr(_m4wrap_level))dnl [EMAIL PROTECTED]'_m4wrap_level)dnl [EMAIL PROTECTED](`m4wrap'_m4wrap_level, `$1')')')dnl +include(`wrapfifo.m4') [EMAIL PROTECTED] +m4wrap(`a`'m4wrap(`c +', `d')')m4wrap(`b') [EMAIL PROTECTED] +^D [EMAIL PROTECTED] [EMAIL PROTECTED] example + +It is likewise possible to emulate LIFO behavior without resorting to +the @acronym{GNU} M4 extension of @code{builtin}, by including the file [EMAIL PROTECTED]@value{VERSION}/@/examples/@/wraplifo.m4} from the +distribution. (Unfortunately, both examples shown here share some +subtle bugs. See if you can find and correct them; or @pxref{Improved +m4wrap, , Answers}). + [EMAIL PROTECTED] examples [EMAIL PROTECTED] +$ @kbd{m4 -I examples} +undivert(`wraplifo.m4')dnl [EMAIL PROTECTED] Redefine m4wrap to have LIFO semantics. [EMAIL PROTECTED](`_m4wrap_level', `0')dnl [EMAIL PROTECTED](`_m4wrap', defn(`m4wrap'))dnl [EMAIL PROTECTED](`m4wrap', [EMAIL PROTECTED](`m4wrap'_m4wrap_level, [EMAIL PROTECTED] `define(`m4wrap'_m4wrap_level, [EMAIL PROTECTED] `$1'defn(`m4wrap'_m4wrap_level))', [EMAIL PROTECTED] `_m4wrap(`define(`_m4wrap_level', incr(_m4wrap_level))dnl [EMAIL PROTECTED]'_m4wrap_level)dnl [EMAIL PROTECTED](`m4wrap'_m4wrap_level, `$1')')')dnl +include(`wraplifo.m4') [EMAIL PROTECTED] +m4wrap(`a`'m4wrap(`c +', `d')')m4wrap(`b') [EMAIL PROTECTED] +^D [EMAIL PROTECTED] [EMAIL PROTECTED] example + Here is an example of implementing a factorial function using @code{m4wrap}: @@ -7570,7 +7774,11 @@ __line__ @result{}8 __line__ @result{}11 +m4wrap(`__line__ +') [EMAIL PROTECTED] ^D [EMAIL PROTECTED] @result{}6 @result{}6 @end example @@ -8287,6 +8495,7 @@ presented here. * Improved exch:: Solution for @code{exch} * Improved forloop:: Solution for @code{forloop} * Improved foreach:: Solution for @code{foreach} +* Improved m4wrap:: Solution for @code{m4wrap} * Improved cleardivert:: Solution for @code{cleardivert} * Improved capitalize:: Solution for @code{capitalize} * Improved fatal_error:: Solution for @code{fatal_error} @@ -8712,6 +8921,77 @@ foreachq(`x', ```active'', ``active''', `<x> @result{}<active> @end example [EMAIL PROTECTED] Improved m4wrap [EMAIL PROTECTED] Solution for @code{m4wrap} + +The replacement @code{m4wrap} versions presented above, designed to +guarantee FIFO or LIFO order regardless of the underlying M4 +implementation, share a bug when dealing with wrapped text that looks +like parameter expansion. Note how the invocation of [EMAIL PROTECTED]@var{n}} interprets these parameters, while using the +builtin preserves them for their intended use. + [EMAIL PROTECTED] examples [EMAIL PROTECTED] +$ @kbd{m4 -I examples} +include(`wraplifo.m4') [EMAIL PROTECTED] +m4wrap(`define(`foo', ``$0:'-$1-$*-$#-')foo(`a', `b') +') [EMAIL PROTECTED] +builtin(`m4wrap', ``'define(`bar', ``$0:'-$1-$*-$#-')bar(`a', `b') +') [EMAIL PROTECTED] +^D [EMAIL PROTECTED]:-a-a,b-2- [EMAIL PROTECTED]:---0- [EMAIL PROTECTED] example + +Additionally, the computation of @code{_m4wrap_level} and creation of +multiple @[EMAIL PROTECTED] placeholders in the original examples is +more expensive in time and memory than strictly necessary. Notice how +the improved version grabs the wrapped text via @code{defn} to avoid +parameter expansion, then undefines @code{_m4wrap_text}, before +stripping a level of quotes with @code{_arg1} to expand the text. That +way, each level of wrapping reuses the single placeholder, which starts +each nesting level in an undefined state. + +Finally, it is worth emulating the @acronym{GNU} M4 extension of saving +all arguments to @code{m4wrap}, separated by a space, rather than saving +just the first argument. This is done with the @code{join} macro +documented previously (@pxref{Shift}). The improved LIFO example is +shipped as @[EMAIL PROTECTED]/@/examples/@/wraplifo2.m4}, and can +easily be converted to a FIFO solution by swapping the adjacent +invocations of @code{joinall} and @code{defn}. + [EMAIL PROTECTED] examples [EMAIL PROTECTED] +$ @kbd{m4 -I examples} +include(`wraplifo2.m4') [EMAIL PROTECTED] +undivert(`wraplifo2.m4')dnl [EMAIL PROTECTED] Redefine m4wrap to have LIFO semantics, improved example. [EMAIL PROTECTED](`join.m4')dnl [EMAIL PROTECTED](`_m4wrap', defn(`m4wrap'))dnl [EMAIL PROTECTED](`_arg1', `$1')dnl [EMAIL PROTECTED](`m4wrap', [EMAIL PROTECTED](`_$0_text', [EMAIL PROTECTED] `define(`_$0_text', joinall(` ', $@@)defn(`_$0_text'))', [EMAIL PROTECTED] `_$0(`_arg1(defn(`_$0_text')undefine(`_$0_text'))')dnl [EMAIL PROTECTED](`_$0_text', joinall(` ', $@@))')')dnl +m4wrap(`define(`foo', ``$0:'-$1-$*-$#-')foo(`a', `b') +') [EMAIL PROTECTED] +m4wrap(`lifo text +m4wrap(`nested', `', `$@@ +')') [EMAIL PROTECTED] +^D [EMAIL PROTECTED] text [EMAIL PROTECTED]:-a-a,b-2- [EMAIL PROTECTED] $@@ [EMAIL PROTECTED] example + @node Improved cleardivert @section Solution for @code{cleardivert} diff --git a/examples/join.m4 b/examples/join.m4 new file mode 100644 index 0000000..8687ac7 --- /dev/null +++ b/examples/join.m4 @@ -0,0 +1,15 @@ +divert(`-1') +# join(sep, args) - join each non-empty ARG into a single +# string, with each element separated by SEP +define(`join', +`ifelse(`$#', `2', ``$2'', + `ifelse(`$2', `', `', ``$2'_')$0(`$1', shift(shift($@)))')') +define(`_join', +`ifelse(`$#$2', `2', `', + `ifelse(`$2', `', `', ``$1$2'')$0(`$1', shift(shift($@)))')') +# joinall(sep, args) - join each ARG, including empty ones, +# into a single string, with each element separated by SEP +define(`joinall', ``$2'_$0(`$1', shift($@))') +define(`_joinall', +`ifelse(`$#', `2', `', ``$1$3'$0(`$1', shift(shift($@)))')') +divert`'dnl diff --git a/examples/wraplifo2.m4 b/examples/wraplifo2.m4 new file mode 100644 index 0000000..5b450a7 --- /dev/null +++ b/examples/wraplifo2.m4 @@ -0,0 +1,9 @@ +dnl Redefine m4wrap to have LIFO semantics, improved example. +include(`join.m4')dnl +define(`_m4wrap', defn(`m4wrap'))dnl +define(`_arg1', `$1')dnl +define(`m4wrap', +`ifdef(`_$0_text', + `define(`_$0_text', joinall(` ', $@)defn(`_$0_text'))', + `_$0(`_arg1(defn(`_$0_text')undefine(`_$0_text'))')dnl +define(`_$0_text', joinall(` ', $@))')')dnl hooks/post-receive -- GNU M4 source repository
