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