m4-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

format and eval


From: Eric Blake
Subject: format and eval
Date: Wed, 03 Jan 2007 07:44:24 -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

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  <address@hidden>

        * 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             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

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
address@hidden GNU extensions
address@hidden @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
address@hidden is the formatted string.
+Works much like the C function @code{printf}.  The first argument
address@hidden 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.
 
address@hidden ignore
address@hidden examples
 @example
-forloop(`i', 1, 10, `format(`%6d squared is %10d
+$ @kbd{m4 -I examples}
+include(`forloop.m4')
address@hidden
+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
address@hidden
 @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},
address@hidden, @samp{f}, @samp{F}, @samp{g}, and @samp{G}; it supports
-field widths and precisions, and the
address@hidden, @samp{f}, @samp{F}, @samp{g}, @samp{G}, and @samp{%}; it
+supports field widths and precisions, and the
 modifiers @samp{+}, @samp{-}, @address@hidden }}, @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
address@hidden 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
address@hidden and @code{decr}, which expand to the numerical value of
address@hidden, 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.
address@hidden deffn
 
 @example
-incr(4)
+incr(`4')
 @result{}5
-decr(7)
+decr(`7')
 @result{}6
+incr()
address@hidden:stdin:3: Warning: incr: empty string treated as 0
address@hidden
+decr()
address@hidden:stdin:4: Warning: decr: empty string treated as 0
address@hidden
 @end example
 
 The builtin macros @code{incr} and @code{decr} are recognized only when
 given arguments.
address@hidden 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
address@hidden {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}:
+
address@hidden {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.
+
+The macro @code{eval} is recognized only with parameters.
address@hidden deffn
 
 Expressions can contain the following operators, listed in order of
 decreasing precedence.
 
 @table @code
address@hidden -
-Unary minus
address@hidden ()
+Parenthesis
address@hidden +  -  ~  !
+Unary plus and minus, and bitwise and logical negation
 @item **
 Exponentiation
address@hidden *  /  %  :
-Multiplication, division, modulo and ratio
address@hidden *  /  %
+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 ^
@@ -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
address@hidden, 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
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.
+
+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.
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: eval: invalid operator: ++0
address@hidden
+eval(`1 = 1')
address@hidden:stdin:9: eval: invalid operator: 1 = 1
address@hidden
+eval(`0 |= 1')
address@hidden:stdin:10: eval: invalid operator: 0 |= 1
address@hidden
+eval(`2 || 1 / 0')
address@hidden
+eval(`0 || 1 / 0')
address@hidden:stdin:12: eval: divide by zero: 0 || 1 / 0
address@hidden
+eval(`0 && 1 % 0')
address@hidden
+eval(`2 && 1 % 0')
address@hidden:stdin:14: eval: modulo by zero: 2 && 1 % 0
address@hidden
address@hidden 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.
address@hidden 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
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{~},
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)

reply via email to

[Prev in Thread] Current Thread [Next in Thread]