Borrow an idea from Java: even though eval always operates on signed
inputs, the ability to do an unsigned right shift is valuable when
operating on bitmasks.

* src/eval.c (eval_token): Add URSHIFT.
(eval_lex): Tokenize it.
(parse_expr): Evaluate it.
* doc/m4.texi (Eval): Document this.
* NEWS: Likewise.
---
 NEWS        |  9 +++++----
 doc/m4.texi | 29 ++++++++++++++++++++---------
 src/eval.c  | 15 ++++++++++++++-
 3 files changed, 39 insertions(+), 14 deletions(-)

diff --git a/NEWS b/NEWS
index 97c4d9c0..0b499a20 100644
--- a/NEWS
+++ b/NEWS
@@ -126,10 +126,11 @@ GNU M4 NEWS - User visible changes.
    context of a macro name, rather than acting on the empty string.  This
    was already done for `define', `pushdef', `builtin', and `indir'.

-** Enhance the `eval' builtin to understand the `?:' operator, and
-   downgrade a failed parse due to an unknown operator from an error to a
-   warning.  Further, the builtin now refuses to recognize `=' as a
-   synonym for `==' (this had emitted a warning since 1.4.8b).
+** Enhance the `eval' builtin to understand the `?:' and `>>>' operators,
+   and downgrade a failed parse due to an unknown operator from an error to
+   a warning (the same as for all other syntax errors).  Further, the
+   builtin now refuses to recognize `=' as a synonym for `==' (this had
+   emitted a warning since 1.4.8b).

 ** A number of portability improvements inherited from gnulib.

diff --git a/doc/m4.texi b/doc/m4.texi
index 65c55209..97da55dd 100644
--- a/doc/m4.texi
+++ b/doc/m4.texi
@@ -7092,7 +7092,7 @@ Eval
 Multiplication, division, and modulo
 @item +  -
 Addition and subtraction
-@item <<  >>
+@item <<  >>  >>>
 Shift left or right
 @item >  >=  <  <=
 Relational operators
@@ -7193,12 +7193,19 @@ Eval
 @end example

 @cindex GNU extensions
-As a 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.  Also, the ternary C operator
-@samp{?:} is supported, including the GNU extension of reusing the
-non-zero value of the left side if the middle term is empty.
+As a GNU extension, several additional operators are supported.  Since
+M4 1.4, the operator @samp{**} performs integral exponentiation.  This
+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.  Additionally, M4 1.6 introduced the @samp{>>>} and
+@samp{?:} operators.  The operator @samp{>>>} has the same precedence as
+@samp{>>}, but where @samp{>>} extends the sign bit into the most
+significant bits, @samp{>>>} performs an unsigned shift that always
+supplies zero bits on the left.  Finally, the right-associative ternary
+C operator @samp{?:} is supported, including the GNU extension of
+reusing the non-zero value of the left side if the middle term is empty.
+This operator evaluates the first term, then if the first is non-zero
+evaluates the second term, otherwise it evaluates the third term.

 @example
 eval(`2 ** 3 ** 2')
@@ -7215,6 +7222,10 @@ Eval
 eval(`4 ** -2')
 @error{}m4:stdin:6: warning: eval: negative exponent: '4 ** -2'
 @result{}
+eval(`-1 >> 1')
+@result{}-1
+eval(`-1 >>> 1')
+@result{}2147483647
 eval(`0 ? 2 : 3')
 @result{}3
 eval(`1 ? 2 : 1/0')
@@ -7226,12 +7237,12 @@ Eval
 eval(`1 ? 2 ? 3 : 4 : 5')
 @result{}3
 eval(`1/0 ? 2 : 3')
-@error{}m4:stdin:12: warning: eval: divide by zero: '1/0 ? 2 : 3'
+@error{}m4:stdin:14: warning: eval: divide by zero: '1/0 ? 2 : 3'
 @result{}
 eval(`2 ?: 3')
 @result{}2
 eval(`1 ? 2-=3 : 4')
-@error{}m4:stdin:14: warning: eval: invalid operator: '1 ? 2-=3 : 4'
+@error{}m4:stdin:16: warning: eval: invalid operator: '1 ? 2-=3 : 4'
 @result{}
 @end example

diff --git a/src/eval.c b/src/eval.c
index 69cd5e41..e3bb8b2c 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -55,6 +55,7 @@ typedef enum eval_token
   LSEQ,
   LSHIFT = 90,
   RSHIFT,
+  URSHIFT,
   /* precedence given for binary op; PLUS and MINUS also serve as a unary op */
   PLUS = 100,
   MINUS,
@@ -255,7 +256,14 @@ eval_lex (int32_t *val)
         }
       else if (*eval_text == '>')
         {
-          if (*++eval_text == '=')
+          eval_text++;
+          if (*eval_text == '>')
+            {
+              if (*++eval_text == '=')
+                return BADOP;
+              return URSHIFT;
+            }
+          else if (*eval_text == '=')
             return BADOP;
           return RSHIFT;
         }
@@ -490,6 +498,11 @@ parse_expr (int32_t *v1, eval_error er, unsigned min_prec)
           u1 >>= (uint32_t) (v2 & 0x1f);
           *v1 = *v1 < 0 ? ~u1 : u1;
           break;
+        case URSHIFT:
+          u1 = *v1;
+          u1 >>= (uint32_t) (v2 & 0x1f);
+          *v1 = u1;
+          break;

         case GT:
           *v1 = *v1 > v2;
-- 
2.49.0


_______________________________________________
M4-patches mailing list
M4-patches@gnu.org
https://lists.gnu.org/mailman/listinfo/m4-patches

Reply via email to