-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
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).
2007-01-03 Eric Blake <[EMAIL PROTECTED]>
* doc/m4.texinfo (Format, Incr): More merges.
(Eval): Ensure C precedence rules are met.
* modules/evalparse.c (BADOP, INVALID_OPERATOR): New enumerators.
(not_term, logical_not_term): Delete; these are same precedence
as other unary operators.
(equality_term): New; these are lower precedence than relational
operators.
(eval_lex, simple_term, m4_evaluate): Recognize forbidden C
operators for better error messages.
(logical_or_term, logical_and_term): Short-circuit out the error
of division by zero.
(unary_term): Allow consecutive unary operators.
* modules/m4.c (int2numb, numb2int): Delete; these potentially
truncate bits.
(numb_not, numb_eor, numb_ior, numb_and): Update callers.
* modules/mpeval.c (reduce1, reduce2): Protect macros better.
* NEWS: Document this change.
- --
Don't work too hard, make some time for fun as well!
Eric Blake [EMAIL PROTECTED]
-----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
iD8DBQFFm8FI84KuGfSFAYARAvR+AKCDSo2HSAVVjTGCUtiRqsitoWULpgCdGzqH
wnBQPOlUh2nVGDwdCKcIv2c=
=Uusy
-----END PGP SIGNATURE-----
Index: NEWS
===================================================================
RCS file: /sources/m4/m4/NEWS,v
retrieving revision 1.33
diff -u -p -r1.33 NEWS
--- NEWS 23 Dec 2006 00:02:20 -0000 1.33
+++ NEWS 3 Jan 2007 14:39:52 -0000
@@ -1,5 +1,5 @@
GNU m4 NEWS - History of user-visible changes. -*- outline -*-
-Copyright (C) 1992, 1993, 1994, 1998, 2000, 2001, 2006 Free Software
+Copyright (C) 1992, 1993, 1994, 1998, 2000, 2001, 2006, 2007 Free Software
Foundation, Inc.
* Version beta 1.9b - ???, by ??? (CVS version 1.9a)
@@ -90,6 +90,9 @@ promoted to 2.0.
*** The `defn' builtin now allows any number of arguments, as POSIX requires.
- FIXME: This still doesn't work with concatenating builtins with text.
+*** The `eval' builtin now follows C precedence rules. Additionally,
+ short-circuit operators correctly short-circuit division by zero.
+
- FIXME: POSIX recommends using ${10} instead of $10 for the tenth
positional argument. We should deprecate $10.
Index: doc/m4.texinfo
===================================================================
RCS file: /sources/m4/m4/doc/m4.texinfo,v
retrieving revision 1.87
diff -u -p -r1.87 m4.texinfo
--- doc/m4.texinfo 27 Dec 2006 14:14:27 -0000 1.87
+++ doc/m4.texinfo 3 Jan 2007 14:39:53 -0000
@@ -46,7 +46,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, 1998, 1999,
-2000, 2001, 2004, 2005, 2006 Free Software Foundation, Inc.
+2000, 2001, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
@quotation
Permission is granted to copy, distribute and/or modify this document
@@ -5619,14 +5619,16 @@ patsubst(`abc')
@cindex formatted output
@cindex output, formatted
[EMAIL PROTECTED] GNU extensions
[EMAIL PROTECTED] @acronym{GNU} extensions
+Formatted output can be made with @code{format}:
+
@deffn {Builtin (gnu)} format (@var{format-string}, @dots{})
-Formatted output can be made with @code{format}, which works much like
-the C function @code{printf}. The first argument is a format string,
-which can contain @samp{%} specifications, and the expansion of
[EMAIL PROTECTED] is the formatted string.
+Works much like the C function @code{printf}. The first argument
[EMAIL PROTECTED] can contain @samp{%} specifications which are
+satisfied by additional arguments, and the expansion of @code{format} is
+the formatted string.
-The builtin macros @code{format} is recognized only when given arguments.
+The macro @code{format} is recognized only with parameters.
@end deffn
Its use is best described by a few examples:
@@ -5642,12 +5644,15 @@ len(format(`%-*X', `5000', `1'))
@result{}5000
@end example
-Using the @code{forloop} macro defined in @xref{Forloop}, this
+Using the @code{forloop} macro defined earlier (@pxref{Forloop}), this
example shows how @code{format} can be used to produce tabular output.
[EMAIL PROTECTED] ignore
[EMAIL PROTECTED] examples
@example
-forloop(`i', 1, 10, `format(`%6d squared is %10d
+$ @kbd{m4 -I examples}
+include(`forloop.m4')
[EMAIL PROTECTED]
+forloop(`i', `1', `10', `format(`%6d squared is %10d
', i, eval(i**2))')
@result{} 1 squared is 1
@result{} 2 squared is 4
@@ -5659,18 +5664,23 @@ forloop(`i', 1, 10, `format(`%6d squared
@result{} 8 squared is 64
@result{} 9 squared is 81
@result{} 10 squared is 100
[EMAIL PROTECTED]
@end example
The builtin @code{format} is modeled after the ANSI C @samp{printf}
-function, and supports the normal @samp{%} specifiers: @samp{c},
+function, and supports these @samp{%} specifiers: @samp{c},
@samp{s}, @samp{d}, @samp{o}, @samp{x}, @samp{X}, @samp{u}, @samp{e},
[EMAIL PROTECTED], @samp{f}, @samp{F}, @samp{g}, and @samp{G}; it supports
-field widths and precisions, and the
[EMAIL PROTECTED], @samp{f}, @samp{F}, @samp{g}, @samp{G}, and @samp{%}; it
+supports field widths and precisions, and the
modifiers @samp{+}, @samp{-}, @[EMAIL PROTECTED] }}, @samp{0}, @samp{#},
@samp{h} and
@samp{l}. For more details on the functioning of @code{printf}, see the
C Library Manual.
@c FIXME - format still needs some improvements.
+For now, unrecognized specifiers are silently ignored, but it is
+anticipated that a future release of @acronym{GNU} @code{m4} will support more
+specifiers, and give warnings when problems are encountered. Likewise,
+escape sequences are not yet recognized.
@node Arithmetic
@chapter Macros for doing arithmetic
@@ -5692,22 +5702,34 @@ decrement operations.
@cindex decrement operator
@cindex increment operator
+Increment and decrement of integers are supported using the builtins
[EMAIL PROTECTED] and @code{decr}:
+
@deffn {Builtin (m4)} incr (@var{number})
@deffnx {Builtin (m4)} decr (@var{number})
-Increment and decrement of integers are supported using the builtins
[EMAIL PROTECTED] and @code{decr}, which expand to the numerical value of
[EMAIL PROTECTED], incremented, or decremented, respectively, by one.
+Expand to the numerical value of @var{number}, incremented
+or decremented, respectively, by one. Except for the empty string, the
+expansion is empty if @var{number} could not be parsed.
+
+The macros @code{incr} and @code{decr} are recognized only with
+parameters.
[EMAIL PROTECTED] deffn
@example
-incr(4)
+incr(`4')
@result{}5
-decr(7)
+decr(`7')
@result{}6
+incr()
[EMAIL PROTECTED]:stdin:3: Warning: incr: empty string treated as 0
[EMAIL PROTECTED]
+decr()
[EMAIL PROTECTED]:stdin:4: Warning: decr: empty string treated as 0
[EMAIL PROTECTED]
@end example
The builtin macros @code{incr} and @code{decr} are recognized only when
given arguments.
[EMAIL PROTECTED] deffn
@node Eval
@section Evaluating integer expressions
@@ -5715,30 +5737,37 @@ given arguments.
@cindex integer expression evaluation
@cindex evaluation, of integer expressions
@cindex expressions, evaluation of integer
[EMAIL PROTECTED] {Builtin (m4)} eval (@var{expression}, @ovar{radix},
@ovar{width})
-Integer expressions are evaluated with @code{eval}, which expands to the
-value of @var{expression}.
+Integer expressions are evaluated with @code{eval}:
+
[EMAIL PROTECTED] {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,
[EMAIL PROTECTED] and @var{width} control the format of the output. An error
+is issued if division by zero is attempted.
+
+The macro @code{eval} is recognized only with parameters.
[EMAIL PROTECTED] deffn
Expressions can contain the following operators, listed in order of
decreasing precedence.
@table @code
[EMAIL PROTECTED] -
-Unary minus
[EMAIL PROTECTED] ()
+Parenthesis
[EMAIL PROTECTED] + - ~ !
+Unary plus and minus, and bitwise and logical negation
@item **
Exponentiation
[EMAIL PROTECTED] * / % :
-Multiplication, division, modulo and ratio
[EMAIL PROTECTED] * / %
+Multiplication, division, and modulo
@item + -
Addition and subtraction
@item << >>
Shift left or right
[EMAIL PROTECTED] == != > >= < <=
[EMAIL PROTECTED] > >= < <=
Relational operators
[EMAIL PROTECTED] !
-Logical negation
[EMAIL PROTECTED] ~
-Bitwise negation
[EMAIL PROTECTED] == !=
+Equality operators
@item &
Bitwise and
@item ^
@@ -5751,12 +5780,67 @@ Logical and
Logical or
@end table
-All operators, except exponentiation, are left associative.
+All operators, except exponentiation, are left associative. C
+operators that perform variable assignment, such as @samp{=} or
[EMAIL PROTECTED], are forbidden, since @code{eval} only operates on constants,
+not variables.
+
+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
[EMAIL PROTECTED] 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.
+
+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
+correct result:
-Note that many @code{m4} implementations use @samp{^} as an alternate
-operator for the exponentiation, while many others use @samp{^} for the
-bitwise exclusive-or. GNU @code{m4} changed its behavior: it used to
-exponentiate for @samp{^}, it now computes the bitwise exclusive-or.
[EMAIL PROTECTED] status: 1
[EMAIL PROTECTED]
+eval(`1 == 2 > 0')
[EMAIL PROTECTED]
+eval(`(1 == 2) > 0')
[EMAIL PROTECTED]
+eval(`! 0 * 2')
[EMAIL PROTECTED]
+eval(`! (0 * 2)')
[EMAIL PROTECTED]
+eval(`1 | 1 ^ 1')
[EMAIL PROTECTED]
+eval(`(1 | 1) ^ 1')
[EMAIL PROTECTED]
+eval(`+ + - ~ ! ~ 0')
[EMAIL PROTECTED]
+eval(`++0')
[EMAIL PROTECTED]:stdin:8: eval: invalid operator: ++0
[EMAIL PROTECTED]
+eval(`1 = 1')
[EMAIL PROTECTED]:stdin:9: eval: invalid operator: 1 = 1
[EMAIL PROTECTED]
+eval(`0 |= 1')
[EMAIL PROTECTED]:stdin:10: eval: invalid operator: 0 |= 1
[EMAIL PROTECTED]
+eval(`2 || 1 / 0')
[EMAIL PROTECTED]
+eval(`0 || 1 / 0')
[EMAIL PROTECTED]:stdin:12: eval: divide by zero: 0 || 1 / 0
[EMAIL PROTECTED]
+eval(`0 && 1 % 0')
[EMAIL PROTECTED]
+eval(`2 && 1 % 0')
[EMAIL PROTECTED]:stdin:14: eval: modulo by zero: 2 && 1 % 0
[EMAIL PROTECTED]
[EMAIL PROTECTED] example
Numbers without special prefix are given decimal. A simple @samp{0}
prefix introduces an octal number. @samp{0x} introduces a hexadecimal
@@ -5776,7 +5860,6 @@ relational operators, a true relation re
relation return @code{0}.
The builtin macro @code{eval} is recognized only when given arguments.
[EMAIL PROTECTED] deffn
Here are a few examples of use of @code{eval}.
@@ -5786,18 +5869,18 @@ eval(`-3 * 5')
@result{}-15
eval(index(`Hello world', `llo') >= 0)
@result{}1
-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')
@error{}m4:stdin:7: eval: bad expression: foo / 6
@result{}
-eval(foo/6)
+eval(foo / 6)
@result{}111
@end example
@@ -5837,7 +5920,9 @@ When @code{m4} is compiled with a multip
It is almost identical to @code{eval}, except the calculations are done
with infinite precision. Numbers may be of any length.
-The @code{:} operator rationally divides two numbers and canonicalizes
[EMAIL PROTECTED] `:' 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{~},
Index: modules/evalparse.c
===================================================================
RCS file: /sources/m4/m4/modules/evalparse.c,v
retrieving revision 1.14
diff -u -p -r1.14 evalparse.c
--- modules/evalparse.c 9 Aug 2006 21:33:24 -0000 1.14
+++ modules/evalparse.c 3 Jan 2007 14:39:53 -0000
@@ -1,5 +1,5 @@
/* GNU m4 -- A simple macro processor
- Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2001, 2006
+ Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2001, 2006, 2007
Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
@@ -40,7 +40,7 @@
typedef enum eval_token
{
- ERROR,
+ ERROR, BADOP,
PLUS, MINUS,
EXPONENT,
TIMES, DIVIDE, MODULO, RATIO,
@@ -62,6 +62,7 @@ typedef enum eval_error
SYNTAX_ERROR,
UNKNOWN_INPUT,
EXCESS_INPUT,
+ INVALID_OPERATOR,
DIVIDE_ZERO,
MODULO_ZERO
}
@@ -72,8 +73,7 @@ static eval_error logical_and_term (m4
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 not_term (m4 *, eval_token, number *);
-static eval_error logical_not_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 *);
@@ -107,7 +107,9 @@ eval_undo (void)
eval_text = last_text;
}
-/* VAL is numerical value, if any. */
+/* VAL is numerical value, if any. Recognize C assignment operators,
+ even though we cannot support them, to issue better error
+ messages. */
static eval_token
eval_lex (number *val)
@@ -159,7 +161,7 @@ eval_lex (number *val)
else
base = 10;
- numb_set_si(val,0);
+ numb_set_si (val, 0);
for (; *eval_text; eval_text++)
{
if (isdigit (*eval_text))
@@ -176,17 +178,17 @@ eval_lex (number *val)
{ /* (*val) = (*val) * base; */
number xbase;
- numb_init(xbase);
- numb_set_si(&xbase,base);
- numb_times(*val,xbase);
- numb_fini(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);
+ numb_init (xdigit);
+ numb_set_si (&xdigit, digit);
+ numb_plus (*val, xdigit);
+ numb_fini (xdigit);
}
}
return NUMBER;
@@ -195,8 +197,12 @@ eval_lex (number *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 == '*')
@@ -204,26 +210,33 @@ eval_lex (number *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 ':':
- return RATIO;
+ return RATIO; /* FIXME - this clashes with supporting ?:. */
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 == '=')
{
@@ -232,7 +245,8 @@ eval_lex (number *val)
}
else if (*eval_text == '>')
{
- eval_text++;
+ if (*eval_text++ == '=')
+ return BADOP;
return RSHIFT;
}
else
@@ -245,12 +259,15 @@ eval_lex (number *val)
}
else if (*eval_text == '<')
{
- eval_text++;
+ if (*eval_text++ == '=')
+ return BADOP;
return LSHIFT;
}
else
return LS;
case '^':
+ if (*eval_text == '=')
+ return BADOP;
return XOR;
case '~':
return NOT;
@@ -260,16 +277,18 @@ eval_lex (number *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 ')':
@@ -289,19 +308,24 @@ logical_or_term (m4 *context, eval_token
if ((er = logical_and_term (context, et, v1)) != NO_ERROR)
return er;
- numb_init(v2);
+ numb_init (v2);
while ((et = eval_lex (&v2)) == LOR)
{
et = eval_lex (&v2);
if (et == ERROR)
return UNKNOWN_INPUT;
- if ((er = logical_and_term (context, et, &v2)) != NO_ERROR)
+ /* Implement short-circuiting of valid syntax. */
+ 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))
+ numb_set (*v1, numb_ONE);
+ else
return er;
-
- numb_lior(*v1,v2);
}
- numb_fini(v2);
+ numb_fini (v2);
if (et == ERROR)
return UNKNOWN_INPUT;
@@ -318,19 +342,24 @@ logical_and_term (m4 *context, eval_toke
if ((er = or_term (context, et, v1)) != NO_ERROR)
return er;
- numb_init(v2);
+ numb_init (v2);
while ((et = eval_lex (&v2)) == LAND)
{
et = eval_lex (&v2);
if (et == ERROR)
return UNKNOWN_INPUT;
- if ((er = or_term (context, et, &v2)) != NO_ERROR)
+ /* Implement short-circuiting of valid syntax. */
+ 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))
+ numb_set (*v1, numb_ZERO);
+ else
return er;
-
- numb_land(*v1,v2);
}
- numb_fini(v2);
+ numb_fini (v2);
if (et == ERROR)
return UNKNOWN_INPUT;
@@ -347,7 +376,7 @@ or_term (m4 *context, eval_token et, num
if ((er = xor_term (context, et, v1)) != NO_ERROR)
return er;
- numb_init(v2);
+ numb_init (v2);
while ((et = eval_lex (&v2)) == OR)
{
et = eval_lex (&v2);
@@ -357,9 +386,9 @@ or_term (m4 *context, eval_token et, num
if ((er = xor_term (context, et, &v2)) != NO_ERROR)
return er;
- numb_ior(context, v1, &v2);
+ numb_ior (context, v1, &v2);
}
- numb_fini(v2);
+ numb_fini (v2);
if (et == ERROR)
return UNKNOWN_INPUT;
@@ -376,7 +405,7 @@ xor_term (m4 *context, eval_token et, nu
if ((er = and_term (context, et, v1)) != NO_ERROR)
return er;
- numb_init(v2);
+ numb_init (v2);
while ((et = eval_lex (&v2)) == XOR)
{
et = eval_lex (&v2);
@@ -386,9 +415,9 @@ xor_term (m4 *context, eval_token et, nu
if ((er = and_term (context, et, &v2)) != NO_ERROR)
return er;
- numb_eor(context, v1, &v2);
+ numb_eor (context, v1, &v2);
}
- numb_fini(v2);
+ numb_fini (v2);
if (et == ERROR)
return UNKNOWN_INPUT;
@@ -402,22 +431,22 @@ and_term (m4 *context, eval_token et, nu
number v2;
eval_error er;
- if ((er = not_term (context, et, v1)) != NO_ERROR)
+ if ((er = equality_term (context, et, v1)) != NO_ERROR)
return er;
- numb_init(v2);
+ numb_init (v2);
while ((et = eval_lex (&v2)) == AND)
{
et = eval_lex (&v2);
if (et == ERROR)
return UNKNOWN_INPUT;
- if ((er = not_term (context, et, &v2)) != NO_ERROR)
+ if ((er = equality_term (context, et, &v2)) != NO_ERROR)
return er;
- numb_and(context, v1, &v2);
+ numb_and (context, v1, &v2);
}
- numb_fini(v2);
+ numb_fini (v2);
if (et == ERROR)
return UNKNOWN_INPUT;
@@ -426,46 +455,35 @@ and_term (m4 *context, eval_token et, nu
}
static eval_error
-not_term (m4 *context, eval_token et, number *v1)
+equality_term (m4 *context, eval_token et, number *v1)
{
+ eval_token op;
+ number v2;
eval_error er;
- if (et == NOT)
- {
- et = eval_lex (v1);
- if (et == ERROR)
- return UNKNOWN_INPUT;
-
- if ((er = not_term (context, et, v1)) != NO_ERROR)
- return er;
- numb_not(context, v1);
- }
- else
- if ((er = logical_not_term (context, et, v1)) != NO_ERROR)
- return er;
-
- return NO_ERROR;
-}
-
-static eval_error
-logical_not_term (m4 *context, eval_token et, number *v1)
-{
- eval_error er;
+ if ((er = cmp_term (context, et, v1)) != NO_ERROR)
+ return er;
- if (et == LNOT)
+ numb_init (v2);
+ 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 (context, et, v1)) != NO_ERROR)
+ if ((er = cmp_term (context, et, &v2)) != NO_ERROR)
return er;
- numb_lnot(*v1);
+
+ if (op == EQ)
+ numb_eq (*v1, v2);
+ else
+ numb_ne (*v1, v2);
}
- else
- if ((er = cmp_term (context, et, v1)) != NO_ERROR)
- return er;
+ numb_fini (v2);
+ if (op == ERROR)
+ return UNKNOWN_INPUT;
+ eval_undo ();
return NO_ERROR;
}
@@ -479,9 +497,8 @@ cmp_term (m4 *context, eval_token et, nu
if ((er = shift_term (context, et, v1)) != NO_ERROR)
return er;
- numb_init(v2);
- while ((op = eval_lex (&v2)) == EQ || op == NOTEQ
- || op == GT || op == GTEQ
+ numb_init (v2);
+ while ((op = eval_lex (&v2)) == GT || op == GTEQ
|| op == LS || op == LSEQ)
{
@@ -494,28 +511,20 @@ cmp_term (m4 *context, eval_token et, nu
switch (op)
{
- case EQ:
- numb_eq(*v1,v2);
- break;
-
- case NOTEQ:
- numb_ne(*v1,v2);
- break;
-
case GT:
- numb_gt(*v1,v2);
+ numb_gt (*v1, v2);
break;
case GTEQ:
- numb_ge(*v1,v2);
+ numb_ge (*v1, v2);
break;
case LS:
- numb_lt(*v1,v2);
+ numb_lt (*v1, v2);
break;
case LSEQ:
- numb_le(*v1,v2);
+ numb_le (*v1, v2);
break;
default:
@@ -523,7 +532,7 @@ cmp_term (m4 *context, eval_token et, nu
abort ();
}
}
- numb_fini(v2);
+ numb_fini (v2);
if (op == ERROR)
return UNKNOWN_INPUT;
@@ -541,7 +550,7 @@ shift_term (m4 *context, eval_token et,
if ((er = add_term (context, et, v1)) != NO_ERROR)
return er;
- numb_init(v2);
+ numb_init (v2);
while ((op = eval_lex (&v2)) == LSHIFT || op == RSHIFT)
{
@@ -555,11 +564,11 @@ shift_term (m4 *context, eval_token et,
switch (op)
{
case LSHIFT:
- numb_lshift(context, v1, &v2);
+ numb_lshift (context, v1, &v2);
break;
case RSHIFT:
- numb_rshift(context, v1, &v2);
+ numb_rshift (context, v1, &v2);
break;
default:
@@ -567,7 +576,7 @@ shift_term (m4 *context, eval_token et,
abort ();
}
}
- numb_fini(v2);
+ numb_fini (v2);
if (op == ERROR)
return UNKNOWN_INPUT;
@@ -585,7 +594,7 @@ add_term (m4 *context, eval_token et, nu
if ((er = mult_term (context, et, v1)) != NO_ERROR)
return er;
- numb_init(v2);
+ numb_init (v2);
while ((op = eval_lex (&v2)) == PLUS || op == MINUS)
{
et = eval_lex (&v2);
@@ -595,13 +604,12 @@ add_term (m4 *context, eval_token et, nu
if ((er = mult_term (context, et, &v2)) != NO_ERROR)
return er;
- if (op == PLUS) {
- numb_plus(*v1,v2);
- } else {
- numb_minus(*v1,v2);
- }
+ if (op == PLUS)
+ numb_plus (*v1, v2);
+ else
+ numb_minus (*v1, v2);
}
- numb_fini(v2);
+ numb_fini (v2);
if (op == ERROR)
return UNKNOWN_INPUT;
@@ -619,7 +627,7 @@ mult_term (m4 *context, eval_token et, n
if ((er = exp_term (context, et, v1)) != NO_ERROR)
return er;
- numb_init(v2);
+ numb_init (v2);
while (op = eval_lex (&v2),
op == TIMES
|| op == DIVIDE
@@ -636,31 +644,28 @@ mult_term (m4 *context, eval_token et, n
switch (op)
{
case TIMES:
- numb_times(*v1,v2);
+ numb_times (*v1, v2);
break;
case DIVIDE:
- if (numb_zerop(v2))
+ if (numb_zerop (v2))
return DIVIDE_ZERO;
- else {
+ else
numb_divide(v1, &v2);
- }
break;
case RATIO:
- if (numb_zerop(v2))
+ if (numb_zerop (v2))
return DIVIDE_ZERO;
- else {
- numb_ratio(*v1,v2);
- }
+ else
+ numb_ratio (*v1, v2);
break;
case MODULO:
- if (numb_zerop(v2))
+ if (numb_zerop (v2))
return MODULO_ZERO;
- else {
- numb_modulo(context, v1, &v2);
- }
+ else
+ numb_modulo (context, v1, &v2);
break;
default:
@@ -668,7 +673,7 @@ mult_term (m4 *context, eval_token et, n
abort ();
}
}
- numb_fini(v2);
+ numb_fini (v2);
if (op == ERROR)
return UNKNOWN_INPUT;
@@ -685,9 +690,9 @@ exp_term (m4 *context, eval_token et, nu
if ((er = unary_term (context, et, v1)) != NO_ERROR)
return er;
- memcpy(&result, v1, sizeof(number));
+ memcpy (&result, v1, sizeof(number));
- numb_init(v2);
+ numb_init (v2);
while ((et = eval_lex (&v2)) == EXPONENT)
{
et = eval_lex (&v2);
@@ -697,9 +702,9 @@ exp_term (m4 *context, eval_token et, nu
if ((er = exp_term (context, et, &v2)) != NO_ERROR)
return er;
- numb_pow(v1, &v2);
+ numb_pow (v1, &v2);
}
- numb_fini(v2);
+ numb_fini (v2);
if (et == ERROR)
return UNKNOWN_INPUT;
@@ -713,17 +718,21 @@ unary_term (m4 *context, eval_token et,
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 (context, et2, v1)) != NO_ERROR)
+ if ((er = unary_term (context, et2, v1)) != NO_ERROR)
return er;
if (et == MINUS)
numb_negate(*v1);
+ else if (et == NOT)
+ numb_not (context, v1);
+ else if (et == LNOT)
+ numb_lnot (*v1);
}
else
if ((er = simple_term (context, et, v1)) != NO_ERROR)
@@ -760,6 +769,9 @@ simple_term (m4 *context, eval_token et,
case NUMBER:
break;
+ case BADOP:
+ return INVALID_OPERATOR;
+
default:
return SYNTAX_ERROR;
}
@@ -782,7 +794,7 @@ m4_evaluate (m4 *context, m4_obstack *ob
if (radix <= 1 || radix > 36)
{
m4_error (context, 0, 0, _("%s: radix out of range: %d"),
- M4ARG(0), radix);
+ M4ARG (0), radix);
return;
}
@@ -791,23 +803,29 @@ m4_evaluate (m4 *context, m4_obstack *ob
if (min <= 0)
{
- m4_error (context, 0, 0, _("%s: negative width: %d"), M4ARG(0), min);
+ m4_error (context, 0, 0, _("%s: negative width: %d"), M4ARG (0), min);
return;
}
numb_initialise ();
eval_init_lex (M4ARG (1));
- numb_init(val);
+ numb_init (val);
et = eval_lex (&val);
err = logical_or_term (context, 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)
{
case NO_ERROR:
+ numb_obstack (obs, val, radix, min);
break;
case MISSING_RIGHT:
@@ -829,6 +847,11 @@ m4_evaluate (m4 *context, m4_obstack *ob
M4ARG (1));
break;
+ case INVALID_OPERATOR:
+ 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));
@@ -844,9 +867,6 @@ m4_evaluate (m4 *context, m4_obstack *ob
abort ();
}
- if (err == NO_ERROR)
- numb_obstack (obs, val, radix, min);
-
numb_fini (val);
}
Index: modules/m4.c
===================================================================
RCS file: /sources/m4/m4/modules/m4.c,v
retrieving revision 1.98
diff -u -p -r1.98 m4.c
--- modules/m4.c 27 Dec 2006 14:14:27 -0000 1.98
+++ modules/m4.c 3 Jan 2007 14:39:53 -0000
@@ -1,5 +1,5 @@
/* GNU m4 -- A simple macro processor
- Copyright (C) 2000, 2002, 2003, 2004, 2006 Free Software Foundation, Inc.
+ Copyright (C) 2000, 2002, 2003, 2004, 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
@@ -1062,55 +1062,52 @@ M4BUILTIN_HANDLER (translit)
-/* The rest of this file contains the functions to evaluate integer
+/* The rest of this file contains the functions to evaluate integer
* expressions for the "eval" macro. `number' should be at least 32 bits.
*/
-#define int2numb(i) ((number)(i))
-#define numb2int(n) ((n))
+#define numb_set(ans, x) ((ans) = (x))
+#define numb_set_si(ans, si) (*(ans) = (number) (si))
-#define numb_set(ans,x) ((ans) = x)
-#define numb_set_si(ans,si) (*(ans) = int2numb(si))
+#define numb_ZERO ((number) 0)
+#define numb_ONE ((number) 1)
-#define numb_init(x) x=((number)0)
+#define numb_init(x) ((x) = numb_ZERO)
#define numb_fini(x)
-#define numb_decr(n) (n) -= 1
-
-#define numb_ZERO ((number)0)
-#define numb_ONE ((number)1)
+#define numb_decr(n) ((n) -= numb_ONE)
#define numb_zerop(x) ((x) == numb_ZERO)
#define numb_positivep(x) ((x) > numb_ZERO)
#define numb_negativep(x) ((x) < numb_ZERO)
-#define numb_eq(x,y) ((x) = ((x) == (y)))
-#define numb_ne(x,y) ((x) = ((x) != (y)))
-#define numb_lt(x,y) ((x) = ((x) < (y)))
-#define numb_le(x,y) ((x) = ((x) <= (y)))
-#define numb_gt(x,y) ((x) = ((x) > (y)))
-#define numb_ge(x,y) ((x) = ((x) >= (y)))
-
-#define numb_lnot(x) ((x) = (! (x)))
-#define numb_lior(x,y) ((x) = ((x) || (y)))
-#define numb_land(x,y) ((x) = ((x) && (y)))
-
-#define numb_not(c,x) (*(x) = int2numb(~numb2int(*(x))))
-#define numb_eor(c,x,y) (*(x) = int2numb(numb2int(*(x)) ^ numb2int(*(y))))
-#define numb_ior(c,x,y) (*(x) = int2numb(numb2int(*(x)) | numb2int(*(y))))
-#define numb_and(c,x,y) (*(x) = int2numb(numb2int(*(x)) & numb2int(*(y))))
-
-#define numb_plus(x,y) ((x) = ((x) + (y)))
-#define numb_minus(x,y) ((x) = ((x) - (y)))
-#define numb_negate(x) ((x) = (- (x)))
-
-#define numb_times(x,y) ((x) = ((x) * (y)))
-#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) = 1 / (x))
+#define numb_eq(x, y) ((x) = ((x) == (y)))
+#define numb_ne(x, y) ((x) = ((x) != (y)))
+#define numb_lt(x, y) ((x) = ((x) < (y)))
+#define numb_le(x, y) ((x) = ((x) <= (y)))
+#define numb_gt(x, y) ((x) = ((x) > (y)))
+#define numb_ge(x, y) ((x) = ((x) >= (y)))
+
+#define numb_lnot(x) ((x) = (! (x)))
+#define numb_lior(x, y) ((x) = ((x) || (y)))
+#define numb_land(x, y) ((x) = ((x) && (y)))
+
+#define numb_not(c, x) (*(x) = ~ *(x))
+#define numb_eor(c, x, y) (*(x) = *(x) ^ *(y))
+#define numb_ior(c, x, y) (*(x) = *(x) | *(y))
+#define numb_and(c, x, y) (*(x) = *(x) & *(y))
+
+#define numb_plus(x, y) ((x) = ((x) + (y)))
+#define numb_minus(x, y) ((x) = ((x) - (y)))
+#define numb_negate(x) ((x) = (- (x)))
+
+#define numb_times(x, y) ((x) = ((x) * (y)))
+#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)))
+#define numb_lshift(c, x, y) (*(x) = (*(x) << *(y)))
+#define numb_rshift(c, x, y) (*(x) = (*(x) >> *(y)))
/* The function ntoa () converts VALUE to a signed ascii representation in
Index: modules/mpeval.c
===================================================================
RCS file: /sources/m4/m4/modules/mpeval.c,v
retrieving revision 1.20
diff -u -p -r1.20 mpeval.c
--- modules/mpeval.c 26 Sep 2006 13:19:26 -0000 1.20
+++ modules/mpeval.c 3 Jan 2007 14:39:53 -0000
@@ -1,5 +1,5 @@
/* GNU m4 -- A simple macro processor
- Copyright (C) 2000, 2001, 2006 Free Software Foundation, Inc.
+ Copyright (C) 2000, 2001, 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
@@ -40,41 +40,57 @@
-#define numb_set(ans,i) mpq_set(ans,i)
-#define numb_set_si(ans,i) mpq_set_si(*(ans),(long)i,(unsigned long)1)
+#define numb_set(ans, i) mpq_set (ans, i)
+#define numb_set_si(ans, i) mpq_set_si (*(ans), (long) i, (unsigned long) 1)
-#define numb_init(x) mpq_init((x))
-#define numb_fini(x) mpq_clear((x))
+#define numb_init(x) mpq_init (x)
+#define numb_fini(x) mpq_clear (x)
-#define numb_zerop(x) (mpq_cmp(x,numb_ZERO) == 0)
-#define numb_positivep(x) (mpq_cmp(x,numb_ZERO) > 0)
-#define numb_negativep(x) (mpq_cmp(x,numb_ZERO) < 0)
-
-#define numb_eq(x,y) numb_set(x,mpq_cmp(x,y)==0? numb_ONE: numb_ZERO)
-#define numb_ne(x,y) numb_set(x,mpq_cmp(x,y)!=0? numb_ONE: numb_ZERO)
-#define numb_lt(x,y) numb_set(x,mpq_cmp(x,y)< 0? numb_ONE: numb_ZERO)
-#define numb_le(x,y) numb_set(x,mpq_cmp(x,y)<=0? numb_ONE: numb_ZERO)
-#define numb_gt(x,y) numb_set(x,mpq_cmp(x,y)> 0? numb_ONE: numb_ZERO)
-#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_land(x,y) numb_set(x,numb_zerop(x)? numb_ZERO: y)
-
-#define reduce1(f1,x) \
- { number T; mpq_init(T); f1(T,x); mpq_set(x,T); mpq_clear(T); }
-#define reduce2(f2,x,y) \
- { number T; mpq_init(T); f2(T,(x),(y)); mpq_set((x),T); mpq_clear(T); }
-
-#define numb_plus(x,y) reduce2(mpq_add,x,y)
-#define numb_minus(x,y) reduce2(mpq_sub,x,y)
-#define numb_negate(x) reduce1(mpq_neg,x)
-
-#define numb_times(x,y) reduce2(mpq_mul,x,y)
-#define numb_ratio(x,y) reduce2(mpq_div,x,y)
-#define numb_invert(x) reduce1(mpq_inv,x)
+#define numb_zerop(x) (mpq_cmp (x, numb_ZERO) == 0)
+#define numb_positivep(x) (mpq_cmp (x, numb_ZERO) > 0)
+#define numb_negativep(x) (mpq_cmp (x, numb_ZERO) < 0)
+
+#define numb_eq(x, y) numb_set (x, mpq_cmp (x, y) == 0 ? numb_ONE : numb_ZERO)
+#define numb_ne(x, y) numb_set (x, mpq_cmp (x, y) != 0 ? numb_ONE : numb_ZERO)
+#define numb_lt(x, y) numb_set (x, mpq_cmp (x, y) < 0 ? numb_ONE : numb_ZERO)
+#define numb_le(x, y) numb_set (x, mpq_cmp (x, y) <= 0 ? numb_ONE : numb_ZERO)
+#define numb_gt(x, y) numb_set (x, mpq_cmp (x, y) > 0 ? numb_ONE : numb_ZERO)
+#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_land(x, y) numb_set (x, numb_zerop (x) ? numb_ZERO : y)
+
+#define reduce1(f1, x) \
+ do \
+ { \
+ number T;
\
+ mpq_init (T); \
+ f1 (T, x); \
+ mpq_set (x, T); \
+ mpq_clear (T); \
+ } \
+ while (0)
+#define reduce2(f2,x,y)
\
+ do \
+ { \
+ number T;
\
+ mpq_init (T); \
+ f2 (T, (x), (y));
\
+ mpq_set ((x), T);
\
+ mpq_clear (T); \
+ } \
+ while (0)
+
+#define numb_plus(x, y) reduce2 (mpq_add, x, y)
+#define numb_minus(x, y) reduce2 (mpq_sub, x, y)
+#define numb_negate(x) reduce1 (mpq_neg, x)
+
+#define numb_times(x, y) reduce2 (mpq_mul, x, y)
+#define numb_ratio(x, y) reduce2 (mpq_div, x, y)
+#define numb_invert(x) reduce1 (mpq_inv, x)
-#define numb_decr(n) numb_minus(n,numb_ONE)
+#define numb_decr(n) numb_minus (n, numb_ONE)
/* Generate prototypes for each builtin handler function. */
#define BUILTIN(handler, macros, blind, side, min, max) M4BUILTIN(handler)
_______________________________________________
M4-patches mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/m4-patches