Skip to content

Commit

Permalink
Merge pull request #4174 from JackStouffer/issue15797
Browse files Browse the repository at this point in the history
[Issue 15797] Add std.regex.splitterWithMatches
  • Loading branch information
DmitryOlshansky committed Apr 12, 2016
2 parents 93e4043 + 02e11ab commit 6c0dbab
Showing 1 changed file with 76 additions and 17 deletions.
93 changes: 76 additions & 17 deletions std/regex/package.d
Expand Up @@ -254,6 +254,7 @@ module std.regex;
import std.regex.internal.ir;
import std.regex.internal.thompson; //TODO: get rid of this dependency
import std.exception, std.traits, std.range;
import std.typecons; // : Flag, Yes, No;

/++
$(D Regex) object holds regular expression pattern in compiled form.
Expand Down Expand Up @@ -1327,11 +1328,17 @@ public R replace(alias fun, R, RegEx)(R input, RegEx re)
return replaceAllWith!(fun, match)(input, re);
}

/++
Range that splits a string using a regular expression as a
separator.
+/
public struct Splitter(Range, alias RegEx = Regex)
/**
Splits a string `r` using a regular expression `pat` as a separator.
Params:
keepSeparators = flag to specify if the matches should be in the resulting range
r = the string to split
pat = the pattern to split on
Returns:
A lazy range of strings
*/
public struct Splitter(Flag!"keepSeparators" keepSeparators = No.keepSeparators, Range, alias RegEx = Regex)
if(isSomeString!Range && isRegexFor!(RegEx, Range))
{
private:
Expand All @@ -1340,6 +1347,8 @@ private:
alias Rx = typeof(match(Range.init,RegEx.init));
Rx _match;

static if (keepSeparators) bool onMatch = false;

@trusted this(Range input, RegEx separator)
{//@@@BUG@@@ generated opAssign of RegexMatch is not @trusted
_input = input;
Expand All @@ -1352,6 +1361,10 @@ private:
else
{
_match = Rx(_input, separator);

static if (keepSeparators)
if (_match.pre.empty)
popFront();
}
}

Expand All @@ -1368,13 +1381,27 @@ public:

assert(!empty && _offset <= _match.pre.length
&& _match.pre.length <= _input.length);
return _input[_offset .. min($, _match.pre.length)];

static if (keepSeparators)
{
if (!onMatch)
return _input[_offset .. min($, _match.pre.length)];
else
return _match.hit();
}
else
{
return _input[_offset .. min($, _match.pre.length)];
}
}

///ditto
@property bool empty()
{
return _offset > _input.length;
static if (keepSeparators)
return _offset >= _input.length;
else
return _offset > _input.length;
}

///ditto
Expand All @@ -1388,9 +1415,27 @@ public:
}
else
{
//skip past the separator
_offset = _match.pre.length + _match.hit.length;
_match.popFront();
static if (keepSeparators)
{
if (!onMatch)
{
//skip past the separator
_offset = _match.pre.length;
}
else
{
_offset += _match.hit.length;
_match.popFront();
}

onMatch = !onMatch;
}
else
{
//skip past the separator
_offset = _match.pre.length + _match.hit.length;
_match.popFront();
}
}
}

Expand All @@ -1401,14 +1446,12 @@ public:
}
}

/**
A helper function, creates a $(D Splitter) on range $(D r) separated by regex $(D pat).
Captured subexpressions have no effect on the resulting range.
*/
public Splitter!(Range, RegEx) splitter(Range, RegEx)(Range r, RegEx pat)
if(is(BasicElementOf!Range : dchar) && isRegexFor!(RegEx, Range))
/// ditto
public Splitter!(keepSeparators, Range, RegEx) splitter(
Flag!"keepSeparators" keepSeparators = No.keepSeparators, Range, RegEx)(Range r, RegEx pat) if (
is(BasicElementOf!Range : dchar) && isRegexFor!(RegEx, Range))
{
return Splitter!(Range, RegEx)(r, pat);
return Splitter!(keepSeparators, Range, RegEx)(r, pat);
}

///
Expand All @@ -1420,6 +1463,22 @@ unittest
["", "abc", "de", "fg", "hi", ""]));
}

/// Split on a pattern, but keep the matches in the resulting range
unittest
{
import std.algorithm.comparison : equal;

auto pattern = regex(`([\.,])`);

assert("2003.04.05"
.splitter!(Yes.keepSeparators)(pattern)
.equal(["2003", ".", "04", ".", "05"]));

assert(",1,2,3"
.splitter!(Yes.keepSeparators)(pattern)
.equal([",", "1", ",", "2", ",", "3"]));
}

///An eager version of $(D splitter) that creates an array with splitted slices of $(D input).
public @trusted String[] split(String, RegEx)(String input, RegEx rx)
if(isSomeString!String && isRegexFor!(RegEx, String))
Expand Down

0 comments on commit 6c0dbab

Please sign in to comment.