Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/changes/changes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@
<body>
<release version="3.7.1" date="YYYY-MM-DD" description="This is a feature and maintenance release. Java 8 or later is required.">
<!-- ADD -->
<action dev="henrib" type="add" issue="JEXL-467">IntelliJ and VSCode editors (TextMate bundle) support for JEXL.</action>
<!-- FIX -->
<action dev="henrib" type="fix" issue="JEXL-466">IllegalStateException parsing a template with string interpolation.</action>
<action dev="NikRom5531" due-to="Felix Rudolphi" type="fix" issue="JEXL-411">Leading zeroes in floating point numbers should be optional.</action>
<action dev="ggregory" type="fix" due-to="Gary Gregory">Pick up commons.jacoco.version from the parent POM.</action>
<action dev="ggregory" type="fix" due-to="Gary Gregory">Add messages when throwing NullPointerException.</action>
<!-- UPDATE -->
Expand Down
23 changes: 23 additions & 0 deletions src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -932,6 +932,29 @@ protected boolean isAmbiguousStatement(final int semicolon) {
return !getFeatures().supportsAmbiguousStatement();
}


/**
* Checks whether a token image is composed only of decimal digits.
* <p>Used to recognize a floating point literal with an omitted leading
* zero (e.g. {@code .1}) that is tokenized as {@code DOT DOT_IDENTIFIER}.</p>
*
* @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.
* <p>
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -1064,6 +1064,9 @@ void FloatLiteral() #NumberLiteral:
{
t=<FLOAT_LITERAL>
{ jjtThis.setReal(t.image); }
|
<DOT> t=<DOT_IDENTIFIER>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice trick :-)

{ if (!isAllDigits(t.image)) { throwParsingException(t); } jjtThis.setReal("." + t.image); }
}

void StringLiteral() :
Expand Down
30 changes: 30 additions & 0 deletions src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the "+.1" test for completeness.

assertEquals(0.1d, jexl.createExpression("+.1").evaluate(jc));
// property / index access via a dot must keep working (not parsed as a float)
final List<Object> array = new java.util.ArrayList<>();
array.add("zero");
array.add("one");
jc.set("array", array);
assertEquals("zero", jexl.createExpression("array.0").evaluate(jc));
assertEquals("one", jexl.createExpression("array.1").evaluate(jc));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the 0/zero test.

}
}
Loading