1 /++
2   Common primitives used across this library
3 
4   Copyright: (C) 2017 Yuxuan Shui
5   Author: Yuxuan Shui
6 +/
7 module sdpc.primitives;
8 import std.algorithm,
9        std.stdio,
10        std.typetuple,
11        std.traits,
12        std.range;
13 
14 /**
15   Get the return types of a parser
16   Params:
17 	R = range type
18 */
19 template ParserReturnType(R) {
20 	alias ParserReturnType(alias T) = typeof(T(R.init));
21 }
22 
23 template Enumerate(T...) {
24 	struct EnumeratePair(uint xid, xT) {
25 		enum id = xid;
26 		alias T = xT;
27 		static if (!is(T == void)) {
28 			T value;
29 			alias value this;
30 		}
31 	}
32 	template E(uint start, T...) {
33 		static if (T.length == 0)
34 			alias E = AliasSeq!();
35 		else static if (T.length == 1)
36 			alias E = AliasSeq!(EnumeratePair!(start, T[0]));
37 		else {
38 			enum mid = start + T.length/2;
39 			alias E = AliasSeq!(E!(start, T[0 .. $/2]),
40 			                    E!(mid, T[$/2..$]));
41 		}
42 	}
43 	alias Enumerate = E!(0, T);
44 }
45 
46 /// Utility function for discard any kind of input data
47 void discard_any(T)(auto ref T i) { }
48 
49 /**
50   Parse result
51   Params:
52 	R = Range type
53 	E = Error type, must be copyable, non reference type
54 	T = Data type, used for returning data from parser
55  */
56 struct ParseResult(R, T = void, E = ulong)
57 if (isForwardRange!R && !is(E: R) && is(typeof(
58     {immutable(E) foo = E.init; E copy = foo;}
59 ))) {
60 @safe:
61 	/// Indicates whether the parser succeeded
62 	immutable(bool) ok;
63 
64 	private union {
65 		R r_;
66 
67 		E e_;
68 	}
69 
70 	/// The data type, for convenience 
71 	alias DataType = T;
72 
73 	alias ErrType = E;
74 
75 	version(D_Ddoc) {
76 		/// Return the data, only available if T != void,
77 		/// fails if ok != true
78 		@property T v();
79 
80 		/// ParseResult is covariant on its data type
81 		auto opCast(U: ParseResult!(R2, E2, T2), R2, E2, T2)() if (is(typeof(cast(T2)T.init)));
82 
83 		/// Create a parse result (implies ok = true)
84 		this(R r, T d);
85 
86 		/// Ditto
87 		this(R r);
88 	}
89 
90 	static if (!is(T == void)) {
91 		private T data_;
92 
93 		@property auto v() {
94 			assert(ok);
95 			return data_;
96 		}
97 
98 		auto opCast(U: ParseResult!(R, T2, E), T2)() if (is(typeof(cast(T2)T.init))) {
99 			if (i.ok)
100 				return U(r.save, cast(T2)data_);
101 			return U(e);
102 		}
103 
104 		@trusted static auto apply(alias func)(auto ref ParseResult!(R, T, E) i) if (is(typeof(func(i.data_)))){
105 			alias RT = typeof(func(i.data_));
106 			alias PR = ParseResult!(R, RT, E);
107 			if (i.ok) {
108 				static if (!is(RT == void))
109 					return PR(i.r_.save, func(i.data_));
110 				else
111 					return PR(i.r_.save);
112 			}
113 			return PR(i.e_);
114 		}
115 
116 		@trusted this(R r, T d) {
117 			this.r_ = r;
118 			this.data_ = d;
119 			this.ok = true;
120 		}
121 	} else {
122 		@trusted this(R r) {
123 			this.r_ = r;
124 			this.ok = true;
125 		}
126 	}
127 
128 	/// Get error information
129 	@property @trusted E err() in {
130 		assert(!ok);
131 	} body {
132 		return e_;
133 	}
134 
135 	/// Transform `func` that takes a `E` into a function that takes a `ParseResult`
136 	@trusted static auto apply_err(alias func)(auto ref ParseResult!(R, T, E) i) if (is(typeof(func(i.e_)))){
137 		alias RT = typeof(func(i.e_));
138 		alias PR = ParseResult!(R, T, RT);
139 		if (i.ok)
140 			return PR(i.r_.save, i.data_);
141 		return PR(func(i.e_));
142 	}
143 
144 	/// Create a parse result with error (implies ok = false)
145 	@trusted this(E e) {
146 		this.ok = false;
147 		this.e_ = e;
148 	}
149 
150 	/// Get result range, where the following parsers should continue parsing
151 	@property @trusted R cont() in {
152 		assert(ok);
153 	} body {
154 		return r_.save();
155 	}
156 
157 }
158 
159 template isParser(T, E = char) {
160 	private {
161 		import std.range.interfaces;
162 		alias R = ForwardRange!E;
163 		R rng;
164 	}
165 
166 	enum isParser = is(T == struct) && is(typeof(T(rng))) &&
167 	                is(typeof(T(rng)) == ParseResult!(S, Err, D), S, Err, D);
168 }
169 
170 /**
171   Apply `func` to the data returned by the `Parser`
172 
173   This transform `Parser` from a parser returning data type `T` to
174   a parser returning data type `typeof(func(T))`
175 */
176 struct transform(Parser, alias func) {
177 	static auto opCall(R)(R r) {
178 		alias PR = typeof(Parser(r));
179 		return PR.apply!func(Parser(r));
180 	}
181 }
182 
183 /**
184   Apply `func` to the error returned by the `Parser`
185 
186   Similar to `transform`, but operates on error instead of data
187 */
188 struct transform_err(Parser, alias func) {
189 	static auto opCall(R)(R r) {
190 		alias PR = typeof(Parser(r));
191 		return PR.apply_err!func(Parser(r));
192 	}
193 }
194 
195 ///
196 unittest {
197 	struct A {
198 		int a;
199 	}
200 	struct B {
201 		int b;
202 	}
203 	struct test01 {
204 		static auto opCall(R)(R t) if (isForwardRange!R) {
205 			return ParseResult!(R, A)(t, A(1));
206 		}
207 	}
208 
209 	alias test02 = transform!(test01, x => B(x.a+1));
210 	auto res = test02("asdf");
211 	static assert(isParser!test01);
212 	static assert(isParser!test02);
213 	static assert(is(typeof(res).DataType == B));
214 }