2 * Takes a token stream from the lexer, and parses it into an abstract syntax tree.
4 * Specification: $(LINK2 https://dlang.org/spec/grammar.html, D Grammar)
6 * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
7 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
8 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/parse.d, _parse.d)
10 * Documentation: https://dlang.org/phobos/dmd_parse.html
11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/parse.d
16 import core.stdc.stdio;
17 import core.stdc.string;
22 import dmd.identifier;
26 import dmd.root.filename;
27 import dmd.common.outbuffer;
29 import dmd.root.rootobject;
30 import dmd.root.string;
33 /***********************************************************
35 class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
37 AST.ModuleDeclaration* md;
45 Loc endloc; // set to location of last right curly
46 int inBrackets; // inside [] of array index or slice
47 Loc lookingForElse; // location of lonely if looking for an else
50 /*********************
51 * Use this constructor for string mixins.
53 * loc location in source file of mixin
55 extern (D) this(const ref Loc loc, AST.Module _module, const(char)[] input, bool doDocComment,
56 ErrorSink errorSink) scope
58 super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false,
60 global.vendor, global.versionNumber());
62 //printf("Parser::Parser()\n");
65 if (!writeMixin(input, scanloc) && loc.filename)
67 /* Create a pseudo-filename for the mixin string, as it may not even exist
70 auto len = strlen(loc.filename) + 7 + (loc.linnum).sizeof * 3 + 1;
71 char* filename = cast(char*)mem.xmalloc(len);
72 snprintf(filename, len, "%s-mixin-%d", loc.filename, cast(int)loc.linnum);
73 scanloc.filename = filename;
78 //nextToken(); // start up the scanner
81 extern (D) this(AST.Module _module, const(char)[] input, bool doDocComment, ErrorSink errorSink) scope
83 super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false,
85 global.vendor, global.versionNumber());
87 //printf("Parser::Parser()\n");
90 //nextToken(); // start up the scanner
94 + Parse a module, i.e. the optional `module x.y.z` declaration and all declarations
95 + found in the current file.
97 + Returns: the list of declarations or an empty list in case of malformed declarations,
98 + the module declaration will be stored as `this.md` if found
100 AST.Dsymbols* parseModule()
102 if (!parseModuleDeclaration())
103 return errorReturn();
105 return parseModuleContent();
109 + Parse the optional module declaration
111 + Returns: false if a malformed module declaration was found
113 final bool parseModuleDeclaration()
115 const comment = token.blockComment;
116 bool isdeprecated = false;
117 AST.Expression msg = null;
119 // Parse optional module attributes
120 parseModuleAttributes(msg, isdeprecated);
122 // ModuleDeclaration leads off
123 if (token.value == TOK.module_)
125 const loc = token.loc;
128 /* parse ModuleFullyQualifiedName
129 * https://dlang.org/spec/module.html#ModuleFullyQualifiedName
132 if (token.value != TOK.identifier)
134 error("identifier expected following `module`");
139 Identifier id = token.ident;
141 while (nextToken() == TOK.dot)
145 if (token.value != TOK.identifier)
147 error("identifier expected following `package`");
153 md = new AST.ModuleDeclaration(loc, a, id, msg, isdeprecated);
155 if (token.value != TOK.semicolon)
156 error("`;` expected following module declaration instead of `%s`", token.toChars());
158 addComment(mod, comment);
164 + Parse the content of a module, i.e. all declarations found until the end of file.
166 + Returns: the list of declarations or an empty list in case of malformed declarations
168 final AST.Dsymbols* parseModuleContent()
170 AST.Dsymbol lastDecl = mod;
171 AST.Dsymbols* decldefs = parseDeclDefs(0, &lastDecl);
173 if (token.value == TOK.rightCurly)
175 error("unmatched closing brace");
176 return errorReturn();
179 if (token.value != TOK.endOfFile)
181 error("unrecognized declaration");
182 return errorReturn();
188 + Skips to the end of the current declaration - denoted by either `;` or EOF
190 + Returns: An empty list of Dsymbols
192 private AST.Dsymbols* errorReturn()
194 while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
197 return new AST.Dsymbols();
200 /**********************************
201 * Parse the ModuleAttributes preceding a module declaration.
203 * ModuleAttributes(opt) module ModuleFullyQualifiedName ;
204 * https://dlang.org/spec/module.html#ModuleAttributes
206 * msg = set to the AssignExpression from DeprecatedAttribute https://dlang.org/spec/module.html#DeprecatedAttribute
207 * isdeprecated = set to true if a DeprecatedAttribute is seen
210 void parseModuleAttributes(out AST.Expression msg, out bool isdeprecated)
213 if (!(skipAttributes(&token, &tk) && tk.value == TOK.module_))
214 return; // no module attributes
216 AST.Expressions* udas = null;
217 while (token.value != TOK.module_)
221 case TOK.deprecated_:
223 // deprecated (...) module ...
225 error("there is only one deprecation attribute allowed for module declaration");
228 if (token.value == TOK.leftParenthesis)
230 check(TOK.leftParenthesis);
231 msg = parseAssignExp();
232 check(TOK.rightParenthesis);
238 AST.Expressions* exps = null;
239 const stc = parseAttribute(exps);
240 if (stc & atAttrGroup)
242 error("`@%s` attribute for module declaration is not supported", token.toChars());
246 udas = AST.UserAttributeDeclaration.concat(udas, exps);
254 error("`module` expected instead of `%s`", token.toChars());
263 auto a = new AST.Dsymbols();
264 auto udad = new AST.UserAttributeDeclaration(udas, a);
265 mod.userAttribDecl = udad;
272 * Parses a `deprecated` declaration
275 * msg = Deprecated message, if any.
276 * Used to support overriding a deprecated storage class with
277 * a deprecated declaration with a message, but to error
278 * if both declaration have a message.
281 * Whether the deprecated declaration has a message
283 private bool parseDeprecatedAttribute(ref AST.Expression msg)
285 if (peekNext() != TOK.leftParenthesis)
289 check(TOK.leftParenthesis);
290 AST.Expression e = parseAssignExp();
291 check(TOK.rightParenthesis);
294 error(token.loc, "conflicting storage class `deprecated(%s)` and `deprecated(%s)`", msg.toChars(), e.toChars());
300 /************************************
301 * Parse declarations and definitions
303 * once = !=0 means parse exactly one decl or def
304 * pLastDecl = set to last decl or def parsed
305 * pAttrs = keep track of attributes
307 * array of declared symbols
309 AST.Dsymbols* parseDeclDefs(int once, AST.Dsymbol* pLastDecl = null, PrefixAttributes!AST* pAttrs = null)
311 AST.Dsymbol lastDecl = null; // used to link unittest to its previous declaration
313 pLastDecl = &lastDecl;
315 const linksave = linkage; // save global state
317 //printf("Parser::parseDeclDefs()\n");
318 auto decldefs = new AST.Dsymbols();
322 AST.Dsymbol s = null;
323 AST.Dsymbols* a = null;
325 PrefixAttributes!AST attrs;
326 if (!once || !pAttrs)
329 pAttrs.comment = token.blockComment.ptr;
331 AST.Visibility.Kind prot;
333 AST.Condition condition;
343 /* Determine if this is a manifest constant declaration,
344 * or a conventional enum.
346 const tv = peekNext();
347 if (tv == TOK.leftCurly || tv == TOK.colon)
349 else if (tv != TOK.identifier)
353 const nextv = peekNext2();
354 if (nextv == TOK.leftCurly || nextv == TOK.colon || nextv == TOK.semicolon)
367 s = cast(AST.Dsymbol)parseTemplateDeclaration();
372 const loc = token.loc;
375 case TOK.leftParenthesis:
378 if (isDeclaration(&token, NeedDeclaratorId.mustIfDstyle, TOK.reserved, null))
382 auto exps = parseArguments();
383 check(TOK.semicolon);
384 s = new AST.CompileDeclaration(loc, exps);
390 s = cast(AST.Dsymbol)parseTemplateDeclaration(true);
416 case TOK.imaginary32:
417 case TOK.imaginary64:
418 case TOK.imaginary80:
435 a = parseDeclarations(false, pAttrs, pAttrs.comment);
437 *pLastDecl = (*a)[a.length - 1];
441 if (peekNext() == TOK.dot)
443 s = parseCtor(pAttrs);
447 s = parseDtor(pAttrs);
451 const tv = peekNext();
452 if (tv == TOK.leftParenthesis || tv == TOK.leftCurly)
454 // invariant { statements... }
455 // invariant() { statements... }
456 // invariant (expression);
457 s = parseInvariant(pAttrs);
460 error("invariant body expected, not `%s`", token.toChars());
465 * Ignore unittests in non-root modules.
467 * This mainly means that unittests *inside templates* are only
468 * ever instantiated if the module lexically declaring the
469 * template is one of the root modules.
471 * E.g., compiling some project with `-unittest` does NOT
472 * compile and later run any unittests in instantiations of
473 * templates declared in other libraries.
475 * Declaring unittests *inside* templates is considered an anti-
476 * pattern. In almost all cases, the unittests don't depend on
477 * the template parameters, but instantiate the template with
478 * fixed arguments (e.g., Nullable!T unittests instantiating
479 * Nullable!int), so compiling and running identical tests for
480 * each template instantiation is hardly desirable.
481 * But adding a unittest right below some function being tested
482 * is arguably good for locality, so unittests end up inside
484 * To make sure a template's unittests are run, it should be
485 * instantiated in the same module, e.g., some module-level
488 * Another reason for ignoring unittests in templates from non-
489 * root modules is for template codegen culling via
490 * TemplateInstance.needsCodegen(). If the compiler decides not
491 * to emit some Nullable!bool because there's an existing
492 * instantiation in some non-root module, it has no idea whether
493 * that module was compiled with -unittest too, and so whether
494 * Nullable!int (instantiated in some unittest inside the
495 * Nullable template) can be culled too. By ignoring unittests
496 * in non-root modules, the compiler won't consider any
497 * template instantiations in these unittests as candidates for
498 * further codegen culling.
500 if (mod.isRoot() && (global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput))
502 linkage = LINK.d; // unittests have D linkage
503 s = parseUnitTest(pAttrs);
505 (*pLastDecl).ddocUnittest = cast(AST.UnitTestDeclaration)s;
509 // Skip over unittest block by counting { }
529 error(loc, "closing `}` of unittest not found before end of file");
537 // Workaround 14894. Add an empty unittest declaration to keep
538 // the number of symbols in this scope independent of -unittest.
539 s = new AST.UnitTestDeclaration(loc, token.loc, STC.undefined_, null);
544 s = parseNew(pAttrs);
549 error("declaration expected, not `%s`", token.toChars());
555 error("declaration expected, not `%s`", token.toChars());
560 const next = peekNext();
561 if (next == TOK.this_)
562 s = parseStaticCtor(pAttrs);
563 else if (next == TOK.tilde)
564 s = parseStaticDtor(pAttrs);
565 else if (next == TOK.assert_)
566 s = parseStaticAssert();
567 else if (next == TOK.if_)
569 const Loc loc = token.loc;
570 condition = parseStaticIfCondition();
572 if (token.value == TOK.colon)
573 athen = parseBlock(pLastDecl);
576 const lookingForElseSave = lookingForElse;
577 lookingForElse = token.loc;
578 athen = parseBlock(pLastDecl);
579 lookingForElse = lookingForElseSave;
581 AST.Dsymbols* aelse = null;
582 if (token.value == TOK.else_)
584 const elseloc = token.loc;
586 aelse = parseBlock(pLastDecl);
587 checkDanglingElse(elseloc);
589 s = new AST.StaticIfDeclaration(loc, condition, athen, aelse);
591 else if (next == TOK.import_)
596 else if (next == TOK.foreach_ || next == TOK.foreach_reverse_)
598 s = parseForeach!(AST.StaticForeachDeclaration)(token.loc, pLastDecl);
608 if (peekNext() == TOK.leftParenthesis)
614 if (peekNext() == TOK.leftParenthesis)
616 stc = STC.immutable_;
621 const next = peekNext();
622 if (next == TOK.leftParenthesis)
624 if (next == TOK.static_)
626 TOK next2 = peekNext2();
627 if (next2 == TOK.this_)
629 s = parseSharedStaticCtor(pAttrs);
632 if (next2 == TOK.tilde)
634 s = parseSharedStaticDtor(pAttrs);
642 if (peekNext() == TOK.leftParenthesis)
667 case TOK.synchronized_:
668 stc = STC.synchronized_;
689 AST.Expressions* exps = null;
690 stc = parseAttribute(exps);
692 goto Lstc; // it's a predefined attribute
693 // no redundant/conflicting check for UDAs
694 pAttrs.udas = AST.UserAttributeDeclaration.concat(pAttrs.udas, exps);
698 pAttrs.storageClass = appendStorageClass(pAttrs.storageClass, stc);
703 /* Look for auto initializers:
704 * storage_class identifier = initializer;
705 * storage_class identifier(...) = initializer;
707 if (token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign))
709 a = parseAutoDeclarations(getStorageClass!AST(pAttrs), pAttrs.comment);
711 *pLastDecl = (*a)[a.length - 1];
714 s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
720 /* Look for return type inference for template functions.
723 if (token.value == TOK.identifier && skipParens(peek(&token), &tk) && skipAttributes(tk, &tk) &&
724 (tk.value == TOK.leftParenthesis || tk.value == TOK.leftCurly || tk.value == TOK.in_ ||
725 tk.value == TOK.out_ || tk.value == TOK.do_ || tk.value == TOK.goesTo ||
726 tk.value == TOK.identifier && tk.ident == Id._body))
728 // @@@DEPRECATED_2.117@@@
729 // https://github.com/dlang/DIPs/blob/1f5959abe482b1f9094f6484a7d0a3ade77fc2fc/DIPs/accepted/DIP1003.md
730 // Deprecated in 2.097 - Can be removed from 2.117
731 // The deprecation period is longer than usual as `body`
732 // was quite widely used.
733 if (tk.value == TOK.identifier && tk.ident == Id._body)
734 deprecation("usage of the `body` keyword is deprecated. Use `do` instead.");
736 a = parseDeclarations(true, pAttrs, pAttrs.comment);
738 *pLastDecl = (*a)[a.length - 1];
741 s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
747 a = parseBlock(pLastDecl, pAttrs);
748 auto stc2 = getStorageClass!AST(pAttrs);
749 if (stc2 != STC.undefined_)
751 s = new AST.StorageClassDeclaration(stc2, a);
757 a = new AST.Dsymbols();
760 s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
765 case TOK.deprecated_:
767 stc |= STC.deprecated_;
768 if (!parseDeprecatedAttribute(pAttrs.depmsg))
771 a = parseBlock(pLastDecl, pAttrs);
772 s = new AST.DeprecatedDeclaration(pAttrs.depmsg, a);
773 pAttrs.depmsg = null;
776 case TOK.leftBracket:
778 if (peekNext() == TOK.rightBracket)
779 error("empty attribute list is not allowed");
780 error("use `@(attributes)` instead of `[attributes]`");
781 AST.Expressions* exps = parseArguments();
782 // no redundant/conflicting check for UDAs
784 pAttrs.udas = AST.UserAttributeDeclaration.concat(pAttrs.udas, exps);
785 a = parseBlock(pLastDecl, pAttrs);
788 s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
795 if (peekNext() != TOK.leftParenthesis)
801 const linkLoc = token.loc;
802 auto res = parseLinkage();
803 if (pAttrs.link != LINK.default_)
805 if (pAttrs.link != res.link)
807 error(token.loc, "conflicting linkage `extern (%s)` and `extern (%s)`", AST.linkageToChars(pAttrs.link), AST.linkageToChars(res.link));
809 else if (res.idents || res.identExps || res.cppmangle != CPPMANGLE.def)
812 // extern(C++, foo) extern(C++, bar) void foo();
813 // to be equivalent with:
814 // extern(C++, foo.bar) void foo();
816 // extern(C++, "ns") extern(C++, class) struct test {}
817 // extern(C++, class) extern(C++, "ns") struct test {}
820 error("redundant linkage `extern (%s)`", AST.linkageToChars(pAttrs.link));
822 pAttrs.link = res.link;
823 this.linkage = res.link;
824 this.linkLoc = linkLoc;
825 a = parseBlock(pLastDecl, pAttrs);
828 assert(res.link == LINK.cpp);
829 assert(res.idents.length);
830 for (size_t i = res.idents.length; i;)
832 Identifier id = (*res.idents)[--i];
835 a = new AST.Dsymbols();
838 s = new AST.Nspace(linkLoc, id, null, a);
840 pAttrs.link = LINK.default_;
842 else if (res.identExps)
844 assert(res.link == LINK.cpp);
845 assert(res.identExps.length);
846 for (size_t i = res.identExps.length; i;)
848 AST.Expression exp = (*res.identExps)[--i];
851 a = new AST.Dsymbols();
854 s = new AST.CPPNamespaceDeclaration(linkLoc, exp, a);
856 pAttrs.link = LINK.default_;
858 else if (res.cppmangle != CPPMANGLE.def)
860 assert(res.link == LINK.cpp);
861 s = new AST.CPPMangleDeclaration(linkLoc, res.cppmangle, a);
863 else if (pAttrs.link != LINK.default_)
865 s = new AST.LinkDeclaration(linkLoc, pAttrs.link, a);
866 pAttrs.link = LINK.default_;
872 prot = AST.Visibility.Kind.private_;
876 prot = AST.Visibility.Kind.package_;
880 prot = AST.Visibility.Kind.protected_;
884 prot = AST.Visibility.Kind.public_;
888 prot = AST.Visibility.Kind.export_;
892 if (pAttrs.visibility.kind != AST.Visibility.Kind.undefined)
894 if (pAttrs.visibility.kind != prot)
895 error(token.loc, "conflicting visibility attribute `%s` and `%s`", AST.visibilityToChars(pAttrs.visibility.kind), AST.visibilityToChars(prot));
897 error("redundant visibility attribute `%s`", AST.visibilityToChars(prot));
899 pAttrs.visibility.kind = prot;
900 const attrloc = token.loc;
904 // optional qualified package identifier to bind
906 Identifier[] pkg_prot_idents;
907 if (pAttrs.visibility.kind == AST.Visibility.Kind.package_ && token.value == TOK.leftParenthesis)
909 pkg_prot_idents = parseQualifiedIdentifier("protection package");
911 check(TOK.rightParenthesis);
914 while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
921 a = parseBlock(pLastDecl, pAttrs);
922 if (pAttrs.visibility.kind != AST.Visibility.Kind.undefined)
924 if (pAttrs.visibility.kind == AST.Visibility.Kind.package_ && pkg_prot_idents)
925 s = new AST.VisibilityDeclaration(attrloc, pkg_prot_idents, a);
927 s = new AST.VisibilityDeclaration(attrloc, pAttrs.visibility, a);
929 pAttrs.visibility = AST.Visibility(AST.Visibility.Kind.undefined);
935 const attrLoc = token.loc;
939 AST.Expression e = null; // default
940 if (token.value == TOK.leftParenthesis)
943 e = parseAssignExp();
944 check(TOK.rightParenthesis);
947 if (pAttrs.setAlignment)
950 error("redundant alignment attribute `align(%s)`", e.toChars());
952 error("redundant alignment attribute `align`");
955 pAttrs.setAlignment = true;
957 a = parseBlock(pLastDecl, pAttrs);
958 if (pAttrs.setAlignment)
960 s = new AST.AlignDeclaration(attrLoc, pAttrs.ealign, a);
961 pAttrs.setAlignment = false;
962 pAttrs.ealign = null;
968 AST.Expressions* args = null;
969 const loc = token.loc;
972 check(TOK.leftParenthesis);
973 if (token.value != TOK.identifier)
975 error("`pragma(identifier)` expected");
978 Identifier ident = token.ident;
980 if (token.value == TOK.comma && peekNext() != TOK.rightParenthesis)
981 args = parseArguments(); // pragma(identifier, args...)
983 check(TOK.rightParenthesis); // pragma(identifier)
985 AST.Dsymbols* a2 = null;
986 if (token.value == TOK.semicolon)
988 /* https://issues.dlang.org/show_bug.cgi?id=2354
989 * Accept single semicolon as an empty
990 * DeclarationBlock following attribute.
992 * Attribute DeclarationBlock
999 a2 = parseBlock(pLastDecl);
1000 s = new AST.PragmaDeclaration(loc, ident, args, a2);
1004 startloc = token.loc;
1006 if (token.value == TOK.assign)
1008 s = parseDebugSpecification();
1011 condition = parseDebugCondition();
1015 startloc = token.loc;
1017 if (token.value == TOK.assign)
1019 s = parseVersionSpecification();
1022 condition = parseVersionCondition();
1027 AST.Dsymbols* athen;
1028 if (token.value == TOK.colon)
1029 athen = parseBlock(pLastDecl);
1032 const lookingForElseSave = lookingForElse;
1033 lookingForElse = token.loc;
1034 athen = parseBlock(pLastDecl);
1035 lookingForElse = lookingForElseSave;
1037 AST.Dsymbols* aelse = null;
1038 if (token.value == TOK.else_)
1040 const elseloc = token.loc;
1042 aelse = parseBlock(pLastDecl);
1043 checkDanglingElse(elseloc);
1045 s = new AST.ConditionalDeclaration(startloc, condition, athen, aelse);
1049 // empty declaration
1050 //error("empty declaration");
1055 error("declaration expected, not `%s`", token.toChars());
1057 while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
1066 if (!s.isAttribDeclaration())
1069 addComment(s, pAttrs.comment);
1071 else if (a && a.length)
1083 /*****************************************
1084 * Parse auto declarations of the form:
1085 * storageClass ident = init, ident = init, ... ;
1086 * and return the array of them.
1087 * Starts with token on the first ident.
1088 * Ends with scanner past closing ';'
1090 private AST.Dsymbols* parseAutoDeclarations(StorageClass storageClass, const(char)* comment)
1092 //printf("parseAutoDeclarations\n");
1093 auto a = new AST.Dsymbols();
1097 const loc = token.loc;
1098 Identifier ident = token.ident;
1099 nextToken(); // skip over ident
1101 AST.TemplateParameters* tpl = null;
1102 if (token.value == TOK.leftParenthesis)
1103 tpl = parseTemplateParameterList();
1105 check(TOK.assign); // skip over '='
1106 AST.Initializer _init = parseInitializer();
1107 auto v = new AST.VarDeclaration(loc, null, ident, _init, storageClass);
1112 auto a2 = new AST.Dsymbols();
1114 auto tempdecl = new AST.TemplateDeclaration(loc, ident, tpl, null, a2, 0);
1118 switch (token.value)
1122 addComment(s, comment);
1127 if (!(token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign)))
1129 error("identifier expected following comma");
1132 addComment(s, comment);
1136 error("semicolon expected following auto declaration, not `%s`", token.toChars());
1144 /********************************************
1145 * Parse declarations after an align, visibility, or extern decl.
1147 private AST.Dsymbols* parseBlock(AST.Dsymbol* pLastDecl, PrefixAttributes!AST* pAttrs = null)
1149 AST.Dsymbols* a = null;
1151 //printf("parseBlock()\n");
1152 switch (token.value)
1155 error("declaration expected following attribute, not `;`");
1160 error("declaration expected following attribute, not end of file");
1165 const lookingForElseSave = lookingForElse;
1166 lookingForElse = Loc();
1169 a = parseDeclDefs(0, pLastDecl);
1170 if (token.value != TOK.rightCurly)
1173 error("matching `}` expected, not `%s`", token.toChars());
1177 lookingForElse = lookingForElseSave;
1182 a = parseDeclDefs(0, pLastDecl); // grab declarations up to closing curly bracket
1186 a = parseDeclDefs(1, pLastDecl, pAttrs);
1193 * Provide an error message if `added` contains storage classes which are
1194 * redundant with those in `orig`; otherwise, return the combination.
1197 * orig = The already applied storage class.
1198 * added = The new storage class to add to `orig`.
1201 * The combination of both storage classes (`orig | added`).
1203 private StorageClass appendStorageClass(StorageClass orig, StorageClass added)
1205 void checkConflictSTCGroup(bool at = false)(StorageClass group)
1207 if (added & group && orig & group & ((orig & group) - 1))
1209 at ? "conflicting attribute `@%s`"
1210 : "conflicting attribute `%s`",
1217 AST.stcToBuffer(&buf, added);
1218 error("redundant attribute `%s`", buf.peekChars());
1219 return orig | added;
1222 const Redundant = (STC.const_ | STC.scope_ |
1223 (global.params.previewIn ? STC.ref_ : 0));
1226 if ((orig & STC.in_) && (added & Redundant))
1228 if (added & STC.const_)
1229 error("attribute `const` is redundant with previously-applied `in`");
1230 else if (global.params.previewIn)
1232 error("attribute `%s` is redundant with previously-applied `in`",
1233 (orig & STC.scope_) ? "scope".ptr : "ref".ptr);
1236 error("attribute `scope` cannot be applied with `in`, use `-preview=in` instead");
1240 if ((added & STC.in_) && (orig & Redundant))
1242 if (orig & STC.const_)
1243 error("attribute `in` cannot be added after `const`: remove `const`");
1244 else if (global.params.previewIn)
1246 // Windows `printf` does not support `%1$s`
1247 const(char*) stc_str = (orig & STC.scope_) ? "scope".ptr : "ref".ptr;
1248 error(token.loc, "attribute `in` cannot be added after `%s`: remove `%s`",
1252 error("attribute `in` cannot be added after `scope`: remove `scope` and use `-preview=in`");
1256 checkConflictSTCGroup(STC.const_ | STC.immutable_ | STC.manifest);
1257 checkConflictSTCGroup(STC.gshared | STC.shared_);
1258 checkConflictSTCGroup!true(STC.safeGroup);
1263 /***********************************************
1264 * Parse attribute(s), lexer is on '@'.
1266 * Attributes can be builtin (e.g. `@safe`, `@nogc`, etc...),
1267 * or be user-defined (UDAs). In the former case, we return the storage
1268 * class via the return value, while in thelater case we return `0`
1272 * pudas = An array of UDAs to append to
1275 * If the attribute is builtin, the return value will be non-zero.
1276 * Otherwise, 0 is returned, and `pudas` will be appended to.
1278 private StorageClass parseAttribute(ref AST.Expressions* udas)
1281 if (token.value == TOK.identifier)
1283 // If we find a builtin attribute, we're done, return immediately.
1284 if (StorageClass stc = isBuiltinAtAttribute(token.ident))
1287 // Allow identifier, template instantiation, or function call
1288 // for `@Argument` (single UDA) form.
1289 AST.Expression exp = parsePrimaryExp();
1290 if (token.value == TOK.leftParenthesis)
1292 const loc = token.loc;
1293 AST.Expressions* args = new AST.Expressions();
1294 AST.Identifiers* names = new AST.Identifiers();
1295 parseNamedArguments(args, names);
1296 exp = new AST.CallExp(loc, exp, args, names);
1300 udas = new AST.Expressions();
1305 if (token.value == TOK.leftParenthesis)
1307 // Multi-UDAs ( `@( ArgumentList )`) form, concatenate with existing
1308 if (peekNext() == TOK.rightParenthesis)
1309 error("empty attribute list is not allowed");
1310 udas = AST.UserAttributeDeclaration.concat(udas, parseArguments());
1314 if (token.isKeyword())
1315 error("`%s` is a keyword, not an `@` attribute", token.toChars());
1317 error("`@identifier` or `@(ArgumentList)` expected, not `@%s`", token.toChars());
1322 /***********************************************
1323 * Parse const/immutable/shared/inout/nothrow/pure postfix
1325 private StorageClass parsePostfix(StorageClass storageClass, AST.Expressions** pudas)
1330 switch (token.value)
1336 case TOK.immutable_:
1337 stc = STC.immutable_;
1358 if (peekNext() == TOK.scope_)
1359 stc |= STC.returnScope; // recognize `return scope`
1368 AST.Expressions* udas = null;
1369 stc = parseAttribute(udas);
1373 *pudas = AST.UserAttributeDeclaration.concat(*pudas, udas);
1377 // void function() @uda fp;
1378 // () @uda { return 1; }
1379 error("user-defined attributes cannot appear as postfixes");
1386 return storageClass;
1388 storageClass = appendStorageClass(storageClass, stc);
1393 private StorageClass parseTypeCtor()
1395 StorageClass storageClass = STC.undefined_;
1399 if (peekNext() == TOK.leftParenthesis)
1400 return storageClass;
1403 switch (token.value)
1409 case TOK.immutable_:
1410 stc = STC.immutable_;
1422 return storageClass;
1424 storageClass = appendStorageClass(storageClass, stc);
1429 /**************************************
1431 * Constraint is of the form:
1432 * if ( ConstraintExpression )
1434 private AST.Expression parseConstraint()
1436 AST.Expression e = null;
1437 if (token.value == TOK.if_)
1439 nextToken(); // skip over 'if'
1440 check(TOK.leftParenthesis);
1441 e = parseExpression();
1442 check(TOK.rightParenthesis);
1447 /**************************************
1448 * Parse a TemplateDeclaration.
1450 private AST.TemplateDeclaration parseTemplateDeclaration(bool ismixin = false)
1452 AST.TemplateDeclaration tempdecl;
1454 AST.TemplateParameters* tpl;
1455 AST.Dsymbols* decldefs;
1456 AST.Expression constraint = null;
1457 const loc = token.loc;
1460 if (token.value != TOK.identifier)
1462 error("identifier expected following `template`");
1467 tpl = parseTemplateParameterList();
1471 constraint = parseConstraint();
1473 if (token.value != TOK.leftCurly)
1475 error("`{` expected after template parameter list, not `%s`", token.toChars());
1478 decldefs = parseBlock(null);
1480 tempdecl = new AST.TemplateDeclaration(loc, id, tpl, constraint, decldefs, ismixin);
1487 /******************************************
1488 * Parse template parameter list.
1490 * flag 0: parsing "( list )"
1491 * 1: parsing non-empty "list $(RPAREN)"
1493 private AST.TemplateParameters* parseTemplateParameterList(int flag = 0)
1495 auto tpl = new AST.TemplateParameters();
1497 if (!flag && token.value != TOK.leftParenthesis)
1499 error("parenthesized template parameter list expected following template identifier");
1504 // Get array of TemplateParameters
1505 if (flag || token.value != TOK.rightParenthesis)
1507 while (token.value != TOK.rightParenthesis)
1509 AST.TemplateParameter tp;
1511 Identifier tp_ident = null;
1512 AST.Type tp_spectype = null;
1513 AST.Type tp_valtype = null;
1514 AST.Type tp_defaulttype = null;
1515 AST.Expression tp_specvalue = null;
1516 AST.Expression tp_defaultvalue = null;
1518 // Get TemplateParameter
1520 // First, look ahead to see if it is a TypeParameter or a ValueParameter
1521 const tv = peekNext();
1522 if (token.value == TOK.alias_)
1526 loc = token.loc; // todo
1527 AST.Type spectype = null;
1528 if (isDeclaration(&token, NeedDeclaratorId.must, TOK.reserved, null))
1530 spectype = parseType(&tp_ident);
1534 if (token.value != TOK.identifier)
1536 error("identifier expected for template `alias` parameter");
1539 tp_ident = token.ident;
1542 RootObject spec = null;
1543 if (token.value == TOK.colon) // : Type
1546 if (isDeclaration(&token, NeedDeclaratorId.no, TOK.reserved, null))
1549 spec = parseCondExp();
1551 RootObject def = null;
1552 if (token.value == TOK.assign) // = Type
1555 if (isDeclaration(&token, NeedDeclaratorId.no, TOK.reserved, null))
1558 def = parseCondExp();
1560 tp = new AST.TemplateAliasParameter(loc, tp_ident, spectype, spec, def);
1562 else if (tv == TOK.colon || tv == TOK.assign || tv == TOK.comma || tv == TOK.rightParenthesis)
1565 if (token.value != TOK.identifier)
1567 error("identifier expected for template type parameter");
1571 tp_ident = token.ident;
1573 if (token.value == TOK.colon) // : Type
1576 tp_spectype = parseType();
1578 if (token.value == TOK.assign) // = Type
1581 tp_defaulttype = parseType();
1583 tp = new AST.TemplateTypeParameter(loc, tp_ident, tp_spectype, tp_defaulttype);
1585 else if (token.value == TOK.identifier && tv == TOK.dotDotDot)
1589 tp_ident = token.ident;
1592 tp = new AST.TemplateTupleParameter(loc, tp_ident);
1594 else if (token.value == TOK.this_)
1598 if (token.value != TOK.identifier)
1600 error("identifier expected for template `this` parameter");
1604 tp_ident = token.ident;
1606 if (token.value == TOK.colon) // : Type
1609 tp_spectype = parseType();
1611 if (token.value == TOK.assign) // = Type
1614 tp_defaulttype = parseType();
1616 tp = new AST.TemplateThisParameter(loc, tp_ident, tp_spectype, tp_defaulttype);
1621 loc = token.loc; // todo
1622 tp_valtype = parseType(&tp_ident);
1625 error("identifier expected for template value parameter");
1626 tp_ident = Identifier.idPool("error");
1628 if (token.value == TOK.colon) // : CondExpression
1631 tp_specvalue = parseCondExp();
1633 if (token.value == TOK.assign) // = CondExpression
1636 tp_defaultvalue = parseDefaultInitExp();
1638 tp = new AST.TemplateValueParameter(loc, tp_ident, tp_valtype, tp_specvalue, tp_defaultvalue);
1641 if (token.value != TOK.comma)
1646 check(TOK.rightParenthesis);
1652 /******************************************
1653 * Parse template mixin.
1656 * mixin a.b.c!(args).Foo!(args);
1657 * mixin Foo!(args) identifier;
1658 * mixin typeof(expr).identifier!(args);
1660 private AST.Dsymbol parseMixin()
1662 AST.TemplateMixin tm;
1664 AST.Objects* tiargs;
1666 //printf("parseMixin()\n");
1667 const locMixin = token.loc;
1668 nextToken(); // skip 'mixin'
1670 auto loc = token.loc;
1671 AST.TypeQualified tqual = null;
1672 if (token.value == TOK.dot)
1678 if (token.value == TOK.typeof_)
1680 tqual = parseTypeof();
1683 if (token.value != TOK.identifier)
1685 error("identifier expected, not `%s`", token.toChars());
1696 if (token.value == TOK.not)
1698 tiargs = parseTemplateArguments();
1701 if (tiargs && token.value == TOK.dot)
1703 auto tempinst = new AST.TemplateInstance(loc, id, tiargs);
1705 tqual = new AST.TypeInstance(loc, tempinst);
1707 tqual.addInst(tempinst);
1713 tqual = new AST.TypeIdentifier(loc, id);
1718 if (token.value != TOK.dot)
1722 if (token.value != TOK.identifier)
1724 error("identifier expected following `.` instead of `%s`", token.toChars());
1733 if (token.value == TOK.identifier)
1739 tm = new AST.TemplateMixin(locMixin, id, tqual, tiargs);
1740 if (token.value != TOK.semicolon)
1741 error("`;` expected after `mixin`");
1747 /******************************************
1748 * Parse template arguments.
1750 * current token is opening '!'
1752 * current token is one after closing '$(RPAREN)'
1754 private AST.Objects* parseTemplateArguments()
1756 AST.Objects* tiargs;
1759 if (token.value == TOK.leftParenthesis)
1761 // ident!(template_arguments)
1762 tiargs = parseTemplateArgumentList();
1766 // ident!template_argument
1767 tiargs = parseTemplateSingleArgument();
1769 if (token.value == TOK.not)
1771 TOK tok = peekNext();
1772 if (tok != TOK.is_ && tok != TOK.in_)
1774 error("multiple ! arguments are not allowed");
1777 if (token.value == TOK.leftParenthesis)
1778 parseTemplateArgumentList();
1780 parseTemplateSingleArgument();
1781 if (token.value == TOK.not && (tok = peekNext()) != TOK.is_ && tok != TOK.in_)
1788 /******************************************
1789 * Parse template argument list.
1791 * current token is opening '$(LPAREN)',
1792 * or ',' for __traits
1794 * current token is one after closing '$(RPAREN)'
1796 private AST.Objects* parseTemplateArgumentList()
1798 //printf("Parser::parseTemplateArgumentList()\n");
1799 auto tiargs = new AST.Objects();
1800 TOK endtok = TOK.rightParenthesis;
1801 assert(token.value == TOK.leftParenthesis || token.value == TOK.comma);
1804 // Get TemplateArgumentList
1805 while (token.value != endtok)
1807 tiargs.push(parseTypeOrAssignExp());
1808 if (token.value != TOK.comma)
1812 check(endtok, "template argument list");
1816 /***************************************
1817 * Parse a Type or an Expression
1819 * RootObject representing the AST
1821 RootObject parseTypeOrAssignExp(TOK endtoken = TOK.reserved)
1823 return isDeclaration(&token, NeedDeclaratorId.no, endtoken, null)
1824 ? parseType() // argument is a type
1825 : parseAssignExp(); // argument is an expression
1828 /*****************************
1829 * Parse single template argument, to support the syntax:
1832 * current token is the arg
1834 private AST.Objects* parseTemplateSingleArgument()
1836 //printf("parseTemplateSingleArgument()\n");
1837 auto tiargs = new AST.Objects();
1839 switch (token.value)
1841 case TOK.identifier:
1842 ta = new AST.TypeIdentifier(token.loc, token.ident);
1850 ta = AST.Type.tvoid;
1854 ta = AST.Type.tint8;
1858 ta = AST.Type.tuns8;
1862 ta = AST.Type.tint16;
1866 ta = AST.Type.tuns16;
1870 ta = AST.Type.tint32;
1874 ta = AST.Type.tuns32;
1878 ta = AST.Type.tint64;
1882 ta = AST.Type.tuns64;
1886 ta = AST.Type.tint128;
1890 ta = AST.Type.tuns128;
1894 ta = AST.Type.tfloat32;
1898 ta = AST.Type.tfloat64;
1902 ta = AST.Type.tfloat80;
1905 case TOK.imaginary32:
1906 ta = AST.Type.timaginary32;
1909 case TOK.imaginary64:
1910 ta = AST.Type.timaginary64;
1913 case TOK.imaginary80:
1914 ta = AST.Type.timaginary80;
1918 ta = AST.Type.tcomplex32;
1922 ta = AST.Type.tcomplex64;
1926 ta = AST.Type.tcomplex80;
1930 ta = AST.Type.tbool;
1934 ta = AST.Type.tchar;
1938 ta = AST.Type.twchar;
1942 ta = AST.Type.tdchar;
1949 case TOK.int32Literal:
1950 case TOK.uns32Literal:
1951 case TOK.int64Literal:
1952 case TOK.uns64Literal:
1953 case TOK.int128Literal:
1954 case TOK.uns128Literal:
1955 case TOK.float32Literal:
1956 case TOK.float64Literal:
1957 case TOK.float80Literal:
1958 case TOK.imaginary32Literal:
1959 case TOK.imaginary64Literal:
1960 case TOK.imaginary80Literal:
1964 case TOK.charLiteral:
1965 case TOK.wcharLiteral:
1966 case TOK.dcharLiteral:
1969 case TOK.fileFullPath:
1971 case TOK.moduleString:
1972 case TOK.functionString:
1973 case TOK.prettyFunction:
1976 // Template argument is an expression
1977 AST.Expression ea = parsePrimaryExp();
1982 error("template argument expected following `!`");
1988 /**********************************
1989 * Parse a static assertion.
1990 * Current token is 'static'.
1992 private AST.StaticAssert parseStaticAssert()
1994 const loc = token.loc;
1996 AST.Expressions* msg = null;
1998 //printf("parseStaticAssert()\n");
2001 check(TOK.leftParenthesis);
2002 exp = parseAssignExp();
2003 if (token.value == TOK.comma)
2005 if (peekNext() == TOK.rightParenthesis)
2007 nextToken(); // consume `,`
2008 nextToken(); // consume `)`
2011 msg = parseArguments();
2014 check(TOK.rightParenthesis);
2015 check(TOK.semicolon, "static assert");
2016 return new AST.StaticAssert(loc, exp, msg);
2019 /***********************************
2020 * Parse typeof(expression).
2021 * Current token is on the 'typeof'.
2023 private AST.TypeQualified parseTypeof()
2025 AST.TypeQualified t;
2026 const loc = token.loc;
2029 check(TOK.leftParenthesis);
2030 if (token.value == TOK.return_) // typeof(return)
2033 t = new AST.TypeReturn(loc);
2037 AST.Expression exp = parseExpression(); // typeof(expression)
2038 t = new AST.TypeTypeof(loc, exp);
2040 check(TOK.rightParenthesis);
2044 /***********************************
2045 * Parse __vector(type).
2046 * Current token is on the '__vector'.
2048 private AST.Type parseVector()
2051 check(TOK.leftParenthesis);
2052 AST.Type tb = parseType();
2053 check(TOK.rightParenthesis);
2054 return new AST.TypeVector(tb);
2057 /***********************************
2060 * extern (C++, namespaces)
2061 * extern (C++, "namespace", "namespaces", ...)
2062 * extern (C++, (StringExp))
2063 * The parser is on the 'extern' token.
2065 private ParsedLinkage!(AST) parseLinkage()
2067 ParsedLinkage!(AST) result;
2069 assert(token.value == TOK.leftParenthesis);
2071 ParsedLinkage!(AST) returnLinkage(LINK link)
2073 check(TOK.rightParenthesis);
2077 ParsedLinkage!(AST) invalidLinkage()
2079 error("valid linkage identifiers are `D`, `C`, `C++`, `Objective-C`, `Windows`, `System`");
2080 return returnLinkage(LINK.d);
2083 if (token.value != TOK.identifier)
2084 return returnLinkage(LINK.d);
2086 Identifier id = token.ident;
2088 if (id == Id.Windows)
2089 return returnLinkage(LINK.windows);
2090 else if (id == Id.D)
2091 return returnLinkage(LINK.d);
2092 else if (id == Id.System)
2093 return returnLinkage(LINK.system);
2094 else if (id == Id.Objective) // Looking for tokens "Objective-C"
2096 if (token.value != TOK.min)
2097 return invalidLinkage();
2100 if (token.ident != Id.C)
2101 return invalidLinkage();
2104 return returnLinkage(LINK.objc);
2106 else if (id != Id.C)
2107 return invalidLinkage();
2109 if (token.value != TOK.plusPlus)
2110 return returnLinkage(LINK.c);
2113 if (token.value != TOK.comma) // , namespaces or class or struct
2114 return returnLinkage(LINK.cpp);
2118 if (token.value == TOK.rightParenthesis)
2119 return returnLinkage(LINK.cpp); // extern(C++,)
2121 if (token.value == TOK.class_ || token.value == TOK.struct_)
2123 result.cppmangle = token.value == TOK.class_ ? CPPMANGLE.asClass : CPPMANGLE.asStruct;
2126 else if (token.value == TOK.identifier) // named scope namespace
2128 result.idents = new AST.Identifiers();
2131 Identifier idn = token.ident;
2132 result.idents.push(idn);
2134 if (token.value == TOK.dot)
2137 if (token.value == TOK.identifier)
2139 error("identifier expected for C++ namespace");
2140 result.idents = null; // error occurred, invalidate list of elements.
2145 else // non-scoped StringExp namespace
2147 result.identExps = new AST.Expressions();
2150 result.identExps.push(parseCondExp());
2151 if (token.value != TOK.comma)
2154 // Allow trailing commas as done for argument lists, arrays, ...
2155 if (token.value == TOK.rightParenthesis)
2159 return returnLinkage(LINK.cpp);
2162 /***********************************
2163 * Parse ident1.ident2.ident3
2166 * entity = what qualified identifier is expected to resolve into.
2167 * Used only for better error message
2170 * array of identifiers with actual qualified one stored last
2172 private Identifier[] parseQualifiedIdentifier(const(char)* entity)
2174 Identifier[] qualified;
2179 if (token.value != TOK.identifier)
2181 error(token.loc, "`%s` expected as dot-separated identifiers, got `%s`", entity, token.toChars());
2185 Identifier id = token.ident;
2190 while (token.value == TOK.dot);
2195 private AST.DebugSymbol parseDebugSpecification()
2199 if (token.value == TOK.identifier)
2200 s = new AST.DebugSymbol(token.loc, token.ident);
2201 else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
2203 // @@@DEPRECATED_2.111@@@
2204 // Deprecated in 2.101, remove in 2.111
2205 deprecation("`debug = <integer>` is deprecated, use debug identifiers instead");
2207 s = new AST.DebugSymbol(token.loc, cast(uint)token.unsvalue);
2211 error("identifier or integer expected, not `%s`", token.toChars());
2215 if (token.value != TOK.semicolon)
2216 error("semicolon expected");
2221 /**************************************
2222 * Parse a debug conditional
2224 private AST.Condition parseDebugCondition()
2227 Identifier id = null;
2228 Loc loc = token.loc;
2230 if (token.value == TOK.leftParenthesis)
2234 if (token.value == TOK.identifier)
2236 else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
2238 // @@@DEPRECATED_2.111@@@
2239 // Deprecated in 2.101, remove in 2.111
2240 deprecation("`debug( <integer> )` is deprecated, use debug identifiers instead");
2242 level = cast(uint)token.unsvalue;
2245 error("identifier or integer expected inside `debug(...)`, not `%s`", token.toChars());
2248 check(TOK.rightParenthesis);
2250 return new AST.DebugCondition(loc, mod, level, id);
2253 /**************************************
2254 * Parse a version specification
2256 private AST.VersionSymbol parseVersionSpecification()
2258 AST.VersionSymbol s;
2260 if (token.value == TOK.identifier)
2261 s = new AST.VersionSymbol(token.loc, token.ident);
2262 else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
2264 // @@@DEPRECATED_2.111@@@
2265 // Deprecated in 2.101, remove in 2.111
2266 deprecation("`version = <integer>` is deprecated, use version identifiers instead");
2267 s = new AST.VersionSymbol(token.loc, cast(uint)token.unsvalue);
2271 error("identifier or integer expected, not `%s`", token.toChars());
2275 if (token.value != TOK.semicolon)
2276 error("semicolon expected");
2281 /**************************************
2282 * Parse a version conditional
2284 private AST.Condition parseVersionCondition()
2287 Identifier id = null;
2290 if (token.value == TOK.leftParenthesis)
2294 * version (unittest)
2296 * even though they are keywords
2299 if (token.value == TOK.identifier)
2301 else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
2303 // @@@DEPRECATED_2.111@@@
2304 // Deprecated in 2.101, remove in 2.111
2305 deprecation("`version( <integer> )` is deprecated, use version identifiers instead");
2307 level = cast(uint)token.unsvalue;
2309 else if (token.value == TOK.unittest_)
2310 id = Identifier.idPool(Token.toString(TOK.unittest_));
2311 else if (token.value == TOK.assert_)
2312 id = Identifier.idPool(Token.toString(TOK.assert_));
2314 error("identifier or integer expected inside `version(...)`, not `%s`", token.toChars());
2316 check(TOK.rightParenthesis);
2319 error("(condition) expected following `version`");
2320 return new AST.VersionCondition(loc, mod, level, id);
2323 /***********************************************
2324 * static if (expression)
2328 * Current token is 'static'.
2330 private AST.Condition parseStaticIfCondition()
2333 AST.Condition condition;
2334 const loc = token.loc;
2338 if (token.value == TOK.leftParenthesis)
2341 exp = parseAssignExp();
2342 check(TOK.rightParenthesis);
2346 error("(expression) expected following `static if`");
2349 condition = new AST.StaticIfCondition(loc, exp);
2353 /*****************************************
2354 * Parse a constructor definition:
2355 * this(parameters) { body }
2357 * this(this) { body }
2358 * or constructor template:
2359 * this(templateparameters)(parameters) { body }
2360 * Current token is 'this'.
2362 private AST.Dsymbol parseCtor(PrefixAttributes!AST* pAttrs)
2364 AST.Expressions* udas = null;
2365 const loc = token.loc;
2366 StorageClass stc = getStorageClass!AST(pAttrs);
2369 if (token.value == TOK.leftParenthesis && peekNext() == TOK.this_ && peekNext2() == TOK.rightParenthesis)
2371 // this(this) { ... }
2374 check(TOK.rightParenthesis);
2376 stc = parsePostfix(stc, &udas);
2377 if (stc & STC.immutable_)
2378 deprecation("`immutable` postblit is deprecated. Please use an unqualified postblit.");
2379 if (stc & STC.shared_)
2380 deprecation("`shared` postblit is deprecated. Please use an unqualified postblit.");
2381 if (stc & STC.const_)
2382 deprecation("`const` postblit is deprecated. Please use an unqualified postblit.");
2383 if (stc & STC.static_)
2384 error(loc, "postblit cannot be `static`");
2386 auto f = new AST.PostBlitDeclaration(loc, Loc.initial, stc, Id.postblit);
2387 AST.Dsymbol s = parseContracts(f);
2390 auto a = new AST.Dsymbols();
2392 s = new AST.UserAttributeDeclaration(udas, a);
2397 /* Look ahead to see if:
2399 * which is a constructor template
2401 AST.TemplateParameters* tpl = null;
2402 if (token.value == TOK.leftParenthesis && peekPastParen(&token).value == TOK.leftParenthesis)
2404 tpl = parseTemplateParameterList();
2407 /* Just a regular constructor
2409 auto parameterList = parseParameterList(null);
2410 stc = parsePostfix(stc, &udas);
2412 if (parameterList.varargs != VarArg.none || AST.Parameter.dim(parameterList.parameters) != 0)
2414 if (stc & STC.static_)
2415 error(loc, "constructor cannot be static");
2417 else if (StorageClass ss = stc & (STC.shared_ | STC.static_)) // this()
2419 if (ss == STC.static_)
2420 error(loc, "use `static this()` to declare a static constructor");
2421 else if (ss == (STC.shared_ | STC.static_))
2422 error(loc, "use `shared static this()` to declare a shared static constructor");
2425 AST.Expression constraint = tpl ? parseConstraint() : null;
2427 AST.Type tf = new AST.TypeFunction(parameterList, null, linkage, stc); // RetrunType -> auto
2428 tf = tf.addSTC(stc);
2430 auto f = new AST.CtorDeclaration(loc, Loc.initial, stc, tf);
2431 AST.Dsymbol s = parseContracts(f, !!tpl);
2434 auto a = new AST.Dsymbols();
2436 s = new AST.UserAttributeDeclaration(udas, a);
2441 // Wrap a template around it
2442 auto decldefs = new AST.Dsymbols();
2444 s = new AST.TemplateDeclaration(loc, f.ident, tpl, constraint, decldefs);
2450 /*****************************************
2451 * Parse a destructor definition:
2453 * Current token is '~'.
2455 private AST.Dsymbol parseDtor(PrefixAttributes!AST* pAttrs)
2457 AST.Expressions* udas = null;
2458 const loc = token.loc;
2459 StorageClass stc = getStorageClass!AST(pAttrs);
2463 check(TOK.leftParenthesis);
2464 check(TOK.rightParenthesis);
2466 stc = parsePostfix(stc, &udas);
2467 if (StorageClass ss = stc & (STC.shared_ | STC.static_))
2469 if (ss == STC.static_)
2470 error(loc, "use `static ~this()` to declare a static destructor");
2471 else if (ss == (STC.shared_ | STC.static_))
2472 error(loc, "use `shared static ~this()` to declare a shared static destructor");
2475 auto f = new AST.DtorDeclaration(loc, Loc.initial, stc, Id.dtor);
2476 AST.Dsymbol s = parseContracts(f);
2479 auto a = new AST.Dsymbols();
2481 s = new AST.UserAttributeDeclaration(udas, a);
2486 /*****************************************
2487 * Parse a static constructor definition:
2488 * static this() { body }
2489 * Current token is 'static'.
2491 private AST.Dsymbol parseStaticCtor(PrefixAttributes!AST* pAttrs)
2493 //Expressions *udas = NULL;
2494 const loc = token.loc;
2495 StorageClass stc = getStorageClass!AST(pAttrs);
2499 check(TOK.leftParenthesis);
2500 check(TOK.rightParenthesis);
2502 stc = parsePostfix(stc & ~STC.TYPECTOR, null) | stc;
2503 if (stc & STC.shared_)
2504 error(loc, "use `shared static this()` to declare a shared static constructor");
2505 else if (stc & STC.static_)
2506 appendStorageClass(stc, STC.static_); // complaint for the redundancy
2507 else if (StorageClass modStc = stc & STC.TYPECTOR)
2510 AST.stcToBuffer(&buf, modStc);
2511 error(loc, "static constructor cannot be `%s`", buf.peekChars());
2513 stc &= ~(STC.static_ | STC.TYPECTOR);
2515 auto f = new AST.StaticCtorDeclaration(loc, Loc.initial, stc);
2516 AST.Dsymbol s = parseContracts(f);
2520 /*****************************************
2521 * Parse a static destructor definition:
2522 * static ~this() { body }
2523 * Current token is 'static'.
2525 private AST.Dsymbol parseStaticDtor(PrefixAttributes!AST* pAttrs)
2527 AST.Expressions* udas = null;
2528 const loc = token.loc;
2529 StorageClass stc = getStorageClass!AST(pAttrs);
2534 check(TOK.leftParenthesis);
2535 check(TOK.rightParenthesis);
2537 stc = parsePostfix(stc & ~STC.TYPECTOR, &udas) | stc;
2538 if (stc & STC.shared_)
2539 error(loc, "use `shared static ~this()` to declare a shared static destructor");
2540 else if (stc & STC.static_)
2541 appendStorageClass(stc, STC.static_); // complaint for the redundancy
2542 else if (StorageClass modStc = stc & STC.TYPECTOR)
2545 AST.stcToBuffer(&buf, modStc);
2546 error(loc, "static destructor cannot be `%s`", buf.peekChars());
2548 stc &= ~(STC.static_ | STC.TYPECTOR);
2550 auto f = new AST.StaticDtorDeclaration(loc, Loc.initial, stc);
2551 AST.Dsymbol s = parseContracts(f);
2554 auto a = new AST.Dsymbols();
2556 s = new AST.UserAttributeDeclaration(udas, a);
2561 /*****************************************
2562 * Parse a shared static constructor definition:
2563 * shared static this() { body }
2564 * Current token is 'shared'.
2566 private AST.Dsymbol parseSharedStaticCtor(PrefixAttributes!AST* pAttrs)
2568 //Expressions *udas = NULL;
2569 const loc = token.loc;
2570 StorageClass stc = getStorageClass!AST(pAttrs);
2575 check(TOK.leftParenthesis);
2576 check(TOK.rightParenthesis);
2578 stc = parsePostfix(stc & ~STC.TYPECTOR, null) | stc;
2579 if (StorageClass ss = stc & (STC.shared_ | STC.static_))
2580 appendStorageClass(stc, ss); // complaint for the redundancy
2581 else if (StorageClass modStc = stc & STC.TYPECTOR)
2584 AST.stcToBuffer(&buf, modStc);
2585 error(loc, "shared static constructor cannot be `%s`", buf.peekChars());
2587 stc &= ~(STC.static_ | STC.TYPECTOR);
2589 auto f = new AST.SharedStaticCtorDeclaration(loc, Loc.initial, stc);
2590 AST.Dsymbol s = parseContracts(f);
2594 /*****************************************
2595 * Parse a shared static destructor definition:
2596 * shared static ~this() { body }
2597 * Current token is 'shared'.
2599 private AST.Dsymbol parseSharedStaticDtor(PrefixAttributes!AST* pAttrs)
2601 AST.Expressions* udas = null;
2602 const loc = token.loc;
2603 StorageClass stc = getStorageClass!AST(pAttrs);
2609 check(TOK.leftParenthesis);
2610 check(TOK.rightParenthesis);
2612 stc = parsePostfix(stc & ~STC.TYPECTOR, &udas) | stc;
2613 if (StorageClass ss = stc & (STC.shared_ | STC.static_))
2614 appendStorageClass(stc, ss); // complaint for the redundancy
2615 else if (StorageClass modStc = stc & STC.TYPECTOR)
2618 AST.stcToBuffer(&buf, modStc);
2619 error(loc, "shared static destructor cannot be `%s`", buf.peekChars());
2621 stc &= ~(STC.static_ | STC.TYPECTOR);
2623 auto f = new AST.SharedStaticDtorDeclaration(loc, Loc.initial, stc);
2624 AST.Dsymbol s = parseContracts(f);
2627 auto a = new AST.Dsymbols();
2629 s = new AST.UserAttributeDeclaration(udas, a);
2634 /*****************************************
2635 * Parse an invariant definition:
2636 * invariant { statements... }
2637 * invariant() { statements... }
2638 * invariant (expression);
2639 * Current token is 'invariant'.
2641 private AST.Dsymbol parseInvariant(PrefixAttributes!AST* pAttrs)
2643 const loc = token.loc;
2644 StorageClass stc = getStorageClass!AST(pAttrs);
2647 if (token.value == TOK.leftParenthesis) // optional () or invariant (expression);
2650 if (token.value != TOK.rightParenthesis) // invariant (expression);
2652 AST.Expression e = parseAssignExp(), msg = null;
2653 if (token.value == TOK.comma)
2656 if (token.value != TOK.rightParenthesis)
2658 msg = parseAssignExp();
2659 if (token.value == TOK.comma)
2663 check(TOK.rightParenthesis);
2664 check(TOK.semicolon, "invariant");
2665 e = new AST.AssertExp(loc, e, msg);
2666 auto fbody = new AST.ExpStatement(loc, e);
2667 auto f = new AST.InvariantDeclaration(loc, token.loc, stc, null, fbody);
2673 auto fbody = parseStatement(ParseStatementFlags.curly);
2674 auto f = new AST.InvariantDeclaration(loc, token.loc, stc, null, fbody);
2678 /*****************************************
2679 * Parse a unittest definition:
2681 * Current token is 'unittest'.
2683 private AST.Dsymbol parseUnitTest(PrefixAttributes!AST* pAttrs)
2685 const loc = token.loc;
2686 StorageClass stc = getStorageClass!AST(pAttrs);
2690 const(char)* begPtr = token.ptr + 1; // skip '{'
2691 const(char)* endPtr = null;
2692 AST.Statement sbody = parseStatement(ParseStatementFlags.curly, &endPtr);
2694 /** Extract unittest body as a string. Must be done eagerly since memory
2695 will be released by the lexer before doc gen. */
2696 char* docline = null;
2697 if (global.params.ddoc.doOutput && endPtr > begPtr)
2699 /* Remove trailing whitespaces */
2700 for (const(char)* p = endPtr - 1; begPtr <= p && (*p == ' ' || *p == '\r' || *p == '\n' || *p == '\t'); --p)
2705 size_t len = endPtr - begPtr;
2708 docline = cast(char*)mem.xmalloc_noscan(len + 2);
2709 memcpy(docline, begPtr, len);
2710 docline[len] = '\n'; // Terminate all lines by LF
2711 docline[len + 1] = '\0';
2715 auto f = new AST.UnitTestDeclaration(loc, token.loc, stc, docline);
2720 /*****************************************
2721 * Parse a new definition:
2723 * Current token is 'new'.
2725 private AST.Dsymbol parseNew(PrefixAttributes!AST* pAttrs)
2727 const loc = token.loc;
2728 StorageClass stc = getStorageClass!AST(pAttrs);
2729 if (!(stc & STC.disable))
2731 error("`new` allocator must be annotated with `@disabled`");
2735 /* @@@DEPRECATED_2.108@@@
2736 * After deprecation period (2.108), remove all code in the version(all) block.
2740 auto parameterList = parseParameterList(null); // parameterList ignored
2741 if (parameterList.parameters.length > 0 || parameterList.varargs != VarArg.none)
2742 deprecation("`new` allocator with non-empty parameter list is deprecated");
2743 auto f = new AST.NewDeclaration(loc, stc);
2744 if (token.value != TOK.semicolon)
2746 deprecation("`new` allocator with function definition is deprecated");
2747 parseContracts(f); // body ignored
2758 check(TOK.leftParenthesis);
2759 check(TOK.rightParenthesis);
2760 check(TOK.semicolon);
2761 return new AST.NewDeclaration(loc, stc);
2765 /**********************************************
2766 * Parse parameter list.
2768 private AST.ParameterList parseParameterList(AST.TemplateParameters** tpl)
2770 auto parameters = new AST.Parameters();
2771 VarArg varargs = VarArg.none;
2772 StorageClass varargsStc;
2774 // Attributes allowed for ...
2775 enum VarArgsStc = STC.const_ | STC.immutable_ | STC.shared_ | STC.scope_ | STC.return_ | STC.returnScope;
2777 check(TOK.leftParenthesis);
2780 Identifier ai = null;
2782 StorageClass storageClass = 0;
2785 AST.Expressions* udas = null;
2786 for (; 1; nextToken())
2789 switch (token.value)
2791 case TOK.rightParenthesis:
2792 if (storageClass != 0 || udas !is null)
2793 error("basic type expected, not `)`");
2797 varargs = VarArg.variadic;
2798 varargsStc = storageClass;
2799 if (varargsStc & ~VarArgsStc)
2802 AST.stcToBuffer(&buf, varargsStc & ~VarArgsStc);
2803 error("variadic parameter cannot have attributes `%s`", buf.peekChars());
2804 varargsStc &= VarArgsStc;
2810 if (peekNext() == TOK.leftParenthesis)
2815 case TOK.immutable_:
2816 if (peekNext() == TOK.leftParenthesis)
2818 stc = STC.immutable_;
2822 if (peekNext() == TOK.leftParenthesis)
2828 if (peekNext() == TOK.leftParenthesis)
2834 AST.Expressions* exps = null;
2835 StorageClass stc2 = parseAttribute(exps);
2836 if (stc2 & atAttrGroup)
2838 error("`@%s` attribute for function parameter is not supported", token.toChars());
2842 udas = AST.UserAttributeDeclaration.concat(udas, exps);
2844 if (token.value == TOK.dotDotDot)
2845 error("variadic parameter cannot have user-defined attributes");
2849 // Don't call nextToken again.
2852 if (global.params.vin)
2853 message(scanloc, "Usage of 'in' on parameter");
2883 if (peekNext() == TOK.scope_)
2884 stc |= STC.returnScope;
2887 storageClass = appendStorageClass(storageClass, stc);
2892 stc = storageClass & (STC.IOR | STC.lazy_);
2893 // if stc is not a power of 2
2894 if (stc & (stc - 1) && !(stc == (STC.in_ | STC.ref_)))
2895 error("incompatible parameter storage classes");
2896 //if ((storageClass & STC.scope_) && (storageClass & (STC.ref_ | STC.out_)))
2897 //error("scope cannot be ref or out");
2899 const tv = peekNext();
2900 if (tpl && token.value == TOK.identifier &&
2901 (tv == TOK.comma || tv == TOK.rightParenthesis || tv == TOK.dotDotDot))
2903 Identifier id = Identifier.generateId("__T");
2904 const loc = token.loc;
2905 at = new AST.TypeIdentifier(loc, id);
2907 *tpl = new AST.TemplateParameters();
2908 AST.TemplateParameter tp = new AST.TemplateTypeParameter(loc, id, null, null);
2916 at = parseType(&ai);
2919 if (token.value == TOK.assign) // = defaultArg
2922 ae = parseDefaultInitExp();
2924 auto param = new AST.Parameter(storageClass | STC.parameter, at, ai, ae, null);
2927 auto a = new AST.Dsymbols();
2928 auto udad = new AST.UserAttributeDeclaration(udas, a);
2929 param.userAttribDecl = udad;
2931 if (token.value == TOK.at)
2933 AST.Expressions* exps = null;
2934 StorageClass stc2 = parseAttribute(exps);
2935 if (stc2 & atAttrGroup)
2937 error("`@%s` attribute for function parameter is not supported", token.toChars());
2941 error("user-defined attributes cannot appear as postfixes", token.toChars());
2946 if (token.value == TOK.dotDotDot)
2951 if (storageClass & (STC.out_ | STC.ref_))
2952 error("variadic argument cannot be `out` or `ref`");
2953 varargs = VarArg.typesafe;
2954 parameters.push(param);
2958 parameters.push(param);
2959 if (token.value == TOK.comma)
2973 check(TOK.rightParenthesis);
2974 return AST.ParameterList(parameters, varargs, varargsStc);
2977 /*************************************
2979 private AST.EnumDeclaration parseEnum()
2981 AST.EnumDeclaration e;
2984 auto loc = token.loc;
2986 // printf("Parser::parseEnum()\n");
2989 if (token.value == TOK.identifier)
2996 if (token.value == TOK.colon)
3000 const typeLoc = token.loc;
3001 memtype = parseBasicType();
3002 memtype = parseDeclarator(memtype, alt, null);
3003 checkCstyleTypeSyntax(typeLoc, memtype, alt, null);
3006 e = new AST.EnumDeclaration(loc, id, memtype);
3007 if (token.value == TOK.semicolon && id)
3009 else if (token.value == TOK.leftCurly)
3011 bool isAnonymousEnum = !id;
3014 //printf("enum definition\n");
3015 e.members = new AST.Dsymbols();
3017 const(char)[] comment = token.blockComment;
3018 while (token.value != TOK.rightCurly)
3020 /* Can take the following forms...
3023 * 3. type ident = value
3024 * ... prefixed by valid attributes
3028 AST.Type type = null;
3029 Identifier ident = null;
3031 AST.Expressions* udas;
3033 AST.Expression deprecationMessage;
3034 enum attributeErrorMessage = "`%s` is not a valid attribute for enum members";
3035 while(token.value != TOK.rightCurly
3036 && token.value != TOK.comma
3037 && token.value != TOK.assign)
3042 if (StorageClass _stc = parseAttribute(udas))
3044 if (_stc == STC.disable)
3049 AST.stcToBuffer(&buf, _stc);
3050 error(attributeErrorMessage, buf.peekChars());
3052 prevTOK = token.value;
3056 case TOK.deprecated_:
3057 stc |= STC.deprecated_;
3058 if (!parseDeprecatedAttribute(deprecationMessage))
3060 prevTOK = token.value;
3064 case TOK.identifier:
3065 const tv = peekNext();
3066 if (tv == TOK.assign || tv == TOK.comma || tv == TOK.rightCurly)
3068 ident = token.ident;
3070 prevTOK = token.value;
3079 if (isAnonymousEnum)
3081 type = parseType(&ident, null);
3082 if (type == AST.Type.terror)
3085 prevTOK = token.value;
3090 prevTOK = TOK.identifier;
3095 error(attributeErrorMessage, token.toChars());
3096 prevTOK = token.value;
3101 if (token.value == TOK.comma)
3103 prevTOK = token.value;
3107 if (type && type != AST.Type.terror)
3110 error("no identifier for declarator `%s`", type.toChars());
3111 if (!isAnonymousEnum)
3112 error("type only allowed if anonymous enum and no enum type");
3114 AST.Expression value;
3115 if (token.value == TOK.assign)
3117 if (prevTOK == TOK.identifier)
3120 value = parseAssignExp();
3124 error("assignment must be preceded by an identifier");
3131 if (type && type != AST.Type.terror && isAnonymousEnum)
3132 error("if type, there must be an initializer");
3135 AST.DeprecatedDeclaration dd;
3136 if (deprecationMessage)
3138 dd = new AST.DeprecatedDeclaration(deprecationMessage, null);
3139 stc |= STC.deprecated_;
3142 auto em = new AST.EnumMember(loc, ident, value, type, stc, null, dd);
3147 auto uad = new AST.UserAttributeDeclaration(udas, new AST.Dsymbols());
3148 em.userAttribDecl = uad;
3151 if (token.value == TOK.rightCurly)
3156 addComment(em, comment);
3160 addComment(em, comment);
3161 comment = token.blockComment;
3163 if (token.value == TOK.endOfFile)
3165 error("premature end of file");
3172 error("enum declaration is invalid");
3174 //printf("-parseEnum() %s\n", e.toChars());
3178 /********************************
3179 * Parse struct, union, interface, class.
3181 private AST.Dsymbol parseAggregate()
3183 AST.TemplateParameters* tpl = null;
3184 AST.Expression constraint;
3185 const loc = token.loc;
3186 TOK tok = token.value;
3188 //printf("Parser::parseAggregate()\n");
3191 if (token.value != TOK.identifier)
3200 if (token.value == TOK.leftParenthesis)
3202 // struct/class template declaration.
3203 tpl = parseTemplateParameterList();
3204 constraint = parseConstraint();
3208 // Collect base class(es)
3209 AST.BaseClasses* baseclasses = null;
3210 if (token.value == TOK.colon)
3212 if (tok != TOK.interface_ && tok != TOK.class_)
3213 error("base classes are not allowed for `%s`, did you mean `;`?", Token.toChars(tok));
3215 baseclasses = parseBaseClasses();
3218 if (token.value == TOK.if_)
3221 error("template constraints appear both before and after BaseClassList, put them before");
3222 constraint = parseConstraint();
3227 error("template constraints not allowed for anonymous `%s`", Token.toChars(tok));
3229 error("template constraints only allowed for templates");
3232 AST.Dsymbols* members = null;
3233 if (token.value == TOK.leftCurly)
3235 //printf("aggregate definition\n");
3236 const lookingForElseSave = lookingForElse;
3237 lookingForElse = Loc();
3239 members = parseDeclDefs(0);
3240 lookingForElse = lookingForElseSave;
3241 if (token.value != TOK.rightCurly)
3244 error(token.loc, "`}` expected following members in `%s` declaration at %s",
3245 Token.toChars(tok), loc.toChars());
3249 else if (token.value == TOK.semicolon && id)
3251 if (baseclasses || constraint)
3252 error("members expected");
3257 error(token.loc, "{ } expected following `%s` declaration", Token.toChars(tok));
3260 AST.AggregateDeclaration a;
3263 case TOK.interface_:
3265 error(loc, "anonymous interfaces not allowed");
3266 a = new AST.InterfaceDeclaration(loc, id, baseclasses);
3267 a.members = members;
3272 error(loc, "anonymous classes not allowed");
3273 bool inObject = md && !md.packages && md.id == Id.object;
3274 a = new AST.ClassDeclaration(loc, id, baseclasses, members, inObject);
3280 bool inObject = md && !md.packages && md.id == Id.object;
3281 a = new AST.StructDeclaration(loc, id, inObject);
3282 a.members = members;
3286 /* Anonymous structs/unions are more like attributes.
3289 return new AST.AnonDeclaration(loc, false, members);
3296 a = new AST.UnionDeclaration(loc, id);
3297 a.members = members;
3301 /* Anonymous structs/unions are more like attributes.
3304 return new AST.AnonDeclaration(loc, true, members);
3314 // Wrap a template around the aggregate declaration
3315 auto decldefs = new AST.Dsymbols();
3317 auto tempdecl = new AST.TemplateDeclaration(loc, id, tpl, constraint, decldefs);
3323 /*******************************************
3325 private AST.BaseClasses* parseBaseClasses()
3327 auto baseclasses = new AST.BaseClasses();
3329 for (; 1; nextToken())
3331 auto b = new AST.BaseClass(parseBasicType());
3332 baseclasses.push(b);
3333 if (token.value != TOK.comma)
3339 AST.Dsymbols* parseImport()
3341 auto decldefs = new AST.Dsymbols();
3342 Identifier aliasid = null;
3344 int isstatic = token.value == TOK.static_;
3348 //printf("Parser::parseImport()\n");
3353 if (token.value != TOK.identifier)
3355 error("identifier expected following `import`");
3359 const loc = token.loc;
3360 Identifier id = token.ident;
3363 if (!aliasid && token.value == TOK.assign)
3368 while (token.value == TOK.dot)
3372 if (token.value != TOK.identifier)
3374 error("identifier expected following `package`");
3381 auto s = new AST.Import(loc, a, id, aliasid, isstatic);
3385 * : alias=name, alias=name;
3388 if (token.value == TOK.colon)
3393 if (token.value != TOK.identifier)
3395 error("identifier expected following `:`");
3398 Identifier _alias = token.ident;
3401 if (token.value == TOK.assign)
3404 if (token.value != TOK.identifier)
3406 error("identifier expected following `%s=`", _alias.toChars());
3417 s.addAlias(name, _alias);
3419 while (token.value == TOK.comma);
3420 break; // no comma-separated imports of this form
3424 while (token.value == TOK.comma);
3426 if (token.value == TOK.semicolon)
3430 error("`;` expected");
3437 AST.Type parseType(Identifier* pident = null, AST.TemplateParameters** ptpl = null)
3439 /* Take care of the storage class prefixes that
3440 * serve as type attributes:
3448 * shared inout const type
3450 StorageClass stc = 0;
3453 switch (token.value)
3456 if (peekNext() == TOK.leftParenthesis)
3457 break; // const as type constructor
3458 stc |= STC.const_; // const as storage class
3462 case TOK.immutable_:
3463 if (peekNext() == TOK.leftParenthesis)
3465 stc |= STC.immutable_;
3470 if (peekNext() == TOK.leftParenthesis)
3477 if (peekNext() == TOK.leftParenthesis)
3489 const typeLoc = token.loc;
3492 t = parseBasicType();
3495 t = parseDeclarator(t, alt, pident, ptpl);
3496 checkCstyleTypeSyntax(typeLoc, t, alt, pident ? *pident : null);
3502 private AST.Type parseBasicType(bool dontLookDotIdents = false)
3507 //printf("parseBasicType()\n");
3508 switch (token.value)
3523 t = AST.Type.tint16;
3527 t = AST.Type.tuns16;
3531 t = AST.Type.tint32;
3535 t = AST.Type.tuns32;
3539 t = AST.Type.tint64;
3541 if (token.value == TOK.int64) // if `long long`
3543 error("use `long` for a 64 bit integer instead of `long long`");
3546 else if (token.value == TOK.float64) // if `long double`
3548 error("use `real` instead of `long double`");
3549 t = AST.Type.tfloat80;
3555 t = AST.Type.tuns64;
3559 t = AST.Type.tint128;
3563 t = AST.Type.tuns128;
3567 t = AST.Type.tfloat32;
3571 t = AST.Type.tfloat64;
3575 t = AST.Type.tfloat80;
3578 case TOK.imaginary32:
3579 t = AST.Type.timaginary32;
3582 case TOK.imaginary64:
3583 t = AST.Type.timaginary64;
3586 case TOK.imaginary80:
3587 t = AST.Type.timaginary80;
3591 t = AST.Type.tcomplex32;
3595 t = AST.Type.tcomplex64;
3599 t = AST.Type.tcomplex80;
3611 t = AST.Type.twchar;
3615 t = AST.Type.tdchar;
3623 case TOK.identifier:
3627 if (token.value == TOK.not)
3629 // ident!(template_arguments)
3630 auto tempinst = new AST.TemplateInstance(loc, id, parseTemplateArguments());
3631 t = parseBasicTypeStartingAt(new AST.TypeInstance(loc, tempinst), dontLookDotIdents);
3635 t = parseBasicTypeStartingAt(new AST.TypeIdentifier(loc, id), dontLookDotIdents);
3640 // https://dlang.org/spec/expression.html#mixin_types
3643 if (token.value != TOK.leftParenthesis)
3644 error(token.loc, "found `%s` when expecting `%s` following `mixin`", token.toChars(), Token.toChars(TOK.leftParenthesis));
3645 auto exps = parseArguments();
3646 t = new AST.TypeMixin(loc, exps);
3650 // Leading . as in .foo
3651 t = parseBasicTypeStartingAt(new AST.TypeIdentifier(token.loc, Id.empty), dontLookDotIdents);
3655 // typeof(expression)
3656 t = parseBasicTypeStartingAt(parseTypeof(), dontLookDotIdents);
3664 if (AST.TraitsExp te = cast(AST.TraitsExp) parsePrimaryExp())
3667 t = new AST.TypeTraits(token.loc, te);
3670 t = new AST.TypeError;
3676 check(TOK.leftParenthesis);
3677 t = parseType().addSTC(STC.const_);
3678 check(TOK.rightParenthesis);
3681 case TOK.immutable_:
3684 check(TOK.leftParenthesis);
3685 t = parseType().addSTC(STC.immutable_);
3686 check(TOK.rightParenthesis);
3692 check(TOK.leftParenthesis);
3693 t = parseType().addSTC(STC.shared_);
3694 check(TOK.rightParenthesis);
3700 check(TOK.leftParenthesis);
3701 t = parseType().addSTC(STC.wild);
3702 check(TOK.rightParenthesis);
3706 error("basic type expected, not `%s`", token.toChars());
3707 if (token.value == TOK.else_)
3708 eSink.errorSupplemental(token.loc, "There's no `static else`, use `else` instead.");
3709 t = AST.Type.terror;
3715 private AST.Type parseBasicTypeStartingAt(AST.TypeQualified tid, bool dontLookDotIdents)
3717 AST.Type maybeArray = null;
3718 // See https://issues.dlang.org/show_bug.cgi?id=1215
3719 // A basic type can look like MyType (typical case), but also:
3720 // MyType.T -> A type
3721 // MyType[expr] -> Either a static array of MyType or a type (iif MyType is a Ttuple)
3722 // MyType[expr].T -> A type.
3723 // MyType[expr].T[expr] -> Either a static array of MyType[expr].T or a type
3724 // (iif MyType[expr].T is a Ttuple)
3727 switch (token.value)
3732 if (token.value != TOK.identifier)
3734 error("identifier expected following `.` instead of `%s`", token.toChars());
3739 // This is actually a TypeTuple index, not an {a/s}array.
3740 // We need to have a while loop to unwind all index taking:
3741 // T[e1][e2].U -> T, addIndex(e1), addIndex(e2)
3742 AST.Objects dimStack;
3743 AST.Type t = maybeArray;
3746 if (t.ty == Tsarray)
3748 // The index expression is an Expression.
3749 AST.TypeSArray a = cast(AST.TypeSArray)t;
3750 dimStack.push(a.dim.syntaxCopy());
3751 t = a.next.syntaxCopy();
3753 else if (t.ty == Taarray)
3755 // The index expression is a Type. It will be interpreted as an expression at semantic time.
3756 AST.TypeAArray a = cast(AST.TypeAArray)t;
3757 dimStack.push(a.index.syntaxCopy());
3758 t = a.next.syntaxCopy();
3765 assert(dimStack.length > 0);
3766 // We're good. Replay indices in the reverse order.
3767 tid = cast(AST.TypeQualified)t;
3768 while (dimStack.length)
3770 tid.addIndex(dimStack.pop());
3774 const loc = token.loc;
3775 Identifier id = token.ident;
3777 if (token.value == TOK.not)
3779 auto tempinst = new AST.TemplateInstance(loc, id, parseTemplateArguments());
3780 tid.addInst(tempinst);
3786 case TOK.leftBracket:
3788 if (dontLookDotIdents) // workaround for https://issues.dlang.org/show_bug.cgi?id=14911
3792 AST.Type t = maybeArray ? maybeArray : cast(AST.Type)tid;
3793 if (token.value == TOK.rightBracket)
3795 // It's a dynamic array, and we're done:
3796 // T[].U does not make sense.
3797 t = new AST.TypeDArray(t);
3801 else if (isDeclaration(&token, NeedDeclaratorId.no, TOK.rightBracket, null))
3803 // This can be one of two things:
3804 // 1 - an associative array declaration, T[type]
3805 // 2 - an associative array declaration, T[expr]
3806 // These can only be disambiguated later.
3807 AST.Type index = parseType(); // [ type ]
3808 maybeArray = new AST.TypeAArray(t, index);
3809 check(TOK.rightBracket);
3813 // This can be one of three things:
3814 // 1 - an static array declaration, T[expr]
3815 // 2 - a slice, T[expr .. expr]
3816 // 3 - a template parameter pack index expression, T[expr].U
3817 // 1 and 3 can only be disambiguated later.
3818 //printf("it's type[expression]\n");
3820 AST.Expression e = parseAssignExp(); // [ expression ]
3821 if (token.value == TOK.slice)
3823 // It's a slice, and we're done.
3825 AST.Expression e2 = parseAssignExp(); // [ exp .. exp ]
3826 t = new AST.TypeSlice(t, e, e2);
3828 check(TOK.rightBracket);
3833 maybeArray = new AST.TypeSArray(t, e);
3835 check(TOK.rightBracket);
3846 return maybeArray ? maybeArray : cast(AST.Type)tid;
3849 /******************************************
3850 * Parse suffixes to type t.
3853 * [AssignExpression]
3854 * [AssignExpression .. AssignExpression]
3856 * delegate Parameters MemberFunctionAttributes(opt)
3857 * function Parameters FunctionAttributes(opt)
3859 * t = the already parsed type
3861 * t with the suffixes added
3863 * https://dlang.org/spec/declaration.html#TypeSuffixes
3865 private AST.Type parseTypeSuffixes(AST.Type t)
3867 //printf("parseTypeSuffixes()\n");
3870 switch (token.value)
3873 t = new AST.TypePointer(t);
3877 case TOK.leftBracket:
3878 // Handle []. Make sure things like
3880 // is (array[1] of array[3] of int)
3882 if (token.value == TOK.rightBracket)
3884 t = new AST.TypeDArray(t); // []
3887 else if (isDeclaration(&token, NeedDeclaratorId.no, TOK.rightBracket, null))
3889 // It's an associative array declaration
3890 //printf("it's an associative array\n");
3891 AST.Type index = parseType(); // [ type ]
3892 t = new AST.TypeAArray(t, index);
3893 check(TOK.rightBracket);
3897 //printf("it's type[expression]\n");
3899 AST.Expression e = parseAssignExp(); // [ expression ]
3903 check(TOK.rightBracket);
3906 if (token.value == TOK.slice)
3909 AST.Expression e2 = parseAssignExp(); // [ exp .. exp ]
3910 t = new AST.TypeSlice(t, e, e2);
3914 t = new AST.TypeSArray(t, e);
3917 check(TOK.rightBracket);
3924 // Handle delegate declaration:
3925 // t delegate(parameter list) nothrow pure
3926 // t function(parameter list) nothrow pure
3927 const save = token.value;
3930 auto parameterList = parseParameterList(null);
3932 StorageClass stc = parsePostfix(STC.undefined_, null);
3933 auto tf = new AST.TypeFunction(parameterList, t, linkage, stc);
3934 if (stc & (STC.const_ | STC.immutable_ | STC.shared_ | STC.wild | STC.return_))
3936 if (save == TOK.function_)
3937 error("`const`/`immutable`/`shared`/`inout`/`return` attributes are only valid for non-static member functions");
3939 tf = cast(AST.TypeFunction)tf.addSTC(stc);
3941 t = save == TOK.delegate_ ? new AST.TypeDelegate(tf) : new AST.TypePointer(tf); // pointer to function
3952 /**********************
3955 * t = base type to start with
3956 * palt = OR in 1 for C-style function pointer declaration syntax,
3957 * 2 for C-style array declaration syntax, otherwise don't modify
3958 * pident = set to Identifier if there is one, null if not
3959 * tpl = if !null, then set to TemplateParameterList
3960 * storageClass = any storage classes seen so far
3961 * pdisable = set to true if @disable seen
3962 * pudas = any user defined attributes seen so far. Merged with any more found
3965 * Reference: https://dlang.org/spec/declaration.html#Declarator
3967 private AST.Type parseDeclarator(AST.Type t, ref int palt, Identifier* pident,
3968 AST.TemplateParameters** tpl = null, StorageClass storageClass = 0,
3969 bool* pdisable = null, AST.Expressions** pudas = null)
3971 //printf("parseDeclarator(tpl = %p)\n", tpl);
3972 t = parseTypeSuffixes(t);
3974 switch (token.value)
3976 case TOK.identifier:
3978 *pident = token.ident;
3980 error("unexpected identifier `%s` in declarator", token.ident.toChars());
3985 case TOK.leftParenthesis:
3988 // like: T ((*fp))();
3989 if (peekNext() == TOK.mul || peekNext() == TOK.leftParenthesis)
3991 /* Parse things with parentheses around the identifier, like:
3993 * although the D style would be:
3998 ts = parseDeclarator(t, palt, pident);
3999 check(TOK.rightParenthesis);
4004 Token* peekt = &token;
4005 /* Completely disallow C-style things like:
4007 * Improve error messages for the common bug of a missing return type
4008 * by looking to see if (a) looks like a parameter list.
4010 if (isParameters(&peekt))
4012 error("function declaration without return type. (Note that constructors are always named `this`)");
4015 error("unexpected `(` in declarator");
4023 // parse DeclaratorSuffixes
4026 switch (token.value)
4028 static if (CARRAYDECL)
4030 /* Support C style array syntax:
4032 * as opposed to D-style:
4035 case TOK.leftBracket:
4037 // This is the old C-style post [] syntax.
4040 if (token.value == TOK.rightBracket)
4042 // It's a dynamic array
4043 ta = new AST.TypeDArray(t); // []
4047 else if (isDeclaration(&token, NeedDeclaratorId.no, TOK.rightBracket, null))
4049 // It's an associative array
4050 //printf("it's an associative array\n");
4051 AST.Type index = parseType(); // [ type ]
4052 check(TOK.rightBracket);
4053 ta = new AST.TypeAArray(t, index);
4058 //printf("It's a static array\n");
4059 AST.Expression e = parseAssignExp(); // [ expression ]
4060 ta = new AST.TypeSArray(t, e);
4061 check(TOK.rightBracket);
4068 * ts -> ... -> ta -> t
4071 for (pt = &ts; *pt != t; pt = &(cast(AST.TypeNext)*pt).next)
4078 case TOK.leftParenthesis:
4082 Token* tk = peekPastParen(&token);
4083 if (tk.value == TOK.leftParenthesis)
4085 /* Look ahead to see if this is (...)(...),
4086 * i.e. a function template declaration
4088 //printf("function template declaration\n");
4090 // Gather template parameter list
4091 *tpl = parseTemplateParameterList();
4093 else if (tk.value == TOK.assign)
4096 * i.e. a variable template declaration
4098 //printf("variable template declaration\n");
4099 *tpl = parseTemplateParameterList();
4104 auto parameterList = parseParameterList(null);
4106 /* Parse const/immutable/shared/inout/nothrow/pure/return postfix
4108 // merge prefix storage classes
4109 StorageClass stc = parsePostfix(storageClass, pudas);
4111 AST.Type tf = new AST.TypeFunction(parameterList, t, linkage, stc);
4112 tf = tf.addSTC(stc);
4114 *pdisable = stc & STC.disable ? true : false;
4119 * ts -> ... -> tf -> t
4122 for (pt = &ts; *pt != t; pt = &(cast(AST.TypeNext)*pt).next)
4136 private void parseStorageClasses(ref StorageClass storage_class, ref LINK link,
4137 ref bool setAlignment, ref AST.Expression ealign, ref AST.Expressions* udas,
4141 bool sawLinkage = false; // seen a linkage declaration
4143 linkloc = Loc.initial;
4147 switch (token.value)
4150 if (peekNext() == TOK.leftParenthesis)
4151 break; // const as type constructor
4152 stc = STC.const_; // const as storage class
4155 case TOK.immutable_:
4156 if (peekNext() == TOK.leftParenthesis)
4158 stc = STC.immutable_;
4162 if (peekNext() == TOK.leftParenthesis)
4168 if (peekNext() == TOK.leftParenthesis)
4190 stc = STC.override_;
4194 stc = STC.abstract_;
4197 case TOK.synchronized_:
4198 stc = STC.synchronized_;
4201 case TOK.deprecated_:
4202 stc = STC.deprecated_;
4223 const tv = peekNext();
4224 if (tv == TOK.leftCurly || tv == TOK.colon)
4226 if (tv == TOK.identifier)
4228 const nextv = peekNext2();
4229 if (nextv == TOK.leftCurly || nextv == TOK.colon || nextv == TOK.semicolon)
4238 stc = parseAttribute(udas);
4244 storage_class = appendStorageClass(storage_class, stc);
4250 if (peekNext() != TOK.leftParenthesis)
4257 error("redundant linkage declaration");
4259 linkloc = token.loc;
4260 auto res = parseLinkage();
4262 if (res.idents || res.identExps)
4264 error("C++ name spaces not allowed here");
4266 if (res.cppmangle != CPPMANGLE.def)
4268 error("C++ mangle declaration not allowed here");
4275 setAlignment = true;
4276 if (token.value == TOK.leftParenthesis)
4279 ealign = parseExpression();
4280 check(TOK.rightParenthesis);
4291 /**********************************
4292 * Parse Declarations.
4294 * 1. declarations at global/class level
4295 * 2. declarations at statement level
4297 * array of Declarations.
4299 private AST.Dsymbols* parseDeclarations(bool autodecl, PrefixAttributes!AST* pAttrs, const(char)* comment)
4301 StorageClass storage_class = STC.undefined_;
4302 LINK link = linkage;
4303 Loc linkloc = this.linkLoc;
4304 bool setAlignment = false;
4305 AST.Expression ealign;
4306 AST.Expressions* udas = null;
4308 //printf("parseDeclarations() %s\n", token.toChars());
4310 comment = token.blockComment.ptr;
4312 /* Look for AliasReassignment
4314 if (token.value == TOK.identifier && peekNext() == TOK.assign)
4315 return parseAliasReassignment(comment);
4317 /* Declarations that start with `alias`
4319 bool isAliasDeclaration = false;
4320 auto aliasLoc = token.loc;
4321 if (token.value == TOK.alias_)
4323 if (auto a = parseAliasDeclarations(comment))
4325 /* Handle these later:
4326 * alias StorageClasses type ident;
4328 isAliasDeclaration = true;
4335 parseStorageClasses(storage_class, link, setAlignment, ealign, udas, linkloc);
4337 if (token.value == TOK.enum_)
4339 AST.Dsymbol d = parseEnum();
4340 auto a = new AST.Dsymbols();
4345 d = new AST.UserAttributeDeclaration(udas, a);
4346 a = new AST.Dsymbols();
4350 addComment(d, comment);
4353 if (token.value == TOK.struct_ ||
4354 token.value == TOK.union_ ||
4355 token.value == TOK.class_ ||
4356 token.value == TOK.interface_)
4358 AST.Dsymbol s = parseAggregate();
4359 auto a = new AST.Dsymbols();
4364 s = new AST.StorageClassDeclaration(storage_class, a);
4365 a = new AST.Dsymbols();
4370 s = new AST.AlignDeclaration(s.loc, ealign, a);
4371 a = new AST.Dsymbols();
4374 if (link != linkage)
4376 s = new AST.LinkDeclaration(linkloc, link, a);
4377 a = new AST.Dsymbols();
4382 s = new AST.UserAttributeDeclaration(udas, a);
4383 a = new AST.Dsymbols();
4387 addComment(s, comment);
4391 /* Look for auto initializers:
4392 * storage_class identifier = initializer;
4393 * storage_class identifier(...) = initializer;
4395 if ((storage_class || udas) && token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign))
4397 AST.Dsymbols* a = parseAutoDeclarations(storage_class, comment);
4400 AST.Dsymbol s = new AST.UserAttributeDeclaration(udas, a);
4401 a = new AST.Dsymbols();
4407 /* Look for return type inference for template functions.
4411 if ((storage_class || udas) && token.value == TOK.identifier && skipParens(peek(&token), &tk) &&
4412 skipAttributes(tk, &tk) &&
4413 (tk.value == TOK.leftParenthesis || tk.value == TOK.leftCurly || tk.value == TOK.in_ || tk.value == TOK.out_ || tk.value == TOK.goesTo ||
4414 tk.value == TOK.do_ || tk.value == TOK.identifier && tk.ident == Id._body))
4416 // @@@DEPRECATED_2.117@@@
4417 // https://github.com/dlang/DIPs/blob/1f5959abe482b1f9094f6484a7d0a3ade77fc2fc/DIPs/accepted/DIP1003.md
4418 // Deprecated in 2.097 - Can be removed from 2.117
4419 // The deprecation period is longer than usual as `body`
4420 // was quite widely used.
4421 if (tk.value == TOK.identifier && tk.ident == Id._body)
4422 deprecation("usage of the `body` keyword is deprecated. Use `do` instead.");
4428 ts = parseBasicType();
4429 ts = parseTypeSuffixes(ts);
4436 storage_class |= pAttrs.storageClass;
4437 //pAttrs.storageClass = STC.undefined_;
4440 AST.Type tfirst = null;
4441 auto a = new AST.Dsymbols();
4445 AST.TemplateParameters* tpl = null;
4449 const loc = token.loc;
4451 auto t = parseDeclarator(ts, alt, &ident, &tpl, storage_class, &disable, &udas);
4455 else if (t != tfirst)
4456 error(token.loc, "multiple declarations must have the same type, not `%s` and `%s`", tfirst.toChars(), t.toChars());
4458 if (token.value == TOK.colon && !ident && t.ty != Tfunction)
4460 // Unnamed bit field
4461 ident = Identifier.generateAnonymousId("BitField");
4464 bool isThis = (t.ty == Tident && (cast(AST.TypeIdentifier)t).ident == Id.This && token.value == TOK.assign);
4466 checkCstyleTypeSyntax(loc, t, alt, ident);
4467 else if (!isThis && (t != AST.Type.terror))
4468 noIdentifierForDeclarator(t);
4470 if (isAliasDeclaration)
4473 AST.Initializer _init = null;
4475 /* Aliases can no longer have multiple declarators, storage classes,
4476 * linkages, or auto declarations.
4477 * These never made any sense, anyway.
4478 * The code below needs to be fixed to reject them.
4479 * The grammar has already been fixed to preclude them.
4483 error("user-defined attributes not allowed for `alias` declarations");
4485 if (token.value == TOK.assign)
4488 _init = parseInitializer();
4493 error(token.loc, "cannot use syntax `alias this = %s`, use `alias %s this` instead", _init.toChars(), _init.toChars());
4495 error("alias cannot have initializer");
4497 v = new AST.AliasDeclaration(aliasLoc, ident, t);
4499 v.storage_class = storage_class;
4502 /* AliasDeclaration distinguish @safe, @system, @trusted attributes
4503 * on prefix and postfix.
4504 * @safe alias void function() FP1;
4505 * alias @safe void function() FP2; // FP2 is not @safe
4506 * alias void function() @safe FP3;
4508 pAttrs.storageClass &= STC.safeGroup;
4512 if (link != linkage)
4514 auto ax = new AST.Dsymbols();
4516 s = new AST.LinkDeclaration(linkloc, link, ax);
4519 switch (token.value)
4523 addComment(s, comment);
4528 addComment(s, comment);
4532 error("semicolon expected to close `alias` declaration, not `%s`", token.toChars());
4536 else if (t.ty == Tfunction)
4538 AST.Expression constraint = null;
4539 //printf("%s funcdecl t = %s, storage_class = x%lx\n", loc.toChars(), t.toChars(), storage_class);
4540 auto f = new AST.FuncDeclaration(loc, Loc.initial, ident, storage_class | (disable ? STC.disable : 0), t);
4542 pAttrs.storageClass = STC.undefined_;
4544 constraint = parseConstraint();
4545 AST.Dsymbol s = parseContracts(f, !!tpl);
4546 auto tplIdent = s.ident;
4548 if (link != linkage)
4550 auto ax = new AST.Dsymbols();
4552 s = new AST.LinkDeclaration(linkloc, link, ax);
4556 auto ax = new AST.Dsymbols();
4558 s = new AST.UserAttributeDeclaration(udas, ax);
4561 /* A template parameter list means it's a function template
4565 // Wrap a template around the function declaration
4566 auto decldefs = new AST.Dsymbols();
4568 auto tempdecl = new AST.TemplateDeclaration(loc, tplIdent, tpl, constraint, decldefs);
4571 StorageClass stc2 = STC.undefined_;
4572 if (storage_class & STC.static_)
4574 assert(f.storage_class & STC.static_);
4575 f.storage_class &= ~STC.static_;
4576 stc2 |= STC.static_;
4578 if (storage_class & STC.deprecated_)
4580 assert(f.storage_class & STC.deprecated_);
4581 f.storage_class &= ~STC.deprecated_;
4582 stc2 |= STC.deprecated_;
4584 if (stc2 != STC.undefined_)
4586 auto ax = new AST.Dsymbols();
4588 s = new AST.StorageClassDeclaration(stc2, ax);
4592 addComment(s, comment);
4596 AST.Expression width;
4597 if (token.value == TOK.colon)
4600 width = parseCondExp();
4603 AST.Initializer _init = null;
4604 if (token.value == TOK.assign)
4607 _init = parseInitializer();
4613 if (!global.params.bitfields)
4614 error("use -preview=bitfields for bitfield support");
4616 error("initializer not allowed for bit-field declaration");
4618 error("storage class not allowed for bit-field declaration");
4619 s = new AST.BitFieldDeclaration(width.loc, t, ident, width);
4623 auto v = new AST.VarDeclaration(loc, t, ident, _init);
4624 v.storage_class = storage_class;
4626 pAttrs.storageClass = STC.undefined_;
4632 auto a2 = new AST.Dsymbols();
4634 auto tempdecl = new AST.TemplateDeclaration(loc, ident, tpl, null, a2, 0);
4639 auto ax = new AST.Dsymbols();
4641 s = new AST.AlignDeclaration(s.loc, ealign, ax);
4643 if (link != linkage)
4645 auto ax = new AST.Dsymbols();
4647 s = new AST.LinkDeclaration(linkloc, link, ax);
4651 auto ax = new AST.Dsymbols();
4653 s = new AST.UserAttributeDeclaration(udas, ax);
4656 switch (token.value)
4660 addComment(s, comment);
4665 addComment(s, comment);
4669 if (loc.linnum != token.loc.linnum)
4671 error(token.loc, "semicolon needed to end declaration of `%s`, instead of `%s`", s.toChars(), token.toChars());
4672 eSink.errorSupplemental(loc, "`%s` declared here", s.toChars());
4676 error(token.loc, "semicolon needed to end declaration of `%s` instead of `%s`", s.toChars(), token.toChars());
4686 /// Report an error that a declaration of type `t` is missing an identifier
4687 /// The parser is expected to sit on the next token after the type.
4688 private void noIdentifierForDeclarator(AST.Type t)
4690 error("no identifier for declarator `%s`", t.toChars());
4691 // A common mistake is to use a reserved keyword as an identifier, e.g. `in` or `out`
4692 if (token.isKeyword)
4694 eSink.errorSupplemental(token.loc, "`%s` is a keyword, perhaps append `_` to make it an identifier", token.toChars());
4699 /********************************
4700 * Parse AliasReassignment:
4701 * identifier = type;
4702 * Parser is sitting on the identifier.
4703 * https://dlang.org/spec/declaration.html#alias-reassignment
4705 * comment = if not null, comment to attach to symbol
4709 private AST.Dsymbols* parseAliasReassignment(const(char)* comment)
4711 const loc = token.loc;
4712 auto ident = token.ident;
4714 nextToken(); // advance past =
4715 auto t = parseType();
4716 AST.Dsymbol s = new AST.AliasAssign(loc, ident, t, null);
4717 check(TOK.semicolon, "alias reassignment");
4718 addComment(s, comment);
4719 auto a = new AST.Dsymbols();
4724 /********************************
4725 * Parse declarations that start with `alias`
4726 * Parser is sitting on the `alias`.
4727 * https://dlang.org/spec/declaration.html#alias
4729 * comment = if not null, comment to attach to symbol
4733 private AST.Dsymbols* parseAliasDeclarations(const(char)* comment)
4735 const loc = token.loc;
4737 Loc linkloc = this.linkLoc;
4738 AST.Expressions* udas;
4739 LINK link = linkage;
4740 StorageClass storage_class = STC.undefined_;
4741 AST.Expression ealign;
4742 bool setAlignment = false;
4745 * alias Identifier this;
4746 * https://dlang.org/spec/class.html#alias-this
4748 if (token.value == TOK.identifier && peekNext() == TOK.this_)
4750 auto s = new AST.AliasThis(loc, token.ident);
4753 check(TOK.semicolon, "`alias Identifier this`");
4754 auto a = new AST.Dsymbols();
4756 addComment(s, comment);
4762 * alias this = identifier;
4764 if (token.value == TOK.this_ && peekNext() == TOK.assign && peekNext2() == TOK.identifier)
4768 auto s = new AliasThis(loc, token.ident);
4770 check(TOK.semicolon, "`alias this = Identifier`");
4771 auto a = new Dsymbols();
4773 addComment(s, comment);
4778 * alias identifier = type;
4779 * alias identifier(...) = type;
4780 * https://dlang.org/spec/declaration.html#alias
4782 if (token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign))
4784 auto a = new AST.Dsymbols();
4787 auto ident = token.ident;
4789 AST.TemplateParameters* tpl = null;
4790 if (token.value == TOK.leftParenthesis)
4791 tpl = parseTemplateParameterList();
4794 bool hasParsedAttributes;
4795 void parseAttributes()
4797 if (hasParsedAttributes) // only parse once
4799 hasParsedAttributes = true;
4801 storage_class = STC.undefined_;
4803 linkloc = this.linkLoc;
4804 setAlignment = false;
4806 parseStorageClasses(storage_class, link, setAlignment, ealign, udas, linkloc);
4809 if (token.value == TOK.at)
4815 // try to parse function type:
4816 // TypeCtors? BasicType ( Parameters ) MemberFunctionAttributes
4817 bool attributesAppended;
4818 const StorageClass funcStc = parseTypeCtor();
4819 Token* tlu = &token;
4821 if (token.value != TOK.function_ &&
4822 token.value != TOK.delegate_ &&
4823 isBasicType(&tlu) && tlu &&
4824 tlu.value == TOK.leftParenthesis)
4826 AST.Type tret = parseBasicType();
4827 auto parameterList = parseParameterList(null);
4831 error("user-defined attributes not allowed for `alias` declarations");
4833 attributesAppended = true;
4834 storage_class = appendStorageClass(storage_class, funcStc);
4835 AST.Type tf = new AST.TypeFunction(parameterList, tret, link, storage_class);
4836 v = new AST.AliasDeclaration(loc, ident, tf);
4838 else if (token.value == TOK.function_ ||
4839 token.value == TOK.delegate_ ||
4840 token.value == TOK.leftParenthesis &&
4841 skipAttributes(peekPastParen(&token), &tk) &&
4842 (tk.value == TOK.goesTo || tk.value == TOK.leftCurly) ||
4843 token.value == TOK.leftCurly ||
4844 token.value == TOK.identifier && peekNext() == TOK.goesTo ||
4845 token.value == TOK.ref_ && peekNext() == TOK.leftParenthesis &&
4846 skipAttributes(peekPastParen(peek(&token)), &tk) &&
4847 (tk.value == TOK.goesTo || tk.value == TOK.leftCurly) ||
4848 token.value == TOK.auto_ && peekNext() == TOK.ref_ &&
4849 peekNext2() == TOK.leftParenthesis &&
4850 skipAttributes(peekPastParen(peek(peek(&token))), &tk) &&
4851 (tk.value == TOK.goesTo || tk.value == TOK.leftCurly)
4854 // function (parameters) { statements... }
4855 // delegate (parameters) { statements... }
4856 // (parameters) { statements... }
4857 // (parameters) => expression
4858 // { statements... }
4859 // identifier => expression
4860 // ref (parameters) { statements... }
4861 // ref (parameters) => expression
4862 // auto ref (parameters) { statements... }
4863 // auto ref (parameters) => expression
4865 s = parseFunctionLiteral();
4869 if (storage_class != 0)
4870 error("cannot put a storage-class in an `alias` declaration.");
4871 // parseAttributes shouldn't have set these variables
4872 assert(link == linkage && !setAlignment && ealign is null);
4873 auto tpl_ = cast(AST.TemplateDeclaration) s;
4874 if (tpl_ is null || tpl_.members.length != 1)
4876 error("user-defined attributes are not allowed on `alias` declarations");
4880 auto fd = cast(AST.FuncLiteralDeclaration) (*tpl_.members)[0];
4881 auto tf = cast(AST.TypeFunction) fd.type;
4882 assert(tf.parameterList.parameters.length > 0);
4883 auto as = new AST.Dsymbols();
4884 (*tf.parameterList.parameters)[0].userAttribDecl = new AST.UserAttributeDeclaration(udas, as);
4888 v = new AST.AliasDeclaration(loc, ident, s);
4895 error("user-defined attributes not allowed for `alias` declarations");
4897 auto t = parseType();
4899 // Disallow meaningless storage classes on type aliases
4902 // Don't raise errors for STC that are part of a function/delegate type, e.g.
4903 // `alias F = ref pure nothrow @nogc @safe int function();`
4904 auto tp = t.isTypePointer;
4905 const isFuncType = (tp && tp.next.isTypeFunction) || t.isTypeDelegate;
4906 const remStc = isFuncType ? (storage_class & ~STC.FUNCATTR) : storage_class;
4911 AST.stcToBuffer(&buf, remStc);
4912 // @@@DEPRECATED_2.103@@@
4913 // Deprecated in 2020-07, can be made an error in 2.103
4914 eSink.deprecation(token.loc, "storage class `%s` has no effect in type aliases", buf.peekChars());
4918 v = new AST.AliasDeclaration(loc, ident, t);
4920 if (!attributesAppended)
4921 storage_class = appendStorageClass(storage_class, funcStc);
4922 v.storage_class = storage_class;
4927 auto a2 = new AST.Dsymbols();
4929 auto tempdecl = new AST.TemplateDeclaration(loc, ident, tpl, null, a2);
4932 if (link != linkage)
4934 auto a2 = new AST.Dsymbols();
4936 s = new AST.LinkDeclaration(linkloc, link, a2);
4940 switch (token.value)
4944 addComment(s, comment);
4949 addComment(s, comment);
4950 if (token.value != TOK.identifier)
4952 error("identifier expected following comma, not `%s`", token.toChars());
4955 if (peekNext() != TOK.assign && peekNext() != TOK.leftParenthesis)
4957 error("`=` expected following identifier");
4964 error("semicolon expected to close `alias` declaration, not `%s`", token.toChars());
4972 // alias StorageClasses type ident;
4976 private AST.Dsymbol parseFunctionLiteral()
4978 const loc = token.loc;
4979 AST.TemplateParameters* tpl = null;
4980 AST.ParameterList parameterList;
4981 AST.Type tret = null;
4982 StorageClass stc = 0;
4983 TOK save = TOK.reserved;
4985 switch (token.value)
4991 if (token.value == TOK.auto_)
4994 if (token.value == TOK.ref_)
4996 // function auto ref (parameters) { statements... }
4997 // delegate auto ref (parameters) { statements... }
4998 stc = STC.auto_ | STC.ref_;
5002 error("`auto` can only be used as part of `auto ref` for function literal return values");
5004 else if (token.value == TOK.ref_)
5006 // function ref (parameters) { statements... }
5007 // delegate ref (parameters) { statements... }
5011 if (token.value != TOK.leftParenthesis && token.value != TOK.leftCurly)
5013 // function type (parameters) { statements... }
5014 // delegate type (parameters) { statements... }
5015 tret = parseBasicType();
5016 tret = parseTypeSuffixes(tret); // function return type
5019 if (token.value == TOK.leftParenthesis)
5021 // function (parameters) { statements... }
5022 // delegate (parameters) { statements... }
5026 // function { statements... }
5027 // delegate { statements... }
5030 goto case TOK.leftParenthesis;
5035 if (token.value == TOK.ref_)
5037 // auto ref (parameters) => expression
5038 // auto ref (parameters) { statements... }
5039 stc = STC.auto_ | STC.ref_;
5043 error("`auto` can only be used as part of `auto ref` for function literal return values");
5044 goto case TOK.leftParenthesis;
5048 // ref (parameters) => expression
5049 // ref (parameters) { statements... }
5052 goto case TOK.leftParenthesis;
5054 case TOK.leftParenthesis:
5056 // (parameters) => expression
5057 // (parameters) { statements... }
5058 parameterList = parseParameterList(&tpl);
5059 stc = parsePostfix(stc, null);
5060 if (StorageClass modStc = stc & STC.TYPECTOR)
5062 if (save == TOK.function_)
5065 AST.stcToBuffer(&buf, modStc);
5066 error("function literal cannot be `%s`", buf.peekChars());
5069 save = TOK.delegate_;
5074 // { statements... }
5077 case TOK.identifier:
5079 // identifier => expression
5080 parameterList.parameters = new AST.Parameters();
5081 Identifier id = Identifier.generateId("__T");
5082 AST.Type t = new AST.TypeIdentifier(loc, id);
5083 parameterList.parameters.push(new AST.Parameter(STC.parameter, t, token.ident, null, null));
5085 tpl = new AST.TemplateParameters();
5086 AST.TemplateParameter tp = new AST.TemplateTypeParameter(loc, id, null, null);
5096 auto tf = new AST.TypeFunction(parameterList, tret, linkage, stc);
5097 tf = cast(AST.TypeFunction)tf.addSTC(stc);
5098 auto fd = new AST.FuncLiteralDeclaration(loc, Loc.initial, tf, save, null, null, stc & STC.auto_);
5100 if (token.value == TOK.goesTo)
5103 if (token.value == TOK.leftCurly)
5105 deprecation("using `(args) => { ... }` to create a delegate that returns a delegate is error-prone.");
5106 deprecationSupplemental("Use `(args) { ... }` for a multi-statement function literal or use `(args) => () { }` if you intended for the lambda to return a delegate.");
5108 const returnloc = token.loc;
5109 AST.Expression ae = parseAssignExp();
5110 fd.fbody = new AST.ReturnStatement(returnloc, ae);
5111 fd.endloc = token.loc;
5120 // Wrap a template around function fd
5121 auto decldefs = new AST.Dsymbols();
5123 return new AST.TemplateDeclaration(fd.loc, fd.ident, tpl, null, decldefs, false, true);
5128 /*****************************************
5129 * Parse contracts following function declaration.
5131 private AST.FuncDeclaration parseContracts(AST.FuncDeclaration f, bool isTemplateFunction = false)
5133 LINK linksave = linkage;
5135 bool literal = f.isFuncLiteralDeclaration() !is null;
5137 // The following is irrelevant, as it is overridden by sc.linkage in
5138 // TypeFunction::semantic
5139 linkage = LINK.d; // nested functions have D linkage
5140 bool requireDo = false;
5142 switch (token.value)
5146 error("missing `do { ... }` after `in` or `out`");
5147 if (!global.params.shortenedMethods)
5148 error("=> shortened method not enabled, compile with compiler switch `-preview=shortenedMethods`");
5149 const returnloc = token.loc;
5151 f.fbody = new AST.ReturnStatement(returnloc, parseExpression());
5152 f.endloc = token.loc;
5153 check(TOK.semicolon);
5158 error("missing `do { ... }` after `in` or `out`");
5159 f.fbody = parseStatement(ParseStatementFlags.semi);
5163 case TOK.identifier:
5164 if (token.ident == Id._body)
5166 // @@@DEPRECATED_2.117@@@
5167 // https://github.com/dlang/DIPs/blob/1f5959abe482b1f9094f6484a7d0a3ade77fc2fc/DIPs/accepted/DIP1003.md
5168 // Deprecated in 2.097 - Can be removed from 2.117
5169 // The deprecation period is longer than usual as `body`
5170 // was quite widely used.
5171 deprecation("usage of the `body` keyword is deprecated. Use `do` instead.");
5178 f.fbody = parseStatement(ParseStatementFlags.curly);
5184 // Do we want this for function declarations, so we can do:
5185 // int x, y, foo(), z;
5192 // in { statements... }
5194 auto loc = token.loc;
5198 f.frequires = new AST.Statements;
5200 if (token.value == TOK.leftParenthesis)
5203 AST.Expression e = parseAssignExp(), msg = null;
5204 if (token.value == TOK.comma)
5207 if (token.value != TOK.rightParenthesis)
5209 msg = parseAssignExp();
5210 if (token.value == TOK.comma)
5214 check(TOK.rightParenthesis);
5215 e = new AST.AssertExp(loc, e, msg);
5216 f.frequires.push(new AST.ExpStatement(loc, e));
5221 auto ret = parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_);
5223 f.frequires.push(ret);
5229 // out { statements... }
5230 // out (; expression)
5231 // out (identifier) { statements... }
5232 // out (identifier; expression)
5233 auto loc = token.loc;
5237 f.fensures = new AST.Ensures;
5239 Identifier id = null;
5240 if (token.value != TOK.leftCurly)
5242 check(TOK.leftParenthesis);
5243 if (token.value != TOK.identifier && token.value != TOK.semicolon)
5244 error("`(identifier) { ... }` or `(identifier; expression)` following `out` expected, not `%s`", token.toChars());
5245 if (token.value != TOK.semicolon)
5250 if (token.value == TOK.semicolon)
5253 AST.Expression e = parseAssignExp(), msg = null;
5254 if (token.value == TOK.comma)
5257 if (token.value != TOK.rightParenthesis)
5259 msg = parseAssignExp();
5260 if (token.value == TOK.comma)
5264 check(TOK.rightParenthesis);
5265 e = new AST.AssertExp(loc, e, msg);
5266 f.fensures.push(AST.Ensure(id, new AST.ExpStatement(loc, e)));
5270 check(TOK.rightParenthesis);
5272 f.fensures.push(AST.Ensure(id, parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_)));
5279 // https://issues.dlang.org/show_bug.cgi?id=15799
5280 // Semicolon becomes a part of function declaration
5281 // only when 'do' is not required
5291 const(char)* sbody = requireDo ? "do " : "";
5292 error("missing `%s{ ... }` for function literal", sbody);
5294 else if (!requireDo) // allow contracts even with no body
5296 TOK t = token.value;
5297 if (t == TOK.const_ || t == TOK.immutable_ || t == TOK.inout_ || t == TOK.return_ ||
5298 t == TOK.shared_ || t == TOK.nothrow_ || t == TOK.pure_)
5299 error("'%s' cannot be placed after a template constraint", token.toChars);
5300 else if (t == TOK.at)
5301 error("attributes cannot be placed after a template constraint");
5302 else if (t == TOK.if_)
5304 if (isTemplateFunction)
5305 error("template constraint must follow parameter lists and attributes");
5307 error("cannot use function constraints for non-template functions. Use `static if` instead");
5310 error("semicolon expected following function declaration");
5314 if (literal && !f.fbody)
5316 // Set empty function body for error recovery
5317 f.fbody = new AST.CompoundStatement(Loc.initial, cast(AST.Statement)null);
5325 /*****************************************
5327 private void checkDanglingElse(Loc elseloc)
5329 if (token.value != TOK.else_ && token.value != TOK.catch_ && token.value != TOK.finally_ && lookingForElse.linnum != 0)
5331 eSink.warning(elseloc, "else is dangling, add { } after condition at %s", lookingForElse.toChars());
5335 /* *************************
5336 * Issue errors if C-style syntax
5338 * alt = !=0 for C-style syntax
5340 private void checkCstyleTypeSyntax(Loc loc, AST.Type t, int alt, Identifier ident)
5345 const(char)* sp = !ident ? "" : " ";
5346 const(char)* s = !ident ? "" : ident.toChars();
5347 error(loc, "instead of C-style syntax, use D-style `%s%s%s`", t.toChars(), sp, s);
5350 /*****************************
5351 * Ad-hoc error message for missing or extra parens that close a condition.
5353 * start = "if", "while", etc. Must be 0 terminated.
5354 * param = if the condition is a declaration, this will be non-null
5355 * condition = if param is null, then this is the conditional Expression. If condition is null,
5356 * then an error in the condition was already reported.
5358 private void closeCondition(string start, AST.Parameter param, AST.Expression condition)
5361 if (token.value != TOK.rightParenthesis && condition)
5363 format = "missing closing `)` after `%s (%s`";
5366 check(TOK.rightParenthesis);
5367 if (token.value == TOK.rightParenthesis)
5369 if (condition) // if not an error in condition
5370 format = "extra `)` after `%s (%s)`";
5374 error(token.loc, format.ptr, start.ptr, param ? "declaration".ptr : condition.toChars());
5377 /*****************************************
5378 * Parses `foreach` statements, `static foreach` statements and
5379 * `static foreach` declarations.
5381 * Foreach = one of Statement, StaticForeachStatement, StaticForeachDeclaration
5382 * loc = location of foreach
5383 * pLastDecl = non-null for StaticForeachDeclaration
5385 * the Foreach generated
5387 private Foreach parseForeach(alias Foreach)(Loc loc, AST.Dsymbol* pLastDecl)
5389 static if (is(Foreach == AST.StaticForeachStatement) || is(Foreach == AST.StaticForeachDeclaration))
5394 TOK op = token.value;
5397 check(TOK.leftParenthesis);
5399 auto parameters = new AST.Parameters();
5403 Identifier ai = null;
5406 StorageClass storageClass = 0;
5407 StorageClass stc = 0;
5411 storageClass = appendStorageClass(storageClass, stc);
5414 switch (token.value)
5425 error("cannot declare `out` loop variable, use `ref` instead");
5430 error("cannot declare `auto` loop variable, omit `auto` to still get type inference");
5439 storageClass = appendStorageClass(storageClass, STC.alias_);
5444 if (peekNext() != TOK.leftParenthesis)
5451 case TOK.immutable_:
5452 if (peekNext() != TOK.leftParenthesis)
5454 stc = STC.immutable_;
5460 if (peekNext() != TOK.leftParenthesis)
5468 if (peekNext() != TOK.leftParenthesis)
5478 if (token.value == TOK.identifier)
5480 const tv = peekNext();
5481 if (tv == TOK.comma || tv == TOK.semicolon || tv == TOK.rightParenthesis)
5483 lastai = token.ident;
5485 at = null; // infer argument type
5490 at = parseType(&ai);
5492 noIdentifierForDeclarator(at);
5494 auto p = new AST.Parameter(storageClass, at, ai, null, null);
5496 if (token.value == TOK.comma)
5503 if (token.value != TOK.semicolon)
5505 error("missing `; expression` before `)` of `foreach`");
5507 if (lastai && parameters.length >= 2)
5509 eSink.errorSupplemental(loc, "perhaps the `;` goes before `%s`", lastai.toChars());
5515 AST.Expression aggr = parseExpression();
5516 if (token.value == TOK.slice && parameters.length == 1)
5518 AST.Parameter p = (*parameters)[0];
5520 AST.Expression upr = parseExpression();
5521 check(TOK.rightParenthesis);
5523 static if (is(Foreach == AST.Statement) || is(Foreach == AST.StaticForeachStatement))
5525 AST.Statement _body = parseStatement(0, null, &endloc);
5529 AST.Statement _body = null;
5531 auto rangefe = new AST.ForeachRangeStatement(loc, op, p, aggr, upr, _body, endloc);
5532 static if (is(Foreach == AST.Statement))
5536 else static if(is(Foreach == AST.StaticForeachDeclaration))
5538 return new AST.StaticForeachDeclaration(new AST.StaticForeach(loc, null, rangefe), parseBlock(pLastDecl));
5540 else static if (is(Foreach == AST.StaticForeachStatement))
5542 return new AST.StaticForeachStatement(loc, new AST.StaticForeach(loc, null, rangefe));
5547 check(TOK.rightParenthesis);
5549 static if (is(Foreach == AST.Statement) || is(Foreach == AST.StaticForeachStatement))
5551 AST.Statement _body = parseStatement(0, null, &endloc);
5555 AST.Statement _body = null;
5557 auto aggrfe = new AST.ForeachStatement(loc, op, parameters, aggr, _body, endloc);
5558 static if (is(Foreach == AST.Statement))
5562 else static if(is(Foreach == AST.StaticForeachDeclaration))
5564 return new AST.StaticForeachDeclaration(new AST.StaticForeach(loc, aggrfe, null), parseBlock(pLastDecl));
5566 else static if (is(Foreach == AST.StaticForeachStatement))
5568 return new AST.StaticForeachStatement(loc, new AST.StaticForeach(loc, aggrfe, null));
5575 * Parse an assignment condition for if or while statements.
5578 * The variable that is declared inside the condition
5580 AST.Parameter parseAssignCondition()
5582 AST.Parameter param = null;
5583 StorageClass storageClass = 0;
5584 StorageClass stc = 0;
5588 storageClass = appendStorageClass(storageClass, stc);
5591 switch (token.value)
5606 if (peekNext() != TOK.leftParenthesis)
5613 case TOK.immutable_:
5614 if (peekNext() != TOK.leftParenthesis)
5616 stc = STC.immutable_;
5622 if (peekNext() != TOK.leftParenthesis)
5630 if (peekNext() != TOK.leftParenthesis)
5640 auto n = peek(&token);
5641 if (storageClass != 0 && token.value == TOK.identifier && n.value == TOK.assign)
5643 Identifier ai = token.ident;
5644 AST.Type at = null; // infer parameter type
5647 param = new AST.Parameter(storageClass, at, ai, null, null);
5649 else if (isDeclaration(&token, NeedDeclaratorId.must, TOK.assign, null))
5652 AST.Type at = parseType(&ai);
5654 param = new AST.Parameter(storageClass, at, ai, null, null);
5656 else if (storageClass != 0)
5657 error("found `%s` while expecting `=` or identifier", n.toChars());
5662 /*****************************************
5666 * pEndloc if { ... statements ... }, store location of closing brace, otherwise loc of last token of statement
5668 AST.Statement parseStatement(int flags, const(char)** endPtr = null, Loc* pEndloc = null)
5672 AST.Statement ifbody;
5673 AST.Statement elsebody;
5675 const loc = token.loc;
5677 //printf("parseStatement()\n");
5678 if (flags & ParseStatementFlags.curly && token.value != TOK.leftCurly)
5679 error("statement expected to be `{ }`, not `%s`", token.toChars());
5681 switch (token.value)
5683 case TOK.identifier:
5685 /* A leading identifier can be a declaration, label, or expression.
5686 * The easiest case to check first is label:
5688 if (peekNext() == TOK.colonColon)
5693 error("use `.` for member lookup, not `::`");
5697 if (peekNext() == TOK.colon)
5700 Identifier ident = token.ident;
5703 if (token.value == TOK.rightCurly)
5705 else if (token.value == TOK.leftCurly)
5706 s = parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_);
5707 else if (flags & ParseStatementFlags.curlyScope)
5708 s = parseStatement(ParseStatementFlags.semiOk | ParseStatementFlags.curlyScope);
5710 s = parseStatement(ParseStatementFlags.semiOk);
5711 s = new AST.LabelStatement(loc, ident, s);
5720 /* https://issues.dlang.org/show_bug.cgi?id=15163
5721 * If tokens can be handled as
5722 * old C-style declaration or D expression, prefer the latter.
5724 if (isDeclaration(&token, NeedDeclaratorId.mustIfDstyle, TOK.reserved, null))
5731 case TOK.int32Literal:
5732 case TOK.uns32Literal:
5733 case TOK.int64Literal:
5734 case TOK.uns64Literal:
5735 case TOK.int128Literal:
5736 case TOK.uns128Literal:
5737 case TOK.float32Literal:
5738 case TOK.float64Literal:
5739 case TOK.float80Literal:
5740 case TOK.imaginary32Literal:
5741 case TOK.imaginary64Literal:
5742 case TOK.imaginary80Literal:
5743 case TOK.charLiteral:
5744 case TOK.wcharLiteral:
5745 case TOK.dcharLiteral:
5750 case TOK.leftParenthesis:
5758 case TOK.minusMinus:
5765 case TOK.leftBracket:
5767 case TOK.fileFullPath:
5769 case TOK.moduleString:
5770 case TOK.functionString:
5771 case TOK.prettyFunction:
5774 AST.Expression exp = parseExpression();
5775 /* https://issues.dlang.org/show_bug.cgi?id=15103
5776 * Improve declaration / initialization syntax error message
5777 * Error: found 'foo' when expecting ';' following statement
5778 * becomes Error: found `(` when expecting `;` or `=`, did you mean `Foo foo = 42`?
5780 if (token.value == TOK.identifier && exp.op == EXP.identifier)
5782 error(token.loc, "found `%s` when expecting `;` or `=`, did you mean `%s %s = %s`?", peek(&token).toChars(), exp.toChars(), token.toChars(), peek(peek(&token)).toChars());
5788 * https://issues.dlang.org/show_bug.cgi?id=22529
5789 * Avoid empty declaration error in case of missing semicolon
5790 * followed by another token and another semicolon. E.g.:
5795 * When the missing `;` error is emitted, token is sitting on return.
5796 * If we simply use `check` to emit the error, the token is advanced
5797 * to `;` and the empty statement error would follow. To avoid that,
5798 * we check if the next token is a semicolon and simply output the error,
5799 * otherwise we fall back on the old path (advancing the token).
5801 if (token.value != TOK.semicolon && peek(&token).value == TOK.semicolon)
5802 error("found `%s` when expecting `;` following statement", token.toChars());
5804 check(TOK.semicolon, "statement");
5806 s = new AST.ExpStatement(loc, exp);
5811 // Look ahead to see if it's static assert() or static if()
5812 const tv = peekNext();
5813 if (tv == TOK.assert_)
5815 s = new AST.StaticAssertStatement(parseStaticAssert());
5820 cond = parseStaticIfCondition();
5823 if (tv == TOK.foreach_ || tv == TOK.foreach_reverse_)
5825 s = parseForeach!(AST.StaticForeachStatement)(loc, null);
5826 if (flags & ParseStatementFlags.scope_)
5827 s = new AST.ScopeStatement(loc, s, token.loc);
5830 if (tv == TOK.import_)
5832 AST.Dsymbols* imports = parseImport();
5833 s = new AST.ImportStatement(loc, imports);
5834 if (flags & ParseStatementFlags.scope_)
5835 s = new AST.ScopeStatement(loc, s, token.loc);
5841 if (peekNext() == TOK.switch_)
5866 case TOK.imaginary32:
5867 case TOK.imaginary64:
5868 case TOK.imaginary80:
5873 // bug 7773: int.max is always a part of expression
5874 if (peekNext() == TOK.dot)
5876 if (peekNext() == TOK.leftParenthesis)
5886 case TOK.immutable_:
5889 case TOK.deprecated_:
5898 case TOK.interface_:
5901 AST.Dsymbols* a = parseDeclarations(false, null, null);
5904 auto as = new AST.Statements();
5905 as.reserve(a.length);
5906 foreach (i; 0 .. a.length)
5908 AST.Dsymbol d = (*a)[i];
5909 s = new AST.ExpStatement(loc, d);
5912 s = new AST.CompoundDeclarationStatement(loc, as);
5914 else if (a.length == 1)
5916 AST.Dsymbol d = (*a)[0];
5917 s = new AST.ExpStatement(loc, d);
5920 s = new AST.ExpStatement(loc, cast(AST.Expression)null);
5921 if (flags & ParseStatementFlags.scope_)
5922 s = new AST.ScopeStatement(loc, s, token.loc);
5927 /* Determine if this is a manifest constant declaration,
5928 * or a conventional enum.
5931 const tv = peekNext();
5932 if (tv == TOK.leftCurly || tv == TOK.colon)
5934 else if (tv != TOK.identifier)
5938 const nextv = peekNext2();
5939 if (nextv == TOK.leftCurly || nextv == TOK.colon || nextv == TOK.semicolon)
5944 s = new AST.ExpStatement(loc, d);
5945 if (flags & ParseStatementFlags.scope_)
5946 s = new AST.ScopeStatement(loc, s, token.loc);
5951 if (isDeclaration(&token, NeedDeclaratorId.mustIfDstyle, TOK.reserved, null))
5953 const tv = peekNext();
5954 if (tv == TOK.leftParenthesis)
5957 AST.Expression e = parseAssignExp();
5958 check(TOK.semicolon, "mixin");
5959 if (e.op == EXP.mixin_)
5961 AST.MixinExp cpe = cast(AST.MixinExp)e;
5962 s = new AST.CompileStatement(loc, cpe.exps);
5966 s = new AST.ExpStatement(loc, e);
5970 else if (tv == TOK.template_)
5974 AST.Dsymbol d = parseTemplateDeclaration(true);
5975 s = new AST.ExpStatement(loc, d);
5978 AST.Dsymbol d = parseMixin();
5979 s = new AST.ExpStatement(loc, d);
5980 if (flags & ParseStatementFlags.scope_)
5981 s = new AST.ScopeStatement(loc, s, token.loc);
5986 const lookingForElseSave = lookingForElse;
5987 lookingForElse = Loc.initial;
5990 //if (token.value == TOK.semicolon)
5991 // error("use `{ }` for an empty statement, not `;`");
5992 auto statements = new AST.Statements();
5993 while (token.value != TOK.rightCurly && token.value != TOK.endOfFile)
5995 statements.push(parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope));
5998 *endPtr = token.ptr;
6002 *pEndloc = token.loc;
6003 pEndloc = null; // don't set it again
6005 s = new AST.CompoundStatement(loc, statements);
6006 if (flags & (ParseStatementFlags.scope_ | ParseStatementFlags.curlyScope))
6007 s = new AST.ScopeStatement(loc, s, token.loc);
6008 check(TOK.rightCurly, "compound statement");
6009 lookingForElse = lookingForElseSave;
6015 check(TOK.leftParenthesis);
6016 auto param = parseAssignCondition();
6017 auto condition = parseExpression();
6018 closeCondition("while", param, condition);
6021 AST.Statement _body = parseStatement(ParseStatementFlags.scope_, null, &endloc);
6022 s = new AST.WhileStatement(loc, condition, _body, endloc, param);
6026 if (!(flags & ParseStatementFlags.semiOk))
6028 if (flags & ParseStatementFlags.semi)
6029 deprecation("use `{ }` for an empty statement, not `;`");
6031 error("use `{ }` for an empty statement, not `;`");
6034 s = new AST.ExpStatement(loc, cast(AST.Expression)null);
6039 AST.Statement _body;
6042 const lookingForElseSave = lookingForElse;
6043 lookingForElse = Loc.initial;
6044 _body = parseStatement(ParseStatementFlags.scope_);
6045 lookingForElse = lookingForElseSave;
6047 check(TOK.leftParenthesis);
6048 auto condition = parseExpression();
6049 closeCondition("do .. while", null, condition);
6050 if (token.value == TOK.semicolon)
6053 error("terminating `;` required after do-while statement");
6054 s = new AST.DoStatement(loc, _body, condition, token.loc);
6059 AST.Statement _init;
6060 AST.Expression condition;
6061 AST.Expression increment;
6064 check(TOK.leftParenthesis);
6065 if (token.value == TOK.semicolon)
6072 const lookingForElseSave = lookingForElse;
6073 lookingForElse = Loc.initial;
6074 _init = parseStatement(0);
6075 lookingForElse = lookingForElseSave;
6077 if (token.value == TOK.semicolon)
6084 condition = parseExpression();
6085 check(TOK.semicolon, "`for` condition");
6087 if (token.value == TOK.rightParenthesis)
6094 increment = parseExpression();
6095 check(TOK.rightParenthesis);
6098 AST.Statement _body = parseStatement(ParseStatementFlags.scope_, null, &endloc);
6099 s = new AST.ForStatement(loc, _init, condition, increment, _body, endloc);
6103 case TOK.foreach_reverse_:
6105 s = parseForeach!(AST.Statement)(loc, null);
6111 check(TOK.leftParenthesis);
6112 auto param = parseAssignCondition();
6113 auto condition = parseExpression();
6114 closeCondition("if", param, condition);
6117 const lookingForElseSave = lookingForElse;
6118 lookingForElse = loc;
6119 ifbody = parseStatement(ParseStatementFlags.scope_);
6120 lookingForElse = lookingForElseSave;
6122 if (token.value == TOK.else_)
6124 const elseloc = token.loc;
6126 elsebody = parseStatement(ParseStatementFlags.scope_);
6127 checkDanglingElse(elseloc);
6131 if (condition && ifbody)
6132 s = new AST.IfStatement(loc, param, condition, ifbody, elsebody, token.loc);
6134 s = null; // don't propagate parsing errors
6139 error("found `else` without a corresponding `if`, `version` or `debug` statement");
6143 if (peekNext() != TOK.leftParenthesis)
6144 goto Ldeclaration; // scope used as storage class
6146 check(TOK.leftParenthesis);
6147 if (token.value != TOK.identifier)
6149 error("scope identifier expected");
6154 TOK t = TOK.onScopeExit;
6155 Identifier id = token.ident;
6157 t = TOK.onScopeExit;
6158 else if (id == Id.failure)
6159 t = TOK.onScopeFailure;
6160 else if (id == Id.success)
6161 t = TOK.onScopeSuccess;
6163 error("valid scope identifiers are `exit`, `failure`, or `success`, not `%s`", id.toChars());
6165 check(TOK.rightParenthesis);
6166 AST.Statement st = parseStatement(ParseStatementFlags.scope_);
6167 s = new AST.ScopeGuardStatement(loc, t, st);
6173 if (token.value == TOK.assign)
6175 if (auto ds = parseDebugSpecification())
6178 ds.error("declaration must be at module level");
6180 ds.error("level declaration must be at module level");
6184 cond = parseDebugCondition();
6189 if (token.value == TOK.assign)
6191 if (auto vs = parseVersionSpecification())
6194 vs.error("declaration must be at module level");
6196 vs.error("level declaration must be at module level");
6200 cond = parseVersionCondition();
6205 const lookingForElseSave = lookingForElse;
6206 lookingForElse = loc;
6207 ifbody = parseStatement(0);
6208 lookingForElse = lookingForElseSave;
6211 if (token.value == TOK.else_)
6213 const elseloc = token.loc;
6215 elsebody = parseStatement(0);
6216 checkDanglingElse(elseloc);
6218 s = new AST.ConditionalStatement(loc, cond, ifbody, elsebody);
6219 if (flags & ParseStatementFlags.scope_)
6220 s = new AST.ScopeStatement(loc, s, token.loc);
6226 AST.Expressions* args = null;
6227 AST.Statement _body;
6230 check(TOK.leftParenthesis);
6231 if (token.value != TOK.identifier)
6233 error("`pragma(identifier)` expected");
6236 ident = token.ident;
6238 if (token.value == TOK.comma && peekNext() != TOK.rightParenthesis)
6239 args = parseArguments(); // pragma(identifier, args...);
6241 check(TOK.rightParenthesis); // pragma(identifier);
6242 if (token.value == TOK.semicolon)
6248 _body = parseStatement(ParseStatementFlags.semi);
6249 s = new AST.PragmaStatement(loc, ident, args, _body);
6259 check(TOK.leftParenthesis);
6260 AST.Expression condition = parseExpression();
6261 closeCondition("switch", null, condition);
6262 AST.Statement _body = parseStatement(ParseStatementFlags.scope_);
6263 s = new AST.SwitchStatement(loc, condition, _body, isfinal);
6269 AST.Expressions cases; // array of Expression's
6270 AST.Expression last = null;
6275 exp = parseAssignExp();
6277 if (token.value != TOK.comma)
6279 nextToken(); //comma
6281 while (token.value != TOK.colon && token.value != TOK.endOfFile);
6284 /* case exp: .. case last:
6286 if (token.value == TOK.slice)
6288 if (cases.length > 1)
6289 error("only one `case` allowed for start of case range");
6292 last = parseAssignExp();
6296 if (flags & ParseStatementFlags.curlyScope)
6298 auto statements = new AST.Statements();
6299 while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
6301 auto cur = parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope);
6302 statements.push(cur);
6304 // https://issues.dlang.org/show_bug.cgi?id=21739
6305 // Stop at the last break s.t. the following non-case statements are
6306 // not merged into the current case. This can happen for
6307 // case 1: ... break;
6308 // debug { case 2: ... }
6309 if (cur && cur.isBreakStatement())
6312 s = new AST.CompoundStatement(loc, statements);
6316 s = parseStatement(ParseStatementFlags.semi);
6318 s = new AST.ScopeStatement(loc, s, token.loc);
6322 s = new AST.CaseRangeStatement(loc, exp, last, s);
6326 // Keep cases in order by building the case statements backwards
6327 for (size_t i = cases.length; i; i--)
6330 s = new AST.CaseStatement(loc, exp, s);
6340 if (flags & ParseStatementFlags.curlyScope)
6342 auto statements = new AST.Statements();
6343 while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
6345 statements.push(parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope));
6347 s = new AST.CompoundStatement(loc, statements);
6350 s = parseStatement(ParseStatementFlags.semi);
6351 s = new AST.ScopeStatement(loc, s, token.loc);
6352 s = new AST.DefaultStatement(loc, s);
6359 exp = token.value == TOK.semicolon ? null : parseExpression();
6360 check(TOK.semicolon, "`return` statement");
6361 s = new AST.ReturnStatement(loc, exp);
6369 if (token.value == TOK.identifier)
6371 ident = token.ident;
6374 check(TOK.semicolon, "`break` statement");
6375 s = new AST.BreakStatement(loc, ident);
6383 if (token.value == TOK.identifier)
6385 ident = token.ident;
6388 check(TOK.semicolon, "`continue` statement");
6389 s = new AST.ContinueStatement(loc, ident);
6396 if (token.value == TOK.default_)
6399 s = new AST.GotoDefaultStatement(loc);
6401 else if (token.value == TOK.case_)
6403 AST.Expression exp = null;
6405 if (token.value != TOK.semicolon)
6406 exp = parseExpression();
6407 s = new AST.GotoCaseStatement(loc, exp);
6411 if (token.value != TOK.identifier)
6413 error("identifier expected following `goto`");
6418 ident = token.ident;
6421 s = new AST.GotoStatement(loc, ident);
6423 check(TOK.semicolon, "`goto` statement");
6426 case TOK.synchronized_:
6429 AST.Statement _body;
6431 Token* t = peek(&token);
6432 if (skipAttributes(t, &t) && t.value == TOK.class_)
6436 if (token.value == TOK.leftParenthesis)
6439 exp = parseExpression();
6440 closeCondition("synchronized", null, exp);
6444 _body = parseStatement(ParseStatementFlags.scope_);
6445 s = new AST.SynchronizedStatement(loc, exp, _body);
6451 AST.Statement _body;
6455 check(TOK.leftParenthesis);
6456 exp = parseExpression();
6457 closeCondition("with", null, exp);
6458 _body = parseStatement(ParseStatementFlags.scope_, null, &endloc);
6459 s = new AST.WithStatement(loc, exp, _body, endloc);
6464 AST.Statement _body;
6465 AST.Catches* catches = null;
6466 AST.Statement finalbody = null;
6469 const lookingForElseSave = lookingForElse;
6470 lookingForElse = Loc.initial;
6471 _body = parseStatement(ParseStatementFlags.scope_);
6472 lookingForElse = lookingForElseSave;
6473 while (token.value == TOK.catch_)
6475 AST.Statement handler;
6479 const catchloc = token.loc;
6482 if (token.value != TOK.leftParenthesis)
6484 deprecation("`catch` statement without an exception specification is deprecated");
6485 deprecationSupplemental("use `catch(Throwable)` for old behavior");
6491 check(TOK.leftParenthesis);
6494 check(TOK.rightParenthesis);
6496 handler = parseStatement(0);
6497 c = new AST.Catch(catchloc, t, id, handler);
6499 catches = new AST.Catches();
6503 if (token.value == TOK.finally_)
6506 finalbody = parseStatement(ParseStatementFlags.scope_);
6510 if (!catches && !finalbody)
6511 error("`catch` or `finally` expected following `try`");
6515 s = new AST.TryCatchStatement(loc, _body, catches);
6517 s = new AST.TryFinallyStatement(loc, s, finalbody);
6525 exp = parseExpression();
6526 check(TOK.semicolon, "`throw` statement");
6527 s = new AST.ThrowStatement(loc, exp);
6537 /* https://issues.dlang.org/show_bug.cgi?id=16088
6539 * At this point it can either be an
6540 * https://dlang.org/spec/grammar.html#ImportExpression
6542 * https://dlang.org/spec/grammar.html#ImportDeclaration.
6543 * See if the next token after `import` is a `(`; if so,
6544 * then it is an import expression.
6546 if (peekNext() == TOK.leftParenthesis)
6548 AST.Expression e = parseExpression();
6549 check(TOK.semicolon, "`import` Expression");
6550 s = new AST.ExpStatement(loc, e);
6554 AST.Dsymbols* imports = parseImport();
6555 s = new AST.ImportStatement(loc, imports);
6556 if (flags & ParseStatementFlags.scope_)
6557 s = new AST.ScopeStatement(loc, s, token.loc);
6563 AST.Dsymbol d = parseTemplateDeclaration();
6564 s = new AST.ExpStatement(loc, d);
6568 error("found `%s` instead of statement", token.toChars());
6572 while (token.value != TOK.rightCurly && token.value != TOK.semicolon && token.value != TOK.endOfFile)
6574 if (token.value == TOK.semicolon)
6576 s = new AST.ErrorStatement;
6585 private AST.ExpInitializer parseExpInitializer(Loc loc)
6587 auto ae = parseAssignExp();
6588 return new AST.ExpInitializer(loc, ae);
6591 private AST.Initializer parseStructInitializer(Loc loc)
6593 /* Scan ahead to discern between a struct initializer and
6594 * parameterless function literal.
6596 * We'll scan the topmost curly bracket level for statement-related
6597 * tokens, thereby ruling out a struct initializer. (A struct
6598 * initializer which itself contains function literals may have
6599 * statements at nested curly bracket levels.)
6601 * It's important that this function literal check not be
6602 * pendantic, otherwise a function having the slightest syntax
6603 * error would emit confusing errors when we proceed to parse it
6604 * as a struct initializer.
6606 * The following two ambiguous cases will be treated as a struct
6607 * initializer (best we can do without type info):
6609 * {{statements...}} - i.e. it could be struct initializer
6610 * with one function literal, or function literal having an
6611 * extra level of curly brackets
6612 * If a function literal is intended in these cases (unlikely),
6613 * source can use a more explicit function literal syntax
6614 * (e.g. prefix with "()" for empty parameter list).
6618 for (auto t = peek(&token); 1; t = peek(t))
6622 case TOK.leftParenthesis:
6625 case TOK.rightParenthesis:
6628 // https://issues.dlang.org/show_bug.cgi?id=21163
6629 // lambda params can have the `scope` storage class, e.g
6630 // `S s = { (scope Type Id){} }`
6632 if (!parens) goto case;
6634 /* Look for a semicolon or keyword of statements which don't
6635 * require a semicolon (typically containing BlockStatement).
6636 * Tokens like "else", "catch", etc. are omitted where the
6637 * leading token of the statement is sufficient.
6644 case TOK.interface_:
6649 case TOK.synchronized_:
6656 return parseExpInitializer(loc);
6663 case TOK.rightCurly:
6677 auto _is = new AST.StructInitializer(loc);
6678 bool commaExpected = false;
6682 switch (token.value)
6684 case TOK.identifier:
6688 error("comma expected separating field initializers");
6689 const t = peek(&token);
6691 if (t.value == TOK.colon)
6695 nextToken(); // skip over ':'
6697 auto value = parseInitializer();
6698 _is.addInit(id, value);
6699 commaExpected = true;
6704 error("expression expected, not `,`");
6706 commaExpected = false;
6709 case TOK.rightCurly: // allow trailing comma's
6714 error("found end of file instead of initializer");
6719 error("comma expected separating field initializers");
6720 auto value = parseInitializer();
6721 _is.addInit(null, value);
6722 commaExpected = true;
6731 /*****************************************
6732 * Parse initializer for variable declaration.
6734 private AST.Initializer parseInitializer()
6736 const loc = token.loc;
6738 switch (token.value)
6741 return parseStructInitializer(loc);
6743 case TOK.leftBracket:
6744 /* Scan ahead to see if it is an array initializer or
6746 * If it ends with a ';' ',' or ']', it is an array initializer.
6749 for (auto t = peek(&token); 1; t = peek(t))
6753 case TOK.leftBracket:
6757 case TOK.rightBracket:
6758 if (--brackets == 0)
6761 if (t.value != TOK.semicolon && t.value != TOK.comma && t.value != TOK.rightBracket && t.value != TOK.rightCurly)
6762 return parseExpInitializer(loc);
6776 auto ia = new AST.ArrayInitializer(loc);
6777 bool commaExpected = false;
6782 switch (token.value)
6787 error("comma expected separating array initializers, not `%s`", token.toChars());
6791 auto e = parseAssignExp();
6795 AST.Initializer value;
6796 if (token.value == TOK.colon)
6799 value = parseInitializer();
6803 value = new AST.ExpInitializer(e.loc, e);
6806 ia.addInit(e, value);
6807 commaExpected = true;
6811 case TOK.leftBracket:
6813 error("comma expected separating array initializers, not `%s`", token.toChars());
6814 auto value = parseInitializer();
6817 if (token.value == TOK.colon)
6820 if (auto ei = value.isExpInitializer())
6823 value = parseInitializer();
6826 error("initializer expression expected following colon, not `%s`", token.toChars());
6828 ia.addInit(e, value);
6829 commaExpected = true;
6834 error("expression expected, not `,`");
6836 commaExpected = false;
6839 case TOK.rightBracket: // allow trailing comma's
6844 error("found `%s` instead of array initializer", token.toChars());
6852 const tv = peekNext();
6853 if (tv == TOK.semicolon || tv == TOK.comma)
6856 return new AST.VoidInitializer(loc);
6858 return parseExpInitializer(loc);
6861 return parseExpInitializer(loc);
6865 /*****************************************
6866 * Parses default argument initializer expression that is an assign expression,
6867 * with special handling for __FILE__, __FILE_DIR__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__.
6869 private AST.Expression parseDefaultInitExp()
6871 AST.Expression e = null;
6872 const tv = peekNext();
6873 if (tv == TOK.comma || tv == TOK.rightParenthesis)
6875 switch (token.value)
6877 case TOK.file: e = new AST.FileInitExp(token.loc, EXP.file); break;
6878 case TOK.fileFullPath: e = new AST.FileInitExp(token.loc, EXP.fileFullPath); break;
6879 case TOK.line: e = new AST.LineInitExp(token.loc); break;
6880 case TOK.moduleString: e = new AST.ModuleInitExp(token.loc); break;
6881 case TOK.functionString: e = new AST.FuncInitExp(token.loc); break;
6882 case TOK.prettyFunction: e = new AST.PrettyFuncInitExp(token.loc); break;
6889 return parseAssignExp();
6892 /********************
6893 * Parse inline assembler block.
6895 * inline assembler block as a Statement
6897 AST.Statement parseAsm()
6899 // Parse the asm block into a sequence of AsmStatements,
6900 // each AsmStatement is one instruction.
6901 // Separate out labels.
6902 // Defer parsing of AsmStatements until semantic processing.
6904 const loc = token.loc;
6908 StorageClass stc = parsePostfix(STC.undefined_, null);
6909 if (stc & (STC.const_ | STC.immutable_ | STC.shared_ | STC.wild))
6910 error("`const`/`immutable`/`shared`/`inout` attributes are not allowed on `asm` blocks");
6912 check(TOK.leftCurly);
6913 Token* toklist = null;
6914 Token** ptoklist = &toklist;
6915 Identifier label = null;
6916 auto statements = new AST.Statements();
6917 size_t nestlevel = 0;
6920 switch (token.value)
6922 case TOK.identifier:
6925 // Look ahead to see if it is a label
6926 if (peekNext() == TOK.colon)
6929 label = token.ident;
6930 labelloc = token.loc;
6942 case TOK.rightCurly:
6948 if (toklist || label)
6950 error("`asm` statements must end in `;`");
6956 error("mismatched number of curly brackets");
6958 if (toklist || label)
6960 // Create AsmStatement from list of tokens we've saved
6961 AST.Statement s = new AST.AsmStatement(token.loc, toklist);
6963 ptoklist = &toklist;
6966 s = new AST.LabelStatement(labelloc, label, s);
6976 error("matching `}` expected, not end of file");
6979 case TOK.colonColon: // treat as two separate : tokens for iasmgcc
6980 *ptoklist = allocateToken();
6981 memcpy(*ptoklist, &token, Token.sizeof);
6982 (*ptoklist).value = TOK.colon;
6983 ptoklist = &(*ptoklist).next;
6985 *ptoklist = allocateToken();
6986 memcpy(*ptoklist, &token, Token.sizeof);
6987 (*ptoklist).value = TOK.colon;
6988 ptoklist = &(*ptoklist).next;
6995 *ptoklist = allocateToken();
6996 memcpy(*ptoklist, &token, Token.sizeof);
6997 ptoklist = &(*ptoklist).next;
7005 auto s = new AST.CompoundAsmStatement(loc, statements, stc);
7009 /**********************************
7010 * Issue error if the current token is not `value`,
7011 * advance to next token.
7013 * loc = location for error message
7014 * value = token value to compare with
7016 void check(Loc loc, TOK value)
7018 if (token.value != value)
7019 error(loc, "found `%s` when expecting `%s`", token.toChars(), Token.toChars(value));
7023 /**********************************
7024 * Issue error if the current token is not `value`,
7025 * advance to next token.
7027 * value = token value to compare with
7029 void check(TOK value)
7031 check(token.loc, value);
7034 /**********************************
7035 * Issue error if the current token is not `value`,
7036 * advance to next token.
7038 * value = token value to compare with
7039 * string = for error message
7041 void check(TOK value, const(char)* string)
7043 if (token.value != value)
7044 error(token.loc, "found `%s` when expecting `%s` following %s", token.toChars(), Token.toChars(value), string);
7048 private void checkParens(TOK value, AST.Expression e)
7050 if (precedence[e.op] == PREC.rel && !e.parens)
7051 error(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`", e.toChars(), Token.toChars(value));
7055 enum NeedDeclaratorId
7057 no, // Declarator part must have no identifier
7058 opt, // Declarator part identifier is optional
7059 must, // Declarator part must have identifier
7060 mustIfDstyle, // Declarator part must have identifier, but don't recognize old C-style syntax
7063 /************************************
7064 * Determine if the scanner is sitting on the start of a declaration.
7066 * t = current token of the scanner
7067 * needId = flag with additional requirements for a declaration
7068 * endtok = ending token
7069 * pt = will be set ending token (if not null)
7071 * true if the token `t` is a declaration, false otherwise
7073 private bool isDeclaration(Token* t, NeedDeclaratorId needId, TOK endtok, Token** pt)
7075 //printf("isDeclaration(needId = %d)\n", needId);
7081 if ((t.value == TOK.const_ || t.value == TOK.immutable_ || t.value == TOK.inout_ || t.value == TOK.shared_) && peek(t).value != TOK.leftParenthesis)
7094 if (!isBasicType(&t))
7098 if (!isDeclarator(&t, &haveId, &haveTpl, endtok, needId != NeedDeclaratorId.mustIfDstyle))
7100 if ((needId == NeedDeclaratorId.no && !haveId) ||
7101 (needId == NeedDeclaratorId.opt) ||
7102 (needId == NeedDeclaratorId.must && haveId) ||
7103 (needId == NeedDeclaratorId.mustIfDstyle && haveId))
7112 //printf("\tis declaration, t = %s\n", t.toChars());
7116 //printf("\tis not declaration\n");
7120 private bool isBasicType(Token** pt)
7122 // This code parallels parseBasicType()
7143 case TOK.imaginary32:
7144 case TOK.imaginary64:
7145 case TOK.imaginary80:
7153 case TOK.identifier:
7156 if (t.value == TOK.not)
7166 if (t.value == TOK.dot)
7170 if (t.value != TOK.identifier)
7173 if (t.value != TOK.not)
7178 * !( args ), !identifier, etc.
7183 case TOK.identifier:
7186 case TOK.leftParenthesis:
7187 if (!skipParens(t, &t))
7208 case TOK.imaginary32:
7209 case TOK.imaginary64:
7210 case TOK.imaginary80:
7215 case TOK.int32Literal:
7216 case TOK.uns32Literal:
7217 case TOK.int64Literal:
7218 case TOK.uns64Literal:
7219 case TOK.int128Literal:
7220 case TOK.uns128Literal:
7221 case TOK.float32Literal:
7222 case TOK.float64Literal:
7223 case TOK.float80Literal:
7224 case TOK.imaginary32Literal:
7225 case TOK.imaginary64Literal:
7226 case TOK.imaginary80Literal:
7230 case TOK.charLiteral:
7231 case TOK.wcharLiteral:
7232 case TOK.dcharLiteral:
7235 case TOK.fileFullPath:
7237 case TOK.moduleString:
7238 case TOK.functionString:
7239 case TOK.prettyFunction:
7256 /* typeof(exp).identifier...
7259 if (!skipParens(t, &t))
7264 // __traits(getMember
7266 if (t.value != TOK.leftParenthesis)
7270 if (t.value != TOK.identifier || t.ident != Id.getMember)
7272 if (!skipParens(lp, &lp))
7274 // we are in a lookup for decl VS statement
7275 // so we expect a declarator following __trait if it's a type.
7276 // other usages wont be ambiguous (alias, template instance, type qual, etc.)
7277 if (lp.value != TOK.identifier)
7283 case TOK.immutable_:
7286 // const(type) or immutable(type) or shared(type) or wild(type)
7288 if (t.value != TOK.leftParenthesis)
7291 if (!isDeclaration(t, NeedDeclaratorId.no, TOK.rightParenthesis, &t))
7306 //printf("is not\n");
7310 private bool isDeclarator(Token** pt, int* haveId, int* haveTpl, TOK endtok, bool allowAltSyntax = true)
7312 // This code parallels parseDeclarator()
7316 //printf("Parser::isDeclarator() %s\n", t.toChars());
7317 if (t.value == TOK.assign)
7330 case TOK.leftBracket:
7332 if (t.value == TOK.rightBracket)
7336 else if (isDeclaration(t, NeedDeclaratorId.no, TOK.rightBracket, &t))
7338 // It's an associative array declaration
7342 if (t.value == TOK.dot && peek(t).value == TOK.identifier)
7351 // [ expression .. expression ]
7352 if (!isExpression(&t))
7354 if (t.value == TOK.slice)
7357 if (!isExpression(&t))
7359 if (t.value != TOK.rightBracket)
7365 if (t.value != TOK.rightBracket)
7369 if (t.value == TOK.dot && peek(t).value == TOK.identifier)
7378 case TOK.identifier:
7385 case TOK.leftParenthesis:
7386 if (!allowAltSyntax)
7387 return false; // Do not recognize C-style declarations.
7390 if (t.value == TOK.rightParenthesis)
7391 return false; // () is not a declarator
7393 /* Regard ( identifier ) as not a declarator
7394 * BUG: what about ( *identifier ) in
7396 * where f is a class instance with overloaded () ?
7397 * Should we just disallow C-style function pointer declarations?
7399 if (t.value == TOK.identifier)
7401 Token* t2 = peek(t);
7402 if (t2.value == TOK.rightParenthesis)
7406 if (!isDeclarator(&t, haveId, null, TOK.rightParenthesis))
7415 if (!isParameters(&t))
7417 skipAttributes(t, &t);
7430 static if (CARRAYDECL)
7432 case TOK.leftBracket:
7435 if (t.value == TOK.rightBracket)
7439 else if (isDeclaration(t, NeedDeclaratorId.no, TOK.rightBracket, &t))
7441 // It's an associative array declaration
7447 if (!isExpression(&t))
7449 if (t.value != TOK.rightBracket)
7456 case TOK.leftParenthesis:
7458 if (Token* tk = peekPastParen(t))
7460 if (tk.value == TOK.leftParenthesis)
7467 else if (tk.value == TOK.assign)
7476 if (!isParameters(&t))
7483 case TOK.immutable_:
7494 t = peek(t); // skip '@'
7495 t = peek(t); // skip identifier
7505 // Valid tokens that follow a declaration
7506 case TOK.rightParenthesis:
7507 case TOK.rightBracket:
7516 // The !parens is to disallow unnecessary parentheses
7517 if (!parens && (endtok == TOK.reserved || endtok == t.value))
7524 case TOK.identifier:
7525 if (t.ident == Id._body)
7527 // @@@DEPRECATED_2.117@@@
7528 // https://github.com/dlang/DIPs/blob/1f5959abe482b1f9094f6484a7d0a3ade77fc2fc/DIPs/accepted/DIP1003.md
7529 // Deprecated in 2.097 - Can be removed from 2.117
7530 // The deprecation period is longer than usual as `body`
7531 // was quite widely used.
7532 deprecation("usage of the `body` keyword is deprecated. Use `do` instead.");
7538 return haveTpl ? true : false;
7540 // Used for mixin type parsing
7542 if (endtok == TOK.endOfFile)
7553 private bool isParameters(Token** pt)
7555 // This code parallels parseParameterList()
7558 //printf("isParameters()\n");
7559 if (t.value != TOK.leftParenthesis)
7563 for (; 1; t = peek(t))
7568 case TOK.rightParenthesis:
7573 if (skipAttributes(t, &pastAttr))
7595 case TOK.immutable_:
7599 if (t.value == TOK.leftParenthesis)
7602 if (!isDeclaration(t, NeedDeclaratorId.no, TOK.rightParenthesis, &t))
7604 t = peek(t); // skip past closing ')'
7611 if (!isBasicType(&t))
7615 if (t.value != TOK.dotDotDot && !isDeclarator(&t, &tmp, null, TOK.reserved))
7617 if (t.value == TOK.assign)
7620 if (!isExpression(&t))
7623 if (t.value == TOK.dotDotDot)
7629 if (t.value == TOK.comma)
7637 if (t.value != TOK.rightParenthesis)
7644 private bool isExpression(Token** pt)
7646 // This is supposed to determine if something is an expression.
7647 // What it actually does is scan until a closing right bracket
7655 for (;; t = peek(t))
7659 case TOK.leftBracket:
7663 case TOK.rightBracket:
7668 case TOK.leftParenthesis:
7673 if (brnest || panest)
7677 case TOK.rightParenthesis:
7686 case TOK.rightCurly:
7687 if (--curlynest >= 0)
7714 /*******************************************
7717 * t = on opening $(LPAREN)
7718 * pt = *pt is set to token past '$(RPAREN)' on true
7721 * false some parsing error
7723 bool skipParens(Token* t, Token** pt)
7725 if (t.value != TOK.leftParenthesis)
7734 case TOK.leftParenthesis:
7738 case TOK.rightParenthesis:
7756 *pt = peek(t); // skip found rparen
7763 private bool skipParensIf(Token* t, Token** pt)
7765 if (t.value != TOK.leftParenthesis)
7771 return skipParens(t, pt);
7774 //returns true if the next value (after optional matching parens) is expected
7775 private bool hasOptionalParensThen(Token* t, TOK expected)
7778 if (!skipParensIf(t, &tk))
7780 return tk.value == expected;
7783 /*******************************************
7786 * t is on a candidate attribute
7788 * *pt is set to first non-attribute token on success
7791 * false some parsing error
7793 private bool skipAttributes(Token* t, Token** pt)
7800 case TOK.immutable_:
7808 case TOK.synchronized_:
7811 case TOK.deprecated_:
7812 if (peek(t).value == TOK.leftParenthesis)
7815 if (!skipParens(t, &t))
7817 // t is on the next of closing parenthesis
7831 if (t.value == TOK.identifier)
7835 * @identifier!(arglist)
7836 * any of the above followed by (arglist)
7837 * @predefined_attribute
7839 if (isBuiltinAtAttribute(t.ident))
7842 if (t.value == TOK.not)
7845 if (t.value == TOK.leftParenthesis)
7847 // @identifier!(arglist)
7848 if (!skipParens(t, &t))
7850 // t is on the next of closing parenthesis
7855 // Do low rent skipTemplateArgument
7856 if (t.value == TOK.vector)
7858 // identifier!__vector(type)
7860 if (!skipParens(t, &t))
7867 if (t.value == TOK.leftParenthesis)
7869 if (!skipParens(t, &t))
7871 // t is on the next of closing parenthesis
7876 if (t.value == TOK.leftParenthesis)
7878 // @( ArgumentList )
7879 if (!skipParens(t, &t))
7881 // t is on the next of closing parenthesis
7900 AST.Expression parseExpression()
7902 auto loc = token.loc;
7904 //printf("Parser::parseExpression() loc = %d\n", loc.linnum);
7905 auto e = parseAssignExp();
7906 while (token.value == TOK.comma)
7909 auto e2 = parseAssignExp();
7910 e = new AST.CommaExp(loc, e, e2, false);
7916 /********************************* Expression Parser ***************************/
7918 AST.Expression parsePrimaryExp()
7923 const loc = token.loc;
7925 //printf("parsePrimaryExp(): loc = %d\n", loc.linnum);
7926 switch (token.value)
7928 case TOK.identifier:
7930 if (peekNext() == TOK.arrow)
7932 // skip `identifier ->`
7935 error("use `.` for member lookup, not `->`");
7939 if (peekNext() == TOK.goesTo)
7945 if (token.value == TOK.not && (save = peekNext()) != TOK.is_ && save != TOK.in_)
7947 // identifier!(template-argument-list)
7948 auto tempinst = new AST.TemplateInstance(loc, id, parseTemplateArguments());
7949 e = new AST.ScopeExp(loc, tempinst);
7952 e = new AST.IdentifierExp(loc, id);
7957 error("`$` is valid only inside [] of index or slice");
7958 e = new AST.DollarExp(loc);
7963 // Signal global scope '.' operator with "" identifier
7964 e = new AST.IdentifierExp(loc, Id.empty);
7968 e = new AST.ThisExp(loc);
7973 e = new AST.SuperExp(loc);
7977 case TOK.int32Literal:
7978 e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint32);
7982 case TOK.uns32Literal:
7983 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns32);
7987 case TOK.int64Literal:
7988 e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint64);
7992 case TOK.uns64Literal:
7993 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns64);
7997 case TOK.float32Literal:
7998 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat32);
8002 case TOK.float64Literal:
8003 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat64);
8007 case TOK.float80Literal:
8008 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat80);
8012 case TOK.imaginary32Literal:
8013 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary32);
8017 case TOK.imaginary64Literal:
8018 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary64);
8022 case TOK.imaginary80Literal:
8023 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary80);
8028 e = new AST.NullExp(loc);
8034 const(char)* s = loc.filename ? loc.filename : mod.ident.toChars();
8035 e = new AST.StringExp(loc, s.toDString());
8039 case TOK.fileFullPath:
8041 assert(loc.isValid(), "__FILE_FULL_PATH__ does not work with an invalid location");
8042 const s = FileName.toAbsolute(loc.filename);
8043 e = new AST.StringExp(loc, s.toDString());
8049 e = new AST.IntegerExp(loc, loc.linnum, AST.Type.tint32);
8053 case TOK.moduleString:
8055 const(char)* s = md ? md.toChars() : mod.toChars();
8056 e = new AST.StringExp(loc, s.toDString());
8060 case TOK.functionString:
8061 e = new AST.FuncInitExp(loc);
8065 case TOK.prettyFunction:
8066 e = new AST.PrettyFuncInitExp(loc);
8071 e = new AST.IntegerExp(loc, 1, AST.Type.tbool);
8076 e = new AST.IntegerExp(loc, 0, AST.Type.tbool);
8080 case TOK.charLiteral:
8081 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tchar);
8085 case TOK.wcharLiteral:
8086 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.twchar);
8090 case TOK.dcharLiteral:
8091 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tdchar);
8097 // cat adjacent strings
8098 auto s = token.ustring;
8099 auto len = token.len;
8100 auto postfix = token.postfix;
8105 if (token.value == TOK.string_)
8109 if (token.postfix != postfix)
8110 error(token.loc, "mismatched string literal postfixes `'%c'` and `'%c'`", postfix, token.postfix);
8111 postfix = token.postfix;
8114 error("implicit string concatenation is error-prone and disallowed in D");
8115 eSink.errorSupplemental(token.loc, "Use the explicit syntax instead " ~
8116 "(concatenating literals is `@nogc`): %s ~ %s",
8117 prev.toChars(), token.toChars());
8120 const len2 = token.len;
8122 auto s2 = cast(char*)mem.xmalloc_noscan(len * char.sizeof);
8123 memcpy(s2, s, len1 * char.sizeof);
8124 memcpy(s2 + len1, token.ustring, len2 * char.sizeof);
8130 e = new AST.StringExp(loc, s[0 .. len], len, 1, postfix);
8146 t = AST.Type.tint16;
8150 t = AST.Type.tuns16;
8154 t = AST.Type.tint32;
8158 t = AST.Type.tuns32;
8162 t = AST.Type.tint64;
8166 t = AST.Type.tuns64;
8170 t = AST.Type.tint128;
8174 t = AST.Type.tuns128;
8178 t = AST.Type.tfloat32;
8182 t = AST.Type.tfloat64;
8186 t = AST.Type.tfloat80;
8189 case TOK.imaginary32:
8190 t = AST.Type.timaginary32;
8193 case TOK.imaginary64:
8194 t = AST.Type.timaginary64;
8197 case TOK.imaginary80:
8198 t = AST.Type.timaginary80;
8202 t = AST.Type.tcomplex32;
8206 t = AST.Type.tcomplex64;
8210 t = AST.Type.tcomplex80;
8222 t = AST.Type.twchar;
8226 t = AST.Type.tdchar;
8229 const next = peekNext();
8230 if (next != TOK.leftParenthesis && next != TOK.dot)
8232 // defer error for better diagnostics
8233 e = new AST.TypeExp(loc, parseType);
8237 if (token.value == TOK.leftParenthesis)
8239 e = new AST.TypeExp(loc, t);
8240 e = new AST.CallExp(loc, e, parseArguments());
8244 if (token.value != TOK.identifier)
8246 error(token.loc, "found `%s` when expecting identifier following `%s`.", token.toChars(), t.toChars());
8249 e = new AST.DotIdExp(loc, new AST.TypeExp(loc, t), token.ident);
8256 e = new AST.TypeExp(loc, t);
8262 e = new AST.TypeExp(loc, t);
8268 check(TOK.leftParenthesis, "`typeid`");
8269 RootObject o = parseTypeOrAssignExp();
8270 check(TOK.rightParenthesis);
8271 e = new AST.TypeidExp(loc, o);
8276 /* __traits(identifier, args...)
8279 AST.Objects* args = null;
8282 check(TOK.leftParenthesis);
8283 if (token.value != TOK.identifier)
8285 error("`__traits(identifier, args...)` expected");
8288 ident = token.ident;
8290 if (token.value == TOK.comma)
8291 args = parseTemplateArgumentList(); // __traits(identifier, args...)
8293 check(TOK.rightParenthesis); // __traits(identifier)
8295 e = new AST.TraitsExp(loc, ident, args);
8301 Identifier ident = null;
8302 AST.Type tspec = null;
8303 TOK tok = TOK.reserved;
8304 TOK tok2 = TOK.reserved;
8305 AST.TemplateParameters* tpl = null;
8308 if (token.value == TOK.leftParenthesis)
8311 if (token.value == TOK.identifier && peekNext() == TOK.leftParenthesis)
8313 error(loc, "unexpected `(` after `%s`, inside `is` expression. Try enclosing the contents of `is` with a `typeof` expression", token.toChars());
8315 Token* tempTok = peekPastParen(&token);
8316 memcpy(&token, tempTok, Token.sizeof);
8319 targ = parseType(&ident);
8320 if (token.value == TOK.colon || token.value == TOK.equal)
8324 if (tok == TOK.equal && (token.value == TOK.struct_ || token.value == TOK.union_
8325 || token.value == TOK.class_ || token.value == TOK.super_ || token.value == TOK.enum_
8326 || token.value == TOK.interface_ || token.value == TOK.package_ || token.value == TOK.module_
8327 || token.value == TOK.argumentTypes || token.value == TOK.parameters
8328 || token.value == TOK.const_ && peekNext() == TOK.rightParenthesis
8329 || token.value == TOK.immutable_ && peekNext() == TOK.rightParenthesis
8330 || token.value == TOK.shared_ && peekNext() == TOK.rightParenthesis
8331 || token.value == TOK.inout_ && peekNext() == TOK.rightParenthesis || token.value == TOK.function_
8332 || token.value == TOK.delegate_ || token.value == TOK.return_
8333 || (token.value == TOK.vector && peekNext() == TOK.rightParenthesis)))
8340 tspec = parseType();
8345 if (token.value == TOK.comma)
8346 tpl = parseTemplateParameterList(1);
8349 tpl = new AST.TemplateParameters();
8350 check(TOK.rightParenthesis);
8354 check(TOK.rightParenthesis);
8358 error("`type identifier : specialization` expected following `is`");
8361 e = new AST.IsExp(loc, targ, ident, tok, tspec, tok2, tpl);
8366 // https://dlang.org/spec/expression.html#assert_expressions
8367 AST.Expression msg = null;
8370 check(TOK.leftParenthesis, "`assert`");
8371 e = parseAssignExp();
8372 if (token.value == TOK.comma)
8375 if (token.value != TOK.rightParenthesis)
8377 msg = parseAssignExp();
8378 if (token.value == TOK.comma)
8382 check(TOK.rightParenthesis);
8383 e = new AST.AssertExp(loc, e, msg);
8388 // https://dlang.org/spec/expression.html#mixin_expressions
8390 if (token.value != TOK.leftParenthesis)
8391 error(token.loc, "found `%s` when expecting `%s` following `mixin`", token.toChars(), Token.toChars(TOK.leftParenthesis));
8392 auto exps = parseArguments();
8393 e = new AST.MixinExp(loc, exps);
8399 check(TOK.leftParenthesis, "`import`");
8400 e = parseAssignExp();
8401 check(TOK.rightParenthesis);
8402 e = new AST.ImportExp(loc, e);
8406 e = parseNewExp(null);
8411 if (peekNext() == TOK.ref_ && peekNext2() == TOK.leftParenthesis)
8413 Token* tk = peekPastParen(peek(peek(&token)));
8414 if (skipAttributes(tk, &tk) && (tk.value == TOK.goesTo || tk.value == TOK.leftCurly))
8416 // auto ref (arguments) => expression
8417 // auto ref (arguments) { statements... }
8422 error("found `%s` when expecting `ref` and function literal following `auto`", token.toChars());
8427 if (peekNext() == TOK.leftParenthesis)
8429 Token* tk = peekPastParen(peek(&token));
8430 if (skipAttributes(tk, &tk) && (tk.value == TOK.goesTo || tk.value == TOK.leftCurly))
8432 // ref (arguments) => expression
8433 // ref (arguments) { statements... }
8438 error("found `%s` when expecting function literal following `ref`", token.toChars());
8441 case TOK.leftParenthesis:
8443 Token* tk = peekPastParen(&token);
8444 if (skipAttributes(tk, &tk) && (tk.value == TOK.goesTo || tk.value == TOK.leftCurly))
8446 // (arguments) => expression
8447 // (arguments) { statements... }
8453 e = parseExpression();
8455 check(loc, TOK.rightParenthesis);
8458 case TOK.leftBracket:
8460 /* Parse array literals and associative array literals:
8461 * [ value, value, value ... ]
8462 * [ key:value, key:value, key:value ... ]
8464 auto values = new AST.Expressions();
8465 AST.Expressions* keys = null;
8468 while (token.value != TOK.rightBracket && token.value != TOK.endOfFile)
8470 e = parseAssignExp();
8471 if (token.value == TOK.colon && (keys || values.length == 0))
8475 keys = new AST.Expressions();
8477 e = parseAssignExp();
8481 error("`key:value` expected for associative array literal");
8485 if (token.value == TOK.rightBracket)
8489 check(loc, TOK.rightBracket);
8492 e = new AST.AssocArrayLiteralExp(loc, keys, values);
8494 e = new AST.ArrayLiteralExp(loc, null, values);
8502 AST.Dsymbol s = parseFunctionLiteral();
8503 e = new AST.FuncExp(loc, s);
8508 error("expression expected, not `%s`", token.toChars());
8510 // Anything for e, as long as it's not NULL
8511 e = new AST.IntegerExp(loc, 0, AST.Type.tint32);
8518 private AST.Expression parseUnaryExp()
8521 const loc = token.loc;
8523 switch (token.value)
8527 e = parseUnaryExp();
8528 e = new AST.AddrExp(loc, e);
8533 e = parseUnaryExp();
8534 //e = new AddAssignExp(loc, e, new IntegerExp(loc, 1, Type::tint32));
8535 e = new AST.PreExp(EXP.prePlusPlus, loc, e);
8538 case TOK.minusMinus:
8540 e = parseUnaryExp();
8541 //e = new MinAssignExp(loc, e, new IntegerExp(loc, 1, Type::tint32));
8542 e = new AST.PreExp(EXP.preMinusMinus, loc, e);
8547 e = parseUnaryExp();
8548 e = new AST.PtrExp(loc, e);
8553 e = parseUnaryExp();
8554 e = new AST.NegExp(loc, e);
8559 e = parseUnaryExp();
8560 e = new AST.UAddExp(loc, e);
8565 e = parseUnaryExp();
8566 e = new AST.NotExp(loc, e);
8571 e = parseUnaryExp();
8572 e = new AST.ComExp(loc, e);
8576 // @@@DEPRECATED_2.109@@@
8577 // Use of `delete` keyword has been an error since 2.099.
8578 // Remove from the parser after 2.109.
8580 e = parseUnaryExp();
8581 e = new AST.DeleteExp(loc, e, false);
8584 case TOK.cast_: // cast(type) expression
8587 check(TOK.leftParenthesis);
8588 /* Look for cast(), cast(const), cast(immutable),
8589 * cast(shared), cast(shared const), cast(wild), cast(shared wild)
8594 switch (token.value)
8597 if (peekNext() == TOK.leftParenthesis)
8598 break; // const as type constructor
8599 m |= MODFlags.const_; // const as storage class
8603 case TOK.immutable_:
8604 if (peekNext() == TOK.leftParenthesis)
8606 m |= MODFlags.immutable_;
8611 if (peekNext() == TOK.leftParenthesis)
8613 m |= MODFlags.shared_;
8618 if (peekNext() == TOK.leftParenthesis)
8629 if (token.value == TOK.rightParenthesis)
8632 e = parseUnaryExp();
8633 e = new AST.CastExp(loc, e, m);
8637 AST.Type t = parseType(); // cast( type )
8638 t = t.addMod(m); // cast( const type )
8639 check(TOK.rightParenthesis);
8640 e = parseUnaryExp();
8641 e = new AST.CastExp(loc, e, t);
8648 case TOK.immutable_: // immutable(type)(arguments) / immutable(type).init
8650 StorageClass stc = parseTypeCtor();
8652 AST.Type t = parseBasicType();
8655 if (stc == 0 && token.value == TOK.dot)
8658 if (token.value != TOK.identifier)
8660 error("identifier expected following `(type)`.");
8661 return AST.ErrorExp.get();
8663 e = new AST.DotIdExp(loc, new AST.TypeExp(loc, t), token.ident);
8665 e = parsePostExp(e);
8669 e = new AST.TypeExp(loc, t);
8670 if (token.value != TOK.leftParenthesis)
8672 error("`(arguments)` expected following `%s`", t.toChars());
8675 e = new AST.CallExp(loc, e, parseArguments());
8679 case TOK.leftParenthesis:
8681 auto tk = peek(&token);
8682 static if (CCASTSYNTAX)
8685 if (isDeclaration(tk, NeedDeclaratorId.no, TOK.rightParenthesis, &tk))
8687 tk = peek(tk); // skip over right parenthesis
8692 if (tk.value == TOK.is_ || tk.value == TOK.in_) // !is or !in
8698 case TOK.minusMinus:
8701 case TOK.leftParenthesis:
8702 case TOK.identifier:
8705 case TOK.int32Literal:
8706 case TOK.uns32Literal:
8707 case TOK.int64Literal:
8708 case TOK.uns64Literal:
8709 case TOK.int128Literal:
8710 case TOK.uns128Literal:
8711 case TOK.float32Literal:
8712 case TOK.float64Literal:
8713 case TOK.float80Literal:
8714 case TOK.imaginary32Literal:
8715 case TOK.imaginary64Literal:
8716 case TOK.imaginary80Literal:
8720 case TOK.charLiteral:
8721 case TOK.wcharLiteral:
8722 case TOK.dcharLiteral:
8730 case TOK.fileFullPath:
8732 case TOK.moduleString:
8733 case TOK.functionString:
8734 case TOK.prettyFunction:
8752 case TOK.imaginary32:
8753 case TOK.imaginary64:
8754 case TOK.imaginary80:
8762 auto t = parseType();
8763 check(TOK.rightParenthesis);
8766 // or .identifier!( ... )
8767 if (token.value == TOK.dot)
8769 if (peekNext() != TOK.identifier && peekNext() != TOK.new_)
8771 error("identifier or new keyword expected following `(...)`.");
8773 return AST.ErrorExp.get();
8775 e = new AST.TypeExp(loc, t);
8777 e = parsePostExp(e);
8781 e = parseUnaryExp();
8782 e = new AST.CastExp(loc, e, t);
8783 error("C style cast illegal, use `%s`", e.toChars());
8792 e = parsePrimaryExp();
8793 e = parsePostExp(e);
8799 // Deviation from the DIP:
8800 // Parse AssignExpression instead of Expression to avoid conflicts for comma
8801 // separated lists, e.g. function arguments
8802 AST.Expression exp = parseAssignExp();
8803 e = new AST.ThrowExp(loc, exp);
8808 e = parsePrimaryExp();
8809 e = parsePostExp(e);
8814 // ^^ is right associative and has higher precedence than the unary operators
8815 while (token.value == TOK.pow)
8818 AST.Expression e2 = parseUnaryExp();
8819 e = new AST.PowExp(loc, e, e2);
8825 private AST.Expression parsePostExp(AST.Expression e)
8829 const loc = token.loc;
8830 switch (token.value)
8834 if (token.value == TOK.identifier)
8836 Identifier id = token.ident;
8839 if (token.value == TOK.not && peekNext() != TOK.is_ && peekNext() != TOK.in_)
8841 AST.Objects* tiargs = parseTemplateArguments();
8842 e = new AST.DotTemplateInstanceExp(loc, e, id, tiargs);
8845 e = new AST.DotIdExp(loc, e, id);
8848 if (token.value == TOK.new_)
8853 error("identifier or `new` expected following `.`, not `%s`", token.toChars());
8857 e = new AST.PostExp(EXP.plusPlus, loc, e);
8860 case TOK.minusMinus:
8861 e = new AST.PostExp(EXP.minusMinus, loc, e);
8864 case TOK.leftParenthesis:
8865 AST.Expressions* args = new AST.Expressions();
8866 AST.Identifiers* names = new AST.Identifiers();
8867 parseNamedArguments(args, names);
8868 e = new AST.CallExp(loc, e, args, names);
8871 case TOK.leftBracket:
8873 // array dereferences:
8876 // array[lwr .. upr]
8877 AST.Expression index;
8879 auto arguments = new AST.Expressions();
8883 while (token.value != TOK.rightBracket && token.value != TOK.endOfFile)
8885 index = parseAssignExp();
8886 if (token.value == TOK.slice)
8888 // array[..., lwr..upr, ...]
8890 upr = parseAssignExp();
8891 arguments.push(new AST.IntervalExp(loc, index, upr));
8894 arguments.push(index);
8895 if (token.value == TOK.rightBracket)
8899 check(TOK.rightBracket);
8901 e = new AST.ArrayExp(loc, e, arguments);
8911 private AST.Expression parseMulExp()
8913 const loc = token.loc;
8914 auto e = parseUnaryExp();
8918 switch (token.value)
8922 auto e2 = parseUnaryExp();
8923 e = new AST.MulExp(loc, e, e2);
8928 auto e2 = parseUnaryExp();
8929 e = new AST.DivExp(loc, e, e2);
8934 auto e2 = parseUnaryExp();
8935 e = new AST.ModExp(loc, e, e2);
8946 private AST.Expression parseAddExp()
8948 const loc = token.loc;
8949 auto e = parseMulExp();
8953 switch (token.value)
8957 auto e2 = parseMulExp();
8958 e = new AST.AddExp(loc, e, e2);
8963 auto e2 = parseMulExp();
8964 e = new AST.MinExp(loc, e, e2);
8969 auto e2 = parseMulExp();
8970 e = new AST.CatExp(loc, e, e2);
8981 private AST.Expression parseShiftExp()
8983 const loc = token.loc;
8984 auto e = parseAddExp();
8988 switch (token.value)
8992 auto e2 = parseAddExp();
8993 e = new AST.ShlExp(loc, e, e2);
8996 case TOK.rightShift:
8998 auto e2 = parseAddExp();
8999 e = new AST.ShrExp(loc, e, e2);
9002 case TOK.unsignedRightShift:
9004 auto e2 = parseAddExp();
9005 e = new AST.UshrExp(loc, e, e2);
9016 private AST.Expression parseCmpExp()
9018 const loc = token.loc;
9020 auto e = parseShiftExp();
9021 EXP op = EXP.reserved;
9023 switch (token.value)
9025 case TOK.equal: op = EXP.equal; goto Lequal;
9026 case TOK.notEqual: op = EXP.notEqual; goto Lequal;
9029 auto e2 = parseShiftExp();
9030 e = new AST.EqualExp(op, loc, e, e2);
9035 // Attempt to identify '!is'
9036 const tv = peekNext();
9041 auto e2 = parseShiftExp();
9042 e = new AST.InExp(loc, e, e2);
9043 e = new AST.NotExp(loc, e);
9049 op = EXP.notIdentity;
9052 case TOK.is_: op = EXP.identity; goto Lidentity;
9055 auto e2 = parseShiftExp();
9056 e = new AST.IdentityExp(op, loc, e, e2);
9059 case TOK.lessThan: op = EXP.lessThan; goto Lcmp;
9060 case TOK.lessOrEqual: op = EXP.lessOrEqual; goto Lcmp;
9061 case TOK.greaterThan: op = EXP.greaterThan; goto Lcmp;
9062 case TOK.greaterOrEqual: op = EXP.greaterOrEqual; goto Lcmp;
9065 auto e2 = parseShiftExp();
9066 e = new AST.CmpExp(op, loc, e, e2);
9071 auto e2 = parseShiftExp();
9072 e = new AST.InExp(loc, e, e2);
9081 private AST.Expression parseAndExp()
9083 Loc loc = token.loc;
9084 auto e = parseCmpExp();
9085 while (token.value == TOK.and)
9087 checkParens(TOK.and, e);
9089 auto e2 = parseCmpExp();
9090 checkParens(TOK.and, e2);
9091 e = new AST.AndExp(loc, e, e2);
9097 private AST.Expression parseXorExp()
9099 const loc = token.loc;
9101 auto e = parseAndExp();
9102 while (token.value == TOK.xor)
9104 checkParens(TOK.xor, e);
9106 auto e2 = parseAndExp();
9107 checkParens(TOK.xor, e2);
9108 e = new AST.XorExp(loc, e, e2);
9113 private AST.Expression parseOrExp()
9115 const loc = token.loc;
9117 auto e = parseXorExp();
9118 while (token.value == TOK.or)
9120 checkParens(TOK.or, e);
9122 auto e2 = parseXorExp();
9123 checkParens(TOK.or, e2);
9124 e = new AST.OrExp(loc, e, e2);
9129 private AST.Expression parseAndAndExp()
9131 const loc = token.loc;
9133 auto e = parseOrExp();
9134 while (token.value == TOK.andAnd)
9137 auto e2 = parseOrExp();
9138 e = new AST.LogicalExp(loc, EXP.andAnd, e, e2);
9143 private AST.Expression parseOrOrExp()
9145 const loc = token.loc;
9147 auto e = parseAndAndExp();
9148 while (token.value == TOK.orOr)
9151 auto e2 = parseAndAndExp();
9152 e = new AST.LogicalExp(loc, EXP.orOr, e, e2);
9157 private AST.Expression parseCondExp()
9159 const loc = token.loc;
9161 auto e = parseOrOrExp();
9162 if (token.value == TOK.question)
9165 auto e1 = parseExpression();
9167 auto e2 = parseCondExp();
9168 e = new AST.CondExp(loc, e, e1, e2);
9173 AST.Expression parseAssignExp()
9180 // require parens for e.g. `t ? a = 1 : b = 2`
9181 void checkRequiredParens()
9183 if (e.op == EXP.question && !e.parens)
9184 dmd.errors.error(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`",
9185 e.toChars(), Token.toChars(token.value));
9188 const loc = token.loc;
9189 switch (token.value)
9192 checkRequiredParens();
9194 auto e2 = parseAssignExp();
9195 e = new AST.AssignExp(loc, e, e2);
9199 checkRequiredParens();
9201 auto e2 = parseAssignExp();
9202 e = new AST.AddAssignExp(loc, e, e2);
9206 checkRequiredParens();
9208 auto e2 = parseAssignExp();
9209 e = new AST.MinAssignExp(loc, e, e2);
9213 checkRequiredParens();
9215 auto e2 = parseAssignExp();
9216 e = new AST.MulAssignExp(loc, e, e2);
9220 checkRequiredParens();
9222 auto e2 = parseAssignExp();
9223 e = new AST.DivAssignExp(loc, e, e2);
9227 checkRequiredParens();
9229 auto e2 = parseAssignExp();
9230 e = new AST.ModAssignExp(loc, e, e2);
9234 checkRequiredParens();
9236 auto e2 = parseAssignExp();
9237 e = new AST.PowAssignExp(loc, e, e2);
9241 checkRequiredParens();
9243 auto e2 = parseAssignExp();
9244 e = new AST.AndAssignExp(loc, e, e2);
9248 checkRequiredParens();
9250 auto e2 = parseAssignExp();
9251 e = new AST.OrAssignExp(loc, e, e2);
9255 checkRequiredParens();
9257 auto e2 = parseAssignExp();
9258 e = new AST.XorAssignExp(loc, e, e2);
9261 case TOK.leftShiftAssign:
9262 checkRequiredParens();
9264 auto e2 = parseAssignExp();
9265 e = new AST.ShlAssignExp(loc, e, e2);
9268 case TOK.rightShiftAssign:
9269 checkRequiredParens();
9271 auto e2 = parseAssignExp();
9272 e = new AST.ShrAssignExp(loc, e, e2);
9275 case TOK.unsignedRightShiftAssign:
9276 checkRequiredParens();
9278 auto e2 = parseAssignExp();
9279 e = new AST.UshrAssignExp(loc, e, e2);
9282 case TOK.concatenateAssign:
9283 checkRequiredParens();
9285 auto e2 = parseAssignExp();
9286 e = new AST.CatAssignExp(loc, e, e2);
9296 /*************************
9297 * Collect argument list.
9298 * Assume current token is ',', '$(LPAREN)' or '['.
9300 private AST.Expressions* parseArguments()
9303 AST.Expressions* arguments = new AST.Expressions();
9304 parseNamedArguments(arguments, null);
9308 /*************************
9309 * Collect argument list.
9310 * Assume current token is ',', '$(LPAREN)' or '['.
9312 private void parseNamedArguments(AST.Expressions* arguments, AST.Identifiers* names)
9316 const endtok = token.value == TOK.leftBracket ? TOK.rightBracket : TOK.rightParenthesis;
9320 while (token.value != endtok && token.value != TOK.endOfFile)
9322 if (peekNext() == TOK.colon)
9324 // Named argument `name: exp`
9325 auto loc = token.loc;
9326 auto ident = token.ident;
9327 check(TOK.identifier);
9332 error(loc, "named arguments not allowed here");
9340 auto arg = parseAssignExp();
9341 arguments.push(arg);
9343 if (token.value != TOK.comma)
9346 nextToken(); //comma
9351 /*******************************************
9353 private AST.Expression parseNewExp(AST.Expression thisexp)
9355 const loc = token.loc;
9358 AST.Expressions* arguments = null;
9359 AST.Identifiers* names = null;
9361 // An anonymous nested class starts with "class"
9362 if (token.value == TOK.class_)
9365 if (token.value == TOK.leftParenthesis)
9367 arguments = new AST.Expressions();
9368 names = new AST.Identifiers();
9369 parseNamedArguments(arguments, names);
9372 AST.BaseClasses* baseclasses = null;
9373 if (token.value != TOK.leftCurly)
9374 baseclasses = parseBaseClasses();
9376 Identifier id = null;
9377 AST.Dsymbols* members = null;
9379 if (token.value != TOK.leftCurly)
9381 error("`{ members }` expected for anonymous class");
9386 members = parseDeclDefs(0);
9387 if (token.value != TOK.rightCurly)
9388 error("class member expected");
9392 auto cd = new AST.ClassDeclaration(loc, id, baseclasses, members, false);
9393 auto e = new AST.NewAnonClassExp(loc, thisexp, cd, arguments);
9397 const stc = parseTypeCtor();
9398 auto t = parseBasicType(true);
9399 t = parseTypeSuffixes(t);
9401 if (t.ty == Taarray)
9403 AST.TypeAArray taa = cast(AST.TypeAArray)t;
9404 AST.Type index = taa.index;
9405 // `new Type[expr]` is a static array
9406 auto edim = AST.typeToExpression(index);
9408 t = new AST.TypeSArray(taa.next, edim);
9410 else if (token.value == TOK.leftParenthesis && t.ty != Tsarray)
9412 arguments = new AST.Expressions();
9413 names = new AST.Identifiers();
9414 parseNamedArguments(arguments, names);
9417 auto e = new AST.NewExp(loc, thisexp, t, arguments, names);
9421 /**********************************************
9423 private void addComment(AST.Dsymbol s, const(char)* blockComment)
9426 this.addComment(s, blockComment.toDString());
9429 private void addComment(AST.Dsymbol s, const(char)[] blockComment)
9433 s.addComment(combineComments(blockComment, token.lineComment, true));
9434 token.lineComment = null;
9438 /**********************************************
9439 * Recognize builtin @ attributes
9441 * ident = identifier
9443 * storage class for attribute, 0 if not
9445 static StorageClass isBuiltinAtAttribute(Identifier ident)
9447 return (ident == Id.property) ? STC.property :
9448 (ident == Id.nogc) ? STC.nogc :
9449 (ident == Id.safe) ? STC.safe :
9450 (ident == Id.trusted) ? STC.trusted :
9451 (ident == Id.system) ? STC.system :
9452 (ident == Id.live) ? STC.live :
9453 (ident == Id.future) ? STC.future :
9454 (ident == Id.disable) ? STC.disable :
9458 enum StorageClass atAttrGroup =
9465 /*STC.future |*/ // probably should be included
9490 /**********************************
9491 * Set operator precedence for each operator.
9495 immutable PREC[EXP.max + 1] precedence =
9497 EXP.type : PREC.expr,
9498 EXP.error : PREC.expr,
9499 EXP.objcClassReference : PREC.expr, // Objective-C class reference, same as EXP.type
9501 EXP.typeof_ : PREC.primary,
9502 EXP.mixin_ : PREC.primary,
9504 EXP.import_ : PREC.primary,
9505 EXP.dotVariable : PREC.primary,
9506 EXP.scope_ : PREC.primary,
9507 EXP.identifier : PREC.primary,
9508 EXP.this_ : PREC.primary,
9509 EXP.super_ : PREC.primary,
9510 EXP.int64 : PREC.primary,
9511 EXP.float64 : PREC.primary,
9512 EXP.complex80 : PREC.primary,
9513 EXP.null_ : PREC.primary,
9514 EXP.string_ : PREC.primary,
9515 EXP.arrayLiteral : PREC.primary,
9516 EXP.assocArrayLiteral : PREC.primary,
9517 EXP.classReference : PREC.primary,
9518 EXP.file : PREC.primary,
9519 EXP.fileFullPath : PREC.primary,
9520 EXP.line : PREC.primary,
9521 EXP.moduleString : PREC.primary,
9522 EXP.functionString : PREC.primary,
9523 EXP.prettyFunction : PREC.primary,
9524 EXP.typeid_ : PREC.primary,
9525 EXP.is_ : PREC.primary,
9526 EXP.assert_ : PREC.primary,
9527 EXP.halt : PREC.primary,
9528 EXP.template_ : PREC.primary,
9529 EXP.dSymbol : PREC.primary,
9530 EXP.function_ : PREC.primary,
9531 EXP.variable : PREC.primary,
9532 EXP.symbolOffset : PREC.primary,
9533 EXP.structLiteral : PREC.primary,
9534 EXP.compoundLiteral : PREC.primary,
9535 EXP.arrayLength : PREC.primary,
9536 EXP.delegatePointer : PREC.primary,
9537 EXP.delegateFunctionPointer : PREC.primary,
9538 EXP.remove : PREC.primary,
9539 EXP.tuple : PREC.primary,
9540 EXP.traits : PREC.primary,
9541 EXP.default_ : PREC.primary,
9542 EXP.overloadSet : PREC.primary,
9543 EXP.void_ : PREC.primary,
9544 EXP.vectorArray : PREC.primary,
9545 EXP._Generic : PREC.primary,
9548 EXP.dotTemplateInstance : PREC.primary,
9549 EXP.dotIdentifier : PREC.primary,
9550 EXP.dotTemplateDeclaration : PREC.primary,
9551 EXP.dot : PREC.primary,
9552 EXP.dotType : PREC.primary,
9553 EXP.plusPlus : PREC.primary,
9554 EXP.minusMinus : PREC.primary,
9555 EXP.prePlusPlus : PREC.primary,
9556 EXP.preMinusMinus : PREC.primary,
9557 EXP.call : PREC.primary,
9558 EXP.slice : PREC.primary,
9559 EXP.array : PREC.primary,
9560 EXP.index : PREC.primary,
9562 EXP.delegate_ : PREC.unary,
9563 EXP.address : PREC.unary,
9564 EXP.star : PREC.unary,
9565 EXP.negate : PREC.unary,
9566 EXP.uadd : PREC.unary,
9567 EXP.not : PREC.unary,
9568 EXP.tilde : PREC.unary,
9569 EXP.delete_ : PREC.unary,
9570 EXP.new_ : PREC.unary,
9571 EXP.newAnonymousClass : PREC.unary,
9572 EXP.cast_ : PREC.unary,
9573 EXP.throw_ : PREC.unary,
9575 EXP.vector : PREC.unary,
9584 EXP.concatenate : PREC.add,
9586 EXP.leftShift : PREC.shift,
9587 EXP.rightShift : PREC.shift,
9588 EXP.unsignedRightShift : PREC.shift,
9590 EXP.lessThan : PREC.rel,
9591 EXP.lessOrEqual : PREC.rel,
9592 EXP.greaterThan : PREC.rel,
9593 EXP.greaterOrEqual : PREC.rel,
9596 /* Note that we changed precedence, so that < and != have the same
9597 * precedence. This change is in the parser, too.
9599 EXP.equal : PREC.rel,
9600 EXP.notEqual : PREC.rel,
9601 EXP.identity : PREC.rel,
9602 EXP.notIdentity : PREC.rel,
9608 EXP.andAnd : PREC.andand,
9609 EXP.orOr : PREC.oror,
9611 EXP.question : PREC.cond,
9613 EXP.assign : PREC.assign,
9614 EXP.construct : PREC.assign,
9615 EXP.blit : PREC.assign,
9616 EXP.addAssign : PREC.assign,
9617 EXP.minAssign : PREC.assign,
9618 EXP.concatenateAssign : PREC.assign,
9619 EXP.concatenateElemAssign : PREC.assign,
9620 EXP.concatenateDcharAssign : PREC.assign,
9621 EXP.mulAssign : PREC.assign,
9622 EXP.divAssign : PREC.assign,
9623 EXP.modAssign : PREC.assign,
9624 EXP.powAssign : PREC.assign,
9625 EXP.leftShiftAssign : PREC.assign,
9626 EXP.rightShiftAssign : PREC.assign,
9627 EXP.unsignedRightShiftAssign : PREC.assign,
9628 EXP.andAssign : PREC.assign,
9629 EXP.orAssign : PREC.assign,
9630 EXP.xorAssign : PREC.assign,
9632 EXP.comma : PREC.expr,
9633 EXP.declaration : PREC.expr,
9635 EXP.interval : PREC.assign,
9638 enum ParseStatementFlags : int
9640 semi = 1, // empty ';' statements are allowed, but deprecated
9641 scope_ = 2, // start a new scope
9642 curly = 4, // { } statement is required
9643 curlyScope = 8, // { } starts a new scope
9644 semiOk = 0x10, // empty ';' are really ok
9647 struct PrefixAttributes(AST)
9649 StorageClass storageClass;
9650 AST.Expression depmsg;
9652 AST.Visibility visibility;
9654 AST.Expression ealign;
9655 AST.Expressions* udas;
9656 const(char)* comment;
9659 /// The result of the `ParseLinkage` function
9660 struct ParsedLinkage(AST)
9662 /// What linkage was specified
9664 /// If `extern(C++, class|struct)`, contains the `class|struct`
9665 CPPMANGLE cppmangle;
9666 /// If `extern(C++, some.identifier)`, will be the identifiers
9667 AST.Identifiers* idents;
9668 /// If `extern(C++, (some_tuple_expression)|"string"), will be the expressions
9669 AST.Expressions* identExps;
9673 /*********************************** Private *************************************/
9675 /***********************
9676 * How multiple declarations are parsed.
9684 private enum CDECLSYNTAX = 0;
9687 * Support C cast syntax:
9688 * (type)(expression)
9690 private enum CCASTSYNTAX = 1;
9693 * Support postfix C array declarations, such as
9696 private enum CARRAYDECL = 1;
9698 /*****************************
9699 * Destructively extract storage class from pAttrs.
9701 private StorageClass getStorageClass(AST)(PrefixAttributes!(AST)* pAttrs)
9703 StorageClass stc = STC.undefined_;
9706 stc = pAttrs.storageClass;
9707 pAttrs.storageClass = STC.undefined_;
9712 /**************************************
9713 * dump mixin expansion to file for better debugging
9715 private bool writeMixin(const(char)[] s, ref Loc loc)
9717 if (!global.params.mixinOut.doOutput)
9720 OutBuffer* ob = global.params.mixinOut.buffer;
9722 ob.writestring("// expansion at ");
9723 ob.writestring(loc.toChars());
9726 global.params.mixinOut.bufferLines++;
9728 loc = Loc(global.params.mixinOut.name.ptr, global.params.mixinOut.bufferLines + 1, loc.charnum);
9730 // write by line to create consistent line endings
9732 for (size_t i = 0; i < s.length; ++i)
9734 // detect LF and CRLF
9736 if (c == '\n' || (c == '\r' && i+1 < s.length && s[i+1] == '\n'))
9738 ob.writestring(s[lastpos .. i]);
9740 global.params.mixinOut.bufferLines++;
9747 if(lastpos < s.length)
9748 ob.writestring(s[lastpos .. $]);
9750 if (s.length == 0 || s[$-1] != '\n')
9752 ob.writenl(); // ensure empty line after expansion
9753 global.params.mixinOut.bufferLines++;
9756 global.params.mixinOut.bufferLines++;