1 module format.parser; 2 3 /** 4 * While we already have a parser in libd, we cannot use it here. 5 * This is because libd's parser is meant to validate that the source 6 * is well a formed D program. However, we want to be able to format 7 * even incomplete programs as part of the developper's process. 8 * 9 * This parser, on the other hand, is meant to recognize common patterns 10 * in the language, without ensuring that they are indeed correct. 11 */ 12 struct Parser { 13 private: 14 import source.dlexer; 15 TokenRange trange; 16 17 import format.chunk; 18 Builder builder; 19 20 bool needDoubleIndent = false; 21 bool doubleIndentBlock = false; 22 bool canBeDeclaration = false; 23 24 enum Mode { 25 Declaration, 26 Statement, 27 Parameter, 28 } 29 30 Mode mode; 31 32 auto changeMode(Mode m) { 33 static struct Guard { 34 ~this() { 35 parser.mode = oldMode; 36 } 37 38 private: 39 Parser* parser; 40 Mode oldMode; 41 } 42 43 Mode oldMode = mode; 44 mode = m; 45 46 return Guard(&this, oldMode); 47 } 48 49 /** 50 * When we can't parse we skip and forward chunks "as this" 51 */ 52 Location skipped; 53 54 /** 55 * Comments to be emitted before the next token. 56 * - inFlightComments: Comments which are on their own. 57 * - nextComments: Comment attached to what comes next. 58 */ 59 Location[] inFlightComments; 60 Location[] nextComments; 61 62 /** 63 * Passthrough for portion of code not to be formatted. 64 * 65 * When formatting is disabled, we keep parsing anyways. This ensures 66 * the state of affairs, such as identation levels, are kept track off. 67 * However, nothign is sent to the builder as parsing progresses, and 68 * everything is sent as one signle chunk at the end of it. 69 */ 70 Position sdfmtOffStart; 71 72 bool skipFormatting() const { 73 return sdfmtOffStart != Position(); 74 } 75 76 public: 77 import source.context; 78 this(Position base, Context context) { 79 this.trange = 80 lex(base, context).withStringDecoding(false).withComments(); 81 } 82 83 Chunk[] parse() in { 84 assert(match(TokenType.Begin)); 85 } do { 86 // Eat the begin token and get the game rolling. 87 nextToken(); 88 parseModule(); 89 90 assert(match(TokenType.End)); 91 92 emitSkippedTokens(); 93 flushComments(); 94 95 return builder.build(); 96 } 97 98 private: 99 /** 100 * Chunk builder facilities 101 */ 102 void write(Location loc, string s) { 103 if (skipFormatting()) { 104 return; 105 } 106 107 if (newLineCount(loc) == 0) { 108 builder.write(s); 109 return; 110 } 111 112 // We have a multi line chunk. 113 import std.array; 114 foreach (i, l; s.split('\n')) { 115 if (i > 0) { 116 builder.split(true, true); 117 builder.newline(1); 118 } 119 120 builder.write(l); 121 } 122 } 123 124 void space() { 125 if (skipFormatting()) { 126 return; 127 } 128 129 builder.space(); 130 } 131 132 void newline() { 133 newline(newLineCount()); 134 } 135 136 void newline(int nl) { 137 if (skipFormatting()) { 138 return; 139 } 140 141 builder.newline(nl); 142 } 143 144 void clearSeparator() { 145 if (skipFormatting()) { 146 return; 147 } 148 149 builder.clearSeparator(); 150 } 151 152 void split(bool glued = false, bool continuation = false) { 153 emitRawContent(); 154 builder.split(glued || skipFormatting(), continuation); 155 } 156 157 auto indent(uint level = 1) { 158 return builder.indent(level); 159 } 160 161 auto unindent(uint level = 1) { 162 return builder.unindent(level); 163 } 164 165 import format.span; 166 auto span(S = Span, T...)(T args) { 167 emitSkippedTokens(); 168 emitInFlightComments(); 169 170 return builder.span!S(args); 171 } 172 173 auto spliceSpan(S = Span, T...)(T args) { 174 emitSkippedTokens(); 175 emitInFlightComments(); 176 177 return builder.spliceSpan!S(args); 178 } 179 180 auto block() { 181 emitRawContent(); 182 return builder.block(); 183 } 184 185 /** 186 * Miscellaneous and conveniences. 187 */ 188 @property 189 auto context() { 190 return trange.context; 191 } 192 193 /** 194 * Whitespace management. 195 */ 196 import source.location; 197 uint getLineNumber(Position p) { 198 return p.getFullPosition(context).getLineNumber(); 199 } 200 201 int newLineCount(Position start, Position stop) { 202 return getLineNumber(stop) - getLineNumber(start); 203 } 204 205 int newLineCount(Location location) { 206 return newLineCount(location.start, location.stop); 207 } 208 209 int newLineCount(ref TokenRange r) { 210 return newLineCount(r.previous, r.front.location.start); 211 } 212 213 int newLineCount() { 214 return newLineCount(trange); 215 } 216 217 uint getSourceOffset(Position p) { 218 return p.getFullPosition(context).getSourceOffset(); 219 } 220 221 int whiteSpaceLength(Position start, Position stop) { 222 return getSourceOffset(stop) - getSourceOffset(start); 223 } 224 225 int whiteSpaceLength() { 226 return whiteSpaceLength(trange.previous, token.location.start); 227 } 228 229 void emitSourceBasedWhiteSpace(Position previous, Location current) { 230 if (auto nl = newLineCount(previous, current.start)) { 231 newline(nl); 232 return; 233 } 234 235 if (whiteSpaceLength(previous, current.start) > 0) { 236 space(); 237 } 238 } 239 240 void emitSourceBasedWhiteSpace() { 241 emitSourceBasedWhiteSpace(trange.previous, token.location); 242 } 243 244 /** 245 * Token processing. 246 */ 247 @property 248 Token token() const { 249 return trange.front; 250 } 251 252 bool match(TokenType t) { 253 return token.type == t; 254 } 255 256 auto runOnType(TokenType T, alias fun)() { 257 if (match(T)) { 258 return fun(); 259 } 260 } 261 262 void nextToken() { 263 emitSkippedTokens(); 264 flushComments(); 265 266 if (match(TokenType.End)) { 267 // We reached the end of our input. 268 return; 269 } 270 271 // Process current token. 272 write(token.location, token.toString(context)); 273 274 trange.popFront(); 275 parseComments(); 276 } 277 278 /** 279 * We skip over portions of the code we can't parse. 280 */ 281 void skipToken() { 282 flushComments(); 283 284 if (skipped.length == 0) { 285 emitSourceBasedWhiteSpace(); 286 space(); 287 split(); 288 289 skipped = Location(trange.previous, token.location.stop); 290 } else { 291 skipped.spanTo(token.location); 292 } 293 294 if (match(TokenType.End)) { 295 // We skipped until the end. 296 return; 297 } 298 299 trange.popFront(); 300 301 // Skip over comments that look related too. 302 while (match(TokenType.Comment) && newLineCount() == 0) { 303 skipped.spanTo(token.location); 304 trange.popFront(); 305 } 306 307 parseComments(); 308 } 309 310 void emitSkippedTokens() { 311 if (skipped.length == 0) { 312 return; 313 } 314 315 import std.string; 316 auto str = skipped.getFullLocation(context).getSlice().strip(); 317 write(skipped, str); 318 skipped = Location.init; 319 320 emitSourceBasedWhiteSpace(); 321 split(); 322 } 323 324 /** 325 * Unformateed code management. 326 */ 327 void emitRawContent() { 328 auto upTo = inFlightComments.length > 0 329 ? inFlightComments[0] 330 : nextComments.length > 0 ? nextComments[0] : token.location; 331 332 emitRawContent(upTo.start); 333 } 334 335 void emitRawContent(Position upTo) { 336 if (!skipFormatting()) { 337 return; 338 } 339 340 builder.write( 341 Location(sdfmtOffStart, upTo).getFullLocation(context).getSlice()); 342 sdfmtOffStart = upTo; 343 } 344 345 /** 346 * Comments management 347 */ 348 void emitComment(Location loc, Position previous) { 349 emitSourceBasedWhiteSpace(previous, loc); 350 351 import std.string; 352 auto comment = loc.getFullLocation(context).getSlice().strip(); 353 if (skipFormatting() && comment == "// sdfmt on") { 354 emitRawContent(loc.start); 355 sdfmtOffStart = Position(); 356 } 357 358 write(loc, comment); 359 360 if (comment == "// sdfmt off") { 361 sdfmtOffStart = loc.stop; 362 assert(skipFormatting(), "We should start skipping."); 363 } 364 365 // Make sure we have a line split after // style comments. 366 if (!skipFormatting() && comment.startsWith("//")) { 367 newline(1); 368 split(); 369 } 370 } 371 372 void emitComments(ref Location[] commentBlock, Location nextTokenLoc) { 373 if (commentBlock.length == 0) { 374 return; 375 } 376 377 scope(success) { 378 commentBlock = []; 379 } 380 381 Position previous = commentBlock[0].start; 382 383 foreach (loc; commentBlock) { 384 scope(success) { 385 previous = loc.stop; 386 } 387 388 emitComment(loc, previous); 389 } 390 391 emitSourceBasedWhiteSpace(previous, nextTokenLoc); 392 } 393 394 void emitInFlightComments() { 395 auto nextTokenLoc = 396 nextComments.length > 0 ? nextComments[0] : token.location; 397 398 emitComments(inFlightComments, nextTokenLoc); 399 } 400 401 void flushComments() { 402 emitInFlightComments(); 403 emitComments(nextComments, token.location); 404 } 405 406 void parseComments() in { 407 assert(inFlightComments == []); 408 assert(nextComments == []); 409 } do { 410 if (!match(TokenType.Comment)) { 411 return; 412 } 413 414 emitSkippedTokens(); 415 416 /** 417 * We distrube comments in 3 groups: 418 * 1 - The comments attached to the previous structural element. 419 * 2 - The comments in flight between two structural elements. 420 * 3 - The comments attached to the next structural element. 421 * We want to emit group 1 right away, but wait for later when 422 * emitting groups 2 and 3. 423 */ 424 while (match(TokenType.Comment) && newLineCount() == 0) { 425 emitComment(token.location, trange.previous); 426 trange.popFront(); 427 } 428 429 emitSourceBasedWhiteSpace(); 430 431 Location[] commentBlock = []; 432 while (match(TokenType.Comment)) { 433 commentBlock ~= token.location; 434 trange.popFront(); 435 436 if (newLineCount() < 2) { 437 continue; 438 } 439 440 inFlightComments ~= commentBlock; 441 commentBlock = []; 442 } 443 444 nextComments = commentBlock; 445 } 446 447 /** 448 * Parsing 449 */ 450 void parseModule() { 451 auto guard = changeMode(Mode.Declaration); 452 453 while (!match(TokenType.End)) { 454 parseStructuralElement(); 455 } 456 } 457 458 void parseStructuralElement() { 459 emitInFlightComments(); 460 461 canBeDeclaration = true; 462 463 Entry: 464 switch (token.type) with (TokenType) { 465 case End: 466 return; 467 468 case Module: 469 parseModuleDeclaration(); 470 break; 471 472 /** 473 * Misc 474 */ 475 case DotDotDot: 476 nextToken(); 477 return; 478 479 /** 480 * Statements 481 */ 482 case OpenBrace: 483 parseBlock(mode); 484 485 // Blocks do not end with a semicolon. 486 return; 487 488 case Identifier: 489 auto lookahead = trange.getLookahead(); 490 lookahead.popFront(); 491 auto t = lookahead.front.type; 492 493 if (mode == Mode.Parameter 494 && (t == Colon || t == Equal || t == DotDotDot)) { 495 parseTemplateParameter(); 496 break; 497 } 498 499 if (t != Colon) { 500 // This is an expression or a declaration. 501 goto default; 502 } 503 504 lookahead.popFront(); 505 if (newLineCount(lookahead) == 0) { 506 nextToken(); 507 nextToken(); 508 space(); 509 goto Entry; 510 } 511 512 { 513 auto guard = unindent(); 514 newline(2); 515 nextToken(); 516 } 517 518 parseColonBlock(); 519 break; 520 521 case If: 522 parseIf(); 523 break; 524 525 case Version, Debug: 526 parseVersion(); 527 break; 528 529 case Else: 530 parseElse(); 531 break; 532 533 case While: 534 parseWhile(); 535 break; 536 537 case Do: 538 parseDoWhile(); 539 break; 540 541 case For: 542 parseFor(); 543 break; 544 545 case Foreach, ForeachReverse: 546 withCaseLevelIndent!parseForeach(); 547 break; 548 549 case Return: 550 // If this is a parameter, then return is a storage class. 551 if (mode == Mode.Parameter) { 552 goto default; 553 } 554 555 goto ReturnLike; 556 557 case Throw: 558 goto ReturnLike; 559 560 ReturnLike: 561 parseReturn(); 562 break; 563 564 case Break, Continue: 565 nextToken(); 566 567 if (match(Identifier)) { 568 space(); 569 nextToken(); 570 } 571 572 break; 573 574 case With: 575 parseWith(); 576 break; 577 578 case Switch: 579 parseSwitch(); 580 break; 581 582 case Case: 583 { 584 auto guard = unindent(); 585 newline(); 586 587 while (true) { 588 nextToken(); 589 space(); 590 591 parseList!parseExpression(TokenType.Colon); 592 593 if (!match(DotDot)) { 594 break; 595 } 596 597 space(); 598 nextToken(); 599 space(); 600 } 601 } 602 603 parseColonBlock(); 604 break; 605 606 case Default: 607 { 608 auto guard = unindent(); 609 newline(); 610 nextToken(); 611 } 612 613 parseColonBlock(); 614 break; 615 616 case Goto: 617 nextToken(); 618 if (match(Identifier) || match(Default)) { 619 space(); 620 nextToken(); 621 } else if (match(Case)) { 622 space(); 623 nextToken(); 624 625 if (!match(Semicolon)) { 626 space(); 627 parseExpression(); 628 } 629 } 630 631 break; 632 633 case Try: 634 parseTry(); 635 break; 636 637 case Catch: 638 parseCatch(); 639 break; 640 641 case Finally: 642 parseFinally(); 643 break; 644 645 case Scope: 646 parseScope(); 647 break; 648 649 case Assert: 650 parseExpression(); 651 break; 652 653 /** 654 * Compile time constructs. 655 */ 656 case Static: 657 withCaseLevelIndent!parseStatic(); 658 break; 659 660 case Mixin: 661 parseMixin(); 662 break; 663 664 /** 665 * Declaration 666 */ 667 case This: 668 // This template parameters. 669 auto lookahead = trange.getLookahead(); 670 lookahead.popFront(); 671 672 auto t = lookahead.front.type; 673 if (t == TokenType.Identifier) { 674 nextToken(); 675 space(); 676 parseTypedDeclaration(); 677 break; 678 } 679 680 if (t != TokenType.OpenParen || mode != Mode.Declaration) { 681 // This is an expression. 682 goto default; 683 } 684 685 parseConstructor(); 686 break; 687 688 case Tilde: 689 // Check for destructors. 690 auto lookahead = trange.getLookahead(); 691 lookahead.popFront(); 692 693 if (lookahead.front.type != TokenType.This) { 694 // This is an expression. 695 goto default; 696 } 697 698 lookahead.popFront(); 699 700 auto t = lookahead.front.type; 701 if (t != TokenType.OpenParen || mode != Mode.Declaration) { 702 // This is an expression. 703 goto default; 704 } 705 706 parseDestructor(); 707 break; 708 709 case Template: 710 parseTemplate(); 711 break; 712 713 case Import: 714 auto lookahead = trange.getLookahead(); 715 lookahead.popFront(); 716 717 if (lookahead.front.type == TokenType.OpenParen) { 718 // This is an import expression. 719 goto default; 720 } 721 722 parseImport(); 723 break; 724 725 case Unittest: 726 nextToken(); 727 space(); 728 729 if (match(Identifier)) { 730 nextToken(); 731 space(); 732 } 733 734 parseBlock(Mode.Statement); 735 736 // Blocks do not end with a semicolon. 737 return; 738 739 case Invariant: 740 nextToken(); 741 parseArgumentList(); 742 743 if (!match(OpenBrace)) { 744 break; 745 } 746 747 space(); 748 parseBlock(Mode.Statement); 749 750 // Blocks do not end with a semicolon. 751 return; 752 753 case Struct, Union, Class, Interface: 754 parseAggregate(); 755 break; 756 757 default: 758 if (parseStorageClassDeclaration()) { 759 break; 760 } 761 762 if (!parseIdentifier()) { 763 // We made no progress, start skipping. 764 skipToken(); 765 return; 766 } 767 768 if (match(Identifier)) { 769 // We have a declaration. 770 parseTypedDeclaration(); 771 break; 772 } 773 774 // We just have some kind of expression. 775 parseAssignExpressionSuffix(); 776 break; 777 } 778 779 if (mode != Mode.Parameter) { 780 if (match(TokenType.Semicolon)) { 781 nextToken(); 782 newline(); 783 } else { 784 emitSourceBasedWhiteSpace(); 785 } 786 } 787 } 788 789 /** 790 * Structural elements. 791 */ 792 void parseModuleDeclaration() in { 793 assert(match(TokenType.Module)); 794 } do { 795 nextToken(); 796 space(); 797 parseIdentifier(); 798 } 799 800 /** 801 * Identifiers 802 */ 803 enum IdentifierKind { 804 None, 805 Symbol, 806 Type, 807 Expression, 808 } 809 810 bool parseIdentifier(IdentifierKind expected = IdentifierKind.Symbol) { 811 flushComments(); 812 auto guard = span(); 813 814 parseIdentifierPrefix(); 815 816 auto kind = parseBaseIdentifier(expected); 817 if (kind == IdentifierKind.None) { 818 return false; 819 } 820 821 kind = parseIdentifierSuffix(kind); 822 823 if (expected <= IdentifierKind.Symbol) { 824 return true; 825 } 826 827 // We expect something specific. 828 while (kind == IdentifierKind.Symbol) { 829 kind = parseIdentifierSuffix(expected); 830 } 831 832 return true; 833 } 834 835 void parseIdentifierPrefix() { 836 while (true) { 837 switch (token.type) with (TokenType) { 838 // Prefixes. 839 case Dot: 840 case Ampersand: 841 case PlusPlus: 842 case MinusMinus: 843 case Star: 844 case Plus: 845 case Minus: 846 case Bang: 847 case Tilde: 848 nextToken(); 849 break; 850 851 case Cast: 852 nextToken(); 853 if (match(OpenParen)) { 854 nextToken(); 855 parseType(); 856 clearSeparator(); 857 } 858 859 runOnType!(CloseParen, nextToken)(); 860 space(); 861 split(); 862 break; 863 864 default: 865 return; 866 } 867 } 868 } 869 870 IdentifierKind parseBaseIdentifier(IdentifierKind kind) { 871 switch (token.type) with (TokenType) { 872 case Identifier: 873 nextToken(); 874 875 import source.parserutil; 876 auto lookahead = trange.getLookahead(); 877 auto t = getStorageClassTokenType(lookahead); 878 879 if (t != EqualMore) { 880 return kind; 881 } 882 883 // Lambda expression 884 parseStorageClasses(true); 885 space(); 886 nextToken(); 887 space(); 888 split(); 889 parseExpression(); 890 return IdentifierKind.Expression; 891 892 // Litterals 893 case This: 894 case Super: 895 case True: 896 case False: 897 case Null: 898 case IntegerLiteral: 899 case FloatLiteral: 900 case StringLiteral: 901 case CharacterLiteral: 902 case __File__: 903 case __Line__: 904 case Dollar: 905 nextToken(); 906 return IdentifierKind.Expression; 907 908 case __Traits: 909 nextToken(); 910 parseArgumentList(); 911 return IdentifierKind.Symbol; 912 913 case Assert, Import: 914 nextToken(); 915 parseArgumentList(); 916 return IdentifierKind.Expression; 917 918 case New: 919 nextToken(); 920 space(); 921 922 if (!match(Class)) { 923 parseType(); 924 parseArgumentList(); 925 return IdentifierKind.Expression; 926 } 927 928 // Ok new class. 929 nextToken(); 930 parseArgumentList(); 931 space(); 932 parseIdentifier(); 933 space(); 934 parseInlineBlock(Mode.Declaration); 935 936 return IdentifierKind.Expression; 937 938 case Is: 939 parseIsExpression(); 940 return IdentifierKind.Expression; 941 942 case OpenParen: { 943 import source.parserutil; 944 auto lookahead = trange.getLookahead(); 945 lookahead.popMatchingDelimiter!OpenParen(); 946 947 auto t = getStorageClassTokenType(lookahead); 948 if (t != OpenBrace && t != EqualMore && t != At && t != Nothrow 949 && t != Pure && t != Ref && t != Synchronized) { 950 // Not a lambda. 951 goto ParenIdentifier; 952 } 953 954 // We have a lambda. 955 parseParameterList(); 956 space(); 957 parseStorageClasses(true); 958 959 switch (token.type) { 960 case OpenBrace: 961 goto Lambda; 962 963 case EqualMore: 964 nextToken(); 965 space(); 966 split(); 967 parseExpression(); 968 break; 969 970 default: 971 break; 972 } 973 974 return IdentifierKind.Expression; 975 } 976 977 ParenIdentifier: 978 auto guard = span(); 979 nextToken(); 980 981 // FIXME: Customize the list parsed based on kind. 982 parseExpression(); 983 984 runOnType!(CloseParen, nextToken)(); 985 return kind; 986 987 case OpenBrace: { 988 // Try to detect if it is a struct literal or a parameterless lambda. 989 import source.parserutil; 990 auto lookahead = trange.getLookahead(); 991 992 lookahead.popFront(); 993 if (lookahead.front.type != Identifier) { 994 goto Lambda; 995 } 996 997 lookahead.popFront(); 998 if (lookahead.front.type != Colon) { 999 goto Lambda; 1000 } 1001 1002 // We may still have a lambda starting with a labeled statement, 1003 // so we go on the hunt for a semicolon. 1004 lookahead.popFront(); 1005 while (true) { 1006 switch (lookahead.front.type) { 1007 case CloseBrace: 1008 goto StructLiteral; 1009 1010 case Semicolon: 1011 goto Lambda; 1012 1013 case End: 1014 // This is malformed, assume literal. 1015 goto StructLiteral; 1016 1017 case OpenParen: 1018 lookahead.popMatchingDelimiter!OpenParen(); 1019 break; 1020 1021 case OpenBrace: 1022 lookahead.popMatchingDelimiter!OpenBrace(); 1023 break; 1024 1025 case OpenBracket: 1026 lookahead.popMatchingDelimiter!OpenBracket(); 1027 break; 1028 1029 default: 1030 lookahead.popFront(); 1031 } 1032 } 1033 } 1034 1035 StructLiteral: 1036 parseStructLiteral(); 1037 return IdentifierKind.Expression; 1038 1039 case Function, Delegate: 1040 nextToken(); 1041 if (!match(OpenParen)) { 1042 // We have an explicit type. 1043 space(); 1044 parseType(); 1045 } 1046 1047 if (match(OpenParen)) { 1048 parseParameterList(); 1049 } 1050 1051 space(); 1052 parseStorageClasses(true); 1053 goto Lambda; 1054 1055 Lambda: 1056 parseInlineBlock(Mode.Statement); 1057 return IdentifierKind.Expression; 1058 1059 case OpenBracket: 1060 parseArrayLiteral(); 1061 return IdentifierKind.Expression; 1062 1063 case Typeid: 1064 nextToken(); 1065 parseArgumentList(); 1066 return IdentifierKind.Expression; 1067 1068 case Mixin: 1069 parseMixin(); 1070 1071 // Assume it is an expression. Technically, it could be a declaration, 1072 // but it does change anything from a formatting perspective. 1073 return IdentifierKind.Expression; 1074 1075 // Types 1076 case Typeof: 1077 nextToken(); 1078 if (!match(OpenParen)) { 1079 return IdentifierKind.Type; 1080 } 1081 1082 auto lookahead = trange.getLookahead(); 1083 lookahead.popFront(); 1084 1085 if (lookahead.front.type == Return) { 1086 nextToken(); 1087 nextToken(); 1088 nextToken(); 1089 } else { 1090 parseArgumentList(); 1091 } 1092 1093 return IdentifierKind.Type; 1094 1095 case Bool: 1096 case Byte, Ubyte: 1097 case Short, Ushort: 1098 case Int, Uint: 1099 case Long, Ulong: 1100 case Cent, Ucent: 1101 case Char, Wchar, Dchar: 1102 case Float, Double, Real: 1103 case Void: 1104 nextToken(); 1105 return IdentifierKind.Type; 1106 1107 // Type qualifiers 1108 case Const, Immutable, Inout, Shared: 1109 nextToken(); 1110 if (!match(OpenParen)) { 1111 space(); 1112 return parseBaseIdentifier(kind); 1113 } 1114 1115 nextToken(); 1116 parseType(); 1117 runOnType!(CloseParen, nextToken)(); 1118 return IdentifierKind.Type; 1119 1120 default: 1121 return IdentifierKind.None; 1122 } 1123 } 1124 1125 IdentifierKind parseIdentifierSuffix(IdentifierKind kind) { 1126 const tryDeclaration = canBeDeclaration; 1127 canBeDeclaration = false; 1128 1129 while (true) { 1130 switch (token.type) with (TokenType) { 1131 case Dot: 1132 split(); 1133 nextToken(); 1134 1135 if (!match(Identifier)) { 1136 return IdentifierKind.None; 1137 } 1138 1139 kind = IdentifierKind.Symbol; 1140 nextToken(); 1141 break; 1142 1143 case Star: 1144 final switch (kind) with (IdentifierKind) { 1145 case Type: 1146 // This is a pointer. 1147 nextToken(); 1148 continue; 1149 1150 case Expression: 1151 // This is a multiplication. 1152 return IdentifierKind.Expression; 1153 1154 case Symbol: 1155 // This could be either. Use lookahead. 1156 break; 1157 1158 case None: 1159 assert(0); 1160 } 1161 1162 auto lookahead = trange.getLookahead(); 1163 lookahead.popFront(); 1164 1165 IdentifierStarLookahead: while (true) { 1166 switch (lookahead.front.type) { 1167 case Identifier: 1168 // Lean toward Indentifier* Identifier being a delcaration. 1169 if (tryDeclaration) { 1170 goto IdentifierStarType; 1171 } 1172 1173 goto IdentifierStarExpression; 1174 1175 case Comma, CloseParen, CloseBracket: 1176 // This indicates some kind of termination, so assume a type. 1177 goto IdentifierStarType; 1178 1179 case Star, Function, Delegate: 1180 goto IdentifierStarType; 1181 1182 IdentifierStarType: 1183 kind = IdentifierKind.Type; 1184 nextToken(); 1185 break IdentifierStarLookahead; 1186 1187 case This: 1188 case Super: 1189 case True: 1190 case False: 1191 case Null: 1192 case IntegerLiteral: 1193 case FloatLiteral: 1194 case StringLiteral: 1195 case CharacterLiteral: 1196 case __File__: 1197 case __Line__: 1198 case Dollar: 1199 goto IdentifierStarExpression; 1200 1201 IdentifierStarExpression: 1202 return IdentifierKind.Expression; 1203 1204 case OpenBracket: 1205 import source.parserutil; 1206 lookahead.popMatchingDelimiter!OpenBracket(); 1207 continue; 1208 1209 default: 1210 // No idea what this is, move on. 1211 return IdentifierKind.Symbol; 1212 } 1213 } 1214 1215 break; 1216 1217 case Function, Delegate: 1218 kind = IdentifierKind.Type; 1219 space(); 1220 nextToken(); 1221 parseParameterList(); 1222 space(); 1223 if (!parseStorageClasses(true)) { 1224 // Not sure how this will fare in the presence of comments, 1225 // but this will have to do for now. 1226 clearSeparator(); 1227 } 1228 1229 break; 1230 1231 case Bang: 1232 if (isBangIsOrIn()) { 1233 // This is a binary expression. 1234 return IdentifierKind.Expression; 1235 } 1236 1237 // Template instance. 1238 kind = IdentifierKind.Symbol; 1239 nextToken(); 1240 if (!parseAliasList()) { 1241 parseBaseIdentifier(IdentifierKind.Symbol); 1242 } 1243 1244 break; 1245 1246 case PlusPlus, MinusMinus: 1247 kind = IdentifierKind.Expression; 1248 nextToken(); 1249 break; 1250 1251 case OpenParen: 1252 // FIXME: customize based on kind. 1253 kind = IdentifierKind.Expression; 1254 parseArgumentList(); 1255 break; 1256 1257 case OpenBracket: 1258 // FIXME: customize based on kind. 1259 // Technically, this is not an array literal, 1260 // but this should do for now. 1261 parseArrayLiteral(); 1262 break; 1263 1264 default: 1265 return kind; 1266 } 1267 } 1268 1269 assert(0, "DMD is not smart enough to figure out this is unreachable."); 1270 } 1271 1272 bool parseMixin() { 1273 if (!match(TokenType.Mixin)) { 1274 return false; 1275 } 1276 1277 nextToken(); 1278 1279 switch (token.type) with (TokenType) { 1280 case Template: 1281 space(); 1282 parseTemplate(); 1283 break; 1284 1285 case OpenParen: 1286 parseArgumentList(); 1287 break; 1288 1289 default: 1290 space(); 1291 parseIdentifier(); 1292 1293 if (match(Identifier)) { 1294 space(); 1295 nextToken(); 1296 } 1297 1298 break; 1299 } 1300 1301 return true; 1302 } 1303 1304 /** 1305 * Statements 1306 */ 1307 bool parseEmptyBlock() { 1308 if (!match(TokenType.CloseBrace) && !match(TokenType.End)) { 1309 return false; 1310 } 1311 1312 { 1313 // Flush comments so that they have the proper indentation. 1314 auto guard = indent(); 1315 flushComments(); 1316 } 1317 1318 nextToken(); 1319 return true; 1320 } 1321 1322 bool parseInlineBlock(Mode m) { 1323 auto oldNeedDoubleIndent = needDoubleIndent; 1324 scope(exit) { 1325 needDoubleIndent = oldNeedDoubleIndent; 1326 clearSeparator(); 1327 } 1328 1329 needDoubleIndent = false; 1330 return parseBlock(m); 1331 } 1332 1333 bool parseBlock(alias fun = parseBlockContent, T...)(T args) { 1334 if (!match(TokenType.OpenBrace)) { 1335 return false; 1336 } 1337 1338 nextToken(); 1339 if (parseEmptyBlock()) { 1340 newline(mode == Mode.Declaration ? 2 : 1); 1341 return true; 1342 } 1343 1344 { 1345 // We have an actual block. 1346 clearSeparator(); 1347 newline(1); 1348 1349 auto blockGuard = block(); 1350 fun(args); 1351 } 1352 1353 if (match(TokenType.CloseBrace)) { 1354 nextToken(); 1355 newline(2); 1356 } 1357 1358 return true; 1359 } 1360 1361 void parseBlockContent(Mode m) { 1362 auto indentGuard = indent(1 + needDoubleIndent); 1363 auto modeGuard = changeMode(m); 1364 1365 auto oldNeedDoubleIndent = needDoubleIndent; 1366 auto oldDoubleIndentBlock = doubleIndentBlock; 1367 scope(exit) { 1368 needDoubleIndent = oldNeedDoubleIndent; 1369 doubleIndentBlock = oldDoubleIndentBlock; 1370 } 1371 1372 doubleIndentBlock = needDoubleIndent; 1373 needDoubleIndent = false; 1374 1375 split(); 1376 1377 while (!match(TokenType.CloseBrace) && !match(TokenType.End)) { 1378 parseStructuralElement(); 1379 } 1380 1381 // Flush comments so that they have the proper indentation. 1382 flushComments(); 1383 } 1384 1385 static isBasicBlockEntry(ref TokenRange r) { 1386 auto t = r.front.type; 1387 if (t == TokenType.Case || t == TokenType.Default) { 1388 return true; 1389 } 1390 1391 if (t != TokenType.Identifier) { 1392 return false; 1393 } 1394 1395 // Check for labeled statements. 1396 r.popFront(); 1397 return r.front.type == TokenType.Colon; 1398 } 1399 1400 static isBasicBlockTerminator(TokenType t) { 1401 return t == TokenType.CloseBrace || t == TokenType.Return 1402 || t == TokenType.Break || t == TokenType.Continue 1403 || t == TokenType.Goto || t == TokenType.Throw; 1404 } 1405 1406 static isBasicBlockBoundary(ref TokenRange r) { 1407 return isBasicBlockTerminator(r.front.type) || isBasicBlockEntry(r); 1408 } 1409 1410 void parseColonBlock() { 1411 runOnType!(TokenType.Colon, nextToken)(); 1412 1413 if (match(TokenType.CloseBrace)) { 1414 // Empty colon block. 1415 return; 1416 } 1417 1418 if (!match(TokenType.OpenBrace)) { 1419 newline(); 1420 parseStructuralElement(); 1421 return; 1422 } 1423 1424 import source.parserutil; 1425 auto lookahead = trange.getLookahead(); 1426 lookahead.popMatchingDelimiter!(TokenType.OpenBrace)(); 1427 if (!isBasicBlockBoundary(lookahead)) { 1428 newline(1); 1429 return; 1430 } 1431 1432 auto guard = unindent(); 1433 space(); 1434 parseBlock(mode); 1435 } 1436 1437 bool parseControlFlowBlock(bool forceNewLine = true) { 1438 if (parseBlock(mode)) { 1439 return true; 1440 } 1441 1442 auto guard = span(); 1443 1444 // Carry indentation just like blocks. 1445 auto indentGuard = indent(needDoubleIndent); 1446 1447 auto oldNeedDoubleIndent = needDoubleIndent; 1448 auto oldDoubleIndentBlock = doubleIndentBlock; 1449 scope(exit) { 1450 needDoubleIndent = oldNeedDoubleIndent; 1451 doubleIndentBlock = oldDoubleIndentBlock; 1452 } 1453 1454 doubleIndentBlock = needDoubleIndent; 1455 needDoubleIndent = false; 1456 1457 if (forceNewLine) { 1458 newline(1); 1459 } else { 1460 space(); 1461 } 1462 1463 split(); 1464 parseStructuralElement(); 1465 return false; 1466 } 1467 1468 void emitPostControlFlowWhitespace(bool isBlock) { 1469 flushComments(); 1470 clearSeparator(); 1471 if (isBlock) { 1472 space(); 1473 } else { 1474 newline(1); 1475 } 1476 } 1477 1478 void parseElsableBlock() { 1479 if (match(TokenType.Colon)) { 1480 parseColonBlock(); 1481 return; 1482 } 1483 1484 space(); 1485 1486 bool isBlock = parseControlFlowBlock(); 1487 if (!match(TokenType.Else)) { 1488 return; 1489 } 1490 1491 emitPostControlFlowWhitespace(isBlock); 1492 parseElse(); 1493 } 1494 1495 void parseCondition(bool glued = false) { 1496 if (!match(TokenType.OpenParen)) { 1497 return; 1498 } 1499 1500 nextToken(); 1501 1502 auto guard = span!AlignedSpan(); 1503 split(glued); 1504 1505 guard.registerFix(function(AlignedSpan s, size_t i) { 1506 s.alignOn(i); 1507 }); 1508 1509 auto modeGuard = changeMode(Mode.Parameter); 1510 1511 parseStructuralElement(); 1512 runOnType!(TokenType.CloseParen, nextToken)(); 1513 } 1514 1515 void parseIf() in { 1516 assert(match(TokenType.If)); 1517 } do { 1518 nextToken(); 1519 space(); 1520 1521 parseCondition(); 1522 parseElsableBlock(); 1523 } 1524 1525 void parseVersion() in { 1526 assert(match(TokenType.Version) || match(TokenType.Debug)); 1527 } do { 1528 nextToken(); 1529 1530 if (match(TokenType.OpenParen)) { 1531 space(); 1532 nextToken(); 1533 1534 if (match(TokenType.Identifier) || match(TokenType.Unittest)) { 1535 nextToken(); 1536 } 1537 1538 runOnType!(TokenType.CloseParen, nextToken)(); 1539 } 1540 1541 parseElsableBlock(); 1542 } 1543 1544 void parseElse() in { 1545 assert(match(TokenType.Else)); 1546 } do { 1547 space(); 1548 nextToken(); 1549 space(); 1550 1551 switch (token.type) with (TokenType) { 1552 case If: 1553 parseIf(); 1554 break; 1555 1556 case Version, Debug: 1557 parseVersion(); 1558 break; 1559 1560 case While: 1561 parseWhile(); 1562 break; 1563 1564 case Do: 1565 parseDoWhile(); 1566 break; 1567 1568 case For: 1569 parseFor(); 1570 break; 1571 1572 case Foreach, ForeachReverse: 1573 parseForeach(); 1574 break; 1575 1576 case Static: 1577 auto lookahead = trange.getLookahead(); 1578 lookahead.popFront(); 1579 1580 auto t = lookahead.front.type; 1581 if (t == If || t == Foreach || t == ForeachReverse) { 1582 parseStatic(); 1583 break; 1584 } 1585 1586 goto default; 1587 1588 default: 1589 parseControlFlowBlock(); 1590 break; 1591 } 1592 } 1593 1594 void parseWhile() in { 1595 assert(match(TokenType.While)); 1596 } do { 1597 nextToken(); 1598 space(); 1599 1600 parseCondition(); 1601 1602 space(); 1603 parseControlFlowBlock(); 1604 } 1605 1606 void parseDoWhile() in { 1607 assert(match(TokenType.Do)); 1608 } do { 1609 nextToken(); 1610 space(); 1611 bool isBlock = parseControlFlowBlock(); 1612 1613 if (!match(TokenType.While)) { 1614 return; 1615 } 1616 1617 emitPostControlFlowWhitespace(isBlock); 1618 nextToken(); 1619 1620 space(); 1621 parseCondition(); 1622 1623 runOnType!(TokenType.Semicolon, nextToken)(); 1624 newline(2); 1625 } 1626 1627 void parseFor() in { 1628 assert(match(TokenType.For)); 1629 } do { 1630 nextToken(); 1631 space(); 1632 1633 if (match(TokenType.OpenParen)) { 1634 nextToken(); 1635 parseForArguments(); 1636 runOnType!(TokenType.CloseParen, nextToken)(); 1637 } 1638 1639 space(); 1640 parseControlFlowBlock(); 1641 } 1642 1643 void parseForArguments() { 1644 auto guard = span!CompactListSpan(); 1645 1646 if (match(TokenType.Semicolon)) { 1647 nextToken(); 1648 } else { 1649 split(); 1650 guard.registerFix(function(CompactListSpan s, size_t i) { 1651 s.registerElement(i); 1652 }); 1653 1654 parseStructuralElement(); 1655 clearSeparator(); 1656 } 1657 1658 if (match(TokenType.Semicolon)) { 1659 nextToken(); 1660 } else { 1661 space(); 1662 split(); 1663 guard.registerFix(function(CompactListSpan s, size_t i) { 1664 s.registerElement(i); 1665 }); 1666 1667 parseCommaExpression(); 1668 runOnType!(TokenType.Semicolon, nextToken)(); 1669 } 1670 1671 if (match(TokenType.CloseParen)) { 1672 nextToken(); 1673 } else { 1674 space(); 1675 split(); 1676 guard.registerFix(function(CompactListSpan s, size_t i) { 1677 s.registerElement(i); 1678 }); 1679 1680 parseCommaExpression(); 1681 } 1682 } 1683 1684 void parseForeach() in { 1685 assert(match(TokenType.Foreach) || match(TokenType.ForeachReverse)); 1686 } do { 1687 nextToken(); 1688 space(); 1689 1690 if (match(TokenType.OpenParen)) { 1691 nextToken(); 1692 auto modeGuard = changeMode(Mode.Parameter); 1693 auto listGuard = span!CompactListSpan(); 1694 1695 split(); 1696 listGuard.registerFix(function(CompactListSpan s, size_t i) { 1697 s.registerElement(i); 1698 }); 1699 1700 parseList!parseStructuralElement(TokenType.Semicolon); 1701 1702 split(); 1703 listGuard.registerFix(function(CompactListSpan s, size_t i) { 1704 s.registerElement(i); 1705 }); 1706 1707 space(); 1708 parseList!parseArrayElement(TokenType.CloseParen); 1709 } 1710 1711 space(); 1712 parseControlFlowBlock(); 1713 } 1714 1715 void parseReturn() in { 1716 assert(match(TokenType.Return) || match(TokenType.Throw)); 1717 } do { 1718 nextToken(); 1719 if (token.type == TokenType.Semicolon) { 1720 nextToken(); 1721 return; 1722 } 1723 1724 auto guard = span(); 1725 1726 space(); 1727 split(); 1728 1729 parseExpression(); 1730 } 1731 1732 void parseWith() in { 1733 assert(match(TokenType.With)); 1734 } do { 1735 nextToken(); 1736 space(); 1737 1738 parseCondition(); 1739 space(); 1740 1741 parseStructuralElement(); 1742 } 1743 1744 void parseSwitch() in { 1745 assert(match(TokenType.Switch)); 1746 } do { 1747 nextToken(); 1748 space(); 1749 1750 parseCondition(); 1751 space(); 1752 split(); 1753 1754 // Request the next nested block to be double indented. 1755 auto oldNeedDoubleIndent = needDoubleIndent; 1756 scope(exit) { 1757 needDoubleIndent = oldNeedDoubleIndent; 1758 } 1759 1760 needDoubleIndent = true; 1761 parseStructuralElement(); 1762 } 1763 1764 auto withCaseLevelIndent(alias fun)() { 1765 // There is nothing special to do in this case, just move on. 1766 if (!isCaseLevelStatement()) { 1767 return fun(); 1768 } 1769 1770 // Request the next nested block to be double indented. 1771 auto oldNeedDoubleIndent = needDoubleIndent; 1772 scope(exit) { 1773 needDoubleIndent = oldNeedDoubleIndent; 1774 } 1775 1776 needDoubleIndent = true; 1777 1778 auto guard = unindent(); 1779 split(); 1780 1781 return fun(); 1782 } 1783 1784 bool isCaseLevelStatement() { 1785 if (!doubleIndentBlock) { 1786 // No case level statement if we are not in 1787 // switch style block. 1788 return false; 1789 } 1790 1791 static void skip(ref TokenRange r) { 1792 while (true) { 1793 switch (r.front.type) with (TokenType) { 1794 case CloseBrace, End: 1795 return; 1796 1797 case Semicolon: 1798 r.popFront(); 1799 return; 1800 1801 case OpenBrace: 1802 import source.parserutil; 1803 r.popMatchingDelimiter!OpenBrace(); 1804 return; 1805 1806 case OpenParen: 1807 // Make sure we don't stop on `for (;;)` 1808 // so skip over parentheses. 1809 import source.parserutil; 1810 r.popMatchingDelimiter!OpenParen(); 1811 continue; 1812 1813 default: 1814 r.popFront(); 1815 continue; 1816 } 1817 } 1818 } 1819 1820 static bool isCaseBlock()(ref TokenRange r) { 1821 if (r.front.type != TokenType.OpenBrace) { 1822 return containsCase(r); 1823 } 1824 1825 r.popFront(); 1826 while (r.front.type != TokenType.End) { 1827 if (containsCase(r)) { 1828 return true; 1829 } 1830 1831 if (r.front.type == TokenType.CloseBrace) { 1832 r.popFront(); 1833 break; 1834 } 1835 } 1836 1837 return false; 1838 } 1839 1840 static bool containsCase(ref TokenRange r, bool doSkip = true) { 1841 // Pop labels. 1842 while (r.front.type == TokenType.Identifier) { 1843 r.popFront(); 1844 if (r.front.type != TokenType.Colon) { 1845 goto Skip; 1846 } 1847 1848 r.popFront(); 1849 } 1850 1851 switch (r.front.type) with (TokenType) { 1852 case Case, Default: 1853 return true; 1854 1855 case Static: 1856 r.popFront(); 1857 1858 auto t = r.front.type; 1859 if (t == If || t == Foreach || t == ForeachReverse) { 1860 goto CheckBlock; 1861 } 1862 1863 break; 1864 1865 case Foreach, ForeachReverse: 1866 goto CheckBlock; 1867 1868 CheckBlock: 1869 // As far as we are concenred here, foreach and 1870 // static if have the same syntax. 1871 r.popFront(); 1872 if (r.front.type == OpenParen) { 1873 import source.parserutil; 1874 r.popMatchingDelimiter!OpenParen(); 1875 } 1876 1877 return isCaseBlock(r); 1878 1879 default: 1880 break; 1881 } 1882 1883 Skip: 1884 if (doSkip) { 1885 skip(r); 1886 } 1887 1888 return false; 1889 } 1890 1891 auto lookahead = trange.getLookahead(); 1892 return containsCase(lookahead, false); 1893 } 1894 1895 void parseTry() in { 1896 assert(match(TokenType.Try)); 1897 } do { 1898 nextToken(); 1899 space(); 1900 bool isBlock = parseControlFlowBlock(); 1901 1902 while (true) { 1903 while (match(TokenType.Catch)) { 1904 emitPostControlFlowWhitespace(isBlock); 1905 isBlock = parseCatch(); 1906 } 1907 1908 if (!match(TokenType.Finally)) { 1909 break; 1910 } 1911 1912 emitPostControlFlowWhitespace(isBlock); 1913 isBlock = parseFinally(); 1914 } 1915 } 1916 1917 bool parseCatch() in { 1918 assert(match(TokenType.Catch)); 1919 } do { 1920 nextToken(); 1921 space(); 1922 parseParameterList(); 1923 space(); 1924 return parseControlFlowBlock(); 1925 } 1926 1927 bool parseFinally() in { 1928 assert(match(TokenType.Finally)); 1929 } do { 1930 nextToken(); 1931 space(); 1932 return parseControlFlowBlock(); 1933 } 1934 1935 void parseScope() in { 1936 assert(match(TokenType.Scope)); 1937 } do { 1938 auto lookahead = trange.getLookahead(); 1939 lookahead.popFront(); 1940 1941 if (lookahead.front.type != TokenType.OpenParen) { 1942 parseStorageClassDeclaration(); 1943 return; 1944 } 1945 1946 nextToken(); 1947 parseArgumentList(); 1948 1949 space(); 1950 parseControlFlowBlock(false); 1951 } 1952 1953 /** 1954 * Types 1955 */ 1956 bool parseType() { 1957 return parseIdentifier(IdentifierKind.Type); 1958 } 1959 1960 /** 1961 * Expressions 1962 */ 1963 void parseExpression() { 1964 canBeDeclaration = false; 1965 parseBaseExpression(); 1966 parseAssignExpressionSuffix(); 1967 } 1968 1969 bool parseBaseExpression() { 1970 return parseIdentifier(IdentifierKind.Expression); 1971 } 1972 1973 void parseCommaExpression() { 1974 parseBaseExpression(); 1975 parseCommaExpressionSuffix(); 1976 } 1977 1978 void parseCommaExpressionSuffix() { 1979 parseAssignExpressionSuffix(); 1980 1981 if (!match(TokenType.Comma)) { 1982 return; 1983 } 1984 1985 auto guard = spliceSpan(); 1986 do { 1987 nextToken(); 1988 split(); 1989 space(); 1990 1991 parseExpression(); 1992 } while (match(TokenType.Comma)); 1993 } 1994 1995 void parseAssignExpressionSuffix() { 1996 parseConditionalExpressionSuffix(); 1997 1998 static bool isAssignExpression(TokenType t) { 1999 return t == TokenType.Equal || t == TokenType.PlusEqual 2000 || t == TokenType.MinusEqual || t == TokenType.StarEqual 2001 || t == TokenType.SlashEqual || t == TokenType.PercentEqual 2002 || t == TokenType.AmpersandEqual || t == TokenType.PipeEqual 2003 || t == TokenType.CaretEqual || t == TokenType.TildeEqual 2004 || t == TokenType.LessLessEqual || t == TokenType.MoreMoreEqual 2005 || t == TokenType.MoreMoreMoreEqual 2006 || t == TokenType.CaretCaretEqual; 2007 } 2008 2009 if (!isAssignExpression(token.type)) { 2010 return; 2011 } 2012 2013 auto guard = spliceSpan(); 2014 do { 2015 space(); 2016 nextToken(); 2017 split(); 2018 space(); 2019 2020 parseBaseExpression(); 2021 parseConditionalExpressionSuffix(); 2022 } while (isAssignExpression(token.type)); 2023 } 2024 2025 void parseConditionalExpressionSuffix() { 2026 parseBinaryExpressionSuffix(); 2027 2028 if (!match(TokenType.QuestionMark)) { 2029 return; 2030 } 2031 2032 auto guard = spliceSpan!ConditionalSpan(); 2033 2034 space(); 2035 split(); 2036 2037 guard.registerFix(function(ConditionalSpan s, size_t i) { 2038 s.setQuestionMarkIndex(i); 2039 }); 2040 2041 nextToken(); 2042 space(); 2043 2044 parseExpression(); 2045 2046 space(); 2047 split(); 2048 2049 runOnType!(TokenType.Comma, nextToken)(); 2050 guard.registerFix(function(ConditionalSpan s, size_t i) { 2051 s.setColonIndex(i); 2052 }); 2053 2054 nextToken(); 2055 space(); 2056 2057 parseBaseExpression(); 2058 parseConditionalExpressionSuffix(); 2059 } 2060 2061 bool isBangIsOrIn() in { 2062 assert(match(TokenType.Bang)); 2063 } do { 2064 auto lookahead = trange.getLookahead(); 2065 lookahead.popFront(); 2066 auto t = lookahead.front.type; 2067 return t == TokenType.Is || t == TokenType.In; 2068 } 2069 2070 uint getPrecedence() { 2071 switch (token.type) with (TokenType) { 2072 case PipePipe: 2073 return 1; 2074 2075 case AmpersandAmpersand: 2076 return 2; 2077 2078 case Pipe: 2079 return 3; 2080 2081 case Caret: 2082 return 4; 2083 2084 case Ampersand: 2085 return 5; 2086 2087 case Is: 2088 case In: 2089 return 6; 2090 2091 case Bang: 2092 return isBangIsOrIn() ? 6 : 0; 2093 2094 case EqualEqual: 2095 case BangEqual: 2096 return 6; 2097 2098 case More: 2099 case MoreEqual: 2100 case Less: 2101 case LessEqual: 2102 return 6; 2103 2104 case LessLess: 2105 case MoreMore: 2106 case MoreMoreMore: 2107 return 7; 2108 2109 case BangLessMoreEqual: 2110 case BangLessMore: 2111 case LessMore: 2112 case LessMoreEqual: 2113 case BangMore: 2114 case BangMoreEqual: 2115 case BangLess: 2116 case BangLessEqual: 2117 return 7; 2118 2119 case Plus: 2120 case Minus: 2121 return 8; 2122 2123 case Slash: 2124 case Star: 2125 case Percent: 2126 return 9; 2127 2128 case Tilde: 2129 return 10; 2130 2131 default: 2132 return 0; 2133 } 2134 } 2135 2136 void parseBinaryExpressionSuffix(uint minPrecedence = 0) { 2137 auto currentPrecedence = getPrecedence(); 2138 2139 while (currentPrecedence > minPrecedence) { 2140 auto previousPrecedence = currentPrecedence; 2141 auto guard = spliceSpan(); 2142 2143 while (previousPrecedence == currentPrecedence) { 2144 scope(success) { 2145 currentPrecedence = getPrecedence(); 2146 if (currentPrecedence > previousPrecedence) { 2147 parseBinaryExpressionSuffix(previousPrecedence); 2148 currentPrecedence = getPrecedence(); 2149 } 2150 2151 assert(currentPrecedence <= previousPrecedence); 2152 } 2153 2154 space(); 2155 split(); 2156 if (match(TokenType.Bang)) { 2157 nextToken(); 2158 } 2159 2160 nextToken(); 2161 space(); 2162 2163 parseBaseExpression(); 2164 } 2165 } 2166 } 2167 2168 void parseArgumentList() { 2169 if (!match(TokenType.OpenParen)) { 2170 return; 2171 } 2172 2173 nextToken(); 2174 parseList!parseExpression(TokenType.CloseParen); 2175 } 2176 2177 void parseArrayLiteral() { 2178 if (!match(TokenType.OpenBracket)) { 2179 return; 2180 } 2181 2182 nextToken(); 2183 parseList!parseArrayElement(TokenType.CloseBracket); 2184 } 2185 2186 void parseArrayElement() { 2187 parseExpression(); 2188 2189 switch (token.type) with (TokenType) { 2190 case Colon: { 2191 auto guard = spliceSpan(); 2192 space(); 2193 nextToken(); 2194 space(); 2195 split(); 2196 parseExpression(); 2197 break; 2198 } 2199 2200 case DotDot: { 2201 auto guard = spliceSpan(); 2202 space(); 2203 split(); 2204 nextToken(); 2205 space(); 2206 parseExpression(); 2207 break; 2208 } 2209 2210 default: 2211 break; 2212 } 2213 } 2214 2215 void parseIsExpression() in { 2216 assert(match(TokenType.Is)); 2217 } do { 2218 auto modeGuard = changeMode(Mode.Parameter); 2219 nextToken(); 2220 runOnType!(TokenType.OpenParen, nextToken)(); 2221 parseList!parseIsElement(TokenType.CloseParen); 2222 } 2223 2224 void parseIsElement(size_t i) { 2225 if (i == 0) { 2226 parseIsSpecialization(); 2227 } else { 2228 parseStructuralElement(); 2229 } 2230 } 2231 2232 void parseIsSpecialization() { 2233 parseType(); 2234 if (match(TokenType.Identifier)) { 2235 space(); 2236 nextToken(); 2237 } 2238 2239 static bool isTypeSpecialization(TokenType t) { 2240 return t == TokenType.Struct || t == TokenType.Union 2241 || t == TokenType.Class || t == TokenType.Interface 2242 || t == TokenType.Enum || t == TokenType.__Vector 2243 || t == TokenType.Function || t == TokenType.Delegate 2244 || t == TokenType.Super || t == TokenType.Return 2245 || t == TokenType.__Parameters || t == TokenType.Module 2246 || t == TokenType.Package; 2247 } 2248 2249 while (match(TokenType.EqualEqual) || match(TokenType.Colon)) { 2250 auto specGuard = span(); 2251 2252 space(); 2253 split(); 2254 nextToken(); 2255 space(); 2256 2257 if (isTypeSpecialization(token.type)) { 2258 nextToken(); 2259 } else { 2260 parseType(); 2261 } 2262 2263 clearSeparator(); 2264 } 2265 } 2266 2267 void parseStructLiteral() { 2268 parseBlock!parseStructLiteralContent(); 2269 clearSeparator(); 2270 } 2271 2272 void parseStructLiteralContent() { 2273 auto indentGuard = indent(); 2274 2275 split(); 2276 2277 while (!match(TokenType.CloseBrace) && !match(TokenType.End)) { 2278 parseMapEntry(); 2279 runOnType!(TokenType.Comma, nextToken)(); 2280 newline(1); 2281 } 2282 2283 // Flush comments so that they have the proper indentation. 2284 flushComments(); 2285 } 2286 2287 void parseMapEntry() { 2288 auto guard = span(); 2289 runOnType!(TokenType.Identifier, nextToken)(); 2290 runOnType!(TokenType.Colon, nextToken)(); 2291 2292 split(); 2293 space(); 2294 parseExpression(); 2295 } 2296 2297 /** 2298 * Declarations 2299 */ 2300 void parseParameterPacks() { 2301 auto guard = changeMode(Mode.Parameter); 2302 2303 while (match(TokenType.OpenParen)) { 2304 nextToken(); 2305 parseList!(parseStructuralElement, 2306 ExpandingListSpan)(TokenType.CloseParen); 2307 } 2308 } 2309 2310 void parseTypedDeclaration() in { 2311 assert(match(TokenType.Identifier)); 2312 } do { 2313 bool isParameter = mode == Mode.Parameter; 2314 while (true) { 2315 auto guard = span!PrefixSpan(); 2316 split(); 2317 space(); 2318 runOnType!(TokenType.Identifier, nextToken)(); 2319 2320 parseParameterPacks(); 2321 2322 // Variable, template parameters, whatever. 2323 if (match(TokenType.Equal) || match(TokenType.Colon)) { 2324 auto valueGuard = spliceSpan(); 2325 2326 space(); 2327 nextToken(); 2328 space(); 2329 split(); 2330 2331 parseExpression(); 2332 } 2333 2334 if (isParameter) { 2335 if (match(TokenType.DotDotDot)) { 2336 nextToken(); 2337 } 2338 2339 break; 2340 } 2341 2342 if (!match(TokenType.Comma)) { 2343 break; 2344 } 2345 2346 nextToken(); 2347 } 2348 2349 parseFunctionBody(); 2350 } 2351 2352 void parseConstructor() in { 2353 assert(match(TokenType.This)); 2354 } do { 2355 nextToken(); 2356 parseParameterPacks(); 2357 parseFunctionBody(); 2358 } 2359 2360 void parseDestructor() in { 2361 assert(match(TokenType.Tilde)); 2362 } do { 2363 nextToken(); 2364 parseConstructor(); 2365 } 2366 2367 void parseFunctionBody() { 2368 if (parseFunctionPostfix()) { 2369 space(); 2370 parseBlock(Mode.Statement); 2371 } 2372 } 2373 2374 bool parseFunctionPostfix() { 2375 auto guard = span!IndentSpan(2); 2376 2377 while (true) { 2378 clearSeparator(); 2379 space(); 2380 2381 switch (token.type) with (TokenType) { 2382 case OpenBrace: 2383 // Function declaration. 2384 return true; 2385 2386 case Body, Do: 2387 split(); 2388 nextToken(); 2389 return true; 2390 2391 case In: 2392 auto lookahead = trange.getLookahead(); 2393 lookahead.popFront(); 2394 2395 if (lookahead.front.type == OpenParen) { 2396 split(); 2397 nextToken(); 2398 parseParameterList(); 2399 } else { 2400 nextToken(); 2401 space(); 2402 parseBlock(Mode.Statement); 2403 } 2404 2405 break; 2406 2407 case Out: 2408 // FIXME: This doesn't looks like it is doing the right thing. 2409 split(); 2410 nextToken(); 2411 parseParameterList(); 2412 space(); 2413 parseBlock(Mode.Statement); 2414 break; 2415 2416 case If: 2417 parseConstraint(); 2418 break; 2419 2420 default: 2421 if (!parseStorageClasses(true)) { 2422 clearSeparator(); 2423 return false; 2424 } 2425 2426 break; 2427 } 2428 } 2429 } 2430 2431 void parseConstraint() { 2432 if (!match(TokenType.If)) { 2433 return; 2434 } 2435 2436 split(); 2437 nextToken(); 2438 space(); 2439 parseCondition(true); 2440 } 2441 2442 void parseTemplate() in { 2443 assert(match(TokenType.Template)); 2444 } do { 2445 nextToken(); 2446 space(); 2447 runOnType!(TokenType.Identifier, nextToken)(); 2448 parseParameterList(); 2449 space(); 2450 2451 if (match(TokenType.If)) { 2452 auto guard = span!IndentSpan(2); 2453 parseConstraint(); 2454 space(); 2455 } 2456 2457 parseBlock(Mode.Declaration); 2458 } 2459 2460 void parseTemplateParameter() in { 2461 assert(token.type == TokenType.Identifier); 2462 } do { 2463 nextToken(); 2464 2465 if (match(TokenType.DotDotDot)) { 2466 nextToken(); 2467 } 2468 2469 while (match(TokenType.Colon) || match(TokenType.Equal)) { 2470 space(); 2471 nextToken(); 2472 space(); 2473 parseType(); 2474 } 2475 } 2476 2477 bool parseParameterList() { 2478 if (!match(TokenType.OpenParen)) { 2479 return false; 2480 } 2481 2482 auto guard = changeMode(Mode.Parameter); 2483 nextToken(); 2484 2485 parseList!(parseStructuralElement, 2486 ExpandingListSpan)(TokenType.CloseParen); 2487 return true; 2488 } 2489 2490 void parseImport() in { 2491 assert(match(TokenType.Import)); 2492 } do { 2493 nextToken(); 2494 2495 auto guard = span!PrefixSpan(); 2496 2497 while (true) { 2498 space(); 2499 split(); 2500 parseIdentifier(); 2501 2502 if (!match(TokenType.Comma)) { 2503 break; 2504 } 2505 2506 nextToken(); 2507 } 2508 2509 parseColonList!parseImportBind(); 2510 } 2511 2512 void parseImportBind() { 2513 parseIdentifier(); 2514 2515 if (!match(TokenType.Equal)) { 2516 return; 2517 } 2518 2519 auto guard = spliceSpan(); 2520 space(); 2521 nextToken(); 2522 space(); 2523 split(); 2524 2525 parseIdentifier(); 2526 } 2527 2528 bool parseAttribute() { 2529 if (!match(TokenType.At)) { 2530 return false; 2531 } 2532 2533 nextToken(); 2534 if (parseAliasList()) { 2535 return true; 2536 } 2537 2538 if (!match(TokenType.Identifier)) { 2539 parseIdentifier(); 2540 return true; 2541 } 2542 2543 nextToken(); 2544 parseIdentifierSuffix(IdentifierKind.Symbol); 2545 return true; 2546 } 2547 2548 bool parseAttributes() { 2549 if (!parseAttribute()) { 2550 return false; 2551 } 2552 2553 while (match(TokenType.At)) { 2554 space(); 2555 split(); 2556 parseAttribute(); 2557 } 2558 2559 space(); 2560 return true; 2561 } 2562 2563 static popDeclarator(ref TokenRange lookahead) { 2564 lookahead.popFront(); 2565 2566 if (lookahead.front.type == TokenType.Identifier) { 2567 lookahead.popFront(); 2568 } 2569 2570 if (lookahead.front.type == TokenType.OpenParen) { 2571 import source.parserutil; 2572 lookahead.popMatchingDelimiter!(TokenType.OpenParen)(); 2573 } 2574 2575 return lookahead.front.type; 2576 } 2577 2578 TokenType getStorageClassTokenType() { 2579 auto lookahead = trange.getLookahead(); 2580 return getStorageClassTokenType(lookahead); 2581 } 2582 2583 static getStorageClassTokenType(ref TokenRange lookahead) { 2584 while (true) { 2585 auto t = lookahead.front.type; 2586 switch (t) with (TokenType) { 2587 case Const, Immutable, Inout, Shared, Scope: 2588 lookahead.popFront(); 2589 if (lookahead.front.type == OpenParen) { 2590 // This is a type. 2591 return t; 2592 } 2593 2594 break; 2595 2596 case Abstract, Auto, Export, Final, In, Lazy, Nothrow, Out, 2597 Override, Private, Protected, Pure, Ref, Return, __Gshared: 2598 lookahead.popFront(); 2599 break; 2600 2601 case Align, Deprecated, Extern, Package, Pragma, Synchronized: 2602 lookahead.popFront(); 2603 if (lookahead.front.type == OpenParen) { 2604 import source.parserutil; 2605 lookahead.popMatchingDelimiter!OpenParen(); 2606 } 2607 2608 break; 2609 2610 case At: 2611 // FIXME: A declarator is not apropriate here. 2612 popDeclarator(lookahead); 2613 break; 2614 2615 case Public: 2616 auto l2 = lookahead.getLookahead(); 2617 l2.popFront(); 2618 2619 if (l2.front.type == Import) { 2620 // This is a public import. 2621 return t; 2622 } 2623 2624 lookahead.popFront(); 2625 break; 2626 2627 case Static: 2628 auto l2 = lookahead.getLookahead(); 2629 l2.popFront(); 2630 2631 auto t2 = l2.front.type; 2632 if (t2 == Assert || t2 == Import || t2 == If 2633 || t2 == Foreach || t2 == ForeachReverse) { 2634 // This is a static something. 2635 return t; 2636 } 2637 2638 lookahead.popFront(); 2639 break; 2640 2641 case Enum: 2642 auto l2 = lookahead.getLookahead(); 2643 popDeclarator(l2); 2644 2645 auto t2 = l2.front.type; 2646 if (t2 == Colon || t2 == OpenBrace) { 2647 // This is an enum declaration. 2648 return t; 2649 } 2650 2651 lookahead.popFront(); 2652 break; 2653 2654 case Alias: 2655 auto l2 = lookahead.getLookahead(); 2656 popDeclarator(l2); 2657 2658 auto t2 = l2.front.type; 2659 if (t2 == This || t2 == Identifier) { 2660 // This is an alias declaration. 2661 return t; 2662 } 2663 2664 lookahead.popFront(); 2665 break; 2666 2667 default: 2668 return t; 2669 } 2670 } 2671 } 2672 2673 bool parseStorageClasses(bool isPostfix = false) { 2674 bool foundStorageClass = false; 2675 while (true) { 2676 scope(success) { 2677 // This will be true after the first loop iterration. 2678 foundStorageClass = true; 2679 } 2680 2681 switch (token.type) with (TokenType) { 2682 case Const, Immutable, Inout, Shared, Scope: 2683 auto lookahead = trange.getLookahead(); 2684 lookahead.popFront(); 2685 if (lookahead.front.type == OpenParen) { 2686 // This is a type. 2687 goto default; 2688 } 2689 2690 nextToken(); 2691 break; 2692 2693 case In, Out: 2694 // Make sure we deambiguate with contracts. 2695 if (isPostfix) { 2696 goto default; 2697 } 2698 2699 nextToken(); 2700 break; 2701 2702 case Abstract, Auto, Export, Final, Lazy, Nothrow, Override, 2703 Private, Protected, Pure, Ref, Return, __Gshared: 2704 nextToken(); 2705 break; 2706 2707 case Align, Deprecated, Extern, Package, Synchronized: 2708 nextToken(); 2709 parseArgumentList(); 2710 break; 2711 2712 case Pragma: 2713 nextToken(); 2714 parseArgumentList(); 2715 if (!isPostfix && !match(Colon)) { 2716 newline(1); 2717 } 2718 2719 break; 2720 2721 case At: 2722 parseAttributes(); 2723 if (!isPostfix && !foundStorageClass && !match(Colon)) { 2724 newline(1); 2725 } 2726 2727 break; 2728 2729 case Public: 2730 auto lookahead = trange.getLookahead(); 2731 lookahead.popFront(); 2732 2733 if (lookahead.front.type == Import) { 2734 // This is a public import. 2735 goto default; 2736 } 2737 2738 nextToken(); 2739 break; 2740 2741 case Static: 2742 auto lookahead = trange.getLookahead(); 2743 lookahead.popFront(); 2744 2745 auto t = lookahead.front.type; 2746 if (t == Assert || t == Import || t == If || t == Foreach 2747 || t == ForeachReverse) { 2748 // This is a static something. 2749 goto default; 2750 } 2751 2752 nextToken(); 2753 break; 2754 2755 case Enum: 2756 auto lookahead = trange.getLookahead(); 2757 popDeclarator(lookahead); 2758 2759 auto t = lookahead.front.type; 2760 if (t == Colon || t == OpenBrace) { 2761 // This is an enum declaration. 2762 goto default; 2763 } 2764 2765 nextToken(); 2766 break; 2767 2768 case Alias: 2769 auto lookahead = trange.getLookahead(); 2770 popDeclarator(lookahead); 2771 2772 auto t = lookahead.front.type; 2773 if (t == This || t == Identifier) { 2774 // This is an alias declaration. 2775 goto default; 2776 } 2777 2778 nextToken(); 2779 break; 2780 2781 default: 2782 return foundStorageClass; 2783 } 2784 2785 if (match(TokenType.Colon) || match(TokenType.Semicolon)) { 2786 clearSeparator(); 2787 } else { 2788 if (!isPostfix && !match(TokenType.Identifier)) { 2789 split(); 2790 } 2791 2792 space(); 2793 } 2794 } 2795 2796 return foundStorageClass; 2797 } 2798 2799 bool parseStorageClassDeclaration() { 2800 auto guard = span!StorageClassSpan(); 2801 2802 bool isColonBlock = getStorageClassTokenType() == TokenType.Colon; 2803 bool foundStorageClass = false; 2804 2805 { 2806 auto indentGuard = unindent(isColonBlock); 2807 foundStorageClass = parseStorageClasses(); 2808 } 2809 2810 // Before bailing, try storage class looking declarations. 2811 switch (token.type) with (TokenType) { 2812 case Public: 2813 return parsePublic(); 2814 2815 case Enum: 2816 return parseEnum(); 2817 2818 case Alias: 2819 return parseAlias(); 2820 2821 default: 2822 break; 2823 } 2824 2825 if (!foundStorageClass) { 2826 return false; 2827 } 2828 2829 switch (token.type) with (TokenType) { 2830 case Colon: 2831 clearSeparator(); 2832 parseColonBlock(); 2833 break; 2834 2835 case Semicolon: 2836 clearSeparator(); 2837 nextToken(); 2838 break; 2839 2840 case OpenBrace: 2841 parseBlock(mode); 2842 break; 2843 2844 case Identifier: 2845 auto lookahead = trange.getLookahead(); 2846 lookahead.popFront(); 2847 2848 auto t = lookahead.front.type; 2849 if (t == Equal || t == OpenParen) { 2850 split(); 2851 parseTypedDeclaration(); 2852 break; 2853 } 2854 2855 goto default; 2856 2857 default: 2858 split(); 2859 parseStructuralElement(); 2860 break; 2861 } 2862 2863 return true; 2864 } 2865 2866 bool parsePublic() { 2867 if (!match(TokenType.Public)) { 2868 return false; 2869 } 2870 2871 auto lookahead = trange.getLookahead(); 2872 lookahead.popFront(); 2873 2874 if (lookahead.front.type == TokenType.Import) { 2875 nextToken(); 2876 space(); 2877 parseImport(); 2878 } else { 2879 parseStorageClassDeclaration(); 2880 } 2881 2882 return true; 2883 } 2884 2885 bool parseStatic() { 2886 if (!match(TokenType.Static)) { 2887 return false; 2888 } 2889 2890 auto lookahead = trange.getLookahead(); 2891 lookahead.popFront(); 2892 2893 auto t = lookahead.front.type; 2894 switch (t) with (TokenType) { 2895 case If: 2896 nextToken(); 2897 space(); 2898 parseIf(); 2899 break; 2900 2901 case Foreach, ForeachReverse: 2902 nextToken(); 2903 space(); 2904 parseForeach(); 2905 break; 2906 2907 case Assert: 2908 nextToken(); 2909 space(); 2910 parseExpression(); 2911 break; 2912 2913 case Import: 2914 nextToken(); 2915 space(); 2916 parseImport(); 2917 break; 2918 2919 default: 2920 parseStorageClassDeclaration(); 2921 break; 2922 } 2923 2924 return true; 2925 } 2926 2927 bool parseEnum() { 2928 if (!match(TokenType.Enum)) { 2929 return false; 2930 } 2931 2932 nextToken(); 2933 if (match(TokenType.Identifier)) { 2934 space(); 2935 nextToken(); 2936 } 2937 2938 if (match(TokenType.Colon)) { 2939 space(); 2940 nextToken(); 2941 space(); 2942 parseType(); 2943 } 2944 2945 if (match(TokenType.OpenBrace)) { 2946 space(); 2947 nextToken(); 2948 parseList!parseEnumEntry(TokenType.CloseBrace, true); 2949 } 2950 2951 return true; 2952 } 2953 2954 void parseEnumEntry() { 2955 if (parseAttributes()) { 2956 newline(1); 2957 } 2958 2959 parseExpression(); 2960 } 2961 2962 bool parseAlias() { 2963 if (!match(TokenType.Alias)) { 2964 return false; 2965 } 2966 2967 nextToken(); 2968 space(); 2969 2970 parseIdentifier(); 2971 2972 if (match(TokenType.Identifier) || match(TokenType.This)) { 2973 space(); 2974 nextToken(); 2975 } 2976 2977 return true; 2978 } 2979 2980 void parseAliasEntry() { 2981 // FIXME: This is wrong because identifier * identifier shouldn't be 2982 // parsed as a declaration here, but provide the right entry point for the 2983 // rest of the code. 2984 parseType(); 2985 parseAssignExpressionSuffix(); 2986 } 2987 2988 bool parseAliasList() { 2989 if (!match(TokenType.OpenParen)) { 2990 return false; 2991 } 2992 2993 nextToken(); 2994 parseList!parseAliasEntry(TokenType.CloseParen); 2995 return true; 2996 } 2997 2998 void parseAggregate() in { 2999 assert(match(TokenType.Struct) || match(TokenType.Union) 3000 || match(TokenType.Class) || match(TokenType.Interface)); 3001 } do { 3002 nextToken(); 3003 space(); 3004 3005 runOnType!(TokenType.Identifier, nextToken)(); 3006 3007 parseParameterList(); 3008 3009 while (true) { 3010 space(); 3011 3012 switch (token.type) with (TokenType) { 3013 case Colon: 3014 parseColonList!parseIdentifier(); 3015 break; 3016 3017 case If: { 3018 auto guard = span!IndentSpan(2); 3019 parseConstraint(); 3020 break; 3021 } 3022 3023 default: 3024 parseBlock(Mode.Declaration); 3025 return; 3026 } 3027 } 3028 } 3029 3030 /** 3031 * Parsing utilities 3032 */ 3033 void parseListAdapter(alias fun)(size_t i) { 3034 fun(); 3035 } 3036 3037 void parseList(alias fun, S = CompactListSpan)(TokenType closingTokenType, 3038 bool addNewLines = false) { 3039 if (match(closingTokenType)) { 3040 auto guard = builder.virtualSpan(); 3041 nextToken(); 3042 return; 3043 } 3044 3045 static if (is(typeof(fun(0)))) { 3046 alias afun = fun; 3047 } else { 3048 alias afun = parseListAdapter!fun; 3049 } 3050 3051 auto guard = span!S(); 3052 3053 size_t i = 0; 3054 while (!match(closingTokenType)) { 3055 if (addNewLines) { 3056 newline(1); 3057 } 3058 3059 split(); 3060 guard.registerFix(function(S s, size_t i) { 3061 s.registerElement(i); 3062 }); 3063 3064 afun(i++); 3065 3066 if (!match(TokenType.Comma)) { 3067 break; 3068 } 3069 3070 nextToken(); 3071 space(); 3072 } 3073 3074 if (match(closingTokenType)) { 3075 if (addNewLines) { 3076 newline(1); 3077 } 3078 3079 split(); 3080 guard.registerFix(function(S s, size_t i) { 3081 s.registerTrailingSplit(i); 3082 }); 3083 3084 nextToken(); 3085 } 3086 3087 if (addNewLines) { 3088 newline(2); 3089 } 3090 } 3091 3092 bool parseColonList(alias fun)() { 3093 if (!match(TokenType.Colon)) { 3094 return false; 3095 } 3096 3097 auto colonGuard = spliceSpan(); 3098 space(); 3099 split(); 3100 nextToken(); 3101 3102 auto listGuard = span!CompactListSpan(); 3103 bool first = true; 3104 while (true) { 3105 space(); 3106 split(first); 3107 first = false; 3108 3109 listGuard.registerFix(function(CompactListSpan s, size_t i) { 3110 s.registerElement(i); 3111 }); 3112 3113 fun(); 3114 3115 if (!match(TokenType.Comma)) { 3116 break; 3117 } 3118 3119 nextToken(); 3120 } 3121 3122 return true; 3123 } 3124 }