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=4cc7916e4dd2c221f37aa7eec159b48d15273157 The branch, branch-1_4 has been updated via 4cc7916e4dd2c221f37aa7eec159b48d15273157 (commit) from d53cf5ec91d8991f633233ed3bd72384b7cbd8b5 (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 4cc7916e4dd2c221f37aa7eec159b48d15273157 Author: Eric Blake <[EMAIL PROTECTED]> Date: Sat Mar 15 15:12:47 2008 -0600 Document join, in order to fix bug in m4wrap example. * examples/join.m4: New file. * examples/wraplifo2.m4: Likewise. * examples/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 ++ doc/m4.texinfo | 301 +++++++++++++++++++++++++++++++++++++++++++----- examples/Makefile.am | 6 +- examples/join.m4 | 15 +++ examples/wraplifo2.m4 | 9 ++ 5 files changed, 309 insertions(+), 34 deletions(-) create mode 100644 examples/join.m4 create mode 100644 examples/wraplifo2.m4 diff --git a/ChangeLog b/ChangeLog index bce309d..599d00f 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. + * examples/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-14 Eric Blake <[EMAIL PROTECTED]> Stage 19: allow builtin tokens in more macros. diff --git a/doc/m4.texinfo b/doc/m4.texinfo index 7ac9867..f0fbb96 100644 --- a/doc/m4.texinfo +++ b/doc/m4.texinfo @@ -269,6 +269,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} @@ -2284,25 +2285,40 @@ builtin token is preserved only when it occurs in isolation. A future version of @acronym{GNU} M4 may lift these restrictions. @example +$ @kbd{m4 -d} define(`a', `A')define(`AA', `b') @result{} +traceon(`defn', `define') [EMAIL PROTECTED] defn(`a', `divnum', `a') [EMAIL PROTECTED]:stdin:2: Warning: defn: cannot concatenate builtin `divnum' [EMAIL PROTECTED]:stdin:3: Warning: defn: cannot concatenate builtin `divnum' [EMAIL PROTECTED]: -1- defn(`a', `divnum', `a') -> ``A'`A'' @result{}AA define(`mydivnum', defn(`divnum', `divnum'))mydivnum [EMAIL PROTECTED]:stdin:3: Warning: defn: cannot concatenate builtin `divnum' [EMAIL PROTECTED]:stdin:3: Warning: defn: cannot concatenate builtin `divnum' [EMAIL PROTECTED]:stdin:4: Warning: defn: cannot concatenate builtin `divnum' [EMAIL PROTECTED]:stdin:4: Warning: defn: cannot concatenate builtin `divnum' [EMAIL PROTECTED]: -2- defn(`divnum', `divnum') [EMAIL PROTECTED]: -1- define(`mydivnum', `') [EMAIL PROTECTED] +traceoff(`defn', `define') @result{} define(`mydivnum', defn(`divnum')defn(`divnum'))mydivnum [EMAIL PROTECTED]:stdin:4: Warning: define: cannot concatenate builtin `divnum' [EMAIL PROTECTED]:stdin:4: Warning: define: cannot concatenate builtin `divnum' [EMAIL PROTECTED]:stdin:6: Warning: define: cannot concatenate builtin `divnum' [EMAIL PROTECTED]:stdin:6: Warning: define: cannot concatenate builtin `divnum' @result{} define(`mydivnum', defn(`divnum')`a')mydivnum [EMAIL PROTECTED]:stdin:5: Warning: define: cannot concatenate builtin `divnum' [EMAIL PROTECTED]:stdin:7: Warning: define: cannot concatenate builtin `divnum' @result{}A define(`mydivnum', `a'defn(`divnum'))mydivnum [EMAIL PROTECTED]:stdin:6: Warning: define: cannot concatenate builtin `divnum' [EMAIL PROTECTED]:stdin:8: Warning: define: cannot concatenate builtin `divnum' @result{}A +define(`q', ``$@@'') [EMAIL PROTECTED] +define(`foo', q(`a', defn(`divnum')))foo [EMAIL PROTECTED]:stdin:10: Warning: define: cannot quote builtin [EMAIL PROTECTED], +ifdef(`foo', `yes', `no') [EMAIL PROTECTED] @end example @node Pushdef @@ -2931,6 +2947,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 @@ -3008,6 +3026,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: @@ -3074,6 +3199,9 @@ 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. + @cindex nine arguments, more than @cindex more than nine arguments @cindex arguments, more than nine @@ -4484,6 +4612,64 @@ in which they were saved (LIFO---last in, first out). However, this behavior is likely to change in a future release, to match @acronym{POSIX}, so you should not depend on this order. +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}: @@ -6423,7 +6609,11 @@ __line__ @result{}8 __line__ @result{}11 +m4wrap(`__line__ +') [EMAIL PROTECTED] ^D [EMAIL PROTECTED] @result{}6 @result{}6 @end example @@ -6873,31 +7063,6 @@ argument to @code{m4wrap} is saved for later evaluation, but @acronym{GNU} @code{m4} saves and processes all arguments, with output separated by spaces. -However, it is possible to emulate @acronym{POSIX} behavior by -including the file @[EMAIL PROTECTED]/@/examples/@/wrapfifo.m4} -from the distribution: - [EMAIL PROTECTED] -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 - @item @acronym{POSIX} states that builtins that require arguments, but are called without arguments, have undefined behavior. Traditional @@ -7104,6 +7269,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} @@ -7557,6 +7723,77 @@ include(`loop.m4')dnl @end ignore [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/Makefile.am b/examples/Makefile.am index 3450eac..254d2ab 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -1,6 +1,6 @@ ## Makefile.am - template for generating Makefile via Automake ## -## Copyright (C) 2006, 2007 Free Software Foundation, Inc. +## Copyright (C) 2006, 2007, 2008 Free Software Foundation, Inc. ## ## This file is part of GNU M4. ## @@ -42,6 +42,7 @@ incl-test.m4 \ incl.m4 \ include.m4 \ indir.m4 \ +join.m4 \ loop.m4 \ misc.m4 \ multiquotes.m4 \ @@ -62,4 +63,5 @@ undivert.incl \ undivert.m4 \ wrap.m4 \ wrapfifo.m4 \ -wraplifo.m4 +wraplifo.m4 \ +wraplifo2.m4 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
