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