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