1 /// Module for easily making parsers.
2 ///
3 /// Can be used for both lexing and parsing grammars.
4 module rcdata.parser;
5 
6 import std.range;
7 import std.traits;
8 import std.exception;
9 
10 import rcdata.utils;
11 
12 // TODO for each matcher, write a set of tests with no captures, one capture and multiple captures, mix them all too!
13 
14 
15 /// Check if the given function is a valid parser data supplier.
16 enum isMatchDataSupplier(Input, alias supply) = __traits(compiles, MatchData!(Input, supply));
17 
18 /// Check if given struct has a default constructor. True for other types.
19 enum hasDefaultConstructor(T) = __traits(compiles, {
20     T t;
21 });
22 
23 /// Get the result type of the given supplier.
24 template MatchData(Input, alias supply) {
25 
26     import std.range;
27 
28     auto supplierResultA() { return supply(Input.init.take(1)); }
29     auto supplierResultB() { return supply(supplierResultA, supplierResultA); }
30 
31     alias SupplierResultA = typeof(supplierResultA());
32     alias SupplierResultB = typeof(supplierResultB());
33 
34     alias MatchData = CommonType!(SupplierResultA, SupplierResultB);
35 
36     static assert(!is(MatchData == void),
37         "Return types of supply(" ~ Take!Input.stringof ~ ") (" ~ SupplierResultA.stringof ~ ")"
38             ~ " and supply(supply(...), supply(...)) (" ~ SupplierResultB.stringof ~ ")"
39             ~ " are not compatible");
40 
41 }
42 
43 /// Result of the match.
44 /// Params:
45 ///     Input  = Input range used by the parser.
46 ///     supply = Function supplying user data such as tokens.
47 ///     Ts     = Optional types for additional values to hold within the match, used to add result data specific to
48 ///         a matcher. If provided, the match can be implicitly casted to the original match type.
49 struct MatchImpl(Input, alias supply, Ts...) {
50 
51     alias Data = MatchData!(Input, supply);
52     alias ParserException = ParserExceptionImpl!(Input, supply);
53     alias Match = typeof(this);
54     alias BaseMatch = MatchImpl!(Input, supply);
55 
56     /// Types of the captured values.
57     alias Capture = Ts;
58 
59     static assert(hasDefaultConstructor!Data, Data.stringof ~ " must support default construction");
60     static assert(hasDefaultConstructor!(Take!Input), Take!Input.stringof ~ " must support default construction");
61 
62 
63     /// If **not null**, the match failed with this message. Empty, but non-null, strings also count as successful
64     /// matches. Use the Match directly as a boolean to see.
65     string error;
66 
67     /// Result of the `supply` function.
68     Data data;
69 
70     /// Source matched by the result. In case of failure, this is an empty range, but `matched.source` can still be
71     /// used, and will contain the source range starting at the point of the match start.
72     Take!Input matched;
73 
74     /// Additional data added by a specific matcher function.
75     Ts capture;
76 
77     // Holding an additional value, make the struct implicitly convert into its base
78     static if (Ts.length != 0) {
79 
80         alias base this;
81 
82     }
83 
84     invariant {
85 
86         assert(error is null || matched.maxLength == 0, "`matched` must be empty if there's an error");
87 
88     }
89 
90 
91     /// Match succeeded. Calls `supply`.
92     auto this(Input source, size_t consumed, Ts values, string filename = __FILE__, size_t line = __LINE__) {
93 
94         this.matched = source.take(consumed);
95         this.data = supply(this.matched);
96         this.capture = values;
97 
98     }
99 
100     /// Match succeeded, but pass the data directly instead of calling `supply`. Useful for altering existing matches.
101     this(Take!Input matched, Data data, Ts values, string filename = __FILE__, size_t line = __LINE__) nothrow pure @safe @nogc {
102 
103         this.matched = matched;
104         this.data = data;
105         this.capture = values;
106 
107     }
108 
109     private this(inout typeof(this.tupleof) args) inout nothrow pure @safe @nogc {
110 
111         this.tupleof = args;
112 
113     }
114 
115     static ok(Input source, size_t consumed, Ts values) {
116 
117         return Match(source, consumed, values);
118 
119     }
120 
121     static ok(Take!Input matched, Data data, Ts values) {
122 
123         return Match(matched, data, values);
124 
125     }
126 
127     /// Match failed.
128     ///
129     /// It's recommended not to use runtime formatted strings for match errors, to avoid constant GC allocations during
130     /// runtime. Formatting should be limited to critical errors, see `matchCritical`.
131     ///
132     /// Params:
133     ///     source = Source that failed to match.
134     ///     error  = Error message, a reason why the match failed.
135     ///     data   = Optionally, context data for the failure, i.e. result data that matched successfully right before.
136     static Match fail(Input source, string error, Data data = Data.init) nothrow pure @safe @nogc {
137 
138         assert(error !is null, "Error for Match.fail must not be null. Note an empty string literal is a valid value.");
139 
140         return Match(error, data, source.take(0), Ts.init);
141 
142     }
143 
144     /// Get the data of the match. Throws `ParserException` if the match has failed.
145     inout(Data) tryData() inout pure @safe {
146 
147         // Throw the exception on error
148         if (error) throw new ParserException(error, matched.source);
149 
150         // Return the data otherwise
151         else return data;
152 
153     }
154 
155     /// Number of source elements consumed.
156     size_t consumed() const nothrow pure @safe @nogc {
157 
158         return matched.maxLength;
159 
160     }
161 
162     /// Get the base match struct, without capture data.
163     inout(BaseMatch) base() inout nothrow pure @safe @nogc {
164 
165         return inout BaseMatch(error, data, matched);
166 
167     }
168 
169     /// Assign a slice of the capture output.
170     void captureFrom(size_t index, Ts...)(Ts values) {
171 
172         static assert(values.length != 0 || Ts.length == 0);
173 
174         this.capture[index .. index + Ts.length] = values;
175 
176     }
177 
178     /// Check if the match succeeded.
179     bool opCast(T : bool)() const nothrow pure @safe @nogc {
180 
181         return error is null;
182 
183     }
184 
185     const toString() {
186 
187         import std.conv;
188         import std.meta;
189         import std.format;
190 
191         enum maxLength = 32;
192 
193         // Copy the match range into scope so we can iterate over it
194         Take!Input matched = matched;
195 
196         // Get the first characters from it
197         const matchedArr = this
198             ? matched.take(maxLength+1).array
199             : matched.source.take(maxLength+1).array;
200 
201         string matchedText;
202 
203         // Include an ellipsis in the format data if the match is longer than length limit
204         if (matchedArr.length == maxLength+1) {
205 
206             // Include length of the original range if available
207             static if (hasLength!(Take!Input))
208                 matchedText = format!"%(%s%)... %s"(matchedArr[0..$-1].only, matchedArr.length);
209             else
210                 matchedText = format!"%(%s%)..."(matchedArr[0..$-1].only);
211 
212         }
213 
214         // Match fits, include the full text
215         else matchedText = format!"%(%s%)"(matchedArr.only);
216 
217 
218         static if (Capture.length)
219             const joined = ", " ~ only(tupleMap!text(capture).expand).join(", ");
220         else
221             const joined = "";
222 
223         return this
224             ? format!"Match(%s, %s%s)"(matchedText, data, joined)
225             : format!"Match.fail(%s, %(%s%)%s)"(matchedText, error.only, joined);
226 
227     }
228 
229 }
230 
231 
232 unittest {
233 
234     import std.conv;
235 
236     template supply() {
237 
238         string[] supply(Take!string input) {
239             return [input.to!string];
240         }
241 
242         string[] supply(string[] a, string[] b) {
243             return a ~ b;
244         }
245 
246     }
247 
248     static assert(isMatchDataSupplier!(string, supply));
249 
250 }
251 
252 /// Mixin to produce matcher templates for processing `Input` input range and creating a `Match` output range.
253 ///
254 /// The parser requires a function with two overloads called `supply`. Both are expected to output a user data type for
255 /// storing the parsing result (eg. a list of tokens). The first one should take a slice of the input range as
256 /// a `std.range.Take!Input` and will be called for any successful match. The second should take two instances of the
257 /// data type and combine them into once.
258 ///
259 /// A basic implementation of a supply string (with `string` as the input range) would be this:
260 ///
261 /// ---
262 /// string[] supply(Take!string input) {
263 ///     return [input.to!string];
264 /// }
265 ///
266 /// string[] supply(string[] a, string[] b) {
267 ///     return a ~ b;
268 /// }
269 /// ---
270 ///
271 /// Notes:
272 ///
273 /// * Performance of the parser heavily depends on `std.range.popFrontN`, so it's the fastest if the range supports
274 ///   slicing and has length. Alternatively, the range may define a `popFrontN` method itself.
275 /// * The mixin can work both in module scope and at `struct`/`class` scope. No context is required for the matcher
276 ///   functions to work.
277 ///
278 ///   Internally the template only defines aliases to closures rather than functions. This allows them to
279 ///   work without an instance in structs and classes, but they may still use context for template parameters.
280 mixin template makeParser(Input, alias supply)
281 if (is(ElementType!Input : dchar)) {
282 
283     import std.string;
284 
285     mixin makeParser!(Input, supply, matchText);
286 
287     static auto matchText(dstring text)(Input input)
288     out (r; text.length == 0 || !r || r.consumed != 0, format!"matchText `%s` consumed nothing"(text))
289     do {
290 
291         import std.range;
292         import std.string;
293         import std.exception;
294 
295         import rcdata.utils;
296 
297         // Match EOF
298         static if (text.length == 0) {
299 
300             return input.empty
301                 ? Match(input, 0)
302                 : Match.fail(input, "Expected end of file");
303 
304         }
305 
306         // Match the text
307         else return input.startsWith(text)
308             ? Match(input, text.length)
309             : Match.fail(input, "Couldn't match");
310 
311     }
312 
313 }
314 
315 /// ditto
316 mixin template makeParser(Input, alias supply, alias basicMatcher) {
317 
318     import std.meta;
319     import std.format;
320     import std.range;
321     import std.traits;
322     import std.sumtype;
323     import std.functional;
324 
325     import rcdata.utils;
326     import rcdata.parser;
327 
328     static:
329 
330     // Check arguments
331     static assert(isInputRange!Input, Input.stringof ~ " isn't an input range");
332 
333     alias Match = rcdata.parser.MatchImpl!(Input, supply);
334     alias MatchCapture(Ts...) = rcdata.parser.MatchImpl!(Input, supply, Ts);
335     alias ParserException = rcdata.parser.ParserExceptionImpl!(Input, supply);
336 
337 
338     /// Get an AliasSeq of capture types held by a given list of `Match` and `MatchCapture` types.
339     template MatchCaptureTypes(Ts...) {
340 
341         alias MatchCaptureTypes = AliasSeq!();
342 
343         static foreach (T; Ts) {
344 
345             static assert(is(T : Match), T.stringof ~ " is not a Match subtype.");
346 
347             // Found a match type
348             static if (is(T : Match)) {
349 
350                 MatchCaptureTypes = AliasSeq!(MatchCaptureTypes, T.Capture);
351 
352             }
353 
354         }
355 
356     }
357 
358     /// Check the match type returned by `match` for the given pattern.
359     template MatchType(pattern...) {
360 
361         static if (pattern.length == 1) {
362 
363             alias MatchType = typeof(matchImpl!pattern(Input.init));
364 
365         }
366 
367         // Note: We return the type of (match!pattern) rather than the tuple of match types.
368         else alias MatchType = MatchCapture!(PatternCaptureTypes!pattern);
369 
370     }
371 
372     /// Get an alias seq of capture types, similarly to `PatternCaptureTypes`, but based on a pattern for `match`.
373     alias PatternCaptureTypes(pattern...) = MatchCaptureTypes!(staticMap!(MatchType, pattern));
374 
375     /// Iterate on the given pattern tuple.
376     struct CaptureTupleIterator(patterns...) {
377 
378         static int opApply(scope int delegate(size_t patternIndex, size_t captureIndex) dg) @system {
379 
380             size_t patternIndex, captureIndex;
381 
382             // Check each item in the pattern
383             static foreach (pattern; patterns) {{
384 
385                 alias T = MatchType!pattern;
386 
387                 static assert(is(T : Match),
388                     pattern.stringof ~ " does not return a valid Match instance but a " ~ T.stringof);
389 
390                 // Return the pattern and capture indices
391                 if (auto result = dg(patternIndex, captureIndex)) {
392 
393                     return result;
394 
395                 }
396 
397                 // Bump them
398                 patternIndex++;
399                 captureIndex += T.Capture.length;
400 
401             }}
402 
403             return 0;
404 
405         }
406 
407     }
408 
409     /// Wrap the tuple in a std.typecons.Tuple if its length isn't 1, otherwise return the sole item.
410     template TupleWrap(items...) {
411 
412         import std.typecons : Tuple;
413 
414         // One item, return it
415         static if (items.length == 1) alias TupleWrap = items[0];
416 
417         // Different count, wrap in a tuple
418         else alias TupleWrap = Tuple!items;
419 
420     }
421 
422     TupleWrap!Ts tupleWrap(Ts...)(Ts args) {
423 
424         static if (Ts.length == 1)
425             return args[0];
426         else
427             return TupleWrap!Ts(args);
428 
429     }
430 
431 
432     static assert(is(Match == MatchCapture!()));
433     static assert(is(MatchCapture!int == MatchCapture!(MatchCaptureTypes!(Match, MatchCapture!int))));
434 
435     /// Match anything.
436     alias matchAny() = (Input input)
437 
438         => matchAny!((ElementType!Input item) => true)(input);
439 
440 
441     /// Match one single item if `fun` returns `true`.
442     alias matchAny(alias fun) = (Input input) {
443 
444         alias funCC = unaryFun!fun;
445 
446         // Check for EOF
447         return input.empty ? Match.fail(input, "Unexpected end of file")
448 
449             // Check if the function matches
450             : funCC(input.front) ? Match(input, 1)
451 
452             // Fail if it doesn't
453             : Match.fail(input, "Delegate didn't match the element");
454 
455     };
456 
457     /// Match multiple items in order. Each matcher in the pattern will be tested against the source in order, and will
458     /// advance the range before the next item in the pattern. The full pattern has to match.
459     ///
460     /// `match` is a building block for any other pattern matching function. It will automatically call the proper
461     /// matcher (either the given matcher function, or `basicMatcher` as a fallback for convenience), and it can be used
462     /// to build any other sequential patterns (such as `matchRepeat`) by utilising the `context` parameter.
463     ///
464     /// A matcher function will be tried with each of the following:
465     ///
466     /// * `fun(input, capture)` if the function returns capture data, to pass on data from its previous iterations.
467     /// * `fun(input)` to perform a regular match.
468     /// * `basicMatcher!fun(input)` as a convenience call for the chosen `basicMatcher`; for example, the default
469     ///   `basicMatcher` for any dchar range would allow using strings as match patterns:
470     ///   `match!"struct"("struct Foo")`
471     /// * If none of those can compile, `match` will output the errors of `fun(input)` and `basicMatcher!fun(input)`.
472     ///
473     /// Params:
474     ///     pattern = Pattern that has to be matched.
475     ///     input   = Source to parse.
476     ///     context = Optionally, previous match result if used within repetitive matchers. Must be successful.
477     template match(pattern...)
478     if (is(MatchType!pattern)) {
479 
480         import std.typecons;
481 
482         // If compatible with the pattern
483         alias match = (Input input, MatchType!pattern context = MatchType!pattern.init) {
484 
485             assert(context, "Given context match has failed");
486 
487             alias Return = MatchType!pattern;
488 
489             Input source = input;
490             Return result = context;
491 
492             // This function cannot match less than matched by the context
493             scope (exit) assert(result.consumed >= context.consumed);
494 
495             // Evaluate each matcher
496             try static foreach (index, captureIndex; CaptureTupleIterator!pattern) {{
497 
498                 alias Local = MatchType!(pattern[index]);
499 
500                 // Get the last capture for this matcher, if any
501                 auto lastCapture = result.capture[captureIndex..captureIndex + Local.Capture.length];
502 
503                 // Try the match
504                 Local local = matchImpl!(pattern[index])(source, lastCapture);
505 
506                 // Combine data
507                 auto data = supply(result.data, local.data);
508 
509                 // If the match failed
510                 if (!local) {
511 
512                     // Return its match with updated data
513                     return Return.fail(local.matched.source, local.error, data);
514 
515                 }
516 
517                 // Success, expand the match to contain the full result
518                 result.matched = result.matched.source.take(result.consumed + local.consumed);
519 
520                 // Add user data
521                 result.data = data;
522                 result.capture[captureIndex .. captureIndex + Local.Capture.length] = local.capture;
523 
524                 assert(result);
525 
526                 // Advance the input range
527                 source.popFrontN(local.consumed);
528 
529             }}
530 
531             // Expand any caught critical exceptions with context info
532             catch (ParserException exc) {
533 
534                 throw exc.extend(result.data);
535 
536             }
537 
538             return result;
539 
540         };
541 
542     }
543 
544     // Try to alias to std.sumtype.match to help with namespace clash
545     template match(handlers...)
546     if (!is(MatchType!handlers)) {
547 
548         alias match = std.sumtype.match!handlers;
549 
550     }
551 
552     /// Try the overloads of the function to perform the match.
553     private template matchImpl(alias fun) {
554 
555         // Overload 1: regular match
556         alias matchImpl = (Input input) {
557 
558             // Try to run the matcher
559             static if (is(typeof(fun(input)) : Match)) {
560 
561                 return fun(input);
562 
563             }
564 
565             // It's not callable. But maybe basicMatcher can handle it?
566             else static if (is(typeof(basicMatcher!fun(input)) : Match)) {
567 
568                 // Alias to basicMatcher
569                 return basicMatcher!fun(input);
570 
571             }
572 
573             // Try both to see what the errors are
574             else {
575 
576                 Match local = fun(input);
577                 local = basicMatcher!fun(input);
578 
579             }
580 
581             assert(false);
582 
583         };
584 
585         alias Capture = typeof(matchImpl(Input.init)).Capture;
586 
587         // Overload 2, if no.1 has a capture, pass the previous capture data into it, to allow things like matchRepeat
588         // keep context.
589         static if (Capture.length != 0)
590         alias matchImpl = (Input input, Capture capture) {
591 
592             alias stringofone(funs...) = funs.stringof;
593 
594             // Special overload supported
595             static if (is(typeof(fun(input, capture)) : Match)) {
596 
597                 static assert(is(typeof(fun(input, capture)) == typeof(fun(input))),
598                     format!("`%s %s(input, capture)` return type doesn't match the one of `%s %2$s(input)`")
599                            (typeof(fun(input, capture)).stringof, stringofone!fun, typeof(fun(input)).stringof));
600 
601                 return fun(input, capture);
602 
603             }
604 
605             // Run the default overload otherwise
606             else return matchImpl(input);
607 
608         };
609 
610     }
611 
612     /// Match one of the given items.
613     alias matchOr(pattern...) = (Input input) {
614 
615         alias ReturnMatch = MatchOr!pattern;
616         alias Capture = ReturnMatch.Capture;
617 
618         // Evaluate each matcher
619         foreach (fun; pattern) {
620 
621             // Return the result of the first one succeeding
622             if (auto result = match!fun(input)) {
623 
624                 // Cast the capture to our expected type
625                 Capture capture = tupleWrap(result.capture);
626 
627                 return ReturnMatch(result.matched, result.data, capture);
628 
629             }
630 
631         }
632 
633         return ReturnMatch.fail(input, "No match found for matchOr!(" ~ pattern.stringof ~ ")");
634 
635     };
636 
637     /// Return the `matchOr` return value for the given pattern.
638     template MatchOr(pattern...) {
639 
640         import std.meta;
641         import std.typecons : Tuple;
642 
643         // Collect possible values for the capture.
644         alias Types = AliasSeq!();
645 
646         // Check each pattern
647         static foreach (fun; pattern) {
648 
649             // Add it to the type list, wrap in a tuple if item count != 1
650             Types = AliasSeq!(Types, TupleWrap!(MatchType!fun.Capture));
651 
652         }
653 
654         // Get rid of duplicates
655         Types = NoDuplicates!Types;
656 
657 
658         // No types, return a plain match
659         static if (Types.length == 0 || is(Types[0] == Tuple!()))
660             alias MatchOr = Match;
661 
662         // One type only, return it
663         else static if (Types.length == 1)
664             alias MatchOr = MatchCapture!Types;
665 
666         // Multiple types, make it a sum type
667         else alias MatchOr = MatchCapture!(SumType!Types);
668 
669     }
670 
671     /// Repeat the token sequence (as in `match`) zero to infinity times
672     alias matchRepeat(pattern...) = (Input input, MatchType!pattern context = MatchType!pattern.init)
673 
674         => matchRepeatMin!(0, pattern)(input, context);
675 
676 
677     /// Repeat the token sequence at least once.
678     alias matchRepeatMinOnce(pattern...) = (Input input, MatchType!pattern context = MatchType!pattern.init)
679 
680         => matchRepeatMin!(1, pattern)(input, context);
681 
682 
683     alias matchRepeatMin(size_t minMatches, pattern...) = (Input input,
684         MatchType!pattern context = MatchType!pattern.init)
685 
686         => matchRepeatRange!pattern(input, context, minMatches);
687 
688 
689     /// Repeat the token sequence (as in match) `minMatches` to `maxMatches` times
690     alias matchRepeatRange(size_t minMatches, size_t maxMatches, pattern...) = (Input input,
691         MatchType!pattern context = MatchType!pattern.init)
692 
693         => matchRepeatRange!pattern(input, context, minMatches, maxMatches);
694 
695 
696     /// ditto
697     template matchRepeatRange(pattern...)
698     if (!is(typeof(pattern[0]) : size_t)) {
699 
700         alias matchRepeatRange = (Input input, MatchType!pattern context = MatchType!pattern.init,
701             size_t minMatches = 0, size_t maxMatches = size_t.max)
702         {
703 
704             size_t matches;
705 
706             while (true) {
707 
708                 MatchType!pattern local;
709 
710                 // Match the token
711                 local = match!pattern(input, context);
712 
713                 // Stop if match failed, we don't care about it
714                 if (!local) break;
715 
716                 // Count the match
717                 matches++;
718 
719                 const length = local.consumed - context.consumed;
720 
721                 // Advance the input
722                 input.popFrontN(length);
723                 context = local;
724 
725                 // Special case: Match is empty, stop to prevent loops
726                 if (length == 0) break;
727 
728                 // Stop if matched enough
729                 if (matches == maxMatches) break;
730 
731             }
732 
733             // Check if matched enough times
734             return matches < minMatches
735                 ? context.fail(input, "matchRepeat didn't match enough times")
736                 : context;
737 
738         };
739 
740     }
741 
742     /// Repeat the sequence until another token matches. Does NOT match the terminator.
743     ///
744     /// If the pattern fails, the failure will be propagated, and `matchUntil` will also report failure.
745     ///
746     /// Params:
747     ///     terminator = Matcher to stop the loop when successful.
748     ///     pattern    = Pattern to match. Defaults to `matchAny`.
749     alias matchUntil(alias terminator) = (Input input)
750 
751         => matchUntil!(terminator, matchAny!())(input);
752 
753 
754     /// ditto
755     template matchUntil(alias terminator, pattern...)
756     if (pattern.length != 0) {
757 
758         alias matchUntil = (Input input) {
759 
760             MatchType!pattern context;
761 
762             while (true) {
763 
764                 // Match the terminator
765                 if (match!terminator(input, context)) break;
766 
767                 // Match the token
768                 auto local = match!pattern(input, context);
769 
770                 // Propagate failures
771                 if (!local) return local;
772 
773                 // Count consumed tokens
774                 const length = local.consumed - context.consumed;
775 
776                 // Special case: Match is empty, stop to prevent loops
777                 if (length == 0) break;
778 
779                 // Advance the range
780                 input.popFrontN(length);
781                 context = local;
782 
783             }
784 
785             return context;
786 
787         };
788 
789     }
790 
791     /// Match zero or one instances of a token.
792     /// Returns: `Match` or if the value contains captures, `MatchCapture!(Nullable!Match)`.
793     alias matchOptional(pattern...) = (Input input) {
794 
795         import std.typecons;
796 
797         // Match the token
798         auto ret = match!pattern(input);
799 
800         // No capture
801         static if (ret.Capture.length == 0) {
802 
803             return ret
804                 ? ret
805                 : Match.ok(input, 0);
806 
807         }
808 
809         // Capture on, add a nullable value
810         else {
811 
812             alias Capture = Nullable!(TupleWrap!(ret.Capture));
813             alias Return = MatchCapture!Capture;
814 
815             return ret
816                 ? Return(ret.matched, ret.data, Capture(tupleWrap(ret.capture)))
817                 : Return(input, 0, Capture());
818 
819         }
820 
821     };
822 
823     /// Construct `T` by matching given `pattern`.
824     ///
825     /// Returns: `MatchCapture!T` holding the constructed value.
826     alias matchCapture(T, pattern...) = (Input input, T value = T.init) {
827 
828         // This function is an ugly mess. I don't blame myself. I don't see how it could've been done better.
829 
830         alias Result = MatchCapture!T;
831 
832         Input source = input;
833         auto result = Result(input, 0, value);
834 
835         /// Union to hold results of each pattern match
836         union LastResult {
837 
838             // Create storage for each value
839             static foreach (ptrdiff_t i, fun; pattern) {
840 
841                 static if (!is(ByIndex!i == void))
842                 mixin("ByIndex!i c", i, ";");
843 
844                 else mixin("typeof(null) c", i, ";");
845 
846             }
847 
848             /// Type of the value at given index.
849             template ByIndex(ptrdiff_t i) {
850 
851                 // Defined
852                 static if (__traits(compiles, mixin("c", i))) alias ByIndex = typeof(byIndex!i);
853 
854                 // Not defined yet
855                 else {
856 
857                     alias ByIndex = typeof(
858                         matchCaptureImpl!(T, pattern[i], typeof(byIndex!(i-1)()))(source, result.capture, byIndex!(i-1))
859                     );
860 
861                 }
862 
863             }
864 
865             /// Get a stored value by its index.
866             auto ref byIndex(ptrdiff_t i)() @trusted {
867 
868                 static if (i < 0) return null;
869                 else return mixin("c", i);
870 
871             }
872 
873         }
874 
875         LastResult lastResult;
876 
877         /// Run the rule at given index. Does not store the result.
878         auto run(ptrdiff_t i)(LastResult lastResult, Input input, ref Result match) {
879 
880             // This must have no context other than pattern[i]
881 
882             auto last = lastResult.byIndex!(i-1);
883 
884             // Run this rule
885             return matchCaptureImpl!(T, pattern[i], typeof(last))(input, match.capture, last);
886 
887         }
888 
889         import core.lifetime;
890 
891         // Evaluate each rule
892         try static foreach (i, fun; pattern) {{
893 
894             // If this is a matcher
895             static if (is(lastResult.ByIndex!i : Match)) {
896 
897                 // Run the rule
898                 auto local = run!i(lastResult, source, result);
899 
900                 // Combine data
901                 auto data = supply(result.data, local.data);
902 
903                 // Match failed
904                 if (!local) {
905 
906                     return Result.fail(local.matched.source, local.error, data);
907 
908                 }
909 
910                 // Success
911                 result = Result(
912                     result.matched.source.take(result.consumed + local.consumed),
913                     data,
914                     result.capture,
915                 );
916 
917                 // Advance the input range
918                 source.popFrontN(local.consumed);
919 
920                 // Move into the register
921                 move(local, lastResult.byIndex!i());
922 
923             }
924 
925             // Void
926             else static if (is(lastResult.ByIndex!i == void)) {
927 
928                 run!i(lastResult, source, result);
929 
930             }
931 
932             // Non-void, run the function and read the value
933             else lastResult.byIndex!i = run!i(lastResult, source, result);
934 
935         }}
936 
937         // Expand any caught exceptions with context info
938         catch (ParserException exc) {
939 
940             throw exc.extend(result.data);
941 
942         }
943 
944         return result;
945 
946     };
947 
948     private alias matchCaptureImpl(T, alias fun, LastResult) = (Input input, ref T value, LastResult lastResult) {
949 
950         // TODO maybe this function should unconditionally return a match?
951 
952         // Got a match
953         static if (is(LastResult : Match)) {
954 
955             auto capture = lastResult.capture;
956 
957         }
958 
959 
960         // Option 1, (LastResult, ref T)
961         static if (__traits(compiles, fun(lastResult, value))) {
962 
963             return fun(lastResult, value);
964 
965         }
966 
967         // Option 2, (LastLastResult.capture, ref T)
968         else static if (__traits(compiles, fun(capture, value))) {
969 
970             return fun(capture, value);
971 
972         }
973 
974         // Option 3, matchImpl(input)
975         else static if (__traits(compiles, matchImpl!fun(input))) {
976 
977             return matchImpl!fun(input);
978 
979         }
980 
981         else {
982 
983             fun(capture, value);
984             static assert(false, stringofone!fun ~ " is not a valid matchCapture predicate; "
985                 ~ "LastResult = `" ~ LastResult.stringof ~ "`");
986 
987         }
988 
989     };
990 
991     /// Require the given rule to match, otherwise throw given exception.
992     /// Params:
993     ///     Exc = Exception type to instantiate and throw. Will be passed a message string and, if supported, input
994     ///         range of the following source text. If omitted, but `message` is given, throws `ParserException`.
995     ///     message = Message of the exception to throw.
996     ///     instance = Already constructed instance of an exception to throw.
997     ///     pattern = Pattern to match. If empty, trying to match this pattern will result with the exception.
998     alias matchCritical(Exc : Throwable, string message, pattern...) = (Input input) {
999 
1000         // Exception accepting source data
1001         static if (__traits(compiles, new Exc(message, input))) {
1002 
1003             return matchCriticalImpl!pattern(input, new Exc(message, input));
1004 
1005         }
1006 
1007         // Regular exception
1008         else return matchCriticalImpl!pattern(input, new Exc(message));
1009 
1010     };
1011 
1012     /// ditto
1013     alias matchCritical(string message, pattern...) = (Input input)
1014 
1015         => matchCritical!(ParserException, message, pattern)(input);
1016 
1017 
1018     /// ditto
1019     alias matchCritical(Throwable instance, pattern...) = (Input input)
1020 
1021         => matchCriticalImpl!pattern(input, instance);
1022 
1023 
1024     /// ditto
1025     alias matchCriticalImpl(pattern...) = (Input input, Throwable instance) {
1026 
1027         // If there's a sequence to check
1028         static if (pattern.length) {
1029 
1030             // Try to match
1031             if (auto result = match!pattern(input)) {
1032 
1033                 return result;
1034 
1035             }
1036 
1037             // Match failed, throw the chosen exception
1038             else throw instance;
1039 
1040         }
1041 
1042         // Just throw on encounter
1043         else {
1044 
1045             // Let the compiler infer the return type
1046             if (false) return match!pattern(input);
1047 
1048             throw instance;
1049 
1050         }
1051 
1052     };
1053 
1054     /// Adjust failure message of the given pattern.
1055     alias matchFailMessage(string message, pattern...) = (Input input) {
1056 
1057         auto result = match!pattern(input);
1058 
1059         return result
1060             ? result
1061             : result.fail(input, message);
1062 
1063     };
1064 
1065     /// Check if the pattern matches, but do not supply it.
1066     alias lookAhead(pattern...) = (Input input) {
1067 
1068         auto result = match!pattern(input);
1069 
1070         // Return empty match on success, the result if failed
1071         return result
1072             ? result.ok(input, 0, result.capture)
1073             : result;
1074 
1075     };
1076 
1077     /// Fail if the pattern matches. Succeeds if the rule fails. Doesn't supply anything.
1078     alias failAhead(pattern...) = (Input input) {
1079 
1080         return match!pattern(input)
1081             ? Match.fail(input, "Rule matched but shouldn't have")
1082             : Match(input, 0);
1083 
1084     };
1085 
1086     /// Never matches.
1087     alias matchNever(string msg) = (Input input) {
1088 
1089         return Match.fail(input, msg);
1090 
1091     };
1092 
1093 }
1094 
1095 /// Exception type thrown on a parser failure, usually `matchCritical`.
1096 class ParserExceptionImpl(Input, alias supply) : RCDataException {
1097 
1098     import std.range;
1099 
1100     static assert(isInputRange!Input, Input.stringof ~ " isn't an input range");
1101 
1102     alias Match = MatchData!(Input, supply);
1103 
1104     Match context;
1105     Input source;
1106 
1107     this(string msg, Input source, string filename = __FILE__, size_t line = __LINE__) pure @safe {
1108 
1109         super(msg, filename, line);
1110         this.source = source;
1111 
1112     }
1113 
1114     this(string msg, Match context, Input source, string filename = __FILE__, size_t line = __LINE__) pure @safe {
1115 
1116         this(msg, source, filename, line);
1117         this.context = context;
1118 
1119     }
1120 
1121     mixin template parserExeptionCtors() {
1122 
1123         this(string msg, Input source, string filename = __FILE__, size_t line = __LINE__) pure @safe {
1124 
1125             super(msg, source, filename, line);
1126 
1127         }
1128 
1129         this(string msg, Match context, Input source, string filename = __FILE__, size_t line = __LINE__)
1130             pure @safe
1131         do {
1132             super(msg, context, source, filename, line);
1133         }
1134 
1135     }
1136 
1137     /// Extend the exception with data about the parent context.
1138     auto extend(Match context) {
1139 
1140         this.context = supply(context, this.context);
1141         return this;
1142 
1143     }
1144 
1145 }