[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: m4sugar speedups
From: |
Eric Blake |
Subject: |
Re: m4sugar speedups |
Date: |
Sat, 13 Oct 2007 08:36:47 -0600 |
User-agent: |
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.6) Gecko/20070728 Thunderbird/2.0.0.6 Mnenhy/0.7.5.666 |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
According to Eric Blake on 10/2/2007 7:07 AM:
>
> And while I'm at it, the regex in m4_version_cmp can be avoided (since
> m4_version_unletter generates output that is all numeric, we don't have to
> worry about re-quoting active macros, so a translit is faster than
> m4_split). This in turn speeds up AC_PREREQ, which is used rather frequently.
Well, maybe AC_PREREQ is not used _that_ frequently (only 19 times in
coreutils, and 5 times in autoconf itself). But this patch:
a) makes m4_version_unletter robust, fixing it so that
m4_define([a],[oops])AC_PREREQ([2.61a])
no longer gives bogus results
b) makes m4_version_unletter handle multi-letter versions, like 2.61aa
(let's hope we never make it there :) or, as is more likely with other
packages, 1.2pre.
c) greatly reduces the number of expansions and regex uses!
2.61 m4_version_prereq([2.60b]) 476 14
2.61 m4_version_prereq([2.61]) 385 12
pre-patch m4_version_prereq([2.60b]) 109 6
pre-patch m4_version_prereq([2.61a]) 153 6
pre-patch m4_version_prereq([2.61b]) 163 6
post-patch m4_version_prereq([2.60b]) 35 2
post-patch m4_version_prereq([2.61a]) 69 2
post-patch m4_version_prereq([2.61b]) 11 2
Not to mention it uses a COOL m4 feature - m4_list_cmp now bases its
recursion decision based on a double argument list (the implementation is
roughly [macro1(args1)(args2)], where the expansion of macro1 produces the
name of macro2).
2007-10-13 Eric Blake <address@hidden>
Make AC_PREREQ faster and more robust.
* lib/m4sugar/m4sugar.m4 (m4_ignore, m4_unquote): New macros.
(m4_version_prereq): Inline constant expansions.
(m4_list_cmp): Reduce number of expansions, by avoiding m4_case.
Rewrite in terms of [] list, not () list.
(_m4_list_cmp, _m4_version_unletter): New helper macros.
(m4_version_unletter): Write wrapper around new implementation to
preserve old semantics.
(m4_version_compare): Pass correct type of list, and avoid
overhead of flattening expressions too early.
(m4_do): Move to be near other quoting macros.
(m4_max, m4_min): Always result in decimal output.
* doc/autoconf.texi (Looping constructs): Add m4_car, m4_cdr.
Move m4_do...
(Evaluation Macros): ...here. Add m4_ignore, m4_unquote.
(Text processing Macros): Move m4_version_compare...
(Number processing Macros): ...to this new node; document m4_cmp,
m4_list_cmp, m4_sign, m4_max, m4_min.
* tests/m4sugar.at (m4@&address@hidden): Enhance test, to pick
up on bugs fixed by this patch.
* NEWS: Document new macros.
- --
Don't work too hard, make some time for fun as well!
Eric Blake address@hidden
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (Cygwin)
Comment: Public key at home.comcast.net/~ericblake/eblake.gpg
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
iD8DBQFHENf+84KuGfSFAYARAtViAKCDxS8NrKjKi6nD1v1uWed/VGp3BgCeMJN3
OwqQaDkfuXSL5QmMzTf/opg=
=GcLK
-----END PGP SIGNATURE-----
>From 6e1d6e2c5bea266157056e5fce5fe9113f14002e Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Sat, 13 Oct 2007 07:47:46 -0600
Subject: [PATCH] Make AC_PREREQ faster and more robust.
* lib/m4sugar/m4sugar.m4 (m4_ignore, m4_unquote): New macros.
(m4_version_prereq): Inline constant expansions.
(m4_list_cmp): Reduce number of expansions, by avoiding m4_case.
Rewrite in terms of [] list, not () list.
(_m4_list_cmp, _m4_version_unletter): New helper macros.
(m4_version_unletter): Write wrapper around new implementation to
preserve old semantics.
(m4_version_compare): Pass correct type of list, and avoid
overhead of flattening expressions too early.
(m4_do): Move to be near other quoting macros.
(m4_max, m4_min): Always result in decimal output.
* doc/autoconf.texi (Looping constructs): Add m4_car, m4_cdr.
Move m4_do...
(Evaluation Macros): ...here. Add m4_ignore, m4_unquote.
(Text processing Macros): Move m4_version_compare...
(Number processing Macros): ...to this new node; document m4_cmp,
m4_list_cmp, m4_sign, m4_max, m4_min.
* tests/m4sugar.at (m4@&address@hidden): Enhance test, to pick
up on bugs fixed by this patch.
* NEWS: Document new macros.
Signed-off-by: Eric Blake <address@hidden>
---
ChangeLog | 24 +++++++
NEWS | 19 +++--
doc/autoconf.texi | 179 +++++++++++++++++++++++++++++++++++++++++------
lib/m4sugar/m4sugar.m4 | 160 +++++++++++++++++++++++++-----------------
tests/m4sugar.at | 9 +++
5 files changed, 295 insertions(+), 96 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 7c3bb04..ac752fd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,27 @@
+2007-10-13 Eric Blake <address@hidden>
+
+ Make AC_PREREQ faster and more robust.
+ * lib/m4sugar/m4sugar.m4 (m4_ignore, m4_unquote): New macros.
+ (m4_version_prereq): Inline constant expansions.
+ (m4_list_cmp): Reduce number of expansions, by avoiding m4_case.
+ Rewrite in terms of [] list, not () list.
+ (_m4_list_cmp, _m4_version_unletter): New helper macros.
+ (m4_version_unletter): Write wrapper around new implementation to
+ preserve old semantics.
+ (m4_version_compare): Pass correct type of list, and avoid
+ overhead of flattening expressions too early.
+ (m4_do): Move to be near other quoting macros.
+ (m4_max, m4_min): Always result in decimal output.
+ * doc/autoconf.texi (Looping constructs): Add m4_car, m4_cdr.
+ Move m4_do...
+ (Evaluation Macros): ...here. Add m4_ignore, m4_unquote.
+ (Text processing Macros): Move m4_version_compare...
+ (Number processing Macros): ...to this new node; document m4_cmp,
+ m4_list_cmp, m4_sign, m4_max, m4_min.
+ * tests/m4sugar.at (m4@&address@hidden): Enhance test, to pick
+ up on bugs fixed by this patch.
+ * NEWS: Document new macros.
+
2007-10-12 Eric Blake <address@hidden>
* doc/autoconf.texi (Text processing Macros): Fix bad merge.
diff --git a/NEWS b/NEWS
index 00d3019..95c7e3b 100644
--- a/NEWS
+++ b/NEWS
@@ -78,18 +78,20 @@ GNU Autoconf NEWS - User visible changes.
- The following macros were previously available as undocumented
interfaces; the macros are now documented as stable interfaces.
- __oline__ m4_assert m4_bmatch m4_bpatsubsts m4_case
- m4_default m4_divert_once m4_divert_pop m4_divert_push
+ __oline__ m4_assert m4_bmatch m4_bpatsubsts m4_car m4_case
+ m4_cdr m4_default m4_divert_once m4_divert_pop m4_divert_push
m4_divert_text m4_do m4_errprintn m4_fatal m4_flatten
m4_ifndef m4_ifset m4_ifval m4_ifvaln m4_location
- m4_n m4_shiftn m4_strip m4_version_compare m4_warn
+ m4_n m4_shiftn m4_strip m4_warn
- The following macros were previously available as undocumented
- interfaces, but had bugs. Packages that relied on the
- undocumented and buggy behavior should analyze their code to make
- sure it still works with the new documented behavior.
+ interfaces, but had bug fixes or semantic changes as part of this
+ release. Packages that relied on the undocumented behavior
+ should be analyzed to make sure they will still work with the
+ new documented behavior.
- m4_join m4_text_box m4_text_wrap
+ m4_cmp m4_list_cmp m4_join m4_sign m4_text_box m4_text_wrap
+ m4_version_compare
- Packages using the undocumented m4sugar macro m4_PACKAGE_VERSION
should consider using the new AC_AUTOCONF_VERSION instead.
@@ -115,7 +117,8 @@ GNU Autoconf NEWS - User visible changes.
be used to take action depending on whether anything was appended.
** The following m4sugar macros are new:
- m4_cond m4_newline m4_shift2 m4_shift3
+ m4_cond m4_ignore m4_max m4_min m4_newline m4_shift2
+ m4_shift3 m4_unquote
** Warnings are now generated by default when an installer invokes
'configure' with an unknown --enable-* or --with-* option.
diff --git a/doc/autoconf.texi b/doc/autoconf.texi
index 26ca6fd..f63dfa2 100644
--- a/doc/autoconf.texi
+++ b/doc/autoconf.texi
@@ -455,6 +455,7 @@ Programming in M4sugar
* Looping constructs:: Iteration in M4
* Evaluation Macros:: More quotation and evaluation control
* Text processing Macros:: String manipulation in M4
+* Number processing Macros:: Arithmetic computation in M4
* Forbidden Patterns:: Catching unexpanded macros
Writing Autoconf Macros
@@ -10087,6 +10088,7 @@ define your own macros into these namespaces.
* Looping constructs:: Iteration in M4
* Evaluation Macros:: More quotation and evaluation control
* Text processing Macros:: String manipulation in M4
+* Number processing Macros:: Arithmetic computation in M4
* Forbidden Patterns:: Catching unexpanded macros
@end menu
@@ -10585,7 +10587,42 @@ Expand to @var{text}, and add a newline if @var{text}
is not empty.
@node Looping constructs
@subsection Looping constructs
-The following macros implement loops in M4.
+The following macros are useful in implementing recursive algorithms in
+M4, including loop operations. An M4 list is formed by quoting a list
+of quoted elements; generally the lists are comma-separated, although
address@hidden is whitespace-separated. For example, the list
address@hidden, [b,c]]} contains two elements: @samp{[a]} and @samp{[b,c]}.
+It is common to see lists with unquoted elements when those elements are
+not likely to be macro names, as in @samp{[fputc_unlocked,
+fgetc_unlocked]}.
+
address@hidden m4_car (@var{list})
address@hidden
+Expands to the quoted first element of the comma-separated quoted
address@hidden Often used with @code{m4_cdr} to recursively iterate
+through a list. Generally, when using quoted lists of quoted elements,
address@hidden should be called without any extra quotes.
address@hidden defmac
+
address@hidden m4_cdr (@var{list})
address@hidden
+Expands to a quoted list of all but the first element of the
+comma-separated quoted @var{list}, or the empty string if @var{list} had
+only one element. Generally, when using quoted lists of quoted
+elements, @code{m4_cdr} should be called without any extra quotes.
+
+For example, this is an implementation of @code{m4_map}; note how each
+iteration of the helper macro @code{_m4_map} checks for the end of
+recursion, then merely applies the first argument to the first element
+of the list, then recurses with the rest of the list.
address@hidden
+m4_define([m4_map], [m4_if([$2], [[]], [], [_$0($@@)])])dnl
+m4_define([_m4_map], [m4_ifval([$2],
+ [$1(m4_unquote(m4_car($2)))[]$0([$1], m4_cdr($2))])])dnl
+m4_map([ m4_eval], [[1],[1+1]])
address@hidden 1 2
address@hidden example
address@hidden defmac
@defmac m4_for (@var{var}, @var{first}, @var{last}, @ovar{step}, @
@var{expression})
@@ -10607,8 +10644,9 @@ outputs two lines:
@example
m4_foreach([myvar], [[foo], [bar, baz]],
[echo myvar
-])
-
+])dnl
address@hidden foo
address@hidden bar, baz
@end example
@end defmac
@@ -10622,14 +10660,7 @@ The deprecated macro @code{AC_FOREACH} is an alias of
@code{m4_foreach_w}.
@end defmac
-The following macros are useful in implementing recursive algorithms.
-
address@hidden m4_do (@dots{})
address@hidden
-This macro loops over its arguments and expands each one in sequence.
-Its main use is for readability; it allows the use of indentation and
-fewer @code{dnl} to result in the same expansion.
address@hidden defmac
address@hidden TODO document m4_map, m4_map_sep
@defmac m4_shiftn (@var{count}, @dots{})
@defmacx m4_shift2 (@dots{})
@@ -10649,18 +10680,59 @@ for two and three shifts, respectively.
@subsection Evaluation Macros
The following macros give some control over the order of the evaluation
-by adding or removing levels of quotes. They are meant for hard-core M4
-programmers.
+by adding or removing levels of quotes.
+
address@hidden m4_do (@var{arg1}, @dots{})
address@hidden
+This macro loops over its arguments and expands each @var{arg} in
+sequence. Its main use is for readability; it allows the use of
+indentation and fewer @code{dnl} to result in the same expansion.
address@hidden defmac
@defmac m4_dquote (@var{arg1}, @dots{})
@msindex{dquote}
Return the arguments as a quoted list of quoted arguments.
+Conveniently, if there is just one @var{arg}, this effectively adds a
+level of quoting.
@end defmac
address@hidden m4_ignore (@dots{})
address@hidden
+This macro was introduced in Autoconf 2.62. Expands to nothing,
+ignoring all of its arguments. By itself, this isn't very useful.
+However, it can be used to conditionally ignore an arbitrary number of
+arguments, by making a decision about which macro name to apply to a
+list of arguments.
address@hidden
+dnl foo outputs a message only if [debug] is defined.
+m4_define([foo],
+[m4_ifdef([debug],[AC_MSG_NOTICE],[m4_ignore])([debug message])])
address@hidden example
+
+Note that for earlier versions of Autoconf, the macro @code{__gnu__} can
+serve the same purpose, although it is less readable.
address@hidden defmac
+
address@hidden m4_noquote is too dangerous to document - it invokes macros that
address@hidden probably rely on @samp{[]} nested quoting for proper operation.
The
address@hidden user should generally prefer m4_unquote instead.
+
@defmac m4_quote (@var{arg1}, @dots{})
@msindex{quote}
Return the arguments as a single entity, i.e., wrap them into a pair of
-quotes.
+quotes. This effectively collapses multiple arguments into one,
+although it loses whitespace after unquoted commas in the process.
address@hidden defmac
+
address@hidden m4_unquote (@var{arg1}, @dots{})
address@hidden
+This macro was introduced in Autoconf 2.62. Expand each argument,
+separated by commas. For a single @var{arg}, this effectively removes a
+layer of quoting, and @code{m4_unquote(address@hidden)} is more efficient
+than the equivalent @code{m4_do(address@hidden)}. For multiple arguments,
+this results in an unquoted list of expansions. This is commonly used
+with @code{m4_split}, in order to convert a single quoted list into a
+series of quoted elements.
@end defmac
The following example aims at emphasizing the difference between (i), not
@@ -10695,7 +10767,7 @@ mkargs
@node Text processing Macros
address@hidden Text processing Macros
address@hidden String manipulation in M4
The following macros may be used to manipulate strings in M4.
They are not intended for casual use.
@@ -10852,17 +10924,76 @@ Return @var{string} with letters converted to upper
or lower case,
respectively.
@end defmac
address@hidden Number processing Macros
address@hidden Arithmetic computation in M4
+
+The following macros make it easier to deal with arithmetic operations.
+Where a parameter is documented as taking an arithmetic expression, you
+can use anything that can be parsed by @code{m4_eval}.
+
address@hidden m4_cmp (@var{expr-1}, @var{expr-2})
address@hidden
+Compare the arithmetic expressions @var{expr-1} and @var{expr-2}, and
+expand to @samp{-1} if @var{expr-1} is smaller, @samp{0} if they are
+equal, and @samp{1} if @var{expr-1} is larger.
address@hidden defmac
+
address@hidden m4_list_cmp (@var{list-1}, @var{list-2})
address@hidden
+Compare the two M4 lists consisting of comma-separated arithmetic
+expressions, left to right. Expand to @samp{-1} for the first element
+pairing where the value from @var{list-1} is smaller, @samp{1} where the
+value from @var{list-2} is smaller, or @samp{0} if both lists have the
+same values. If one list is shorter than the other, the remaining
+elements of the longer list are compared against 0.
address@hidden
+m4_list_cmp([1, 0], [1])
address@hidden
+m4_list_cmp([1, [1 * 0]], [1, 0])
address@hidden
+m4_list_cmp([1, 2], [1, 0])
address@hidden
+m4_list_cmp([1, [1+1], 3],[1, 2])
address@hidden
+m4_list_cmp([1, 2, -3], [1, 2])
address@hidden
+m4_list_cmp([1, 0], [1, 2])
address@hidden
+m4_list_cmp([1], [1, 2])
address@hidden
address@hidden example
address@hidden defmac
+
address@hidden m4_max (@var{arg1}, @dots{})
address@hidden
+This macro was introduced in Autoconf 2.62. Expand to the decimal value
+of the maximum arithmetic expression among all the arguments.
address@hidden defmac
+
address@hidden m4_min (@var{arg1}, @dots{})
address@hidden
+This macro was introduced in Autoconf 2.62. Expand to the decimal value
+of the minimum arithmetic expression among all the arguments.
address@hidden defmac
+
address@hidden m4_sign (@var{expr})
address@hidden
+Expand to @samp{-1} if the arithmetic expression @var{expr} is negative,
address@hidden if it is positive, and @samp{0} if it is zero.
address@hidden defmac
+
@anchor{m4_version_compare}
@defmac m4_version_compare (@var{version-1}, @var{version-2})
@msindex{version_compare}
-This macro was introduced in Autoconf 2.53. Compare the version strings
address@hidden and @var{version-2}, and expand to @samp{-1} if
address@hidden is smaller, @samp{0} if they are the same, or @samp{1}
address@hidden is smaller. Version strings must be a list of elements
-separated by @samp{.}, where each element is a number along with an
-optional lower case letter. The comparison stops at the leftmost
-element that contains a difference, although a 0 element compares equal
-to a missing element.
+This macro was introduced in Autoconf 2.53, but had a number of
+usability limitations that were not lifted until Autoconf 2.62. Compare
+the version strings @var{version-1} and @var{version-2}, and expand to
address@hidden if @var{version-1} is smaller, @samp{0} if they are the same,
+or @samp{1} @var{version-2} is smaller. Version strings must be a list
+of elements separated by @samp{.}, where each element is a number along
+with optional case-insensitive letters designating beta releases. The
+comparison stops at the leftmost element that contains a difference,
+although a 0 element compares equal to a missing element.
@example
m4_version_compare([1.1], [2.0])
@@ -10875,6 +11006,8 @@ m4_version_compare([1.2], [1.1.1a])
@result{}1
m4_version_compare([1.0], [1])
@result{}0
+m4_version_compare([1.1pre], [1.1PRE])
address@hidden
m4_version_compare([1.1a], [1,10])
@result{}-1
@end example
diff --git a/lib/m4sugar/m4sugar.m4 b/lib/m4sugar/m4sugar.m4
index 677695b..dd5138e 100644
--- a/lib/m4sugar/m4sugar.m4
+++ b/lib/m4sugar/m4sugar.m4
@@ -491,18 +491,6 @@ m4_define([_m4_bpatsubsts],
m4_shift3($@))])])
-
-# m4_do(STRING, ...)
-# ------------------
-# This macro invokes all its arguments (in sequence, of course). It is
-# useful for making your macros more structured and readable by dropping
-# unnecessary dnl's and have the macros indented properly.
-m4_define([m4_do],
-[m4_if($#, 0, [],
- $#, 1, [$1],
- [$1[]m4_do(m4_shift($@))])])
-
-
# m4_define_default(MACRO, VALUE)
# -------------------------------
# If MACRO is undefined, set it to VALUE.
@@ -610,16 +598,15 @@ m4_define([m4_undefine],
## 7. Quoting manipulation. ##
## ------------------------- ##
-# m4_quote(ARGS)
-# --------------
-# Return ARGS as a single argument. Any whitespace after unquoted commas
-# is stripped.
-#
-# It is important to realize the difference between `m4_quote(exp)' and
-# `[exp]': in the first case you obtain the quoted *result* of the
-# expansion of EXP, while in the latter you just obtain the string
-# `exp'.
-m4_define([m4_quote], [[$*]])
+# m4_do(STRING, ...)
+# ------------------
+# This macro invokes all its arguments (in sequence, of course). It is
+# useful for making your macros more structured and readable by dropping
+# unnecessary dnl's and have the macros indented properly.
+m4_define([m4_do],
+[m4_if([$#], 0, [],
+ [$#], 1, [$1],
+ [$1[]m4_do(m4_shift($@))])])
# m4_dquote(ARGS)
@@ -628,16 +615,47 @@ m4_define([m4_quote], [[$*]])
m4_define([m4_dquote], address@hidden)
+# m4_ignore(ARGS)
+# ---------------
+# Expands to nothing. Useful for conditionally ignoring an arbitrary
+# number of arguments (see _m4_list_cmp for an example).
+m4_define([m4_ignore])
+
+
# m4_noquote(STRING)
# ------------------
# Return the result of ignoring all quotes in STRING and invoking the
# macros it contains. Amongst other things, this is useful for enabling
# macro invocations inside strings with [] blocks (for instance regexps
-# and help-strings).
+# and help-strings). On the other hand, since all quotes are disabled,
+# any macro expanded during this time that relies on nested [] quoting
+# will likely crash and burn. This macro is seldom useful; consider
+# m4_unquote instead.
m4_define([m4_noquote],
[m4_changequote(-=<{,}>=-)$1-=<{}>=-m4_changequote([,])])
+# m4_quote(ARGS)
+# --------------
+# Return ARGS as a single argument. Any whitespace after unquoted commas
+# is stripped.
+#
+# It is important to realize the difference between `m4_quote(exp)' and
+# `[exp]': in the first case you obtain the quoted *result* of the
+# expansion of EXP, while in the latter you just obtain the string
+# `exp'.
+m4_define([m4_quote], [[$*]])
+
+
+# m4_unquote(ARGS)
+# ----------------
+# Remove one layer of quotes from each ARG, performing one level of
+# expansion. For one argument, m4_unquote([arg]) is more efficient than
+# m4_do([arg]), but for multiple arguments, the difference is that
+# m4_unquote separates arguments with commas while m4_do concatenates.
+m4_define([m4_unquote], [$*])
+
+
## -------------------------- ##
## 8. Implementing m4 loops. ##
## -------------------------- ##
@@ -1850,42 +1868,50 @@ m4_define([m4_cmp],
# m4_list_cmp(A, B)
# -----------------
#
-# Compare the two lists of integers A and B. For instance:
-# m4_list_cmp((1, 0), (1)) -> 0
-# m4_list_cmp((1, 0), (1, 0)) -> 0
-# m4_list_cmp((1, 2), (1, 0)) -> 1
-# m4_list_cmp((1, 2, 3), (1, 2)) -> 1
-# m4_list_cmp((1, 2, -3), (1, 2)) -> -1
-# m4_list_cmp((1, 0), (1, 2)) -> -1
-# m4_list_cmp((1), (1, 2)) -> -1
+# Compare the two lists of integer expressions A and B. For instance:
+# m4_list_cmp([1, 0], [1]) -> 0
+# m4_list_cmp([1, 0], [1, 0]) -> 0
+# m4_list_cmp([1, 2], [1, 0]) -> 1
+# m4_list_cmp([1, 2, 3], [1, 2]) -> 1
+# m4_list_cmp([1, 2, -3], [1, 2]) -> -1
+# m4_list_cmp([1, 0], [1, 2]) -> -1
+# m4_list_cmp([1], [1, 2]) -> -1
+# m4_define([xa], [oops])dnl
+# m4_list_cmp([[0xa]], [5+5]) -> 0
+#
+# Rather than face the overhead of m4_case, we use a helper function whose
+# expansion includes the name of the macro to invoke on the tail, either
+# m4_ignore or m4_unquote. This is particularly useful when comparing
+# long lists, since less text is being expanded to determine when to recurse.
m4_define([m4_list_cmp],
-[m4_if([$1$2], [()()], 0,
- [$1], [()], [$0((0), [$2])],
- [$2], [()], [$0([$1], (0))],
- [m4_case(m4_cmp(m4_car$1, m4_car$2),
- -1, -1,
- 1, 1,
- 0, [$0((m4_shift$1), (m4_shift$2))])])])
-
-# m4_max(A, B, ...)
-# m4_min(A, B, ...)
+[m4_if([$1$2], [], 0,
+ [$1], [], [$0(0, [$2])],
+ [$2], [], [$0([$1], 0)],
+ [$1], [$2], 0,
+ [_$0(m4_cmp(m4_car($1), m4_car($2)))([$0(m4_cdr($1), m4_cdr($2))])])])
+m4_define([_m4_list_cmp],
+[m4_if([$1], 0, [m4_unquote], [$1m4_ignore])])
+
+# m4_max(EXPR, ...)
+# m4_min(EXPR, ...)
# -----------------
-# Return the maximum (or minimum) of a series of integer expressions.
+# Return the decimal value of the maximum (or minimum) in a series of
+# integer expressions.
#
# M4 1.4.x doesn't provide ?:. Hence this huge m4_eval. Avoid m4_eval
# if both arguments are identical, but be aware of m4_max(0xa, 10) (hence
# the use of <=, not just <, in the second multiply).
m4_define([m4_max],
[m4_if([$#], [0], [m4_fatal([too few arguments to $0])],
- [$#], [1], [$1],
- [$#$1], [2$2], [$1],
+ [$#], [1], [m4_eval([$1])],
+ [$#$1], [2$2], [m4_eval([$1])],
[$#], [2],
[m4_eval((([$1]) > ([$2])) * ([$1]) + (([$1]) <= ([$2])) * ([$2]))],
[$0($0([$1], [$2]), m4_shift2($@))])])
m4_define([m4_min],
[m4_if([$#], [0], [m4_fatal([too few arguments to $0])],
- [$#], [1], [$1],
- [$#$1], [2$2], [$1],
+ [$#], [1], [m4_eval([$1])],
+ [$#$1], [2$2], [m4_eval([$1])],
[$#], [2],
[m4_eval((([$1]) < ([$2])) * ([$1]) + (([$1]) >= ([$2])) * ([$2]))],
[$0($0([$1], [$2]), m4_shift2($@))])])
@@ -1906,23 +1932,28 @@ m4_define([m4_sign],
# m4_version_unletter(VERSION)
# ----------------------------
-# Normalize beta version numbers with letters to numbers only for comparison.
+# Normalize beta version numbers with letters to numeric expressions, which
+# can then be handed to m4_eval for the purpose of comparison.
#
# Nl -> (N+1).-1.(l#)
#
-#i.e., 2.14a -> 2.15.-1.1, 2.14b -> 2.15.-1.2, etc.
-# This macro is absolutely not robust to active macro, it expects
-# reasonable version numbers and is valid up to `z', no double letters.
+# for example:
+# [2.14a] -> [2.14+1.-1.[0r36:a]] -> 2.15.-1.10
+# [2.14b] -> [2.15+1.-1.[0r36:b]] -> 2.15.-1.11
+# [2.61aa.b] -> [2.61+1.-1.[0r36:aa],+1.-1.[0r36:b]] -> 2.62.-1.370.1.-1.11
+#
+# This macro expects reasonable version numbers, but can handle double
+# letters and does not expand one-letter macros. Inline constant expansions,
+# to avoid m4_defn overhead. _m4_version_unletter is the real workhorse
+# used by m4_version_compare, but since [0r36:a] is less readable than 10,
+# we provide a wrapper for human use.
m4_define([m4_version_unletter],
-[m4_translit(m4_bpatsubsts(m4_tolower([[$1]]),
- [\([0-9]+\)\([abcdefghi]\)],
- [m4_eval(\1 + 1).-1.\2],
- [\([0-9]+\)\([jklmnopqrs]\)],
- [m4_eval(\1 + 1).-1.1\2],
- [\([0-9]+\)\([tuvwxyz]\)],
- [m4_eval(\1 + 1).-1.2\2]),
- [abcdefghijklmnopqrstuvwxyz],
- [12345678901234567890123456])])
+[m4_map_sep([m4_eval], [.], _$0([$1]))])
+m4_define([_m4_version_unletter],
+[m4_translit(m4_bpatsubst([[[$1]]], ]dnl
+m4_dquote(m4_dquote(m4_defn([m4_cr_Letters])))[[+],
+ [+1.-1.[0r36:\&]]),
+ [.], [,])])
# m4_version_compare(VERSION-1, VERSION-2)
@@ -1932,8 +1963,7 @@ m4_define([m4_version_unletter],
# 0 if =
# 1 if >
m4_define([m4_version_compare],
-[m4_list_cmp((m4_translit(m4_version_unletter([$1]), [.], [,])),
- (m4_translit(m4_version_unletter([$2]), [.], [,])))])
+[m4_list_cmp(_m4_version_unletter([$1]), _m4_version_unletter([$2]))])
# m4_PACKAGE_NAME
@@ -1949,12 +1979,12 @@ m4_include([m4sugar/version.m4])
# ----------------------------------------------------
# Check this Autoconf version against VERSION.
m4_define([m4_version_prereq],
-[m4_if(m4_version_compare(m4_defn([m4_PACKAGE_VERSION]), [$1]), -1,
+[m4_if(m4_version_compare(]m4_dquote(m4_defn([m4_PACKAGE_VERSION]))[, [$1]),
+ [-1],
[m4_default([$3],
[m4_fatal([Autoconf version $1 or higher is required],
- 63)])],
- [$2])[]dnl
-])
+ [63])])],
+ [$2])])
diff --git a/tests/m4sugar.at b/tests/m4sugar.at
index 3c537e0..a946104 100644
--- a/tests/m4sugar.at
+++ b/tests/m4sugar.at
@@ -313,6 +313,11 @@ m4_version_compare([1.2], [1.1.1a])
m4_version_compare([1.0], [1])
m4_version_compare([1.0a], [1.0a])
m4_version_compare([1.1a], [1.1a.1])
+m4_version_compare([1.10], [1.1a])
+m4_version_compare([1.1a], [1.1A])
+m4_define([a], [oops])dnl
+m4_version_compare([1.1a], [1.1A])
+m4_version_compare([1z], [1aa])
]],
[[-1
1
@@ -322,6 +327,10 @@ m4_version_compare([1.1a], [1.1a.1])
0
0
-1
+1
+0
+0
+-1
]])
AT_CLEANUP
--
1.5.3.2