pub fn str_split_regex(
s: &str,
re_val: &Value,
limit: Option<usize>,
) -> Result<Value, String>Expand description
str.split(re[, limit]): split on regex matches; captured groups are spliced
into the output (JS semantics).
String.prototype.split(regexp[, limit]) — 22.2.6.14 RegExp.prototype [@@split].
The previous shape of this was a captures_iter with one hand-rolled
special case for a zero-width match at position 0, and it got every other
empty-match position wrong: 'ab'.split(/(?:)/) grew a trailing "",
''.split(/(?:)/) answered [""] instead of [], and 'ab'.split(/()/)
answered five elements instead of three.
The spec loop is what gets those right, and the single rule doing the work
is e == p: a match ENDING where the previous piece began contributes
nothing and only advances the scan. The final piece is always the tail from
p, appended after the loop — which is why an empty match at the very end
does not produce an extra "" (the loop stops at q == size before ever
matching there) while a real separator at the end does.
The scan is in UTF-16 code units, since that is what the spec indexes and
what .index/lastIndex report elsewhere in this file. Where the spec
advances q one position at a time until a match occurs AT q, this jumps
straight to the next match at or after q: every position skipped is one
the spec would have failed to match, so the two agree.
One divergence remains and is not fixable here: a lone surrogate cannot be
represented in a Rust String, so a split position INSIDE an astral
character does not exist to be split at. '\u{1F600}a'.split(/(?:)/) is
['\u{1F600}', 'a'] here and three elements in node, which splits the
surrogate pair. '\u{1F600}'.split('') has always had the same limit; it
is the string representation, not this algorithm.