You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by he...@apache.org on 2022/02/15 15:20:58 UTC

[commons-jexl] 01/01: JEXL-360: added operators, syntax, basic tests

This is an automated email from the ASF dual-hosted git repository.

henrib pushed a commit to branch JEXL-360
in repository https://gitbox.apache.org/repos/asf/commons-jexl.git

commit 9f768d75c5967e331d2c415e792ac15ef959655d
Author: henrib <he...@apache.org>
AuthorDate: Tue Feb 15 16:20:42 2022 +0100

    JEXL-360: added operators, syntax, basic tests
---
 .../org/apache/commons/jexl3/JexlArithmetic.java   |  39 ++++++
 .../org/apache/commons/jexl3/JexlOperator.java     |  45 +++++++
 .../apache/commons/jexl3/internal/Debugger.java    | 109 +++++------------
 .../apache/commons/jexl3/internal/Interpreter.java | 131 ++++++++-------------
 .../apache/commons/jexl3/internal/Operators.java   |   6 +
 .../commons/jexl3/internal/ScriptVisitor.java      | 106 +++++------------
 .../org/apache/commons/jexl3/parser/Parser.jjt     |  45 +++++--
 .../apache/commons/jexl3/parser/ParserVisitor.java |  12 ++
 .../org/apache/commons/jexl3/ArithmeticTest.java   |  30 +++++
 9 files changed, 280 insertions(+), 243 deletions(-)

diff --git a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
index 9c3033a..b34d367 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
@@ -1309,6 +1309,45 @@ public class JexlArithmetic {
     }
 
     /**
+     * Shifts a bit pattern to the right.
+     *
+     * @param left  left argument
+     * @param right  right argument
+     * @return left &lt;&lt; right.
+     */
+    public Object shiftLeft(Object left, Object right) {
+        final long l = toLong(left);
+        final int r = toInteger(right);
+        return l << r;
+    }
+
+    /**
+     * Shifts a bit pattern to the right.
+     *
+     * @param left  left argument
+     * @param right  right argument
+     * @return left &gt;&gt; right.
+     */
+    public Object shiftRight(Object left, Object right) {
+        final long l = toLong(left);
+        final long r = toInteger(right);
+        return l >> r;
+    }
+
+    /**
+     * Shifts a bit pattern to the right unsigned.
+     *
+     * @param left  left argument
+     * @param right  right argument
+     * @return left &gt;&gt;&gt; right.
+     */
+    public Object shiftRightUnsigned(Object left, Object right) {
+        final long l = toLong(left);
+        final long r = toInteger(right);
+        return l >>> r;
+    }
+
+    /**
      * Performs a comparison.
      *
      * @param left     the left operand
diff --git a/src/main/java/org/apache/commons/jexl3/JexlOperator.java b/src/main/java/org/apache/commons/jexl3/JexlOperator.java
index 6f21e41..daa68f3 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlOperator.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlOperator.java
@@ -105,6 +105,30 @@ public enum JexlOperator {
     XOR("^", "xor", 2),
 
     /**
+     * Bit-pattern right-shift operator.
+     * <br><strong>Syntax:</strong> <code>x >> y</code>
+     * <br><strong>Method:</strong> <code>T rightShift(L x, R y);</code>.
+     * @see JexlArithmetic#shiftRight(Object, Object)
+     */
+    SHIFTRIGHT(">>", "shiftRight", 2),
+
+    /**
+     * Bit-pattern right-shift unsigned operator.
+     * <br><strong>Syntax:</strong> <code>x >>> y</code>
+     * <br><strong>Method:</strong> <code>T rightShiftUnsigned(L x, R y);</code>.
+     * @see JexlArithmetic#shiftRightUnsigned(Object, Object)
+     */
+    SHIFTRIGHTU(">>>", "shiftRightUnsigned", 2),
+
+    /**
+     * Bit-pattern left-shift operator.
+     * <br><strong>Syntax:</strong> <code>x << y</code>
+     * <br><strong>Method:</strong> <code>T leftShift(L x, R y);</code>.
+     * @see JexlArithmetic#shiftLeft(Object, Object)
+     */
+    SHIFTLEFT("<<", "shiftLeft", 2),
+
+    /**
      * Equals operator.
      * <br><strong>Syntax:</strong> <code>x == y</code>
      * <br><strong>Method:</strong> <code>boolean equals(L x, R y);</code>.
@@ -273,6 +297,27 @@ public enum JexlOperator {
     SELF_XOR("^=", "selfXor", XOR),
 
     /**
+     * Self-right-shift operator.
+     * <br><strong>Syntax:</strong> <code>x >>= y</code>
+     * <br><strong>Method:</strong> <code>T selfShiftRight(L x, R y);</code>.
+     */
+    SELF_SHIFTRIGHT(">>", "selfShiftRight", SHIFTRIGHT),
+
+    /**
+     * Self-right-shift unsigned operator.
+     * <br><strong>Syntax:</strong> <code>x >>> y</code>
+     * <br><strong>Method:</strong> <code>T selfShiftRightUnsigned(L x, R y);</code>.
+     */
+    SELF_SHIFTRIGHTU(">>>=", "selfShiftRightUnsigned", SHIFTRIGHTU),
+
+    /**
+     * Self-left-shift operator.
+     * <br><strong>Syntax:</strong> <code>x << y</code>
+     * <br><strong>Method:</strong> <code>T selfShiftLeft(L x, R y);</code>.
+     */
+    SELF_SHIFTLEFT("<<=", "selfShiftLeft", SHIFTLEFT),
+
+    /**
      * Marker for side effect.
      * <br>Returns this from 'self*' overload method to let the engine know the side effect has been performed and
      * there is no need to assign the result.
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Debugger.java b/src/main/java/org/apache/commons/jexl3/internal/Debugger.java
index 0f5e577..d3f52a2 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Debugger.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Debugger.java
@@ -20,86 +20,9 @@ package org.apache.commons.jexl3.internal;
 import org.apache.commons.jexl3.JexlExpression;
 import org.apache.commons.jexl3.JexlInfo;
 import org.apache.commons.jexl3.JexlScript;
-import org.apache.commons.jexl3.parser.ASTAddNode;
-import org.apache.commons.jexl3.parser.ASTAndNode;
-import org.apache.commons.jexl3.parser.ASTArguments;
-import org.apache.commons.jexl3.parser.ASTArrayAccess;
-import org.apache.commons.jexl3.parser.ASTArrayLiteral;
-import org.apache.commons.jexl3.parser.ASTAssignment;
-import org.apache.commons.jexl3.parser.ASTBitwiseAndNode;
-import org.apache.commons.jexl3.parser.ASTBitwiseComplNode;
-import org.apache.commons.jexl3.parser.ASTBitwiseOrNode;
-import org.apache.commons.jexl3.parser.ASTBitwiseXorNode;
-import org.apache.commons.jexl3.parser.ASTBlock;
-import org.apache.commons.jexl3.parser.ASTBreak;
-import org.apache.commons.jexl3.parser.ASTConstructorNode;
-import org.apache.commons.jexl3.parser.ASTContinue;
-import org.apache.commons.jexl3.parser.ASTDivNode;
-import org.apache.commons.jexl3.parser.ASTDoWhileStatement;
-import org.apache.commons.jexl3.parser.ASTEQNode;
-import org.apache.commons.jexl3.parser.ASTERNode;
-import org.apache.commons.jexl3.parser.ASTEWNode;
-import org.apache.commons.jexl3.parser.ASTEmptyFunction;
-import org.apache.commons.jexl3.parser.ASTExtendedLiteral;
-import org.apache.commons.jexl3.parser.ASTFalseNode;
-import org.apache.commons.jexl3.parser.ASTForeachStatement;
-import org.apache.commons.jexl3.parser.ASTFunctionNode;
-import org.apache.commons.jexl3.parser.ASTGENode;
-import org.apache.commons.jexl3.parser.ASTGTNode;
-import org.apache.commons.jexl3.parser.ASTIdentifier;
-import org.apache.commons.jexl3.parser.ASTIdentifierAccess;
-import org.apache.commons.jexl3.parser.ASTIfStatement;
-import org.apache.commons.jexl3.parser.ASTJexlLambda;
-import org.apache.commons.jexl3.parser.ASTJexlScript;
-import org.apache.commons.jexl3.parser.ASTJxltLiteral;
-import org.apache.commons.jexl3.parser.ASTLENode;
-import org.apache.commons.jexl3.parser.ASTLTNode;
-import org.apache.commons.jexl3.parser.ASTMapEntry;
-import org.apache.commons.jexl3.parser.ASTMapLiteral;
-import org.apache.commons.jexl3.parser.ASTMethodNode;
-import org.apache.commons.jexl3.parser.ASTModNode;
-import org.apache.commons.jexl3.parser.ASTMulNode;
-import org.apache.commons.jexl3.parser.ASTNENode;
-import org.apache.commons.jexl3.parser.ASTNEWNode;
-import org.apache.commons.jexl3.parser.ASTNRNode;
-import org.apache.commons.jexl3.parser.ASTNSWNode;
-import org.apache.commons.jexl3.parser.ASTNotNode;
-import org.apache.commons.jexl3.parser.ASTNullLiteral;
-import org.apache.commons.jexl3.parser.ASTNumberLiteral;
-import org.apache.commons.jexl3.parser.ASTOrNode;
-import org.apache.commons.jexl3.parser.ASTRangeNode;
-import org.apache.commons.jexl3.parser.ASTReference;
-import org.apache.commons.jexl3.parser.ASTReferenceExpression;
-import org.apache.commons.jexl3.parser.ASTRegexLiteral;
-import org.apache.commons.jexl3.parser.ASTReturnStatement;
-import org.apache.commons.jexl3.parser.ASTSWNode;
-import org.apache.commons.jexl3.parser.ASTSetAddNode;
-import org.apache.commons.jexl3.parser.ASTSetAndNode;
-import org.apache.commons.jexl3.parser.ASTSetDivNode;
-import org.apache.commons.jexl3.parser.ASTSetLiteral;
-import org.apache.commons.jexl3.parser.ASTSetModNode;
-import org.apache.commons.jexl3.parser.ASTSetMultNode;
-import org.apache.commons.jexl3.parser.ASTSetOrNode;
-import org.apache.commons.jexl3.parser.ASTSetSubNode;
-import org.apache.commons.jexl3.parser.ASTSetXorNode;
-import org.apache.commons.jexl3.parser.ASTSizeFunction;
-import org.apache.commons.jexl3.parser.ASTStringLiteral;
-import org.apache.commons.jexl3.parser.ASTSubNode;
-import org.apache.commons.jexl3.parser.ASTTernaryNode;
-import org.apache.commons.jexl3.parser.ASTTrueNode;
-import org.apache.commons.jexl3.parser.ASTUnaryMinusNode;
-import org.apache.commons.jexl3.parser.ASTVar;
-import org.apache.commons.jexl3.parser.ASTWhileStatement;
-import org.apache.commons.jexl3.parser.ASTAnnotatedStatement;
-import org.apache.commons.jexl3.parser.ASTAnnotation;
-import org.apache.commons.jexl3.parser.ASTNullpNode;
-
-import org.apache.commons.jexl3.parser.JexlNode;
-import org.apache.commons.jexl3.parser.ParserVisitor;
+import org.apache.commons.jexl3.parser.*;
 
 import java.util.regex.Pattern;
-import org.apache.commons.jexl3.parser.ASTUnaryPlusNode;
-import org.apache.commons.jexl3.parser.StringParser;
 
 /**
  * Helps pinpoint the cause of problems in expressions that fail during evaluation.
@@ -492,6 +415,21 @@ public class Debugger extends ParserVisitor implements JexlInfo.Detail {
     }
 
     @Override
+    protected Object visit(final ASTShiftRightNode node, final Object data) {
+        return infixChildren(node, " >> ", false, data);
+    }
+
+    @Override
+    protected Object visit(final ASTShiftRightUnsignedNode node, final Object data) {
+        return infixChildren(node, " >>> ", false, data);
+    }
+
+    @Override
+    protected Object visit(final ASTShiftLeftNode node, final Object data) {
+        return infixChildren(node, " << ", false, data);
+    }
+
+    @Override
     protected Object visit(final ASTBitwiseComplNode node, final Object data) {
         return prefixChild(node, "~", data);
     }
@@ -1059,6 +997,21 @@ public class Debugger extends ParserVisitor implements JexlInfo.Detail {
     }
 
     @Override
+    protected Object visit(final ASTSetShiftRightNode node, final Object data) {
+        return infixChildren(node, " >>= ", false, data);
+    }
+
+    @Override
+    protected Object visit(final ASTSetShiftRightUnsignedNode node, final Object data) {
+        return infixChildren(node, " >>>= ", false, data);
+    }
+
+    @Override
+    protected Object visit(final ASTSetShiftLeftNode node, final Object data) {
+        return infixChildren(node, " <<= ", false, data);
+    }
+
+    @Override
     protected Object visit(final ASTAnnotation node, final Object data) {
         final int num = node.jjtGetNumChildren();
         builder.append('@');
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
index 3590d9c..7141b0e 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -33,82 +33,7 @@ import org.apache.commons.jexl3.JxltEngine;
 import org.apache.commons.jexl3.introspection.JexlMethod;
 import org.apache.commons.jexl3.introspection.JexlPropertyGet;
 
-import org.apache.commons.jexl3.parser.ASTAddNode;
-import org.apache.commons.jexl3.parser.ASTAndNode;
-import org.apache.commons.jexl3.parser.ASTAnnotatedStatement;
-import org.apache.commons.jexl3.parser.ASTAnnotation;
-import org.apache.commons.jexl3.parser.ASTArguments;
-import org.apache.commons.jexl3.parser.ASTArrayAccess;
-import org.apache.commons.jexl3.parser.ASTArrayLiteral;
-import org.apache.commons.jexl3.parser.ASTAssignment;
-import org.apache.commons.jexl3.parser.ASTBitwiseAndNode;
-import org.apache.commons.jexl3.parser.ASTBitwiseComplNode;
-import org.apache.commons.jexl3.parser.ASTBitwiseOrNode;
-import org.apache.commons.jexl3.parser.ASTBitwiseXorNode;
-import org.apache.commons.jexl3.parser.ASTBlock;
-import org.apache.commons.jexl3.parser.ASTBreak;
-import org.apache.commons.jexl3.parser.ASTConstructorNode;
-import org.apache.commons.jexl3.parser.ASTContinue;
-import org.apache.commons.jexl3.parser.ASTDivNode;
-import org.apache.commons.jexl3.parser.ASTDoWhileStatement;
-import org.apache.commons.jexl3.parser.ASTEQNode;
-import org.apache.commons.jexl3.parser.ASTERNode;
-import org.apache.commons.jexl3.parser.ASTEWNode;
-import org.apache.commons.jexl3.parser.ASTEmptyFunction;
-import org.apache.commons.jexl3.parser.ASTExtendedLiteral;
-import org.apache.commons.jexl3.parser.ASTFalseNode;
-import org.apache.commons.jexl3.parser.ASTForeachStatement;
-import org.apache.commons.jexl3.parser.ASTFunctionNode;
-import org.apache.commons.jexl3.parser.ASTGENode;
-import org.apache.commons.jexl3.parser.ASTGTNode;
-import org.apache.commons.jexl3.parser.ASTIdentifier;
-import org.apache.commons.jexl3.parser.ASTIdentifierAccess;
-import org.apache.commons.jexl3.parser.ASTIdentifierAccessJxlt;
-import org.apache.commons.jexl3.parser.ASTIfStatement;
-import org.apache.commons.jexl3.parser.ASTJexlLambda;
-import org.apache.commons.jexl3.parser.ASTJexlScript;
-import org.apache.commons.jexl3.parser.ASTJxltLiteral;
-import org.apache.commons.jexl3.parser.ASTLENode;
-import org.apache.commons.jexl3.parser.ASTLTNode;
-import org.apache.commons.jexl3.parser.ASTMapEntry;
-import org.apache.commons.jexl3.parser.ASTMapLiteral;
-import org.apache.commons.jexl3.parser.ASTMethodNode;
-import org.apache.commons.jexl3.parser.ASTModNode;
-import org.apache.commons.jexl3.parser.ASTMulNode;
-import org.apache.commons.jexl3.parser.ASTNENode;
-import org.apache.commons.jexl3.parser.ASTNEWNode;
-import org.apache.commons.jexl3.parser.ASTNRNode;
-import org.apache.commons.jexl3.parser.ASTNSWNode;
-import org.apache.commons.jexl3.parser.ASTNotNode;
-import org.apache.commons.jexl3.parser.ASTNullLiteral;
-import org.apache.commons.jexl3.parser.ASTNullpNode;
-import org.apache.commons.jexl3.parser.ASTNumberLiteral;
-import org.apache.commons.jexl3.parser.ASTOrNode;
-import org.apache.commons.jexl3.parser.ASTRangeNode;
-import org.apache.commons.jexl3.parser.ASTReference;
-import org.apache.commons.jexl3.parser.ASTReferenceExpression;
-import org.apache.commons.jexl3.parser.ASTRegexLiteral;
-import org.apache.commons.jexl3.parser.ASTReturnStatement;
-import org.apache.commons.jexl3.parser.ASTSWNode;
-import org.apache.commons.jexl3.parser.ASTSetAddNode;
-import org.apache.commons.jexl3.parser.ASTSetAndNode;
-import org.apache.commons.jexl3.parser.ASTSetDivNode;
-import org.apache.commons.jexl3.parser.ASTSetLiteral;
-import org.apache.commons.jexl3.parser.ASTSetModNode;
-import org.apache.commons.jexl3.parser.ASTSetMultNode;
-import org.apache.commons.jexl3.parser.ASTSetOrNode;
-import org.apache.commons.jexl3.parser.ASTSetSubNode;
-import org.apache.commons.jexl3.parser.ASTSetXorNode;
-import org.apache.commons.jexl3.parser.ASTSizeFunction;
-import org.apache.commons.jexl3.parser.ASTStringLiteral;
-import org.apache.commons.jexl3.parser.ASTSubNode;
-import org.apache.commons.jexl3.parser.ASTTernaryNode;
-import org.apache.commons.jexl3.parser.ASTTrueNode;
-import org.apache.commons.jexl3.parser.ASTUnaryMinusNode;
-import org.apache.commons.jexl3.parser.ASTUnaryPlusNode;
-import org.apache.commons.jexl3.parser.ASTVar;
-import org.apache.commons.jexl3.parser.ASTWhileStatement;
-import org.apache.commons.jexl3.parser.JexlNode;
+import org.apache.commons.jexl3.parser.*;
 
 /**
  * An interpreter of JEXL syntax.
@@ -338,9 +263,6 @@ public class Interpreter extends InterpreterBase {
     protected Object visit(final ASTBitwiseOrNode node, final Object data) {
         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
-        if (arithmetic.isStrict(JexlOperator.OR) && left == null || right == null) {
-            // boum
-        }
         try {
             final Object result = operators.tryOverload(node, JexlOperator.OR, left, right);
             return result != JexlEngine.TRY_FAILED ? result : arithmetic.or(left, right);
@@ -362,6 +284,42 @@ public class Interpreter extends InterpreterBase {
     }
 
     @Override
+    protected Object visit(final ASTShiftLeftNode node, final Object data) {
+        final Object left = node.jjtGetChild(0).jjtAccept(this, data);
+        final Object right = node.jjtGetChild(1).jjtAccept(this, data);
+        try {
+            final Object result = operators.tryOverload(node, JexlOperator.SHIFTLEFT, left, right);
+            return result != JexlEngine.TRY_FAILED ? result : arithmetic.shiftLeft(left, right);
+        } catch (final ArithmeticException xrt) {
+            throw new JexlException(findNullOperand(node, left, right), "<< error", xrt);
+        }
+    }
+
+    @Override
+    protected Object visit(final ASTShiftRightNode node, final Object data) {
+        final Object left = node.jjtGetChild(0).jjtAccept(this, data);
+        final Object right = node.jjtGetChild(1).jjtAccept(this, data);
+        try {
+            final Object result = operators.tryOverload(node, JexlOperator.SHIFTRIGHT, left, right);
+            return result != JexlEngine.TRY_FAILED ? result : arithmetic.shiftRight(left, right);
+        } catch (final ArithmeticException xrt) {
+            throw new JexlException(findNullOperand(node, left, right), ">> error", xrt);
+        }
+    }
+
+    @Override
+    protected Object visit(final ASTShiftRightUnsignedNode node, final Object data) {
+        final Object left = node.jjtGetChild(0).jjtAccept(this, data);
+        final Object right = node.jjtGetChild(1).jjtAccept(this, data);
+        try {
+            final Object result = operators.tryOverload(node, JexlOperator.SHIFTRIGHTU, left, right);
+            return result != JexlEngine.TRY_FAILED ? result : arithmetic.shiftRightUnsigned(left, right);
+        } catch (final ArithmeticException xrt) {
+            throw new JexlException(findNullOperand(node, left, right), ">> error", xrt);
+        }
+    }
+
+    @Override
     protected Object visit(final ASTEQNode node, final Object data) {
         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
         final Object right = node.jjtGetChild(1).jjtAccept(this, data);
@@ -1283,6 +1241,21 @@ public class Interpreter extends InterpreterBase {
         return executeAssign(node, JexlOperator.SELF_XOR, data);
     }
 
+    @Override
+    protected Object visit(final ASTSetShiftLeftNode node, final Object data) {
+        return executeAssign(node, JexlOperator.SELF_SHIFTLEFT, data);
+    }
+
+    @Override
+    protected Object visit(final ASTSetShiftRightNode node, final Object data) {
+        return executeAssign(node, JexlOperator.SELF_SHIFTRIGHT, data);
+    }
+
+    @Override
+    protected Object visit(final ASTSetShiftRightUnsignedNode node, final Object data) {
+        return executeAssign(node, JexlOperator.SELF_SHIFTRIGHTU, data);
+    }
+
     /**
      * Executes an assignment with an optional side-effect operator.
      * @param node     the node
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Operators.java b/src/main/java/org/apache/commons/jexl3/internal/Operators.java
index a72dc1d..e26d1ea 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Operators.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Operators.java
@@ -187,6 +187,12 @@ public class Operators {
                     return arithmetic.or(args[0], args[1]);
                 case SELF_XOR:
                     return arithmetic.xor(args[0], args[1]);
+                case SELF_SHIFTLEFT:
+                    return arithmetic.shiftLeft(args[0], args[1]);
+                case SELF_SHIFTRIGHT:
+                    return arithmetic.shiftRight(args[0], args[1]);
+                case SELF_SHIFTRIGHTU:
+                    return arithmetic.shiftRightUnsigned(args[0], args[1]);
                 default:
                     // unexpected, new operator added?
                     throw new UnsupportedOperationException(operator.getOperatorSymbol());
diff --git a/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java b/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java
index 19afd27..bdcd51e 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java
@@ -18,81 +18,7 @@ package org.apache.commons.jexl3.internal;
 
 import org.apache.commons.jexl3.JexlExpression;
 import org.apache.commons.jexl3.JexlScript;
-import org.apache.commons.jexl3.parser.ASTAddNode;
-import org.apache.commons.jexl3.parser.ASTAndNode;
-import org.apache.commons.jexl3.parser.ASTAnnotatedStatement;
-import org.apache.commons.jexl3.parser.ASTAnnotation;
-import org.apache.commons.jexl3.parser.ASTArguments;
-import org.apache.commons.jexl3.parser.ASTArrayAccess;
-import org.apache.commons.jexl3.parser.ASTArrayLiteral;
-import org.apache.commons.jexl3.parser.ASTAssignment;
-import org.apache.commons.jexl3.parser.ASTBitwiseAndNode;
-import org.apache.commons.jexl3.parser.ASTBitwiseComplNode;
-import org.apache.commons.jexl3.parser.ASTBitwiseOrNode;
-import org.apache.commons.jexl3.parser.ASTBitwiseXorNode;
-import org.apache.commons.jexl3.parser.ASTBlock;
-import org.apache.commons.jexl3.parser.ASTBreak;
-import org.apache.commons.jexl3.parser.ASTConstructorNode;
-import org.apache.commons.jexl3.parser.ASTContinue;
-import org.apache.commons.jexl3.parser.ASTDivNode;
-import org.apache.commons.jexl3.parser.ASTDoWhileStatement;
-import org.apache.commons.jexl3.parser.ASTEQNode;
-import org.apache.commons.jexl3.parser.ASTERNode;
-import org.apache.commons.jexl3.parser.ASTEWNode;
-import org.apache.commons.jexl3.parser.ASTEmptyFunction;
-import org.apache.commons.jexl3.parser.ASTExtendedLiteral;
-import org.apache.commons.jexl3.parser.ASTFalseNode;
-import org.apache.commons.jexl3.parser.ASTForeachStatement;
-import org.apache.commons.jexl3.parser.ASTFunctionNode;
-import org.apache.commons.jexl3.parser.ASTGENode;
-import org.apache.commons.jexl3.parser.ASTGTNode;
-import org.apache.commons.jexl3.parser.ASTIdentifier;
-import org.apache.commons.jexl3.parser.ASTIdentifierAccess;
-import org.apache.commons.jexl3.parser.ASTIfStatement;
-import org.apache.commons.jexl3.parser.ASTJexlScript;
-import org.apache.commons.jexl3.parser.ASTJxltLiteral;
-import org.apache.commons.jexl3.parser.ASTLENode;
-import org.apache.commons.jexl3.parser.ASTLTNode;
-import org.apache.commons.jexl3.parser.ASTMapEntry;
-import org.apache.commons.jexl3.parser.ASTMapLiteral;
-import org.apache.commons.jexl3.parser.ASTMethodNode;
-import org.apache.commons.jexl3.parser.ASTModNode;
-import org.apache.commons.jexl3.parser.ASTMulNode;
-import org.apache.commons.jexl3.parser.ASTNENode;
-import org.apache.commons.jexl3.parser.ASTNEWNode;
-import org.apache.commons.jexl3.parser.ASTNRNode;
-import org.apache.commons.jexl3.parser.ASTNSWNode;
-import org.apache.commons.jexl3.parser.ASTNotNode;
-import org.apache.commons.jexl3.parser.ASTNullLiteral;
-import org.apache.commons.jexl3.parser.ASTNullpNode;
-import org.apache.commons.jexl3.parser.ASTNumberLiteral;
-import org.apache.commons.jexl3.parser.ASTOrNode;
-import org.apache.commons.jexl3.parser.ASTRangeNode;
-import org.apache.commons.jexl3.parser.ASTReference;
-import org.apache.commons.jexl3.parser.ASTReferenceExpression;
-import org.apache.commons.jexl3.parser.ASTRegexLiteral;
-import org.apache.commons.jexl3.parser.ASTReturnStatement;
-import org.apache.commons.jexl3.parser.ASTSWNode;
-import org.apache.commons.jexl3.parser.ASTSetAddNode;
-import org.apache.commons.jexl3.parser.ASTSetAndNode;
-import org.apache.commons.jexl3.parser.ASTSetDivNode;
-import org.apache.commons.jexl3.parser.ASTSetLiteral;
-import org.apache.commons.jexl3.parser.ASTSetModNode;
-import org.apache.commons.jexl3.parser.ASTSetMultNode;
-import org.apache.commons.jexl3.parser.ASTSetOrNode;
-import org.apache.commons.jexl3.parser.ASTSetSubNode;
-import org.apache.commons.jexl3.parser.ASTSetXorNode;
-import org.apache.commons.jexl3.parser.ASTSizeFunction;
-import org.apache.commons.jexl3.parser.ASTStringLiteral;
-import org.apache.commons.jexl3.parser.ASTSubNode;
-import org.apache.commons.jexl3.parser.ASTTernaryNode;
-import org.apache.commons.jexl3.parser.ASTTrueNode;
-import org.apache.commons.jexl3.parser.ASTUnaryMinusNode;
-import org.apache.commons.jexl3.parser.ASTUnaryPlusNode;
-import org.apache.commons.jexl3.parser.ASTVar;
-import org.apache.commons.jexl3.parser.ASTWhileStatement;
-import org.apache.commons.jexl3.parser.JexlNode;
-import org.apache.commons.jexl3.parser.ParserVisitor;
+import org.apache.commons.jexl3.parser.*;
 
 /**
  * Fully abstract to avoid public interface exposition.
@@ -231,6 +157,21 @@ public class ScriptVisitor extends ParserVisitor {
     }
 
     @Override
+    protected Object visit(final ASTShiftLeftNode node, final Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(final ASTShiftRightUnsignedNode node, final Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(final ASTShiftRightNode node, final Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
     protected Object visit(final ASTEQNode node, final Object data) {
         return visitNode(node, data);
     }
@@ -486,6 +427,21 @@ public class ScriptVisitor extends ParserVisitor {
     }
 
     @Override
+    protected Object visit(final ASTSetShiftLeftNode node, final Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(final ASTSetShiftRightUnsignedNode node, final Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
+    protected Object visit(final ASTSetShiftRightNode node, final Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
     protected Object visit(final ASTJxltLiteral node, final Object data) {
         return visitNode(node, data);
     }
diff --git a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
index 46fb92c..d167b36 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
+++ b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
@@ -195,6 +195,9 @@ TOKEN_MGR_DECLS : {
     | < and_assign : "&=" >
     | < or_assign : "|=" >
     | < xor_assign : "^=" >
+    | < lshift_assign : "<<=" >
+    | < rshiftu_assign : ">>>=" >
+    | < rshift_assign : ">>=" >
 
     | < assign : "=" >
     | < plus : "+" >
@@ -209,6 +212,9 @@ TOKEN_MGR_DECLS : {
     | < and : "&" >
     | < or : "|" >
     | < xor : "^" >
+    | < lshift : "<<" >
+    | < rshiftu : ">>>" >
+    | < rshift : ">>" >
 
     | < tilda : "~" >
     | < range : ".." >
@@ -505,6 +511,12 @@ void AssignmentExpression() #void : {}
   |
     <minus_assign>  Expression() #SetSubNode(2)
   |
+    <lshift_assign>  Expression() #SetShiftLeftNode(2)
+  |
+    <rshift_assign>  Expression() #SetShiftRightNode(2)
+  |
+    <rshiftu_assign>  Expression() #SetShiftRightUnsignedNode(2)
+  |
     <assign> Expression() #Assignment(2)
   ) )*
 }
@@ -569,33 +581,44 @@ void EqualityExpression() #void : {}
 
 void RelationalExpression() #void : {}
 {
-  AdditiveExpression()
+  ShiftExpression()
   (
-    (<lt> |<LT>)  AdditiveExpression() #LTNode(2)
+    (<lt> |<LT>)  ShiftExpression() #LTNode(2)
    |
-    (<gt> | <GT>) AdditiveExpression() #GTNode(2)
+    (<gt> | <GT>) ShiftExpression() #GTNode(2)
    |
-    (<le> | <LE>) AdditiveExpression() #LENode(2)
+    (<le> | <LE>) ShiftExpression() #LENode(2)
    |
-    (<ge> | <GE>) AdditiveExpression() #GENode(2)
+    (<ge> | <GE>) ShiftExpression() #GENode(2)
    |
-    <req> AdditiveExpression() #ERNode(2) // equals regexp
+    <req> ShiftExpression() #ERNode(2) // equals regexp
    |
-    <rne> AdditiveExpression() #NRNode(2) // not equals regexp
+    <rne> ShiftExpression() #NRNode(2) // not equals regexp
    |
-    <seq> AdditiveExpression() #SWNode(2) // starts with
+    <seq> ShiftExpression() #SWNode(2) // starts with
    |
-    <sne> AdditiveExpression() #NSWNode(2) // not starts with
+    <sne> ShiftExpression() #NSWNode(2) // not starts with
    |
-    <eeq> AdditiveExpression() #EWNode(2) // ends with
+    <eeq> ShiftExpression() #EWNode(2) // ends with
    |
-    <ene> AdditiveExpression() #NEWNode(2) // not ends with
+    <ene> ShiftExpression() #NEWNode(2) // not ends with
   )?
 }
 
 /***************************************
  *      Arithmetic
  ***************************************/
+void ShiftExpression() #void : {}
+{
+    AdditiveExpression()
+    ( LOOKAHEAD(2) (
+      <lshift> AdditiveExpression() #ShiftLeftNode(2) // left shift
+    |
+      <rshift> AdditiveExpression() #ShiftRightNode(2) // right shift
+    |
+      <rshiftu> AdditiveExpression() #ShiftRightUnsignedNode(2) // right shift unsigned
+    ) )*
+}
 
 void AdditiveExpression() #void : {}
 {
diff --git a/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java b/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java
index 876a61b..a656022 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java
@@ -78,6 +78,12 @@ public abstract class ParserVisitor {
 
     protected abstract Object visit(ASTBitwiseAndNode node, Object data);
 
+    protected abstract Object visit(ASTShiftLeftNode node, final Object data);
+
+    protected abstract Object visit(ASTShiftRightNode node, final Object data);
+
+    protected abstract Object visit(ASTShiftRightUnsignedNode node, final Object data);
+
     protected abstract Object visit(ASTEQNode node, Object data);
 
     protected abstract Object visit(ASTNENode node, Object data);
@@ -180,6 +186,12 @@ public abstract class ParserVisitor {
 
     protected abstract Object visit(ASTSetXorNode node, Object data);
 
+    protected abstract Object visit(ASTSetShiftLeftNode node, final Object data);
+
+    protected abstract Object visit(ASTSetShiftRightNode node, final Object data);
+
+    protected abstract Object visit(ASTSetShiftRightUnsignedNode node, final Object data);
+
     protected abstract Object visit(ASTJxltLiteral node, Object data);
 
     protected abstract Object visit(ASTAnnotation node, Object data);
diff --git a/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java b/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
index 91f704c..8b8cacc 100644
--- a/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
+++ b/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
@@ -980,6 +980,18 @@ public class ArithmeticTest extends JexlTestCase {
             return new Var(lhs.value ^ rhs.value);
         }
 
+        public Var shiftRight(final Var lhs, final Var rhs) {
+            return new Var(lhs.value >> rhs.value);
+        }
+
+        public Var shiftRightUnsigned(final Var lhs, final Var rhs) {
+            return new Var(lhs.value >>> rhs.value);
+        }
+
+        public Var shiftLeft(final Var lhs, final Var rhs) {
+            return new Var(lhs.value << rhs.value);
+        }
+
         public Boolean contains(final Var lhs, final Var rhs) {
             return lhs.toString().contains(rhs.toString());
         }
@@ -1163,6 +1175,24 @@ public class ArithmeticTest extends JexlTestCase {
         result = script.execute(jc, new Var(35), new Var(7));
         Assert.assertEquals(36L, ((Var) result).value);
 
+        script = jexl.createScript("(x, y)->{ x << y }");
+        result = script.execute(jc, 35, 1);
+        Assert.assertEquals(70L, result);
+        result = script.execute(jc, new Var(35), new Var(1));
+        Assert.assertEquals(70L, ((Var) result).value);
+
+        script = jexl.createScript("(x, y)->{ x >> y }");
+        result = script.execute(jc, 42, 1);
+        Assert.assertEquals(21L, result);
+        result = script.execute(jc, new Var(42), new Var(1));
+        Assert.assertEquals(21, ((Var) result).value);
+
+        script = jexl.createScript("(x, y)->{ x >>> y }");
+        result = script.execute(jc, 84, 2);
+        Assert.assertEquals(21L, result);
+        result = script.execute(jc, new Var(84), new Var(2));
+        Assert.assertEquals(21, ((Var) result).value);
+
         script = jexl.createScript("(x, y)->{ x & y }");
         result = script.execute(jc, 35, 7);
         Assert.assertEquals(3L, result);