Skip to content

[JEXL-411] Allow optional leading zeroes in floating point literals - #409

Merged
henrib merged 2 commits into
apache:masterfrom
NikRom5531:JEXL-411-leading-zeroes
Aug 12, 2026
Merged

[JEXL-411] Allow optional leading zeroes in floating point literals#409
henrib merged 2 commits into
apache:masterfrom
NikRom5531:JEXL-411-leading-zeroes

Conversation

@NikRom5531

Copy link
Copy Markdown
Contributor

Summary / Why

Fixing bug JEXL-411 (https://issues.apache.org/jira/browse/JEXL-411):
JEXL could not parse a floating point literal with an omitted leading
zero (e.g. ".1"), so "(1+0.1)*2" evaluated to 2.2 while "(1+.1)*2"
raised a parsing error, although ".1" is valid in Java.

How

A leading-dot number is tokenized as "DOT DOT_IDENTIFIER", which is
identical to the postfix index access "x.3" and cannot be told apart at
the lexer level. The literal is therefore recognized in the parser, in
operand position. FloatLiteral() in Parser.jjt now also accepts
"." (only all-digit DOT_IDENTIFIER images, checked via a new
isAllDigits guard) and rebuilds the value as "." + image before
handing it to NumberParser. Dot-based index/property access is
parsed first by MemberAccess/IdentifierAccess and left untouched.

Tests

ArithmeticTest#testLeadingDotFloatLiteral:

  • "(1+.1)*2" == 2.2 (JEXL-411 reproduction)
  • "(1+0.1)*2" == 2.2 (regression)
  • ".1" == 0.1, "-.1" == -0.1, ".5 + .5" == 1.0, ".55" == 0.55
  • unary/compound: "-.1+.2" == 0.1, ".1+-.2" == -0.1, ".5 - -.9" == 1.4
  • "array.1" still resolves to index access (regression)
  • The test fails on the previous grammar and passes on this one.

Verification

  • mvn (incl. apache-rat license check): BUILD SUCCESS
  • mvn test: 1164 tests, 0 failures, 0 errors

Notes

  • The JIRA account was read-only, so a PR link could not be added to the
    ticket; the issue is referenced via the key in the branch, commits and
    this description.

Thanks for your contribution to Apache Commons! Your help is appreciated!

Before you push a pull request, review this list:

  • Read the contribution guidelines for this project.
  • Read the ASF Generative Tooling Guidance if you use Artificial Intelligence (AI).
  • I used AI to create any part of, or all of, this pull request. Which AI tool was used to create this pull request, and to what extent did it contribute?
  • Run a successful build using the default Maven goal with mvn; that's mvn on the command line by itself.
  • Write unit tests that match behavioral changes, where the tests fail if the changes to the runtime are not applied. This may not always be possible, but it is a best practice.
  • Write a pull request description that is detailed enough to understand what the pull request does, how, and why.
  • Each commit in the pull request should have a meaningful subject line and body. Note that a maintainer may squash commits during the merge process.

JEXL could not parse a floating point literal with an omitted leading
zero (e.g. ".1"), so "(1+0.1)*2" evaluated to 2.2 while "(1+.1)*2"
raised a parsing error, although ".1" is valid in Java.

A leading-dot number is tokenized as "DOT DOT_IDENTIFIER", which is
identical to the postfix index access "x.3". The literal is therefore
recognized in the parser, in operand position, rather than at the
lexer level. FloatLiteral() now also accepts ".<digits>" (only
all-digit DOT_IDENTIFIER images, checked via isAllDigits) and rebuilds
the value as "." + image before handing it to NumberParser. Dot-based
index/property access is left untouched.

Add an arithmetic test covering the JEXL-411 reproduction, leading-dot
literals, unary sign and the index-access regression.
@garydgregory
garydgregory requested a lite review from Copilot August 12, 2026 12:26
@garydgregory garydgregory changed the title JEXL-411: Allow optional leading zeroes in floating point literals [JEXL-411] Allow optional leading zeroes in floating point literals Aug 12, 2026
@henrib
henrib self-requested a review August 12, 2026 12:27

@henrib henrib left a comment

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.

Good thinking, nicely addressed.

* @param image the token image
* @return true if every character is a decimal digit
*/
private static boolean isAllDigits(final String image) {

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.

The method might move to JexlParser since all helpers live there.

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 :-)

array.add("zero");
array.add("one");
jc.set("array", array);
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.

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.

@henrib

henrib commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

I pushed an additional commit to apache/commons-jexl:JEXL-411-leading-zeroes (commit a4b5751a: "JEXL-411: move isAllDigits to JexlParser; - update changes.xml;") but was unable to push it directly to your fork. Could you pull it into your branch?

git remote add upstream https://github.com/apache/commons-jexl.git
git fetch upstream JEXL-411-leading-zeroes
git cherry-pick a4b5751a
git push origin JEXL-411-leading-zeroes

@henrib
henrib merged commit 9b0c586 into apache:master Aug 12, 2026
16 of 17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants