1 #!/usr/bin/env rdmd-dev-module
2 
3 module slicing;
4 
5 /** Slice at all positions where $(D isTerminator) is $(D false) before current
6     element and $(D true) at current.
7 
8     TODO: Can this be replaced by chunkBy
9     See also: http://dlang.org/library/std/algorithm/splitter.html.
10     See also: http://forum.dlang.org/post/cwqeywykubsuynkidlux@forum.dlang.org
11 */
12 auto preSlicer(alias isTerminator, R)(R input)
13 /* if (((isRandomAccessRange!R && */
14 /*       hasSlicing!R) || */
15 /*      isSomeString!R) && */
16 /*     is(typeof(unaryFun!isTerminator(input.front)))) */
17 {
18     import std.functional : unaryFun;
19     return PreSlicer!(unaryFun!isTerminator, R)(input);
20 }
21 
22 private struct PreSlicer(alias isTerminator, R)
23 {
24     private R _input;
25     private size_t _end = 0;
26 
27     private void findTerminator()
28     {
29         import std.range : save;
30         import std.algorithm : find;
31         auto hit = _input.save.find!(a => !isTerminator(a));
32         auto r = hit.find!isTerminator();
33         _end = _input.length - r.length;
34     }
35 
36     this(R input)
37     {
38         _input = input;
39         import std.range : empty;
40         if (_input.empty)
41         {
42             _end = size_t.max;
43         }
44         else
45         {
46             findTerminator();
47         }
48     }
49 
50     import std.range : isInfinite;
51 
52     static if (isInfinite!R)
53     {
54         enum bool empty = false;  // propagate infiniteness
55     }
56     else
57     {
58         @property bool empty()
59         {
60             return _end == size_t.max;
61         }
62     }
63 
64     @property auto front()
65     {
66         return _input[0 .. _end];
67     }
68 
69     void popFront()
70     {
71         _input = _input[_end .. _input.length];
72         import std.range : empty;
73         if (_input.empty)
74         {
75             _end = size_t.max;
76             return;
77         }
78         findTerminator();
79     }
80 
81     @property PreSlicer save()
82     {
83         auto ret = this;
84         import std.range : save;
85         ret._input = _input.save;
86         return ret;
87     }
88 }
89 alias preSplitter = preSlicer;
90 
91 unittest
92 {
93     import std.uni : isUpper;
94     import std.algorithm : equal;
95 
96     assert(equal("doThis".preSlicer!isUpper, ["do", "This"]));
97     assert(equal("doThisIf".preSlicer!isUpper, ["do", "This", "If"]));
98     assert(equal("utcOffset".preSlicer!isUpper, ["utc", "Offset"]));
99     assert(equal("isURI".preSlicer!isUpper, ["is", "URI"]));
100     // TODO assert(equal("baseSIUnit".preSlicer!isUpper, ["base", "SI", "Unit"]));
101 
102     assert(equal("SomeGreatVariableName".preSlicer!isUpper, ["Some", "Great", "Variable", "Name"]));
103     assert(equal("someGGGreatVariableName".preSlicer!isUpper, ["some", "GGGreat", "Variable", "Name"]));
104 
105     string[] e;
106     assert(equal("".preSlicer!isUpper, e));
107     assert(equal("a".preSlicer!isUpper, ["a"]));
108     assert(equal("A".preSlicer!isUpper, ["A"]));
109     assert(equal("A".preSlicer!isUpper, ["A"]));
110 
111     assert(equal([1, -1, 1, -1].preSlicer!(a => a > 0), [[1, -1], [1, -1]]));
112 
113     /* TODO Add bidir support */
114     /* import std.range : retro; */
115     /* assert(equal([-1, 1, -1, 1].retro.preSlicer!(a => a > 0), [[1, -1], [1, -1]])); */
116 }