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

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


The following commit(s) were added to refs/heads/JEXL-440 by this push:
     new e9375c97 JEXL-440 : basic switch statement & expression tests added;
e9375c97 is described below

commit e9375c974926c19f328c8c39aac9e1f6531a5105
Author: Henrib <hbies...@gmail.com>
AuthorDate: Sun Jun 8 17:17:42 2025 +0200

    JEXL-440 : basic switch statement & expression tests added;
---
 .../org/apache/commons/jexl3/JexlFeatures.java     | 21 +++++++++-
 .../apache/commons/jexl3/internal/Interpreter.java | 48 ++++++++++++++++------
 .../commons/jexl3/parser/ASTSwitchStatement.java   |  4 +-
 .../org/apache/commons/jexl3/parser/Parser.jjt     |  6 +--
 .../org/apache/commons/jexl3/Issues400Test.java    | 26 +++++++++++-
 5 files changed, 84 insertions(+), 21 deletions(-)

diff --git a/src/main/java/org/apache/commons/jexl3/JexlFeatures.java 
b/src/main/java/org/apache/commons/jexl3/JexlFeatures.java
index d2cb4dbe..65b7fced 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlFeatures.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlFeatures.java
@@ -172,8 +172,7 @@ public final class JexlFeatures {
      * @since 3.3.1
      */
     private static final Set<String> RESERVED_WORDS =
-        Collections.unmodifiableSet(
-            new HashSet<>(Arrays.asList( "class", "jexl", "$jexl")));
+        Collections.unmodifiableSet(new HashSet<>(Arrays.asList( "class", 
"jexl", "$jexl")));
 
     /*
      * *WARNING*
@@ -718,10 +717,28 @@ public final class JexlFeatures {
         }
     }
 
+    /**
+     * Sets whether statements can be ambiguous.
+     * <p>
+     * When enabled, the semicolumn is not required between expressions that 
otherwise are considered
+     * ambiguous. The default will report ambiguity in cases like <code>if 
(true) { x 5 }</code> considering this
+     * may be missing an operator or that the intent is not clear.
+     * </p>
+     * @param flag true to enable, false to disable
+     */
     public void setAmbiguousStatement(final boolean flag) {
         setFeature(AMBIGUOUS_STATEMENT, flag);
     }
 
+    /**
+     * Checks whether statements can be ambiguous.
+     * <p>
+     * When enabled, the semicolumn is not required between expressions that 
otherwise are considered
+     * ambiguous. The default will report ambiguity in cases like <code>if 
(true) { x 5 }</code> considering this
+     * may be missing an operator or that the intent is not clear.
+     * </p>
+     * @return true if statements can be ambiguous, false otherwise
+     */
     public boolean supportsAmbiguousStatement() {
         return getFeature(AMBIGUOUS_STATEMENT);
     }
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 dc097f99..decbc26e 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -1230,24 +1230,19 @@ public class Interpreter extends InterpreterBase {
         }
     }
 
-    @Override
-    protected Object visit(ASTSwitchStatement node, Object data) {
-        return null;
-    }
-
     @Override
     protected Object visit(ASTCaseStatement node, Object data) {
-        return null;
-    }
-
-    @Override
-    protected Object visit(ASTSwitchExpression node, Object data) {
-        return null;
+        final int argc = node.jjtGetNumChildren();
+        Object result = null;
+        for (int i = 0; i < argc; i++) {
+            result = node.jjtGetChild(i).jjtAccept(this, data);
+        }
+        return result;
     }
 
     @Override
     protected Object visit(ASTCaseExpression node, Object data) {
-        return null;
+        return node.jjtGetChild(0).jjtAccept(this, data);
     }
 
     @Override
@@ -2026,6 +2021,35 @@ public class Interpreter extends InterpreterBase {
         return operators.startsWith(node, JexlOperator.STARTSWITH, left, 
right);
     }
 
+
+    @Override
+    protected Object visit(final ASTSwitchExpression node, final Object data) {
+        final Object value = node.jjtGetChild(0).jjtAccept(this, data);
+        final int index = node.switchIndex(value);
+        return index >= 0 ? node.jjtGetChild(index).jjtAccept(this, data) : 
null;
+    }
+
+    @Override
+    protected Object visit(final ASTSwitchStatement node, final Object data) {
+        final int count = node.jjtGetNumChildren();
+        Object value = node.jjtGetChild(0).jjtAccept(this, data);
+        int index = node.switchIndex(value);
+        if (index > 0) {
+            for (int i = index; i < count; ++i) {
+                try {
+                    // evaluate the switch body
+                    value = node.jjtGetChild(i).jjtAccept(this, data);
+                } catch (final JexlException.Break xbreak) {
+                   break; // break out of the switch
+                } catch (final JexlException.Continue xcontinue) {
+                    // continue to next case
+                }
+            }
+            return value;
+        }
+        return null;
+    }
+
     @Override
     protected Object visit(final ASTTernaryNode node, final Object data) {
         Object condition;
diff --git 
a/src/main/java/org/apache/commons/jexl3/parser/ASTSwitchStatement.java 
b/src/main/java/org/apache/commons/jexl3/parser/ASTSwitchStatement.java
index 40f966be..6ea12992 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/ASTSwitchStatement.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/ASTSwitchStatement.java
@@ -80,7 +80,7 @@ public class ASTSwitchStatement extends JexlNode {
     if (index == null) {
       index = cases.get(JexlParser.DFLT);
     }
-    if (index != null && index >= 0 && index < jjtGetNumChildren()) {
+    if (index != null && index >= 1 && index < jjtGetNumChildren()) {
       return index; // index is 1-based, children are 0-based
     }
     return -1;
@@ -91,7 +91,7 @@ public class ASTSwitchStatement extends JexlNode {
    * <p>It detects duplicates cases and default.</p>
    */
   public static class Helper {
-    private int nswitch = 0;
+    private int nswitch = 1; // switch index, starts at 1 since the first 
child is the switch expression
     private boolean defaultDefined = false;
     private final Map<Object, Integer> dispatch = new LinkedHashMap<>();
 
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 397be2d9..caed1e89 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
+++ b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
@@ -440,7 +440,7 @@ void FunctionStatement() #JexlLambda : {}
 
 void ExpressionStatement() #void : {}
 {
-    Expression() (LOOKAHEAD(Expression()/*, { 
getFeatures().supportsAmbiguousStatement() }*/) Expression() #Ambiguous(1))* 
(LOOKAHEAD(1) <SEMICOL>)?
+    Expression() (LOOKAHEAD(Expression(), { 
!getFeatures().supportsAmbiguousStatement() } ) Expression() #Ambiguous(1))* 
(LOOKAHEAD(1) <SEMICOL>)?
 }
 
 
@@ -611,8 +611,8 @@ void SwitchStatementCase(SwitchSet cases) #CaseStatement :
     loopCount += 1;
 }
 {
-    ( ( LOOKAHEAD(2) <CASE> constant=constLiteral() <COLON> )+
-        { constants = Collections.singletonList(constant); 
jjtThis.setValues(constants); cases.addAll(constants); }
+    ( ( LOOKAHEAD(2) <CASE> constant=constLiteral() <COLON>
+        { constants = Collections.singletonList(constant); 
jjtThis.setValues(constants); cases.addAll(constants); } )+
         |
         <CASE_DEFAULT> <COLON> )
     ( LOOKAHEAD(<LCURLY>) Block() | (StatementNoVar())+ )?
diff --git a/src/test/java/org/apache/commons/jexl3/Issues400Test.java 
b/src/test/java/org/apache/commons/jexl3/Issues400Test.java
index 426e1009..8a969b2d 100644
--- a/src/test/java/org/apache/commons/jexl3/Issues400Test.java
+++ b/src/test/java/org/apache/commons/jexl3/Issues400Test.java
@@ -733,11 +733,22 @@ public class Issues400Test {
         f.setAmbiguousStatement(true);
         JexlEngine jexl = new 
JexlBuilder().features(f).safe(false).strict(true).create();
         String src =
-"let y = switch (x) { case 10,11 -> 3 case 20, 21 -> 4\ndefault -> { let z = 
4; z + 6 } } y";
+"let y = switch (x) { case 10,11 -> 3 case 20, 21 -> 4\ndefault -> { let z = 
4; z + x } } y";
         JexlScript script = jexl.createScript(src, "x");
         assertNotNull(script);
         String dbgStr = script.getParsedText();
         assertNotNull(dbgStr);
+
+        Object result = script.execute(null, 10);
+        Assertions.assertEquals(3, result);
+        result = script.execute(null, 11);
+        Assertions.assertEquals(3, result);
+        result = script.execute(null, 20);
+        Assertions.assertEquals(4, result);
+        result = script.execute(null, 21);
+        Assertions.assertEquals(4, result);
+        result = script.execute(null, 38);
+        Assertions.assertEquals(42, result);
         src =
                 "let y = switch (x) { case 10,11 -> break; case 20, 21 -> 4 } 
y";
         try {
@@ -752,10 +763,21 @@ public class Issues400Test {
     void test440b() {
         JexlEngine jexl = new JexlBuilder().safe(false).strict(true).create();
         final String src =
-"switch (x) { case 10 : 3; case 20 : case 21 : 4; case 32: break; default : 
return 5; } ";
+"switch (x) { case 10 : return 3; case 20 : case 21 : return 4; case 32: 
break; default : return x + 4; } 169";
         final JexlScript script = jexl.createScript(src, "x");
         assertNotNull(script);
         String dbgStr = script.getParsedText();
         assertNotNull(dbgStr);
+
+        Object result = script.execute(null, 10);
+        Assertions.assertEquals(3, result);
+        result = script.execute(null, 20);
+        Assertions.assertEquals(4, result);
+        result = script.execute(null, 21);
+        Assertions.assertEquals(4, result);
+        result = script.execute(null, 32);
+        Assertions.assertEquals(169, result);
+        result = script.execute(null, 38);
+        Assertions.assertEquals(42, result);
     }
 }

Reply via email to