Skip to content

fix: trailing decimal point drops the following unit - #74

Merged
dy merged 1 commit into
jkroso:masterfrom
afonsojanu:fix/trailing-decimal-point-drops-unit
Sep 28, 2026
Merged

dy merged 1 commit into
jkroso:masterfrom
afonsojanu:fix/trailing-decimal-point-drops-unit

Conversation

@afonsojanu

Copy link
Copy Markdown
Contributor

The number regex's decimal branch is \d+(?:\.\d+)?, which requires at least one digit after a decimal point. If a number is written with a trailing dot and no fraction digits (5.), that branch fails, so only the bare digits (5) match and the dot is left dangling. Since the unit capture group has to sit immediately after the number match, the dot in between breaks the adjacency and the unit gets captured as empty. The term then falls back to the base format (ms) instead of the unit that was actually written.

parse('5 seconds')   // 5000, as expected
parse('5. seconds')  // 5   <- wrong, silently treated as "5ms"
parse('5.seconds')   // 5   <- same problem, no space needed to trigger it

There's already a test for this shape ('+0. secs' => 0), but it uses a zero value, so it can't tell the difference between the unit being applied correctly and the unit being dropped, since 0 * anything is 0 either way. That's what let this slip through.

Fix: let the fractional part of the digit-first branch match zero digits (\d+(?:\.\d*)?), so a trailing . is consumed as part of the number instead of left over to break the unit match. The lone-dot branch (\.\d+, for .5-style values) is untouched, so a bare . on its own still isn't treated as a number.

Added a test block covering both a spaced and unspaced trailing dot, plus a negative value, and confirmed it fails against the old regex and passes with the fix. Full existing suite (102 cases) still passes, now 106 with the new ones.

A number like "5." (integer part followed by a bare decimal point with
no fraction digits) failed the number regex's decimal branch, which
requires at least one digit after the dot. The engine then matched
just "5" with an empty unit capture, so the term fell back to the
base format (ms) instead of picking up the unit that followed the
dot.

parse('5. seconds') returned 5 instead of 5000, silently disagreeing
with parse('5 seconds'). The existing test for this shape used a
zero value ('+0. secs' => 0), which passes either way and hid the bug.

Made the fractional part of the digit-first branch accept zero
digits after the dot, so "5." is consumed as part of the number and
the trailing unit is captured correctly. Added regression tests
covering a space and no space before the unit, and a negative value.
@dy
dy merged commit 6e4a57c into jkroso:master Sep 28, 2026
3 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