Re: documentation ideas from autoconf

Eric Blake <[email protected]>
Newsgroups gmane.comp.gnu.m4.patches
Message-ID <[email protected]>
Eric Blake <ebb9 <at> byu.net> writes:

> 
> I'm committing this to branch-1.6 and master; it documents some slick m4 
tricks 
> learned recently in autoconf's m4sugar.

And in trying to backport it to branch-1.4, I noticed a typo in stack.m4:

> +
> +@deffn Composite stack_foreach (@var{macro}, @var{action})
> +@deffnx Composite stack_foreach_lifo (@var{macro}, @var{action})

> +++ b/examples/stack.m4
> @@ -0,0 +1,16 @@
> +divert(`-1')
> +# stack_foreach(action, macro)

as well as a limitation in M4 1.4.x that has already been fixed in branch-1.6 - 
passing a builtin token through curry flattens to the empty string with older 
m4:

curry(`define',`mylen')(defn(`len'))
mylen(`abc') => `' (not the expected `3')

Fortunately, autoconf had already resolved the issue, as a side effect of 
writing a more efficient copy algorithm.  So I'm applying this followup to 
branch-1.6 and master, then backporting the series of doc patches to branch-1.4:

From: Eric Blake <[email protected]>
Date: Thu, 18 Dec 2008 10:28:06 -0700
Subject: [PATCH] Deal with M4 1.4.x limitation on builtin tokens.

* doc/m4.texinfo (Composition): Mention limitation on curry.
(Improved copy): New node.
(Stacks): Fix typo.
* examples/stack.m4: Likewise.
* examples/stack_sep.m4: New file.
* examples/Makefile.am (EXTRA_DIST): Distribute it.

Signed-off-by: Eric Blake <[email protected]>
---
 ChangeLog             |   10 ++++
 doc/m4.texinfo        |  113 +++++++++++++++++++++++++++++++++++++++++++++++-
 examples/Makefile.am  |    1 +
 examples/stack.m4     |    4 +-
 examples/stack_sep.m4 |   17 +++++++
 5 files changed, 140 insertions(+), 5 deletions(-)
 create mode 100644 examples/stack_sep.m4

diff --git a/ChangeLog b/ChangeLog
index 63a0516..895d17a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2008-12-18  Eric Blake  <[email protected]>
+
+	Deal with M4 1.4.x limitation on builtin tokens.
+	* doc/m4.texinfo (Composition): Mention limitation on curry.
+	(Improved copy): New node.
+	(Stacks): Fix typo.
+	* examples/stack.m4: Likewise.
+	* examples/stack_sep.m4: New file.
+	* examples/Makefile.am (EXTRA_DIST): Distribute it.
+
 2008-12-10  Eric Blake  <[email protected]>
 
 	Double size of temp file cache.
diff --git a/doc/m4.texinfo b/doc/m4.texinfo
index 7c6bf56..50d0ace 100644
--- a/doc/m4.texinfo
+++ b/doc/m4.texinfo
@@ -272,6 +272,7 @@ Top
 * Improved exch::               Solution for @code{exch}
 * Improved forloop::            Solution for @code{forloop}
 * Improved foreach::            Solution for @code{foreach}
+* Improved copy::               Solution for @code{copy}
 * Improved m4wrap::             Solution for @code{m4wrap}
 * Improved cleardivert::        Solution for @code{cleardivert}
 * Improved capitalize::         Solution for @code{capitalize}
@@ -3742,13 +3743,13 @@ Stacks
 $ @kbd{m4 -I examples}
 undivert(`stack.m4')dnl
 @result{}divert(`-1')
-@result{}# stack_foreach(action, macro)
+@result{}# stack_foreach(macro, action)
 @result{}# Invoke ACTION with a single argument of each definition
 @result{}# from the definition stack of MACRO, starting with the oldest.
 @result{}define(`stack_foreach',
 @result{}`_stack_reverse(`$1', `tmp-$1')'dnl
 @result{}`_stack_reverse(`tmp-$1', `$1', `$2(defn(`$1'))')')
-@result{}# stack_foreach_lifo(action, macro)
+@result{}# stack_foreach_lifo(macro, action)
 @result{}# Invoke ACTION with a single argument of each definition
 @result{}# from the definition stack of MACRO, starting with the newest.
 @result{}define(`stack_foreach_lifo',
@@ -3885,6 +3886,23 @@ Composition
 @result{}divert`'dnl
 @end example
 
+Unfortunately, with M4 1.4.x, @code{curry} is unable to handle builtin
+tokens, which are silently flattened to the empty string when passed
+through another text macro.  The following example demonstrates a usage
+of @code{curry} that works in M4 1.6, but is not portable to earlier
+versions:
+
+@comment examples
+@example
+$ @kbd{m4 -I examples}
+include(`curry.m4')
+@result{}
+curry(`define', `mylen')(defn(`len'))
+@result{}
+mylen(`abc')
+@result{}3
+@end example
+
 @cindex renaming macros
 @cindex copying macros
 @cindex macros, copying
@@ -3900,7 +3918,10 @@ Composition
 via this macro.
 @end deffn
 
-The implementation is relatively straightforward:
+The implementation is relatively straightforward (although since it uses
+@code{curry}, it is unable to copy builtin macros when used with M4
+1.4.x.  See if you can design a portable version that works across all
+M4 versions, or @pxref{Improved copy, , Answers}).
 
 @comment examples
 @example
@@ -8101,6 +8122,7 @@ Answers
 * Improved exch::               Solution for @code{exch}
 * Improved forloop::            Solution for @code{forloop}
 * Improved foreach::            Solution for @code{foreach}
+* Improved copy::               Solution for @code{copy}
 * Improved m4wrap::             Solution for @code{m4wrap}
 * Improved cleardivert::        Solution for @code{cleardivert}
 * Improved capitalize::         Solution for @code{capitalize}
@@ -8709,6 +8731,91 @@ Improved foreach
 
 @end ignore
 
+@node Improved copy
+@section Solution for @code{copy}
+
+The macro @code{copy} presented above works with M4 1.6 and newer, but
+is unable to handle builtin tokens with M4 1.4.x, because it tries to
+pass the builtin token through the macro @code{curry}, where it is
+silently flattened to an empty string (@pxref{Composition}).  Rather
+than using the problematic @code{curry} to work around the limitation
+that @code{stack_foreach} expects to invoke a macro that takes exactly
+one argument, we can write a new macro that lets us form the exact
+two-argument @code{pushdef} call sequence needed, so that we are no
+longer passing a builtin token through a text macro.
+
+@deffn Composite stack_foreach_sep (@var{macro}, @var{pre}, @var{post}, @
+  @var{sep})
+@deffnx Composite stack_foreach_sep_lifo (@var{macro}, @var{pre}, @
+  @var{post}, @var{sep})
+For each of the @code{pushdef} definitions associated with @var{macro},
+expand the sequence @samp{@var{pre}`'definition`'@var{post}}.
+Additionally, expand @var{sep} between definitions.
+@code{stack_foreach_sep} visits the oldest definition first, while
+@code{stack_foreach_sep_lifo} visits the current definition first.  The
+expansion may dereference @var{macro}, but should not modify it.  There
+are a few special macros, such as @code{defn}, which cannot be used as
+the @var{macro} parameter.
+@end deffn
+
+Note that @code{stack_foreach(`@var{macro}', `@var{action}')} is
+equivalent to @code{stack_foreach_sep(`@var{macro}', `@var{action}(',
+`)')}.  By supplying explicit parentheses, split among the @var{pre} and
+@var{post} arguments to @code{stack_foreach_sep}, it is now possible to
+construct macro calls with more than one argument, without passing
+builtin tokens through a macro call.  It is likewise possible to
+directly reference the stack definitions without a macro call, by
+leaving @var{pre} and @var{post} empty.  The new macro also adds a
+separator that is only output after the first iteration of the helper
+@var{_stack_reverse_sep}, implemented by prepending the original
+@var{sep} to @var{pre} and omitting a @var{sep} argument in subsequent
+iterations.  As an added bonus, using @code{stack_foreach_sep} to
+implement @code{copy} performs fewer macro invocations.  The improved
+stack walking macros are available in
+@file{m4-@value{VERSION}/@/examples/@/stack_sep.m4}:
+
+@comment examples
+@example
+$ @kbd{m4 -I examples}
+include(`stack_sep.m4')
+@result{}
+define(`copy', `ifdef(`$2', `errprint(`$2 already defined
+')m4exit(`1')',
+   `stack_foreach_sep(`$1', `pushdef(`$2',', `)')')')dnl
+pushdef(`a', `1')pushdef(`a', defn(`divnum'))
+@result{}
+copy(`a', `b')
+@result{}
+b
+@result{}0
+popdef(`b')
+@result{}
+b
+@result{}1
+pushdef(`c', `1')pushdef(`c', `2')
+@result{}
+stack_foreach_sep_lifo(`c', `', `', `, ')
+@result{}2, 1
+undivert(`stack_sep.m4')dnl
+@result{}divert(`-1')
+@result{}# stack_foreach_sep(macro, pre, post, sep)
+@result{}# Invoke PRE`'defn`'POST with a single argument of each definition
+@result{}# from the definition stack of MACRO, starting with the oldest, and
+@result{}# separated by SEP between definitions.
+@result{}define(`stack_foreach_sep',
+@result{}`_stack_reverse_sep(`$1', `tmp-$1')'dnl
+@result{}`_stack_reverse_sep(`tmp-$1', `$1', `$2`'defn(`$1')$3', `$4')')
+@result{}# stack_foreach_sep_lifo(macro, pre, post, sep)
+@result{}# Like stack_foreach_sep, but starting with the newest definition.
+@result{}define(`stack_foreach_sep_lifo',
+@result{}`_stack_reverse_sep(`$1', `tmp-$1', `$2`'defn(`$1')$3', `$4')'dnl
+@result{}`_stack_reverse_sep(`tmp-$1', `$1')')
+@result{}define(`_stack_reverse_sep',
+@result{}`ifdef(`$1', `pushdef(`$2', defn(`$1'))$3`'popdef(`$1')$0(
+@result{}  `$1', `$2', `$4`'$3')')')
+@result{}divert`'dnl
+@end example
+
 @node Improved m4wrap
 @section Solution for @code{m4wrap}
 
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 1a97347..0db3a88 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -58,6 +58,7 @@ quote.m4 \
 regexp.m4 \
 reverse.m4 \
 stack.m4 \
+stack_sep.m4 \
 stackovf.sh \
 sync-lines.m4 \
 sysv-args.m4 \
diff --git a/examples/stack.m4 b/examples/stack.m4
index ae3c48e..c1b9833 100644
--- a/examples/stack.m4
+++ b/examples/stack.m4
@@ -1,11 +1,11 @@
 divert(`-1')
-# stack_foreach(action, macro)
+# stack_foreach(macro, action)
 # Invoke ACTION with a single argument of each definition
 # from the definition stack of MACRO, starting with the oldest.
 define(`stack_foreach',
 `_stack_reverse(`$1', `tmp-$1')'dnl
 `_stack_reverse(`tmp-$1', `$1', `$2(defn(`$1'))')')
-# stack_foreach_lifo(action, macro)
+# stack_foreach_lifo(macro, action)
 # Invoke ACTION with a single argument of each definition
 # from the definition stack of MACRO, starting with the newest.
 define(`stack_foreach_lifo',
diff --git a/examples/stack_sep.m4 b/examples/stack_sep.m4
new file mode 100644
index 0000000..b11bc83
--- /dev/null
+++ b/examples/stack_sep.m4
@@ -0,0 +1,17 @@
+divert(`-1')
+# stack_foreach_sep(macro, pre, post, sep)
+# Invoke PRE`'defn`'POST with a single argument of each definition
+# from the definition stack of MACRO, starting with the oldest, and
+# separated by SEP between definitions.
+define(`stack_foreach_sep',
+`_stack_reverse_sep(`$1', `tmp-$1')'dnl
+`_stack_reverse_sep(`tmp-$1', `$1', `$2`'defn(`$1')$3', `$4')')
+# stack_foreach_sep_lifo(macro, pre, post, sep)
+# Like stack_foreach_sep, but starting with the newest definition.
+define(`stack_foreach_sep_lifo',
+`_stack_reverse_sep(`$1', `tmp-$1', `$2`'defn(`$1')$3', `$4')'dnl
+`_stack_reverse_sep(`tmp-$1', `$1')')
+define(`_stack_reverse_sep',
+`ifdef(`$1', `pushdef(`$2', defn(`$1'))$3`'popdef(`$1')$0(
+  `$1', `$2', `$4`'$3')')')
+divert`'dnl
-- 
1.6.0.4
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.