Repository: sanctuary-js/sanctuary (js)

Issue:
Add splitOnRegex 
Create a regex version of [splitOn](https://github.com/sanctuary-js/sanctuary#spliton--string---string---arraystring).

Pull request description:
string: add S.splitOnRegex
Closes #339 

Would appreciate some feedback please, especially on the description.

Patch:
diff --git a/index.js b/index.js
index 1f4d9de..c8467ef 100644
--- a/index.js
+++ b/index.js
@@ -4163,7 +4163,7 @@
   //. Returns the substrings of its second argument separated by occurrences
   //. of its first argument.
   //.
-  //. See also [`joinWith`](#joinWith).
+  //. See also [`joinWith`](#joinWith) and [`splitOnRegex`](#splitOnRegex).
   //.
   //. ```javascript
   //. > S.splitOn('::', 'foo::bar::baz')
@@ -4175,6 +4175,49 @@
   S.splitOn =
   def('splitOn', {}, [$.String, $.String, $.Array($.String)], splitOn);
 
+  //# splitOnRegex :: GlobalRegExp -> String -> Array String
+  //.
+  //. Takes a pattern and a string, and returns the result of splitting the
+  //. string at every non-overlapping occurrence of the pattern.
+  //.
+  //. Properties:
+  //.
+  //.   - `forall s :: forall s :: String, t :: String.
+  //.     S.joinWith(s, S.splitOnRegex(S.regex('g', S.regexEscape(s)), t)) = t
+  //.
+  //. See also [`splitOn`](#splitOn).
+  //.
+  //. ```javascript
+  //. > S.splitOnRegex(/[,;][ ]*/g, 'foo, bar, baz')
+  //. ['foo', 'bar', 'baz']
+  //.
+  //. > S.splitOnRegex(/[,;][ ]*/g, 'foo;bar;baz')
+  //. ['foo', 'bar', 'baz']
+  //. ```
+  function splitOnRegex(pattern, s) {
+    return withRegex(pattern, function() {
+      var result = [];
+      var lastIndex = 0;
+      var match;
+      while ((match = pattern.exec(s)) != null) {
+        if (pattern.lastIndex === lastIndex && match[0] === '') {
+          if (pattern.lastIndex === s.length) return result;
+          pattern.lastIndex += 1;
+        } else {
+          result.push(s.slice(lastIndex, match.index));
+          lastIndex = match.index + match[0].length;
+        }
+      }
+      result.push(s.slice(lastIndex));
+      return result;
+    });
+  }
+  S.splitOnRegex =
+  def('splitOnRegex',
+      {},
+      [$.GlobalRegExp, $.String, $.Array($.String)],
+      splitOnRegex);
+
   return S;
 
   /* eslint-enable indent */


Test patch:
diff --git a/test/splitOnRegex.js b/test/splitOnRegex.js
new file mode 100644
index 0000000..040e297
--- /dev/null
+++ b/test/splitOnRegex.js
@@ -0,0 +1,59 @@
+'use strict';
+
+var jsc = require('jsverify');
+
+var S = require('..');
+
+var eq = require('./internal/eq');
+
+
+test('splitOnRegex', function() {
+
+  eq(typeof S.splitOnRegex, 'function');
+  eq(S.splitOnRegex.length, 2);
+  eq(S.splitOnRegex.toString(), 'splitOnRegex :: GlobalRegExp -> String -> Array String');
+
+  eq(S.splitOnRegex(/b/g, 'abc'), ['a', 'c']);
+  eq(S.splitOnRegex(/d/g, 'abc'), ['abc']);
+  eq(S.splitOnRegex(/b(?=c)/g, 'abc abd'), ['a', 'c abd']);
+  eq(S.splitOnRegex(/b(?!c)/g, 'abc abd'), ['abc a', 'd']);
+
+  eq(S.splitOnRegex(/\s+/g, 'foo  bar baz   qux'), ['foo', 'bar', 'baz', 'qux']);
+  eq(S.splitOnRegex(/\s+/g, 'foo  bar baz\tqux\nquux'), ['foo', 'bar', 'baz', 'qux', 'quux']);
+  eq(S.splitOnRegex(/\s+/g, 'foobar'), ['foobar']);
+  eq(S.splitOnRegex(/([:;])\1/g, 'foo::bar:;baz;;quux'), ['foo', 'bar:;baz', 'quux']);
+
+  eq(S.splitOnRegex(/./g, 'a\rb\nc'), ['', '\r', '\n', '']);
+  eq(S.splitOnRegex(/[^]/g, 'a\nb'), ['', '', '', '']);
+
+  eq(S.splitOnRegex(/^/g, 'foo\nbar\nbaz\n'), ['foo\nbar\nbaz\n']);
+  eq(S.splitOnRegex(/^/gm, 'foo\nbar\nbaz\n'), ['foo\n', 'bar\n', 'baz\n']);
+
+  eq(S.splitOnRegex(/foo/g, 'FOObar'), ['FOObar']);
+  eq(S.splitOnRegex(/foo/gi, 'FOObar'), ['', 'bar']);
+
+  eq(S.splitOnRegex(/(?:)/g, ''), []);
+  eq(S.splitOnRegex(/(?:)/g, 'a'), ['a']);
+  eq(S.splitOnRegex(/(?:)/g, 'ab'), ['a', 'b']);
+  eq(S.splitOnRegex(/(?:)/g, 'abc'), ['a', 'b', 'c']);
+
+  eq(S.splitOnRegex(/[^]/g, ''), ['']);
+  eq(S.splitOnRegex(/[^]/g, 'a'), ['', '']);
+  eq(S.splitOnRegex(/[^]/g, 'ab'), ['', '', '']);
+  eq(S.splitOnRegex(/[^]/g, 'abc'), ['', '', '', '']);
+
+  eq(S.splitOnRegex(/.*/g, ''), []);
+  eq(S.splitOnRegex(/.*/g, 'hello'), ['']);
+  eq(S.splitOnRegex(/./g, ''), ['']);
+  eq(S.splitOnRegex(/./g, 'hello'), ['', '', '', '', '', '']);
+
+  jsc.assert(jsc.forall(jsc.asciistring, function(t) {
+    var min = 0;
+    var max = t.length;
+    var i = jsc.random(min, max);
+    var j = jsc.random(min, max);
+    var s = t.slice(Math.min(i, j), Math.max(i, j));
+    return S.joinWith(s, S.splitOnRegex(S.regex('g', S.regexEscape(s)), t)) === t;
+  }), {tests: 1000});
+
+});