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


The following commit(s) were added to refs/heads/master by this push:
     new ab1a6d9c JEXL-398: implemented trailing comma and ellipsis for 
array/set/map literals; - Tests, changes and release notes; - A few incidental 
tweaks here and there (Closure, error reporting);
ab1a6d9c is described below

commit ab1a6d9c3c32fd9c5e49def7fe8f1763dce09d7d
Author: Henri Biestro <hbies...@cloudera.com>
AuthorDate: Thu Jul 6 19:13:56 2023 +0200

    JEXL-398: implemented trailing comma and ellipsis for array/set/map 
literals;
    - Tests, changes and release notes;
    - A few incidental tweaks here and there (Closure, error reporting);
---
 BUILDING.txt                                       |  5 +-
 RELEASE-NOTES.txt                                  | 17 ++++++
 src/changes/changes.xml                            | 10 +++-
 .../org/apache/commons/jexl3/JexlArithmetic.java   | 32 +++++++++--
 .../commons/jexl3/internal/ArrayBuilder.java       | 19 +++++--
 .../org/apache/commons/jexl3/internal/Closure.java |  4 +-
 .../apache/commons/jexl3/internal/Interpreter.java | 60 ++++++++------------
 .../commons/jexl3/internal/InterpreterBase.java    |  4 +-
 .../apache/commons/jexl3/internal/MapBuilder.java  | 12 +++-
 .../apache/commons/jexl3/internal/SetBuilder.java  | 12 +++-
 .../commons/jexl3/parser/ASTArrayLiteral.java      | 30 +---------
 .../apache/commons/jexl3/parser/ASTMapLiteral.java | 32 +----------
 .../apache/commons/jexl3/parser/ASTSetLiteral.java | 29 +---------
 .../commons/jexl3/parser/ExtensibleNode.java       | 64 ++++++++++++++++++++++
 .../org/apache/commons/jexl3/parser/Parser.jjt     | 12 +++-
 .../org/apache/commons/jexl3/ArrayLiteralTest.java | 40 ++++++++++----
 .../commons/jexl3/CollectionLiteralTest.java       | 24 ++++----
 .../org/apache/commons/jexl3/Issues300Test.java    | 33 +++++++++++
 .../org/apache/commons/jexl3/MapLiteralTest.java   | 31 +++++++----
 .../org/apache/commons/jexl3/SetLiteralTest.java   | 37 +++++++++----
 .../org/apache/commons/jexl3/internal/Util.java    |  2 +-
 .../commons/jexl3/introspection/SandboxTest.java   |  2 +-
 .../commons/jexl3/jexl342/OptionalArithmetic.java  | 12 ++--
 23 files changed, 322 insertions(+), 201 deletions(-)

diff --git a/BUILDING.txt b/BUILDING.txt
index 926eacad..3b67478e 100644
--- a/BUILDING.txt
+++ b/BUILDING.txt
@@ -1,4 +1,4 @@
-JEXL 3.0 uses Maven 3 as the build tool, and requires Java 6 (or later).
+JEXL 3.0 uses Maven 3 as the build tool, and requires Java 8 (or later).
 
 The following goals may be useful:
 * mvn clean - clean up
@@ -10,5 +10,4 @@ The following goals may be useful:
 Note that the Maven build process uses JavaCC to generate some sources.
 These are created under target/generated sources and automatically compiled.
 The generated class files are needed to compile some of the ordinary Java 
source files.
-This can cause problems for some IDEs, which will need to be configured 
accordingly.
- 
\ No newline at end of file
+This can cause problems for some IDEs, which will need to be configured 
accordingly.
\ No newline at end of file
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 29450e27..a75a5b81 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -18,6 +18,23 @@ Its goal is to expose scripting features usable by technical 
operatives or consu
 
   https://commons.apache.org/jexl/
 
+========================================================================================================================
+Release 3.2.1
+========================================================================================================================
+
+Version 3.3.1 is a maintenance release.
+
+Compatibility with previous releases
+====================================
+Version 3.3.1 is source and binary compatible with 3.3.
+
+New Features in 3.3.1:
+====================
+* JEXL-398:     Allow 'trailing commas' or ellipsis while defining array, map 
and set literals
+
+Bugs Fixed in 3.3.1:
+===================
+
 
========================================================================================================================
 Release 3.3
 
========================================================================================================================
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 251375a1..187a6ed3 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -28,22 +28,26 @@
     </properties>
     <body>
         <release version="3.3.1" date="20YY-MM-DD">
+            <!-- ADD -->
+            <action dev="henrib" type="add" due-to="Xu Pengcheng">
+                Allow 'trailing commas' or ellipsis while defining array, map 
and set literals
+            </action>
             <!-- FIX -->
             <action dev="ggregory" type="fix" due-to="step-security-bot, Gary 
Gregory">
                 [StepSecurity] ci: Harden GitHub Actions #180.
             </action>            
             <!-- UPDATE -->
-            <action dev="hbiestro" type="update" due-to="dependabot">
+            <action dev="henrib" type="update" due-to="dependabot">
                 Bumps ossf/scorecard-action from 2.1.3 to 2.2.0.
             </action>
-            <action dev="hbiestro" type="update" due-to="dependabot">
+            <action dev="henrib" type="update" due-to="dependabot">
                 Bumps github/codeql-action from 2.1.22 to 2.20.1.
             </action>
             <action dev="ggregory" type="update" due-to="Gary Gregory">
                 Bump commons-parent from 57 to 58.
             </action>
         </release>
-        <release version="3.3" date="2020-03-20">
+        <release version="3.3" date="2023-03-20">
             <!-- ADD -->
             <action dev="henrib" type="add" issue="JEXL-392">
                 Enable namespace declaration based on scripts
diff --git a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java 
b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
index 30ff02dc..8c808a02 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlArithmetic.java
@@ -281,11 +281,19 @@ public class JexlArithmetic {
      * Called by the interpreter when evaluating a literal array.
      *
      * @param size the number of elements in the array
+     * @param extended whether the map is extended or not
      * @return the array builder
      */
-    public ArrayBuilder arrayBuilder(final int size) {
+    public ArrayBuilder arrayBuilder(final int size, final boolean extended) {
         return new org.apache.commons.jexl3.internal.ArrayBuilder(size);
     }
+    @Deprecated
+    /**
+     * @deprecated since 3.3.1
+     */
+    public ArrayBuilder arrayBuilder(final int size) {
+        return arrayBuilder(size, false);
+    }
 
     /**
      * Helper interface used when creating a set literal.
@@ -311,11 +319,19 @@ public class JexlArithmetic {
      * Called by the interpreter when evaluating a literal set.
      *
      * @param size the number of elements in the set
+     * @param extended whether the set is extended or not
      * @return the array builder
      */
-    public SetBuilder setBuilder(final int size) {
+    public SetBuilder setBuilder(final int size, boolean extended) {
         return new org.apache.commons.jexl3.internal.SetBuilder(size);
     }
+    @Deprecated
+    /**
+     * @deprecated since 3.3.1
+     */
+    public SetBuilder setBuilder(final int size) {
+        return setBuilder(size, false);
+    }
 
     /**
      * Helper interface used when creating a map literal.
@@ -342,12 +358,20 @@ public class JexlArithmetic {
      * Called by the interpreter when evaluating a literal map.
      *
      * @param size the number of elements in the map
+     * @param extended whether the map is extended or not
      * @return the map builder
      */
-    public MapBuilder mapBuilder(final int size) {
+    public MapBuilder mapBuilder(final int size, boolean extended) {
         return new org.apache.commons.jexl3.internal.MapBuilder(size);
     }
-
+    @Deprecated
+    /**
+     * @deprecated since 3.3.1
+     */
+    public MapBuilder mapBuilder(final int size) {
+        return mapBuilder(size, false);
+    }
+    
     /**
      * Creates a literal range.
      * <p>The default implementation only accepts integers and longs.</p>
diff --git a/src/main/java/org/apache/commons/jexl3/internal/ArrayBuilder.java 
b/src/main/java/org/apache/commons/jexl3/internal/ArrayBuilder.java
index 76188fd4..4a905e77 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/ArrayBuilder.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/ArrayBuilder.java
@@ -46,7 +46,7 @@ public class ArrayBuilder implements 
JexlArithmetic.ArrayBuilder {
     }
 
     /**
-     * Gets the primitive type of a given class (when it exists).
+     * Gets the primitive type of given class (when it exists).
      * @param parm a class
      * @return the primitive type or null it the argument is not unboxable
      */
@@ -65,13 +65,24 @@ public class ArrayBuilder implements 
JexlArithmetic.ArrayBuilder {
     protected final Object[] untyped;
     /** Number of added items. */
     protected int added = 0;
+    /** Extended? */
+    protected final boolean extended;
 
     /**
      * Creates a new builder.
      * @param size the exact array size
      */
     public ArrayBuilder(final int size) {
-        untyped = new Object[size];
+        this(size, false);
+    }
+    /**
+     * Creates a new builder.
+     * @param size the exact array size
+     * @param extended whether the array is extended
+     */
+    public ArrayBuilder(final int size, final boolean extended) {
+        this.untyped = new Object[size];
+        this.extended = extended;
     }
 
     @Override
@@ -111,11 +122,11 @@ public class ArrayBuilder implements 
JexlArithmetic.ArrayBuilder {
     }
 
     @Override
-    public Object create(final boolean extended) {
+    public Object create(final boolean e) {
         if (untyped == null) {
             return new Object[0];
         }
-        if (extended) {
+        if (extended || e) {
             final List<Object> list = new ArrayList<>(added);
             list.addAll(Arrays.asList(untyped).subList(0, added));
             return list;
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Closure.java 
b/src/main/java/org/apache/commons/jexl3/internal/Closure.java
index 0c836b94..72cdd8bc 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Closure.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Closure.java
@@ -137,7 +137,7 @@ public class Closure extends Script {
     public Object execute(final JexlContext context, final Object... args) {
         final Frame local = frame != null? frame.assign(args) : null;
         final Interpreter interpreter = createInterpreter(context, local, 
options);
-        return interpreter.runClosure(this, null);
+        return interpreter.runClosure(this);
     }
 
     @Override
@@ -146,7 +146,7 @@ public class Closure extends Script {
         return new Callable(createInterpreter(context, local, options)) {
             @Override
             public Object interpret() {
-                return interpreter.runClosure(Closure.this, null);
+                return interpreter.runClosure(Closure.this);
             }
         };
     }
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 817d1f1e..6bc25b60 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -164,28 +164,6 @@ public class Interpreter extends InterpreterBase {
         return null;
     }
 
-    /**
-     * Gets an attribute of an object.
-     *
-     * @param object    to retrieve value from
-     * @param attribute the attribute of the object, e.g. an index (1, 0, 2) 
or key for a map
-     * @return the attribute value
-     */
-    public Object getAttribute(final Object object, final Object attribute) {
-        return getAttribute(object, attribute, null);
-    }
-
-    /**
-     * Sets an attribute of an object.
-     *
-     * @param object    to set the value to
-     * @param attribute the attribute of the object, e.g. an index (1, 0, 2) 
or key for a map
-     * @param value     the value to assign to the object's attribute
-     */
-    public void setAttribute(final Object object, final Object attribute, 
final Object value) {
-        setAttribute(object, attribute, value, null);
-    }
-
     @Override
     protected Object visit(final ASTAddNode node, final Object data) {
         final Object left = node.jjtGetChild(0).jjtAccept(this, data);
@@ -923,7 +901,7 @@ public class Interpreter extends InterpreterBase {
     @Override
     protected Object visit(final ASTArrayLiteral node, final Object data) {
         final int childCount = node.jjtGetNumChildren();
-        final JexlArithmetic.ArrayBuilder ab = 
arithmetic.arrayBuilder(childCount);
+        final JexlArithmetic.ArrayBuilder ab = 
arithmetic.arrayBuilder(childCount, node.isExtended());
         boolean extended = false;
         for (int i = 0; i < childCount; i++) {
             cancelCheck(node);
@@ -946,11 +924,14 @@ public class Interpreter extends InterpreterBase {
     @Override
     protected Object visit(final ASTSetLiteral node, final Object data) {
         final int childCount = node.jjtGetNumChildren();
-        final JexlArithmetic.SetBuilder mb = arithmetic.setBuilder(childCount);
+        final JexlArithmetic.SetBuilder mb = arithmetic.setBuilder(childCount, 
node.isExtended());
         for (int i = 0; i < childCount; i++) {
             cancelCheck(node);
-            final Object entry = node.jjtGetChild(i).jjtAccept(this, data);
-            mb.add(entry);
+            final JexlNode child = node.jjtGetChild(i);
+            if (!(child instanceof ASTExtendedLiteral)) {
+                final Object entry = child.jjtAccept(this, data);
+                mb.add(entry);
+            }
         }
         return mb.create();
     }
@@ -958,11 +939,14 @@ public class Interpreter extends InterpreterBase {
     @Override
     protected Object visit(final ASTMapLiteral node, final Object data) {
         final int childCount = node.jjtGetNumChildren();
-        final JexlArithmetic.MapBuilder mb = arithmetic.mapBuilder(childCount);
+        final JexlArithmetic.MapBuilder mb = arithmetic.mapBuilder(childCount, 
node.isExtended());
         for (int i = 0; i < childCount; i++) {
             cancelCheck(node);
-            final Object[] entry = (Object[]) 
(node.jjtGetChild(i)).jjtAccept(this, data);
-            mb.put(entry[0], entry[1]);
+            final JexlNode child = node.jjtGetChild(i);
+            if (!(child instanceof ASTExtendedLiteral)) {
+                final Object[] entry = (Object[]) child.jjtAccept(this, data);
+                mb.put(entry[0], entry[1]);
+            }
         }
         return mb.create();
     }
@@ -1052,10 +1036,9 @@ public class Interpreter extends InterpreterBase {
     /**
      * Runs a closure.
      * @param closure the closure
-     * @param data the usual data
      * @return the closure return value
      */
-    protected Object runClosure(final Closure closure, final Object data) {
+    protected Object runClosure(final Closure closure) {
         final ASTJexlScript script = closure.getScript();
         // if empty script, nothing to evaluate
         final int numChildren = script.jjtGetNumChildren();
@@ -1813,13 +1796,16 @@ public class Interpreter extends InterpreterBase {
                 narrow = true;
                 // continue;
             }
-        } catch (final JexlException.Method xmethod) {
-            // ignore and handle at end; treat as an inner discover that fails
-        } catch (final JexlException.TryFailed xany) {
+        }
+        catch (final JexlException.TryFailed xany) {
             throw invocationException(node, methodName, xany);
-        } catch (final JexlException xthru) {
-            throw xthru;
-        } catch (final Exception xany) {
+        }
+        catch (final JexlException xthru) {
+            if (xthru.getInfo() != null) {
+                throw xthru;
+            }
+        }
+        catch (final Exception xany) {
             throw invocationException(node, methodName, xany);
         }
         // we have either evaluated and returned or no method was found
diff --git 
a/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java 
b/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
index 2c3deb4d..42153ced 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
@@ -679,7 +679,7 @@ public abstract class InterpreterBase extends ParserVisitor 
{
      * <p>When target == context, we are dealing with a global namespace 
function call
      * @param target the pseudo-method owner, first to-be argument
      * @param narrow whether we should attempt to narrow number arguments
-     * @param args   the other (non null) arguments
+     * @param args   the other (non-null) arguments
      * @return the arguments array
      */
     protected Object[] functionArguments(final Object target, final boolean 
narrow, final Object[] args) {
@@ -708,7 +708,7 @@ public abstract class InterpreterBase extends ParserVisitor 
{
      * Concatenate arguments in call(...).
      * @param target the pseudo-method owner, first to-be argument
      * @param narrow whether we should attempt to narrow number arguments
-     * @param args   the other (non null) arguments
+     * @param args   the other (non-null) arguments
      * @return the arguments array
      */
     protected Object[] callArguments(final Object target, final boolean 
narrow, final Object[] args) {
diff --git a/src/main/java/org/apache/commons/jexl3/internal/MapBuilder.java 
b/src/main/java/org/apache/commons/jexl3/internal/MapBuilder.java
index 57527c3c..27fd12d1 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/MapBuilder.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/MapBuilder.java
@@ -17,6 +17,7 @@
 package org.apache.commons.jexl3.internal;
 
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.Map;
 import org.apache.commons.jexl3.JexlArithmetic;
 
@@ -27,12 +28,21 @@ public class MapBuilder implements 
JexlArithmetic.MapBuilder {
     /** The map being created. */
     protected final Map<Object, Object> map;
 
+    /**
+     * Creates a new builder.
+     * @param size the expected map size
+     * @param extended whether the map is extended
+     */
+    public MapBuilder(final int size, final boolean extended) {
+        map = extended? new LinkedHashMap<>(size) : new HashMap<>(size);
+    }
+
     /**
      * Creates a new builder.
      * @param size the expected map size
      */
     public MapBuilder(final int size) {
-        map = new HashMap<>(size);
+        this(size, false);
     }
 
     @Override
diff --git a/src/main/java/org/apache/commons/jexl3/internal/SetBuilder.java 
b/src/main/java/org/apache/commons/jexl3/internal/SetBuilder.java
index 649f53e3..0b894c0d 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/SetBuilder.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/SetBuilder.java
@@ -18,6 +18,7 @@ package org.apache.commons.jexl3.internal;
 
 import org.apache.commons.jexl3.JexlArithmetic;
 import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.Set;
 
 /**
@@ -32,7 +33,16 @@ public class SetBuilder implements JexlArithmetic.SetBuilder 
{
      * @param size the expected set size
      */
     public SetBuilder(final int size) {
-        set = new HashSet<>(size);
+        this(size, false);
+    }
+
+    /**
+     * Creates a new builder.
+     * @param size the expected set size
+     * @param extended whether the set is extended
+     */
+    public SetBuilder(final int size, final boolean extended) {
+        set = extended? new LinkedHashSet<>(size) : new HashSet<>(size);
     }
 
     @Override
diff --git a/src/main/java/org/apache/commons/jexl3/parser/ASTArrayLiteral.java 
b/src/main/java/org/apache/commons/jexl3/parser/ASTArrayLiteral.java
index e17b6146..2054c131 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/ASTArrayLiteral.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/ASTArrayLiteral.java
@@ -16,18 +16,14 @@
  */
 package org.apache.commons.jexl3.parser;
 
-import org.apache.commons.jexl3.internal.Debugger;
-
 /**
  * An array literal.
  */
-public final class ASTArrayLiteral extends JexlNode {
+public final class ASTArrayLiteral extends ExtensibleNode {
     /**
      *
      */
     private static final long serialVersionUID = 1L;
-    /** Whether this array is constant or not. */
-    private boolean constant = false;
 
     ASTArrayLiteral(final int id) {
         super(id);
@@ -37,30 +33,6 @@ public final class ASTArrayLiteral extends JexlNode {
         super(p, id);
     }
 
-    @Override
-    public String toString() {
-        final Debugger dbg = new Debugger();
-        return dbg.data(this);
-    }
-
-    @Override
-    protected boolean isConstant(final boolean literal) {
-        return constant;
-    }
-
-    @Override
-    public void jjtClose() {
-        constant = true;
-        for (int c = 0; c < jjtGetNumChildren() && constant; ++c) {
-            final JexlNode child = jjtGetChild(c);
-            if (child instanceof ASTReference) {
-                constant = child.isConstant(true);
-            } else if (!child.isConstant()) {
-                constant = false;
-            }
-        }
-    }
-
     @Override
     public Object jjtAccept(final ParserVisitor visitor, final Object data) {
         return visitor.visit(this, data);
diff --git a/src/main/java/org/apache/commons/jexl3/parser/ASTMapLiteral.java 
b/src/main/java/org/apache/commons/jexl3/parser/ASTMapLiteral.java
index 696c6cc8..11907b8d 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/ASTMapLiteral.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/ASTMapLiteral.java
@@ -16,16 +16,11 @@
  */
 package org.apache.commons.jexl3.parser;
 
-import org.apache.commons.jexl3.internal.Debugger;
-
-public final class ASTMapLiteral extends JexlNode {
+public final class ASTMapLiteral extends ExtensibleNode {
     /**
      *
      */
     private static final long serialVersionUID = 1L;
-    /** Whether this array is constant or not. */
-    private boolean constant = false;
-
     ASTMapLiteral(final int id) {
         super(id);
     }
@@ -34,33 +29,8 @@ public final class ASTMapLiteral extends JexlNode {
         super(p, id);
     }
 
-    @Override
-    public String toString() {
-        final Debugger dbg = new Debugger();
-        return dbg.data(this);
-    }
-
-    @Override
-    protected boolean isConstant(final boolean literal) {
-        return constant;
-    }
-
-    @Override
-    public void jjtClose() {
-        constant = true;
-        for (int c = 0; c < jjtGetNumChildren() && constant; ++c) {
-            final JexlNode child = jjtGetChild(c);
-            if (child instanceof ASTMapEntry) {
-                constant = child.isConstant(true);
-            } else if (!child.isConstant()) {
-                constant = false;
-            }
-        }
-    }
-
     @Override
     public Object jjtAccept(final ParserVisitor visitor, final Object data) {
         return visitor.visit(this, data);
     }
-
 }
diff --git a/src/main/java/org/apache/commons/jexl3/parser/ASTSetLiteral.java 
b/src/main/java/org/apache/commons/jexl3/parser/ASTSetLiteral.java
index 59177c70..ebdda078 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/ASTSetLiteral.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/ASTSetLiteral.java
@@ -16,15 +16,11 @@
  */
 package org.apache.commons.jexl3.parser;
 
-import org.apache.commons.jexl3.internal.Debugger;
-
-public final class ASTSetLiteral extends JexlNode {
+public final class ASTSetLiteral extends ExtensibleNode {
     /**
      *
      */
     private static final long serialVersionUID = 1L;
-    /** Whether this set is constant or not. */
-    private boolean constant = false;
 
     ASTSetLiteral(final int id) {
         super(id);
@@ -34,31 +30,8 @@ public final class ASTSetLiteral extends JexlNode {
         super(p, id);
     }
 
-    @Override
-    public String toString() {
-        final Debugger dbg = new Debugger();
-        return dbg.data(this);
-    }
-
-    @Override
-    protected boolean isConstant(final boolean literal) {
-        return constant;
-    }
-
-    @Override
-    public void jjtClose() {
-        constant = true;
-        for (int c = 0; c < jjtGetNumChildren() && constant; ++c) {
-            final JexlNode child = jjtGetChild(c);
-            if (!child.isConstant()) {
-                constant = false;
-            }
-        }
-    }
-
     @Override
     public Object jjtAccept(final ParserVisitor visitor, final Object data) {
         return visitor.visit(this, data);
     }
-
 }
diff --git a/src/main/java/org/apache/commons/jexl3/parser/ExtensibleNode.java 
b/src/main/java/org/apache/commons/jexl3/parser/ExtensibleNode.java
new file mode 100644
index 00000000..a9ce4b75
--- /dev/null
+++ b/src/main/java/org/apache/commons/jexl3/parser/ExtensibleNode.java
@@ -0,0 +1,64 @@
+/*
+ * 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;
+
+import org.apache.commons.jexl3.internal.Debugger;
+
+/**
+ * Base node for array/set/map literals.
+ * <p>Captures constness and extensibility (...)</p>
+ */
+public class ExtensibleNode extends JexlNode {
+  /** Whether this array/set/map is constant or not. */
+  protected boolean constant = false;
+  /** Whether this array/set/map is extended or not. */
+  private boolean extended = false;
+
+  public ExtensibleNode(int id) {
+    super(id);
+  }
+
+  public ExtensibleNode(Parser p, int id) {
+    super(p, id);
+  }
+
+  public void setExtended(boolean e) {
+    this.extended = e;
+  }
+
+  public boolean isExtended() {
+    return extended;
+  }
+
+  @Override
+  protected boolean isConstant(final boolean literal) {
+    return constant;
+  }
+
+  @Override
+  public String toString() {
+    final Debugger dbg = new Debugger();
+    return dbg.data(this);
+  }
+
+  @Override
+  public void jjtClose() {
+    constant = super.isConstant(true);
+  }
+
+}
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 cb522f8a..655a0612 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
+++ b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
@@ -870,7 +870,8 @@ void ArrayLiteral() : {}
    (
         ExtendedLiteral()
     |
-        (Expression() (LOOKAHEAD(2) <COMMA> Expression() )*)? (<COMMA> 
ExtendedLiteral())?
+        (Expression() (LOOKAHEAD(2) <COMMA> Expression())*
+            (<COMMA> (ExtendedLiteral() { jjtThis.setExtended(true); })? )? )?
    )
    <RBRACKET>
 }
@@ -879,7 +880,8 @@ void MapLiteral() : {}
 {
     <LCURLY>
     (
-        MapEntry() ( <COMMA> MapEntry() )*
+        MapEntry() (LOOKAHEAD(2) <COMMA> MapEntry() )*
+            (<COMMA> (ExtendedLiteral(){ jjtThis.setExtended(true); })? )?
     |
         <COLON>
     ) <RCURLY>
@@ -892,7 +894,11 @@ void MapEntry() : {}
 
 void SetLiteral() : {}
 {
-    <LCURLY> (Expression() ( <COMMA> Expression() )*)? <RCURLY>
+    <LCURLY>
+    (
+    Expression() (LOOKAHEAD(2) <COMMA> Expression())*
+        (<COMMA> (ExtendedLiteral() { jjtThis.setExtended(true); } )? )?
+    )? <RCURLY>
 }
 
 
diff --git a/src/test/java/org/apache/commons/jexl3/ArrayLiteralTest.java 
b/src/test/java/org/apache/commons/jexl3/ArrayLiteralTest.java
index 0d8c5f0c..43a001f6 100644
--- a/src/test/java/org/apache/commons/jexl3/ArrayLiteralTest.java
+++ b/src/test/java/org/apache/commons/jexl3/ArrayLiteralTest.java
@@ -42,16 +42,29 @@ public class ArrayLiteralTest extends JexlTestCase {
         o = JEXL.createExpression("[...]").evaluate(jc);
         Assert.assertTrue(o instanceof List<?>);
         Assert.assertEquals(0, ((List<?>) o).size());
+        try {
+            Object ff = JEXL.createExpression("[ , ]");
+            Assert.fail(ff.toString());
+        } catch(JexlException.Parsing parsing) {
+            Assert.assertNotNull(parsing);
+        }
+        try {
+            Object ff = JEXL.createExpression("[ ... , ]");
+            Assert.fail(ff.toString());
+        } catch(JexlException.Parsing parsing) {
+            Assert.assertNotNull(parsing);
+        }
     }
 
     @Test
     public void testLiteralWithStrings() throws Exception {
-        final JexlExpression e = JEXL.createExpression("[ 'foo' , 'bar' ]");
-        final JexlContext jc = new MapContext();
-
-        final Object o = e.evaluate(jc);
         final Object[] check = {"foo", "bar"};
-        Assert.assertArrayEquals(check, (Object[]) o);
+        List<String> sources = Arrays.asList("[ 'foo' , 'bar' ]", "[ 'foo' , 
'bar', ]");
+        for(String src : sources) {
+            final JexlExpression e = JEXL.createExpression(src);
+            final Object o = e.evaluate(null);
+            Assert.assertArrayEquals(check, (Object[]) o);
+        }
     }
 
     @Test
@@ -63,16 +76,23 @@ public class ArrayLiteralTest extends JexlTestCase {
         final Object[] check = {"foo", "bar"};
         Assert.assertEquals(Arrays.asList(check), o);
         Assert.assertEquals(2, ((List<?>) o).size());
+        try {
+            JEXL.createExpression("[ 'foo' , 'bar', ... , ]");
+            Assert.fail("syntax");
+        } catch(JexlException.Parsing parsing) {
+            Assert.assertNotNull(parsing);
+        }
     }
 
     @Test
     public void testLiteralWithOneEntry() throws Exception {
-        final JexlExpression e = JEXL.createExpression("[ 'foo' ]");
-        final JexlContext jc = new MapContext();
-
-        final Object o = e.evaluate(jc);
         final Object[] check = {"foo"};
-        Assert.assertArrayEquals(check, (Object[]) o);
+        List<String> sources = Arrays.asList("[ 'foo']", "[ 'foo' , ]");
+        for(String src : sources) {
+            final JexlExpression e = JEXL.createExpression(src);
+            final Object o = e.evaluate(null);
+            Assert.assertArrayEquals(check, (Object[]) o);
+        }
     }
 
     @Test
diff --git a/src/test/java/org/apache/commons/jexl3/CollectionLiteralTest.java 
b/src/test/java/org/apache/commons/jexl3/CollectionLiteralTest.java
index 06ecc60f..1898d278 100644
--- a/src/test/java/org/apache/commons/jexl3/CollectionLiteralTest.java
+++ b/src/test/java/org/apache/commons/jexl3/CollectionLiteralTest.java
@@ -50,21 +50,21 @@ public class CollectionLiteralTest extends JexlTestCase {
             super(strict);
         }
 
-        @Override public MapBuilder mapBuilder(int size) {
-            return new CountingMapBuilder(maps, size);
+        @Override public MapBuilder mapBuilder(int size, boolean extended) {
+            return new CountingMapBuilder(maps, size, extended);
         }
-        @Override public SetBuilder setBuilder(int size) {
-            return new CountingSetBuilder(sets, size);
+        @Override public SetBuilder setBuilder(int size, boolean extended) {
+            return new CountingSetBuilder(sets, size, extended);
         }
-        @Override public ArrayBuilder arrayBuilder(int size) {
-            return new CountingArrayBuilder(arrays, size);
+        @Override public ArrayBuilder arrayBuilder(int size, boolean extended) 
{
+            return new CountingArrayBuilder(arrays, size, extended);
         }
     }
 
     static class CountingSetBuilder extends SetBuilder {
         final AtomicInteger count;
-        public CountingSetBuilder(AtomicInteger ai, int size) {
-            super(size);
+        public CountingSetBuilder(AtomicInteger ai, int size, boolean 
extended) {
+            super(size, extended);
             count = ai;
         }
         @Override public Set<?> create() {
@@ -76,8 +76,8 @@ public class CollectionLiteralTest extends JexlTestCase {
 
     static class CountingMapBuilder extends MapBuilder {
         final AtomicInteger count;
-        public CountingMapBuilder(AtomicInteger ai, int size) {
-            super(size);
+        public CountingMapBuilder(AtomicInteger ai, int size, boolean 
extended) {
+            super(size, extended);
             count = ai;
         }
         @Override public Map<Object, Object> create() {
@@ -90,8 +90,8 @@ public class CollectionLiteralTest extends JexlTestCase {
     static class CountingArrayBuilder extends ArrayBuilder {
         final AtomicInteger count;
 
-        public CountingArrayBuilder(AtomicInteger ai, int size) {
-            super(size);
+        public CountingArrayBuilder(AtomicInteger ai, int size, boolean 
extended) {
+            super(size, extended);
             count = ai;
         }
 
diff --git a/src/test/java/org/apache/commons/jexl3/Issues300Test.java 
b/src/test/java/org/apache/commons/jexl3/Issues300Test.java
index b44eb879..b1aa8277 100644
--- a/src/test/java/org/apache/commons/jexl3/Issues300Test.java
+++ b/src/test/java/org/apache/commons/jexl3/Issues300Test.java
@@ -1320,4 +1320,37 @@ public class Issues300Test {
         result = (String) script.execute(null, proxy);
         Assert.assertEquals(control, result);
     }
+
+    @Test
+    public void testUnsolvableMethod() throws Exception {
+        final JexlEngine jexl = new JexlBuilder().create();
+        JexlScript script = jexl.createScript(
+            "var myFunction1 = function(object) {"
+                + " myNonExistentFunction();"
+                + "}"
+                + "var myFunction2 = function(object) {"
+                + " myFunction1();"
+                + "}"
+                + "myFunction2();");
+        try {
+            script.execute(new MapContext());
+            Assert.fail("myNonExistentFunction() is not solvable");
+        } catch (final JexlException.Method unsolvable) {
+            Assert.assertEquals("myNonExistentFunction", 
unsolvable.getMethod());
+        }
+    }
+
+    @Test
+    public void testIssue398() {
+        String src = "let m = {\n" +
+            "  \"foo\": 1,\n" +
+            "  \"bar\": 2,\n" +
+            "}";
+        final JexlEngine jexl = new JexlBuilder().create();
+        JexlScript script = jexl.createScript(src);
+        Object result = script.execute(null);
+        Assert.assertTrue(result instanceof Map);
+        Map<?,?> map = (Map<?, ?>) result;
+        Assert.assertEquals(2, map.size());
+    }
 }
diff --git a/src/test/java/org/apache/commons/jexl3/MapLiteralTest.java 
b/src/test/java/org/apache/commons/jexl3/MapLiteralTest.java
index 3322b58f..1b5291f2 100644
--- a/src/test/java/org/apache/commons/jexl3/MapLiteralTest.java
+++ b/src/test/java/org/apache/commons/jexl3/MapLiteralTest.java
@@ -16,8 +16,10 @@
  */
 package org.apache.commons.jexl3;
 
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import org.junit.Assert;
 import org.junit.Test;
@@ -34,24 +36,31 @@ public class MapLiteralTest extends JexlTestCase {
 
     @Test
     public void testLiteralWithStrings() throws Exception {
-        final JexlExpression e = JEXL.createExpression("{ 'foo' : 'bar' }");
-        final JexlContext jc = new MapContext();
-
-        final Object o = e.evaluate(jc);
-        Assert.assertEquals(Collections.singletonMap("foo", "bar"), o);
+        List<String> sources = Arrays.asList("{ 'foo' : 'bar' }", "{ 'foo' : 
'bar', }");
+        for(String src : sources) {
+            final JexlExpression e = JEXL.createExpression(src);
+            final Object o = e.evaluate(null);
+            Assert.assertEquals(Collections.singletonMap("foo", "bar"), o);
+        }
+        try {
+            Object ff = JEXL.createExpression("{  : , }");
+            Assert.fail(ff.toString());
+        } catch(JexlException.Parsing parsing) {
+            Assert.assertNotNull(parsing);
+        }
     }
 
     @Test
     public void testLiteralWithMultipleEntries() throws Exception {
-        final JexlExpression e = JEXL.createExpression("{ 'foo' : 'bar', 'eat' 
: 'food' }");
-        final JexlContext jc = new MapContext();
-
         final Map<String, String> expected = new HashMap<>();
         expected.put("foo", "bar");
         expected.put("eat", "food");
-
-        final Object o = e.evaluate(jc);
-        Assert.assertEquals(expected, o);
+        List<String> sources = Arrays.asList("{ 'foo' : 'bar', 'eat' : 'food' 
}", "{ 'foo' : 'bar', 'eat' : 'food', }");
+        for(String src : sources) {
+            final JexlExpression e = JEXL.createExpression("{ 'foo' : 'bar', 
'eat' : 'food' }");
+            final Object o = e.evaluate(null);
+            Assert.assertEquals(expected, o);
+        }
     }
 
     @Test
diff --git a/src/test/java/org/apache/commons/jexl3/SetLiteralTest.java 
b/src/test/java/org/apache/commons/jexl3/SetLiteralTest.java
index 09993d34..9216df21 100644
--- a/src/test/java/org/apache/commons/jexl3/SetLiteralTest.java
+++ b/src/test/java/org/apache/commons/jexl3/SetLiteralTest.java
@@ -19,6 +19,7 @@ package org.apache.commons.jexl3;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 import org.junit.Assert;
 import org.junit.Test;
@@ -40,22 +41,34 @@ public class SetLiteralTest extends JexlTestCase {
 
     @Test
     public void testSetLiteralWithStrings() throws Exception {
-        final JexlExpression e = JEXL.createExpression("{ 'foo' , 'bar' }");
-        final JexlContext jc = new MapContext();
-
-        final Object o = e.evaluate(jc);
-        final Set<?> check = createSet("foo", "bar");
-        Assert.assertEquals(check, o);
+        List<String> sources = Arrays.asList("{ 'foo', 'bar' }", "{ 'foo', 
'bar', ... }", "{ 'foo', 'bar', }");
+        for(String src : sources) {
+            final JexlExpression e = JEXL.createExpression(src);
+            final JexlContext jc = new MapContext();
+
+            final Object o = e.evaluate(jc);
+            final Set<?> check = createSet("foo", "bar");
+            Assert.assertEquals(check, o);
+        }
+        try {
+            JEXL.createExpression("{ , }");
+            Assert.fail("syntax");
+        } catch(JexlException.Parsing parsing) {
+            Assert.assertNotNull(parsing);
+        }
     }
 
     @Test
     public void testLiteralWithOneEntry() throws Exception {
-        final JexlExpression e = JEXL.createExpression("{ 'foo' }");
-        final JexlContext jc = new MapContext();
-
-        final Object o = e.evaluate(jc);
-        final Set<?> check = createSet("foo");
-        Assert.assertEquals(check, o);
+        List<String> sources = Arrays.asList("{ 'foo' }", "{ 'foo', }");
+        for(String src : sources) {
+            final JexlExpression e = JEXL.createExpression(src);
+            final JexlContext jc = new MapContext();
+
+            final Object o = e.evaluate(jc);
+            final Set<?> check = createSet("foo");
+            Assert.assertEquals(check, o);
+        }
     }
 
     @Test
diff --git a/src/test/java/org/apache/commons/jexl3/internal/Util.java 
b/src/test/java/org/apache/commons/jexl3/internal/Util.java
index f49b02f8..1628aab5 100644
--- a/src/test/java/org/apache/commons/jexl3/internal/Util.java
+++ b/src/test/java/org/apache/commons/jexl3/internal/Util.java
@@ -53,7 +53,7 @@ public class Util {
             final JexlFeatures features = entry.getKey().getFeatures();
             // recreate expr from string
             try {
-                final Script exprdbg = jdbg.createScript(features, null, 
expressiondbg, null);
+                final Script exprdbg = jdbg.createScript(features, null, 
expressiondbg);
                 // make arg cause become the root cause
                 JexlNode root = exprdbg.script;
                 while (root.jjtGetParent() != null) {
diff --git 
a/src/test/java/org/apache/commons/jexl3/introspection/SandboxTest.java 
b/src/test/java/org/apache/commons/jexl3/introspection/SandboxTest.java
index 475062c9..93e6bac4 100644
--- a/src/test/java/org/apache/commons/jexl3/introspection/SandboxTest.java
+++ b/src/test/java/org/apache/commons/jexl3/introspection/SandboxTest.java
@@ -626,7 +626,7 @@ public class SandboxTest extends JexlTestCase {
             super(astrict);
         }
         @Override
-        public MapBuilder mapBuilder(final int size) {
+        public MapBuilder mapBuilder(final int size, boolean extended) {
             return mb;
         }
         Map<?,?> getLastMap() {
diff --git 
a/src/test/java/org/apache/commons/jexl3/jexl342/OptionalArithmetic.java 
b/src/test/java/org/apache/commons/jexl3/jexl342/OptionalArithmetic.java
index a3856cdb..d579e4e9 100644
--- a/src/test/java/org/apache/commons/jexl3/jexl342/OptionalArithmetic.java
+++ b/src/test/java/org/apache/commons/jexl3/jexl342/OptionalArithmetic.java
@@ -234,8 +234,8 @@ public class OptionalArithmetic extends JexlArithmetic {
     }
 
     @Override
-    public ArrayBuilder arrayBuilder(final int size) {
-        return new org.apache.commons.jexl3.internal.ArrayBuilder(size) {
+    public ArrayBuilder arrayBuilder(final int size, final boolean extended) {
+        return new org.apache.commons.jexl3.internal.ArrayBuilder(size, 
extended) {
             @Override
             public void add(Object value) {
                 super.add(star(value));
@@ -244,8 +244,8 @@ public class OptionalArithmetic extends JexlArithmetic {
     }
 
     @Override
-    public SetBuilder setBuilder(final int size) {
-        return new org.apache.commons.jexl3.internal.SetBuilder(size) {
+    public SetBuilder setBuilder(final int size, final boolean extended) {
+        return new org.apache.commons.jexl3.internal.SetBuilder(size, 
extended) {
             @Override
             public void add(Object value) {
                 super.add(star(value));
@@ -254,8 +254,8 @@ public class OptionalArithmetic extends JexlArithmetic {
     }
 
     @Override
-    public MapBuilder mapBuilder(final int size) {
-        return new org.apache.commons.jexl3.internal.MapBuilder(size) {
+    public MapBuilder mapBuilder(final int size, final boolean extended) {
+        return new org.apache.commons.jexl3.internal.MapBuilder(size, 
extended) {
             @Override
             public void put(Object key, Object value) {
                 super.put(key, star(value));


Reply via email to