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 }