1 /++ 2 Common primitives used across this library 3 4 Copyright: 2017 Yuxuan Shui 5 +/ 6 module sdpc.primitives; 7 import std.algorithm, 8 std.stdio, 9 std.typetuple, 10 std.traits, 11 std.functional, 12 std.variant; 13 import std.range : isInputRange, ElementType; 14 15 /// The unit type 16 alias Unit = byte[0]; 17 18 /// Convert void to Unit 19 template unitizeType(T) { 20 static if (is(T == void)) 21 alias Unitize = Unit; 22 else 23 alias Unitize = T; 24 } 25 26 /// Convert a function that returns void, to function that returns Unit 27 template unitizeFunc(func...) if (func.length == 1) { 28 pragma(inline) auto unitizeFunc(Args...)(Args args) if (is(typeof(func[0](args)))) { 29 static if (is(typeof(func[0](args)) == void)) { 30 func[0](args); 31 return Unit.init; 32 } else { 33 return func[0](args); 34 } 35 } 36 } 37 38 unittest { 39 void testfn() {} 40 41 pragma(msg, typeof(unitizeFunc!testfn())); 42 static assert(!is(typeof(unitizeFunc!testfn()) == void)); 43 } 44 /** 45 Get the return types of a parser 46 Params: 47 R = range type 48 */ 49 template ParserReturnTypes(R, T...) { 50 static if (T.length == 1) 51 alias ParserReturnTypes = AliasSeq!(ElementType!(typeof(T[0](R.init)))); 52 else 53 alias ParserReturnTypes = AliasSeq!(ParserReturnTypes!(R, T[0..$/2]), ParserReturnTypes!(R, T[$/2..$])); 54 } 55 56 // relax requirement of forward range 57 enum bool isForwardRange(R) = isInputRange!R 58 && is(Unqual!(ReturnType!((R r) => r.save)) == Unqual!R); 59 60 /// Whether a range is a parsing range. In addition to an input range, 61 /// a parsing range also has a memeber `err` representing parsing errors, 62 /// and a member `cont` representing its position in the input. 63 enum bool isParsingRange(R) = isInputRange!R 64 && is(typeof(R.init.err)) && isInputRange!(typeof(R.init.cont)); 65 66 /** 67 Utility function for generate a tuple of `EnumeratePair` from an 68 input tuple. Each `EnumeratePair` will have an index attached to it 69 */ 70 template staticEnumerate(T...) { 71 struct EnumeratePair(uint xid, xT) { 72 enum id = xid; 73 alias T = xT; 74 static if (!is(T == void)) { 75 T value; 76 //alias value this; 77 } 78 } 79 template E(uint start, T...) { 80 static if (T.length == 0) 81 alias E = AliasSeq!(); 82 else static if (T.length == 1) 83 alias E = AliasSeq!(EnumeratePair!(start, T[0])); 84 else { 85 enum mid = start + T.length/2; 86 alias E = AliasSeq!(E!(start, T[0 .. $/2]), 87 E!(mid, T[$/2..$])); 88 } 89 } 90 alias staticEnumerate = E!(0, T); 91 } 92 93 /// 94 unittest { 95 alias T = AliasSeq!(int, long, float); 96 // (EnumeratePair!(0u, int), EnumeratePair!(1u, long), EnumeratePair!(2u, float)) 97 alias T2 = staticEnumerate!T; 98 foreach(id, EP; T2) { 99 static assert(id == EP.id); 100 static assert(is(EP.T == T[id])); 101 } 102 } 103 104 struct Span { 105 int begin_row, begin_col; 106 int end_row, end_col; 107 @safe this(R)(in auto ref R start, in auto ref R end) { 108 static if (hasPosition!R) { 109 assert(end.row >= start.row); 110 assert(start.col <= end.col || start.row < end.row); 111 begin_col = start.col; 112 begin_row = start.row; 113 end_row = end.row; 114 end_col = end.col; 115 } 116 } 117 } 118 119 interface ICache(R) if (isForwardRange!R) { 120 } 121 122 import std.experimental.allocator.gc_allocator : GCAllocator; 123 class Cache(R, Allocator=GCAllocator) : ICache!R { 124 125 } 126 127 /// Keep track of line and column 128 struct PositionRange(R) { 129 pure: 130 import std.range : ElementType; 131 R r; 132 int row = 1, col = 1; 133 bool empty() const { return r.empty; } 134 auto front() const { return r.front; } 135 void popFront() { 136 import std.ascii; 137 if (front == '\n') { 138 row++; 139 col = 1; 140 } else 141 col++; 142 r.popFront; 143 } 144 auto save() const { 145 return PositionRange(r.save, row, col); 146 } 147 } 148 149 struct PositionRangeNonWhite(R) if (isSomeChar!(ElementType!R)) { 150 import std.range : ElementType; 151 R r; 152 int row = 1, col = 1; 153 private int real_row = 1, real_col = 1; 154 bool empty() const { return r.empty; } 155 auto front() const { return r.front; } 156 void popFront() { 157 import std.ascii; 158 if (front == '\n') { 159 real_row++; 160 real_col = 1; 161 } else 162 real_col++; 163 r.popFront; 164 if (!empty && !front.isWhite) { 165 row = real_row; 166 col = real_col; 167 } 168 } 169 auto save() const { 170 auto ret = PositionRangeNonWhite(r.save, row, col, real_row, real_col); 171 return ret; 172 } 173 } 174 175 template hasPosition(R) { 176 enum hasPosition = is(typeof(R.init.col)) && is(typeof(R.init.row)); 177 } 178 179 auto with_position(R)(R i) { 180 return PositionRange!R(i); 181 } 182 183 inout(R)[] save(R)(inout(R[]) i) { 184 return i; 185 } 186 187 public import std.range.primitives : popFront, front, empty; 188 189 /// 190 version(legacy) 191 unittest { 192 PositionRange!string a; 193 import std.functional; 194 struct A { 195 int a; 196 } 197 struct B { 198 int b; 199 } 200 struct test01 { 201 static auto opCall(R)(R t) if (isForwardRange!R) { 202 return Result!(R, A)(t, A(1)); 203 } 204 } 205 206 alias test02 = pipe!(test01, wrap!(x => B(x.a+1))); 207 auto res = test02("asdf"); 208 static assert(is(typeof(res).DataType == B)); 209 }