[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: format and eval
From: |
Eric Blake |
Subject: |
Re: format and eval |
Date: |
Sat, 06 Jan 2007 12:52:50 -0700 |
User-agent: |
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.9) Gecko/20061207 Thunderbird/1.5.0.9 Mnenhy/0.7.4.666 |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
According to Eric Blake on 1/3/2007 7:44 AM:
> This is part 1 of enhancements to format and eval. It merges
> documentation from the branch, and fixes eval to obey POSIX precedence
> rules. eval still needs some behavior merges from the branch, as well as
> my proposed extension to support variables. format also needs
> enhancements to support more % specifiers, as well as \n and other escape
> sequences, to be more like printf(1).
Part 2. Add >>> (Java's unsigned right shift, since eval is always signed
math), ?: and , to head's eval, and forward port all improvements from the
branch. Also rename : to \ for the ratio operator, since : was ambiguous
with the new ?:. Then backport the POSIX compliance portions to the
branch. Also backport the ability to check exit status in the branch
testsuite. I'm not sure whether it is worth trying to make head be 32-bit
if POSIX but intmax_t otherwise (which would mean compiling evalparse.c
twice in the context of libm4.so), or just waiting to see if the Austin
Group will give a ruling on XCU ERN 137 that would allow intmax_t math
everywhere; but for the branch, I left things with int32_t to match
current POSIX wording.
This patch also means that eval(1=1) will now result in an error, as
required by POSIX (and the way BSD behaves), even though it used to be an
undocumented synonym for eval(1==1) (and still behaves this way on
Solaris, meaning Solaris is not POSIX compliant). I'm not sure if any
existing m4 scripts depended on this undocumented behavior. If you think
it wise, I could do a followup patch to edit the branch to issue a warning
but still perform the equality test, rather than this patch's behavior of
treating = as a hard error.
mpeval has still got some major issues - there are loads of memory leaks,
radix 1 handling doesn't really work, and there are probably some
inefficiencies that can be minimized.
Also on my TODO list - figuring out how to support variable semantics.
That introduces a wrinkle, as we must make sure that invalid parses, such
as eval(a=1,+), as well as short circuits, such as eval(1||(a=1)), do not
modify a, while eval(a=1,a) results in 1, and even trickier,
eval(1||(a=1),a) results in an error if a started out undefined. But the
current recursive descent parser wants to perform all calculations up
front; so I have to rewrite it a bit to perform the complete parse first
before committing to any macro definition modifications.
head:
2007-01-06 Eric Blake <address@hidden>
* m4/m4private.h (to_uchar): Move...
* m4/m4module.h: here, so modules can use it.
* src/main.c (usage, long_options): Support --posix as a synonym
for --traditional.
* modules/m4.c (numb_invert): Detect unsupported x**-y.
(numb_incr, numb_urshift, numb_extension): New macros.
(numb_obstack): Port patch from branch to support radix 1 output.
* modules/mpeval.c (numb_obstack): Use \ for ratios.
(numb_incr, numb_urshift, numb_extension): New macros.
(mpq2mpz): Warn rather than error.
* modules/evalparse.c (URSHIFT, NEGATIVE_EXPONENT): New
enumerators.
(eval_error): Rearrange, so that all ignorable errors are less
than SYNTAX_ERROR.
(eval_text, last_text): Obey C++ type safety rules.
(numb_pow): Change return type.
(exp_term): Adjust caller.
(m4_evaluate): Fail on bad exponent.
(logical_or_term, logical_and_term): Ignore failed ** in short
circuit.
(eval_lex): Fix typos when detecting <<= and >>=. Use \ instead
of : for ratio. Support >>>, ,, and ?:. Port patch from branch
to parse radix 1 numbers.
(condition_term, comma_term): New parse functions.
(shift_term): Support >>>.
(m4_evaluate): Port patch from branch to output radix 1 numbers.
Warn instead of error for most parse problems.
* doc/m4.texinfo (Limits control, Eval): Document these changes.
* NEWS: Document new operators and new command line option.
branch:
2007-01-06 Eric Blake <address@hidden>
* m4/gnulib-cache.m4: Augment with 'gnulib-tool --import stdint'.
* checks/check-them: Record expected exit status.
* checks/get-them: Check exit status.
* src/m4.h (eval_t, unsigned_eval_t): Delete, use POSIX int32_t
instead.
* src/builtin.c: All users changed.
* src/eval.c: Likewise. Also document where we are triggering
undefined or implementation-defined behavior.
(BADOP, NEGATIVE_EXPONENT, INVALID_OPERATOR, eval_lex, evaluate)
(logical_or_term, logical_and_term, logical_not_term, not_term)
(equality_term, unary_term): Port from head to follow POSIX
semantics.
(exp_term): Reject 0**0 as undefined.
* doc/m4.texinfo (History): Mention 1.4.9.
(Format, Incompatibilities): Update to document POSIX compliance.
* NEWS: Document this change.
- --
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
iD8DBQFFn/4Q84KuGfSFAYARAtsxAJ0ftS8OdvANTOIIMv4TLXl5r2ehtgCgvK0X
dLA6qgKcI81CYI/2UzeCSr0=
=MRG8
-----END PGP SIGNATURE-----
Index: NEWS
===================================================================
RCS file: /sources/m4/m4/NEWS,v
retrieving revision 1.34
diff -u -p -r1.34 NEWS
--- NEWS 3 Jan 2007 14:44:11 -0000 1.34
+++ NEWS 6 Jan 2007 19:01:23 -0000
@@ -39,13 +39,13 @@ promoted to 2.0.
*** New `-g'/`--gnu' command-line option overrides `-G'/`--traditional',
allowing the GNU module to be selected even when POSIXLY_CORRECT.
-*** The `-H'/`--hashsize' options, which were made no-ops in a previous
- beta, now issue a deprecation warning.
+*** The `-H'/`--hashsize' command-line options, which were made no-ops in
+ a previous beta, now issue a deprecation warning.
*** The `-L'/`--nesting-limit' command-line option now performs argument
validation and accepts an optional multiplier suffix.
-*** The `-o'/`--error-output' options, which were replaced by
+*** The `-o'/`--error-output' command-line options, which were replaced by
`--debugfile' in M4 1.4.7, now issue a deprecation warning. This
warning interferes with all versions of Autoconf prior to 2.61, so plan
on installing an updated Autoconf when installing M4 2.0.
@@ -54,6 +54,8 @@ promoted to 2.0.
control over macro definitions from the command line between input
files.
+*** New `--posix' command-line option is a synonym for `-G'/`--traditional'.
+
*** New `-r'/`--regexp-syntax' command-line option changes the default
regular expression syntax used by M4. Without this option, M4
continues to use EMACS style expressions. A new section in the info
@@ -109,15 +111,15 @@ promoted to 2.0.
regular expression flavor, to match `-r'/`--regexp-syntax' command-line
option.
-*** A new builtin `debuglen' is introduced which allows runtime setting
- of debug output length, previously controlled only by the `-l' command
+*** New `debuglen' builtin is introduced which allows runtime setting of
+ debug output length, previously controlled only by the `-l' command
line argument. Additionally, whether using the new macro or the
command line argument, the length limitation now affects dumpdef output
as well as trace output, undergoes argument validation, and accepts an
optional multiplier suffix.
- FIXME the multiplier suffix isn't reliable yet
-*** New builtin `mkdtemp' parallels `mkstemp', but allows the creation of
+*** New `mkdtemp' builtin parallels `mkstemp', but allows the creation of
temporary directories instead of files.
*** New `renamesyms' builtin allows programmatic renaming of all symbols
@@ -152,19 +154,23 @@ promoted to 2.0.
and remove characters from a syntax class without having to specify the
entire set of characters in that class.
-*** New `m' flag to `-d'/`--debug' option or `debugmode' macro traces
- actions related to module loading and unloading, and affects `dumpdef'
- and trace output to show where builtins come from. New `s' flag shows
- the entire stack of `pushdef' definitions during `dumpdef'. The `c'
- flag has been updated to output two lines instead of three (since the
- last two had always been paired), and to add information to the first
- line to show the definition of the macro being expanded. The 'e' flag
- has been updated to output non-text expansions.
+*** New `m' flag to `-d'/`--debug' command-line option or `debugmode'
+ builtin traces actions related to module loading and unloading, and
+ affects `dumpdef' and trace output to show where builtins come from.
+ New `s' flag shows the entire stack of `pushdef' definitions during
+ `dumpdef'. The `c' flag has been updated to output two lines instead
+ of three (since the last two had always been paired), and to add
+ information to the first line to show the definition of the macro being
+ expanded. The 'e' flag has been updated to output non-text expansions.
-*** The `dumpdef' macro now always outputs to standard error, rather than
+*** The `dumpdef' builtin now always outputs to standard error, rather than
the debug file specified by the `--debugfile' option or `debugfile'
macro.
+*** The `eval' and `mpeval' builtins now support the following new
+ operators: `>>>', `\', `?:', and `,'. However, when complying
+ strictly with POSIX, `?:' and `,' are rejected inside `eval'.
+
*** The `maketemp' builtin now always warns that it is obsolete, even in GNU
mode where it uses the same secure algorithm as `mkstemp', because of
the recommendation of POSIX to obsolete `maketemp' as inherently
@@ -174,10 +180,11 @@ promoted to 2.0.
to use a different regular expression syntax for the duration of that
invocation.
-*** The semantics of `traceon' and `traceoff' now match traditional
- implementations: when called without arguments, they affect global
- state rather than affecting only the macros defined at that moment.
- The manual includes an example of how to recover 1.4.x semantics.
+*** The semantics of the `traceon' and `traceoff' builtins now match
+ traditional implementations: when called without arguments, they affect
+ global state rather than affecting only the macros defined at that
+ moment. The manual includes an example of how to recover 1.4.x
+ semantics.
** Other changes
@@ -189,6 +196,7 @@ promoted to 2.0.
FIXME: include the (long) list of changes in 1.4.x that were not already
in earlier betas.
+* Version 1.4.9
* Version 1.4.8
* Version 1.4.7
* Version 1.4.6
Index: doc/m4.texinfo
===================================================================
RCS file: /sources/m4/m4/doc/m4.texinfo,v
retrieving revision 1.88
diff -u -p -r1.88 m4.texinfo
--- doc/m4.texinfo 3 Jan 2007 14:44:11 -0000 1.88
+++ doc/m4.texinfo 6 Jan 2007 19:01:24 -0000
@@ -508,6 +508,7 @@ be loaded to use that macro. The standa
m4 extensions), and @samp{traditional} (for compatibility with System V
m4). @xref{Modules}.
address@hidden numbers
All macro arguments in @code{m4} are strings, but some are given
special interpretation, e.g., as numbers, file names, regular
expressions, etc. The documentation for each macro will state how the
@@ -812,6 +813,7 @@ default unless @env{POSIXLY_CORRECT} is
exists to allow overriding @option{--traditional}.
@item -G
address@hidden --posix
@itemx --traditional
Suppress all the extensions made in this implementation, compared to the
System V version. @xref{Compatibility}, for a list of these. This
@@ -4646,6 +4648,7 @@ using @code{undivert} (@pxref{Undivert})
@acronym{GNU} @code{m4} allows included files to be found in other directories
than the current working directory.
address@hidden @env{M4PATH}
If the @option{--prepend-include} or @option{-B} command-line option was
provided (@pxref{Preprocessor features, , Invoking m4}), those
directories are searched first, in reverse order that those options were
@@ -4667,6 +4670,7 @@ Diversions are a way of temporarily savi
reinserted into the output stream, @dfn{undiverted}, again at a later
time.
address@hidden @env{TMPDIR}
Numbered diversions are counted from 0 upwards, diversion number 0
being the normal output stream. The number of simultaneous diversions
is limited mainly by the memory used to describe them, because @acronym{GNU}
@@ -5741,29 +5745,32 @@ Integer expressions are evaluated with @
@deffn {Builtin (m4)} eval (@var{expression}, @dvar{radix, 10}, @ovar{width})
Expands to the value of @var{expression}. The expansion is empty
-if an error is encountered while parsing the arguments. If specified,
address@hidden and @var{width} control the format of the output. An error
-is issued if division by zero is attempted.
+if a problem is encountered while parsing the arguments. If specified,
address@hidden and @var{width} control the format of the output.
-The macro @code{eval} is recognized only with parameters.
address@hidden deffn
+Calculations are done with signed numbers, using at least 31-bit
+precision, but as a @acronym{GNU} extension, @code{m4} will use wider
+integers if available. Precision is finite, based on the platform's
+notion of @code{intmax_t}, and overflow silently results in wraparound.
+A warning is issued if division by zero is attempted, or if
address@hidden could not be parsed.
Expressions can contain the following operators, listed in order of
decreasing precedence.
address@hidden @code
address@hidden @samp
@item ()
-Parenthesis
+Parentheses
@item + - ~ !
Unary plus and minus, and bitwise and logical negation
@item **
Exponentiation
address@hidden * / %
-Multiplication, division, and modulo
address@hidden * / % \
+Multiplication, division, modulo, and ratio
@item + -
Addition and subtraction
address@hidden << >>
-Shift left or right
address@hidden << >> >>>
+Shift left, shift right, unsigned shift right
@item > >= < <=
Relational operators
@item == !=
@@ -5778,31 +5785,42 @@ Bitwise or
Logical and
@item ||
Logical or
address@hidden ?:
+Conditional ternary
address@hidden ,
+Sequential evaluation
@end table
-All operators, except exponentiation, are left associative. C
+The macro @code{eval} is recognized only with parameters.
address@hidden deffn
+
+All binary operators, except exponentiation, are left associative. C
operators that perform variable assignment, such as @samp{=} or
address@hidden, are forbidden, since @code{eval} only operates on constants,
-not variables.
address@hidden, are forbidden by @acronym{POSIX}, since @code{eval} only
+operates on constants, not variables. Attempting to use them results
+in an error.
address@hidden fixme If XCU ERN 137 is approved, then we could provide an
address@hidden extension that supported assignment operators.
Note that some older @code{m4} implementations use @samp{^} as an
alternate operator for the exponentiation, although @acronym{POSIX}
requires the C behavior of bitwise exclusive-or. The precedence of the
negation operators, @samp{~} and @samp{!}, was traditionally lower than
-equality. The unary operators @samp{-} and @samp{+} could not be used
-more than once on the same term. The traditional precedence of the
-equality operators @samp{==} and @samp{!=} was identical instead of
-lower than the relational operators such as @samp{<}, even in
address@hidden M4 1.4.x. Starting with version 2.0, @acronym{GNU} M4
-correctly follows @acronym{POSIX} precedence rules. M4 scripts designed
-to be portable between releases must be aware that parentheses may be
-required to enforce C precedence rules. Likewise, division by zero,
-even in the unused branch of a short-circuiting operator, is not always
-well-defined in other implementations.
+equality. The unary operators could not be used reliably more than once
+on the same term without intervening parentheses. The traditional
+precedence of the equality operators @samp{==} and @samp{!=} was
+identical instead of lower than the relational operators such as
address@hidden<}, even through @acronym{GNU} M4 1.4.8. Starting with version
+1.4.9, @acronym{GNU} M4 correctly follows @acronym{POSIX} precedence
+rules. M4 scripts designed to be portable between releases must be
+aware that parentheses may be required to enforce C precedence rules.
+Likewise, division by zero, even in the unused branch of a
+short-circuiting operator, is not always well-defined in other
+implementations.
Following are some examples where the current version of M4 follows C
precedence rules, but where older versions and some other
-implementations of @code{m4} require explicit parenthesis to get the
+implementations of @code{m4} require explicit parentheses to get the
correct result:
@comment status: 1
@@ -5833,42 +5851,110 @@ eval(`0 |= 1')
eval(`2 || 1 / 0')
@result{}1
eval(`0 || 1 / 0')
address@hidden:stdin:12: eval: divide by zero: 0 || 1 / 0
address@hidden:stdin:12: Warning: eval: divide by zero: 0 || 1 / 0
@result{}
eval(`0 && 1 % 0')
@result{}0
eval(`2 && 1 % 0')
address@hidden:stdin:14: eval: modulo by zero: 2 && 1 % 0
address@hidden:stdin:14: Warning: eval: modulo by zero: 2 && 1 % 0
address@hidden
address@hidden example
+
address@hidden @acronym{GNU} extensions
+As a @acronym{GNU} extension, @code{eval} supports several operators
+that do not appear in C. A right-associative exponentiation operator
address@hidden computes the value of the left argument raised to the right,
+modulo the numeric precision width. If evaluated, the exponent must be
+non-negative, and at least one of the arguments must be non-zero, or a
+warning is issued. An unsigned shift operator @samp{>>>} allows
+shifting a negative number as though it were an unsigned bit pattern,
+which shifts in 0 bits rather than twos-complement sign-extension. A
+ratio operator @samp{\} behaves like normal division @samp{/} on
+integers, but is provided for symmetry with @code{mpeval}.
+
address@hidden
+eval(`2 ** 3 ** 2')
address@hidden
+eval(`(2 ** 3) ** 2')
address@hidden
+eval(`0 ** 1')
address@hidden
+eval(`2 ** 0')
address@hidden
+eval(`0 ** 0')
address@hidden
address@hidden:stdin:5: Warning: eval: divide by zero: 0 ** 0
+eval(`4 ** -2')
address@hidden:stdin:6: Warning: eval: negative exponent: 4 ** -2
@result{}
+eval(`2 || 4 ** -2')
address@hidden
+eval(`(-1 >> 1) == -1')
address@hidden
+eval(`(-1 >>> 1) > (1 << 30)')
address@hidden
+eval(`6 \ 3')
address@hidden
address@hidden example
+
+Furthermore, when you do not use the @option{--traditional} command line
+option (or @option{-G}, @pxref{Limits control, , Invoking m4}), the C
+operators @samp{,} and @samp{?:} are supported. But in traditional
+mode, @acronym{POSIX} requires that the use of these two operators cause
+an error.
+
address@hidden
+$ @kbd{m4}
+eval(`1?2:3')
address@hidden
+eval(`0?2:3')
address@hidden
+eval(`1?2:1/0')
address@hidden
+eval(`0?1/0:3')
address@hidden
+eval(`4,5')
address@hidden
@end example
-Numbers without special prefix are given decimal. A simple @samp{0}
-prefix introduces an octal number. @samp{0x} introduces a hexadecimal
-number. @samp{0b} introduces a binary number. @samp{0r} introduces a
-number expressed in any radix between 1 and 36: the prefix should be
-immediately followed by the decimal expression of the radix, a colon,
-then the digits making the number. For any radix, the digits are
address@hidden, @samp{1}, @samp{2}, @dots{}. Beyond @samp{9}, the digits are
address@hidden, @samp{b} @dots{} up to @samp{z}. Lower and upper case letters
-can be used interchangeably in numbers prefixes and as number digits.
address@hidden options: -G
address@hidden status: 1
address@hidden
+$ @kbd{m4 --posix}
+eval(`1?2:3')
address@hidden:stdin:1: eval: invalid operator: 1?2:3
address@hidden
+eval(`4,5')
address@hidden:stdin:2: eval: invalid operator: 4,5
address@hidden
address@hidden example
-Calculations are done in at least 32 bit, but @code{m4} will use wider
-integers if available.
+Within @var{expression}, (but not @var{radix} or @var{width}), numbers
+without a special prefix are decimal. A simple @samp{0} prefix
+introduces an octal number. @samp{0x} introduces a hexadecimal number.
+As @acronym{GNU} extensions, @samp{0b} introduces a binary number.
address@hidden introduces a number expressed in any radix between 1 and 36:
+the prefix should be immediately followed by the decimal expression of
+the radix, a colon, then the digits making the number. For radix 1,
+leading zeros are ignored, and all remaining digits must be @samp{1};
+for all other radices, the digits are @samp{0}, @samp{1}, @samp{2},
address@hidden Beyond @samp{9}, the digits are @samp{a}, @samp{b} @dots{} up
+to @samp{z}. Lower and upper case letters can be used interchangeably
+in numbers prefixes and as number digits.
Parentheses may be used to group subexpressions whenever needed. For the
relational operators, a true relation returns @code{1}, and a false
relation return @code{0}.
-The builtin macro @code{eval} is recognized only when given arguments.
-
Here are a few examples of use of @code{eval}.
address@hidden status: 1
@example
eval(`-3 * 5')
@result{}-15
eval(index(`Hello world', `llo') >= 0)
@result{}1
+eval(`0r1:0111 + 0b100 + 0r3:12')
address@hidden
define(`square', `eval(`($1) ** 2')')
@result{}
square(`9')
@@ -5878,60 +5964,121 @@ square(square(`5')` + 1')
define(`foo', `666')
@result{}
eval(`foo / 6')
address@hidden:stdin:7: eval: bad expression: foo / 6
address@hidden:stdin:8: Warning: eval: bad expression: foo / 6
@result{}
eval(foo / 6)
@result{}111
@end example
-As the second to last example shows, @code{eval} does not handle macro
+As the last two lines show, @code{eval} does not handle macro
names, even if they expand to a valid expression (or part of a valid
expression). Therefore all macros must be expanded before they are
passed to @code{eval}.
address@hidden update this if we add support for variables.
+
+Some calculations are not portable to other implementations, since they
+have undefined semantics in C, but @acronym{GNU} @code{m4} has
+well-defined behavior on overflow. When shifting, an out-of-range shift
+amount is implicitly brought into the range of the precision using
+modulo arithmetic (for example, on 32-bit integers, this would be an
+implicit bit-wise and with 0x1f). This example should work whether your
+platform uses 32-bit integers, 64-bit integers, or even some other
+atypical size.
+
address@hidden
+define(`max_int', eval(`-1 >>> 1'))
address@hidden
+define(`min_int', eval(max_int` + 1'))
address@hidden
+eval(min_int` < 0')
address@hidden
+eval(max_int` > 0')
address@hidden
+ifelse(eval(min_int` / -1'), min_int, `overflow occurred')
address@hidden occurred
+eval(`0x80000000 % -1')
address@hidden
+eval(`-4 >> 1')
address@hidden
+eval(`-4 >> 'eval(len(eval(max_int, `2'))` + 2'))
address@hidden
address@hidden example
If @var{radix} is specified, it specifies the radix to be used in the
-expansion. The default radix is 10. The result of @code{eval} is
-always taken to be signed. The @var{width} argument specifies a minimum
-output width. The result is zero-padded to extend the expansion to the
-requested width.
+expansion. The default radix is 10; this is also the case if
address@hidden is the empty string. A warning results if the radix is
+outside the range of 1 through 36, inclusive. The result of @code{eval}
+is always taken to be signed. No radix prefix is output, and for
+radices greater than 10, the digits are lower case. The @var{width}
+argument specifies the minimum output width, excluding any negative
+sign. The result is zero-padded to extend the expansion to the
+requested width. A warning results if the width is negative. If
address@hidden or @var{width} is out of bounds, the expansion of
address@hidden is empty.
@example
-eval(666, 10)
+eval(`666', `10')
@result{}666
-eval(666, 11)
+eval(`666', `11')
@result{}556
-eval(666, 6)
+eval(`666', `6')
@result{}3030
-eval(666, 6, 10)
+eval(`666', `6', `10')
@result{}0000003030
-eval(-666, 6, 10)
address@hidden
+eval(`-666', `6', `10')
address@hidden
+eval(`10', `', `0')
address@hidden
+`0r1:'eval(`10', `1', `11')
address@hidden:01111111111
+eval(`10', `16')
address@hidden
+eval(`1', `37')
address@hidden:stdin:9: Warning: eval: radix out of range: 37
address@hidden
+eval(`1', , `-1')
address@hidden:stdin:10: Warning: eval: negative width: -1
address@hidden
+eval()
address@hidden:stdin:11: Warning: eval: empty string treated as zero
address@hidden
@end example
-Take note that @var{radix} cannot be larger than 36.
-
@node Mpeval
@section Multiple precision arithmetic
When @code{m4} is compiled with a multiple precision arithmetic library
(@pxref{Experiments}), a builtin @code{mpeval} is defined.
address@hidden {Builtin (mpeval)} mpeval (@var{expression}, @ovar{radix},
@ovar{width})
-It is almost identical to @code{eval}, except the calculations are done
-with infinite precision. Numbers may be of any length.
-
address@hidden `:' as ratio conflicts with `?:' - is it worth using `\' instead?
-A new operator, @code{:}, is provided with the same precedence as
-division, and rationally divides two numbers and canonicalizes
-the result. The @code{/} operator always returns the quotient of the
-division. To convert a rational value to integral, divide (@code{/}) by
-1. Some operators such as @code{%}, @code{<<}, @code{>>}, @code{~},
address@hidden&}, @code{|} and @code{^} operate only on integers and will
-truncate any rational remainder. @code{**} assumes that the exponent is
-integral.
address@hidden {Builtin (mpeval)} mpeval (@var{expression}, @dvar{radix, 10}, @
+ @ovar{width})
+Behaves similarly to @code{eval}, except the calculations are done with
+infinite precision, and rational numbers are supported. Numbers may be
+of any length.
+
+The macro @code{mpeval} is recognized only with parameters.
address@hidden deffn
+
+The ratio operator, @samp{\}, is provided with the same precedence as
+division, and rationally divides two numbers and canonicalizes the
+result, whereas the division operator @samp{/} always returns the
+integer quotient of the division. To convert a rational value to
+integral, divide (@samp{/}) by 1. Some operators, such as @samp{%},
address@hidden<<}, @samp{>>}, @samp{~}, @samp{&}, @samp{|} and @samp{^} operate
+only on integers and will truncate any rational remainder. The unsigned
+shift operator, @samp{>>>}, behaves identically with regular right
+shifts, @samp{>>}, since with infinite precision, it is not possible to
+convert a negative number to a positive using shifts. The
+exponentiation operator, @samp{**}, assumes that the exponent is
+integral, but allows negative exponents. With the short-circuit logical
+operators, @samp{||} and @samp{&&}, a non-zero result preserves the
+value of the argument that ended evaluation, rather than collapsing to
address@hidden The operators @samp{?:} and @samp{,} are always available,
+even in @acronym{POSIX} mode, since @code{mpeval} does not have to
+conform to the @acronym{POSIX} rules for @code{eval}.
-The builtin macro @code{mpeval} is recognized only when given arguments.
address@hidden deffn
address@hidden fixme - need some examples, but conditional on whether modules
address@hidden and mpeval are both supported
@node Shell commands
@chapter Macros for running shell commands
@@ -6003,7 +6150,7 @@ syscmd(`echo hi')
@node Esyscmd
@section Reading the output of commands
address@hidden GNU extensions
address@hidden @acronym{GNU} extensions
@deffn {Builtin (gnu)} esyscmd (@var{shell-command})
If you want @code{m4} to read the output of a shell command, use
@code{esyscmd}, which expands to the standard output of the shell
@@ -6495,7 +6642,7 @@ user's input file, or else, @code{includ
@cindex initialization, frozen state
@cindex dumping into frozen file
@cindex reloading a frozen file
address@hidden GNU extensions
address@hidden @acronym{GNU} extensions
Reading the common base of a big application, over and over again, may
be time consuming. GNU @code{m4} offers some machinery to speed up
the start of an application using lengthy common bases. Presume the
@@ -6689,7 +6836,7 @@ is made to summarize these here.
@node Extensions
@section Extensions in @acronym{GNU} M4
address@hidden GNU extensions
address@hidden @acronym{GNU} extensions
@cindex @acronym{POSIX}
@cindex @env{POSIXLY_CORRECT}
This version of @code{m4} contains a few facilities that do not exist
@@ -6954,7 +7101,6 @@ version also optimizes based on the fact
not need to be passed to the helper @address@hidden
@comment examples
address@hidden status: 1
@example
$ @kbd{m4 -I examples}
undivert(`forloop2.m4')dnl
@@ -6979,7 +7125,7 @@ forloop(`', `1', `2', ` odd iterator nam
forloop(`i', `5 + 5', `0xc', ` 0x`'eval(i, `16')')
@result{} 0xa 0xb 0xc
forloop(`i', `a', `b', `non-numeric bounds')
address@hidden:stdin:6: eval: bad input: (b) >= (a)
address@hidden:stdin:6: Warning: eval: bad input: (b) >= (a)
@result{}
@end example
Index: m4/m4module.h
===================================================================
RCS file: /sources/m4/m4/m4/m4module.h,v
retrieving revision 1.102
diff -u -p -r1.102 m4module.h
--- m4/m4module.h 23 Dec 2006 00:02:20 -0000 1.102
+++ m4/m4module.h 6 Jan 2007 19:01:24 -0000
@@ -1,7 +1,7 @@
/* GNU m4 -- A simple macro processor
Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 1999, 2000, 2003,
- 2004, 2005, 2006 Free Software Foundation, Inc.
+ 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -440,6 +440,16 @@ extern FILE * m4_path_search (m4 *,
#define obstack_chunk_alloc xmalloc
#define obstack_chunk_free free
+
+/* Convert a possibly-signed character to an unsigned character. This is
+ a bit safer than casting to unsigned char, since it catches some type
+ errors that the cast doesn't. */
+#if HAVE_INLINE
+static inline unsigned char to_uchar (char ch) { return ch; }
+#else
+# define to_uchar(C) ((unsigned char) (C))
+#endif
+
END_C_DECLS
#endif /* !M4MODULE_H */
Index: m4/m4private.h
===================================================================
RCS file: /sources/m4/m4/m4/m4private.h,v
retrieving revision 1.74
diff -u -p -r1.74 m4private.h
--- m4/m4private.h 23 Dec 2006 00:02:20 -0000 1.74
+++ m4/m4private.h 6 Jan 2007 19:01:25 -0000
@@ -1,7 +1,7 @@
/* GNU m4 -- A simple macro processor
Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 1998, 1999, 2004, 2005,
- 2006 Free Software Foundation, Inc.
+ 2006, 2007 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -358,16 +358,6 @@ struct m4__search_path_info {
extern void m4__include_init (m4 *);
-/* Convert a possibly-signed character to an unsigned character. This is
- a bit safer than casting to unsigned char, since it catches some type
- errors that the cast doesn't. */
-#if HAVE_INLINE
-static inline unsigned char to_uchar (char ch) { return ch; }
-#else
-# define to_uchar(C) ((unsigned char) (C))
-#endif
-
-
/* Debugging the memory allocator. */
#if WITH_DMALLOC
Index: modules/evalparse.c
===================================================================
RCS file: /sources/m4/m4/modules/evalparse.c,v
retrieving revision 1.15
diff -u -p -r1.15 evalparse.c
--- modules/evalparse.c 3 Jan 2007 14:44:11 -0000 1.15
+++ modules/evalparse.c 6 Jan 2007 19:01:25 -0000
@@ -45,10 +45,11 @@ typedef enum eval_token
EXPONENT,
TIMES, DIVIDE, MODULO, RATIO,
EQ, NOTEQ, GT, GTEQ, LS, LSEQ,
- LSHIFT, RSHIFT,
+ LSHIFT, RSHIFT, URSHIFT,
LNOT, LAND, LOR,
NOT, AND, OR, XOR,
LEFTP, RIGHTP,
+ QUESTION, COLON, COMMA,
NUMBER, EOTEXT
}
eval_token;
@@ -58,44 +59,51 @@ eval_token;
typedef enum eval_error
{
NO_ERROR,
- MISSING_RIGHT,
+ DIVIDE_ZERO,
+ MODULO_ZERO,
+ NEGATIVE_EXPONENT,
+ /* All errors prior to SYNTAX_ERROR can be ignored in a dead
+ branch of && and ||. All errors after are just more details
+ about a syntax error. */
SYNTAX_ERROR,
+ MISSING_RIGHT,
+ MISSING_COLON,
UNKNOWN_INPUT,
EXCESS_INPUT,
- INVALID_OPERATOR,
- DIVIDE_ZERO,
- MODULO_ZERO
+ INVALID_OPERATOR
}
eval_error;
-static eval_error logical_or_term (m4 *, eval_token, number *);
-static eval_error logical_and_term (m4 *, eval_token, number *);
-static eval_error or_term (m4 *, eval_token, number *);
-static eval_error xor_term (m4 *, eval_token, number *);
-static eval_error and_term (m4 *, eval_token, number *);
-static eval_error equality_term (m4 *, eval_token, number *);
-static eval_error cmp_term (m4 *, eval_token, number *);
-static eval_error shift_term (m4 *, eval_token, number *);
-static eval_error add_term (m4 *, eval_token, number *);
-static eval_error mult_term (m4 *, eval_token, number *);
-static eval_error exp_term (m4 *, eval_token, number *);
-static eval_error unary_term (m4 *, eval_token, number *);
-static eval_error simple_term (m4 *, eval_token, number *);
-static void numb_pow (number *x, number *y);
+static eval_error comma_term (m4 *, eval_token, number *);
+static eval_error condition_term (m4 *, eval_token, number *);
+static eval_error logical_or_term (m4 *, eval_token, number *);
+static eval_error logical_and_term (m4 *, eval_token, number *);
+static eval_error or_term (m4 *, eval_token, number *);
+static eval_error xor_term (m4 *, eval_token, number *);
+static eval_error and_term (m4 *, eval_token, number *);
+static eval_error equality_term (m4 *, eval_token, number *);
+static eval_error cmp_term (m4 *, eval_token, number *);
+static eval_error shift_term (m4 *, eval_token, number *);
+static eval_error add_term (m4 *, eval_token, number *);
+static eval_error mult_term (m4 *, eval_token, number *);
+static eval_error exp_term (m4 *, eval_token, number *);
+static eval_error unary_term (m4 *, eval_token, number *);
+static eval_error simple_term (m4 *, eval_token, number *);
+static eval_error numb_pow (number *, number *);
/* --- LEXICAL FUNCTIONS --- */
/* Pointer to next character of input text. */
-static const unsigned char *eval_text;
+static const char *eval_text;
/* Value of eval_text, from before last call of eval_lex (). This is so we
can back up, if we have read too much. */
-static const unsigned char *last_text;
+static const char *last_text;
static void
-eval_init_lex (const unsigned char *text)
+eval_init_lex (const char *text)
{
eval_text = text;
last_text = NULL;
@@ -114,7 +122,7 @@ eval_undo (void)
static eval_token
eval_lex (number *val)
{
- while (isspace (*eval_text))
+ while (isspace (to_uchar (*eval_text)))
eval_text++;
last_text = eval_text;
@@ -122,7 +130,7 @@ eval_lex (number *val)
if (*eval_text == '\0')
return EOTEXT;
- if (isdigit (*eval_text))
+ if (isdigit (to_uchar (*eval_text)))
{
int base, digit;
@@ -147,7 +155,7 @@ eval_lex (number *val)
case 'R':
base = 0;
eval_text++;
- while (isdigit (*eval_text) && base <= 36)
+ while (isdigit (to_uchar (*eval_text)) && base <= 36)
base = 10 * base + *eval_text++ - '0';
if (base == 0 || base > 36 || *eval_text != ':')
return ERROR;
@@ -164,32 +172,42 @@ eval_lex (number *val)
numb_set_si (val, 0);
for (; *eval_text; eval_text++)
{
- if (isdigit (*eval_text))
+ if (isdigit (to_uchar (*eval_text)))
digit = *eval_text - '0';
- else if (islower (*eval_text))
+ else if (islower (to_uchar (*eval_text)))
digit = *eval_text - 'a' + 10;
- else if (isupper (*eval_text))
+ else if (isupper (to_uchar (*eval_text)))
digit = *eval_text - 'A' + 10;
else
break;
- if (digit >= base)
+ if (base == 1)
+ {
+ if (digit == 1)
+ numb_incr (*val);
+ else if (digit == 0 && numb_zerop (*val))
+ continue;
+ else
+ break;
+ }
+ else if (digit >= base)
break;
+ else
+ {
+ number xbase;
+ number xdigit;
- { /* (*val) = (*val) * base; */
- number xbase;
- numb_init (xbase);
- numb_set_si (&xbase, base);
- numb_times (*val, xbase);
- numb_fini (xbase);
- }
- { /* (*val) = (*val) + digit; */
- number xdigit;
- numb_init (xdigit);
- numb_set_si (&xdigit, digit);
- numb_plus (*val, xdigit);
- numb_fini (xdigit);
- }
+ /* (*val) = (*val) * base; */
+ numb_init (xbase);
+ numb_set_si (&xbase, base);
+ numb_times (*val, xbase);
+ numb_fini (xbase);
+ /* (*val) = (*val) + digit; */
+ numb_init (xdigit);
+ numb_set_si (&xdigit, digit);
+ numb_plus (*val, xdigit);
+ numb_fini (xdigit);
+ }
}
return NUMBER;
}
@@ -221,8 +239,8 @@ eval_lex (number *val)
if (*eval_text == '=')
return BADOP;
return MODULO;
- case ':':
- return RATIO; /* FIXME - this clashes with supporting ?:. */
+ case '\\':
+ return RATIO;
case '=':
if (*eval_text == '=')
{
@@ -245,8 +263,14 @@ eval_lex (number *val)
}
else if (*eval_text == '>')
{
- if (*eval_text++ == '=')
+ eval_text++;
+ if (*eval_text == '=')
return BADOP;
+ else if (*eval_text == '>')
+ {
+ eval_text++;
+ return URSHIFT;
+ }
return RSHIFT;
}
else
@@ -259,7 +283,7 @@ eval_lex (number *val)
}
else if (*eval_text == '<')
{
- if (*eval_text++ == '=')
+ if (*++eval_text == '=')
return BADOP;
return LSHIFT;
}
@@ -293,6 +317,12 @@ eval_lex (number *val)
return LEFTP;
case ')':
return RIGHTP;
+ case '?':
+ return QUESTION;
+ case ':':
+ return COLON;
+ case ',':
+ return COMMA;
default:
return ERROR;
}
@@ -300,6 +330,90 @@ eval_lex (number *val)
/* Recursive descent parser. */
static eval_error
+comma_term (m4 *context, eval_token et, number *v1)
+{
+ number v2;
+ eval_error er;
+
+ if ((er = condition_term (context, et, v1)) != NO_ERROR)
+ return er;
+
+ numb_init (v2);
+ while ((et = eval_lex (&v2)) == COMMA)
+ {
+ /* Unless XCU ERN 137 is approved, eval must reject this in
+ POSIX mode. */
+ numb_extension (context);
+ et = eval_lex (&v2);
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ if ((er = condition_term (context, et, &v2)) != NO_ERROR)
+ return er;
+ numb_set (*v1, v2);
+ }
+ numb_fini (v2);
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ eval_undo ();
+ return NO_ERROR;
+}
+
+static eval_error
+condition_term (m4 *context, eval_token et, number *v1)
+{
+ number v2;
+ number v3;
+ eval_error er;
+
+ if ((er = logical_or_term (context, et, v1)) != NO_ERROR)
+ return er;
+
+ numb_init (v2);
+ numb_init (v3);
+ if ((et = eval_lex (&v2)) == QUESTION)
+ {
+ /* Unless XCU ERN 137 is approved, eval must reject this in
+ POSIX mode. */
+ numb_extension (context);
+ et = eval_lex (&v2);
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ /* Implement short-circuiting of valid syntax. */
+ er = comma_term (context, et, &v2);
+ if (er != NO_ERROR
+ && !(numb_zerop (*v1) && er < SYNTAX_ERROR))
+ return er;
+
+ et = eval_lex (&v3);
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+ if (et != COLON)
+ return MISSING_COLON;
+
+ et = eval_lex (&v3);
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ er = condition_term (context, et, &v3);
+ if (er != NO_ERROR
+ && !(! numb_zerop (*v1) && er < SYNTAX_ERROR))
+ return er;
+
+ numb_set (*v1, ! numb_zerop (*v1) ? v2 : v3);
+ }
+ numb_fini (v2);
+ numb_fini (v3);
+ if (et == ERROR)
+ return UNKNOWN_INPUT;
+
+ eval_undo ();
+ return NO_ERROR;
+}
+
+static eval_error
logical_or_term (m4 *context, eval_token et, number *v1)
{
number v2;
@@ -319,8 +433,7 @@ logical_or_term (m4 *context, eval_token
er = logical_and_term (context, et, &v2);
if (er == NO_ERROR)
numb_lior (*v1, v2);
- else if (! numb_zerop (*v1)
- && (er == DIVIDE_ZERO || er == MODULO_ZERO))
+ else if (! numb_zerop (*v1) && er < SYNTAX_ERROR)
numb_set (*v1, numb_ONE);
else
return er;
@@ -353,8 +466,7 @@ logical_and_term (m4 *context, eval_toke
er = or_term (context, et, &v2);
if (er == NO_ERROR)
numb_land (*v1, v2);
- else if (numb_zerop (*v1)
- && (er == DIVIDE_ZERO || er == MODULO_ZERO))
+ else if (numb_zerop (*v1) && er < SYNTAX_ERROR)
numb_set (*v1, numb_ZERO);
else
return er;
@@ -551,7 +663,7 @@ shift_term (m4 *context, eval_token et,
return er;
numb_init (v2);
- while ((op = eval_lex (&v2)) == LSHIFT || op == RSHIFT)
+ while ((op = eval_lex (&v2)) == LSHIFT || op == RSHIFT || op == URSHIFT)
{
et = eval_lex (&v2);
@@ -571,6 +683,10 @@ shift_term (m4 *context, eval_token et,
numb_rshift (context, v1, &v2);
break;
+ case URSHIFT:
+ numb_urshift (context, v1, &v2);
+ break;
+
default:
assert (!"INTERNAL ERROR: bad shift operator in shift_term ()");
abort ();
@@ -684,13 +800,11 @@ mult_term (m4 *context, eval_token et, n
static eval_error
exp_term (m4 *context, eval_token et, number *v1)
{
- number result;
number v2;
eval_error er;
if ((er = unary_term (context, et, v1)) != NO_ERROR)
return er;
- memcpy (&result, v1, sizeof(number));
numb_init (v2);
while ((et = eval_lex (&v2)) == EXPONENT)
@@ -702,7 +816,8 @@ exp_term (m4 *context, eval_token et, nu
if ((er = exp_term (context, et, &v2)) != NO_ERROR)
return er;
- numb_pow (v1, &v2);
+ if ((er = numb_pow (v1, &v2)) != NO_ERROR)
+ return er;
}
numb_fini (v2);
if (et == ERROR)
@@ -734,9 +849,8 @@ unary_term (m4 *context, eval_token et,
else if (et == LNOT)
numb_lnot (*v1);
}
- else
- if ((er = simple_term (context, et, v1)) != NO_ERROR)
- return er;
+ else if ((er = simple_term (context, et, v1)) != NO_ERROR)
+ return er;
return NO_ERROR;
}
@@ -754,7 +868,7 @@ simple_term (m4 *context, eval_token et,
if (et == ERROR)
return UNKNOWN_INPUT;
- if ((er = logical_or_term (context, et, v1)) != NO_ERROR)
+ if ((er = comma_term (context, et, v1)) != NO_ERROR)
return er;
et = eval_lex (&v2);
@@ -786,24 +900,24 @@ m4_evaluate (m4 *context, m4_obstack *ob
int min = 1;
number val;
eval_token et;
- eval_error err;
+ eval_error err = NO_ERROR;
- if (argc >= 3 && !m4_numeric_arg (context, argc, argv, 2, &radix))
+ if (*M4ARG (2) && !m4_numeric_arg (context, argc, argv, 2, &radix))
return;
- if (radix <= 1 || radix > 36)
+ if (radix < 1 || radix > 36)
{
- m4_error (context, 0, 0, _("%s: radix out of range: %d"),
- M4ARG (0), radix);
+ m4_warn (context, 0, _("%s: radix out of range: %d"),
+ M4ARG (0), radix);
return;
}
if (argc >= 4 && !m4_numeric_arg (context, argc, argv, 3, &min))
return;
- if (min <= 0)
+ if (min < 0)
{
- m4_error (context, 0, 0, _("%s: negative width: %d"), M4ARG (0), min);
+ m4_warn (context, 0, _("%s: negative width: %d"), M4ARG (0), min);
return;
}
@@ -812,7 +926,13 @@ m4_evaluate (m4 *context, m4_obstack *ob
numb_init (val);
et = eval_lex (&val);
- err = logical_or_term (context, et, &val);
+ if (et == EOTEXT)
+ {
+ m4_warn (context, 0, _("%s: empty string treated as zero"), M4ARG (0));
+ numb_set (val, numb_ZERO);
+ }
+ else
+ err = comma_term (context, et, &val);
if (err == NO_ERROR && *eval_text != '\0')
{
@@ -829,37 +949,48 @@ m4_evaluate (m4 *context, m4_obstack *ob
break;
case MISSING_RIGHT:
- m4_error (context, 0, 0, _("%s: missing right parenthesis: %s"),
- M4ARG (0), M4ARG (1));
+ m4_warn (context, 0, _("%s: missing right parenthesis: %s"),
+ M4ARG (0), M4ARG (1));
+ break;
+
+ case MISSING_COLON:
+ m4_warn (context, 0, _("%s: missing colon: %s"),
+ M4ARG (0), M4ARG (1));
break;
case SYNTAX_ERROR:
- m4_error (context, 0, 0, _("%s: bad expression: %s"),
- M4ARG (0), M4ARG (1));
+ m4_warn (context, 0, _("%s: bad expression: %s"),
+ M4ARG (0), M4ARG (1));
break;
case UNKNOWN_INPUT:
- m4_error (context, 0, 0, _("%s: bad input: %s"), M4ARG (0), M4ARG (1));
+ m4_warn (context, 0, _("%s: bad input: %s"), M4ARG (0), M4ARG (1));
break;
case EXCESS_INPUT:
- m4_error (context, 0, 0, _("%s: excess input: %s"), M4ARG (0),
- M4ARG (1));
+ m4_warn (context, 0, _("%s: excess input: %s"), M4ARG (0),
+ M4ARG (1));
break;
case INVALID_OPERATOR:
+ /* POSIX requires an error here, unless XCU ERN 137 is approved. */
m4_error (context, 0, 0, _("%s: invalid operator: %s"), M4ARG (0),
M4ARG (1));
break;
case DIVIDE_ZERO:
- m4_error (context, 0, 0, _("%s: divide by zero: %s"), M4ARG (0),
- M4ARG (1));
+ m4_warn (context, 0, _("%s: divide by zero: %s"), M4ARG (0),
+ M4ARG (1));
break;
case MODULO_ZERO:
- m4_error (context, 0, 0, _("%s: modulo by zero: %s"), M4ARG (0),
- M4ARG (1));
+ m4_warn (context, 0, _("%s: modulo by zero: %s"), M4ARG (0),
+ M4ARG (1));
+ break;
+
+ case NEGATIVE_EXPONENT:
+ m4_warn (context, 0, _("%s: negative exponent: %s"), M4ARG (0),
+ M4ARG (1));
break;
default:
@@ -870,7 +1001,7 @@ m4_evaluate (m4 *context, m4_obstack *ob
numb_fini (val);
}
-static void
+static eval_error
numb_pow (number *x, number *y)
{
/* y should be integral */
@@ -880,13 +1011,16 @@ numb_pow (number *x, number *y)
numb_init (ans);
numb_set_si (&ans, 1);
+ if (numb_zerop (*x) && numb_zerop (*y))
+ return DIVIDE_ZERO;
+
numb_init (yy);
numb_set (yy, *y);
if (numb_negativep (yy))
{
- numb_invert (*x);
numb_negate (yy);
+ numb_invert (*x);
}
while (numb_positivep (yy))
@@ -898,4 +1032,5 @@ numb_pow (number *x, number *y)
numb_fini (ans);
numb_fini (yy);
+ return NO_ERROR;
}
Index: modules/m4.c
===================================================================
RCS file: /sources/m4/m4/modules/m4.c,v
retrieving revision 1.99
diff -u -p -r1.99 m4.c
--- modules/m4.c 3 Jan 2007 14:44:11 -0000 1.99
+++ modules/m4.c 6 Jan 2007 19:01:25 -0000
@@ -119,8 +119,8 @@ static int dumpdef_cmp_CB (const void *s
static void * dump_symbol_CB (m4_symbol_table *ignored, const char *name,
m4_symbol *symbol, void *userdata);
static const char *ntoa (number value, int radix);
-static void numb_obstack (m4_obstack *obs, const number value,
- const int radix, int min);
+static void numb_obstack (m4_obstack *obs, number value,
+ int radix, int min);
/* Generate prototypes for each builtin handler function. */
@@ -1074,6 +1074,7 @@ M4BUILTIN_HANDLER (translit)
#define numb_init(x) ((x) = numb_ZERO)
#define numb_fini(x)
+#define numb_incr(n) ((n) += numb_ONE)
#define numb_decr(n) ((n) -= numb_ONE)
#define numb_zerop(x) ((x) == numb_ZERO)
@@ -1104,10 +1105,23 @@ M4BUILTIN_HANDLER (translit)
#define numb_ratio(x, y) ((x) = ((x) / ((y))))
#define numb_divide(x, y) (*(x) = (*(x) / (*(y))))
#define numb_modulo(c, x, y) (*(x) = (*(x) % *(y)))
-#define numb_invert(x) ((x) = numb_ONE / (x))
-
-#define numb_lshift(c, x, y) (*(x) = (*(x) << *(y)))
-#define numb_rshift(c, x, y) (*(x) = (*(x) >> *(y)))
+/* numb_invert is only used in the context of x**-y, which integral math
+ does not support. */
+#define numb_invert(x) return NEGATIVE_EXPONENT
+
+#define numb_lshift(c, x, y) (*(x) = (*(x) << *(y)))
+#define numb_rshift(c, x, y) (*(x) = (*(x) >> *(y)))
+#define numb_urshift(c, x, y) \
+ (*(x) = (number) ((unumber) *(x) >> (unumber) *(y)))
+
+#define numb_extension(c) \
+ do \
+ { \
+ /* Revisit this if XCU ERN 137 is approved. */ \
+ if (m4_get_posixly_correct_opt (context)) \
+ return INVALID_OPERATOR; \
+ } \
+ while (0)
/* The function ntoa () converts VALUE to a signed ascii representation in
@@ -1149,15 +1163,30 @@ ntoa (number value, int radix)
}
static void
-numb_obstack(m4_obstack *obs, const number value,
- const int radix, int min)
+numb_obstack(m4_obstack *obs, number value, int radix, int min)
{
- const char *s = ntoa (value, radix);
+ const char *s;
+ if (radix == 1)
+ {
+ /* FIXME - this code currently depends on undefined behavior. */
+ if (value < 0)
+ {
+ obstack_1grow (obs, '-');
+ value = -value;
+ }
+ while (min-- - value > 0)
+ obstack_1grow (obs, '0');
+ while (value-- != 0)
+ obstack_1grow (obs, '1');
+ obstack_1grow (obs, '\0');
+ return;
+ }
+
+ s = ntoa (value, radix);
if (*s == '-')
{
obstack_1grow (obs, '-');
- min--;
s++;
}
for (min -= strlen (s); --min >= 0;)
Index: modules/mpeval.c
===================================================================
RCS file: /sources/m4/m4/modules/mpeval.c,v
retrieving revision 1.21
diff -u -p -r1.21 mpeval.c
--- modules/mpeval.c 3 Jan 2007 14:44:11 -0000 1.21
+++ modules/mpeval.c 6 Jan 2007 19:01:25 -0000
@@ -20,6 +20,7 @@
#include <config.h>
#include <m4module.h>
+#include <m4private.h>
#if HAVE_GMP_H
# include <gmp.h>
@@ -58,7 +59,7 @@
#define numb_ge(x, y) numb_set (x, mpq_cmp (x, y) >= 0 ? numb_ONE : numb_ZERO)
#define numb_lnot(x) numb_set (x, numb_zerop (x) ? numb_ONE : numb_ZERO)
-#define numb_lior(x, y) numb_set (x, numb_zerop (x) ? y : numb_ONE)
+#define numb_lior(x, y) numb_set (x, numb_zerop (x) ? y : x)
#define numb_land(x, y) numb_set (x, numb_zerop (x) ? numb_ZERO : y)
#define reduce1(f1, x) \
@@ -90,6 +91,7 @@
#define numb_ratio(x, y) reduce2 (mpq_div, x, y)
#define numb_invert(x) reduce1 (mpq_inv, x)
+#define numb_incr(n) numb_plus (n, numb_ONE)
#define numb_decr(n) numb_minus (n, numb_ONE)
/* Generate prototypes for each builtin handler function. */
@@ -140,6 +142,8 @@ static void numb_eor (m4 *context, numbe
static void numb_not (m4 *context, number *x);
static void numb_lshift (m4 *context, number *x, number *y);
static void numb_rshift (m4 *context, number *x, number *y);
+#define numb_urshift(c, x, y) numb_rshift (c, x, y)
+#define numb_extension(c) /* no-op */
static number numb_ZERO;
@@ -172,12 +176,11 @@ numb_obstack (m4_obstack *obs, const num
mpz_init (i);
mpq_get_num (i, value);
- s = mpz_get_str ((char *) 0, radix, i);
+ s = mpz_get_str (NULL, radix, i);
if (*s == '-')
{
obstack_1grow (obs, '-');
- min--;
s++;
}
for (min -= strlen (s); --min >= 0;)
@@ -188,7 +191,7 @@ numb_obstack (m4_obstack *obs, const num
mpq_get_den (i, value);
if (mpz_cmp_si (i, (long) 1) != 0)
{
- obstack_1grow (obs, ':');
+ obstack_1grow (obs, '\\');
s = mpz_get_str ((char *) 0, radix, i);
obstack_grow (obs, s, strlen (s));
}
@@ -203,7 +206,7 @@ static void
mpq2mpz (m4 *context, mpz_t z, const number q, const char *noisily)
{
if (noisily && mpz_cmp_si (mpq_denref (q), (long) 1) != 0)
- m4_error (context, 0, 0, _("loss of precision in eval: %s"), noisily);
+ m4_warn (context, 0, _("loss of precision in eval: %s"), noisily);
mpz_div (z, mpq_numref (q), mpq_denref (q));
}
Index: src/main.c
===================================================================
RCS file: /sources/m4/m4/src/main.c,v
retrieving revision 1.105
diff -u -p -r1.105 main.c
--- src/main.c 17 Dec 2006 04:34:32 -0000 1.105
+++ src/main.c 6 Jan 2007 19:01:25 -0000
@@ -1,7 +1,7 @@
/* GNU m4 -- A simple macro processor
Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 1999, 2000, 2003,
- 2004, 2005, 2006 Free Software Foundation, Inc.
+ 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -129,7 +129,7 @@ Preprocessor features:\n\
fputs (_("\
Limits control:\n\
-g, --gnu override -G to re-enable GNU extensions\n\
- -G, --traditional suppress all GNU extensions\n\
+ -G, --traditional, --posix suppress all GNU extensions\n\
-L, --nesting-limit=NUMBER change artificial nesting limit [1024]\n\
"), stdout);
puts ("");
@@ -229,6 +229,7 @@ static const struct option long_options[
{"load-module", required_argument, NULL, 'm'},
{"module-directory", required_argument, NULL, 'M'},
{"nesting-limit", required_argument, NULL, 'L'},
+ {"posix", no_argument, NULL, 'G'},
{"prefix-builtins", no_argument, NULL, 'P'},
{"pushdef", required_argument, NULL, 'p'},
{"quiet", no_argument, NULL, 'Q'},
Index: NEWS
===================================================================
RCS file: /sources/m4/m4/NEWS,v
retrieving revision 1.1.1.1.2.86
diff -u -p -r1.1.1.1.2.86 NEWS
--- NEWS 5 Jan 2007 03:03:36 -0000 1.1.1.1.2.86
+++ NEWS 6 Jan 2007 19:00:54 -0000
@@ -8,6 +8,11 @@ Version 1.4.9 - ?? ??? 2007, by ???? (C
files larger than 2GiB on some platforms.
* Fix a regression introduced in 1.4.8 that made m4 dump core when
invoked as 'm4 -- file'.
+* The `eval' builtin now follows C precedence rules. Additionally,
+ short-circuit operators correctly short-circuit division by zero.
+ The undocumented alias of '=' meaning '==' in eval now triggers
+ an error, so that a future version of M4 can implement a form of
+ variable assignment as an extension.
Version 1.4.8 - 20 November 2006, by Eric Blake (CVS version 1.4.7a)
Index: checks/check-them
===================================================================
RCS file: /sources/m4/m4/checks/Attic/check-them,v
retrieving revision 1.1.1.1.2.13
diff -u -p -r1.1.1.1.2.13 check-them
--- checks/check-them 7 Sep 2006 13:08:54 -0000 1.1.1.1.2.13
+++ checks/check-them 6 Jan 2007 19:00:54 -0000
@@ -1,6 +1,6 @@
#!/bin/sh
# Check GNU m4 against examples from the manual source.
-# Copyright (C) 1992, 2006 Free Software Foundation, Inc.
+# Copyright (C) 1992, 2006, 2007 Free Software Foundation, Inc.
# Sanity check what we are testing
m4 --version
@@ -59,12 +59,22 @@ do
echo "Checking $file"
sed -e '/^dnl @/d' -e '/^\^D$/q' "$file" \
| LC_MESSAGES=C m4 -d -I "$examples" - >$out 2>$err
+ stat=$?
- if test $? = 77 ; then
- skipped="$skipped $file"
- cat $err
- continue
- fi
+ xstat=`sed -ne '2s/^dnl @ expected status: //p;2q' "$file"`
+ case $stat in
+ 77)
+ skipped="$skipped $file"
+ cat $err
+ continue
+ ;;
+ $xstat) ;;
+ *)
+ failed="$failed $file:status"
+ echo `sed -e 's/^dnl //' -e 1q $file`
+ echo "$file: status was $stat, expected $xstat"
+ ;;
+ esac
sed -e '/^dnl @result{}/!d' -e 's///' -e "s|\.\./examples|$examples|" \
"$file" > $xout
Index: checks/get-them
===================================================================
RCS file: /sources/m4/m4/checks/Attic/get-them,v
retrieving revision 1.1.1.1.2.7
diff -u -p -r1.1.1.1.2.7 get-them
--- checks/get-them 21 Oct 2006 02:55:56 -0000 1.1.1.1.2.7
+++ checks/get-them 6 Jan 2007 19:00:54 -0000
@@ -1,6 +1,7 @@
#!/bin/sh
+# -*- AWK -*-
# Extract all examples from the manual source.
-# Copyright (C) 1992, 2005, 2006 Free Software Foundation, Inc.
+# Copyright (C) 1992, 2005, 2006, 2007 Free Software Foundation, Inc.
# This script is for use with GNU awk.
@@ -13,6 +14,7 @@ BEGIN {
seq = -1;
count = 0;
file = "NONE";
+ status = 0;
}
/address@hidden / {
@@ -34,9 +36,14 @@ BEGIN {
/address@hidden ignore$/ {
getline;
+ status = 0;
next;
}
+/address@hidden status: / {
+ status = $3;
+}
+
/address@hidden/, /address@hidden example$/ {
if (seq < 0)
next;
@@ -47,11 +54,13 @@ BEGIN {
count++;
file = sprintf("%03d.%s", count, node);
printf("dnl @ %s:%d: Origin of test\n"\
- "dnl @ Copyright (C) 2006 Free Software Foundation\n"\
+ "dnl @ expected status: %d\n"\
+ "dnl @ Copyright (C) 2006, 2007 Free Software Foundation\n"\
"dnl @ This file is free software; the Free Software Foundation\n"\
"dnl @ gives unlimited permission to copy and/or distribute it\n"\
"dnl @ with or without modifications, as long as this notice\n"\
- "dnl @ is preserved.\n", FILENAME, NR) > file;
+ "dnl @ is preserved.\n", FILENAME, NR, status) > file;
+ status = 0;
next;
}
if ($0 ~ /address@hidden example$/) {
Index: doc/m4.texinfo
===================================================================
RCS file: /sources/m4/m4/doc/m4.texinfo,v
retrieving revision 1.1.1.1.2.105
diff -u -p -r1.1.1.1.2.105 m4.texinfo
--- doc/m4.texinfo 27 Dec 2006 14:15:12 -0000 1.1.1.1.2.105
+++ doc/m4.texinfo 6 Jan 2007 19:00:56 -0000
@@ -44,7 +44,7 @@ This manual is for @acronym{GNU} M4 (ver
a package containing an implementation of the m4 macro language.
Copyright @copyright{} 1989, 1990, 1991, 1992, 1993, 1994, 2004, 2005,
-2006 Free Software Foundation, Inc.
+2006, 2007 Free Software Foundation, Inc.
@quotation
Permission is granted to copy, distribute and/or modify this document
@@ -376,11 +376,13 @@ engine, since all other implementations
limitations.
More recently, in 2004, Paul Eggert released 1.4.1 and 1.4.2 which
-addressed some long standing bugs in the venerable 1.4 release.
-Then in 2005 Gary V. Vaughan collected together the many
-patches to @acronym{GNU} @code{m4} 1.4 that were floating around the net and
+addressed some long standing bugs in the venerable 1.4 release. Then in
+2005 Gary V. Vaughan collected together the many patches to
address@hidden @code{m4} 1.4 that were floating around the net and
released 1.4.3 and 1.4.4. And in 2006, Eric Blake joined the team and
-prepared patches for the release of 1.4.5, 1.4.6, 1.4.7, and 1.4.8.
+prepared patches for the release of 1.4.5, 1.4.6, 1.4.7, and 1.4.8. The
+1.4.x series remains open for bug fixes, including release 1.4.9 in
+2007.
Meanwhile, development has continued on new features for @code{m4}, such
as dynamic module loading and additional builtins. When complete,
@@ -1189,6 +1191,7 @@ f
It is an error if the end of file occurs while collecting arguments.
address@hidden status: 1
@example
hello world
@result{}hello world
@@ -2990,6 +2993,7 @@ changequote(`"', `"')
It is an error if the end of file occurs within a quoted string.
address@hidden status: 1
@example
`hello world'
@result{}hello world
@@ -3133,6 +3137,7 @@ echo(hi,hi)bye)
It is an error if the end of file occurs within a comment.
address@hidden status: 1
@example
changecom(`/*', `*/')
@result{}
@@ -3440,6 +3445,7 @@ m4wrap(`a')m4wrap(`a')
however, the transition between recursion levels behaves like an end of
file condition between two input files.
address@hidden status: 1
@example
m4wrap(`m4wrap(`)')len(abc')
@result{}
@@ -4298,7 +4304,7 @@ patsubst(`abc')
@end example
@node Format
address@hidden Formatted output
address@hidden Formatting strings (printf-like)
@cindex formatted output
@cindex output, formatted
@@ -4417,32 +4423,33 @@ Integer expressions are evaluated with @
@deffn Builtin eval (@var{expression}, @dvar{radix, 10}, @ovar{width})
Expands to the value of @var{expression}. The expansion is empty
-if an error is encountered while parsing the arguments. If specified,
+if a problem is encountered while parsing the arguments. If specified,
@var{radix} and @var{width} control the format of the output.
-The macro @code{eval} is recognized only with parameters.
address@hidden deffn
+Calculations are done with 32-bit signed numbers. Overflow silently
+results in wraparound. A warning is issued if division by zero is
+attempted, or if @var{expression} could not be parsed.
Expressions can contain the following operators, listed in order of
decreasing precedence.
address@hidden @code
address@hidden + -
-Unary plus and minus
address@hidden @samp
address@hidden ()
+Parentheses
address@hidden + - ~ !
+Unary plus and minus, and bitwise and logical negation
@item **
Exponentiation
@item * / %
-Multiplication, division and modulo
+Multiplication, division, and modulo
@item + -
Addition and subtraction
@item << >>
Shift left or right
address@hidden == != > >= < <=
address@hidden > >= < <=
Relational operators
address@hidden !
-Logical negation
address@hidden ~
-Bitwise negation
address@hidden == !=
+Equality operators
@item &
Bitwise and
@item ^
@@ -4455,30 +4462,108 @@ Logical and
Logical or
@end table
-All operators, except exponentiation, are left associative.
+The macro @code{eval} is recognized only with parameters.
address@hidden deffn
+
+All binary operators, except exponentiation, are left associative. C
+operators that perform variable assignment, such as @samp{=} or
address@hidden, are forbidden by @acronym{POSIX}, since @code{eval} only
+operates on constants, not variables. Attempting to use them results
+in an error.
Note that some older @code{m4} implementations use @samp{^} as an
-alternate operator for exponentiation, although @acronym{POSIX} requires
-the C behavior of bitwise exclusive-or. On the other hand, the
-precedence of @samp{~} and @samp{!} are different in @acronym{GNU}
address@hidden than
-they are in C, matching the precedence in traditional @code{m4}
-implementations. This behavior is likely to change in a future
-version to match @acronym{POSIX}, so use parentheses to force the
-desired precedence.
-
-Within @var{expression}, (but not @var{radix} or @var{width}),
-numbers without a special prefix are decimal. A simple @samp{0}
-prefix introduces an octal number. @samp{0x} introduces a hexadecimal
-number. @samp{0b} introduces a binary number. @samp{0r} introduces a
-number expressed in any radix between 1 and 36: the prefix should be
-immediately followed by the decimal expression of the radix, a colon,
-then the digits making the number. For radix 1, leading zeros are
-ignored and all remaining digits must be @samp{1}; for all other
-radices, the digits are
address@hidden, @samp{1}, @samp{2}, @dots{}. Beyond @samp{9}, the digits are
address@hidden, @samp{b} @dots{} up to @samp{z}. Lower and upper case letters
-can be used interchangeably in numbers prefixes and as number digits.
+alternate operator for the exponentiation, although @acronym{POSIX}
+requires the C behavior of bitwise exclusive-or. The precedence of the
+negation operators, @samp{~} and @samp{!}, was traditionally lower than
+equality. The unary operators could not be used reliably more than once
+on the same term without intervening parentheses. The traditional
+precedence of the equality operators @samp{==} and @samp{!=} was
+identical instead of lower than the relational operators such as
address@hidden<}, even through @acronym{GNU} M4 1.4.8. Starting with version
+1.4.9, @acronym{GNU} M4 correctly follows @acronym{POSIX} precedence
+rules. M4 scripts designed to be portable between releases must be
+aware that parentheses may be required to enforce C precedence rules.
+Likewise, division by zero, even in the unused branch of a
+short-circuiting operator, is not always well-defined in other
+implementations.
+
+Following are some examples where the current version of M4 follows C
+precedence rules, but where older versions and some other
+implementations of @code{m4} require explicit parentheses to get the
+correct result:
+
address@hidden status: 1
address@hidden
+eval(`1 == 2 > 0')
address@hidden
+eval(`(1 == 2) > 0')
address@hidden
+eval(`! 0 * 2')
address@hidden
+eval(`! (0 * 2)')
address@hidden
+eval(`1 | 1 ^ 1')
address@hidden
+eval(`(1 | 1) ^ 1')
address@hidden
+eval(`+ + - ~ ! ~ 0')
address@hidden
+eval(`++0')
address@hidden:stdin:8: invalid operator in eval: ++0
address@hidden
+eval(`1 = 1')
address@hidden:stdin:9: invalid operator in eval: 1 = 1
address@hidden
+eval(`0 |= 1')
address@hidden:stdin:10: invalid operator in eval: 0 |= 1
address@hidden
+eval(`2 || 1 / 0')
address@hidden
+eval(`0 || 1 / 0')
address@hidden:stdin:12: divide by zero in eval: 0 || 1 / 0
address@hidden
+eval(`0 && 1 % 0')
address@hidden
+eval(`2 && 1 % 0')
address@hidden:stdin:14: modulo by zero in eval: 2 && 1 % 0
address@hidden
address@hidden example
+
address@hidden @acronym{GNU} extensions
+As a @acronym{GNU} extension, the operator @samp{**} performs integral
+exponentiation. The operator is right-associative, and if evaluated,
+the exponent must be non-negative, and at least one of the arguments
+must be non-zero, or a warning is issued.
+
address@hidden
+eval(`2 ** 3 ** 2')
address@hidden
+eval(`(2 ** 3) ** 2')
address@hidden
+eval(`0 ** 1')
address@hidden
+eval(`2 ** 0')
address@hidden
+eval(`0 ** 0')
address@hidden
address@hidden:stdin:5: divide by zero in eval: 0 ** 0
+eval(`4 ** -2')
address@hidden:stdin:6: negative exponent in eval: 4 ** -2
address@hidden
address@hidden example
+
+Within @var{expression}, (but not @var{radix} or @var{width}), numbers
+without a special prefix are decimal. A simple @samp{0} prefix
+introduces an octal number. @samp{0x} introduces a hexadecimal number.
+As @acronym{GNU} extensions, @samp{0b} introduces a binary number.
address@hidden introduces a number expressed in any radix between 1 and 36:
+the prefix should be immediately followed by the decimal expression of
+the radix, a colon, then the digits making the number. For radix 1,
+leading zeros are ignored, and all remaining digits must be @samp{1};
+for all other radices, the digits are @samp{0}, @samp{1}, @samp{2},
address@hidden Beyond @samp{9}, the digits are @samp{a}, @samp{b} @dots{} up
+to @samp{z}. Lower and upper case letters can be used interchangeably
+in numbers prefixes and as number digits.
Parentheses may be used to group subexpressions whenever needed. For the
relational operators, a true relation returns @code{1}, and a false
@@ -4493,18 +4578,18 @@ eval(index(`Hello world', `llo') >= 0)
@result{}1
eval(`0r1:0111 + 0b100 + 0r3:12')
@result{}12
-define(`square', `eval(`('$1`)**2')')
+define(`square', `eval(`($1) ** 2')')
@result{}
square(`9')
@result{}81
-square(square(`5')`+1')
+square(square(`5')` + 1')
@result{}676
define(`foo', `666')
@result{}
-eval(`foo/6')
address@hidden:stdin:8: bad expression in eval: foo/6
+eval(`foo / 6')
address@hidden:stdin:8: bad expression in eval: foo / 6
@result{}
-eval(foo/6)
+eval(foo / 6)
@result{}111
@end example
@@ -4513,34 +4598,43 @@ names, even if they expand to a valid ex
expression). Therefore all macros must be expanded before they are
passed to @code{eval}.
-All evaluation is done with 32-bit signed integers, assuming
-2's-complement with wrap-around. The shift operators are defined in
address@hidden
address@hidden by doing an implicit bit-wise and of the right-hand operand
-with 0x1f, and sign-extension with right shift.
+Some calculations are not portable to other implementations, since they
+have undefined semantics in C, but @acronym{GNU} @code{m4} has
+well-defined behavior on overflow. When shifting, an out-of-range shift
+amount is implicitly brought into the range of 32-bit signed integers
+using an implicit bit-wise and with 0x1f).
@example
-eval(0x80000000 / -1)
+define(`max_int', eval(`0x7fffffff'))
address@hidden
+define(`min_int', incr(max_int))
address@hidden
+eval(min_int` < 0')
address@hidden
+eval(max_int` > 0')
address@hidden
+ifelse(eval(min_int` / -1'), min_int, `overflow occurred')
address@hidden occurred
+min_int
@result{}-2147483648
-eval(0x80000000 % -1)
+eval(`0x80000000 % -1')
@result{}0
-eval(0x7fffffff)
address@hidden
-incr(eval(0x7fffffff))
address@hidden
-eval(-4 >> 33)
+eval(`-4 >> 1')
address@hidden
+eval(`-4 >> 33')
@result{}-2
@end example
If @var{radix} is specified, it specifies the radix to be used in the
expansion. The default radix is 10; this is also the case if
address@hidden is the empty string. It is an error if the radix is outside
-the range of 1 through 36, inclusive. The result of @code{eval} is
-always taken to be signed. No radix prefix is output, and for radices
-greater than 10, the digits are lower case. The @var{width} argument
-specifies the minimum output width, excluding any negative sign. The
-result is zero-padded to extend the expansion to the requested width.
-It is an error if the width is negative. On error, the expansion of
address@hidden is the empty string. A warning results if the radix is
+outside the range of 1 through 36, inclusive. The result of @code{eval}
+is always taken to be signed. No radix prefix is output, and for
+radices greater than 10, the digits are lower case. The @var{width}
+argument specifies the minimum output width, excluding any negative
+sign. The result is zero-padded to extend the expansion to the
+requested width. A warning results if the width is negative. If
address@hidden or @var{width} is out of bounds, the expansion of
@code{eval} is empty.
@example
@@ -4560,6 +4654,15 @@ eval(`10', `', `0')
@result{}0r1:01111111111
eval(`10', `16')
@result{}a
+eval(`1', `37')
address@hidden:stdin:9: radix 37 in builtin `eval' out of range
address@hidden
+eval(`1', , `-1')
address@hidden:stdin:10: negative width to builtin `eval'
address@hidden
+eval()
address@hidden:stdin:11: empty string treated as 0 in builtin `eval'
address@hidden
@end example
@node Shell commands
@@ -5081,6 +5184,7 @@ Abort processing with an error message a
resulting string to standard error.
@end deffn
address@hidden status: 1
@example
define(`fatal_error',
`errprint(__program__:__file__:__line__`: fatal error: $*
@@ -5559,15 +5663,16 @@ that is preserved even if the macro is c
@item
@acronym{POSIX} requires @code{eval} (@pxref{Eval}) to treat all
-operators with the same precedence as C. However, @acronym{GNU} @code{m4}
-currently follows the traditional precedence of other @code{m4}
-implementations, where bitwise and logical negation (@samp{~} and
address@hidden) have lower precedence than equality operators, rather than
-equal precedence with other unary operators. Use explicit parentheses
-to ensure proper precedence. As extensions to @acronym{POSIX}, @acronym{GNU}
address@hidden treats the shift operators @samp{<<} and @samp{>>} as
-well-defined on signed integers (even though they are not in C), and
-adds the exponentiation operator @samp{**}.
+operators with the same precedence as C. However, earlier versions of
address@hidden @code{m4} followed the traditional behavior of other
address@hidden implementations, where bitwise and logical negation (@samp{~}
+and @samp{!}) have lower precedence than equality operators; and where
+equality operators (@samp{==} and @samp{!=}) had the same precedence as
+relational operators (such as @samp{<}). Use explicit parentheses to
+ensure proper precedence. As extensions to @acronym{POSIX},
address@hidden @code{m4} gives well-defined semantics to operations that
+C leaves undefined, such as when overflow occurs, when shifting negative
+numbers, or when performing division by zero.
@item
@acronym{POSIX} requires @code{translit} (@pxref{Translit}) to treat
@@ -5962,6 +6067,7 @@ though all files start at line 1. Furth
@code{fatal_error} to work across the entire 1.4.x release series, a
better implementation would be:
address@hidden status: 1
@example
define(`fatal_error',
`errprint(ifdef(`__program__', `__program__', ``m4'')'dnl
Index: m4/gnulib-cache.m4
===================================================================
RCS file: /sources/m4/m4/m4/Attic/gnulib-cache.m4,v
retrieving revision 1.1.2.21
diff -u -p -r1.1.2.21 gnulib-cache.m4
--- m4/gnulib-cache.m4 3 Jan 2007 21:09:23 -0000 1.1.2.21
+++ m4/gnulib-cache.m4 6 Jan 2007 19:00:56 -0000
@@ -15,11 +15,11 @@
# Specification in the form of a command-line invocation:
-# gnulib-tool --import --dir=. --lib=libm4 --source-base=lib --m4-base=m4
--doc-base=doc --aux-dir=. --no-libtool --macro-prefix=M4 avltree-oset
binary-io clean-temp cloexec close-stream closeout config-h error fdl
fopen-safer free gendocs getopt gnupload mkstemp obstack regex stdbool
stdlib-safer strstr strtol unlocked-io verror version-etc-fsf xalloc xvasprintf
+# gnulib-tool --import --dir=. --lib=libm4 --source-base=lib --m4-base=m4
--doc-base=doc --aux-dir=. --no-libtool --macro-prefix=M4 avltree-oset
binary-io clean-temp cloexec close-stream closeout config-h error fdl
fopen-safer free gendocs getopt gnupload mkstemp obstack regex stdbool stdint
stdlib-safer strstr strtol unlocked-io verror version-etc-fsf xalloc xvasprintf
# Specification in the form of a few gnulib-tool.m4 macro invocations:
gl_LOCAL_DIR([])
-gl_MODULES([avltree-oset binary-io clean-temp cloexec close-stream closeout
config-h error fdl fopen-safer free gendocs getopt gnupload mkstemp obstack
regex stdbool stdlib-safer strstr strtol unlocked-io verror version-etc-fsf
xalloc xvasprintf])
+gl_MODULES([avltree-oset binary-io clean-temp cloexec close-stream closeout
config-h error fdl fopen-safer free gendocs getopt gnupload mkstemp obstack
regex stdbool stdint stdlib-safer strstr strtol unlocked-io verror
version-etc-fsf xalloc xvasprintf])
gl_AVOID([])
gl_SOURCE_BASE([lib])
gl_M4_BASE([m4])
Index: src/builtin.c
===================================================================
RCS file: /sources/m4/m4/src/Attic/builtin.c,v
retrieving revision 1.1.1.1.2.52
diff -u -p -r1.1.1.1.2.52 builtin.c
--- src/builtin.c 11 Nov 2006 16:26:58 -0000 1.1.1.1.2.52
+++ src/builtin.c 6 Jan 2007 19:00:56 -0000
@@ -1,6 +1,6 @@
/* GNU m4 -- A simple macro processor
- Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2000, 2004, 2006
+ Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2000, 2004, 2006, 2007
Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
@@ -359,10 +359,10 @@ numeric_arg (token_data *macro, const ch
static char const digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
static const char *
-ntoa (eval_t value, int radix)
+ntoa (int32_t value, int radix)
{
bool negative;
- unsigned_eval_t uvalue;
+ uint32_t uvalue;
static char str[256];
char *s = &str[sizeof str];
@@ -371,12 +371,12 @@ ntoa (eval_t value, int radix)
if (value < 0)
{
negative = true;
- uvalue = (unsigned_eval_t) -value;
+ uvalue = -(uint32_t) value;
}
else
{
negative = false;
- uvalue = (unsigned_eval_t) value;
+ uvalue = (uint32_t) value;
}
do
@@ -401,7 +401,7 @@ shipout_int (struct obstack *obs, int va
{
const char *s;
- s = ntoa ((eval_t) val, 10);
+ s = ntoa ((int32_t) val, 10);
obstack_grow (obs, s, strlen (s));
}
@@ -940,7 +940,7 @@ m4_sysval (struct obstack *obs, int argc
static void
m4_eval (struct obstack *obs, int argc, token_data **argv)
{
- eval_t value = 0;
+ int32_t value = 0;
int radix = 10;
int min = 1;
const char *s;
@@ -954,8 +954,8 @@ m4_eval (struct obstack *obs, int argc,
if (radix < 1 || radix > (int) strlen (digits))
{
M4ERROR ((warning_status, 0,
- "radix in builtin `%s' out of range (radix = %d)",
- ARG (0), radix));
+ "radix %d in builtin `%s' out of range",
+ radix, ARG (0)));
return;
}
@@ -1304,7 +1304,7 @@ m4_maketemp (struct obstack *obs, int ar
if (str[i - 1] != 'X')
break;
obstack_grow (obs, str, i);
- str = ntoa ((eval_t) getpid (), 10);
+ str = ntoa ((int32_t) getpid (), 10);
len2 = strlen (str);
if (len2 > len - i)
obstack_grow0 (obs, str + len2 - (len - i), len - i);
Index: src/eval.c
===================================================================
RCS file: /sources/m4/m4/src/Attic/eval.c,v
retrieving revision 1.1.1.1.2.7
diff -u -p -r1.1.1.1.2.7 eval.c
--- src/eval.c 1 Nov 2006 22:29:08 -0000 1.1.1.1.2.7
+++ src/eval.c 6 Jan 2007 19:00:56 -0000
@@ -1,6 +1,6 @@
/* GNU m4 -- A simple macro processor
- Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2006
+ Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2006, 2007
Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
@@ -22,8 +22,7 @@
/* This file contains the functions to evaluate integer expressions for
the "eval" macro. It is a little, fairly self-contained module, with
its own scanner, and a recursive descent parser. The only entry point
- is evaluate (). For POSIX semantics of the "eval" macro, the type
- eval_t must be a 32-bit signed integer. */
+ is evaluate (). */
#include "m4.h"
@@ -31,7 +30,7 @@
typedef enum eval_token
{
- ERROR,
+ ERROR, BADOP,
PLUS, MINUS,
EXPONENT,
TIMES, DIVIDE, MODULO,
@@ -49,29 +48,33 @@ eval_token;
typedef enum eval_error
{
NO_ERROR,
- MISSING_RIGHT,
+ DIVIDE_ZERO,
+ MODULO_ZERO,
+ NEGATIVE_EXPONENT,
+ /* All errors prior to SYNTAX_ERROR can be ignored in a dead
+ branch of && and ||. All errors after are just more details
+ about a syntax error. */
SYNTAX_ERROR,
+ MISSING_RIGHT,
UNKNOWN_INPUT,
EXCESS_INPUT,
- DIVIDE_ZERO,
- MODULO_ZERO
+ INVALID_OPERATOR
}
eval_error;
-static eval_error logical_or_term (eval_token, eval_t *);
-static eval_error logical_and_term (eval_token, eval_t *);
-static eval_error or_term (eval_token, eval_t *);
-static eval_error xor_term (eval_token, eval_t *);
-static eval_error and_term (eval_token, eval_t *);
-static eval_error not_term (eval_token, eval_t *);
-static eval_error logical_not_term (eval_token, eval_t *);
-static eval_error cmp_term (eval_token, eval_t *);
-static eval_error shift_term (eval_token, eval_t *);
-static eval_error add_term (eval_token, eval_t *);
-static eval_error mult_term (eval_token, eval_t *);
-static eval_error exp_term (eval_token, eval_t *);
-static eval_error unary_term (eval_token, eval_t *);
-static eval_error simple_term (eval_token, eval_t *);
+static eval_error logical_or_term (eval_token, int32_t *);
+static eval_error logical_and_term (eval_token, int32_t *);
+static eval_error or_term (eval_token, int32_t *);
+static eval_error xor_term (eval_token, int32_t *);
+static eval_error and_term (eval_token, int32_t *);
+static eval_error equality_term (eval_token, int32_t *);
+static eval_error cmp_term (eval_token, int32_t *);
+static eval_error shift_term (eval_token, int32_t *);
+static eval_error add_term (eval_token, int32_t *);
+static eval_error mult_term (eval_token, int32_t *);
+static eval_error exp_term (eval_token, int32_t *);
+static eval_error unary_term (eval_token, int32_t *);
+static eval_error simple_term (eval_token, int32_t *);
/*--------------------.
| Lexical functions. |
@@ -100,7 +103,7 @@ eval_undo (void)
/* VAL is numerical value, if any. */
static eval_token
-eval_lex (eval_t *val)
+eval_lex (int32_t *val)
{
while (isspace (to_uchar (*eval_text)))
eval_text++;
@@ -149,7 +152,8 @@ eval_lex (eval_t *val)
else
base = 10;
- (*val) = 0;
+ /* FIXME - this calculation can overflow. Consider xstrtol. */
+ *val = 0;
for (; *eval_text; eval_text++)
{
if (isdigit (to_uchar (*eval_text)))
@@ -173,7 +177,7 @@ eval_lex (eval_t *val)
else if (digit >= base)
break;
else
- (*val) = (*val) * base + digit;
+ *val = *val * base + digit;
}
return NUMBER;
}
@@ -181,8 +185,12 @@ eval_lex (eval_t *val)
switch (*eval_text++)
{
case '+':
+ if (*eval_text == '+' || *eval_text == '=')
+ return BADOP;
return PLUS;
case '-':
+ if (*eval_text == '-' || *eval_text == '=')
+ return BADOP;
return MINUS;
case '*':
if (*eval_text == '*')
@@ -190,24 +198,31 @@ eval_lex (eval_t *val)
eval_text++;
return EXPONENT;
}
- else
- return TIMES;
+ else if (*eval_text == '=')
+ return BADOP;
+ return TIMES;
case '/':
+ if (*eval_text == '=')
+ return BADOP;
return DIVIDE;
case '%':
+ if (*eval_text == '=')
+ return BADOP;
return MODULO;
case '=':
if (*eval_text == '=')
- eval_text++;
- return EQ;
+ {
+ eval_text++;
+ return EQ;
+ }
+ return BADOP;
case '!':
if (*eval_text == '=')
{
eval_text++;
return NOTEQ;
}
- else
- return LNOT;
+ return LNOT;
case '>':
if (*eval_text == '=')
{
@@ -216,11 +231,11 @@ eval_lex (eval_t *val)
}
else if (*eval_text == '>')
{
- eval_text++;
+ if (*++eval_text == '=')
+ return BADOP;
return RSHIFT;
}
- else
- return GT;
+ return GT;
case '<':
if (*eval_text == '=')
{
@@ -229,12 +244,14 @@ eval_lex (eval_t *val)
}
else if (*eval_text == '<')
{
- eval_text++;
+ if (*++eval_text == '=')
+ return BADOP;
return LSHIFT;
}
- else
- return LS;
+ return LS;
case '^':
+ if (*eval_text == '=')
+ return BADOP;
return XOR;
case '~':
return NOT;
@@ -244,16 +261,18 @@ eval_lex (eval_t *val)
eval_text++;
return LAND;
}
- else
- return AND;
+ else if (*eval_text == '=')
+ return BADOP;
+ return AND;
case '|':
if (*eval_text == '|')
{
eval_text++;
return LOR;
}
- else
- return OR;
+ else if (*eval_text == '=')
+ return BADOP;
+ return OR;
case '(':
return LEFTP;
case ')':
@@ -268,7 +287,7 @@ eval_lex (eval_t *val)
`---------------------------------------*/
bool
-evaluate (const char *expr, eval_t *val)
+evaluate (const char *expr, int32_t *val)
{
eval_token et;
eval_error err;
@@ -278,7 +297,12 @@ evaluate (const char *expr, eval_t *val)
err = logical_or_term (et, val);
if (err == NO_ERROR && *eval_text != '\0')
- err = EXCESS_INPUT;
+ {
+ if (eval_lex (val) == BADOP)
+ err = INVALID_OPERATOR;
+ else
+ err = EXCESS_INPUT;
+ }
switch (err)
{
@@ -306,6 +330,12 @@ evaluate (const char *expr, eval_t *val)
"bad expression in eval (excess input): %s", expr));
break;
+ case INVALID_OPERATOR:
+ M4ERROR ((warning_status, 0,
+ "invalid operator in eval: %s", expr));
+ retcode = EXIT_FAILURE;
+ break;
+
case DIVIDE_ZERO:
M4ERROR ((warning_status, 0,
"divide by zero in eval: %s", expr));
@@ -316,6 +346,11 @@ evaluate (const char *expr, eval_t *val)
"modulo by zero in eval: %s", expr));
break;
+ case NEGATIVE_EXPONENT:
+ M4ERROR ((warning_status, 0,
+ "negative exponent in eval: %s", expr));
+ break;
+
default:
M4ERROR ((warning_status, 0,
"INTERNAL ERROR: bad error code in evaluate ()"));
@@ -330,9 +365,9 @@ evaluate (const char *expr, eval_t *val)
`---------------------------*/
static eval_error
-logical_or_term (eval_token et, eval_t *v1)
+logical_or_term (eval_token et, int32_t *v1)
{
- eval_t v2;
+ int32_t v2;
eval_error er;
if ((er = logical_and_term (et, v1)) != NO_ERROR)
@@ -344,10 +379,14 @@ logical_or_term (eval_token et, eval_t *
if (et == ERROR)
return UNKNOWN_INPUT;
- if ((er = logical_and_term (et, &v2)) != NO_ERROR)
+ /* Implement short-circuiting of valid syntax. */
+ er = logical_and_term (et, &v2);
+ if (er == NO_ERROR)
+ *v1 = *v1 || v2;
+ else if (*v1 != 0 && er < SYNTAX_ERROR)
+ *v1 = 1;
+ else
return er;
-
- *v1 = *v1 || v2;
}
if (et == ERROR)
return UNKNOWN_INPUT;
@@ -357,9 +396,9 @@ logical_or_term (eval_token et, eval_t *
}
static eval_error
-logical_and_term (eval_token et, eval_t *v1)
+logical_and_term (eval_token et, int32_t *v1)
{
- eval_t v2;
+ int32_t v2;
eval_error er;
if ((er = or_term (et, v1)) != NO_ERROR)
@@ -371,10 +410,14 @@ logical_and_term (eval_token et, eval_t
if (et == ERROR)
return UNKNOWN_INPUT;
- if ((er = or_term (et, &v2)) != NO_ERROR)
+ /* Implement short-circuiting of valid syntax. */
+ er = or_term (et, &v2);
+ if (er == NO_ERROR)
+ *v1 = *v1 && v2;
+ else if (*v1 == 0 && er < SYNTAX_ERROR)
+ ; /* v1 is already 0 */
+ else
return er;
-
- *v1 = *v1 && v2;
}
if (et == ERROR)
return UNKNOWN_INPUT;
@@ -384,9 +427,9 @@ logical_and_term (eval_token et, eval_t
}
static eval_error
-or_term (eval_token et, eval_t *v1)
+or_term (eval_token et, int32_t *v1)
{
- eval_t v2;
+ int32_t v2;
eval_error er;
if ((er = xor_term (et, v1)) != NO_ERROR)
@@ -401,7 +444,7 @@ or_term (eval_token et, eval_t *v1)
if ((er = xor_term (et, &v2)) != NO_ERROR)
return er;
- *v1 = *v1 | v2;
+ *v1 |= v2;
}
if (et == ERROR)
return UNKNOWN_INPUT;
@@ -411,9 +454,9 @@ or_term (eval_token et, eval_t *v1)
}
static eval_error
-xor_term (eval_token et, eval_t *v1)
+xor_term (eval_token et, int32_t *v1)
{
- eval_t v2;
+ int32_t v2;
eval_error er;
if ((er = and_term (et, v1)) != NO_ERROR)
@@ -428,7 +471,7 @@ xor_term (eval_token et, eval_t *v1)
if ((er = and_term (et, &v2)) != NO_ERROR)
return er;
- *v1 = *v1 ^ v2;
+ *v1 ^= v2;
}
if (et == ERROR)
return UNKNOWN_INPUT;
@@ -438,12 +481,12 @@ xor_term (eval_token et, eval_t *v1)
}
static eval_error
-and_term (eval_token et, eval_t *v1)
+and_term (eval_token et, int32_t *v1)
{
- eval_t v2;
+ int32_t v2;
eval_error er;
- if ((er = not_term (et, v1)) != NO_ERROR)
+ if ((er = equality_term (et, v1)) != NO_ERROR)
return er;
while ((et = eval_lex (&v2)) == AND)
@@ -452,10 +495,10 @@ and_term (eval_token et, eval_t *v1)
if (et == ERROR)
return UNKNOWN_INPUT;
- if ((er = not_term (et, &v2)) != NO_ERROR)
+ if ((er = equality_term (et, &v2)) != NO_ERROR)
return er;
- *v1 = *v1 & v2;
+ *v1 &= v2;
}
if (et == ERROR)
return UNKNOWN_INPUT;
@@ -465,61 +508,43 @@ and_term (eval_token et, eval_t *v1)
}
static eval_error
-not_term (eval_token et, eval_t *v1)
+equality_term (eval_token et, int32_t *v1)
{
+ eval_token op;
+ int32_t v2;
eval_error er;
- if (et == NOT)
- {
- et = eval_lex (v1);
- if (et == ERROR)
- return UNKNOWN_INPUT;
-
- if ((er = not_term (et, v1)) != NO_ERROR)
- return er;
- *v1 = ~*v1;
- }
- else
- if ((er = logical_not_term (et, v1)) != NO_ERROR)
- return er;
-
- return NO_ERROR;
-}
-
-static eval_error
-logical_not_term (eval_token et, eval_t *v1)
-{
- eval_error er;
+ if ((er = cmp_term (et, v1)) != NO_ERROR)
+ return er;
- if (et == LNOT)
+ while ((op = eval_lex (&v2)) == EQ || op == NOTEQ)
{
- et = eval_lex (v1);
+ et = eval_lex (&v2);
if (et == ERROR)
return UNKNOWN_INPUT;
- if ((er = logical_not_term (et, v1)) != NO_ERROR)
+ if ((er = cmp_term (et, &v2)) != NO_ERROR)
return er;
- *v1 = !*v1;
+ *v1 = (op == EQ) == (*v1 == v2);
}
- else
- if ((er = cmp_term (et, v1)) != NO_ERROR)
- return er;
+ if (op == ERROR)
+ return UNKNOWN_INPUT;
+ eval_undo ();
return NO_ERROR;
}
static eval_error
-cmp_term (eval_token et, eval_t *v1)
+cmp_term (eval_token et, int32_t *v1)
{
eval_token op;
- eval_t v2;
+ int32_t v2;
eval_error er;
if ((er = shift_term (et, v1)) != NO_ERROR)
return er;
- while ((op = eval_lex (&v2)) == EQ || op == NOTEQ
- || op == GT || op == GTEQ
+ while ((op = eval_lex (&v2)) == GT || op == GTEQ
|| op == LS || op == LSEQ)
{
@@ -532,14 +557,6 @@ cmp_term (eval_token et, eval_t *v1)
switch (op)
{
- case EQ:
- *v1 = *v1 == v2;
- break;
-
- case NOTEQ:
- *v1 = *v1 != v2;
- break;
-
case GT:
*v1 = *v1 > v2;
break;
@@ -570,10 +587,11 @@ cmp_term (eval_token et, eval_t *v1)
}
static eval_error
-shift_term (eval_token et, eval_t *v1)
+shift_term (eval_token et, int32_t *v1)
{
eval_token op;
- eval_t v2;
+ int32_t v2;
+ uint32_t u1;
eval_error er;
if ((er = add_term (et, v1)) != NO_ERROR)
@@ -589,20 +607,24 @@ shift_term (eval_token et, eval_t *v1)
if ((er = add_term (et, &v2)) != NO_ERROR)
return er;
- /* Shifting by a negative number, or by greater than the width, is
- undefined in C, but POSIX requires eval to operate on 32-bit signed
- numbers. Explicitly mask the right argument to ensure defined
- behavior. */
+ /* Minimize undefined C behavior (shifting by a negative number,
+ shifting by the width or greater, left shift overflow, or
+ right shift of a negative number). Implement Java 32-bit
+ wrap-around semantics. This code assumes that the
+ implementation-defined overflow when casting unsigned to
+ signed is a silent twos-complement wrap-around. */
switch (op)
{
case LSHIFT:
- *v1 = *v1 << (v2 & 0x1f);
+ u1 = *v1;
+ u1 <<= (uint32_t) (v2 & 0x1f);
+ *v1 = u1;
break;
case RSHIFT:
- /* This assumes 2's-complement with sign-extension, since shifting
- a negative number right is implementation-defined in C. */
- *v1 = *v1 >> (v2 & 0x1f);
+ u1 = *v1 < 0 ? ~*v1 : *v1;
+ u1 >>= (uint32_t) (v2 & 0x1f);
+ *v1 = *v1 < 0 ? ~u1 : u1;
break;
default:
@@ -619,10 +641,10 @@ shift_term (eval_token et, eval_t *v1)
}
static eval_error
-add_term (eval_token et, eval_t *v1)
+add_term (eval_token et, int32_t *v1)
{
eval_token op;
- eval_t v2;
+ int32_t v2;
eval_error er;
if ((er = mult_term (et, v1)) != NO_ERROR)
@@ -637,10 +659,14 @@ add_term (eval_token et, eval_t *v1)
if ((er = mult_term (et, &v2)) != NO_ERROR)
return er;
+ /* Minimize undefined C behavior on overflow. This code assumes
+ that the implementation-defined overflow when casting
+ unsigned to signed is a silent twos-complement
+ wrap-around. */
if (op == PLUS)
- *v1 = *v1 + v2;
+ *v1 = (int32_t) ((uint32_t) *v1 + (uint32_t) v2);
else
- *v1 = *v1 - v2;
+ *v1 = (int32_t) ((uint32_t) *v1 - (uint32_t) v2);
}
if (op == ERROR)
return UNKNOWN_INPUT;
@@ -650,10 +676,10 @@ add_term (eval_token et, eval_t *v1)
}
static eval_error
-mult_term (eval_token et, eval_t *v1)
+mult_term (eval_token et, int32_t *v1)
{
eval_token op;
- eval_t v2;
+ int32_t v2;
eval_error er;
if ((er = exp_term (et, v1)) != NO_ERROR)
@@ -668,10 +694,14 @@ mult_term (eval_token et, eval_t *v1)
if ((er = exp_term (et, &v2)) != NO_ERROR)
return er;
+ /* Minimize undefined C behavior on overflow. This code assumes
+ that the implementation-defined overflow when casting
+ unsigned to signed is a silent twos-complement
+ wrap-around. */
switch (op)
{
case TIMES:
- *v1 = *v1 * v2;
+ *v1 = (int32_t) ((uint32_t) *v1 * (uint32_t) v2);
break;
case DIVIDE:
@@ -679,9 +709,9 @@ mult_term (eval_token et, eval_t *v1)
return DIVIDE_ZERO;
else if (v2 == -1)
/* Avoid the x86 SIGFPE on INT_MIN / -1. */
- *v1 = -*v1;
+ *v1 = (int32_t) -(uint32_t) *v1;
else
- *v1 = *v1 / v2;
+ *v1 = (int32_t) ((uint32_t) *v1 / (uint32_t) v2);
break;
case MODULO:
@@ -691,7 +721,7 @@ mult_term (eval_token et, eval_t *v1)
/* Avoid the x86 SIGFPE on INT_MIN % -1. */
*v1 = 0;
else
- *v1 = *v1 % v2;
+ *v1 %= v2;
break;
default:
@@ -708,15 +738,14 @@ mult_term (eval_token et, eval_t *v1)
}
static eval_error
-exp_term (eval_token et, eval_t *v1)
+exp_term (eval_token et, int32_t *v1)
{
- register eval_t result;
- eval_t v2;
+ uint32_t result;
+ int32_t v2;
eval_error er;
if ((er = unary_term (et, v1)) != NO_ERROR)
return er;
- result = *v1;
while ((et = eval_lex (&v2)) == EXPONENT)
{
@@ -727,9 +756,17 @@ exp_term (eval_token et, eval_t *v1)
if ((er = exp_term (et, &v2)) != NO_ERROR)
return er;
+ /* Minimize undefined C behavior on overflow. This code assumes
+ that the implementation-defined overflow when casting
+ unsigned to signed is a silent twos-complement
+ wrap-around. */
result = 1;
+ if (v2 < 0)
+ return NEGATIVE_EXPONENT;
+ if (*v1 == 0 && v2 == 0)
+ return DIVIDE_ZERO;
while (v2-- > 0)
- result *= *v1;
+ result *= (uint32_t) *v1;
*v1 = result;
}
if (et == ERROR)
@@ -740,34 +777,41 @@ exp_term (eval_token et, eval_t *v1)
}
static eval_error
-unary_term (eval_token et, eval_t *v1)
+unary_term (eval_token et, int32_t *v1)
{
eval_token et2 = et;
eval_error er;
- if (et == PLUS || et == MINUS)
+ if (et == PLUS || et == MINUS || et == NOT || et == LNOT)
{
et2 = eval_lex (v1);
if (et2 == ERROR)
return UNKNOWN_INPUT;
- if ((er = simple_term (et2, v1)) != NO_ERROR)
+ if ((er = unary_term (et2, v1)) != NO_ERROR)
return er;
+ /* Minimize undefined C behavior on overflow. This code assumes
+ that the implementation-defined overflow when casting
+ unsigned to signed is a silent twos-complement
+ wrap-around. */
if (et == MINUS)
- *v1 = -*v1;
+ *v1 = (int32_t) -(uint32_t) *v1;
+ else if (et == NOT)
+ *v1 = ~*v1;
+ else if (et == LNOT)
+ *v1 = *v1 == 0 ? 1 : 0;
}
- else
- if ((er = simple_term (et, v1)) != NO_ERROR)
- return er;
+ else if ((er = simple_term (et, v1)) != NO_ERROR)
+ return er;
return NO_ERROR;
}
static eval_error
-simple_term (eval_token et, eval_t *v1)
+simple_term (eval_token et, int32_t *v1)
{
- eval_t v2;
+ int32_t v2;
eval_error er;
switch (et)
@@ -792,6 +836,9 @@ simple_term (eval_token et, eval_t *v1)
case NUMBER:
break;
+ case BADOP:
+ return INVALID_OPERATOR;
+
default:
return SYNTAX_ERROR;
}
Index: src/m4.h
===================================================================
RCS file: /sources/m4/m4/src/m4.h,v
retrieving revision 1.1.1.1.2.35
diff -u -p -r1.1.1.1.2.35 m4.h
--- src/m4.h 11 Nov 2006 13:36:56 -0000 1.1.1.1.2.35
+++ src/m4.h 6 Jan 2007 19:00:56 -0000
@@ -1,7 +1,7 @@
/* GNU m4 -- A simple macro processor
- Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2004, 2005, 2006 Free
- Software Foundation, Inc.
+ Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2004, 2005, 2006, 2007
+ Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -28,6 +28,7 @@
#include <ctype.h>
#include <errno.h>
#include <stdbool.h>
+#include <stdint.h>
#include <string.h>
#include <sys/types.h>
@@ -415,11 +416,7 @@ FILE *m4_path_search (const char *, char
/* File: eval.c --- expression evaluation. */
-/* eval_t and unsigned_eval_t should be at least 32 bits. */
-typedef int eval_t;
-typedef unsigned int unsigned_eval_t;
-
-bool evaluate (const char *, eval_t *);
+bool evaluate (const char *, int32_t *);
/* File: format.c --- printf like formatting. */