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

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

commit 732f933cec0f0d0ee442702aefc00a50959a7f59
Author: Dmitri Blinov <[email protected]>
AuthorDate: Mon Aug 20 12:00:36 2018 +0300

    Introduce regex Pattern literal ~//
---
 .../apache/commons/jexl3/internal/Debugger.java    |  7 +++
 .../apache/commons/jexl3/internal/Interpreter.java |  8 +++
 .../commons/jexl3/internal/ScriptVisitor.java      |  6 +++
 .../commons/jexl3/parser/ASTRegexLiteral.java      | 58 ++++++++++++++++++++++
 .../org/apache/commons/jexl3/parser/Parser.jjt     | 19 +++++++
 .../apache/commons/jexl3/parser/ParserVisitor.java |  2 +
 .../apache/commons/jexl3/parser/StringParser.java  | 30 +++++++++++
 src/site/xdoc/reference/syntax.xml                 |  8 +++
 .../commons/jexl3/ArithmeticOperatorTest.java      | 10 ++++
 9 files changed, 148 insertions(+)

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 db75d81..5a57fc0 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Debugger.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Debugger.java
@@ -70,6 +70,7 @@ 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;
@@ -906,6 +907,12 @@ public class Debugger extends ParserVisitor implements 
JexlInfo.Detail {
     }
 
     @Override
+    protected Object visit(ASTRegexLiteral node, Object data) {
+        String img = node.getLiteral().replace("/", "\\/");
+        return check(node, "~/" + img + "/", data);
+    }
+
+    @Override
     protected Object visit(ASTTernaryNode node, Object data) {
         accept(node.jjtGetChild(0), data);
         if (node.jjtGetNumChildren() > 2) {
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 0ff4796..5098b9c 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -84,6 +84,7 @@ 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;
@@ -111,7 +112,9 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.regex.Pattern;
 import java.util.concurrent.Callable;
+
 import org.apache.commons.jexl3.JxltEngine;
 
 
@@ -788,6 +791,11 @@ public class Interpreter extends InterpreterBase {
     }
 
     @Override
+    protected Object visit(ASTRegexLiteral node, Object data) {
+        return Pattern.compile(node.getLiteral());
+    }
+
+    @Override
     protected Object visit(ASTArrayLiteral node, Object data) {
         int childCount = node.jjtGetNumChildren();
         JexlArithmetic.ArrayBuilder ab = arithmetic.arrayBuilder(childCount);
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 8f72800..1f5015a 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java
@@ -70,6 +70,7 @@ 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;
@@ -355,6 +356,11 @@ public class ScriptVisitor extends ParserVisitor {
     }
 
     @Override
+    protected Object visit(ASTRegexLiteral node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
     protected Object visit(ASTSetLiteral node, Object data) {
         return visitNode(node, data);
     }
diff --git a/src/main/java/org/apache/commons/jexl3/parser/ASTRegexLiteral.java 
b/src/main/java/org/apache/commons/jexl3/parser/ASTRegexLiteral.java
new file mode 100644
index 0000000..30d4dc8
--- /dev/null
+++ b/src/main/java/org/apache/commons/jexl3/parser/ASTRegexLiteral.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl3.parser;
+
+public final class ASTRegexLiteral extends JexlNode implements 
JexlNode.Constant<String> {
+    /** The actual literal value; the inherited 'value' member may host a 
cached getter. */
+    private String literal = null;
+
+    ASTRegexLiteral(int id) {
+        super(id);
+    }
+
+    ASTRegexLiteral(Parser p, int id) {
+        super(p, id);
+    }
+
+    @Override
+    public String toString() {
+        return this.literal;
+    }
+
+    /**
+     * Gets the literal value.
+     * @return the string literal
+     */
+    @Override
+    public String getLiteral() {
+        return this.literal;
+    }
+
+    @Override
+    protected boolean isConstant(boolean literal) {
+        return true;
+    }
+
+    void setLiteral(String literal) {
+        this.literal = literal;
+    }
+
+    @Override
+    public Object jjtAccept(ParserVisitor visitor, Object data) {
+        return visitor.visit(this, 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 6bcaae4..0574099 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
+++ b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
@@ -265,6 +265,13 @@ TOKEN_MGR_DECLS : {
   > { popDot(); } /* Revert state to default if was DOT_ID. */
 }
 
+<*> TOKEN :
+{
+  < REGEX_LITERAL:
+    "~" "/" (~["/","\n","\r","\u2028","\u2029"] | "\\" "/" )* "/"
+  > { popDot(); } /* Revert state to default if was DOT_ID. */
+}
+
 /***************************************
  *      Statements
  ***************************************/
@@ -624,6 +631,8 @@ void Literal() #void :
 |
   StringLiteral()
 |
+  RegexLiteral()
+|
   NullLiteral()
 |
   NaNLiteral()
@@ -686,6 +695,16 @@ void JxltLiteral() #JxltLiteral :
    { jjtThis.setLiteral(Parser.buildString(t.image, true)); }
 }
 
+void RegexLiteral() :
+{
+   Token t;
+}
+{
+  t=<REGEX_LITERAL>
+  { jjtThis.setLiteral(Parser.buildRegex(t.image)); }
+}
+
+
 void ExtendedLiteral() #ExtendedLiteral() : {}
 {
    <ELIPSIS>
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 0304738..e20f670 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java
@@ -128,6 +128,8 @@ public abstract class ParserVisitor {
 
     protected abstract Object visit(ASTStringLiteral node, Object data);
 
+    protected abstract Object visit(ASTRegexLiteral node, Object data);
+
     protected abstract Object visit(ASTSetLiteral node, Object data);
 
     protected abstract Object visit(ASTExtendedLiteral node, Object data);
diff --git a/src/main/java/org/apache/commons/jexl3/parser/StringParser.java 
b/src/main/java/org/apache/commons/jexl3/parser/StringParser.java
index f58288d..6fa6479 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/StringParser.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/StringParser.java
@@ -54,6 +54,36 @@ public class StringParser {
     }
 
     /**
+     * Builds a regex pattern string, handles escaping '/' through '\/' syntax.
+     * @param str the string to build from
+     * @return the built string
+     */
+    public static String buildRegex(CharSequence str) {
+
+        int length = str.length();
+
+        StringBuilder strb = new StringBuilder(length);
+
+        int start = 2;
+        int end = length - 1;
+
+        for (int i = start; i < end; ++i) {
+            char c = str.charAt(i);
+            if (c == '\\') {
+                if (i+1 < end && str.charAt(i+1) == '/') {
+                    strb.append("/");
+                    i++;
+                } else {
+                    strb.append(c);
+                }
+            } else {
+                strb.append(c);
+            }
+        }
+        return strb.toString();
+    }
+
+    /**
      * Read the remainder of a string till a given separator,
      * handles escaping through '\' syntax.
      * @param strb the destination buffer to copy characters into
diff --git a/src/site/xdoc/reference/syntax.xml 
b/src/site/xdoc/reference/syntax.xml
index e0ac82d..1c63bd0 100644
--- a/src/site/xdoc/reference/syntax.xml
+++ b/src/site/xdoc/reference/syntax.xml
@@ -346,6 +346,14 @@
                     </td>
                 </tr>
                 <tr>
+                    <td>Regex literals</td>
+                    <td>
+                        Start with <code>~/</code> and ends with 
<code>/</code> delimiters, e.g.
+                        <code>~/ABC.*/</code>
+                        <p>The escape character is <code>\</code> (backslash); 
it only escapes the string delimiter <code>\</code> (slash)</p>
+                    </td>
+                </tr>
+                <tr>
                     <td>Boolean literals</td>
                     <td>
                         The literals <code>true</code> and <code>false</code> 
can be used, e.g.
diff --git a/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java 
b/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java
index 0a03a0b..c7c051b 100644
--- a/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java
+++ b/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java
@@ -91,6 +91,16 @@ public class ArithmeticOperatorTest extends JexlTestCase {
     }
 
     @Test
+    public void testRegexp2() throws Exception {
+        asserter.setVariable("str", "abc456");
+        asserter.assertExpression("str =~ ~/.*456/", Boolean.TRUE);
+        asserter.assertExpression("str !~ ~/ABC.*/", Boolean.TRUE);
+        asserter.assertExpression("str =~ ~/abc\\d{3}/", Boolean.TRUE);
+        asserter.setVariable("str", "4/6");
+        asserter.assertExpression("str =~ ~/\\d\\/\\d/", Boolean.TRUE);
+    }
+
+    @Test
     public void testStartsEndsWithString() throws Exception {
         asserter.setVariable("x", "foobar");
         asserter.assertExpression("x =^ 'foo'", Boolean.TRUE);

Reply via email to