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 2018/09/13 16:26:31 UTC

[commons-jexl] 01/04: Introduce do/while loop syntax

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

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

commit 65ce82f5ab9779b149f9ee453c80744ca13605a0
Author: Dmitri Blinov <dm...@mail.ru>
AuthorDate: Mon Aug 20 11:55:20 2018 +0300

    Introduce do/while loop syntax
---
 .../apache/commons/jexl3/internal/Debugger.java    | 16 +++++
 .../apache/commons/jexl3/internal/Interpreter.java | 23 +++++++
 .../commons/jexl3/internal/ScriptVisitor.java      |  6 ++
 .../commons/jexl3/parser/FeatureController.java    |  8 +++
 .../org/apache/commons/jexl3/parser/Parser.jjt     |  8 ++-
 .../apache/commons/jexl3/parser/ParserVisitor.java |  2 +
 src/site/xdoc/reference/syntax.xml                 | 15 ++++-
 .../java/org/apache/commons/jexl3/DoWhileTest.java | 76 ++++++++++++++++++++++
 8 files changed, 150 insertions(+), 4 deletions(-)

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..3126647 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Debugger.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Debugger.java
@@ -35,6 +35,7 @@ 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;
@@ -282,6 +283,7 @@ public class Debugger extends ParserVisitor implements JexlInfo.Detail {
             || child instanceof ASTIfStatement
             || child instanceof ASTForeachStatement
             || child instanceof ASTWhileStatement
+            || child instanceof ASTDoWhileStatement
             || child instanceof ASTAnnotation)) {
             builder.append(';');
             if (indent > 0) {
@@ -961,6 +963,20 @@ public class Debugger extends ParserVisitor implements JexlInfo.Detail {
     }
 
     @Override
+    protected Object visit(ASTDoWhileStatement node, Object data) {
+        builder.append("do ");
+
+        acceptStatement(node.jjtGetChild(0), data);
+
+        builder.append(" while (");
+
+        accept(node.jjtGetChild(1), data);
+
+        builder.append(")");
+        return data;
+    }
+
+    @Override
     protected Object visit(ASTSetAddNode node, Object data) {
         return infixChildren(node, " += ", false, data);
     }
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..067405c 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -47,6 +47,7 @@ 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;
@@ -706,6 +707,28 @@ public class Interpreter extends InterpreterBase {
     }
 
     @Override
+    protected Object visit(ASTDoWhileStatement node, Object data) {
+        Object result = null;
+        /* last objectNode is the expression */
+        Node expressionNode = node.jjtGetChild(1);
+        do {
+            cancelCheck(node);
+
+            try {
+                // execute statement
+                result = node.jjtGetChild(0).jjtAccept(this, data);
+            } catch (JexlException.Break stmtBreak) {
+                break;
+            } catch (JexlException.Continue stmtContinue) {
+                //continue;
+            }
+
+        } while (arithmetic.toBoolean(expressionNode.jjtAccept(this, data)));
+
+        return result;
+    }
+
+    @Override
     protected Object visit(ASTAndNode node, Object data) {
         /**
          * The pattern for exception mgmt is to let the child*.jjtAccept out of the try/catch loop so that if one fails,
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..2d1e7ca 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java
@@ -35,6 +35,7 @@ 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;
@@ -155,6 +156,11 @@ public class ScriptVisitor extends ParserVisitor {
     }
 
     @Override
+    protected Object visit(ASTDoWhileStatement node, Object data) {
+        return visitNode(node, data);
+    }
+
+    @Override
     protected Object visit(ASTContinue node, Object data) {
         return visitNode(node, data);
     }
diff --git a/src/main/java/org/apache/commons/jexl3/parser/FeatureController.java b/src/main/java/org/apache/commons/jexl3/parser/FeatureController.java
index 92d0e72..e40192d 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/FeatureController.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/FeatureController.java
@@ -112,6 +112,14 @@ public class FeatureController extends ScriptVisitor {
     }
 
     @Override
+    protected Object visit(ASTDoWhileStatement node, Object data) {
+        if (!features.supportsLoops()) {
+            throwFeatureException(JexlFeatures.LOOP, node);
+        }
+        return data;
+    }
+
+    @Override
     protected Object visit(ASTForeachStatement node, Object data) {
         if (!features.supportsLoops()) {
             throwFeatureException(JexlFeatures.LOOP, node);
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..d63911b 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
+++ b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
@@ -121,6 +121,7 @@ TOKEN_MGR_DECLS : {
     | < ELSE : "else" >
     | < FOR : "for" >
     | < WHILE : "while" >
+    | < DO : "do" >
     | < NEW : "new" >
     | < VAR : "var" >
     | < EMPTY : "empty" > { popDot(); } /* Revert state to default if was DOT_ID. */
@@ -311,6 +312,7 @@ void Statement() #void : {}
     | IfStatement()
     | ForeachStatement()
     | WhileStatement()
+    | DoWhileStatement()
     | ExpressionStatement()
     | ReturnStatement()
     | Continue()
@@ -338,12 +340,16 @@ void IfStatement() : {}
     ( LOOKAHEAD(1) <ELSE>  (LOOKAHEAD(1) Block() | Statement()) )?
 }
 
-
 void WhileStatement() : {}
 {
     <WHILE> <LPAREN> Expression() <RPAREN>  { loopCount += 1; }  (LOOKAHEAD(1) Block() | Statement()) { loopCount -= 1; }
 }
 
+void DoWhileStatement() : {}
+{
+    <DO> { loopCount += 1; } (LOOKAHEAD(1) Block() | Statement()) <WHILE> <LPAREN> Expression() <RPAREN> { loopCount -= 1; }
+}
+
 void ReturnStatement() : {}
 {
     <RETURN> ExpressionStatement()
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..e749226 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java
@@ -48,6 +48,8 @@ public abstract class ParserVisitor {
 
     protected abstract Object visit(ASTWhileStatement node, Object data);
 
+    protected abstract Object visit(ASTDoWhileStatement node, Object data);
+
     protected abstract Object visit(ASTContinue node, Object data);
 
     protected abstract Object visit(ASTBreak node, Object data);
diff --git a/src/site/xdoc/reference/syntax.xml b/src/site/xdoc/reference/syntax.xml
index e0ac82d..3909a86 100644
--- a/src/site/xdoc/reference/syntax.xml
+++ b/src/site/xdoc/reference/syntax.xml
@@ -94,7 +94,7 @@
                         </p>
                         <p>
                             <strong>N.B.</strong> the following keywords are reserved, and cannot be used as a variable name or property when using the dot operator:
-                            <code>or and eq ne lt gt le ge div mod not null true false new var break continue return</code>
+                            <code>or and eq ne lt gt le ge div mod not null true false new do while var break continue return</code>
                             For example, the following is invalid:
                             <code>my.new.dotted.var // invalid ('new' is keyword)</code>
                             In such cases, quoted identifiers or the [ ] operator can be used, for example:
@@ -902,15 +902,24 @@
                     </td>
                 </tr>
                 <tr>
+                    <td>do/while</td>
+                    <td>
+                        Loop until a condition is satisfied, e.g.
+                        <code>do {
+                            x = x + 2;
+                            } while (x lt 10)</code>
+                    </td>
+                </tr>
+                <tr>
                     <td>continue</td>
                     <td>
-                        Within loops (while/for), allows to skip to the next iteration.
+                        Within loops (do/while/for), allows to skip to the next iteration.
                     </td>
                 </tr>
                 <tr>
                     <td>break</td>
                     <td>
-                        Allows to break from a loop (while/for) inconditionally.
+                        Allows to break from a loop (do/while/for) unconditionally.
                     </td>
                 </tr>
             </table>
diff --git a/src/test/java/org/apache/commons/jexl3/DoWhileTest.java b/src/test/java/org/apache/commons/jexl3/DoWhileTest.java
new file mode 100644
index 0000000..a01e851
--- /dev/null
+++ b/src/test/java/org/apache/commons/jexl3/DoWhileTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests for while statement.
+ * @since 1.1
+ */
+@SuppressWarnings({"UnnecessaryBoxing", "AssertEqualsBetweenInconvertibleTypes"})
+public class DoWhileTest extends JexlTestCase {
+
+    public DoWhileTest() {
+        super("DoWhileTest");
+    }
+
+    @Test
+    public void testSimpleWhileFalse() throws Exception {
+        JexlScript e = JEXL.createScript("do {} while (false)");
+        JexlContext jc = new MapContext();
+
+        Object o = e.execute(jc);
+        Assert.assertNull("Result is not null", o);
+    }
+
+    @Test
+    public void testWhileExecutesExpressionWhenLooping() throws Exception {
+        JexlScript e = JEXL.createScript("do x = x + 1 while (x < 10)");
+        JexlContext jc = new MapContext();
+        jc.set("x", new Integer(1));
+
+        Object o = e.execute(jc);
+        Assert.assertEquals("Result is wrong", new Integer(10), o);
+    }
+
+    @Test
+    public void testWhileWithBlock() throws Exception {
+        JexlScript e = JEXL.createScript("do { x = x + 1; y = y * 2; } while (x < 10)");
+        JexlContext jc = new MapContext();
+        jc.set("x", new Integer(1));
+        jc.set("y", new Integer(1));
+
+        Object o = e.execute(jc);
+        Assert.assertEquals("Result is wrong", new Integer(512), o);
+        Assert.assertEquals("x is wrong", new Integer(10), jc.get("x"));
+        Assert.assertEquals("y is wrong", new Integer(512), jc.get("y"));
+    }
+
+    @Test
+    public void testWhileRemoveBroken() throws Exception {
+        try {
+            JexlScript e = JEXL.createScript("do remove while (x < 10)");
+            Assert.fail("remove is out of loop!");
+        } catch (JexlException.Parsing xparse) {
+            String str = xparse.detailedMessage();
+            Assert.assertTrue(str.contains("remove"));
+        }
+    }
+
+}