1 module dfix; 2 3 import std.experimental.lexer; 4 import dparse.lexer; 5 import dparse.parser; 6 import dparse.ast; 7 import std.stdio; 8 import std.format; 9 import std.file; 10 11 int main(string[] args) 12 { 13 import std.getopt : getopt; 14 import std.parallelism : parallel; 15 16 // http://wiki.dlang.org/DIP64 17 bool dip64; 18 // http://wiki.dlang.org/DIP65 19 bool dip65 = true; 20 //https://github.com/dlang/DIPs/blob/master/DIPs/DIP1003.md 21 bool dip1003 = true; 22 23 bool help; 24 25 try 26 { 27 getopt(args, 28 "dip64", &dip64, 29 "dip65", &dip65, 30 "dip1003", &dip1003, 31 "help|h", &help, 32 ); 33 } 34 catch (Exception e) 35 { 36 stderr.writeln(e.msg); 37 return 1; 38 } 39 40 if (help) 41 { 42 printHelp(); 43 return 0; 44 } 45 46 if (args.length < 2) 47 { 48 stderr.writeln("File path is a required argument"); 49 return 1; 50 } 51 52 string[] files; 53 54 foreach (arg; args[1 .. $]) 55 { 56 if (isDir(arg)) 57 { 58 foreach (f; dirEntries(arg, "*.{d,di}", SpanMode.depth)) 59 files ~= f; 60 } 61 else 62 files ~= arg; 63 } 64 65 foreach (f; parallel(files)) 66 { 67 try 68 upgradeFile(f, dip64, dip65, dip1003); 69 catch (Exception e) 70 stderr.writeln("Failed to upgrade ", f, ":(", e.file, ":", e.line, ") ", e.msg); 71 } 72 73 return 0; 74 } 75 76 /** 77 * Prints help message 78 */ 79 void printHelp() 80 { 81 stdout.writeln(` 82 Dfix automatically upgrades D source code to comply with new language changes. 83 Files are modified in place, so have backup copies ready or use a source 84 control system. 85 86 Usage: 87 88 dfix [Options] FILES DIRECTORIES 89 90 Options: 91 92 --dip64 93 Rewrites attributes to be compliant with DIP64. This defaults to 94 "false". Do not use this feature if you want your code to compile. 95 It exists as a proof-of-concept for enabling DIP64. 96 --dip65 97 Rewrites catch blocks to be compliant with DIP65. This defaults to 98 "true". Use --dip65=false to disable this fix. 99 --dip1003 100 Rewrites body blocks to be compliant with DIP1003. This defaults to 101 "true". Use --dip1003=false to disable this fix. 102 --help -h 103 Prints this help message 104 `); 105 } 106 107 /** 108 * Fixes the given file. 109 */ 110 void upgradeFile(string fileName, bool dip64, bool dip65, bool dip1003) 111 { 112 import std.algorithm : filter, canFind; 113 import std.range : retro; 114 import std.array : array, uninitializedArray; 115 import dparse.formatter : Formatter; 116 import std.exception : enforce; 117 import dparse.rollback_allocator : RollbackAllocator; 118 import std.functional : toDelegate; 119 120 File input = File(fileName, "rb"); 121 ubyte[] inputBytes = uninitializedArray!(ubyte[])(cast(size_t) input.size); 122 input.rawRead(inputBytes); 123 input.close(); 124 StringCache cache = StringCache(StringCache.defaultBucketCount); 125 LexerConfig config; 126 config.fileName = fileName; 127 config.stringBehavior = StringBehavior.source; 128 auto tokens = byToken(inputBytes, config, &cache).array; 129 auto parseTokens = tokens.filter!(a => a != tok!"whitespace" 130 && a != tok!"comment" && a != tok!"specialTokenSequence").array; 131 132 RollbackAllocator allocator; 133 uint errorCount; 134 auto mod = parseModule(parseTokens, fileName, &allocator, toDelegate(&reportErrors), &errorCount); 135 if (errorCount > 0) 136 { 137 stderr.writefln("%d parse errors encountered. Aborting upgrade of %s", 138 errorCount, fileName); 139 return; 140 } 141 142 File output = File(fileName, "wb"); 143 auto visitor = new DFixVisitor; 144 visitor.visit(mod); 145 relocateMarkers(visitor.markers, tokens); 146 147 SpecialMarker[] markers = visitor.markers; 148 149 auto formatter = new Formatter!(File.LockingTextWriter)(File.LockingTextWriter.init); 150 151 void writeType(T)(File output, T tokens, ref size_t i) 152 { 153 if (isBasicType(tokens[i].type)) 154 { 155 writeToken(output, tokens[i]); 156 i++; 157 } 158 else if ((tokens[i] == tok!"const" || tokens[i] == tok!"immutable" 159 || tokens[i] == tok!"shared" || tokens[i] == tok!"inout") 160 && tokens[i + 1] == tok!"(") 161 { 162 writeToken(output, tokens[i]); 163 i++; 164 skipAndWrite!("(", ")")(output, tokens, i); 165 } 166 else 167 { 168 skipIdentifierChain(output, tokens, i, true); 169 if (i < tokens.length && tokens[i] == tok!"!") 170 { 171 writeToken(output, tokens[i]); 172 i++; 173 if (i + 1 < tokens.length && tokens[i + 1] == tok!"(") 174 skipAndWrite!("(", ")")(output, tokens, i); 175 else if (tokens[i].type == tok!"identifier") 176 skipIdentifierChain(output, tokens, i, true); 177 else 178 { 179 writeToken(output, tokens[i]); 180 i++; 181 } 182 } 183 } 184 skipWhitespace(output, tokens, i); 185 // print out suffixes 186 while (i < tokens.length && (tokens[i] == tok!"*" || tokens[i] == tok!"[")) 187 { 188 if (tokens[i] == tok!"*") 189 { 190 writeToken(output, tokens[i]); 191 i++; 192 } 193 else if (tokens[i] == tok!"[") 194 skipAndWrite!("[", "]")(output, tokens, i); 195 } 196 } 197 198 for (size_t i = 0; i < tokens.length; i++) 199 { 200 markerLoop: foreach (marker; markers) 201 { 202 with (SpecialMarkerType) final switch (marker.type) 203 { 204 case bodyEnd: 205 if (tokens[i].index != marker.index) 206 break; 207 assert (tokens[i].type == tok!"}", format("%d %s", tokens[i].line, str(tokens[i].type))); 208 writeToken(output, tokens[i]); 209 i++; 210 if (i < tokens.length && tokens[i] == tok!";") 211 i++; 212 markers = markers[1 .. $]; 213 break markerLoop; 214 case functionAttributePrefix: 215 if (tokens[i].index != marker.index) 216 break; 217 // skip over token to be moved 218 i++; 219 skipWhitespace(output, tokens, i, false); 220 221 // skip over function return type 222 writeType(output, tokens, i); 223 skipWhitespace(output, tokens, i); 224 225 // skip over function name 226 skipIdentifierChain(output, tokens, i, true); 227 skipWhitespace(output, tokens, i, false); 228 229 // skip first paramters 230 skipAndWrite!("(", ")")(output, tokens, i); 231 232 immutable bookmark = i; 233 skipWhitespace(output, tokens, i, false); 234 235 // If there is a second set of parameters, go back to the bookmark 236 // and print out the whitespace 237 if (i < tokens.length && tokens[i] == tok!"(") 238 { 239 i = bookmark; 240 skipWhitespace(output, tokens, i); 241 skipAndWrite!("(", ")")(output, tokens, i); 242 skipWhitespace(output, tokens, i, false); 243 } 244 else 245 i = bookmark; 246 247 // write out the attribute being moved 248 output.write(" ", marker.functionAttribute); 249 250 // if there was no whitespace, add it after the moved attribute 251 if (i < tokens.length && tokens[i] != tok!"whitespace" && tokens[i] != tok!";") 252 output.write(" "); 253 254 markers = markers[1 .. $]; 255 break markerLoop; 256 case cStyleArray: 257 if (i != marker.index) 258 break; 259 formatter.sink = output.lockingTextWriter(); 260 foreach (node; retro(marker.nodes)) 261 formatter.format(node); 262 formatter.sink = File.LockingTextWriter.init; 263 skipWhitespace(output, tokens, i); 264 writeToken(output, tokens[i]); 265 i++; 266 suffixLoop: while (i < tokens.length) switch (tokens[i].type) 267 { 268 case tok!"(": skipAndWrite!("(", ")")(output, tokens, i); break; 269 case tok!"[": skip!("[", "]")(tokens, i); break; 270 case tok!"*": i++; break; 271 default: break suffixLoop; 272 } 273 markers = markers[1 .. $]; 274 break markerLoop; 275 } 276 } 277 278 if (i >= tokens.length) 279 break; 280 281 switch (tokens[i].type) 282 { 283 case tok!"asm": 284 skipAsmBlock(output, tokens, i); 285 goto default; 286 case tok!"catch": 287 if (!dip65) 288 goto default; 289 size_t j = i + 1; 290 while (j < tokens.length && (tokens[j] == tok!"whitespace" || tokens[j] == tok!"comment")) 291 j++; 292 if (j < tokens.length && tokens[j].type != tok!"(") 293 { 294 output.write("catch (Throwable)"); 295 break; 296 } 297 else 298 goto default; 299 case tok!"deprecated": 300 if (dip64) 301 output.write("@"); 302 output.writeToken(tokens[i]); 303 i++; 304 if (i < tokens.length && tokens[i] == tok!"(") 305 skipAndWrite!("(", ")")(output, tokens, i); 306 if (i < tokens.length) 307 goto default; 308 else 309 break; 310 case tok!"body": 311 if (dip1003) 312 output.write("do"); 313 else 314 output.write("body"); 315 break; 316 case tok!"stringLiteral": 317 immutable size_t stringBookmark = i; 318 while (tokens[i] == tok!"stringLiteral") 319 { 320 i++; 321 skipWhitespace(output, tokens, i, false); 322 } 323 immutable bool parensNeeded = stringBookmark + 1 != i && tokens[i] == tok!"."; 324 i = stringBookmark; 325 if (parensNeeded) 326 output.write("("); 327 output.writeToken(tokens[i]); 328 i++; 329 skipWhitespace(output, tokens, i); 330 while (tokens[i] == tok!"stringLiteral") 331 { 332 output.write("~ "); 333 output.writeToken(tokens[i]); 334 i++; 335 skipWhitespace(output, tokens, i); 336 } 337 if (parensNeeded) 338 output.write(")"); 339 if (i < tokens.length) 340 goto default; 341 else 342 break; 343 case tok!"override": 344 case tok!"final": 345 case tok!"abstract": 346 case tok!"align": 347 case tok!"pure": 348 case tok!"nothrow": 349 if (!dip64) 350 goto default; 351 output.write("@"); 352 output.write(str(tokens[i].type)); 353 break; 354 case tok!"alias": 355 bool multipleAliases = false; 356 bool oldStyle = true; 357 output.writeToken(tokens[i]); // alias 358 i++; 359 size_t j = i + 1; 360 361 int depth; 362 loop: while (j < tokens.length) switch (tokens[j].type) 363 { 364 case tok!"(": 365 depth++; 366 j++; 367 break; 368 case tok!")": 369 depth--; 370 if (depth < 0) 371 { 372 oldStyle = false; 373 break loop; 374 } 375 j++; 376 break; 377 case tok!"=": 378 case tok!"this": 379 j++; 380 oldStyle = false; 381 break; 382 case tok!",": 383 j++; 384 if (depth == 0) 385 multipleAliases = true; 386 break; 387 case tok!";": 388 break loop; 389 default: 390 j++; 391 break; 392 } 393 394 if (!oldStyle) foreach (k; i .. j + 1) 395 { 396 output.writeToken(tokens[k]); 397 i = k; 398 } 399 else 400 { 401 skipWhitespace(output, tokens, i); 402 403 size_t beforeStart = i; 404 size_t beforeEnd = beforeStart; 405 406 loop2: while (beforeEnd < tokens.length) switch (tokens[beforeEnd].type) 407 { 408 case tok!"bool": 409 case tok!"byte": 410 case tok!"ubyte": 411 case tok!"short": 412 case tok!"ushort": 413 case tok!"int": 414 case tok!"uint": 415 case tok!"long": 416 case tok!"ulong": 417 case tok!"char": 418 case tok!"wchar": 419 case tok!"dchar": 420 case tok!"float": 421 case tok!"double": 422 case tok!"real": 423 case tok!"ifloat": 424 case tok!"idouble": 425 case tok!"ireal": 426 case tok!"cfloat": 427 case tok!"cdouble": 428 case tok!"creal": 429 case tok!"void": 430 beforeEnd++; 431 break loop2; 432 case tok!".": 433 beforeEnd++; 434 goto case; 435 case tok!"identifier": 436 skipIdentifierChain(output, tokens, beforeEnd); 437 break loop2; 438 case tok!"typeof": 439 beforeEnd++; 440 skip!("(", ")")(tokens, beforeEnd); 441 skipWhitespace(output, tokens, beforeEnd, false); 442 if (tokens[beforeEnd] == tok!".") 443 skipIdentifierChain(output, tokens, beforeEnd); 444 break loop2; 445 case tok!"@": 446 beforeEnd++; 447 if (tokens[beforeEnd] == tok!"identifier") 448 beforeEnd++; 449 if (tokens[beforeEnd] == tok!"(") 450 skip!("(", ")")(tokens, beforeEnd); 451 skipWhitespace(output, tokens, beforeEnd, false); 452 break; 453 case tok!"static": 454 case tok!"const": 455 case tok!"immutable": 456 case tok!"inout": 457 case tok!"shared": 458 case tok!"extern": 459 case tok!"nothrow": 460 case tok!"pure": 461 case tok!"__vector": 462 beforeEnd++; 463 skipWhitespace(output, tokens, beforeEnd, false); 464 if (tokens[beforeEnd] == tok!"(") 465 skip!("(", ")")(tokens, beforeEnd); 466 if (beforeEnd >= tokens.length) 467 break loop2; 468 size_t k = beforeEnd; 469 skipWhitespace(output, tokens, k, false); 470 if (k + 1 < tokens.length && tokens[k + 1].type == tok!";") 471 break loop2; 472 else 473 beforeEnd = k; 474 break; 475 default: 476 break loop2; 477 } 478 479 i = beforeEnd; 480 481 skipWhitespace(output, tokens, i, false); 482 483 if (tokens[i] == tok!"*" || tokens[i] == tok!"[" 484 || tokens[i] == tok!"function" || tokens[i] == tok!"delegate") 485 { 486 beforeEnd = i; 487 } 488 489 loop3: while (beforeEnd < tokens.length) switch (tokens[beforeEnd].type) 490 { 491 case tok!"*": 492 beforeEnd++; 493 size_t m = beforeEnd; 494 skipWhitespace(output, tokens, m, false); 495 if (m < tokens.length && (tokens[m] == tok!"*" 496 || tokens[m] == tok!"[" || tokens[m] == tok!"function" 497 || tokens[m] == tok!"delegate")) 498 { 499 beforeEnd = m; 500 } 501 break; 502 case tok!"[": 503 skip!("[", "]")(tokens, beforeEnd); 504 size_t m = beforeEnd; 505 skipWhitespace(output, tokens, m, false); 506 if (m < tokens.length && (tokens[m] == tok!"*" 507 || tokens[m] == tok!"[" || tokens[m] == tok!"function" 508 || tokens[m] == tok!"delegate")) 509 { 510 beforeEnd = m; 511 } 512 break; 513 case tok!"function": 514 case tok!"delegate": 515 beforeEnd++; 516 skipWhitespace(output, tokens, beforeEnd, false); 517 skip!("(", ")")(tokens, beforeEnd); 518 size_t l = beforeEnd; 519 skipWhitespace(output, tokens, l, false); 520 loop4: while (l < tokens.length) switch (tokens[l].type) 521 { 522 case tok!"const": 523 case tok!"nothrow": 524 case tok!"pure": 525 case tok!"immutable": 526 case tok!"inout": 527 case tok!"shared": 528 beforeEnd = l + 1; 529 l = beforeEnd; 530 skipWhitespace(output, tokens, l, false); 531 if (l < tokens.length && tokens[l].type == tok!"identifier") 532 { 533 beforeEnd = l - 1; 534 break loop4; 535 } 536 break; 537 case tok!"@": 538 beforeEnd = l + 1; 539 skipWhitespace(output, tokens, beforeEnd, false); 540 if (tokens[beforeEnd] == tok!"(") 541 skip!("(", ")")(tokens, beforeEnd); 542 else 543 { 544 beforeEnd++; // identifier 545 skipWhitespace(output, tokens, beforeEnd, false); 546 if (tokens[beforeEnd] == tok!"(") 547 skip!("(", ")")(tokens, beforeEnd); 548 } 549 l = beforeEnd; 550 skipWhitespace(output, tokens, l, false); 551 if (l < tokens.length && tokens[l].type == tok!"identifier") 552 { 553 beforeEnd = l - 1; 554 break loop4; 555 } 556 break; 557 default: 558 break loop4; 559 } 560 break; 561 default: 562 break loop3; 563 } 564 565 i = beforeEnd; 566 skipWhitespace(output, tokens, i, false); 567 568 output.writeToken(tokens[i]); 569 output.write(" = "); 570 foreach (l; beforeStart .. beforeEnd) 571 output.writeToken(tokens[l]); 572 573 if (multipleAliases) 574 { 575 i++; 576 skipWhitespace(output, tokens, i, false); 577 while (tokens[i] == tok!",") 578 { 579 i++; // , 580 output.write(", "); 581 skipWhitespace(output, tokens, i, false); 582 output.writeToken(tokens[i]); 583 output.write(" = "); 584 foreach (l; beforeStart .. beforeEnd) 585 output.writeToken(tokens[l]); 586 } 587 } 588 } 589 break; 590 default: 591 output.writeToken(tokens[i]); 592 break; 593 } 594 } 595 } 596 597 /** 598 * The types of special token ranges identified by the parsing pass 599 */ 600 enum SpecialMarkerType 601 { 602 /// Function declarations such as "const int foo();" 603 functionAttributePrefix, 604 /// Variable and parameter declarations such as "int bar[]" 605 cStyleArray, 606 /// The location of a closing brace for an interface, class, struct, union, 607 /// or enum. 608 bodyEnd 609 } 610 611 /** 612 * Identifies ranges of tokens in the source tokens that need to be rewritten 613 */ 614 struct SpecialMarker 615 { 616 /// Range type 617 SpecialMarkerType type; 618 619 /// Begin byte position (before relocateMarkers) or token index 620 /// (after relocateMarkers) 621 size_t index; 622 623 /// The type suffix AST nodes that should be moved 624 const(TypeSuffix[]) nodes; 625 626 /// The function attribute such as const, immutable, or inout to move 627 string functionAttribute; 628 } 629 630 /** 631 * Scans a module's parsed AST and looks for C-style array variables and 632 * parameters, storing the locations in the markers array. 633 */ 634 class DFixVisitor : ASTVisitor 635 { 636 // C-style arrays variables 637 override void visit(const VariableDeclaration varDec) 638 { 639 if (varDec.declarators.length == 0) 640 return; 641 markers ~= SpecialMarker(SpecialMarkerType.cStyleArray, 642 varDec.declarators[0].name.index, varDec.declarators[0].cstyle); 643 } 644 645 // C-style array parameters 646 override void visit(const Parameter param) 647 { 648 if (param.cstyle.length > 0) 649 markers ~= SpecialMarker(SpecialMarkerType.cStyleArray, param.name.index, 650 param.cstyle); 651 param.accept(this); 652 } 653 654 // interface, union, class, struct body closing braces 655 override void visit(const StructBody structBody) 656 { 657 structBody.accept(this); 658 markers ~= SpecialMarker(SpecialMarkerType.bodyEnd, structBody.endLocation); 659 } 660 661 // enum body closing braces 662 override void visit(const EnumBody enumBody) 663 { 664 enumBody.accept(this); 665 // skip over enums whose body is a single semicolon 666 if (enumBody.endLocation == 0 && enumBody.startLocation == 0) 667 return; 668 markers ~= SpecialMarker(SpecialMarkerType.bodyEnd, enumBody.endLocation); 669 } 670 671 // Confusing placement of function attributes 672 override void visit(const Declaration dec) 673 { 674 if (dec.functionDeclaration is null) 675 goto end; 676 if (dec.attributes.length == 0) 677 goto end; 678 foreach (attr; dec.attributes) 679 { 680 if (attr.attribute == tok!"") 681 continue; 682 if (attr.attribute == tok!"const" 683 || attr.attribute == tok!"inout" 684 || attr.attribute == tok!"immutable") 685 { 686 markers ~= SpecialMarker(SpecialMarkerType.functionAttributePrefix, 687 attr.attribute.index, null, str(attr.attribute.type)); 688 } 689 } 690 end: 691 dec.accept(this); 692 } 693 694 alias visit = ASTVisitor.visit; 695 696 /// Parts of the source file identified as needing a rewrite 697 SpecialMarker[] markers; 698 } 699 700 /** 701 * Converts the marker index from a byte index into the source code to an index 702 * into the tokens array. 703 */ 704 void relocateMarkers(SpecialMarker[] markers, const(Token)[] tokens) pure nothrow @nogc 705 { 706 foreach (ref marker; markers) 707 { 708 if (marker.type != SpecialMarkerType.cStyleArray) 709 continue; 710 size_t index = 0; 711 while (tokens[index].index != marker.index) 712 index++; 713 marker.index = index - 1; 714 } 715 } 716 717 /** 718 * Writes a token to the output file. 719 */ 720 void writeToken(File output, ref const(Token) token) 721 { 722 output.write(token.text is null ? str(token.type) : token.text); 723 } 724 725 void skipAndWrite(alias Open, alias Close)(File output, const(Token)[] tokens, ref size_t index) 726 { 727 int depth = 1; 728 writeToken(output, tokens[index]); 729 index++; 730 while (index < tokens.length && depth > 0) switch (tokens[index].type) 731 { 732 case tok!Open: 733 depth++; 734 writeToken(output, tokens[index]); 735 index++; 736 break; 737 case tok!Close: 738 depth--; 739 writeToken(output, tokens[index]); 740 index++; 741 break; 742 default: 743 writeToken(output, tokens[index]); 744 index++; 745 break; 746 } 747 } 748 749 /** 750 * Skips balanced parens, braces, or brackets. index will be incremented to 751 * index tokens just after the balanced closing token. 752 */ 753 void skip(alias Open, alias Close)(const(Token)[] tokens, ref size_t index) 754 { 755 int depth = 1; 756 index++; 757 while (index < tokens.length && depth > 0) switch (tokens[index].type) 758 { 759 case tok!Open: depth++; index++; break; 760 case tok!Close: depth--; index++; break; 761 default: index++; break; 762 } 763 } 764 765 /** 766 * Skips whitespace tokens, incrementing index until it indexes tokens at a 767 * non-whitespace token. 768 */ 769 void skipWhitespace(File output, const(Token)[] tokens, ref size_t index, bool print = true) 770 { 771 while (index < tokens.length && (tokens[index] == tok!"whitespace" || tokens[index] == tok!"comment")) 772 { 773 if (print) output.writeToken(tokens[index]); 774 index++; 775 } 776 } 777 778 /** 779 * Advances index until it indexs the token just after an identifier or template 780 * chain. 781 */ 782 void skipIdentifierChain(File output, const(Token)[] tokens, ref size_t index, bool print = false) 783 { 784 loop: while (index < tokens.length) switch (tokens[index].type) 785 { 786 case tok!".": 787 if (print) 788 writeToken(output, tokens[index]); 789 index++; 790 skipWhitespace(output, tokens, index, false); 791 break; 792 case tok!"identifier": 793 if (print) 794 writeToken(output, tokens[index]); 795 index++; 796 size_t i = index; 797 skipWhitespace(output, tokens, i, false); 798 if (tokens[i] == tok!"!") 799 { 800 i++; 801 if (print) 802 writeToken(output, tokens[index]); 803 index++; 804 skipWhitespace(output, tokens, i, false); 805 if (tokens[i] == tok!"(") 806 { 807 if (print) 808 skipAndWrite!("(", ")")(output, tokens, i); 809 else 810 skip!("(", ")")(tokens, i); 811 index = i; 812 } 813 else 814 { 815 i++; 816 if (print) 817 writeToken(output, tokens[index]); 818 index++; 819 } 820 } 821 if (tokens[i] != tok!".") 822 break loop; 823 break; 824 case tok!"whitespace": 825 index++; 826 break; 827 default: 828 break loop; 829 } 830 } 831 832 /** 833 * Skips over an attribute 834 */ 835 void skipAttribute(File output, const(Token)[] tokens, ref size_t i) 836 { 837 switch (tokens[i].type) 838 { 839 case tok!"@": 840 output.writeToken(tokens[i]); 841 i++; // @ 842 skipWhitespace(output, tokens, i, true); 843 switch (tokens[i].type) 844 { 845 case tok!"identifier": 846 output.writeToken(tokens[i]); 847 i++; // identifier 848 skipWhitespace(output, tokens, i, true); 849 if (tokens[i].type == tok!"(") 850 goto case tok!"("; 851 break; 852 case tok!"(": 853 int depth = 1; 854 output.writeToken(tokens[i]); 855 i++; 856 while (i < tokens.length && depth > 0) switch (tokens[i].type) 857 { 858 case tok!"(": depth++; output.writeToken(tokens[i]); i++; break; 859 case tok!")": depth--; output.writeToken(tokens[i]); i++; break; 860 default: output.writeToken(tokens[i]); i++; break; 861 } 862 break; 863 default: 864 break; 865 } 866 break; 867 case tok!"nothrow": 868 case tok!"pure": 869 output.writeToken(tokens[i]); 870 i++; 871 break; 872 default: 873 break; 874 } 875 } 876 877 /** 878 * Skips over (and prints) an asm block 879 */ 880 void skipAsmBlock(File output, const(Token)[] tokens, ref size_t i) 881 { 882 import std.exception : enforce; 883 884 output.write("asm"); 885 i++; // asm 886 skipWhitespace(output, tokens, i); 887 loop: while (true) switch (tokens[i].type) 888 { 889 case tok!"@": 890 case tok!"nothrow": 891 case tok!"pure": 892 skipAttribute(output, tokens, i); 893 skipWhitespace(output, tokens, i); 894 break; 895 case tok!"{": 896 break loop; 897 default: 898 break loop; 899 } 900 enforce(tokens[i].type == tok!"{"); 901 output.write("{"); 902 i++; // { 903 int depth = 1; 904 while (depth > 0 && i < tokens.length) switch (tokens[i].type) 905 { 906 case tok!"{": depth++; goto default; 907 case tok!"}": depth--; goto default; 908 default: writeToken(output, tokens[i]); i++; break; 909 } 910 } 911 912 /** 913 * Dummy message output function for the lexer/parser 914 */ 915 void reportErrors(string fileName, size_t lineNumber, size_t columnNumber, 916 string message, bool isError) 917 { 918 import std.stdio : stderr; 919 920 if (!isError) 921 return; 922 stderr.writefln("%s(%d:%d)[error]: %s", fileName, lineNumber, columnNumber, message); 923 }