1 /**
2   Combinators
3 
4   Copyright: 2017 Yuxuan Shui
5 */
6 module sdpc.combinators;
7 import sdpc.primitives;
8 import std.traits,
9        std.string,
10        std.stdio,
11        std.typetuple,
12        std.functional,
13        std.variant,
14        std.experimental.allocator;
15 import std.typecons : Tuple;
16 import std.range : ElementType;
17 
18 import std.experimental.allocator.gc_allocator : GCAllocator;
19 
20 ///Match pattern `begin func end`, return the result of func.
21 version(legacy) alias between(alias begin, alias func, alias end) = pipe!(seq!(discard!begin, func, discard!end), wrap!"move(a[1])");
22 
23 ///
24 version(legacy) unittest {
25 	import sdpc.parsers;
26 	auto i = "(asdf)";
27 	auto r = between!(token!"(", token!"asdf", token!")")(i);
28 	assert(r.ok);
29 	assert(!r.cont.length);
30 }
31 
32 /// Generate a new parser that applies `m` to outputs of parser `p`
33 template pmap(alias p, alias m) {
34 auto pmap(R)(in auto ref R i)
35 if (isForwardRange!R) {
36 	alias P = typeof(p(i));
37 	static struct Pmap {
38 		private P inner;
39 		typeof(m(inner.front)) front;
40 		@property bool empty() { return inner.empty; }
41 		@property ref auto err() { return inner.err; }
42 		@property ref R cont() { return inner.cont; }
43 		void popFront() {
44 			inner.popFront;
45 			if (!inner.empty) {
46 				front = m(inner.front);
47 			}
48 		}
49 		this(R i) {
50 			inner = p(i);
51 			front = m(inner.front);
52 		}
53 	}
54 	return Pmap(i);
55 }}
56 
57 // Produce a parser that exhaust parse `p` and folds over its outputs.
58 // if `allow_empty` is false, the result will be empty if `p` produce 0 output. Otherwise,
59 // the result will always have 1 element.
60 template pfold(alias p, alias func, alias seed, bool allow_empty = false){
61 auto pfold(R)(in auto ref R i)
62 if (isForwardRange!R) {
63 	alias InnerRT = ElementType!(typeof(p(i)));
64 	alias InnerET = typeof(p(i).err);
65 	alias RT = typeof(binaryFun!func(seed, InnerRT.init));
66 	static assert (is(typeof(seed) == RT), "return type of "~func~" should be the save as typeof(seed)");
67 	static struct Pfold {
68 		bool empty;
69 		R cont;
70 		RT front = seed;
71 		InnerET err;
72 		size_t length = 0;
73 
74 		this(R i) {
75 			import std.algorithm : fold;
76 			auto inner = p(i);
77 			empty = inner.empty && !allow_empty;
78 			// Could've used (&inner).fold here, but it fails
79 			// to compile
80 			while (!inner.empty) {
81 				front = binaryFun!func(front, inner.front);
82 				inner.popFront;
83 			}
84 			length = 1;
85 			cont = inner.cont;
86 			err = inner.err;
87 		}
88 		void popFront() { empty = true; }
89 	}
90 	return Pfold(i);
91 }}
92 
93 /// Match one of the given pattern. Remembers the matching parser.
94 /// Repeated invocation will keep invoking the parser matched the first time.
95 template choice(T...) {
96 auto choice(R)(in auto ref R i)
97 if (isForwardRange!R) {
98 	import std.range : iota;
99 	alias GetP(alias A) = typeof(A(i));
100 	alias InnerP = staticMap!(GetP, T);
101 	alias GetDT(A) = typeof(A.init.front);
102 	alias GetET(A) = typeof(A.init.err);
103 	alias PDT = staticMap!(GetDT, InnerP);
104 	alias PET = staticMap!(GetET, InnerP);
105 	static assert(allSameType!PDT);
106 	static assert(allSatisfy!(isParsingRange, InnerP));
107 
108 	static struct Choice {
109 		private immutable int chosen;
110 		private InnerP inner;
111 		bool empty;
112 		PET err;
113 		R cont;
114 
115 		this(R i) {
116 			cont = i;
117 			empty = true;
118 			foreach(id, p; T) {
119 				auto ret = p(cont);
120 				if (!ret.empty) {
121 					empty = false;
122 					cont = ret.cont;
123 					chosen = id;
124 					inner[id] = ret;
125 					return;
126 				}
127 				err[id] = ret.err;
128 			}
129 		}
130 
131 		ref PDT[0] front() {
132 			final switch(chosen) {
133 			static foreach(i; 0..T.length) {
134 			case i: {
135 				return inner[i].front;
136 			}}}
137 		}
138 		void popFront() {
139 			o:final switch(chosen) {
140 			static foreach(i; 0..T.length) {
141 			case i: {
142 				inner[i].popFront;
143 				empty = inner[i].empty;
144 				cont = inner[i].cont;
145 				break o;
146 			}}}
147 		}
148 
149 	}
150 	return Choice(i);
151 }}
152 
153 /**
154   First invocation matches `p`. Following invocations matches `delim p`
155 */
156 template delimited(alias p, alias delim) {
157 auto delimited(R)(in auto ref R i)
158 if (isForwardRange!R) {
159 	import std.typecons : Nullable, tuple, nullable;
160 	alias T = ParserReturnTypes!(R, p);
161 	alias DT = ParserReturnTypes!(R, delim);
162 	alias TE = typeof(p(i).err);
163 	alias DTE = typeof(delim(i).err);
164 	alias RDT = Tuple!(T[0], Nullable!(DT[0]));
165 	static assert(is(TE == DTE));
166 	static struct Delimited {
167 		bool empty;
168 		RDT front;
169 		R cont;
170 		TE err;
171 		this(R i) {
172 			auto ret = p(i);
173 			empty = ret.empty;
174 			cont = ret.cont;
175 			if (!ret.empty) {
176 				front = tuple(ret.front, Nullable!(DT[0]).init);
177 			} else {
178 				err = ret.err;
179 			}
180 		}
181 		void popFront() {
182 			empty = true;
183 			auto ret1 = delim(cont);
184 			cont = ret1.cont;
185 			if (ret1.empty) {
186 				err = ret1.err;
187 				return;
188 			}
189 			auto ret2 = p(cont);
190 			cont = ret2.cont;
191 			if (ret2.empty) {
192 				err = ret2.err;
193 				return;
194 			}
195 
196 			front = tuple(ret2.front, nullable(ret1.front));
197 			empty = false;
198 		}
199 	}
200 	return Delimited(i);
201 }}
202 
203 ///
204 unittest {
205 	import sdpc.parsers;
206 	import std.algorithm;
207 
208 	alias calc = pfold!(delimited!(number!(), token!"+"), "a+b[0]", 0);
209 	auto i = "1+2+3+4+5";
210 	auto r = calc(i);
211 	assert(!r.empty);
212 	assert(r.front == 15);
213 	assert(r.cont.empty);
214 }
215 
216 /**
217   Like `many_r` but with default `reduce` function that put all return
218   values into an array, or return number of matches
219 */
220 version(legacy)
221 struct many(alias func, bool allow_none = false) {
222 	static auto opCall(R)(in auto ref R i)
223 	if (isForwardRange!R) {
224 		import containers.dynamicarray : DynamicArray;
225 		import std.algorithm.mutation : move;
226 		alias PR = typeof(func(i));
227 		static if (is(PR.DataType == void))
228 			alias PDTA = void;
229 		else {
230 			alias PDTA = DynamicArray!(PR.DataType, RCIAllocator*);
231 			auto res = PDTA(&theAllocator());
232 		}
233 		bool none = true;
234 		alias RT = Result!(R, PDTA, PR.ErrType);
235 
236 		auto last_range = i.save;
237 		while(true) {
238 			auto ret = func(last_range);
239 			if (!ret.ok) {
240 				if (allow_none || !none) {
241 					static if (!is(PDTA == void))
242 						return RT(last_range, move(res));
243 					else
244 						return RT(last_range);
245 				} else
246 						return RT(ret.err);
247 			}
248 			static if (!is(PDTA == void))
249 				res ~= ret.v;
250 			none = false;
251 			last_range = ret.cont;
252 		}
253 	}
254 }
255 
256 ///
257 unittest {
258 	import sdpc.parsers;
259 	import std.array : array;
260 	auto i = "aabcdaaddcc";
261 	alias abcdparser = choice!(token!"a", token!"b", token!"c", token!"d");
262 	auto r2 = abcdparser(i);
263 	assert(!r2.empty);
264 	assert(r2.array.length == 2); // only matches the first two 'a's
265 
266 	/*
267 	i = "abcde";
268 	auto r3 = abcdparser(i);
269 	assert(r3.ok); //Parse is OK because 4 char are consumed
270 	assert(r3.v.length == 4);
271 	assert(r3.cont.length); //But the end-of-buffer is not reached
272 	*/
273 }
274 
275 /**
276   Appply a sequence of parsers one after another.
277 
278   Don't use tuple as data type in any of the parsers. The return
279   data type is a special type of tuple. To get the data of each of
280   the parsers, use `Result.v.v!index`, where `index` is the
281   index of the desired parser in `T`.
282 */
283 version(legacy)
284 struct seq(T...) {
285 	enum notVoid(T) = !is(T == void);
286 	private static string genParserCalls(int n) {
287 		import std.range : iota;
288 		import std.algorithm : map;
289 		string ret = "";
290 		foreach(i; 0..n) {
291 			ret ~= format("auto ret%s = T[%s](last_range);", i, i);
292 			ret ~= format("if(!ret%s.ok) return RT(ret%s.err);", i, i);
293 			ret ~= format("last_range = ret%s.cont;", i);
294 		}
295 
296 		auto retstr = iota(n).map!(a => format("move(ret%s.v)", a)).join(",");
297 		ret ~= "return RT(last_range, Tuple!PDT("~retstr~"));";
298 		return ret;
299 	}
300 	static auto opCall(R)(in auto ref R i)
301 	if (isForwardRange!R) {
302 		import std.algorithm : move;
303 		alias GetDT(A) = A.DataType;
304 		alias GetET(A) = A.ErrType;
305 		alias PRS = ParserReturnTypes!(R, T);
306 		alias PDT = staticMap!(GetDT, PRS);
307 		alias PET = Filter!(notVoid, staticMap!(GetET, PRS));
308 		static assert(allSameType!PET);
309 
310 		alias RT = Result!(R, Tuple!PDT, PET[0]);
311 		auto last_range = i.save;
312 		mixin(genParserCalls(T.length));
313 	}
314 }
315 
316 ///
317 version(legacy)
318 unittest {
319 	import sdpc.parsers;
320 	auto i = "abcde";
321 	auto r4 = seq!(token!"a", token!"b", token!"c", token!"d", token!"e")(i);
322 	assert(r4.ok);
323 	assert(r4.v[0] == "a");
324 	assert(r4.v[1] == "b");
325 	assert(r4.v[2] == "c");
326 	assert(r4.v[3] == "d");
327 	assert(r4.v[4] == "e");
328 
329 	auto r5 = seq!(token!"a")(i); //seq with single argument.
330 	assert(r5.ok);
331 	assert(r5.v[0] == "a");
332 }
333 
334 /** Optionally matches `p`
335 
336   Return data type will be a Nullable of `p`'s data type
337 */
338 version(legacy)
339 struct optional(alias p) {
340 	static auto opCall(R)(in auto ref R i)
341 	if (isForwardRange!R) {
342 		import std.typecons;
343 		auto r = p(i);
344 		alias PR = typeof(r);
345 		alias RT = Result!(R, Nullable!(PR.DataType), PR.ErrType);
346 		if (!r.ok)
347 			return RT(i, Nullable!(PR.DataType).init);
348 		else
349 			return RT(r.cont, nullable(r.v));
350 	}
351 }
352 
353 ///
354 version(legacy)
355 unittest {
356 	import sdpc.parsers;
357 
358 	auto i = "asdf";
359 	auto r6 = optional!(token!"x")(i);
360 	assert(r6.ok);
361 	assert(r6.v.isNull);
362 
363 	auto r7 = optional!(token!"a")(i);
364 	assert(r7.ok);
365 	assert(!r7.v.isNull);
366 }
367 
368 /// Match `u` but doesn't consume anything from the input range
369 version(legacy)
370 struct lookahead(alias u, bool negative = false){
371 	static auto opCall(R)(in auto ref R i)
372 	if (isForwardRange!R) {
373 		auto r = u(i);
374 		alias PR = typeof(r);
375 		alias RT = Result!(R, Unit, PR.ErrType);
376 		static if (negative) {
377 			if (r.ok)
378 				return RT(RT.ErrType.init);
379 			else
380 				return RT(i);
381 		} else {
382 			if (r.ok)
383 				return RT(i, []);
384 			else
385 				return RT(r.err);
386 		}
387 	}
388 }
389 
390 version(legacy)
391 struct span(alias p) {
392 	static auto opCall(R)(in auto ref R i)
393 	if (isForwardRange!R) {
394 		auto r = p(i);
395 		if (r.ok)
396 			r.span = Span(i, r.cont);
397 		return r;
398 	}
399 }
400 
401 ///
402 version(legacy)
403 unittest {
404 	import sdpc.parsers;
405 
406 	// Accept "asdf" if followed by "g"
407 	alias p = seq!(token!"asdf", lookahead!(token!"g"));
408 	auto i = "asdfg";
409 	auto r1 = p(i);
410 	assert(r1.ok);
411 	assert(r1.cont == "g", r1.cont);
412 
413 	i = "asdff";
414 	auto r2 = p(i);
415 	assert(!r2.ok);
416 }
417 
418 ///Skip `p` zero or more times
419 version(legacy)
420 alias skip(alias p) = discard_err!(discard!(many!(discard!p, true)));
421 
422 ///Match `p` but discard the result
423 version(legacy)
424 alias discard(alias p) = pipe!(p, wrap!((ref _) { }));
425 version(legacy)
426 alias discard_err(alias p) = pipe!(p, wrap_err!((ref _) { }));