On Fri, Apr 04, 2025 at 03:52:54AM -0400, Douglas McIlroy wrote:
> I'm delighted to receive the progress report, and especially the
> mention of tail recursion. Am I right in guessing that all tail calls
> will benefit, so that n levels of tail recursion will use O(1) stack
> space rather than O(n)?

Tail recursion in m4 already uses O(1) stack rather than O(n)
(execution-wise, m4 collects the expansion of the prior macro before
starting to rescan it to start collecting arguments to the next pass).
You can watch that by using 'm4 -daeqt file.m4' on something that uses
shift($@) tail recursion, and see how each iteration is still at the
same call depth and memory overhead (different from macro nesting,
where an unquoted parameter is expanded before the outer macro is
called, and where memory usage grows as there remains more levels of
macro expansion to manage).  Still, tail recursion requiring O(n)
parsing on each iteration is what made m4 1.4 O(n^2) in time instead
of O(n) for any recursive algorithm that processes $@ one item at a
time with shift().  Thankfully, back in 2007, I also figured out that
even on m4 1.4, there are other ways to get O(n) iteration, such as
pushdef stacks or by the use of GNU m4 extensions such as patsubst()
or the ability to address $10 and beyond (POSIX only requires support
for addressing up to $9, anything greater requires shift) to rewrite
long $@ lists into O(N) processing in m4sugar's m4_foreach.

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:  qemu.org | libguestfs.org


Reply via email to