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 }