+ IntelliJ and VSCode editors (TextMate bundle) support for JEXL.
+ IllegalStateException parsing a template with string interpolation.
+ Leading zeroes in floating point numbers should be optional.Pick up commons.jacoco.version from the parent POM.Add messages when throwing NullPointerException.
diff --git a/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java b/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
index d79ccca62..b01ca0ba7 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
@@ -932,6 +932,29 @@ protected boolean isAmbiguousStatement(final int semicolon) {
return !getFeatures().supportsAmbiguousStatement();
}
+
+ /**
+ * Checks whether a token image is composed only of decimal digits.
+ *
Used to recognize a floating point literal with an omitted leading
+ * zero (e.g. {@code .1}) that is tokenized as {@code DOT DOT_IDENTIFIER}.
+ *
+ * @param image the token image
+ * @return true if every character is a decimal digit
+ */
+ protected boolean isAllDigits(final String image) {
+ int len = image != null ? image.length() : 0;
+ if (len == 0) {
+ return false;
+ }
+ for (int i = 0; i < len; ++i) {
+ final char c = image.charAt(i);
+ if (c < '0' || c > '9') {
+ return false;
+ }
+ }
+ return true;
+ }
+
/**
* Called by parser at end of node construction.
*
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 2d9eb1a20..7105234ec 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
+++ b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
@@ -1064,6 +1064,9 @@ void FloatLiteral() #NumberLiteral:
{
t=
{ jjtThis.setReal(t.image); }
+ |
+ t=
+ { if (!isAllDigits(t.image)) { throwParsingException(t); } jjtThis.setReal("." + t.image); }
}
void StringLiteral() :
diff --git a/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java b/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
index 09433b28b..2893f0b3e 100644
--- a/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
+++ b/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
@@ -2341,4 +2341,34 @@ void setOptions(final JexlOptions options) {
this.options = options;
}
}
+
+ @Test void testLeadingDotFloatLiteral() {
+ final JexlEngine jexl = new JexlBuilder().create();
+ final JexlContext jc = new MapContext();
+ // the exact case from JEXL-411
+ assertEquals(2.2d, jexl.createExpression("(1+.1)*2").evaluate(jc));
+ // the regression case that already worked
+ assertEquals(2.2d, jexl.createExpression("(1+0.1)*2").evaluate(jc));
+ // standalone and compound leading-dot literals
+ assertEquals(0.1d, jexl.createExpression(".1").evaluate(jc));
+ assertEquals(1.0d, jexl.createExpression(".5 + .5").evaluate(jc));
+ assertEquals(0.55d, jexl.createExpression(".55").evaluate(jc));
+ assertEquals(0.1d, jexl.createExpression("-.1 + .2").evaluate(jc));
+ assertEquals(-0.1d, jexl.createExpression(".1 + -.2").evaluate(jc));
+ assertEquals(0.1d, jexl.createExpression("-.1+.2").evaluate(jc));
+ assertEquals(-0.1d, jexl.createExpression(".1+-.2").evaluate(jc));
+ assertEquals(1.4d, jexl.createExpression(".5 - -.9").evaluate(jc));
+ assertEquals(0.4d, jexl.createExpression("-.5 - -.9").evaluate(jc));
+ assertEquals(-1.4d, jexl.createExpression("-.5+-.9").evaluate(jc));
+ // unary handling
+ assertEquals(-0.1d, jexl.createExpression("-.1").evaluate(jc));
+ assertEquals(0.1d, jexl.createExpression("+.1").evaluate(jc));
+ // property / index access via a dot must keep working (not parsed as a float)
+ final List