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 }