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"));
+ }
+ }
+
+}