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 <[email protected]> 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")); + } + } + +}
