Skip to main content

str_split_regex

Function str_split_regex 

Source
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.