1 /**
2 This module is an attempt at introducing pattern-matching in D.
3 
4 Pattern matching is a type- or value-matching present in functional languages like ML or Haskell.
5 
6 The goal here is to obtain code like:
7 
8 ----
9 Pattern!"[ a, b, ... ]" p1; // match any range with at least two elements
10                             // the rest being discarded.
11 
12 auto m1 = p1([0,1,2,3,4]); // match and associate 'a' and 'b' with O and 1.
13 assert(m1.a == 0 && m1.b == 1);
14 auto m2 = p1("abc");
15 assert(m2.a = 'a' && m2.b == 'b');
16 auto m3 = p1(tuple("abc",0)); // not a range, the pattern fails
17 assert(m3 == matchFailure; // predefined constant
18 
19 Pattern!"Tuple!(.,.) t" p2; // match any std.typecons.Tuple with two elements, of any type
20 auto m4 = p2(tuple("abc",0));
21 assert(m4.t = tuple("abc",0); // only the global pattern is named, and hence captured
22 assert(p2(tuple("abc")) == matchFailure); // only one member -> failure
23 assert(p2(tuple("abc",0,1)) == matchFailure); // three members -> failure (the pattern has no trailing ...
24 
25 Pattern!" . { int, double, double }" p3; // match any aggregate (struct, class)
26                                          // with three members being int,double and double, in that order.
27 ----
28 
29 Don't salivate, it's not implemented yet.
30 */
31 module pegged.examples.pattern;
32 
33 import std.range;
34 import std.traits;
35 import std.typecons;
36 import std.typetuple;
37 
38 import pegged.grammar;
39 
40 mixin(grammar(`
41 Pattern:
42     Global     <  Choice :eoi
43     Choice     <  Sequence (:'/' Sequence)*
44     Sequence   <  Primary (:',' Primary)*
45     Primary    <  (Aggregate / Range / Identifier / Literal / REST / ANY) Name?
46 
47     Name       <  identifier
48     Aggregate  <  (Identifier / ANY) (Tuple / Record)
49     Tuple      <  '{' (Choice (:',' Choice)*)? '}'
50     Record     <  '{' Field (','Field)* '}'
51     Field      <  Identifier :'=' Choice
52     Range      <  '[' (Choice (:',' Choice)*)? ']'
53     Identifier <  identifier
54     Literal    <  Number / Char / String/ Bool
55     Number     <~ Digit+ ('.' Digit*)?
56     Digit      <  [0-9]
57     Char       <~ quote . quote # TODO: adding escape chars and Unicode chars
58     String     <~ doublequote (!doublequote .)* doublequote
59     Bool       <  "true" / "false"
60 
61     REST       <  "..."
62     ANY        <  "."
63     Spacing    <: spacing
64 `));
65 
66 template TypeFailure(T...)
67 {
68     enum successful = false;
69     alias TypeTuple!() Types;
70     enum begin = 0;
71     enum end = 0;
72     alias T Rest;
73 }
74 
75 struct TypeAny
76 {
77     template Match(T...)
78     {
79         static if (T.length == 0)
80             mixin TypeFailure!(T);
81         else
82         {
83             enum successful = true;
84             alias T[0..1] Types;
85             enum begin = 0;
86             enum end = 1;
87             alias T[1..$] Rest;
88         }
89     }
90 }
91 
92 struct TypeEnd
93 {
94     template Match(T...)
95     {
96         static if (T.length == 0)
97         {
98             enum successful = true;
99             alias TypeTuple!() Types;
100             enum begin = 0;
101             enum end = 0;
102             alias T Rest;
103         }
104         else
105             mixin TypeFailure!(T);
106     }
107 }
108 
109 struct TypeEps
110 {
111     template Match(T...)
112     {
113         enum successful = true;
114         alias TypeTuple!() Types;
115         enum begin = 0;
116         enum end = 0;
117         alias T Rest;
118     }
119 }
120 
121 struct TypeLiteral(U...)
122 {
123     template Match(T...)
124     {
125         static if (T.length < U.length || !is(T[0..U.length] == U))
126             mixin TypeFailure!(T);
127         else
128         {
129             enum successful = true;
130             alias T[0..U.length] Types;
131             enum begin = 0;
132             enum end = U.length;
133             alias T[U.length..$] Rest;
134         }
135     }
136 }
137 
138 template isSubtype(T...)
139 {
140     static if (T.length == 0)
141         enum isSubtype = true;
142     else static if (is(T[0] : T[$/2]))
143         enum isSubtype = isSubtype!(T[1..$/2],T[$/2+1..$]);
144     else
145         enum isSubtype = false;
146 }
147 
148 struct TypeSubType(U...)
149 {
150     template Match(T...)
151     {
152         static if (T.length < U.length || !isSubtype!(T[0..U.length],U))
153             mixin TypeFailure!(T);
154         else
155         {
156             enum successful = true;
157             alias T[0..U.length] Types;
158             enum begin = 0;
159             enum end = U.length;
160             alias T[U.length..$] Rest;
161         }
162     }
163 }
164 
165 struct TypeOr(alias Pattern1, alias Pattern2)
166 {
167     template Match(T...)
168     {
169         alias Pattern1.Match!(T) P1;
170         static if (P1.successful)
171             alias P1 Match;
172         else
173             alias Pattern2.Match!(T) Match;
174     }
175 }
176 
177 template TypeOr(Patterns...) if (Patterns.length > 2)
178 {
179     alias TypeOr!(Patterns[0], TypeOr!(Patterns[1..$])) TypeOr;
180 }
181 
182 template TypeOr(Patterns...) if (Patterns.length == 1)
183 {
184     alias Patterns[0] TypeOr;
185 }
186 
187 struct TypeAnd(alias Pattern1, alias Pattern2)
188 {
189     template Match(T...)
190     {
191         alias Pattern1.Match!(T) P1;
192         alias Pattern2.Match!(Pattern1.Match!(T).Rest) P2;
193         static if (P1.successful && P2.successful)
194         {
195             enum successful = true;
196             alias TypeTuple!(P1.Types, P2.Types) Types;
197             enum begin = P1.begin;
198             enum end = P2.end;
199             alias P2.Rest Rest;
200         }
201         else
202             mixin TypeFailure!(T);
203     }
204 }
205 
206 template TypeAnd(Patterns...) if (Patterns.length > 2)
207 {
208     alias TypeAnd!(Patterns[0], TypeAnd!(Patterns[1..$])) And;
209 }
210 
211 template TypeAnd(Patterns...) if (Patterns.length == 1)
212 {
213     alias Patterns[0] TypeAnd;
214 }
215 
216 struct TypeOption(alias Pattern)
217 {
218     template Match(T...)
219     {
220         alias Pattern.Match!(T) P;
221         static if (P.successful)
222             alias P Match;
223         else
224         {
225             enum successful = true;
226             alias TypeTuple!() Types;
227             enum begin = 0;
228             enum end = 0;
229             alias T Rest;
230         }
231     }
232 }
233 
234 struct TypeZeroOrMore(alias Pattern)
235 {
236     template Match(T...)
237     {
238         alias Pattern.Match!(T) P;
239         static if (P.successful)
240         {
241             enum successful = true;
242             alias TypeTuple!(P.Types, TypeZeroOrMore!(Pattern).Match!(P.Rest).Types) Types;
243             enum begin = P.begin;
244             alias TypeZeroOrMore!(Pattern).Match!(P.Rest) More;
245             enum end = P.end + More.end;
246             alias TypeZeroOrMore!(Pattern).Match!(P.Rest).Rest Rest;
247         }
248         else
249         {
250             enum successful = true;
251             alias TypeTuple!() Types;
252             enum begin = 0;
253             enum end = 0;
254             alias T Rest;
255         }
256     }
257 }
258 
259 struct TypeOneOrMore(alias Pattern)
260 {
261     template Match(T...)
262     {
263         alias Pattern.Match!(T) P;
264         static if (P.successful)
265         {
266             enum successful = true;
267             alias TypeTuple!(P.Types, TypeZeroOrMore!(Pattern).Match!(P.Rest).Types) Types;
268             enum begin = P.begin;
269             alias TypeZeroOrMore!(Pattern).Match!(P.Rest) More;
270             enum end = P.end + More.end;
271             alias TypeZeroOrMore!(Pattern).Match!(P.Rest).Rest Rest;
272         }
273         else
274             mixin TypeFailure!(T);
275     }
276 }
277 
278 /**
279 Discards the matched types, but propagate the indices.
280 */
281 struct TypeDiscardMatch(alias Pattern)
282 {
283     template Match(T...)
284     {
285         alias Pattern.Match!(T) P;
286         static if (P.successful)
287         {
288             enum successful = true;
289             alias TypeTuple!() Types; // Forget the match,
290             enum begin = P.begin;     // but propagate indices
291             enum end = P.end;         //
292             alias P.Rest Rest;        // and the input
293         }
294         else
295             mixin TypeFailure!(T);
296     }
297 }
298 
299 struct TypePosLookAhead(alias Pattern)
300 {
301     template Match(T...)
302     {
303         alias Pattern.Match!(T) P;
304         static if (P.successful)
305         {
306             enum successful = true;
307             alias TypeTuple!() Types;
308             enum begin = 0;
309             enum end = 0;
310             alias T Rest;
311         }
312         else
313             mixin TypeFailure!(T);
314     }
315 }
316 
317 struct TypeNegLookAhead(alias Pattern)
318 {
319     template Match(T...)
320     {
321         alias Pattern.Match!(T) P;
322         static if (P.successful)
323             mixin TypeFailure!(T);
324         else
325         {
326             enum successful = true;
327             alias TypeTuple!() Types;
328             enum begin = 0;
329             enum end = 0;
330             alias T Rest;
331         }
332     }
333 }
334 
335 struct isRange
336 {
337     template Match(T...)
338     {
339         static if (isInputRange!(T[0]))
340         {
341             enum successful = true;
342             alias T[0] Types;
343             enum begin = 0;
344             enum end = 1;
345             alias T[1..$] Rest;
346         }
347         else
348             mixin TypeFailure!(T);
349     }
350 }
351 
352 struct TypeSatisfy(alias test)
353 {
354     template Match(T...)
355     {
356         static if (test!(T[0]))
357         {
358             enum successful = true;
359             alias T[0] Types;
360             enum begin = 0;
361             enum end = 1;
362             alias T[1..$] Rest;
363         }
364         else
365             mixin TypeFailure!(T);
366     }
367 }
368 
369 struct MatchResult(size_t junction, T...)
370 {
371     bool successful;
372     T[0..junction] match;
373     size_t begin;
374     size_t end;
375     T[junction..$] rest;
376 }
377 
378 struct Success(M...)
379 {
380     M match;
381     size_t begin;
382     size_t end;
383 
384     auto rest(R...)(R rest)
385     {
386         return MatchResult!(M.length, M, R)(true, match, begin, end, rest);
387     }
388 }
389 
390 Success!(M) success(M...)(M match, size_t begin, size_t end)
391 {
392     return Success!(M)(match, begin, end);
393 }
394 
395 auto failure(R...)(R rest)
396 {
397     return MatchResult!(0,R)(false, 0,0, rest);
398 }
399 
400 auto anyValue(Args...)(Args args)
401 {
402     static if (Args.length > 0)
403         return success(args[0],0,1).rest(args[1..$]);
404     else
405         return failure(args);
406 }
407 
408 auto endValue(Args...)(Args args)
409 {
410     static if (Args.length == 0)
411         return success(0,0).rest(args);
412     else
413         return failure(args);
414 }
415 
416 /+
417 template literalValue(values...)
418 {
419     auto literalValue(Args...)(Args args)
420     {
421         if (
422             return success(0,values.length).rest(args[values.length..$]);
423         else
424             return failure(args);
425     }
426 }
427 +/
428 
429 Tuple!(ElementType!R, R) rangeAny(R)(R r) if (isInputRange!R)
430 {
431     if (r.empty)
432         throw new Exception("Empty range, could not pattern-match");
433     else
434     {
435         auto head = r.front();
436         r.popFront();
437         return tuple(head, r);
438     }
439 }
440 
441 Tuple!(R,R) rangeRest(R)(R r) if (isInputRange!R)
442 {
443     if (r.empty)
444         throw new Exception("Empty range, could not pattern-match");
445     else
446         return tuple(r, r); // second r will not be used, but is needed to be compatible with other range patterns: Tuple!(result,rest)
447 }
448 
449 template RangePatternResult(R) if (isInputRange!R)
450 {
451     template RangePatternResult(alias T)
452     {
453         alias ReturnType!(T!R) RangePatternResult;
454     }
455 }
456 
457 template onRange(RangePatterns...)
458 {
459     auto onRange(R)(R r) if (isInputRange!(R))
460     {
461         alias RangePatternResult!R GetType;
462 
463         staticMap!(GetType, RangePatterns) _temp;
464 
465         static if (RangePatterns.length > 0)
466         {
467             _temp[0] = RangePatterns[0](r);
468             foreach(i,pat; RangePatterns[1..$])
469                 _temp[i+1] = RangePatterns[i+1](_temp[i][1]);
470         }
471 
472         struct MatchResult
473         {
474             GetType!(RangePatterns[0]).Types[0] a;
475             GetType!(RangePatterns[1]).Types[0] b;
476             GetType!(RangePatterns[2]).Types[0] c;
477         }
478 
479         return MatchResult(_temp[0][0],_temp[1][0],_temp[2][0]);
480     }
481 }
482