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