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