1 module d.parser.declaration; 2 3 import d.parser.adt; 4 import d.parser.base; 5 import d.parser.conditional; 6 import d.parser.expression; 7 import d.parser.identifier; 8 import d.parser.statement; 9 import d.parser.dtemplate; 10 import d.parser.type; 11 12 import d.ast.declaration; 13 import d.ast.expression; 14 import d.ast.identifier; 15 import d.ast.type; 16 17 import d.ir.expression; 18 19 import source.name; 20 21 /** 22 * Parse a set of declarations. 23 */ 24 auto parseAggregate(bool globBraces = true)(ref TokenRange trange) { 25 static if (globBraces) { 26 trange.match(TokenType.OpenBrace); 27 } 28 29 Declaration[] declarations; 30 31 while (!trange.empty && trange.front.type != TokenType.CloseBrace) { 32 declarations ~= trange.parseDeclaration(); 33 } 34 35 static if (globBraces) { 36 trange.match(TokenType.CloseBrace); 37 } 38 39 return declarations; 40 } 41 42 /** 43 * Parse a declaration 44 */ 45 Declaration parseDeclaration(ref TokenRange trange) { 46 auto location = trange.front.location; 47 48 // First, declarations that do not support storage classes. 49 switch (trange.front.type) with(TokenType) { 50 case Static: 51 // Handle static if. 52 auto lookahead = trange.getLookahead(); 53 lookahead.popFront(); 54 switch (lookahead.front.type) { 55 case If: 56 return trange.parseStaticIf!Declaration(); 57 58 case Assert: 59 return trange.parseStaticAssert!Declaration(); 60 61 default: 62 break; 63 } 64 65 break; 66 67 case Import: 68 return trange.parseImport(); 69 70 case Version: 71 return trange.parseVersion!Declaration(); 72 73 case Debug: 74 return trange.parseDebug!Declaration(); 75 76 case Mixin: 77 return trange.parseMixin!Declaration(); 78 79 default : 80 break; 81 } 82 83 auto qualifier = TypeQualifier.Mutable; 84 StorageClass stc = defaultStorageClass; 85 86 StorageClassLoop: while (true) { 87 switch (trange.front.type) with(TokenType) { 88 case Const: 89 qualifier = TypeQualifier.Const; 90 goto HandleTypeQualifier; 91 92 case Immutable: 93 qualifier = TypeQualifier.Immutable; 94 goto HandleTypeQualifier; 95 96 case Inout: 97 qualifier = TypeQualifier.Inout; 98 goto HandleTypeQualifier; 99 100 case Shared: 101 qualifier = TypeQualifier.Shared; 102 goto HandleTypeQualifier; 103 104 HandleTypeQualifier: { 105 auto lookahead = trange.getLookahead(); 106 lookahead.popFront(); 107 if (lookahead.front.type == OpenParen) { 108 // This is a type not a storage class. 109 break StorageClassLoop; 110 } 111 112 // We have a qualifier(type) name type of declaration. 113 stc.hasQualifier = true; 114 stc.qualifier = stc.qualifier.add(qualifier); 115 goto HandleStorageClass; 116 } 117 118 case Ref: 119 stc.isRef = true; 120 goto HandleStorageClass; 121 122 case Abstract: 123 stc.isAbstract = true; 124 goto HandleStorageClass; 125 126 case Deprecated: 127 stc.isDeprecated = true; 128 goto HandleStorageClass; 129 130 case Nothrow: 131 stc.isNoThrow = true; 132 goto HandleStorageClass; 133 134 case Override: 135 stc.isOverride = true; 136 goto HandleStorageClass; 137 138 case Pure: 139 stc.isPure = true; 140 goto HandleStorageClass; 141 142 case Static: 143 stc.isStatic = true; 144 goto HandleStorageClass; 145 146 case Synchronized: 147 stc.isSynchronized = true; 148 goto HandleStorageClass; 149 150 case __Gshared: 151 stc.isGshared = true; 152 goto HandleStorageClass; 153 154 case Private: 155 stc.hasVisibility = true; 156 stc.visibility = Visibility.Private; 157 goto HandleStorageClass; 158 159 case Package: 160 stc.hasVisibility = true; 161 stc.visibility = Visibility.Package; 162 goto HandleStorageClass; 163 164 case Protected: 165 stc.hasVisibility = true; 166 stc.visibility = Visibility.Protected; 167 goto HandleStorageClass; 168 169 case Public: 170 stc.hasVisibility = true; 171 stc.visibility = Visibility.Public; 172 goto HandleStorageClass; 173 174 case Export: 175 stc.hasVisibility = true; 176 stc.visibility = Visibility.Export; 177 goto HandleStorageClass; 178 179 HandleStorageClass: 180 trange.popFront(); 181 break; 182 183 case Extern: 184 trange.popFront(); 185 trange.match(OpenParen); 186 auto linkageName = trange.front.name; 187 trange.match(Identifier); 188 189 stc.hasLinkage = true; 190 if (linkageName == BuiltinName!"D") { 191 stc.linkage = Linkage.D; 192 } else if (linkageName == BuiltinName!"C") { 193 // TODO: C++ 194 stc.linkage = Linkage.C; 195 } else if (linkageName == BuiltinName!"Windows") { 196 stc.linkage = Linkage.Windows; 197 } else if (linkageName == BuiltinName!"System") { 198 stc.linkage = Linkage.System; 199 } else if (linkageName == BuiltinName!"Pascal") { 200 stc.linkage = Linkage.Pascal; 201 } else if (linkageName == BuiltinName!"Java") { 202 stc.linkage = Linkage.Java; 203 } else { 204 assert( 205 0, 206 "Linkage not supported : " 207 ~ linkageName.toString(trange.context), 208 ); 209 } 210 211 trange.match(CloseParen); 212 break; 213 214 // Enum is a bit of a strange beast. half storage class, half declaration itself. 215 case Enum: 216 stc.isEnum = true; 217 218 auto lookahead = trange.getLookahead(); 219 lookahead.popFront(); 220 221 switch (lookahead.front.type) { 222 // enum : and enum { are special construct, 223 // not classic storage class declaration. 224 case Colon, OpenBrace: 225 return trange.parseEnum(stc); 226 227 case Identifier: 228 lookahead.popFront(); 229 auto nextType = lookahead.front.type; 230 if (nextType == Colon || nextType == OpenBrace) { 231 // Named verion of the above. 232 return trange.parseEnum(stc); 233 } 234 235 break; 236 237 default : 238 break; 239 } 240 241 goto HandleStorageClass; 242 243 case At: 244 trange.popFront(); 245 auto attr = trange.front.name; 246 trange.match(Identifier); 247 248 if (attr == BuiltinName!"property") { 249 stc.isProperty = true; 250 } else if (attr == BuiltinName!"nogc") { 251 stc.isNoGC = true; 252 } else { 253 assert( 254 0, 255 "@" ~ attr.toString(trange.context) 256 ~ " is not supported.", 257 ); 258 } 259 260 break; 261 262 default: 263 break StorageClassLoop; 264 } 265 266 switch (trange.front.type) with(TokenType) { 267 case Identifier: 268 auto lookahead = trange.getLookahead(); 269 lookahead.popFront(); 270 auto nextType = lookahead.front.type; 271 if (nextType == Equal || nextType == OpenParen) { 272 return trange.parseTypedDeclaration( 273 location, 274 stc, 275 AstType.getAuto(), 276 ); 277 } 278 279 break StorageClassLoop; 280 281 case Colon: 282 trange.popFront(); 283 auto declarations = trange.parseAggregate!false(); 284 285 location.spanTo(trange.front.location); 286 return new GroupDeclaration(location, stc, declarations); 287 288 case OpenBrace: 289 auto declarations = trange.parseAggregate(); 290 291 location.spanTo(trange.front.location); 292 return new GroupDeclaration(location, stc, declarations); 293 294 default: 295 break; 296 } 297 } 298 299 switch (trange.front.type) with(TokenType) { 300 // XXX: auto as a storage class ? 301 case Auto: 302 trange.popFront(); 303 return trange.parseTypedDeclaration(location, stc, AstType.getAuto()); 304 305 case Interface: 306 return trange.parseInterface(stc); 307 308 case Class: 309 return trange.parseClass(stc); 310 311 case Struct: 312 return trange.parseStruct(stc); 313 314 case Union: 315 return trange.parseUnion(stc); 316 317 case This: 318 return trange.parseConstructor(stc); 319 320 case Tilde: 321 return trange.parseDestructor(stc); 322 323 case Template: 324 return trange.parseTemplate(stc); 325 326 case Alias: 327 return trange.parseAlias(stc); 328 329 case Unittest: 330 trange.popFront(); 331 332 auto name = BuiltinName!""; 333 if (trange.front.type == Identifier) { 334 name = trange.front.name; 335 trange.popFront(); 336 } 337 338 // In the future we may want to skip parsing unittest blocks. 339 // if (!trange.context.enableUnittest) { 340 // import source.parserutil; 341 // popMatchingDelimiter!(TokenType.OpenBrace)(); 342 // } 343 344 auto fbody = trange.parseBlock(); 345 location.spanTo(trange.previous); 346 347 return new UnittestDeclaration(location, stc, name, fbody); 348 349 default: 350 return trange.parseTypedDeclaration(location, stc); 351 } 352 353 assert(0); 354 } 355 356 /** 357 * Parse type identifier ... declarations. 358 * Function/variables. 359 */ 360 Declaration parseTypedDeclaration( 361 ref TokenRange trange, 362 Location location, 363 StorageClass stc, 364 ) { 365 return trange.parseTypedDeclaration(location, stc, trange.parseType()); 366 } 367 368 /** 369 * Parse a declaration when you already have its type. 370 */ 371 Declaration parseTypedDeclaration( 372 ref TokenRange trange, 373 Location location, 374 StorageClass stc, 375 AstType type, 376 ) { 377 auto lookahead = trange.getLookahead(); 378 lookahead.popFront(); 379 if (lookahead.front.type == TokenType.OpenParen) { 380 auto idLoc = trange.front.location; 381 auto name = trange.front.name; 382 trange.match(TokenType.Identifier); 383 384 if (name.isReserved) { 385 import source.exception; 386 throw new CompileException( 387 idLoc, 388 name.toString(trange.context) ~ " is a reserved name", 389 ); 390 } 391 392 // TODO: implement ref return. 393 return trange.parseFunction( 394 location, 395 stc, 396 type.getParamType(stc.isRef ? ParamKind.Ref : ParamKind.Regular), 397 name, 398 ); 399 } else { 400 Declaration[] variables; 401 402 while (true) { 403 auto name = trange.front.name; 404 Location variableLocation = trange.front.location; 405 trange.match(TokenType.Identifier); 406 407 AstExpression value; 408 if (trange.front.type == TokenType.Equal) { 409 trange.popFront(); 410 value = trange.parseInitializer(); 411 variableLocation.spanTo(value.location); 412 } 413 414 variables ~= new VariableDeclaration( 415 location, 416 stc, 417 type, 418 name, 419 value, 420 ); 421 422 if (trange.front.type != TokenType.Comma) { 423 break; 424 } 425 426 trange.popFront(); 427 } 428 429 location.spanTo(trange.front.location); 430 trange.match(TokenType.Semicolon); 431 432 return new GroupDeclaration(location, stc, variables); 433 } 434 } 435 436 // XXX: one callsite, remove 437 private Declaration parseConstructor(ref TokenRange trange, StorageClass stc) { 438 auto location = trange.front.location; 439 trange.match(TokenType.This); 440 441 return trange.parseFunction( 442 location, 443 stc, 444 AstType.getAuto().getParamType(ParamKind.Regular), 445 BuiltinName!"__ctor", 446 ); 447 } 448 449 // XXX: one callsite, remove 450 private Declaration parseDestructor(ref TokenRange trange, StorageClass stc) { 451 auto location = trange.front.location; 452 trange.match(TokenType.Tilde); 453 trange.match(TokenType.This); 454 455 return trange.parseFunction( 456 location, 457 stc, 458 AstType.getAuto().getParamType(ParamKind.Regular), 459 BuiltinName!"__dtor", 460 ); 461 } 462 463 /** 464 * Parse function declaration, starting with parameters. 465 * This allow to parse function as well as constructor or any special function. 466 * Additionnal parameters are used to construct the function. 467 */ 468 private Declaration parseFunction( 469 ref TokenRange trange, 470 Location location, 471 StorageClass stc, 472 ParamAstType returnType, 473 Name name, 474 ) { 475 // Function declaration. 476 bool isVariadic; 477 AstTemplateParameter[] tplParameters; 478 479 // Check if we have a function template 480 import source.parserutil; 481 auto lookahead = trange.getLookahead(); 482 lookahead.popMatchingDelimiter!(TokenType.OpenParen)(); 483 484 bool isTemplate = lookahead.front.type == TokenType.OpenParen; 485 if (isTemplate) { 486 tplParameters = trange.parseTemplateParameters(); 487 } 488 489 auto parameters = trange.parseParameters(isVariadic); 490 491 // If it is a template, it can have a constraint. 492 if (tplParameters.ptr) { 493 if (trange.front.type == TokenType.If) { 494 trange.parseConstraint(); 495 } 496 } 497 498 auto qualifier = TypeQualifier.Mutable; 499 500 while (true) { 501 switch (trange.front.type) with(TokenType) { 502 case Pure: 503 stc.isPure = true; 504 goto HandleStorageClass; 505 506 case Const: 507 qualifier = TypeQualifier.Const; 508 goto HandleTypeQualifier; 509 510 case Immutable: 511 qualifier = TypeQualifier.Immutable; 512 goto HandleTypeQualifier; 513 514 case Inout: 515 qualifier = TypeQualifier.Inout; 516 goto HandleTypeQualifier; 517 518 case Shared: 519 qualifier = TypeQualifier.Shared; 520 goto HandleTypeQualifier; 521 522 HandleTypeQualifier: { 523 // We have a qualifier(type) name type of declaration. 524 stc.hasQualifier = true; 525 stc.qualifier = stc.qualifier.add(qualifier); 526 goto HandleStorageClass; 527 } 528 529 HandleStorageClass: 530 trange.popFront(); 531 break; 532 533 case At: 534 trange.popFront(); 535 auto attr = trange.front.name; 536 trange.match(Identifier); 537 538 if (attr == BuiltinName!"property") { 539 stc.isProperty = true; 540 } else if (attr == BuiltinName!"nogc") { 541 stc.isNoGC = true; 542 } else { 543 assert( 544 0, 545 "@" ~ attr.toString(trange.context) 546 ~ " is not supported.", 547 ); 548 } 549 550 continue; 551 552 default: 553 break; 554 } 555 556 break; 557 } 558 559 // TODO: parse contracts. 560 // Skip contracts 561 switch (trange.front.type) with(TokenType) { 562 case In, Out: 563 trange.popFront(); 564 trange.parseBlock(); 565 566 switch (trange.front.type) { 567 case In, Out: 568 trange.popFront(); 569 trange.parseBlock(); 570 break; 571 572 default : 573 break; 574 } 575 576 trange.match(Body); 577 break; 578 579 case Body: 580 // Body without contract is just skipped. 581 trange.popFront(); 582 break; 583 584 default: 585 break; 586 } 587 588 import d.ast.statement; 589 BlockStatement fbody; 590 switch (trange.front.type) with(TokenType) { 591 case Semicolon: 592 location.spanTo(trange.front.location); 593 trange.popFront(); 594 595 break; 596 597 case OpenBrace: 598 fbody = trange.parseBlock(); 599 location.spanTo(fbody.location); 600 601 break; 602 603 default: 604 // TODO: error. 605 trange.match(Begin); 606 assert(0); 607 } 608 609 auto fun = new FunctionDeclaration( 610 location, 611 stc, 612 returnType, 613 name, 614 parameters, 615 isVariadic, 616 fbody, 617 ); 618 619 if (!isTemplate) { 620 return fun; 621 } 622 623 return new TemplateDeclaration(location, stc, name, tplParameters, [fun]); 624 } 625 626 /** 627 * Parse function and delegate parameters. 628 */ 629 auto parseParameters(bool matchOpenParen = true)(ref TokenRange trange, out bool isVariadic) { 630 static if (matchOpenParen) { 631 trange.match(TokenType.OpenParen); 632 } 633 634 ParamDecl[] parameters; 635 while (trange.front.type != TokenType.CloseParen) { 636 if (trange.front.type == TokenType.DotDotDot) { 637 // This is a variadic function. 638 trange.popFront(); 639 isVariadic = true; 640 break; 641 } 642 643 parameters ~= trange.parseParameter(); 644 if (trange.front.type != TokenType.Comma) { 645 break; 646 } 647 648 trange.popFront(); 649 } 650 651 trange.match(TokenType.CloseParen); 652 return parameters; 653 } 654 655 /** 656 * Parse Initializer 657 */ 658 auto parseInitializer(ref TokenRange trange) { 659 if (trange.front.type != TokenType.Void) { 660 return trange.parseAssignExpression(); 661 } 662 663 auto location = trange.front.location; 664 665 trange.popFront(); 666 return new AstVoidInitializer(location); 667 } 668 669 private: 670 auto parseParameter(ref TokenRange trange) { 671 bool isRef; 672 673 // TODO: parse storage class 674 ParseStorageClassLoop: while (true) { 675 switch (trange.front.type) with(TokenType) { 676 case In, Out, Lazy: 677 assert( 678 0, 679 "storageclasses: in, out and lazy are not yet implemented", 680 ); 681 682 case Ref: 683 trange.popFront(); 684 isRef = true; 685 686 break; 687 688 default: 689 break ParseStorageClassLoop; 690 } 691 } 692 693 auto location = trange.front.location; 694 auto type = trange.parseType() 695 .getParamType(isRef ? ParamKind.Ref : ParamKind.Regular); 696 697 auto name = BuiltinName!""; 698 AstExpression value; 699 700 if (trange.front.type == TokenType.Identifier) { 701 name = trange.front.name; 702 703 trange.popFront(); 704 if (trange.front.type == TokenType.Equal) { 705 trange.popFront(); 706 value = trange.parseAssignExpression(); 707 } 708 } 709 710 location.spanTo(trange.front.location); 711 return ParamDecl(location, type, name, value); 712 } 713 714 /** 715 * Parse alias declaration 716 */ 717 Declaration parseAlias(ref TokenRange trange, StorageClass stc) { 718 Location location = trange.front.location; 719 trange.match(TokenType.Alias); 720 721 auto name = trange.front.name; 722 trange.match(TokenType.Identifier); 723 724 if (trange.front.type == TokenType.Equal) { 725 trange.popFront(); 726 727 import d.parser.ambiguous; 728 return trange.parseAmbiguous!(delegate Declaration(parsed) { 729 location.spanTo(trange.front.location); 730 trange.match(TokenType.Semicolon); 731 732 alias T = typeof(parsed); 733 static if (is(T : AstType)) { 734 return new TypeAliasDeclaration(location, stc, name, parsed); 735 } else static if (is(T : AstExpression)) { 736 return new ValueAliasDeclaration(location, stc, name, parsed); 737 } else { 738 return new IdentifierAliasDeclaration( 739 location, 740 stc, 741 name, 742 parsed, 743 ); 744 } 745 })(); 746 } else if (trange.front.type == TokenType.This) { 747 // FIXME: move this before storage class parsing. 748 trange.popFront(); 749 location.spanTo(trange.front.location); 750 trange.match(TokenType.Semicolon); 751 752 return new AliasThisDeclaration(location, name); 753 } 754 755 trange.match(TokenType.Begin); 756 assert(0); 757 } 758 759 /** 760 * Parse import declaration 761 */ 762 auto parseImport(ref TokenRange trange) { 763 Location location = trange.front.location; 764 trange.match(TokenType.Import); 765 766 auto parseModuleName(TokenRange)(ref TokenRange trange) { 767 auto mod = [trange.front.name]; 768 trange.match(TokenType.Identifier); 769 while (trange.front.type == TokenType.Dot) { 770 trange.popFront(); 771 772 mod ~= trange.front.name; 773 trange.match(TokenType.Identifier); 774 } 775 776 return mod; 777 } 778 779 auto modules = [parseModuleName(trange)]; 780 while (trange.front.type == TokenType.Comma) { 781 trange.popFront(); 782 783 modules ~= parseModuleName(trange); 784 } 785 786 location.spanTo(trange.front.location); 787 trange.match(TokenType.Semicolon); 788 789 return new ImportDeclaration(location, modules); 790 }