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 _) { }));