d: Merge upstream dmd, druntime 4c18eed967, phobos d945686a4.
[official-gcc.git] / gcc / d / dmd / parse.d
blob3821f947a97e042fa45674a4b657e354c15971f4
1 /**
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
14 module dmd.parse;
16 import core.stdc.stdio;
17 import core.stdc.string;
19 import dmd.astenums;
20 import dmd.errorsink;
21 import dmd.id;
22 import dmd.identifier;
23 import dmd.lexer;
24 import dmd.location;
25 import dmd.root.filename;
26 import dmd.common.outbuffer;
27 import dmd.root.rmem;
28 import dmd.root.rootobject;
29 import dmd.root.string;
30 import dmd.tokens;
32 alias CompileEnv = dmd.lexer.CompileEnv;
34 /***********************************************************
36 class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
38 AST.ModuleDeclaration* md;
40 protected
42 AST.Module mod;
43 LINK linkage;
44 Loc linkLoc;
45 CPPMANGLE cppmangle;
46 Loc endloc; // set to location of last right curly
47 int inBrackets; // inside [] of array index or slice
48 Loc lookingForElse; // location of lonely if looking for an else
49 bool doUnittests; // parse unittest blocks
52 bool transitionIn = false; /// `-transition=in` is active, `in` parameters are listed
54 /*********************
55 * Use this constructor for string mixins.
56 * Input:
57 * loc = location in source file of mixin
59 extern (D) this(const ref Loc loc, AST.Module _module, const(char)[] input, bool doDocComment,
60 ErrorSink errorSink, const CompileEnv* compileEnv, const bool doUnittests) scope
62 //printf("Parser::Parser()1 %d\n", doUnittests);
63 this(_module, input, doDocComment, errorSink, compileEnv, doUnittests);
64 scanloc = loc;
67 /**************************************************
68 * Main Parser constructor.
70 extern (D) this(AST.Module _module, const(char)[] input, bool doDocComment, ErrorSink errorSink,
71 const CompileEnv* compileEnv, const bool doUnittests) scope
73 super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false,
74 errorSink,
75 compileEnv);
77 //printf("Parser::Parser()2 %d\n", doUnittests);
78 this.mod = _module;
79 this.linkage = LINK.d;
80 this.doUnittests = doUnittests;
83 /++
84 + Parse a module, i.e. the optional `module x.y.z` declaration and all declarations
85 + found in the current file.
87 + Returns: the list of declarations or an empty list in case of malformed declarations,
88 + the module declaration will be stored as `this.md` if found
90 AST.Dsymbols* parseModule()
92 if (!parseModuleDeclaration())
93 return errorReturn();
95 return parseModuleContent();
98 /++
99 + Parse the optional module declaration
101 + Returns: false if a malformed module declaration was found
103 final bool parseModuleDeclaration()
105 const comment = token.blockComment;
106 bool isdeprecated = false;
107 AST.Expression msg = null;
109 // Parse optional module attributes
110 parseModuleAttributes(msg, isdeprecated);
112 // ModuleDeclaration leads off
113 if (token.value == TOK.module_)
115 const loc = token.loc;
116 nextToken();
118 /* parse ModuleFullyQualifiedName
119 * https://dlang.org/spec/module.html#ModuleFullyQualifiedName
122 if (token.value != TOK.identifier)
124 error("identifier expected following `module`");
125 return false;
128 Identifier[] a;
129 Identifier id = token.ident;
131 while (nextToken() == TOK.dot)
133 a ~= id;
134 nextToken();
135 if (token.value != TOK.identifier)
137 error("identifier expected following `package`");
138 return false;
140 id = token.ident;
143 md = new AST.ModuleDeclaration(loc, a, id, msg, isdeprecated);
145 if (token.value != TOK.semicolon)
146 error("`;` expected following module declaration instead of `%s`", token.toChars());
147 nextToken();
148 addComment(mod, comment);
150 return true;
154 + Parse the content of a module, i.e. all declarations found until the end of file.
156 + Returns: the list of declarations or an empty list in case of malformed declarations
158 final AST.Dsymbols* parseModuleContent()
160 AST.Dsymbol lastDecl = mod;
161 AST.Dsymbols* decldefs = parseDeclDefs(0, &lastDecl);
163 if (token.value == TOK.rightCurly)
165 error("unmatched closing brace");
166 return errorReturn();
169 if (token.value != TOK.endOfFile)
171 error("unrecognized declaration");
172 return errorReturn();
174 return decldefs;
178 + Skips to the end of the current declaration - denoted by either `;` or EOF
180 + Returns: An empty list of Dsymbols
182 private AST.Dsymbols* errorReturn()
184 while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
185 nextToken();
186 nextToken();
187 return new AST.Dsymbols();
190 /**********************************
191 * Parse the ModuleAttributes preceding a module declaration.
192 * ModuleDeclaration:
193 * ModuleAttributes(opt) module ModuleFullyQualifiedName ;
194 * https://dlang.org/spec/module.html#ModuleAttributes
195 * Params:
196 * msg = set to the AssignExpression from DeprecatedAttribute https://dlang.org/spec/module.html#DeprecatedAttribute
197 * isdeprecated = set to true if a DeprecatedAttribute is seen
199 private
200 void parseModuleAttributes(out AST.Expression msg, out bool isdeprecated)
202 Token* tk;
203 if (!(skipAttributes(&token, &tk) && tk.value == TOK.module_))
204 return; // no module attributes
206 AST.Expressions* udas = null;
207 while (token.value != TOK.module_)
209 switch (token.value)
211 case TOK.deprecated_:
213 // deprecated (...) module ...
214 if (isdeprecated)
215 error("there is only one deprecation attribute allowed for module declaration");
216 isdeprecated = true;
217 nextToken();
218 if (token.value == TOK.leftParenthesis)
220 check(TOK.leftParenthesis);
221 msg = parseAssignExp();
222 check(TOK.rightParenthesis);
224 break;
226 case TOK.at:
228 AST.Expressions* exps = null;
229 const stc = parseAttribute(exps);
230 if (stc & atAttrGroup)
232 error("`@%s` attribute for module declaration is not supported", token.toChars());
234 else
236 udas = AST.UserAttributeDeclaration.concat(udas, exps);
238 if (stc)
239 nextToken();
240 break;
242 default:
244 error("`module` expected instead of `%s`", token.toChars());
245 nextToken();
246 break;
251 if (udas)
253 auto a = new AST.Dsymbols();
254 auto udad = new AST.UserAttributeDeclaration(udas, a);
255 mod.userAttribDecl = udad;
259 final:
262 * Parses a `deprecated` declaration
264 * Params:
265 * msg = Deprecated message, if any.
266 * Used to support overriding a deprecated storage class with
267 * a deprecated declaration with a message, but to error
268 * if both declaration have a message.
270 * Returns:
271 * Whether the deprecated declaration has a message
273 private bool parseDeprecatedAttribute(ref AST.Expression msg)
275 if (peekNext() != TOK.leftParenthesis)
276 return false;
278 nextToken();
279 check(TOK.leftParenthesis);
280 AST.Expression e = parseAssignExp();
281 check(TOK.rightParenthesis);
282 if (msg)
284 error(token.loc, "conflicting storage class `deprecated(%s)` and `deprecated(%s)`", msg.toChars(), e.toChars());
286 msg = e;
287 return true;
290 /************************************
291 * Parse declarations and definitions
292 * Params:
293 * once = !=0 means parse exactly one decl or def
294 * pLastDecl = set to last decl or def parsed
295 * pAttrs = keep track of attributes
296 * Returns:
297 * array of declared symbols
299 AST.Dsymbols* parseDeclDefs(int once, AST.Dsymbol* pLastDecl = null, PrefixAttributes!AST* pAttrs = null)
301 AST.Dsymbol lastDecl = null; // used to link unittest to its previous declaration
302 if (!pLastDecl)
303 pLastDecl = &lastDecl;
305 const linksave = linkage; // save global state
307 //printf("Parser::parseDeclDefs()\n");
308 auto decldefs = new AST.Dsymbols();
311 // parse result
312 AST.Dsymbol s = null;
313 AST.Dsymbols* a = null;
315 PrefixAttributes!AST attrs;
316 if (!once || !pAttrs)
318 pAttrs = &attrs;
319 pAttrs.comment = token.blockComment.ptr;
321 AST.Visibility.Kind prot;
322 StorageClass stc;
323 AST.Condition condition;
325 linkage = linksave;
327 Loc startloc;
328 Loc scdLoc;
330 switch (token.value)
332 case TOK.enum_:
334 /* Determine if this is a manifest constant declaration,
335 * or a conventional enum.
337 const tv = peekNext();
338 if (tv == TOK.leftCurly || tv == TOK.colon)
339 s = parseEnum();
340 else if (tv != TOK.identifier)
341 goto Ldeclaration;
342 else
344 const nextv = peekNext2();
345 if (nextv == TOK.leftCurly || nextv == TOK.colon || nextv == TOK.semicolon)
346 s = parseEnum();
347 else
348 goto Ldeclaration;
350 break;
352 case TOK.import_:
353 a = parseImport();
354 // keep pLastDecl
355 break;
357 case TOK.template_:
358 s = cast(AST.Dsymbol)parseTemplateDeclaration();
359 break;
361 case TOK.mixin_:
363 const loc = token.loc;
364 switch (peekNext())
366 case TOK.leftParenthesis:
368 // MixinType
369 if (isDeclaration(&token, NeedDeclaratorId.mustIfDstyle, TOK.reserved, null))
370 goto Ldeclaration;
371 // mixin(string)
372 nextToken();
373 auto exps = parseArguments();
374 check(TOK.semicolon);
375 s = new AST.MixinDeclaration(loc, exps);
376 break;
378 case TOK.template_:
379 // mixin template
380 nextToken();
381 s = cast(AST.Dsymbol)parseTemplateDeclaration(true);
382 break;
384 default:
385 s = parseMixin();
386 break;
388 break;
390 case TOK.wchar_:
391 case TOK.dchar_:
392 case TOK.bool_:
393 case TOK.char_:
394 case TOK.int8:
395 case TOK.uns8:
396 case TOK.int16:
397 case TOK.uns16:
398 case TOK.int32:
399 case TOK.uns32:
400 case TOK.int64:
401 case TOK.uns64:
402 case TOK.int128:
403 case TOK.uns128:
404 case TOK.float32:
405 case TOK.float64:
406 case TOK.float80:
407 case TOK.imaginary32:
408 case TOK.imaginary64:
409 case TOK.imaginary80:
410 case TOK.complex32:
411 case TOK.complex64:
412 case TOK.complex80:
413 case TOK.void_:
414 case TOK.alias_:
415 case TOK.identifier:
416 case TOK.super_:
417 case TOK.typeof_:
418 case TOK.dot:
419 case TOK.vector:
420 case TOK.struct_:
421 case TOK.union_:
422 case TOK.class_:
423 case TOK.interface_:
424 case TOK.traits:
425 Ldeclaration:
426 a = parseDeclarations(false, pAttrs, pAttrs.comment);
427 if (a && a.length)
428 *pLastDecl = (*a)[a.length - 1];
429 break;
431 case TOK.this_:
432 if (peekNext() == TOK.dot)
433 goto Ldeclaration;
434 s = parseCtor(pAttrs);
435 break;
437 case TOK.tilde:
438 s = parseDtor(pAttrs);
439 break;
441 case TOK.invariant_:
442 const tv = peekNext();
443 if (tv == TOK.leftParenthesis || tv == TOK.leftCurly)
445 // invariant { statements... }
446 // invariant() { statements... }
447 // invariant (expression);
448 s = parseInvariant(pAttrs);
449 break;
451 error("invariant body expected, not `%s`", token.toChars());
452 goto Lerror;
454 case TOK.unittest_:
456 * Ignore unittests in non-root modules.
458 * This mainly means that unittests *inside templates* are only
459 * ever instantiated if the module lexically declaring the
460 * template is one of the root modules.
462 * E.g., compiling some project with `-unittest` does NOT
463 * compile and later run any unittests in instantiations of
464 * templates declared in other libraries.
466 * Declaring unittests *inside* templates is considered an anti-
467 * pattern. In almost all cases, the unittests don't depend on
468 * the template parameters, but instantiate the template with
469 * fixed arguments (e.g., Nullable!T unittests instantiating
470 * Nullable!int), so compiling and running identical tests for
471 * each template instantiation is hardly desirable.
472 * But adding a unittest right below some function being tested
473 * is arguably good for locality, so unittests end up inside
474 * templates.
475 * To make sure a template's unittests are run, it should be
476 * instantiated in the same module, e.g., some module-level
477 * unittest.
479 * Another reason for ignoring unittests in templates from non-
480 * root modules is for template codegen culling via
481 * TemplateInstance.needsCodegen(). If the compiler decides not
482 * to emit some Nullable!bool because there's an existing
483 * instantiation in some non-root module, it has no idea whether
484 * that module was compiled with -unittest too, and so whether
485 * Nullable!int (instantiated in some unittest inside the
486 * Nullable template) can be culled too. By ignoring unittests
487 * in non-root modules, the compiler won't consider any
488 * template instantiations in these unittests as candidates for
489 * further codegen culling.
491 // The isRoot check is here because it can change after parsing begins (see dmodule.d)
492 if (doUnittests && mod.isRoot())
494 linkage = LINK.d; // unittests have D linkage
495 s = parseUnitTest(pAttrs);
496 if (*pLastDecl)
497 (*pLastDecl).ddocUnittest = cast(AST.UnitTestDeclaration)s;
499 else
501 // Skip over unittest block by counting { }
502 Loc loc = token.loc;
503 int braces = 0;
504 while (1)
506 nextToken();
507 switch (token.value)
509 case TOK.leftCurly:
510 ++braces;
511 continue;
513 case TOK.rightCurly:
514 if (--braces)
515 continue;
516 nextToken();
517 break;
519 case TOK.endOfFile:
520 /* { */
521 error(loc, "closing `}` of unittest not found before end of file");
522 goto Lerror;
524 default:
525 continue;
527 break;
529 // Workaround 14894. Add an empty unittest declaration to keep
530 // the number of symbols in this scope independent of -unittest.
531 s = new AST.UnitTestDeclaration(loc, token.loc, STC.undefined_, null);
533 break;
535 case TOK.new_:
536 s = parseNew(pAttrs);
537 break;
539 case TOK.colon:
540 case TOK.leftCurly:
541 error("declaration expected, not `%s`", token.toChars());
542 goto Lerror;
544 case TOK.rightCurly:
545 case TOK.endOfFile:
546 if (once)
547 error("declaration expected, not `%s`", token.toChars());
548 return decldefs;
550 case TOK.static_:
552 const next = peekNext();
553 if (next == TOK.this_)
554 s = parseStaticCtor(pAttrs);
555 else if (next == TOK.tilde)
556 s = parseStaticDtor(pAttrs);
557 else if (next == TOK.assert_)
558 s = parseStaticAssert();
559 else if (next == TOK.if_)
561 const Loc loc = token.loc;
562 condition = parseStaticIfCondition();
563 AST.Dsymbols* athen;
564 if (token.value == TOK.colon)
565 athen = parseBlock(pLastDecl);
566 else
568 const lookingForElseSave = lookingForElse;
569 lookingForElse = token.loc;
570 athen = parseBlock(pLastDecl);
571 lookingForElse = lookingForElseSave;
573 AST.Dsymbols* aelse = null;
574 if (token.value == TOK.else_)
576 const elseloc = token.loc;
577 nextToken();
578 aelse = parseBlock(pLastDecl);
579 checkDanglingElse(elseloc);
581 s = new AST.StaticIfDeclaration(loc, condition, athen, aelse);
583 else if (next == TOK.import_)
585 a = parseImport();
586 // keep pLastDecl
588 else if (next == TOK.foreach_ || next == TOK.foreach_reverse_)
590 s = parseForeach!(AST.StaticForeachDeclaration)(token.loc, pLastDecl);
592 else
594 stc = STC.static_;
595 goto Lstc;
597 break;
599 case TOK.const_:
600 if (peekNext() == TOK.leftParenthesis)
601 goto Ldeclaration;
602 stc = STC.const_;
603 goto Lstc;
605 case TOK.immutable_:
606 if (peekNext() == TOK.leftParenthesis)
607 goto Ldeclaration;
608 stc = STC.immutable_;
609 goto Lstc;
611 case TOK.shared_:
613 const next = peekNext();
614 if (next == TOK.leftParenthesis)
615 goto Ldeclaration;
616 if (next == TOK.static_)
618 TOK next2 = peekNext2();
619 if (next2 == TOK.this_)
621 s = parseSharedStaticCtor(pAttrs);
622 break;
624 if (next2 == TOK.tilde)
626 s = parseSharedStaticDtor(pAttrs);
627 break;
630 stc = STC.shared_;
631 goto Lstc;
633 case TOK.inout_:
634 if (peekNext() == TOK.leftParenthesis)
635 goto Ldeclaration;
636 stc = STC.wild;
637 goto Lstc;
639 case TOK.final_:
640 stc = STC.final_;
641 goto Lstc;
643 case TOK.auto_:
644 stc = STC.auto_;
645 goto Lstc;
647 case TOK.scope_:
648 stc = STC.scope_;
649 goto Lstc;
651 case TOK.override_:
652 stc = STC.override_;
653 goto Lstc;
655 case TOK.abstract_:
656 stc = STC.abstract_;
657 goto Lstc;
659 case TOK.synchronized_:
660 stc = STC.synchronized_;
661 goto Lstc;
663 case TOK.nothrow_:
664 stc = STC.nothrow_;
665 goto Lstc;
667 case TOK.pure_:
668 stc = STC.pure_;
669 goto Lstc;
671 case TOK.ref_:
672 stc = STC.ref_;
673 goto Lstc;
675 case TOK.gshared:
676 stc = STC.gshared;
677 goto Lstc;
679 case TOK.at:
681 AST.Expressions* exps = null;
682 stc = parseAttribute(exps);
683 if (stc)
684 goto Lstc; // it's a predefined attribute
685 // no redundant/conflicting check for UDAs
686 pAttrs.udas = AST.UserAttributeDeclaration.concat(pAttrs.udas, exps);
687 goto Lautodecl;
689 Lstc:
690 pAttrs.storageClass = appendStorageClass(pAttrs.storageClass, stc);
691 scdLoc = token.loc;
692 nextToken();
694 Lautodecl:
696 /* Look for auto initializers:
697 * storage_class identifier = initializer;
698 * storage_class identifier(...) = initializer;
700 if (token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign))
702 a = parseAutoDeclarations(getStorageClass!AST(pAttrs), pAttrs.comment);
703 if (a && a.length)
704 *pLastDecl = (*a)[a.length - 1];
705 if (pAttrs.udas)
707 s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
708 pAttrs.udas = null;
710 break;
713 /* Look for return type inference for template functions.
715 Token* tk;
716 if (token.value == TOK.identifier && skipParens(peek(&token), &tk) && skipAttributes(tk, &tk) &&
717 (tk.value == TOK.leftParenthesis || tk.value == TOK.leftCurly || tk.value == TOK.in_ ||
718 tk.value == TOK.out_ || tk.value == TOK.do_ || tk.value == TOK.goesTo ||
719 tk.value == TOK.identifier && tk.ident == Id._body))
721 if (tk.value == TOK.identifier && tk.ident == Id._body)
722 usageOfBodyKeyword();
724 a = parseDeclarations(true, pAttrs, pAttrs.comment);
725 if (a && a.length)
726 *pLastDecl = (*a)[a.length - 1];
727 if (pAttrs.udas)
729 s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
730 pAttrs.udas = null;
732 break;
735 a = parseBlock(pLastDecl, pAttrs);
736 auto stc2 = getStorageClass!AST(pAttrs);
737 if (stc2 != STC.undefined_)
739 s = new AST.StorageClassDeclaration(scdLoc, stc2, a);
741 if (pAttrs.udas)
743 if (s)
745 a = new AST.Dsymbols();
746 a.push(s);
748 s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
749 pAttrs.udas = null;
751 break;
753 case TOK.deprecated_:
755 stc |= STC.deprecated_;
756 if (!parseDeprecatedAttribute(pAttrs.depmsg))
757 goto Lstc;
759 a = parseBlock(pLastDecl, pAttrs);
760 s = new AST.DeprecatedDeclaration(pAttrs.depmsg, a);
761 pAttrs.depmsg = null;
762 break;
764 case TOK.leftBracket:
766 if (peekNext() == TOK.rightBracket)
767 error("empty attribute list is not allowed");
768 error("use `@(attributes)` instead of `[attributes]`");
769 AST.Expressions* exps = parseArguments();
770 // no redundant/conflicting check for UDAs
772 pAttrs.udas = AST.UserAttributeDeclaration.concat(pAttrs.udas, exps);
773 a = parseBlock(pLastDecl, pAttrs);
774 if (pAttrs.udas)
776 s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
777 pAttrs.udas = null;
779 break;
781 case TOK.extern_:
783 if (peekNext() != TOK.leftParenthesis)
785 stc = STC.extern_;
786 goto Lstc;
789 const linkLoc = token.loc;
790 auto res = parseLinkage();
791 if (pAttrs.link != LINK.default_)
793 if (pAttrs.link != res.link)
795 error(token.loc, "conflicting linkage `extern (%s)` and `extern (%s)`", AST.linkageToChars(pAttrs.link), AST.linkageToChars(res.link));
797 else if (res.idents || res.identExps || res.cppmangle != CPPMANGLE.def)
799 // Allow:
800 // extern(C++, foo) extern(C++, bar) void foo();
801 // to be equivalent with:
802 // extern(C++, foo.bar) void foo();
803 // Allow also:
804 // extern(C++, "ns") extern(C++, class) struct test {}
805 // extern(C++, class) extern(C++, "ns") struct test {}
807 else
808 error("redundant linkage `extern (%s)`", AST.linkageToChars(pAttrs.link));
810 pAttrs.link = res.link;
811 this.linkage = res.link;
812 this.linkLoc = linkLoc;
813 a = parseBlock(pLastDecl, pAttrs);
814 if (res.idents)
816 assert(res.link == LINK.cpp);
817 assert(res.idents.length);
818 for (size_t i = res.idents.length; i;)
820 Identifier id = (*res.idents)[--i];
821 if (s)
823 a = new AST.Dsymbols();
824 a.push(s);
826 s = new AST.Nspace(linkLoc, id, null, a);
828 pAttrs.link = LINK.default_;
830 else if (res.identExps)
832 assert(res.link == LINK.cpp);
833 assert(res.identExps.length);
834 for (size_t i = res.identExps.length; i;)
836 AST.Expression exp = (*res.identExps)[--i];
837 if (s)
839 a = new AST.Dsymbols();
840 a.push(s);
842 s = new AST.CPPNamespaceDeclaration(linkLoc, exp, a);
844 pAttrs.link = LINK.default_;
846 else if (res.cppmangle != CPPMANGLE.def)
848 assert(res.link == LINK.cpp);
849 s = new AST.CPPMangleDeclaration(linkLoc, res.cppmangle, a);
851 else if (pAttrs.link != LINK.default_)
853 s = new AST.LinkDeclaration(linkLoc, pAttrs.link, a);
854 pAttrs.link = LINK.default_;
856 break;
859 case TOK.private_:
860 prot = AST.Visibility.Kind.private_;
861 goto Lprot;
863 case TOK.package_:
864 prot = AST.Visibility.Kind.package_;
865 goto Lprot;
867 case TOK.protected_:
868 prot = AST.Visibility.Kind.protected_;
869 goto Lprot;
871 case TOK.public_:
872 prot = AST.Visibility.Kind.public_;
873 goto Lprot;
875 case TOK.export_:
876 prot = AST.Visibility.Kind.export_;
877 goto Lprot;
878 Lprot:
880 if (pAttrs.visibility.kind != AST.Visibility.Kind.undefined)
882 if (pAttrs.visibility.kind != prot)
883 error(token.loc, "conflicting visibility attribute `%s` and `%s`", AST.visibilityToChars(pAttrs.visibility.kind), AST.visibilityToChars(prot));
884 else
885 error("redundant visibility attribute `%s`", AST.visibilityToChars(prot));
887 pAttrs.visibility.kind = prot;
888 const attrloc = token.loc;
890 nextToken();
892 // optional qualified package identifier to bind
893 // visibility to
894 Identifier[] pkg_prot_idents;
895 if (pAttrs.visibility.kind == AST.Visibility.Kind.package_ && token.value == TOK.leftParenthesis)
897 pkg_prot_idents = parseQualifiedIdentifier("protection package");
898 if (pkg_prot_idents)
899 check(TOK.rightParenthesis);
900 else
902 while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
903 nextToken();
904 nextToken();
905 break;
909 a = parseBlock(pLastDecl, pAttrs);
910 if (pAttrs.visibility.kind != AST.Visibility.Kind.undefined)
912 if (pAttrs.visibility.kind == AST.Visibility.Kind.package_ && pkg_prot_idents)
913 s = new AST.VisibilityDeclaration(attrloc, pkg_prot_idents, a);
914 else
915 s = new AST.VisibilityDeclaration(attrloc, pAttrs.visibility, a);
917 pAttrs.visibility = AST.Visibility(AST.Visibility.Kind.undefined);
919 break;
921 case TOK.align_:
923 const attrLoc = token.loc;
925 nextToken();
927 AST.Expression e = null; // default
928 if (token.value == TOK.leftParenthesis)
930 nextToken();
931 e = parseAssignExp();
932 check(TOK.rightParenthesis);
935 if (pAttrs.setAlignment)
937 if (e)
938 error("redundant alignment attribute `align(%s)`", e.toChars());
939 else
940 error("redundant alignment attribute `align`");
943 pAttrs.setAlignment = true;
944 pAttrs.ealign = e;
945 a = parseBlock(pLastDecl, pAttrs);
946 if (pAttrs.setAlignment)
948 s = new AST.AlignDeclaration(attrLoc, pAttrs.ealign, a);
949 pAttrs.setAlignment = false;
950 pAttrs.ealign = null;
952 break;
954 case TOK.pragma_:
956 AST.Expressions* args = null;
957 const loc = token.loc;
959 nextToken();
960 check(TOK.leftParenthesis);
961 if (token.value != TOK.identifier)
963 error("`pragma(identifier)` expected");
964 goto Lerror;
966 Identifier ident = token.ident;
967 nextToken();
968 if (token.value == TOK.comma && peekNext() != TOK.rightParenthesis)
969 args = parseArguments(); // pragma(identifier, args...)
970 else
971 check(TOK.rightParenthesis); // pragma(identifier)
973 AST.Dsymbols* a2 = null;
974 if (token.value == TOK.semicolon)
976 /* https://issues.dlang.org/show_bug.cgi?id=2354
977 * Accept single semicolon as an empty
978 * DeclarationBlock following attribute.
980 * Attribute DeclarationBlock
981 * Pragma DeclDef
984 nextToken();
986 else
987 a2 = parseBlock(pLastDecl);
988 s = new AST.PragmaDeclaration(loc, ident, args, a2);
989 break;
991 case TOK.debug_:
992 startloc = token.loc;
993 nextToken();
994 if (token.value == TOK.assign)
996 s = parseDebugSpecification();
997 break;
999 condition = parseDebugCondition();
1000 goto Lcondition;
1002 case TOK.version_:
1003 startloc = token.loc;
1004 nextToken();
1005 if (token.value == TOK.assign)
1007 s = parseVersionSpecification();
1008 break;
1010 condition = parseVersionCondition();
1011 goto Lcondition;
1013 Lcondition:
1015 AST.Dsymbols* athen;
1016 if (token.value == TOK.colon)
1017 athen = parseBlock(pLastDecl);
1018 else
1020 const lookingForElseSave = lookingForElse;
1021 lookingForElse = token.loc;
1022 athen = parseBlock(pLastDecl);
1023 lookingForElse = lookingForElseSave;
1025 AST.Dsymbols* aelse = null;
1026 if (token.value == TOK.else_)
1028 const elseloc = token.loc;
1029 nextToken();
1030 aelse = parseBlock(pLastDecl);
1031 checkDanglingElse(elseloc);
1033 s = new AST.ConditionalDeclaration(startloc, condition, athen, aelse);
1034 break;
1036 case TOK.semicolon:
1037 // empty declaration
1038 //error("empty declaration");
1039 nextToken();
1040 continue;
1042 default:
1043 error("declaration expected, not `%s`", token.toChars());
1044 Lerror:
1045 while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
1046 nextToken();
1047 nextToken();
1048 s = null;
1049 continue;
1052 if (s)
1054 if (!s.isAttribDeclaration())
1055 *pLastDecl = s;
1056 decldefs.push(s);
1057 addComment(s, pAttrs.comment);
1059 else if (a && a.length)
1061 decldefs.append(a);
1064 while (!once);
1066 linkage = linksave;
1068 return decldefs;
1071 /*****************************************
1072 * Parse auto declarations of the form:
1073 * storageClass ident = init, ident = init, ... ;
1074 * and return the array of them.
1075 * Starts with token on the first ident.
1076 * Ends with scanner past closing ';'
1078 private AST.Dsymbols* parseAutoDeclarations(StorageClass storageClass, const(char)* comment)
1080 //printf("parseAutoDeclarations\n");
1081 auto a = new AST.Dsymbols();
1083 while (1)
1085 const loc = token.loc;
1086 Identifier ident = token.ident;
1087 nextToken(); // skip over ident
1089 AST.TemplateParameters* tpl = null;
1090 if (token.value == TOK.leftParenthesis)
1091 tpl = parseTemplateParameterList();
1093 check(TOK.assign); // skip over '='
1094 AST.Initializer _init = parseInitializer();
1095 auto v = new AST.VarDeclaration(loc, null, ident, _init, storageClass);
1097 AST.Dsymbol s = v;
1098 if (tpl)
1100 auto a2 = new AST.Dsymbols();
1101 a2.push(v);
1102 auto tempdecl = new AST.TemplateDeclaration(loc, ident, tpl, null, a2, 0);
1103 s = tempdecl;
1105 a.push(s);
1106 switch (token.value)
1108 case TOK.semicolon:
1109 nextToken();
1110 addComment(s, comment);
1111 break;
1113 case TOK.comma:
1114 nextToken();
1115 if (!(token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign)))
1117 error("identifier expected following comma");
1118 break;
1120 addComment(s, comment);
1121 continue;
1123 default:
1124 error("semicolon expected following auto declaration, not `%s`", token.toChars());
1125 break;
1127 break;
1129 return a;
1132 /********************************************
1133 * Parse declarations after an align, visibility, or extern decl.
1135 private AST.Dsymbols* parseBlock(AST.Dsymbol* pLastDecl, PrefixAttributes!AST* pAttrs = null)
1137 AST.Dsymbols* a = null;
1139 //printf("parseBlock()\n");
1140 switch (token.value)
1142 case TOK.semicolon:
1143 error("declaration expected following attribute, not `;`");
1144 nextToken();
1145 break;
1147 case TOK.endOfFile:
1148 error("declaration expected following attribute, not end of file");
1149 break;
1151 case TOK.leftCurly:
1153 const lcLoc = token.loc;
1154 const lookingForElseSave = lookingForElse;
1155 lookingForElse = Loc();
1157 nextToken();
1158 a = parseDeclDefs(0, pLastDecl);
1159 if (token.value != TOK.rightCurly)
1161 /* left curly brace */
1162 error("matching `}` expected, not `%s`", token.toChars());
1163 eSink.errorSupplemental(lcLoc, "unmatched `{`");
1165 else
1166 nextToken();
1167 lookingForElse = lookingForElseSave;
1168 break;
1170 case TOK.colon:
1171 nextToken();
1172 a = parseDeclDefs(0, pLastDecl); // grab declarations up to closing curly bracket
1173 break;
1175 default:
1176 a = parseDeclDefs(1, pLastDecl, pAttrs);
1177 break;
1179 return a;
1183 * Provide an error message if `added` contains storage classes which are
1184 * redundant with those in `orig`; otherwise, return the combination.
1186 * Params:
1187 * orig = The already applied storage class.
1188 * added = The new storage class to add to `orig`.
1190 * Returns:
1191 * The combination of both storage classes (`orig | added`).
1193 private StorageClass appendStorageClass(StorageClass orig, StorageClass added)
1195 void checkConflictSTCGroup(bool at = false)(StorageClass group)
1197 if (added & group && orig & group & ((orig & group) - 1))
1198 error(
1199 at ? "conflicting attribute `@%s`"
1200 : "conflicting attribute `%s`",
1201 token.toChars());
1204 if (orig & added)
1206 OutBuffer buf;
1207 AST.stcToBuffer(buf, added);
1208 error("redundant attribute `%s`", buf.peekChars());
1209 return orig | added;
1212 const Redundant = (STC.const_ | STC.scope_ | STC.ref_);
1213 orig |= added;
1215 if ((orig & STC.in_) && (added & Redundant))
1217 if (added & STC.const_)
1218 error("attribute `const` is redundant with previously-applied `in`");
1219 else if (compileEnv.previewIn)
1221 error("attribute `%s` is redundant with previously-applied `in`",
1222 (orig & STC.scope_) ? "scope".ptr : "ref".ptr);
1224 else if (added & STC.ref_)
1226 // accept for legacy compatibility
1227 //deprecation("using `in ref` is deprecated, use `-preview=in` and `in` instead");
1229 else
1230 error("attribute `scope` cannot be applied with `in`, use `-preview=in` instead");
1231 return orig;
1234 if ((added & STC.in_) && (orig & Redundant))
1236 if (orig & STC.const_)
1237 error("attribute `in` cannot be added after `const`: remove `const`");
1238 else if (compileEnv.previewIn)
1240 // Windows `printf` does not support `%1$s`
1241 const(char*) stc_str = (orig & STC.scope_) ? "scope".ptr : "ref".ptr;
1242 error(token.loc, "attribute `in` cannot be added after `%s`: remove `%s`",
1243 stc_str, stc_str);
1245 else if (orig & STC.ref_)
1247 // accept for legacy compatibility
1248 //deprecation("using `in ref` is deprecated, use `-preview=in` and `in` instead");
1250 else
1251 error("attribute `in` cannot be added after `scope`: remove `scope` and use `-preview=in`");
1252 return orig;
1255 checkConflictSTCGroup(STC.const_ | STC.immutable_ | STC.manifest);
1256 checkConflictSTCGroup(STC.gshared | STC.shared_);
1257 checkConflictSTCGroup!true(STC.safeGroup);
1259 return orig;
1262 /***********************************************
1263 * Parse attribute(s), lexer is on '@'.
1265 * Attributes can be builtin (e.g. `@safe`, `@nogc`, etc...),
1266 * or be user-defined (UDAs). In the former case, we return the storage
1267 * class via the return value, while in thelater case we return `0`
1268 * and set `pudas`.
1270 * Params:
1271 * pudas = An array of UDAs to append to
1273 * Returns:
1274 * If the attribute is builtin, the return value will be non-zero.
1275 * Otherwise, 0 is returned, and `pudas` will be appended to.
1277 private StorageClass parseAttribute(ref AST.Expressions* udas)
1279 nextToken();
1280 if (token.value == TOK.identifier)
1282 // If we find a builtin attribute, we're done, return immediately.
1283 if (StorageClass stc = isBuiltinAtAttribute(token.ident))
1284 return stc;
1286 // Allow identifier, template instantiation, or function call
1287 // for `@Argument` (single UDA) form.
1288 AST.Expression exp = parsePrimaryExp();
1289 if (token.value == TOK.leftParenthesis)
1291 const loc = token.loc;
1292 AST.Expressions* args = new AST.Expressions();
1293 AST.Identifiers* names = new AST.Identifiers();
1294 parseNamedArguments(args, names);
1295 exp = new AST.CallExp(loc, exp, args, names);
1298 if (udas is null)
1299 udas = new AST.Expressions();
1300 udas.push(exp);
1301 return 0;
1304 AST.Expression templateArgToExp(RootObject o, const ref Loc loc)
1306 switch (o.dyncast)
1308 case DYNCAST.expression:
1309 return cast(AST.Expression) o;
1310 case DYNCAST.type:
1311 return new AST.TypeExp(loc, cast(AST.Type)o);
1312 default:
1313 assert(0);
1317 if (token.value == TOK.leftParenthesis)
1319 // Multi-UDAs ( `@( ArgumentList )`) form, concatenate with existing
1320 if (peekNext() == TOK.rightParenthesis)
1321 error("empty attribute list is not allowed");
1323 if (udas is null)
1324 udas = new AST.Expressions();
1325 auto args = parseTemplateArgumentList();
1326 foreach (arg; *args)
1327 udas.push(templateArgToExp(arg, token.loc));
1328 return 0;
1331 if (auto o = parseTemplateSingleArgument())
1333 if (udas is null)
1334 udas = new AST.Expressions();
1335 udas.push(templateArgToExp(o, token.loc));
1336 return 0;
1339 if (token.isKeyword())
1340 error("`%s` is a keyword, not an `@` attribute", token.toChars());
1341 else
1342 error("`@identifier` or `@(ArgumentList)` expected, not `@%s`", token.toChars());
1344 return 0;
1347 /***********************************************
1348 * Parse const/immutable/shared/inout/nothrow/pure postfix
1350 private StorageClass parsePostfix(StorageClass storageClass, AST.Expressions** pudas)
1352 while (1)
1354 StorageClass stc;
1355 switch (token.value)
1357 case TOK.const_:
1358 stc = STC.const_;
1359 break;
1361 case TOK.immutable_:
1362 stc = STC.immutable_;
1363 break;
1365 case TOK.shared_:
1366 stc = STC.shared_;
1367 break;
1369 case TOK.inout_:
1370 stc = STC.wild;
1371 break;
1373 case TOK.nothrow_:
1374 stc = STC.nothrow_;
1375 break;
1377 case TOK.pure_:
1378 stc = STC.pure_;
1379 break;
1381 case TOK.return_:
1382 stc = STC.return_;
1383 if (peekNext() == TOK.scope_)
1384 stc |= STC.returnScope; // recognize `return scope`
1385 break;
1387 case TOK.scope_:
1388 stc = STC.scope_;
1389 break;
1391 case TOK.at:
1393 AST.Expressions* udas = null;
1394 stc = parseAttribute(udas);
1395 if (udas)
1397 if (pudas)
1398 *pudas = AST.UserAttributeDeclaration.concat(*pudas, udas);
1399 else
1401 // Disallow:
1402 // void function() @uda fp;
1403 // () @uda { return 1; }
1404 error("user-defined attributes cannot appear as postfixes");
1406 continue;
1408 break;
1410 default:
1411 Token* tk;
1412 if (skipAttributes(&token, &tk) && tk.ptr != token.ptr ||
1413 token.value == TOK.static_ || token.value == TOK.extern_)
1415 error("`%s` token is not allowed in postfix position",
1416 Token.toChars(token.value));
1417 nextToken();
1418 continue;
1420 return storageClass;
1422 storageClass = appendStorageClass(storageClass, stc);
1423 nextToken();
1427 private StorageClass parseTypeCtor()
1429 StorageClass storageClass = STC.undefined_;
1431 while (1)
1433 if (peekNext() == TOK.leftParenthesis)
1434 return storageClass;
1436 StorageClass stc;
1437 switch (token.value)
1439 case TOK.const_:
1440 stc = STC.const_;
1441 break;
1443 case TOK.immutable_:
1444 stc = STC.immutable_;
1445 break;
1447 case TOK.shared_:
1448 stc = STC.shared_;
1449 break;
1451 case TOK.inout_:
1452 stc = STC.wild;
1453 break;
1455 default:
1456 return storageClass;
1458 storageClass = appendStorageClass(storageClass, stc);
1459 nextToken();
1463 /**************************************
1464 * Parse constraint.
1465 * Constraint is of the form:
1466 * if ( ConstraintExpression )
1468 private AST.Expression parseConstraint()
1470 AST.Expression e = null;
1471 if (token.value == TOK.if_)
1473 nextToken(); // skip over 'if'
1474 check(TOK.leftParenthesis);
1475 e = parseExpression();
1476 check(TOK.rightParenthesis);
1478 return e;
1481 /**************************************
1482 * Parse a TemplateDeclaration.
1484 private AST.TemplateDeclaration parseTemplateDeclaration(bool ismixin = false)
1486 AST.TemplateDeclaration tempdecl;
1487 Identifier id;
1488 AST.TemplateParameters* tpl;
1489 AST.Dsymbols* decldefs;
1490 AST.Expression constraint = null;
1491 const loc = token.loc;
1493 nextToken();
1494 if (token.value != TOK.identifier)
1496 error("identifier expected following `template`");
1497 goto Lerr;
1499 id = token.ident;
1500 nextToken();
1501 tpl = parseTemplateParameterList();
1502 if (!tpl)
1503 goto Lerr;
1505 constraint = parseConstraint();
1507 if (token.value != TOK.leftCurly)
1509 error("`{` expected after template parameter list, not `%s`", token.toChars()); /* } */
1510 goto Lerr;
1512 decldefs = parseBlock(null);
1514 tempdecl = new AST.TemplateDeclaration(loc, id, tpl, constraint, decldefs, ismixin);
1515 return tempdecl;
1517 Lerr:
1518 return null;
1521 /******************************************
1522 * Parse template parameter list.
1523 * Input:
1524 * flag 0: parsing "( list )"
1525 * 1: parsing non-empty "list $(RPAREN)"
1527 private AST.TemplateParameters* parseTemplateParameterList(int flag = 0)
1529 auto tpl = new AST.TemplateParameters();
1531 if (!flag && token.value != TOK.leftParenthesis)
1533 error("parenthesized template parameter list expected following template identifier");
1534 goto Lerr;
1536 nextToken();
1538 // Get array of TemplateParameters
1539 if (flag || token.value != TOK.rightParenthesis)
1541 while (token.value != TOK.rightParenthesis)
1543 AST.TemplateParameter tp;
1544 Loc loc;
1545 Identifier tp_ident = null;
1546 AST.Type tp_spectype = null;
1547 AST.Type tp_valtype = null;
1548 AST.Type tp_defaulttype = null;
1549 AST.Expression tp_specvalue = null;
1550 AST.Expression tp_defaultvalue = null;
1552 // Get TemplateParameter
1554 // First, look ahead to see if it is a TypeParameter or a ValueParameter
1555 const tv = peekNext();
1556 if (token.value == TOK.alias_)
1558 // AliasParameter
1559 nextToken();
1560 loc = token.loc; // todo
1561 AST.Type spectype = null;
1562 if (isDeclaration(&token, NeedDeclaratorId.must, TOK.reserved, null))
1564 spectype = parseType(&tp_ident);
1566 else
1568 if (token.value != TOK.identifier)
1570 error("identifier expected for template `alias` parameter");
1571 goto Lerr;
1573 tp_ident = token.ident;
1574 nextToken();
1576 RootObject spec = null;
1577 if (token.value == TOK.colon) // : Type
1579 nextToken();
1580 if (isDeclaration(&token, NeedDeclaratorId.no, TOK.reserved, null))
1581 spec = parseType();
1582 else
1583 spec = parseCondExp();
1585 RootObject def = null;
1586 if (token.value == TOK.assign) // = Type
1588 nextToken();
1589 if (isDeclaration(&token, NeedDeclaratorId.no, TOK.reserved, null))
1590 def = parseType();
1591 else
1592 def = parseCondExp();
1594 tp = new AST.TemplateAliasParameter(loc, tp_ident, spectype, spec, def);
1596 else if (tv == TOK.colon || tv == TOK.assign || tv == TOK.comma || tv == TOK.rightParenthesis)
1598 // TypeParameter
1599 if (token.value != TOK.identifier)
1601 error("identifier expected for template type parameter");
1602 goto Lerr;
1604 loc = token.loc;
1605 tp_ident = token.ident;
1606 nextToken();
1607 if (token.value == TOK.colon) // : Type
1609 nextToken();
1610 tp_spectype = parseType();
1612 if (token.value == TOK.assign) // = Type
1614 nextToken();
1615 tp_defaulttype = parseType();
1617 tp = new AST.TemplateTypeParameter(loc, tp_ident, tp_spectype, tp_defaulttype);
1619 else if (token.value == TOK.identifier && tv == TOK.dotDotDot)
1621 // ident...
1622 loc = token.loc;
1623 tp_ident = token.ident;
1624 nextToken();
1625 nextToken();
1626 tp = new AST.TemplateTupleParameter(loc, tp_ident);
1628 else if (token.value == TOK.this_)
1630 // ThisParameter
1631 nextToken();
1632 if (token.value != TOK.identifier)
1634 error("identifier expected for template `this` parameter");
1635 goto Lerr;
1637 loc = token.loc;
1638 tp_ident = token.ident;
1639 nextToken();
1640 if (token.value == TOK.colon) // : Type
1642 nextToken();
1643 tp_spectype = parseType();
1645 if (token.value == TOK.assign) // = Type
1647 nextToken();
1648 tp_defaulttype = parseType();
1650 tp = new AST.TemplateThisParameter(loc, tp_ident, tp_spectype, tp_defaulttype);
1652 else
1654 // ValueParameter
1655 loc = token.loc; // todo
1656 tp_valtype = parseType(&tp_ident);
1657 if (!tp_ident)
1659 error("identifier expected for template value parameter");
1660 tp_ident = Identifier.idPool("error");
1662 if (token.value == TOK.colon) // : CondExpression
1664 nextToken();
1665 tp_specvalue = parseCondExp();
1667 if (token.value == TOK.assign) // = CondExpression
1669 nextToken();
1670 tp_defaultvalue = parseDefaultInitExp();
1672 tp = new AST.TemplateValueParameter(loc, tp_ident, tp_valtype, tp_specvalue, tp_defaultvalue);
1674 tpl.push(tp);
1675 if (token.value != TOK.comma)
1676 break;
1677 nextToken();
1680 check(TOK.rightParenthesis);
1682 Lerr:
1683 return tpl;
1686 /******************************************
1687 * Parse template mixin.
1688 * mixin Foo;
1689 * mixin Foo!(args);
1690 * mixin a.b.c!(args).Foo!(args);
1691 * mixin Foo!(args) identifier;
1692 * mixin typeof(expr).identifier!(args);
1694 private AST.Dsymbol parseMixin()
1696 AST.TemplateMixin tm;
1697 Identifier id;
1698 AST.Objects* tiargs;
1700 //printf("parseMixin()\n");
1701 const locMixin = token.loc;
1702 nextToken(); // skip 'mixin'
1704 auto loc = token.loc;
1705 AST.TypeQualified tqual = null;
1706 if (token.value == TOK.dot)
1708 id = Id.empty;
1710 else
1712 if (token.value == TOK.typeof_)
1714 tqual = parseTypeof();
1715 check(TOK.dot);
1717 if (token.value != TOK.identifier)
1719 error("identifier expected, not `%s`", token.toChars());
1720 id = Id.empty;
1722 else
1723 id = token.ident;
1724 nextToken();
1727 while (1)
1729 tiargs = null;
1730 if (token.value == TOK.not)
1732 tiargs = parseTemplateArguments();
1735 if (tiargs && token.value == TOK.dot)
1737 auto tempinst = new AST.TemplateInstance(loc, id, tiargs);
1738 if (!tqual)
1739 tqual = new AST.TypeInstance(loc, tempinst);
1740 else
1741 tqual.addInst(tempinst);
1742 tiargs = null;
1744 else
1746 if (!tqual)
1747 tqual = new AST.TypeIdentifier(loc, id);
1748 else
1749 tqual.addIdent(id);
1752 if (token.value != TOK.dot)
1753 break;
1755 nextToken();
1756 if (token.value != TOK.identifier)
1758 error("identifier expected following `.` instead of `%s`", token.toChars());
1759 break;
1761 loc = token.loc;
1762 id = token.ident;
1763 nextToken();
1766 id = null;
1767 if (token.value == TOK.identifier)
1769 id = token.ident;
1770 nextToken();
1773 tm = new AST.TemplateMixin(locMixin, id, tqual, tiargs);
1774 if (token.value != TOK.semicolon)
1775 error("`;` expected after `mixin`");
1776 nextToken();
1778 return tm;
1781 /******************************************
1782 * Parse template arguments.
1783 * Input:
1784 * current token is opening '!'
1785 * Output:
1786 * current token is one after closing '$(RPAREN)'
1788 private AST.Objects* parseTemplateArguments()
1790 AST.Objects* tiargs;
1792 nextToken();
1793 if (token.value == TOK.leftParenthesis)
1795 // ident!(template_arguments)
1796 tiargs = parseTemplateArgumentList();
1798 else
1800 // ident!template_argument
1801 RootObject o = parseTemplateSingleArgument();
1802 if (!o)
1804 error("template argument expected following `!`");
1806 else
1808 tiargs = new AST.Objects();
1809 tiargs.push(o);
1812 if (token.value == TOK.not)
1814 TOK tok = peekNext();
1815 if (tok != TOK.is_ && tok != TOK.in_)
1817 error("multiple ! arguments are not allowed");
1818 Lagain:
1819 nextToken();
1820 if (token.value == TOK.leftParenthesis)
1821 parseTemplateArgumentList();
1822 else
1823 parseTemplateSingleArgument();
1824 if (token.value == TOK.not && (tok = peekNext()) != TOK.is_ && tok != TOK.in_)
1825 goto Lagain;
1828 return tiargs;
1831 /******************************************
1832 * Parse template argument list.
1833 * Input:
1834 * current token is opening '$(LPAREN)',
1835 * or ',' for __traits
1836 * Output:
1837 * current token is one after closing '$(RPAREN)'
1839 private AST.Objects* parseTemplateArgumentList()
1841 //printf("Parser::parseTemplateArgumentList()\n");
1842 auto tiargs = new AST.Objects();
1843 TOK endtok = TOK.rightParenthesis;
1844 assert(token.value == TOK.leftParenthesis || token.value == TOK.comma);
1845 nextToken();
1847 // Get TemplateArgumentList
1848 while (token.value != endtok)
1850 tiargs.push(parseTypeOrAssignExp());
1851 if (token.value != TOK.comma)
1852 break;
1853 nextToken();
1855 check(endtok, "template argument list");
1856 return tiargs;
1859 /***************************************
1860 * Parse a Type or an Expression
1861 * Returns:
1862 * RootObject representing the AST
1864 RootObject parseTypeOrAssignExp(TOK endtoken = TOK.reserved)
1866 return isDeclaration(&token, NeedDeclaratorId.no, endtoken, null)
1867 ? parseType() // argument is a type
1868 : parseAssignExp(); // argument is an expression
1871 /*****************************
1872 * Parse single template argument, to support the syntax:
1873 * foo!arg
1874 * Input:
1875 * current token is the arg
1876 * Returns: An AST.Type, AST.Expression, or `null` on error
1878 private RootObject parseTemplateSingleArgument()
1880 //printf("parseTemplateSingleArgument()\n");
1881 AST.Type ta;
1882 switch (token.value)
1884 case TOK.identifier:
1885 ta = new AST.TypeIdentifier(token.loc, token.ident);
1886 goto LabelX;
1888 case TOK.vector:
1889 ta = parseVector();
1890 goto LabelX;
1892 case TOK.void_:
1893 ta = AST.Type.tvoid;
1894 goto LabelX;
1896 case TOK.int8:
1897 ta = AST.Type.tint8;
1898 goto LabelX;
1900 case TOK.uns8:
1901 ta = AST.Type.tuns8;
1902 goto LabelX;
1904 case TOK.int16:
1905 ta = AST.Type.tint16;
1906 goto LabelX;
1908 case TOK.uns16:
1909 ta = AST.Type.tuns16;
1910 goto LabelX;
1912 case TOK.int32:
1913 ta = AST.Type.tint32;
1914 goto LabelX;
1916 case TOK.uns32:
1917 ta = AST.Type.tuns32;
1918 goto LabelX;
1920 case TOK.int64:
1921 ta = AST.Type.tint64;
1922 goto LabelX;
1924 case TOK.uns64:
1925 ta = AST.Type.tuns64;
1926 goto LabelX;
1928 case TOK.int128:
1929 ta = AST.Type.tint128;
1930 goto LabelX;
1932 case TOK.uns128:
1933 ta = AST.Type.tuns128;
1934 goto LabelX;
1936 case TOK.float32:
1937 ta = AST.Type.tfloat32;
1938 goto LabelX;
1940 case TOK.float64:
1941 ta = AST.Type.tfloat64;
1942 goto LabelX;
1944 case TOK.float80:
1945 ta = AST.Type.tfloat80;
1946 goto LabelX;
1948 case TOK.imaginary32:
1949 ta = AST.Type.timaginary32;
1950 goto LabelX;
1952 case TOK.imaginary64:
1953 ta = AST.Type.timaginary64;
1954 goto LabelX;
1956 case TOK.imaginary80:
1957 ta = AST.Type.timaginary80;
1958 goto LabelX;
1960 case TOK.complex32:
1961 ta = AST.Type.tcomplex32;
1962 goto LabelX;
1964 case TOK.complex64:
1965 ta = AST.Type.tcomplex64;
1966 goto LabelX;
1968 case TOK.complex80:
1969 ta = AST.Type.tcomplex80;
1970 goto LabelX;
1972 case TOK.bool_:
1973 ta = AST.Type.tbool;
1974 goto LabelX;
1976 case TOK.char_:
1977 ta = AST.Type.tchar;
1978 goto LabelX;
1980 case TOK.wchar_:
1981 ta = AST.Type.twchar;
1982 goto LabelX;
1984 case TOK.dchar_:
1985 ta = AST.Type.tdchar;
1986 goto LabelX;
1987 LabelX:
1988 nextToken();
1989 return ta;
1991 case TOK.int32Literal:
1992 case TOK.uns32Literal:
1993 case TOK.int64Literal:
1994 case TOK.uns64Literal:
1995 case TOK.int128Literal:
1996 case TOK.uns128Literal:
1997 case TOK.float32Literal:
1998 case TOK.float64Literal:
1999 case TOK.float80Literal:
2000 case TOK.imaginary32Literal:
2001 case TOK.imaginary64Literal:
2002 case TOK.imaginary80Literal:
2003 case TOK.null_:
2004 case TOK.true_:
2005 case TOK.false_:
2006 case TOK.charLiteral:
2007 case TOK.wcharLiteral:
2008 case TOK.dcharLiteral:
2009 case TOK.string_:
2010 case TOK.hexadecimalString:
2011 case TOK.file:
2012 case TOK.fileFullPath:
2013 case TOK.line:
2014 case TOK.moduleString:
2015 case TOK.functionString:
2016 case TOK.prettyFunction:
2017 case TOK.this_:
2019 // Template argument is an expression
2020 return parsePrimaryExp();
2022 default:
2023 return null;
2027 /**********************************
2028 * Parse a static assertion.
2029 * Current token is 'static'.
2031 private AST.StaticAssert parseStaticAssert()
2033 const loc = token.loc;
2034 AST.Expression exp;
2035 AST.Expressions* msg = null;
2037 //printf("parseStaticAssert()\n");
2038 nextToken();
2039 nextToken();
2040 check(TOK.leftParenthesis);
2041 exp = parseAssignExp();
2042 if (token.value == TOK.comma)
2044 if (peekNext() == TOK.rightParenthesis)
2046 nextToken(); // consume `,`
2047 nextToken(); // consume `)`
2049 else
2050 msg = parseArguments();
2052 else
2053 check(TOK.rightParenthesis);
2054 check(TOK.semicolon, "static assert");
2055 return new AST.StaticAssert(loc, exp, msg);
2058 /***********************************
2059 * Parse typeof(expression).
2060 * Current token is on the 'typeof'.
2062 private AST.TypeQualified parseTypeof()
2064 AST.TypeQualified t;
2065 const loc = token.loc;
2067 nextToken();
2068 check(TOK.leftParenthesis);
2069 if (token.value == TOK.return_) // typeof(return)
2071 nextToken();
2072 t = new AST.TypeReturn(loc);
2074 else
2076 AST.Expression exp = parseExpression(); // typeof(expression)
2077 t = new AST.TypeTypeof(loc, exp);
2079 check(TOK.rightParenthesis);
2080 return t;
2083 /***********************************
2084 * Parse __vector(type).
2085 * Current token is on the '__vector'.
2087 private AST.Type parseVector()
2089 nextToken();
2090 check(TOK.leftParenthesis);
2091 AST.Type tb = parseType();
2092 check(TOK.rightParenthesis);
2093 return new AST.TypeVector(tb);
2096 /***********************************
2097 * Parse:
2098 * extern (linkage)
2099 * extern (C++, namespaces)
2100 * extern (C++, "namespace", "namespaces", ...)
2101 * extern (C++, (StringExp))
2102 * The parser is on the 'extern' token.
2104 private ParsedLinkage!(AST) parseLinkage()
2106 ParsedLinkage!(AST) result;
2107 nextToken();
2108 assert(token.value == TOK.leftParenthesis);
2109 nextToken();
2110 ParsedLinkage!(AST) returnLinkage(LINK link)
2112 check(TOK.rightParenthesis);
2113 result.link = link;
2114 return result;
2116 ParsedLinkage!(AST) invalidLinkage()
2118 error("valid linkage identifiers are `D`, `C`, `C++`, `Objective-C`, `Windows`, `System`");
2119 return returnLinkage(LINK.d);
2122 if (token.value != TOK.identifier)
2123 return returnLinkage(LINK.d);
2125 Identifier id = token.ident;
2126 nextToken();
2127 if (id == Id.Windows)
2128 return returnLinkage(LINK.windows);
2129 else if (id == Id.D)
2130 return returnLinkage(LINK.d);
2131 else if (id == Id.System)
2132 return returnLinkage(LINK.system);
2133 else if (id == Id.Objective) // Looking for tokens "Objective-C"
2135 if (token.value != TOK.min)
2136 return invalidLinkage();
2138 nextToken();
2139 if (token.ident != Id.C)
2140 return invalidLinkage();
2142 nextToken();
2143 return returnLinkage(LINK.objc);
2145 else if (id != Id.C)
2146 return invalidLinkage();
2148 if (token.value != TOK.plusPlus)
2149 return returnLinkage(LINK.c);
2151 nextToken();
2152 if (token.value != TOK.comma) // , namespaces or class or struct
2153 return returnLinkage(LINK.cpp);
2155 nextToken();
2157 if (token.value == TOK.rightParenthesis)
2158 return returnLinkage(LINK.cpp); // extern(C++,)
2160 if (token.value == TOK.class_ || token.value == TOK.struct_)
2162 result.cppmangle = token.value == TOK.class_ ? CPPMANGLE.asClass : CPPMANGLE.asStruct;
2163 nextToken();
2165 else if (token.value == TOK.identifier) // named scope namespace
2167 result.idents = new AST.Identifiers();
2168 while (1)
2170 Identifier idn = token.ident;
2171 result.idents.push(idn);
2172 nextToken();
2173 if (token.value == TOK.dot)
2175 nextToken();
2176 if (token.value == TOK.identifier)
2177 continue;
2178 error("identifier expected for C++ namespace");
2179 result.idents = null; // error occurred, invalidate list of elements.
2181 break;
2184 else // non-scoped StringExp namespace
2186 result.identExps = new AST.Expressions();
2187 while (1)
2189 result.identExps.push(parseCondExp());
2190 if (token.value != TOK.comma)
2191 break;
2192 nextToken();
2193 // Allow trailing commas as done for argument lists, arrays, ...
2194 if (token.value == TOK.rightParenthesis)
2195 break;
2198 return returnLinkage(LINK.cpp);
2201 /***********************************
2202 * Parse ident1.ident2.ident3
2204 * Params:
2205 * entity = what qualified identifier is expected to resolve into.
2206 * Used only for better error message
2208 * Returns:
2209 * array of identifiers with actual qualified one stored last
2211 private Identifier[] parseQualifiedIdentifier(const(char)* entity)
2213 Identifier[] qualified;
2217 nextToken();
2218 if (token.value != TOK.identifier)
2220 error(token.loc, "`%s` expected as dot-separated identifiers, got `%s`", entity, token.toChars());
2221 return qualified;
2224 Identifier id = token.ident;
2225 qualified ~= id;
2227 nextToken();
2229 while (token.value == TOK.dot);
2231 return qualified;
2234 private AST.DebugSymbol parseDebugSpecification()
2236 AST.DebugSymbol s;
2237 nextToken();
2238 if (token.value == TOK.identifier)
2239 s = new AST.DebugSymbol(token.loc, token.ident);
2240 else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
2242 // @@@DEPRECATED_2.111@@@
2243 // Deprecated in 2.101, remove in 2.111
2244 deprecation("`debug = <integer>` is deprecated, use debug identifiers instead");
2246 s = new AST.DebugSymbol(token.loc, cast(uint)token.unsvalue);
2248 else
2250 error("identifier or integer expected, not `%s`", token.toChars());
2251 s = null;
2253 nextToken();
2254 if (token.value != TOK.semicolon)
2255 error("semicolon expected");
2256 nextToken();
2257 return s;
2260 /**************************************
2261 * Parse a debug conditional
2263 private AST.Condition parseDebugCondition()
2265 uint level = 1;
2266 Identifier id = null;
2267 Loc loc = token.loc;
2269 if (token.value == TOK.leftParenthesis)
2271 nextToken();
2273 if (token.value == TOK.identifier)
2274 id = token.ident;
2275 else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
2277 // @@@DEPRECATED_2.111@@@
2278 // Deprecated in 2.101, remove in 2.111
2279 deprecation("`debug( <integer> )` is deprecated, use debug identifiers instead");
2281 level = cast(uint)token.unsvalue;
2283 else
2284 error("identifier or integer expected inside `debug(...)`, not `%s`", token.toChars());
2285 loc = token.loc;
2286 nextToken();
2287 check(TOK.rightParenthesis);
2289 return new AST.DebugCondition(loc, mod, level, id);
2292 /**************************************
2293 * Parse a version specification
2295 private AST.VersionSymbol parseVersionSpecification()
2297 AST.VersionSymbol s;
2298 nextToken();
2299 if (token.value == TOK.identifier)
2300 s = new AST.VersionSymbol(token.loc, token.ident);
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");
2306 s = new AST.VersionSymbol(token.loc, cast(uint)token.unsvalue);
2308 else
2310 error("identifier or integer expected, not `%s`", token.toChars());
2311 s = null;
2313 nextToken();
2314 if (token.value != TOK.semicolon)
2315 error("semicolon expected");
2316 nextToken();
2317 return s;
2320 /**************************************
2321 * Parse a version conditional
2323 private AST.Condition parseVersionCondition()
2325 uint level = 1;
2326 Identifier id = null;
2327 Loc loc;
2329 if (token.value == TOK.leftParenthesis)
2331 nextToken();
2332 /* Allow:
2333 * version (unittest)
2334 * version (assert)
2335 * even though they are keywords
2337 loc = token.loc;
2338 if (token.value == TOK.identifier)
2339 id = token.ident;
2340 else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
2342 // @@@DEPRECATED_2.111@@@
2343 // Deprecated in 2.101, remove in 2.111
2344 deprecation("`version( <integer> )` is deprecated, use version identifiers instead");
2346 level = cast(uint)token.unsvalue;
2348 else if (token.value == TOK.unittest_)
2349 id = Identifier.idPool(Token.toString(TOK.unittest_));
2350 else if (token.value == TOK.assert_)
2351 id = Identifier.idPool(Token.toString(TOK.assert_));
2352 else
2353 error("identifier or integer expected inside `version(...)`, not `%s`", token.toChars());
2354 nextToken();
2355 check(TOK.rightParenthesis);
2357 else
2358 error("(condition) expected following `version`");
2359 return new AST.VersionCondition(loc, mod, level, id);
2362 /***********************************************
2363 * static if (expression)
2364 * body
2365 * else
2366 * body
2367 * Current token is 'static'.
2369 private AST.Condition parseStaticIfCondition()
2371 AST.Expression exp;
2372 AST.Condition condition;
2373 const loc = token.loc;
2375 nextToken();
2376 nextToken();
2377 if (token.value == TOK.leftParenthesis)
2379 nextToken();
2380 exp = parseAssignExp();
2381 check(TOK.rightParenthesis);
2383 else
2385 error("(expression) expected following `static if`");
2386 exp = null;
2388 condition = new AST.StaticIfCondition(loc, exp);
2389 return condition;
2392 /*****************************************
2393 * Parse a constructor definition:
2394 * this(parameters) { body }
2395 * or postblit:
2396 * this(this) { body }
2397 * or constructor template:
2398 * this(templateparameters)(parameters) { body }
2399 * Current token is 'this'.
2401 private AST.Dsymbol parseCtor(PrefixAttributes!AST* pAttrs)
2403 AST.Expressions* udas = null;
2404 const loc = token.loc;
2405 StorageClass stc = getStorageClass!AST(pAttrs);
2407 nextToken();
2408 if (token.value == TOK.leftParenthesis && peekNext() == TOK.this_ && peekNext2() == TOK.rightParenthesis)
2410 // this(this) { ... }
2411 nextToken();
2412 nextToken();
2413 check(TOK.rightParenthesis);
2415 stc = parsePostfix(stc, &udas);
2416 if (stc & STC.immutable_)
2417 deprecation("`immutable` postblit is deprecated. Please use an unqualified postblit.");
2418 if (stc & STC.shared_)
2419 deprecation("`shared` postblit is deprecated. Please use an unqualified postblit.");
2420 if (stc & STC.const_)
2421 deprecation("`const` postblit is deprecated. Please use an unqualified postblit.");
2422 if (stc & STC.static_)
2423 error(loc, "postblit cannot be `static`");
2425 auto f = new AST.PostBlitDeclaration(loc, Loc.initial, stc, Id.postblit);
2426 AST.Dsymbol s = parseContracts(f);
2427 if (udas)
2429 auto a = new AST.Dsymbols();
2430 a.push(f);
2431 s = new AST.UserAttributeDeclaration(udas, a);
2433 return s;
2436 /* Look ahead to see if:
2437 * this(...)(...)
2438 * which is a constructor template
2440 AST.TemplateParameters* tpl = null;
2441 if (token.value == TOK.leftParenthesis && peekPastParen(&token).value == TOK.leftParenthesis)
2443 tpl = parseTemplateParameterList();
2446 /* Just a regular constructor
2448 auto parameterList = parseParameterList(null);
2449 stc = parsePostfix(stc, &udas);
2451 if (parameterList.varargs != VarArg.none || AST.Parameter.dim(parameterList.parameters) != 0)
2453 if (stc & STC.static_)
2454 error(loc, "constructor cannot be static");
2456 else if (StorageClass ss = stc & (STC.shared_ | STC.static_)) // this()
2458 if (ss == STC.static_)
2459 error(loc, "use `static this()` to declare a static constructor");
2460 else if (ss == (STC.shared_ | STC.static_))
2461 error(loc, "use `shared static this()` to declare a shared static constructor");
2464 AST.Expression constraint = tpl ? parseConstraint() : null;
2466 AST.Type tf = new AST.TypeFunction(parameterList, null, linkage, stc); // RetrunType -> auto
2467 tf = tf.addSTC(stc);
2469 auto f = new AST.CtorDeclaration(loc, Loc.initial, stc, tf);
2470 AST.Dsymbol s = parseContracts(f, !!tpl);
2471 if (udas)
2473 auto a = new AST.Dsymbols();
2474 a.push(f);
2475 s = new AST.UserAttributeDeclaration(udas, a);
2478 if (tpl)
2480 // Wrap a template around it
2481 auto decldefs = new AST.Dsymbols();
2482 decldefs.push(s);
2483 s = new AST.TemplateDeclaration(loc, f.ident, tpl, constraint, decldefs);
2486 return s;
2489 /*****************************************
2490 * Parse a destructor definition:
2491 * ~this() { body }
2492 * Current token is '~'.
2494 private AST.Dsymbol parseDtor(PrefixAttributes!AST* pAttrs)
2496 AST.Expressions* udas = null;
2497 const loc = token.loc;
2498 StorageClass stc = getStorageClass!AST(pAttrs);
2500 nextToken();
2501 check(TOK.this_);
2502 check(TOK.leftParenthesis);
2503 check(TOK.rightParenthesis);
2505 stc = parsePostfix(stc, &udas);
2506 if (StorageClass ss = stc & (STC.shared_ | STC.static_))
2508 if (ss == STC.static_)
2509 error(loc, "use `static ~this()` to declare a static destructor");
2510 else if (ss == (STC.shared_ | STC.static_))
2511 error(loc, "use `shared static ~this()` to declare a shared static destructor");
2514 auto f = new AST.DtorDeclaration(loc, Loc.initial, stc, Id.dtor);
2515 AST.Dsymbol s = parseContracts(f);
2516 if (udas)
2518 auto a = new AST.Dsymbols();
2519 a.push(f);
2520 s = new AST.UserAttributeDeclaration(udas, a);
2522 return s;
2525 /*****************************************
2526 * Parse a static constructor definition:
2527 * static this() { body }
2528 * Current token is 'static'.
2530 private AST.Dsymbol parseStaticCtor(PrefixAttributes!AST* pAttrs)
2532 //Expressions *udas = NULL;
2533 const loc = token.loc;
2534 StorageClass stc = getStorageClass!AST(pAttrs);
2536 nextToken();
2537 nextToken();
2538 check(TOK.leftParenthesis);
2539 check(TOK.rightParenthesis);
2541 stc = parsePostfix(stc & ~STC.TYPECTOR, null) | stc;
2542 if (stc & STC.shared_)
2543 error(loc, "use `shared static this()` to declare a shared static constructor");
2544 else if (stc & STC.static_)
2545 appendStorageClass(stc, STC.static_); // complaint for the redundancy
2546 else if (StorageClass modStc = stc & STC.TYPECTOR)
2548 OutBuffer buf;
2549 AST.stcToBuffer(buf, modStc);
2550 error(loc, "static constructor cannot be `%s`", buf.peekChars());
2552 stc &= ~(STC.static_ | STC.TYPECTOR);
2554 auto f = new AST.StaticCtorDeclaration(loc, Loc.initial, stc);
2555 AST.Dsymbol s = parseContracts(f);
2556 return s;
2559 /*****************************************
2560 * Parse a static destructor definition:
2561 * static ~this() { body }
2562 * Current token is 'static'.
2564 private AST.Dsymbol parseStaticDtor(PrefixAttributes!AST* pAttrs)
2566 AST.Expressions* udas = null;
2567 const loc = token.loc;
2568 StorageClass stc = getStorageClass!AST(pAttrs);
2570 nextToken();
2571 nextToken();
2572 check(TOK.this_);
2573 check(TOK.leftParenthesis);
2574 check(TOK.rightParenthesis);
2576 stc = parsePostfix(stc & ~STC.TYPECTOR, &udas) | stc;
2577 if (stc & STC.shared_)
2578 error(loc, "use `shared static ~this()` to declare a shared static destructor");
2579 else if (stc & STC.static_)
2580 appendStorageClass(stc, STC.static_); // complaint for the redundancy
2581 else if (StorageClass modStc = stc & STC.TYPECTOR)
2583 OutBuffer buf;
2584 AST.stcToBuffer(buf, modStc);
2585 error(loc, "static destructor cannot be `%s`", buf.peekChars());
2587 stc &= ~(STC.static_ | STC.TYPECTOR);
2589 auto f = new AST.StaticDtorDeclaration(loc, Loc.initial, stc);
2590 AST.Dsymbol s = parseContracts(f);
2591 if (udas)
2593 auto a = new AST.Dsymbols();
2594 a.push(f);
2595 s = new AST.UserAttributeDeclaration(udas, a);
2597 return s;
2600 /*****************************************
2601 * Parse a shared static constructor definition:
2602 * shared static this() { body }
2603 * Current token is 'shared'.
2605 private AST.Dsymbol parseSharedStaticCtor(PrefixAttributes!AST* pAttrs)
2607 //Expressions *udas = NULL;
2608 const loc = token.loc;
2609 StorageClass stc = getStorageClass!AST(pAttrs);
2611 nextToken();
2612 nextToken();
2613 nextToken();
2614 check(TOK.leftParenthesis);
2615 check(TOK.rightParenthesis);
2617 stc = parsePostfix(stc & ~STC.TYPECTOR, null) | stc;
2618 if (StorageClass ss = stc & (STC.shared_ | STC.static_))
2619 appendStorageClass(stc, ss); // complaint for the redundancy
2620 else if (StorageClass modStc = stc & STC.TYPECTOR)
2622 OutBuffer buf;
2623 AST.stcToBuffer(buf, modStc);
2624 error(loc, "shared static constructor cannot be `%s`", buf.peekChars());
2626 stc &= ~(STC.static_ | STC.TYPECTOR);
2628 auto f = new AST.SharedStaticCtorDeclaration(loc, Loc.initial, stc);
2629 AST.Dsymbol s = parseContracts(f);
2630 return s;
2633 /*****************************************
2634 * Parse a shared static destructor definition:
2635 * shared static ~this() { body }
2636 * Current token is 'shared'.
2638 private AST.Dsymbol parseSharedStaticDtor(PrefixAttributes!AST* pAttrs)
2640 AST.Expressions* udas = null;
2641 const loc = token.loc;
2642 StorageClass stc = getStorageClass!AST(pAttrs);
2644 nextToken();
2645 nextToken();
2646 nextToken();
2647 check(TOK.this_);
2648 check(TOK.leftParenthesis);
2649 check(TOK.rightParenthesis);
2651 stc = parsePostfix(stc & ~STC.TYPECTOR, &udas) | stc;
2652 if (StorageClass ss = stc & (STC.shared_ | STC.static_))
2653 appendStorageClass(stc, ss); // complaint for the redundancy
2654 else if (StorageClass modStc = stc & STC.TYPECTOR)
2656 OutBuffer buf;
2657 AST.stcToBuffer(buf, modStc);
2658 error(loc, "shared static destructor cannot be `%s`", buf.peekChars());
2660 stc &= ~(STC.static_ | STC.TYPECTOR);
2662 auto f = new AST.SharedStaticDtorDeclaration(loc, Loc.initial, stc);
2663 AST.Dsymbol s = parseContracts(f);
2664 if (udas)
2666 auto a = new AST.Dsymbols();
2667 a.push(f);
2668 s = new AST.UserAttributeDeclaration(udas, a);
2670 return s;
2673 /*****************************************
2674 * Parse an invariant definition:
2675 * invariant { statements... }
2676 * invariant() { statements... }
2677 * invariant (expression);
2678 * Current token is 'invariant'.
2680 private AST.Dsymbol parseInvariant(PrefixAttributes!AST* pAttrs)
2682 const loc = token.loc;
2683 StorageClass stc = getStorageClass!AST(pAttrs);
2685 nextToken();
2686 if (token.value == TOK.leftParenthesis) // optional () or invariant (expression);
2688 nextToken();
2689 if (token.value != TOK.rightParenthesis) // invariant (expression);
2691 AST.Expression e = parseAssignExp(), msg = null;
2692 if (token.value == TOK.comma)
2694 nextToken();
2695 if (token.value != TOK.rightParenthesis)
2697 msg = parseAssignExp();
2698 if (token.value == TOK.comma)
2699 nextToken();
2702 check(TOK.rightParenthesis);
2703 check(TOK.semicolon, "invariant");
2704 e = new AST.AssertExp(loc, e, msg);
2705 auto fbody = new AST.ExpStatement(loc, e);
2706 auto f = new AST.InvariantDeclaration(loc, token.loc, stc, null, fbody);
2707 return f;
2709 nextToken();
2712 auto fbody = parseStatement(ParseStatementFlags.curly);
2713 auto f = new AST.InvariantDeclaration(loc, token.loc, stc, null, fbody);
2714 return f;
2717 /*****************************************
2718 * Parse a unittest definition:
2719 * unittest { body }
2720 * Current token is 'unittest'.
2722 private AST.Dsymbol parseUnitTest(PrefixAttributes!AST* pAttrs)
2724 const loc = token.loc;
2725 StorageClass stc = getStorageClass!AST(pAttrs);
2727 nextToken();
2729 const(char)* begPtr = token.ptr + 1; // skip left curly brace
2730 const(char)* endPtr = null;
2731 AST.Statement sbody = parseStatement(ParseStatementFlags.curly, &endPtr);
2733 /** Extract unittest body as a string. Must be done eagerly since memory
2734 will be released by the lexer before doc gen. */
2735 char* docline = null;
2736 if (compileEnv.ddocOutput && endPtr > begPtr)
2738 /* Remove trailing whitespaces */
2739 for (const(char)* p = endPtr - 1; begPtr <= p && (*p == ' ' || *p == '\r' || *p == '\n' || *p == '\t'); --p)
2741 endPtr = p;
2744 size_t len = endPtr - begPtr;
2745 if (len > 0)
2747 docline = cast(char*)mem.xmalloc_noscan(len + 2);
2748 memcpy(docline, begPtr, len);
2749 docline[len] = '\n'; // Terminate all lines by LF
2750 docline[len + 1] = '\0';
2754 auto f = new AST.UnitTestDeclaration(loc, token.loc, stc, docline);
2755 f.fbody = sbody;
2756 return f;
2759 /*****************************************
2760 * Parse a new definition:
2761 * @disable new();
2762 * Current token is 'new'.
2764 private AST.Dsymbol parseNew(PrefixAttributes!AST* pAttrs)
2766 const loc = token.loc;
2767 StorageClass stc = getStorageClass!AST(pAttrs);
2768 if (!(stc & STC.disable))
2770 error("`new` allocator must be annotated with `@disabled`");
2772 nextToken();
2774 /* @@@DEPRECATED_2.108@@@
2775 * After deprecation period (2.108), remove all code in the version(all) block.
2777 version (all)
2779 auto parameterList = parseParameterList(null); // parameterList ignored
2780 if (parameterList.parameters.length > 0 || parameterList.varargs != VarArg.none)
2781 deprecation("`new` allocator with non-empty parameter list is deprecated");
2782 auto f = new AST.NewDeclaration(loc, stc);
2783 if (token.value != TOK.semicolon)
2785 deprecation("`new` allocator with function definition is deprecated");
2786 parseContracts(f); // body ignored
2787 f.fbody = null;
2788 f.fensures = null;
2789 f.frequires = null;
2791 else
2792 nextToken();
2793 return f;
2795 else
2797 check(TOK.leftParenthesis);
2798 check(TOK.rightParenthesis);
2799 check(TOK.semicolon);
2800 return new AST.NewDeclaration(loc, stc);
2804 /**********************************************
2805 * Parse parameter list.
2807 private AST.ParameterList parseParameterList(AST.TemplateParameters** tpl)
2809 auto parameters = new AST.Parameters();
2810 VarArg varargs = VarArg.none;
2811 StorageClass varargsStc;
2813 // Attributes allowed for ...
2814 enum VarArgsStc = STC.const_ | STC.immutable_ | STC.shared_ | STC.scope_ | STC.return_ | STC.returnScope;
2816 check(TOK.leftParenthesis);
2817 while (1)
2819 Identifier ai = null;
2820 AST.Type at;
2821 StorageClass storageClass = 0;
2822 StorageClass stc;
2823 AST.Expression ae;
2824 AST.Expressions* udas = null;
2825 for (; 1; nextToken())
2828 switch (token.value)
2830 case TOK.rightParenthesis:
2831 if (storageClass != 0 || udas !is null)
2832 error("basic type expected, not `)`");
2833 break;
2835 case TOK.dotDotDot:
2836 varargs = VarArg.variadic;
2837 varargsStc = storageClass;
2838 if (varargsStc & ~VarArgsStc)
2840 OutBuffer buf;
2841 AST.stcToBuffer(buf, varargsStc & ~VarArgsStc);
2842 error("variadic parameter cannot have attributes `%s`", buf.peekChars());
2843 varargsStc &= VarArgsStc;
2845 nextToken();
2846 break;
2848 case TOK.const_:
2849 if (peekNext() == TOK.leftParenthesis)
2850 goto default;
2851 stc = STC.const_;
2852 goto L2;
2854 case TOK.immutable_:
2855 if (peekNext() == TOK.leftParenthesis)
2856 goto default;
2857 stc = STC.immutable_;
2858 goto L2;
2860 case TOK.shared_:
2861 if (peekNext() == TOK.leftParenthesis)
2862 goto default;
2863 stc = STC.shared_;
2864 goto L2;
2866 case TOK.inout_:
2867 if (peekNext() == TOK.leftParenthesis)
2868 goto default;
2869 stc = STC.wild;
2870 goto L2;
2871 case TOK.at:
2873 AST.Expressions* exps = null;
2874 StorageClass stc2 = parseAttribute(exps);
2875 if (stc2 & atAttrGroup)
2877 error("`@%s` attribute for function parameter is not supported", token.toChars());
2879 else
2881 udas = AST.UserAttributeDeclaration.concat(udas, exps);
2883 if (token.value == TOK.dotDotDot)
2884 error("variadic parameter cannot have user-defined attributes");
2885 if (stc2)
2886 nextToken();
2887 goto L3;
2888 // Don't call nextToken again.
2890 case TOK.in_:
2891 if (transitionIn)
2892 eSink.message(scanloc, "Usage of 'in' on parameter");
2893 stc = STC.in_;
2894 goto L2;
2896 case TOK.out_:
2897 stc = STC.out_;
2898 goto L2;
2900 case TOK.ref_:
2901 stc = STC.ref_;
2902 goto L2;
2904 case TOK.lazy_:
2905 stc = STC.lazy_;
2906 goto L2;
2908 case TOK.scope_:
2909 stc = STC.scope_;
2910 goto L2;
2912 case TOK.final_:
2913 stc = STC.final_;
2914 goto L2;
2916 case TOK.auto_:
2917 stc = STC.auto_;
2918 goto L2;
2920 case TOK.return_:
2921 stc = STC.return_;
2922 if (peekNext() == TOK.scope_)
2923 stc |= STC.returnScope;
2924 goto L2;
2926 storageClass = appendStorageClass(storageClass, stc);
2927 continue;
2929 default:
2931 stc = storageClass & (STC.IOR | STC.lazy_);
2932 // if stc is not a power of 2
2933 if (stc & (stc - 1) && !(stc == (STC.in_ | STC.ref_)))
2934 error("incompatible parameter storage classes");
2935 //if ((storageClass & STC.scope_) && (storageClass & (STC.ref_ | STC.out_)))
2936 //error("scope cannot be ref or out");
2938 const tv = peekNext();
2939 Loc loc;
2940 if (tpl && token.value == TOK.identifier &&
2941 (tv == TOK.comma || tv == TOK.rightParenthesis || tv == TOK.dotDotDot))
2943 Identifier id = Identifier.generateId("__T");
2944 loc = token.loc;
2945 at = new AST.TypeIdentifier(loc, id);
2946 if (!*tpl)
2947 *tpl = new AST.TemplateParameters();
2948 AST.TemplateParameter tp = new AST.TemplateTypeParameter(loc, id, null, null);
2949 (*tpl).push(tp);
2951 ai = token.ident;
2952 nextToken();
2954 else
2956 at = parseType(&ai, null, &loc);
2958 ae = null;
2959 if (token.value == TOK.assign) // = defaultArg
2961 nextToken();
2962 ae = parseDefaultInitExp();
2964 auto param = new AST.Parameter(loc, storageClass | STC.parameter, at, ai, ae, null);
2965 if (udas)
2967 auto a = new AST.Dsymbols();
2968 auto udad = new AST.UserAttributeDeclaration(udas, a);
2969 param.userAttribDecl = udad;
2971 if (token.value == TOK.at)
2973 AST.Expressions* exps = null;
2974 StorageClass stc2 = parseAttribute(exps);
2975 if (stc2 & atAttrGroup)
2977 error("`@%s` attribute for function parameter is not supported", token.toChars());
2979 else
2981 error("user-defined attributes cannot appear as postfixes", token.toChars());
2983 if (stc2)
2984 nextToken();
2986 if (token.value == TOK.dotDotDot)
2988 /* This is:
2989 * at ai ...
2991 if (storageClass & (STC.out_ | STC.ref_))
2992 error("variadic argument cannot be `out` or `ref`");
2993 varargs = VarArg.typesafe;
2994 parameters.push(param);
2995 nextToken();
2996 break;
2998 parameters.push(param);
2999 if (token.value == TOK.comma)
3001 nextToken();
3002 goto L1;
3004 break;
3007 break;
3009 break;
3013 check(TOK.rightParenthesis);
3014 return AST.ParameterList(parameters, varargs, varargsStc);
3017 /*************************************
3019 private AST.EnumDeclaration parseEnum()
3021 AST.EnumDeclaration e;
3022 Identifier id;
3023 AST.Type memtype;
3024 auto loc = token.loc;
3026 // printf("Parser::parseEnum()\n");
3027 nextToken();
3028 id = null;
3029 if (token.value == TOK.identifier)
3031 id = token.ident;
3032 nextToken();
3035 memtype = null;
3036 if (token.value == TOK.colon)
3038 nextToken();
3039 int alt = 0;
3040 const typeLoc = token.loc;
3041 memtype = parseBasicType();
3042 memtype = parseDeclarator(memtype, alt, null);
3043 checkCstyleTypeSyntax(typeLoc, memtype, alt, null);
3046 e = new AST.EnumDeclaration(loc, id, memtype);
3047 // opaque type
3048 if (token.value == TOK.semicolon && id)
3049 nextToken();
3050 else if (token.value == TOK.leftCurly)
3052 bool isAnonymousEnum = !id;
3054 //printf("enum definition\n");
3055 e.members = new AST.Dsymbols();
3056 nextToken();
3057 const(char)[] comment = token.blockComment;
3058 while (token.value != TOK.rightCurly)
3060 /* Can take the following forms...
3061 * 1. ident
3062 * 2. ident = value
3063 * 3. type ident = value
3064 * ... prefixed by valid attributes
3066 loc = token.loc;
3068 AST.Type type = null;
3069 Identifier ident = null;
3071 AST.Expressions* udas;
3072 StorageClass stc;
3073 AST.Expression deprecationMessage;
3074 enum attributeErrorMessage = "`%s` is not a valid attribute for enum members";
3075 Lattrs:
3076 while (1)
3078 switch (token.value)
3080 case TOK.at:
3081 if (StorageClass _stc = parseAttribute(udas))
3083 if (_stc == STC.disable)
3084 stc |= _stc;
3085 else
3087 OutBuffer buf;
3088 AST.stcToBuffer(buf, _stc);
3089 error(attributeErrorMessage, buf.peekChars());
3091 nextToken();
3093 break;
3094 case TOK.deprecated_:
3095 stc |= STC.deprecated_;
3096 if (!parseDeprecatedAttribute(deprecationMessage))
3098 nextToken();
3100 break;
3101 default:
3102 break Lattrs;
3105 if (token.value == TOK.identifier)
3107 const tv = peekNext();
3108 if (tv == TOK.assign || tv == TOK.comma || tv == TOK.rightCurly)
3110 ident = token.ident;
3111 type = null;
3112 nextToken();
3114 else
3116 if (isAnonymousEnum)
3117 goto Ltype;
3119 nextToken();
3120 error("expected `,` or `=` after identifier, not `%s`", token.toChars());
3123 else
3125 if (isAnonymousEnum)
3127 Ltype:
3128 // Type identifier
3129 type = parseType(&ident, null);
3130 if (type == AST.Type.terror)
3132 type = null;
3133 nextToken();
3135 else if (!ident)
3137 error("no identifier for declarator `%s`", type.toChars());
3138 type = null;
3140 else
3142 const tv = token.value;
3143 if (tv != TOK.assign && tv != TOK.comma && tv != TOK.rightCurly)
3145 error("expected `,` or `=` after identifier, not `%s`", token.toChars());
3146 nextToken();
3150 else
3152 Token* t = &token;
3153 if (isBasicType(&t))
3155 error("named enum cannot declare member with type", (*t).toChars());
3156 nextToken();
3158 else
3159 check(TOK.identifier);
3161 // avoid extra error messages
3162 const tv = token.value;
3163 if (tv != TOK.assign && tv != TOK.comma && tv != TOK.rightCurly && tv != TOK.endOfFile)
3164 continue;
3168 AST.Expression value;
3169 if (token.value == TOK.assign)
3171 nextToken();
3172 value = parseAssignExp();
3174 else
3176 value = null;
3177 if (type && isAnonymousEnum)
3178 error("initializer required after `%s` when type is specified", ident.toChars());
3181 AST.DeprecatedDeclaration dd;
3182 if (deprecationMessage)
3184 dd = new AST.DeprecatedDeclaration(deprecationMessage, null);
3185 stc |= STC.deprecated_;
3188 auto em = new AST.EnumMember(loc, ident, value, type, stc, null, dd);
3189 e.members.push(em);
3191 if (udas)
3193 auto uad = new AST.UserAttributeDeclaration(udas, new AST.Dsymbols());
3194 em.userAttribDecl = uad;
3197 if (token.value != TOK.rightCurly)
3199 addComment(em, comment);
3200 comment = null;
3201 check(TOK.comma);
3203 addComment(em, comment);
3204 comment = token.blockComment;
3206 if (token.value == TOK.endOfFile)
3208 error("premature end of file");
3209 break;
3212 nextToken();
3214 else
3216 nextToken();
3217 error("expected `{`, not `%s` for enum declaration", token.toChars());
3219 //printf("-parseEnum() %s\n", e.toChars());
3220 return e;
3223 /********************************
3224 * Parse struct, union, interface, class.
3226 private AST.Dsymbol parseAggregate()
3228 AST.TemplateParameters* tpl = null;
3229 AST.Expression constraint;
3230 const loc = token.loc;
3231 TOK tok = token.value;
3233 //printf("Parser::parseAggregate()\n");
3234 nextToken();
3235 Identifier id;
3236 if (token.value != TOK.identifier)
3238 id = null;
3240 else
3242 id = token.ident;
3243 nextToken();
3245 if (token.value == TOK.leftParenthesis)
3247 // struct/class template declaration.
3248 tpl = parseTemplateParameterList();
3249 constraint = parseConstraint();
3253 // Collect base class(es)
3254 AST.BaseClasses* baseclasses = null;
3255 if (token.value == TOK.colon)
3257 if (tok != TOK.interface_ && tok != TOK.class_)
3258 error("base classes are not allowed for `%s`, did you mean `;`?", Token.toChars(tok));
3259 nextToken();
3260 baseclasses = parseBaseClasses();
3263 if (token.value == TOK.if_)
3265 if (constraint)
3266 error("template constraints appear both before and after BaseClassList, put them before");
3267 constraint = parseConstraint();
3269 if (constraint)
3271 if (!id)
3272 error("template constraints not allowed for anonymous `%s`", Token.toChars(tok));
3273 if (!tpl)
3274 error("template constraints only allowed for templates");
3277 AST.Dsymbols* members = null;
3278 if (token.value == TOK.leftCurly)
3280 //printf("aggregate definition\n");
3281 const lookingForElseSave = lookingForElse;
3282 lookingForElse = Loc();
3283 nextToken();
3284 members = parseDeclDefs(0);
3285 lookingForElse = lookingForElseSave;
3286 if (token.value != TOK.rightCurly)
3288 /* { */
3289 error(token.loc, "`}` expected following members in `%s` declaration",
3290 Token.toChars(tok));
3291 if (id)
3292 eSink.errorSupplemental(loc, "%s `%s` starts here",
3293 Token.toChars(tok), id.toChars());
3294 else
3295 eSink.errorSupplemental(loc, "%s starts here",
3296 Token.toChars(tok));
3298 nextToken();
3300 else if (token.value == TOK.semicolon && id)
3302 if (baseclasses || constraint)
3303 error("members expected");
3304 nextToken();
3306 else
3308 error(token.loc, "{ } expected following `%s` declaration", Token.toChars(tok));
3311 AST.AggregateDeclaration a;
3312 switch (tok)
3314 case TOK.interface_:
3315 if (!id)
3316 error(loc, "anonymous interfaces not allowed");
3317 a = new AST.InterfaceDeclaration(loc, id, baseclasses);
3318 a.members = members;
3319 break;
3321 case TOK.class_:
3322 if (!id)
3323 error(loc, "anonymous classes not allowed");
3324 bool inObject = md && !md.packages && md.id == Id.object;
3325 a = new AST.ClassDeclaration(loc, id, baseclasses, members, inObject);
3326 break;
3328 case TOK.struct_:
3329 if (id)
3331 bool inObject = md && !md.packages && md.id == Id.object;
3332 a = new AST.StructDeclaration(loc, id, inObject);
3333 a.members = members;
3335 else
3337 /* Anonymous structs/unions are more like attributes.
3339 assert(!tpl);
3340 return new AST.AnonDeclaration(loc, false, members);
3342 break;
3344 case TOK.union_:
3345 if (id)
3347 a = new AST.UnionDeclaration(loc, id);
3348 a.members = members;
3350 else
3352 /* Anonymous structs/unions are more like attributes.
3354 assert(!tpl);
3355 return new AST.AnonDeclaration(loc, true, members);
3357 break;
3359 default:
3360 assert(0);
3363 if (tpl)
3365 // Wrap a template around the aggregate declaration
3366 auto decldefs = new AST.Dsymbols();
3367 decldefs.push(a);
3368 auto tempdecl = new AST.TemplateDeclaration(loc, id, tpl, constraint, decldefs);
3369 return tempdecl;
3371 return a;
3374 /*******************************************
3376 private AST.BaseClasses* parseBaseClasses()
3378 auto baseclasses = new AST.BaseClasses();
3380 for (; 1; nextToken())
3382 auto b = new AST.BaseClass(parseBasicType());
3383 baseclasses.push(b);
3384 if (token.value != TOK.comma)
3385 break;
3387 return baseclasses;
3390 AST.Dsymbols* parseImport()
3392 auto decldefs = new AST.Dsymbols();
3393 Identifier aliasid = null;
3395 int isstatic = token.value == TOK.static_;
3396 if (isstatic)
3397 nextToken();
3399 //printf("Parser::parseImport()\n");
3403 nextToken();
3404 if (token.value != TOK.identifier)
3406 error("identifier expected following `import`");
3407 break;
3410 const loc = token.loc;
3411 Identifier id = token.ident;
3412 Identifier[] a;
3413 nextToken();
3414 if (!aliasid && token.value == TOK.assign)
3416 aliasid = id;
3417 goto L1;
3419 while (token.value == TOK.dot)
3421 a ~= id;
3422 nextToken();
3423 if (token.value != TOK.identifier)
3425 error("identifier expected following `package`");
3426 break;
3428 id = token.ident;
3429 nextToken();
3432 auto s = new AST.Import(loc, a, id, aliasid, isstatic);
3433 decldefs.push(s);
3435 /* Look for
3436 * : alias=name, alias=name;
3437 * syntax.
3439 if (token.value == TOK.colon)
3443 nextToken();
3444 if (token.value != TOK.identifier)
3446 error("identifier expected following `:`");
3447 break;
3449 Identifier _alias = token.ident;
3450 Identifier name;
3451 nextToken();
3452 if (token.value == TOK.assign)
3454 nextToken();
3455 if (token.value != TOK.identifier)
3457 error("identifier expected following `%s=`", _alias.toChars());
3458 break;
3460 name = token.ident;
3461 nextToken();
3463 else
3465 name = _alias;
3466 _alias = null;
3468 s.addAlias(name, _alias);
3470 while (token.value == TOK.comma);
3471 break; // no comma-separated imports of this form
3473 aliasid = null;
3475 while (token.value == TOK.comma);
3477 if (token.value == TOK.semicolon)
3478 nextToken();
3479 else
3481 error("`;` expected");
3482 nextToken();
3485 return decldefs;
3488 /* Parse a type and optional identifier
3489 * Params:
3490 * pident = set to Identifier if there is one, null if not
3491 * ptpl = if !null, then set to TemplateParameterList
3492 * pdeclLoc = if !null, then set to location of the declarator
3494 AST.Type parseType(Identifier* pident = null, AST.TemplateParameters** ptpl = null, Loc* pdeclLoc = null)
3496 /* Take care of the storage class prefixes that
3497 * serve as type attributes:
3498 * const type
3499 * immutable type
3500 * shared type
3501 * inout type
3502 * inout const type
3503 * shared const type
3504 * shared inout type
3505 * shared inout const type
3507 StorageClass stc = 0;
3508 while (1)
3510 switch (token.value)
3512 case TOK.const_:
3513 if (peekNext() == TOK.leftParenthesis)
3514 break; // const as type constructor
3515 stc |= STC.const_; // const as storage class
3516 nextToken();
3517 continue;
3519 case TOK.immutable_:
3520 if (peekNext() == TOK.leftParenthesis)
3521 break;
3522 stc |= STC.immutable_;
3523 nextToken();
3524 continue;
3526 case TOK.shared_:
3527 if (peekNext() == TOK.leftParenthesis)
3528 break;
3529 stc |= STC.shared_;
3530 nextToken();
3531 continue;
3533 case TOK.inout_:
3534 if (peekNext() == TOK.leftParenthesis)
3535 break;
3536 stc |= STC.wild;
3537 nextToken();
3538 continue;
3540 default:
3541 break;
3543 break;
3546 const typeLoc = token.loc;
3548 AST.Type t;
3549 t = parseBasicType();
3551 if (pdeclLoc)
3552 *pdeclLoc = token.loc;
3553 int alt = 0;
3554 t = parseDeclarator(t, alt, pident, ptpl);
3555 checkCstyleTypeSyntax(typeLoc, t, alt, pident ? *pident : null);
3557 t = t.addSTC(stc);
3558 return t;
3561 private AST.Type parseBasicType(bool dontLookDotIdents = false)
3563 AST.Type t;
3564 Loc loc;
3565 Identifier id;
3566 //printf("parseBasicType()\n");
3567 switch (token.value)
3569 case TOK.void_:
3570 t = AST.Type.tvoid;
3571 goto LabelX;
3573 case TOK.int8:
3574 t = AST.Type.tint8;
3575 goto LabelX;
3577 case TOK.uns8:
3578 t = AST.Type.tuns8;
3579 goto LabelX;
3581 case TOK.int16:
3582 t = AST.Type.tint16;
3583 goto LabelX;
3585 case TOK.uns16:
3586 t = AST.Type.tuns16;
3587 goto LabelX;
3589 case TOK.int32:
3590 t = AST.Type.tint32;
3591 goto LabelX;
3593 case TOK.uns32:
3594 t = AST.Type.tuns32;
3595 goto LabelX;
3597 case TOK.int64:
3598 t = AST.Type.tint64;
3599 nextToken();
3600 if (token.value == TOK.int64) // if `long long`
3602 error("use `long` for a 64 bit integer instead of `long long`");
3603 nextToken();
3605 else if (token.value == TOK.float64) // if `long double`
3607 error("use `real` instead of `long double`");
3608 t = AST.Type.tfloat80;
3609 nextToken();
3611 break;
3613 case TOK.uns64:
3614 t = AST.Type.tuns64;
3615 goto LabelX;
3617 case TOK.int128:
3618 t = AST.Type.tint128;
3619 goto LabelX;
3621 case TOK.uns128:
3622 t = AST.Type.tuns128;
3623 goto LabelX;
3625 case TOK.float32:
3626 t = AST.Type.tfloat32;
3627 goto LabelX;
3629 case TOK.float64:
3630 t = AST.Type.tfloat64;
3631 goto LabelX;
3633 case TOK.float80:
3634 t = AST.Type.tfloat80;
3635 goto LabelX;
3637 case TOK.imaginary32:
3638 t = AST.Type.timaginary32;
3639 goto LabelX;
3641 case TOK.imaginary64:
3642 t = AST.Type.timaginary64;
3643 goto LabelX;
3645 case TOK.imaginary80:
3646 t = AST.Type.timaginary80;
3647 goto LabelX;
3649 case TOK.complex32:
3650 t = AST.Type.tcomplex32;
3651 goto LabelX;
3653 case TOK.complex64:
3654 t = AST.Type.tcomplex64;
3655 goto LabelX;
3657 case TOK.complex80:
3658 t = AST.Type.tcomplex80;
3659 goto LabelX;
3661 case TOK.bool_:
3662 t = AST.Type.tbool;
3663 goto LabelX;
3665 case TOK.char_:
3666 t = AST.Type.tchar;
3667 goto LabelX;
3669 case TOK.wchar_:
3670 t = AST.Type.twchar;
3671 goto LabelX;
3673 case TOK.dchar_:
3674 t = AST.Type.tdchar;
3675 goto LabelX;
3676 LabelX:
3677 nextToken();
3678 break;
3680 case TOK.this_:
3681 case TOK.super_:
3682 case TOK.identifier:
3683 loc = token.loc;
3684 id = token.ident;
3685 nextToken();
3686 if (token.value == TOK.not)
3688 // ident!(template_arguments)
3689 auto tempinst = new AST.TemplateInstance(loc, id, parseTemplateArguments());
3690 t = parseBasicTypeStartingAt(new AST.TypeInstance(loc, tempinst), dontLookDotIdents);
3692 else
3694 t = parseBasicTypeStartingAt(new AST.TypeIdentifier(loc, id), dontLookDotIdents);
3696 break;
3698 case TOK.mixin_:
3699 // https://dlang.org/spec/expression.html#mixin_types
3700 loc = token.loc;
3701 nextToken();
3702 if (token.value != TOK.leftParenthesis)
3703 error(token.loc, "found `%s` when expecting `%s` following `mixin`", token.toChars(), Token.toChars(TOK.leftParenthesis));
3704 auto exps = parseArguments();
3705 t = new AST.TypeMixin(loc, exps);
3706 break;
3708 case TOK.dot:
3709 // Leading . as in .foo
3710 t = parseBasicTypeStartingAt(new AST.TypeIdentifier(token.loc, Id.empty), dontLookDotIdents);
3711 break;
3713 case TOK.typeof_:
3714 // typeof(expression)
3715 t = parseBasicTypeStartingAt(parseTypeof(), dontLookDotIdents);
3716 break;
3718 case TOK.vector:
3719 t = parseVector();
3720 break;
3722 case TOK.traits:
3723 if (AST.TraitsExp te = cast(AST.TraitsExp) parsePrimaryExp())
3724 if (te.ident)
3726 t = new AST.TypeTraits(token.loc, te);
3727 break;
3729 t = new AST.TypeError;
3730 break;
3732 case TOK.const_:
3733 // const(type)
3734 nextToken();
3735 check(TOK.leftParenthesis);
3736 t = parseType().addSTC(STC.const_);
3737 check(TOK.rightParenthesis);
3738 break;
3740 case TOK.immutable_:
3741 // immutable(type)
3742 nextToken();
3743 check(TOK.leftParenthesis);
3744 t = parseType().addSTC(STC.immutable_);
3745 check(TOK.rightParenthesis);
3746 break;
3748 case TOK.shared_:
3749 // shared(type)
3750 nextToken();
3751 check(TOK.leftParenthesis);
3752 t = parseType().addSTC(STC.shared_);
3753 check(TOK.rightParenthesis);
3754 break;
3756 case TOK.inout_:
3757 // wild(type)
3758 nextToken();
3759 check(TOK.leftParenthesis);
3760 t = parseType().addSTC(STC.wild);
3761 check(TOK.rightParenthesis);
3762 break;
3764 default:
3765 error("basic type expected, not `%s`", token.toChars());
3766 if (token.value == TOK.else_)
3767 eSink.errorSupplemental(token.loc, "There's no `static else`, use `else` instead.");
3768 t = AST.Type.terror;
3769 break;
3771 return t;
3774 private AST.Type parseBasicTypeStartingAt(AST.TypeQualified tid, bool dontLookDotIdents)
3776 AST.Type maybeArray = null;
3777 // See https://issues.dlang.org/show_bug.cgi?id=1215
3778 // A basic type can look like MyType (typical case), but also:
3779 // MyType.T -> A type
3780 // MyType[expr] -> Either a static array of MyType or a type (iif MyType is a Ttuple)
3781 // MyType[expr].T -> A type.
3782 // MyType[expr].T[expr] -> Either a static array of MyType[expr].T or a type
3783 // (iif MyType[expr].T is a Ttuple)
3784 while (1)
3786 switch (token.value)
3788 case TOK.dot:
3790 nextToken();
3791 if (token.value != TOK.identifier)
3793 error("identifier expected following `.` instead of `%s`", token.toChars());
3794 break;
3796 if (maybeArray)
3798 // This is actually a TypeTuple index, not an {a/s}array.
3799 // We need to have a while loop to unwind all index taking:
3800 // T[e1][e2].U -> T, addIndex(e1), addIndex(e2)
3801 AST.Objects dimStack;
3802 AST.Type t = maybeArray;
3803 while (true)
3805 if (t.ty == Tsarray)
3807 // The index expression is an Expression.
3808 AST.TypeSArray a = cast(AST.TypeSArray)t;
3809 dimStack.push(a.dim.syntaxCopy());
3810 t = a.next.syntaxCopy();
3812 else if (t.ty == Taarray)
3814 // The index expression is a Type. It will be interpreted as an expression at semantic time.
3815 AST.TypeAArray a = cast(AST.TypeAArray)t;
3816 dimStack.push(a.index.syntaxCopy());
3817 t = a.next.syntaxCopy();
3819 else
3821 break;
3824 assert(dimStack.length > 0);
3825 // We're good. Replay indices in the reverse order.
3826 tid = cast(AST.TypeQualified)t;
3827 while (dimStack.length)
3829 tid.addIndex(dimStack.pop());
3831 maybeArray = null;
3833 const loc = token.loc;
3834 Identifier id = token.ident;
3835 nextToken();
3836 if (token.value == TOK.not)
3838 auto tempinst = new AST.TemplateInstance(loc, id, parseTemplateArguments());
3839 tid.addInst(tempinst);
3841 else
3842 tid.addIdent(id);
3843 continue;
3845 case TOK.leftBracket:
3847 if (dontLookDotIdents) // workaround for https://issues.dlang.org/show_bug.cgi?id=14911
3848 goto Lend;
3850 nextToken();
3851 AST.Type t = maybeArray ? maybeArray : cast(AST.Type)tid;
3852 if (token.value == TOK.rightBracket)
3854 // It's a dynamic array, and we're done:
3855 // T[].U does not make sense.
3856 t = new AST.TypeDArray(t);
3857 nextToken();
3858 return t;
3860 else if (isDeclaration(&token, NeedDeclaratorId.no, TOK.rightBracket, null))
3862 // This can be one of two things:
3863 // 1 - an associative array declaration, T[type]
3864 // 2 - an associative array declaration, T[expr]
3865 // These can only be disambiguated later.
3866 AST.Type index = parseType(); // [ type ]
3867 maybeArray = new AST.TypeAArray(t, index);
3868 check(TOK.rightBracket);
3870 else
3872 // This can be one of three things:
3873 // 1 - an static array declaration, T[expr]
3874 // 2 - a slice, T[expr .. expr]
3875 // 3 - a template parameter pack index expression, T[expr].U
3876 // 1 and 3 can only be disambiguated later.
3877 //printf("it's type[expression]\n");
3878 inBrackets++;
3879 AST.Expression e = parseAssignExp(); // [ expression ]
3880 if (token.value == TOK.slice)
3882 // It's a slice, and we're done.
3883 nextToken();
3884 AST.Expression e2 = parseAssignExp(); // [ exp .. exp ]
3885 t = new AST.TypeSlice(t, e, e2);
3886 inBrackets--;
3887 check(TOK.rightBracket);
3888 return t;
3890 else
3892 maybeArray = new AST.TypeSArray(t, e);
3893 inBrackets--;
3894 check(TOK.rightBracket);
3895 continue;
3898 break;
3900 default:
3901 goto Lend;
3904 Lend:
3905 return maybeArray ? maybeArray : cast(AST.Type)tid;
3908 /******************************************
3909 * Parse suffixes to type t.
3911 * []
3912 * [AssignExpression]
3913 * [AssignExpression .. AssignExpression]
3914 * [Type]
3915 * delegate Parameters MemberFunctionAttributes(opt)
3916 * function Parameters FunctionAttributes(opt)
3917 * Params:
3918 * t = the already parsed type
3919 * Returns:
3920 * t with the suffixes added
3921 * See_Also:
3922 * https://dlang.org/spec/declaration.html#TypeSuffixes
3924 private AST.Type parseTypeSuffixes(AST.Type t)
3926 //printf("parseTypeSuffixes()\n");
3927 while (1)
3929 switch (token.value)
3931 case TOK.mul:
3932 t = new AST.TypePointer(t);
3933 nextToken();
3934 continue;
3936 case TOK.leftBracket:
3937 // Handle []. Make sure things like
3938 // int[3][1] a;
3939 // is (array[1] of array[3] of int)
3940 nextToken();
3941 if (token.value == TOK.rightBracket)
3943 t = new AST.TypeDArray(t); // []
3944 nextToken();
3946 else if (isDeclaration(&token, NeedDeclaratorId.no, TOK.rightBracket, null))
3948 // It's an associative array declaration
3949 //printf("it's an associative array\n");
3950 AST.Type index = parseType(); // [ type ]
3951 t = new AST.TypeAArray(t, index);
3952 check(TOK.rightBracket);
3954 else
3956 //printf("it's type[expression]\n");
3957 inBrackets++;
3958 AST.Expression e = parseAssignExp(); // [ expression ]
3959 if (!e)
3961 inBrackets--;
3962 check(TOK.rightBracket);
3963 continue;
3965 if (token.value == TOK.slice)
3967 nextToken();
3968 AST.Expression e2 = parseAssignExp(); // [ exp .. exp ]
3969 t = new AST.TypeSlice(t, e, e2);
3971 else
3973 t = new AST.TypeSArray(t, e);
3975 inBrackets--;
3976 check(TOK.rightBracket);
3978 continue;
3980 case TOK.delegate_:
3981 case TOK.function_:
3983 // Handle delegate declaration:
3984 // t delegate(parameter list) nothrow pure
3985 // t function(parameter list) nothrow pure
3986 const save = token.value;
3987 nextToken();
3989 auto parameterList = parseParameterList(null);
3991 StorageClass stc = parsePostfix(STC.undefined_, null);
3992 auto tf = new AST.TypeFunction(parameterList, t, linkage, stc);
3993 if (stc & (STC.const_ | STC.immutable_ | STC.shared_ | STC.wild | STC.return_))
3995 if (save == TOK.function_)
3996 error("`const`/`immutable`/`shared`/`inout`/`return` attributes are only valid for non-static member functions");
3997 else
3998 tf = cast(AST.TypeFunction)tf.addSTC(stc);
4000 t = save == TOK.delegate_ ? new AST.TypeDelegate(tf) : new AST.TypePointer(tf); // pointer to function
4001 continue;
4003 default:
4004 return t;
4006 assert(0);
4008 assert(0);
4011 /**********************
4012 * Parse Declarator
4013 * Params:
4014 * t = base type to start with
4015 * palt = OR in 1 for C-style function pointer declaration syntax,
4016 * 2 for C-style array declaration syntax, otherwise don't modify
4017 * pident = set to Identifier if there is one, null if not
4018 * tpl = if !null, then set to TemplateParameterList
4019 * storageClass = any storage classes seen so far
4020 * pdisable = set to true if @disable seen
4021 * pudas = any user defined attributes seen so far. Merged with any more found
4022 * Returns:
4023 * type declared
4024 * Reference: https://dlang.org/spec/declaration.html#Declarator
4026 private AST.Type parseDeclarator(AST.Type t, ref int palt, Identifier* pident,
4027 AST.TemplateParameters** tpl = null, StorageClass storageClass = 0,
4028 bool* pdisable = null, AST.Expressions** pudas = null)
4030 //printf("parseDeclarator(tpl = %p)\n", tpl);
4031 t = parseTypeSuffixes(t);
4032 AST.Type ts;
4033 switch (token.value)
4035 case TOK.identifier:
4036 if (pident)
4037 *pident = token.ident;
4038 else
4039 error("unexpected identifier `%s` in declarator", token.ident.toChars());
4040 ts = t;
4041 nextToken();
4042 break;
4044 case TOK.leftParenthesis:
4046 // like: T (*fp)();
4047 // like: T ((*fp))();
4048 if (peekNext() == TOK.mul || peekNext() == TOK.leftParenthesis)
4050 /* Parse things with parentheses around the identifier, like:
4051 * int (*ident[3])[]
4052 * although the D style would be:
4053 * int[]*[3] ident
4055 palt |= 1;
4056 nextToken();
4057 ts = parseDeclarator(t, palt, pident);
4058 check(TOK.rightParenthesis);
4059 break;
4061 ts = t;
4063 Token* peekt = &token;
4064 /* Completely disallow C-style things like:
4065 * T (a);
4066 * Improve error messages for the common bug of a missing return type
4067 * by looking to see if (a) looks like a parameter list.
4069 if (isParameters(&peekt))
4071 error("function declaration without return type. (Note that constructors are always named `this`)");
4073 else
4074 error("unexpected `(` in declarator");
4075 break;
4077 default:
4078 ts = t;
4079 break;
4082 // parse DeclaratorSuffixes
4083 while (1)
4085 switch (token.value)
4087 static if (CARRAYDECL)
4089 /* Support C style array syntax:
4090 * int ident[]
4091 * as opposed to D-style:
4092 * int[] ident
4094 case TOK.leftBracket:
4096 // This is the old C-style post [] syntax.
4097 AST.TypeNext ta;
4098 nextToken();
4099 if (token.value == TOK.rightBracket)
4101 // It's a dynamic array
4102 ta = new AST.TypeDArray(t); // []
4103 nextToken();
4104 palt |= 2;
4106 else if (isDeclaration(&token, NeedDeclaratorId.no, TOK.rightBracket, null))
4108 // It's an associative array
4109 //printf("it's an associative array\n");
4110 AST.Type index = parseType(); // [ type ]
4111 check(TOK.rightBracket);
4112 ta = new AST.TypeAArray(t, index);
4113 palt |= 2;
4115 else
4117 //printf("It's a static array\n");
4118 AST.Expression e = parseAssignExp(); // [ expression ]
4119 ta = new AST.TypeSArray(t, e);
4120 check(TOK.rightBracket);
4121 palt |= 2;
4124 /* Insert ta into
4125 * ts -> ... -> t
4126 * so that
4127 * ts -> ... -> ta -> t
4129 AST.Type* pt;
4130 for (pt = &ts; *pt != t; pt = &(cast(AST.TypeNext)*pt).next)
4133 *pt = ta;
4134 continue;
4137 case TOK.leftParenthesis:
4139 if (tpl)
4141 Token* tk = peekPastParen(&token);
4142 if (tk.value == TOK.leftParenthesis)
4144 /* Look ahead to see if this is (...)(...),
4145 * i.e. a function template declaration
4147 //printf("function template declaration\n");
4149 // Gather template parameter list
4150 *tpl = parseTemplateParameterList();
4152 else if (tk.value == TOK.assign)
4154 /* or (...) =,
4155 * i.e. a variable template declaration
4157 //printf("variable template declaration\n");
4158 *tpl = parseTemplateParameterList();
4159 break;
4163 auto parameterList = parseParameterList(null);
4165 /* Parse const/immutable/shared/inout/nothrow/pure/return postfix
4167 // merge prefix storage classes
4168 StorageClass stc = parsePostfix(storageClass, pudas);
4170 AST.Type tf = new AST.TypeFunction(parameterList, t, linkage, stc);
4171 tf = tf.addSTC(stc);
4172 if (pdisable)
4173 *pdisable = stc & STC.disable ? true : false;
4175 /* Insert tf into
4176 * ts -> ... -> t
4177 * so that
4178 * ts -> ... -> tf -> t
4180 AST.Type* pt;
4181 for (pt = &ts; *pt != t; pt = &(cast(AST.TypeNext)*pt).next)
4184 *pt = tf;
4185 break;
4187 default:
4188 break;
4190 break;
4192 return ts;
4195 private void parseStorageClasses(ref StorageClass storage_class, ref LINK link,
4196 ref bool setAlignment, ref AST.Expression ealign, ref AST.Expressions* udas,
4197 out Loc linkloc)
4199 StorageClass stc;
4200 bool sawLinkage = false; // seen a linkage declaration
4202 linkloc = Loc.initial;
4204 while (1)
4206 switch (token.value)
4208 case TOK.const_:
4209 if (peekNext() == TOK.leftParenthesis)
4210 break; // const as type constructor
4211 stc = STC.const_; // const as storage class
4212 goto L1;
4214 case TOK.immutable_:
4215 if (peekNext() == TOK.leftParenthesis)
4216 break;
4217 stc = STC.immutable_;
4218 goto L1;
4220 case TOK.shared_:
4221 if (peekNext() == TOK.leftParenthesis)
4222 break;
4223 stc = STC.shared_;
4224 goto L1;
4226 case TOK.inout_:
4227 if (peekNext() == TOK.leftParenthesis)
4228 break;
4229 stc = STC.wild;
4230 goto L1;
4232 case TOK.static_:
4233 stc = STC.static_;
4234 goto L1;
4236 case TOK.final_:
4237 stc = STC.final_;
4238 goto L1;
4240 case TOK.auto_:
4241 stc = STC.auto_;
4242 goto L1;
4244 case TOK.scope_:
4245 stc = STC.scope_;
4246 goto L1;
4248 case TOK.override_:
4249 stc = STC.override_;
4250 goto L1;
4252 case TOK.abstract_:
4253 stc = STC.abstract_;
4254 goto L1;
4256 case TOK.synchronized_:
4257 stc = STC.synchronized_;
4258 goto L1;
4260 case TOK.deprecated_:
4261 stc = STC.deprecated_;
4262 goto L1;
4264 case TOK.nothrow_:
4265 stc = STC.nothrow_;
4266 goto L1;
4268 case TOK.pure_:
4269 stc = STC.pure_;
4270 goto L1;
4272 case TOK.ref_:
4273 stc = STC.ref_;
4274 goto L1;
4276 case TOK.gshared:
4277 stc = STC.gshared;
4278 goto L1;
4280 case TOK.enum_:
4282 const tv = peekNext();
4283 if (tv == TOK.leftCurly || tv == TOK.colon)
4284 break;
4285 if (tv == TOK.identifier)
4287 const nextv = peekNext2();
4288 if (nextv == TOK.leftCurly || nextv == TOK.colon || nextv == TOK.semicolon)
4289 break;
4291 stc = STC.manifest;
4292 goto L1;
4295 case TOK.at:
4297 stc = parseAttribute(udas);
4298 if (stc)
4299 goto L1;
4300 continue;
4303 storage_class = appendStorageClass(storage_class, stc);
4304 nextToken();
4305 continue;
4307 case TOK.extern_:
4309 if (peekNext() != TOK.leftParenthesis)
4311 stc = STC.extern_;
4312 goto L1;
4315 if (sawLinkage)
4316 error("redundant linkage declaration");
4317 sawLinkage = true;
4318 linkloc = token.loc;
4319 auto res = parseLinkage();
4320 link = res.link;
4321 if (res.idents || res.identExps)
4323 error("C++ name spaces not allowed here");
4325 if (res.cppmangle != CPPMANGLE.def)
4327 error("C++ mangle declaration not allowed here");
4329 continue;
4331 case TOK.align_:
4333 nextToken();
4334 setAlignment = true;
4335 if (token.value == TOK.leftParenthesis)
4337 nextToken();
4338 ealign = parseExpression();
4339 check(TOK.rightParenthesis);
4341 continue;
4343 default:
4344 break;
4346 break;
4350 /**********************************
4351 * Parse Declarations.
4352 * These can be:
4353 * 1. declarations at global/class level
4354 * 2. declarations at statement level
4355 * Returns:
4356 * array of Declarations.
4358 private AST.Dsymbols* parseDeclarations(bool autodecl, PrefixAttributes!AST* pAttrs, const(char)* comment)
4360 StorageClass storage_class = STC.undefined_;
4361 LINK link = linkage;
4362 Loc linkloc = this.linkLoc;
4363 bool setAlignment = false;
4364 AST.Expression ealign;
4365 AST.Expressions* udas = null;
4367 //printf("parseDeclarations() %s\n", token.toChars());
4368 if (!comment)
4369 comment = token.blockComment.ptr;
4371 /* Look for AliasReassignment
4373 if (token.value == TOK.identifier && peekNext() == TOK.assign)
4374 return parseAliasReassignment(comment);
4376 /* Declarations that start with `alias`
4378 bool isAliasDeclaration = false;
4379 auto aliasLoc = token.loc;
4380 if (token.value == TOK.alias_)
4382 if (auto a = parseAliasDeclarations(comment))
4383 return a;
4384 /* Handle these later:
4385 * alias StorageClasses type ident;
4387 isAliasDeclaration = true;
4390 AST.Type ts;
4392 if (!autodecl)
4394 parseStorageClasses(storage_class, link, setAlignment, ealign, udas, linkloc);
4396 if (token.value == TOK.enum_)
4398 AST.Dsymbol d = parseEnum();
4399 auto a = new AST.Dsymbols();
4400 a.push(d);
4402 if (udas)
4404 d = new AST.UserAttributeDeclaration(udas, a);
4405 a = new AST.Dsymbols();
4406 a.push(d);
4409 addComment(d, comment);
4410 return a;
4412 if (token.value == TOK.struct_ ||
4413 token.value == TOK.union_ ||
4414 token.value == TOK.class_ ||
4415 token.value == TOK.interface_)
4417 AST.Dsymbol s = parseAggregate();
4418 auto a = new AST.Dsymbols();
4419 a.push(s);
4421 if (storage_class)
4423 s = new AST.StorageClassDeclaration(storage_class, a);
4424 a = new AST.Dsymbols();
4425 a.push(s);
4427 if (setAlignment)
4429 s = new AST.AlignDeclaration(s.loc, ealign, a);
4430 a = new AST.Dsymbols();
4431 a.push(s);
4433 if (link != linkage)
4435 s = new AST.LinkDeclaration(linkloc, link, a);
4436 a = new AST.Dsymbols();
4437 a.push(s);
4439 if (udas)
4441 s = new AST.UserAttributeDeclaration(udas, a);
4442 a = new AST.Dsymbols();
4443 a.push(s);
4446 addComment(s, comment);
4447 return a;
4450 /* Look for auto initializers:
4451 * storage_class identifier = initializer;
4452 * storage_class identifier(...) = initializer;
4454 if ((storage_class || udas) && token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign))
4456 AST.Dsymbols* a = parseAutoDeclarations(storage_class, comment);
4457 if (udas)
4459 AST.Dsymbol s = new AST.UserAttributeDeclaration(udas, a);
4460 a = new AST.Dsymbols();
4461 a.push(s);
4463 return a;
4466 /* Look for return type inference for template functions.
4469 Token* tk;
4470 if ((storage_class || udas) && token.value == TOK.identifier && skipParens(peek(&token), &tk) &&
4471 skipAttributes(tk, &tk) &&
4472 (tk.value == TOK.leftParenthesis || tk.value == TOK.leftCurly || tk.value == TOK.in_ || tk.value == TOK.out_ || tk.value == TOK.goesTo ||
4473 tk.value == TOK.do_ || tk.value == TOK.identifier && tk.ident == Id._body))
4475 if (tk.value == TOK.identifier && tk.ident == Id._body)
4476 usageOfBodyKeyword();
4478 ts = null;
4480 else
4482 ts = parseBasicType();
4483 ts = parseTypeSuffixes(ts);
4488 if (pAttrs)
4490 storage_class |= pAttrs.storageClass;
4491 //pAttrs.storageClass = STC.undefined_;
4494 AST.Type tfirst = null;
4495 auto a = new AST.Dsymbols();
4497 while (1)
4499 AST.TemplateParameters* tpl = null;
4500 bool disable;
4501 int alt = 0;
4503 const loc = token.loc;
4504 Identifier ident;
4505 auto t = parseDeclarator(ts, alt, &ident, &tpl, storage_class, &disable, &udas);
4506 assert(t);
4507 if (!tfirst)
4508 tfirst = t;
4509 else if (t != tfirst)
4510 error(token.loc, "multiple declarations must have the same type, not `%s` and `%s`", tfirst.toChars(), t.toChars());
4512 if (token.value == TOK.colon && !ident && t.ty != Tfunction)
4514 // Unnamed bit field
4515 ident = Identifier.generateAnonymousId("BitField");
4518 bool isThis = (t.ty == Tident && (cast(AST.TypeIdentifier)t).ident == Id.This && token.value == TOK.assign);
4519 if (ident)
4520 checkCstyleTypeSyntax(loc, t, alt, ident);
4521 else if (!isThis && (t != AST.Type.terror))
4522 noIdentifierForDeclarator(t);
4524 if (isAliasDeclaration)
4526 AST.Declaration v;
4527 AST.Initializer _init = null;
4529 /* Aliases can no longer have multiple declarators, storage classes,
4530 * linkages, or auto declarations.
4531 * These never made any sense, anyway.
4532 * The code below needs to be fixed to reject them.
4533 * The grammar has already been fixed to preclude them.
4536 if (udas)
4537 error("user-defined attributes not allowed for `alias` declarations");
4539 if (token.value == TOK.assign)
4541 nextToken();
4542 _init = parseInitializer();
4544 if (_init)
4546 error("alias cannot have initializer");
4548 v = new AST.AliasDeclaration(aliasLoc, ident, t);
4550 v.storage_class = storage_class;
4551 if (pAttrs)
4553 /* AliasDeclaration distinguish @safe, @system, @trusted attributes
4554 * on prefix and postfix.
4555 * @safe alias void function() FP1;
4556 * alias @safe void function() FP2; // FP2 is not @safe
4557 * alias void function() @safe FP3;
4559 pAttrs.storageClass &= STC.safeGroup;
4561 AST.Dsymbol s = v;
4563 if (link != linkage)
4565 auto ax = new AST.Dsymbols();
4566 ax.push(v);
4567 s = new AST.LinkDeclaration(linkloc, link, ax);
4569 a.push(s);
4570 switch (token.value)
4572 case TOK.semicolon:
4573 nextToken();
4574 addComment(s, comment);
4575 break;
4577 case TOK.comma:
4578 nextToken();
4579 addComment(s, comment);
4580 continue;
4582 default:
4583 error("semicolon expected to close `alias` declaration, not `%s`", token.toChars());
4584 break;
4587 else if (t.ty == Tfunction)
4589 /* @@@DEPRECATED_2.115@@@
4590 * change to error, deprecated in 2.105.1 */
4591 if (storage_class & STC.manifest)
4592 deprecation("function cannot have enum storage class");
4594 AST.Expression constraint = null;
4595 //printf("%s funcdecl t = %s, storage_class = x%lx\n", loc.toChars(), t.toChars(), storage_class);
4596 auto f = new AST.FuncDeclaration(loc, Loc.initial, ident, storage_class | (disable ? STC.disable : 0), t);
4597 if (pAttrs)
4598 pAttrs.storageClass = STC.undefined_;
4599 if (tpl)
4600 constraint = parseConstraint();
4601 AST.Dsymbol s = parseContracts(f, !!tpl);
4602 auto tplIdent = s.ident;
4604 if (link != linkage)
4606 auto ax = new AST.Dsymbols();
4607 ax.push(s);
4608 s = new AST.LinkDeclaration(linkloc, link, ax);
4610 if (udas)
4612 auto ax = new AST.Dsymbols();
4613 ax.push(s);
4614 s = new AST.UserAttributeDeclaration(udas, ax);
4617 /* A template parameter list means it's a function template
4619 if (tpl)
4621 // @@@DEPRECATED_2.114@@@
4622 // Both deprecated in 2.104, change to error
4623 if (storage_class & STC.override_)
4624 deprecation(loc, "a function template is not virtual so cannot be marked `override`");
4625 else if (storage_class & STC.abstract_)
4626 deprecation(loc, "a function template is not virtual so cannot be marked `abstract`");
4628 // Wrap a template around the function declaration
4629 auto decldefs = new AST.Dsymbols();
4630 decldefs.push(s);
4631 auto tempdecl = new AST.TemplateDeclaration(loc, tplIdent, tpl, constraint, decldefs);
4632 s = tempdecl;
4634 StorageClass stc2 = STC.undefined_;
4635 if (storage_class & STC.static_)
4637 assert(f.storage_class & STC.static_);
4638 f.storage_class &= ~STC.static_;
4639 stc2 |= STC.static_;
4641 if (storage_class & STC.deprecated_)
4643 assert(f.storage_class & STC.deprecated_);
4644 f.storage_class &= ~STC.deprecated_;
4645 stc2 |= STC.deprecated_;
4647 if (stc2 != STC.undefined_)
4649 auto ax = new AST.Dsymbols();
4650 ax.push(s);
4651 s = new AST.StorageClassDeclaration(stc2, ax);
4654 a.push(s);
4655 addComment(s, comment);
4657 else if (ident)
4659 AST.Expression width;
4660 if (token.value == TOK.colon)
4662 nextToken();
4663 width = parseCondExp();
4666 AST.Initializer _init = null;
4667 if (token.value == TOK.assign)
4669 nextToken();
4670 _init = parseInitializer();
4673 AST.Dsymbol s;
4674 if (width)
4676 if (_init)
4677 error("initializer not allowed for bit-field declaration");
4678 if (storage_class)
4679 error("storage class not allowed for bit-field declaration");
4680 s = new AST.BitFieldDeclaration(width.loc, t, ident, width);
4682 else
4684 auto v = new AST.VarDeclaration(loc, t, ident, _init);
4685 v.storage_class = storage_class;
4686 if (pAttrs)
4687 pAttrs.storageClass = STC.undefined_;
4688 s = v;
4691 if (tpl && _init)
4693 auto a2 = new AST.Dsymbols();
4694 a2.push(s);
4695 auto tempdecl = new AST.TemplateDeclaration(loc, ident, tpl, null, a2, 0);
4696 s = tempdecl;
4698 if (setAlignment)
4700 auto ax = new AST.Dsymbols();
4701 ax.push(s);
4702 s = new AST.AlignDeclaration(s.loc, ealign, ax);
4704 if (link != linkage)
4706 auto ax = new AST.Dsymbols();
4707 ax.push(s);
4708 s = new AST.LinkDeclaration(linkloc, link, ax);
4710 if (udas)
4712 auto ax = new AST.Dsymbols();
4713 ax.push(s);
4714 s = new AST.UserAttributeDeclaration(udas, ax);
4716 a.push(s);
4717 switch (token.value)
4719 case TOK.semicolon:
4720 nextToken();
4721 addComment(s, comment);
4722 break;
4724 case TOK.comma:
4725 nextToken();
4726 addComment(s, comment);
4727 continue;
4729 default:
4730 if (loc.linnum != token.loc.linnum)
4732 error(token.loc, "semicolon needed to end declaration of `%s`, instead of `%s`", s.toChars(), token.toChars());
4733 eSink.errorSupplemental(loc, "`%s` declared here", s.toChars());
4735 else
4737 error(token.loc, "semicolon needed to end declaration of `%s` instead of `%s`", s.toChars(), token.toChars());
4739 break;
4742 break;
4744 return a;
4747 /// Report an error that a declaration of type `t` is missing an identifier
4748 /// The parser is expected to sit on the next token after the type.
4749 private void noIdentifierForDeclarator(AST.Type t)
4751 error("no identifier for declarator `%s`", t.toChars());
4752 // A common mistake is to use a reserved keyword as an identifier, e.g. `in` or `out`
4753 if (token.isKeyword)
4755 eSink.errorSupplemental(token.loc, "`%s` is a keyword, perhaps append `_` to make it an identifier", token.toChars());
4756 nextToken();
4760 /********************************
4761 * Parse AliasReassignment:
4762 * identifier = type;
4763 * Parser is sitting on the identifier.
4764 * https://dlang.org/spec/declaration.html#alias-reassignment
4765 * Params:
4766 * comment = if not null, comment to attach to symbol
4767 * Returns:
4768 * array of symbols
4770 private AST.Dsymbols* parseAliasReassignment(const(char)* comment)
4772 const loc = token.loc;
4773 auto ident = token.ident;
4774 nextToken();
4775 nextToken(); // advance past =
4776 auto t = parseType();
4777 AST.Dsymbol s = new AST.AliasAssign(loc, ident, t, null);
4778 check(TOK.semicolon, "alias reassignment");
4779 addComment(s, comment);
4780 auto a = new AST.Dsymbols();
4781 a.push(s);
4782 return a;
4785 /********************************
4786 * Parse declarations that start with `alias`
4787 * Parser is sitting on the `alias`.
4788 * https://dlang.org/spec/declaration.html#alias
4789 * Params:
4790 * comment = if not null, comment to attach to symbol
4791 * Returns:
4792 * array of symbols
4794 private AST.Dsymbols* parseAliasDeclarations(const(char)* comment)
4796 const loc = token.loc;
4797 nextToken();
4798 Loc linkloc = this.linkLoc;
4799 AST.Expressions* udas;
4800 LINK link = linkage;
4801 StorageClass storage_class = STC.undefined_;
4802 AST.Expression ealign;
4803 bool setAlignment = false;
4805 /* Look for:
4806 * alias Identifier this;
4807 * https://dlang.org/spec/class.html#alias-this
4809 if (token.value == TOK.identifier && peekNext() == TOK.this_)
4811 auto s = new AST.AliasThis(loc, token.ident);
4812 nextToken();
4813 check(TOK.this_);
4814 check(TOK.semicolon, "`alias Identifier this`");
4815 auto a = new AST.Dsymbols();
4816 a.push(s);
4817 addComment(s, comment);
4818 return a;
4820 /* Look for:
4821 * alias this = identifier;
4823 if (token.value == TOK.this_ && peekNext() == TOK.assign && peekNext2() == TOK.identifier)
4825 check(TOK.this_);
4826 check(TOK.assign);
4827 auto s = new AST.AliasThis(loc, token.ident);
4828 nextToken();
4829 check(TOK.semicolon, "`alias this = Identifier`");
4830 auto a = new AST.Dsymbols();
4831 a.push(s);
4832 addComment(s, comment);
4833 return a;
4835 /* Look for:
4836 * alias identifier = type;
4837 * alias identifier(...) = type;
4838 * https://dlang.org/spec/declaration.html#alias
4840 if (token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign))
4842 auto a = new AST.Dsymbols();
4843 while (1)
4845 auto ident = token.ident;
4846 nextToken();
4847 AST.TemplateParameters* tpl = null;
4848 if (token.value == TOK.leftParenthesis)
4849 tpl = parseTemplateParameterList();
4850 check(TOK.assign);
4852 bool hasParsedAttributes;
4853 void parseAttributes()
4855 if (hasParsedAttributes) // only parse once
4856 return;
4857 hasParsedAttributes = true;
4858 udas = null;
4859 storage_class = STC.undefined_;
4860 link = linkage;
4861 linkloc = this.linkLoc;
4862 setAlignment = false;
4863 ealign = null;
4864 parseStorageClasses(storage_class, link, setAlignment, ealign, udas, linkloc);
4867 if (token.value == TOK.at)
4868 parseAttributes;
4870 AST.Declaration v;
4871 AST.Dsymbol s;
4873 // try to parse function type:
4874 // TypeCtors? BasicType ( Parameters ) MemberFunctionAttributes
4875 bool attributesAppended;
4876 const StorageClass funcStc = parseTypeCtor();
4877 Token* tlu = &token;
4878 Token* tk;
4879 if (token.value != TOK.function_ &&
4880 token.value != TOK.delegate_ &&
4881 isBasicType(&tlu) && tlu &&
4882 tlu.value == TOK.leftParenthesis)
4884 AST.Type tret = parseBasicType();
4885 auto parameterList = parseParameterList(null);
4887 parseAttributes();
4888 if (udas)
4889 error("user-defined attributes not allowed for `alias` declarations");
4891 attributesAppended = true;
4892 storage_class = appendStorageClass(storage_class, funcStc);
4893 AST.Type tf = new AST.TypeFunction(parameterList, tret, link, storage_class);
4894 v = new AST.AliasDeclaration(loc, ident, tf);
4896 else if (token.value == TOK.function_ ||
4897 token.value == TOK.delegate_ ||
4898 token.value == TOK.leftParenthesis &&
4899 skipAttributes(peekPastParen(&token), &tk) &&
4900 (tk.value == TOK.goesTo || tk.value == TOK.leftCurly) ||
4901 token.value == TOK.leftCurly ||
4902 token.value == TOK.identifier && peekNext() == TOK.goesTo ||
4903 token.value == TOK.ref_ && peekNext() == TOK.leftParenthesis &&
4904 skipAttributes(peekPastParen(peek(&token)), &tk) &&
4905 (tk.value == TOK.goesTo || tk.value == TOK.leftCurly) ||
4906 token.value == TOK.auto_ && peekNext() == TOK.ref_ &&
4907 peekNext2() == TOK.leftParenthesis &&
4908 skipAttributes(peekPastParen(peek(peek(&token))), &tk) &&
4909 (tk.value == TOK.goesTo || tk.value == TOK.leftCurly)
4912 // function (parameters) { statements... }
4913 // delegate (parameters) { statements... }
4914 // (parameters) { statements... }
4915 // (parameters) => expression
4916 // { statements... }
4917 // identifier => expression
4918 // ref (parameters) { statements... }
4919 // ref (parameters) => expression
4920 // auto ref (parameters) { statements... }
4921 // auto ref (parameters) => expression
4923 s = parseFunctionLiteral();
4925 if (udas !is null)
4927 if (storage_class != 0)
4928 error("cannot put a storage-class in an `alias` declaration.");
4929 // parseAttributes shouldn't have set these variables
4930 assert(link == linkage && !setAlignment && ealign is null);
4931 auto tpl_ = cast(AST.TemplateDeclaration) s;
4932 if (tpl_ is null || tpl_.members.length != 1)
4934 error("user-defined attributes are not allowed on `alias` declarations");
4936 else
4938 auto fd = cast(AST.FuncLiteralDeclaration) (*tpl_.members)[0];
4939 auto tf = cast(AST.TypeFunction) fd.type;
4940 assert(tf.parameterList.parameters.length > 0);
4941 auto as = new AST.Dsymbols();
4942 (*tf.parameterList.parameters)[0].userAttribDecl = new AST.UserAttributeDeclaration(udas, as);
4946 v = new AST.AliasDeclaration(loc, ident, s);
4948 else
4950 parseAttributes();
4951 // type
4952 if (udas)
4953 error("user-defined attributes not allowed for `alias` declarations");
4955 auto t = parseType();
4957 // Disallow meaningless storage classes on type aliases
4958 if (storage_class)
4960 // Don't raise errors for STC that are part of a function/delegate type, e.g.
4961 // `alias F = ref pure nothrow @nogc @safe int function();`
4962 auto tp = t.isTypePointer;
4963 const isFuncType = (tp && tp.next.isTypeFunction) || t.isTypeDelegate;
4964 const remStc = isFuncType ? (storage_class & ~STC.FUNCATTR) : storage_class;
4966 if (remStc)
4968 OutBuffer buf;
4969 AST.stcToBuffer(buf, remStc);
4970 // @@@DEPRECATED_2.103@@@
4971 // Deprecated in 2020-07, can be made an error in 2.103
4972 eSink.deprecation(token.loc, "storage class `%s` has no effect in type aliases", buf.peekChars());
4976 v = new AST.AliasDeclaration(loc, ident, t);
4978 if (!attributesAppended)
4979 storage_class = appendStorageClass(storage_class, funcStc);
4980 v.storage_class = storage_class;
4982 s = v;
4983 if (tpl)
4985 auto a2 = new AST.Dsymbols();
4986 a2.push(s);
4987 auto tempdecl = new AST.TemplateDeclaration(loc, ident, tpl, null, a2);
4988 s = tempdecl;
4990 if (link != linkage)
4992 auto a2 = new AST.Dsymbols();
4993 a2.push(s);
4994 s = new AST.LinkDeclaration(linkloc, link, a2);
4996 a.push(s);
4998 switch (token.value)
5000 case TOK.semicolon:
5001 nextToken();
5002 addComment(s, comment);
5003 break;
5005 case TOK.comma:
5006 nextToken();
5007 addComment(s, comment);
5008 if (token.value != TOK.identifier)
5010 error("identifier expected following comma, not `%s`", token.toChars());
5011 break;
5013 if (peekNext() != TOK.assign && peekNext() != TOK.leftParenthesis)
5015 error("`=` expected following identifier");
5016 nextToken();
5017 break;
5019 continue;
5021 default:
5022 error("semicolon expected to close `alias` declaration, not `%s`", token.toChars());
5023 break;
5025 break;
5027 return a;
5030 // alias StorageClasses type ident;
5031 return null;
5034 private AST.Dsymbol parseFunctionLiteral()
5036 const loc = token.loc;
5037 AST.TemplateParameters* tpl = null;
5038 AST.ParameterList parameterList;
5039 AST.Type tret = null;
5040 StorageClass stc = 0;
5041 TOK save = TOK.reserved;
5043 switch (token.value)
5045 case TOK.function_:
5046 case TOK.delegate_:
5047 save = token.value;
5048 nextToken();
5049 if (token.value == TOK.auto_)
5051 nextToken();
5052 if (token.value == TOK.ref_)
5054 // function auto ref (parameters) { statements... }
5055 // delegate auto ref (parameters) { statements... }
5056 stc = STC.auto_ | STC.ref_;
5057 nextToken();
5059 else
5060 error("`auto` can only be used as part of `auto ref` for function literal return values");
5062 else if (token.value == TOK.ref_)
5064 // function ref (parameters) { statements... }
5065 // delegate ref (parameters) { statements... }
5066 stc = STC.ref_;
5067 nextToken();
5069 if (token.value != TOK.leftParenthesis && token.value != TOK.leftCurly &&
5070 token.value != TOK.goesTo)
5072 // function type (parameters) { statements... }
5073 // delegate type (parameters) { statements... }
5074 tret = parseBasicType();
5075 tret = parseTypeSuffixes(tret); // function return type
5078 if (token.value == TOK.leftParenthesis)
5080 // function (parameters) { statements... }
5081 // delegate (parameters) { statements... }
5083 else
5085 // function { statements... }
5086 // delegate { statements... }
5087 break;
5089 goto case TOK.leftParenthesis;
5091 case TOK.auto_:
5093 nextToken();
5094 if (token.value == TOK.ref_)
5096 // auto ref (parameters) => expression
5097 // auto ref (parameters) { statements... }
5098 stc = STC.auto_ | STC.ref_;
5099 nextToken();
5101 else
5102 error("`auto` can only be used as part of `auto ref` for function literal return values");
5103 goto case TOK.leftParenthesis;
5105 case TOK.ref_:
5107 // ref (parameters) => expression
5108 // ref (parameters) { statements... }
5109 stc = STC.ref_;
5110 nextToken();
5111 goto case TOK.leftParenthesis;
5113 case TOK.leftParenthesis:
5115 // (parameters) => expression
5116 // (parameters) { statements... }
5117 parameterList = parseParameterList(&tpl);
5118 stc = parsePostfix(stc, null);
5119 if (StorageClass modStc = stc & STC.TYPECTOR)
5121 if (save == TOK.function_)
5123 OutBuffer buf;
5124 AST.stcToBuffer(buf, modStc);
5125 error("function literal cannot be `%s`", buf.peekChars());
5127 else
5128 save = TOK.delegate_;
5130 break;
5132 case TOK.leftCurly:
5133 // { statements... }
5134 break;
5136 case TOK.identifier:
5138 // identifier => expression
5139 parameterList.parameters = new AST.Parameters();
5140 Identifier id = Identifier.generateId("__T");
5141 AST.Type t = new AST.TypeIdentifier(loc, id);
5142 parameterList.parameters.push(new AST.Parameter(loc, STC.parameter, t, token.ident, null, null));
5144 tpl = new AST.TemplateParameters();
5145 AST.TemplateParameter tp = new AST.TemplateTypeParameter(loc, id, null, null);
5146 tpl.push(tp);
5148 nextToken();
5149 break;
5151 default:
5152 assert(0);
5155 auto tf = new AST.TypeFunction(parameterList, tret, linkage, stc);
5156 tf = cast(AST.TypeFunction)tf.addSTC(stc);
5157 auto fd = new AST.FuncLiteralDeclaration(loc, Loc.initial, tf, save, null, null, stc & STC.auto_);
5159 if (token.value == TOK.goesTo)
5161 check(TOK.goesTo);
5162 if (token.value == TOK.leftCurly)
5164 deprecation("using `(args) => { ... }` to create a delegate that returns a delegate is error-prone.");
5165 deprecationSupplemental("Use `(args) { ... }` for a multi-statement function literal or use `(args) => () { }` if you intended for the lambda to return a delegate.");
5167 const returnloc = token.loc;
5168 AST.Expression ae = parseAssignExp();
5169 fd.fbody = new AST.ReturnStatement(returnloc, ae);
5170 fd.endloc = token.loc;
5172 else
5174 parseContracts(fd);
5177 if (tpl)
5179 // Wrap a template around function fd
5180 auto decldefs = new AST.Dsymbols();
5181 decldefs.push(fd);
5182 return new AST.TemplateDeclaration(fd.loc, fd.ident, tpl, null, decldefs, false, true);
5184 return fd;
5187 /*****************************************
5188 * Parse contracts following function declaration.
5190 private AST.FuncDeclaration parseContracts(AST.FuncDeclaration f, bool isTemplateFunction = false)
5192 LINK linksave = linkage;
5194 bool literal = f.isFuncLiteralDeclaration() !is null;
5196 // The following is irrelevant, as it is overridden by sc.linkage in
5197 // TypeFunction::semantic
5198 linkage = LINK.d; // nested functions have D linkage
5199 bool requireDo = false;
5201 switch (token.value)
5203 case TOK.goesTo:
5204 if (requireDo)
5205 error("missing `do { ... }` after `in` or `out`");
5206 if (!compileEnv.shortenedMethods)
5207 error("=> shortened method not enabled, compile with compiler switch `-preview=shortenedMethods`");
5208 const returnloc = token.loc;
5209 nextToken();
5210 f.fbody = new AST.ReturnStatement(returnloc, parseExpression());
5211 f.endloc = token.loc;
5212 check(TOK.semicolon);
5213 break;
5215 case TOK.leftCurly:
5216 if (requireDo)
5217 error("missing `do { ... }` after `in` or `out`");
5218 f.fbody = parseStatement(0);
5219 f.endloc = endloc;
5220 break;
5222 case TOK.identifier:
5223 if (token.ident == Id._body)
5225 usageOfBodyKeyword();
5226 goto case TOK.do_;
5228 goto default;
5230 case TOK.do_:
5231 nextToken();
5232 f.fbody = parseStatement(ParseStatementFlags.curly);
5233 f.endloc = endloc;
5234 break;
5236 version (none)
5238 // Do we want this for function declarations, so we can do:
5239 // int x, y, foo(), z;
5240 case TOK.comma:
5241 nextToken();
5242 continue;
5245 case TOK.in_:
5246 // in { statements... }
5247 // in (expression)
5248 auto loc = token.loc;
5249 nextToken();
5250 if (!f.frequires)
5252 f.frequires = new AST.Statements;
5254 if (token.value == TOK.leftParenthesis)
5256 nextToken();
5257 AST.Expression e = parseAssignExp(), msg = null;
5258 if (token.value == TOK.comma)
5260 nextToken();
5261 if (token.value != TOK.rightParenthesis)
5263 msg = parseAssignExp();
5264 if (token.value == TOK.comma)
5265 nextToken();
5268 check(TOK.rightParenthesis);
5269 e = new AST.AssertExp(loc, e, msg);
5270 f.frequires.push(new AST.ExpStatement(loc, e));
5271 requireDo = false;
5273 else
5275 auto ret = parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_);
5276 assert(ret);
5277 f.frequires.push(ret);
5278 requireDo = true;
5280 goto L1;
5282 case TOK.out_:
5283 // out { statements... }
5284 // out (; expression)
5285 // out (identifier) { statements... }
5286 // out (identifier; expression)
5287 auto loc = token.loc;
5288 nextToken();
5289 if (!f.fensures)
5291 f.fensures = new AST.Ensures;
5293 Identifier id = null;
5294 if (token.value != TOK.leftCurly)
5296 check(TOK.leftParenthesis);
5297 if (token.value != TOK.identifier && token.value != TOK.semicolon)
5298 error("`(identifier) { ... }` or `(identifier; expression)` following `out` expected, not `%s`", token.toChars());
5299 if (token.value != TOK.semicolon)
5301 id = token.ident;
5302 nextToken();
5304 if (token.value == TOK.semicolon)
5306 nextToken();
5307 AST.Expression e = parseAssignExp(), msg = null;
5308 if (token.value == TOK.comma)
5310 nextToken();
5311 if (token.value != TOK.rightParenthesis)
5313 msg = parseAssignExp();
5314 if (token.value == TOK.comma)
5315 nextToken();
5318 check(TOK.rightParenthesis);
5319 e = new AST.AssertExp(loc, e, msg);
5320 f.fensures.push(AST.Ensure(id, new AST.ExpStatement(loc, e)));
5321 requireDo = false;
5322 goto L1;
5324 check(TOK.rightParenthesis);
5326 f.fensures.push(AST.Ensure(id, parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_)));
5327 requireDo = true;
5328 goto L1;
5330 case TOK.semicolon:
5331 if (!literal)
5333 // https://issues.dlang.org/show_bug.cgi?id=15799
5334 // Semicolon becomes a part of function declaration
5335 // only when 'do' is not required
5336 if (!requireDo)
5337 nextToken();
5338 break;
5340 goto default;
5342 default:
5343 if (literal)
5345 const(char)* sbody = requireDo ? "do " : "";
5346 error("missing `%s{ ... }` for function literal", sbody);
5348 else if (!requireDo) // allow contracts even with no body
5350 TOK t = token.value;
5351 if (t == TOK.const_ || t == TOK.immutable_ || t == TOK.inout_ || t == TOK.return_ ||
5352 t == TOK.shared_ || t == TOK.nothrow_ || t == TOK.pure_)
5353 error("'%s' cannot be placed after a template constraint", token.toChars);
5354 else if (t == TOK.at)
5355 error("attributes cannot be placed after a template constraint");
5356 else if (t == TOK.if_)
5358 if (isTemplateFunction)
5359 error("template constraint must follow parameter lists and attributes");
5360 else
5361 error("cannot use function constraints for non-template functions. Use `static if` instead");
5363 else
5364 error("semicolon expected following function declaration, not `%s`", token.toChars());
5366 break;
5368 if (literal && !f.fbody)
5370 // Set empty function body for error recovery
5371 f.fbody = new AST.CompoundStatement(Loc.initial, cast(AST.Statement)null);
5374 linkage = linksave;
5376 return f;
5379 /*****************************************
5381 private void checkDanglingElse(Loc elseloc)
5383 if (token.value != TOK.else_ && token.value != TOK.catch_ && token.value != TOK.finally_ && lookingForElse.linnum != 0)
5385 eSink.warning(elseloc, "else is dangling, add { } after condition at %s", lookingForElse.toChars());
5389 /* *************************
5390 * Issue errors if C-style syntax
5391 * Params:
5392 * alt = !=0 for C-style syntax
5394 private void checkCstyleTypeSyntax(Loc loc, AST.Type t, int alt, Identifier ident)
5396 if (!alt)
5397 return;
5399 const(char)* sp = !ident ? "" : " ";
5400 const(char)* s = !ident ? "" : ident.toChars();
5401 error(loc, "instead of C-style syntax, use D-style `%s%s%s`", t.toChars(), sp, s);
5404 /*****************************
5405 * Ad-hoc error message for missing or extra parens that close a condition.
5406 * Params:
5407 * start = "if", "while", etc. Must be 0 terminated.
5408 * param = if the condition is a declaration, this will be non-null
5409 * condition = if param is null, then this is the conditional Expression. If condition is null,
5410 * then an error in the condition was already reported.
5412 private void closeCondition(string start, AST.Parameter param, AST.Expression condition)
5414 string format;
5415 if (token.value != TOK.rightParenthesis && condition)
5417 format = "missing closing `)` after `%s (%s`";
5419 else
5420 check(TOK.rightParenthesis);
5421 if (token.value == TOK.rightParenthesis)
5423 if (condition) // if not an error in condition
5424 format = "extra `)` after `%s (%s)`";
5425 nextToken();
5427 if (format)
5428 error(token.loc, format.ptr, start.ptr, param ? "declaration".ptr : condition.toChars());
5431 /*****************************************
5432 * Parses `foreach` statements, `static foreach` statements and
5433 * `static foreach` declarations.
5434 * Params:
5435 * Foreach = one of Statement, StaticForeachStatement, StaticForeachDeclaration
5436 * loc = location of foreach
5437 * pLastDecl = non-null for StaticForeachDeclaration
5438 * Returns:
5439 * the Foreach generated
5441 private Foreach parseForeach(alias Foreach)(Loc loc, AST.Dsymbol* pLastDecl)
5443 static if (is(Foreach == AST.StaticForeachStatement) || is(Foreach == AST.StaticForeachDeclaration))
5445 nextToken();
5448 TOK op = token.value;
5450 nextToken();
5451 check(TOK.leftParenthesis);
5453 auto parameters = new AST.Parameters();
5454 Identifier lastai;
5455 while (1)
5457 Identifier ai = null;
5458 AST.Type at;
5459 Loc aloc;
5461 StorageClass storageClass = 0;
5462 StorageClass stc = 0;
5463 Lagain:
5464 if (stc)
5466 storageClass = appendStorageClass(storageClass, stc);
5467 nextToken();
5469 switch (token.value)
5471 case TOK.ref_:
5472 stc = STC.ref_;
5473 goto Lagain;
5475 case TOK.scope_:
5476 stc = STC.scope_;
5477 goto Lagain;
5479 case TOK.out_:
5480 error("cannot declare `out` loop variable, use `ref` instead");
5481 stc = STC.out_;
5482 goto Lagain;
5484 case TOK.auto_:
5485 error("cannot declare `auto` loop variable, omit `auto` to still get type inference");
5486 stc = STC.auto_;
5487 goto Lagain;
5489 case TOK.enum_:
5490 stc = STC.manifest;
5491 goto Lagain;
5493 case TOK.alias_:
5494 storageClass = appendStorageClass(storageClass, STC.alias_);
5495 nextToken();
5496 break;
5498 case TOK.const_:
5499 if (peekNext() != TOK.leftParenthesis)
5501 stc = STC.const_;
5502 goto Lagain;
5504 break;
5506 case TOK.immutable_:
5507 if (peekNext() != TOK.leftParenthesis)
5509 stc = STC.immutable_;
5510 goto Lagain;
5512 break;
5514 case TOK.shared_:
5515 if (peekNext() != TOK.leftParenthesis)
5517 stc = STC.shared_;
5518 goto Lagain;
5520 break;
5522 case TOK.inout_:
5523 if (peekNext() != TOK.leftParenthesis)
5525 stc = STC.wild;
5526 goto Lagain;
5528 break;
5530 default:
5531 break;
5533 if (token.value == TOK.identifier)
5535 const tv = peekNext();
5536 if (tv == TOK.comma || tv == TOK.semicolon || tv == TOK.rightParenthesis)
5538 lastai = token.ident;
5539 ai = token.ident;
5540 at = null; // infer argument type
5541 aloc = token.loc;
5542 nextToken();
5543 goto Larg;
5546 at = parseType(&ai);
5547 if (!ai)
5548 noIdentifierForDeclarator(at);
5549 Larg:
5550 auto p = new AST.Parameter(aloc, storageClass, at, ai, null, null);
5551 parameters.push(p);
5552 if (token.value == TOK.comma)
5554 nextToken();
5555 continue;
5557 break;
5559 if (token.value != TOK.semicolon)
5561 error("missing `; expression` before `)` of `foreach`");
5562 nextToken();
5563 if (lastai && parameters.length >= 2)
5565 eSink.errorSupplemental(loc, "perhaps the `;` goes before `%s`", lastai.toChars());
5567 return null;
5569 nextToken();
5571 AST.Expression aggr = parseExpression();
5572 if (token.value == TOK.slice && parameters.length == 1)
5574 AST.Parameter p = (*parameters)[0];
5575 nextToken();
5576 AST.Expression upr = parseExpression();
5577 check(TOK.rightParenthesis);
5578 Loc endloc;
5579 static if (is(Foreach == AST.Statement) || is(Foreach == AST.StaticForeachStatement))
5581 AST.Statement _body = parseStatement(0, null, &endloc);
5583 else
5585 AST.Statement _body = null;
5587 auto rangefe = new AST.ForeachRangeStatement(loc, op, p, aggr, upr, _body, endloc);
5588 static if (is(Foreach == AST.Statement))
5590 return rangefe;
5592 else static if(is(Foreach == AST.StaticForeachDeclaration))
5594 return new AST.StaticForeachDeclaration(new AST.StaticForeach(loc, null, rangefe), parseBlock(pLastDecl));
5596 else static if (is(Foreach == AST.StaticForeachStatement))
5598 return new AST.StaticForeachStatement(loc, new AST.StaticForeach(loc, null, rangefe));
5601 else
5603 check(TOK.rightParenthesis);
5604 Loc endloc;
5605 static if (is(Foreach == AST.Statement) || is(Foreach == AST.StaticForeachStatement))
5607 AST.Statement _body = parseStatement(0, null, &endloc);
5609 else
5611 AST.Statement _body = null;
5613 auto aggrfe = new AST.ForeachStatement(loc, op, parameters, aggr, _body, endloc);
5614 static if (is(Foreach == AST.Statement))
5616 return aggrfe;
5618 else static if(is(Foreach == AST.StaticForeachDeclaration))
5620 return new AST.StaticForeachDeclaration(new AST.StaticForeach(loc, aggrfe, null), parseBlock(pLastDecl));
5622 else static if (is(Foreach == AST.StaticForeachStatement))
5624 return new AST.StaticForeachStatement(loc, new AST.StaticForeach(loc, aggrfe, null));
5630 /***
5631 * Parse an assignment condition for if or while statements.
5633 * Returns:
5634 * The variable that is declared inside the condition
5636 AST.Parameter parseAssignCondition()
5638 AST.Parameter param = null;
5639 StorageClass storageClass = 0;
5640 StorageClass stc = 0;
5641 Lwhile:
5642 while (1)
5644 switch (token.value)
5646 // parse ref for better error
5647 case TOK.ref_:
5648 stc = STC.ref_;
5649 break;
5651 case TOK.scope_:
5652 stc = STC.scope_;
5653 break;
5655 case TOK.auto_:
5656 stc = STC.auto_;
5657 break;
5659 case TOK.const_:
5660 if (peekNext() != TOK.leftParenthesis)
5662 stc = STC.const_;
5663 break;
5665 goto default;
5667 case TOK.immutable_:
5668 if (peekNext() != TOK.leftParenthesis)
5670 stc = STC.immutable_;
5671 break;
5673 goto default;
5675 case TOK.shared_:
5676 if (peekNext() != TOK.leftParenthesis)
5678 stc = STC.shared_;
5679 break;
5681 goto default;
5683 case TOK.inout_:
5684 if (peekNext() != TOK.leftParenthesis)
5686 stc = STC.wild;
5687 break;
5689 goto default;
5691 default:
5692 break Lwhile;
5694 storageClass = appendStorageClass(storageClass, stc);
5695 nextToken();
5697 auto n = peek(&token);
5698 if (storageClass != 0 && token.value == TOK.identifier && n.value == TOK.assign)
5700 Identifier ai = token.ident;
5701 AST.Type at = null; // infer parameter type
5702 const aloc = token.loc;
5703 nextToken();
5704 check(TOK.assign);
5705 param = new AST.Parameter(aloc, storageClass, at, ai, null, null);
5707 else if (isDeclaration(&token, NeedDeclaratorId.must, TOK.assign, null))
5709 Identifier ai;
5710 const aloc = token.loc;
5711 AST.Type at = parseType(&ai);
5712 check(TOK.assign);
5713 param = new AST.Parameter(aloc, storageClass, at, ai, null, null);
5715 else if (storageClass != 0)
5716 error("found `%s` while expecting `=` or identifier", n.toChars());
5718 return param;
5721 /*****************************************
5722 * Input:
5723 * flags PSxxxx
5724 * Output:
5725 * pEndloc if { ... statements ... }, store location of closing brace, otherwise loc of last token of statement
5727 AST.Statement parseStatement(int flags, const(char)** endPtr = null, Loc* pEndloc = null)
5729 AST.Statement s;
5730 AST.Condition cond;
5731 AST.Statement ifbody;
5732 AST.Statement elsebody;
5733 bool isfinal;
5734 const loc = token.loc;
5736 //printf("parseStatement()\n");
5737 if (flags & ParseStatementFlags.curly && token.value != TOK.leftCurly)
5738 error("statement expected to be `{ }`, not `%s`", token.toChars());
5740 switch (token.value)
5742 case TOK.identifier:
5744 /* A leading identifier can be a declaration, label, or expression.
5745 * The easiest case to check first is label:
5747 if (peekNext() == TOK.colonColon)
5749 // skip ident::
5750 nextToken();
5751 nextToken();
5752 error("use `.` for member lookup, not `::`");
5753 break;
5756 if (peekNext() == TOK.colon)
5758 // It's a label
5759 Identifier ident = token.ident;
5760 nextToken();
5761 nextToken();
5762 if (token.value == TOK.rightCurly)
5763 s = null;
5764 else if (token.value == TOK.leftCurly)
5765 s = parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_);
5766 else if (flags & ParseStatementFlags.curlyScope)
5767 s = parseStatement(ParseStatementFlags.semiOk | ParseStatementFlags.curlyScope);
5768 else
5769 s = parseStatement(ParseStatementFlags.semiOk);
5770 s = new AST.LabelStatement(loc, ident, s);
5771 break;
5773 goto case TOK.dot;
5775 case TOK.dot:
5776 case TOK.typeof_:
5777 case TOK.vector:
5778 case TOK.traits:
5779 /* https://issues.dlang.org/show_bug.cgi?id=15163
5780 * If tokens can be handled as
5781 * old C-style declaration or D expression, prefer the latter.
5783 if (isDeclaration(&token, NeedDeclaratorId.mustIfDstyle, TOK.reserved, null))
5784 goto Ldeclaration;
5785 goto Lexp;
5787 case TOK.assert_:
5788 case TOK.this_:
5789 case TOK.super_:
5790 case TOK.int32Literal:
5791 case TOK.uns32Literal:
5792 case TOK.int64Literal:
5793 case TOK.uns64Literal:
5794 case TOK.int128Literal:
5795 case TOK.uns128Literal:
5796 case TOK.float32Literal:
5797 case TOK.float64Literal:
5798 case TOK.float80Literal:
5799 case TOK.imaginary32Literal:
5800 case TOK.imaginary64Literal:
5801 case TOK.imaginary80Literal:
5802 case TOK.charLiteral:
5803 case TOK.wcharLiteral:
5804 case TOK.dcharLiteral:
5805 case TOK.null_:
5806 case TOK.true_:
5807 case TOK.false_:
5808 case TOK.string_:
5809 case TOK.hexadecimalString:
5810 case TOK.leftParenthesis:
5811 case TOK.cast_:
5812 case TOK.mul:
5813 case TOK.min:
5814 case TOK.add:
5815 case TOK.tilde:
5816 case TOK.not:
5817 case TOK.plusPlus:
5818 case TOK.minusMinus:
5819 case TOK.new_:
5820 case TOK.delete_:
5821 case TOK.delegate_:
5822 case TOK.function_:
5823 case TOK.typeid_:
5824 case TOK.is_:
5825 case TOK.leftBracket:
5826 case TOK.file:
5827 case TOK.fileFullPath:
5828 case TOK.line:
5829 case TOK.moduleString:
5830 case TOK.functionString:
5831 case TOK.prettyFunction:
5832 Lexp:
5834 AST.Expression exp = parseExpression();
5835 /* https://issues.dlang.org/show_bug.cgi?id=15103
5836 * Improve declaration / initialization syntax error message
5837 * Error: found 'foo' when expecting ';' following expression
5838 * becomes Error: found `(` when expecting `;` or `=`, did you mean `Foo foo = 42`?
5840 if (token.value == TOK.identifier && exp.op == EXP.identifier)
5842 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());
5843 nextToken();
5845 else
5848 * https://issues.dlang.org/show_bug.cgi?id=22529
5849 * Avoid empty declaration error in case of missing semicolon
5850 * followed by another token and another semicolon. E.g.:
5852 * foo()
5853 * return;
5855 * When the missing `;` error is emitted, token is sitting on return.
5856 * If we simply use `check` to emit the error, the token is advanced
5857 * to `;` and the empty statement error would follow. To avoid that,
5858 * we check if the next token is a semicolon and simply output the error,
5859 * otherwise we fall back on the old path (advancing the token).
5861 if (token.value != TOK.semicolon && peek(&token).value == TOK.semicolon)
5862 error("found `%s` when expecting `;` following expression", token.toChars());
5863 else
5865 if (token.value != TOK.semicolon)
5867 error("found `%s` when expecting `;` following expression", token.toChars());
5868 eSink.errorSupplemental(exp.loc, "expression: `%s`", exp.toChars());
5870 nextToken();
5873 s = new AST.ExpStatement(loc, exp);
5874 break;
5876 case TOK.static_:
5878 // Look ahead to see if it's static assert() or static if()
5879 const tv = peekNext();
5880 if (tv == TOK.assert_)
5882 s = new AST.StaticAssertStatement(parseStaticAssert());
5883 break;
5885 if (tv == TOK.if_)
5887 cond = parseStaticIfCondition();
5888 goto Lcondition;
5890 if (tv == TOK.foreach_ || tv == TOK.foreach_reverse_)
5892 s = parseForeach!(AST.StaticForeachStatement)(loc, null);
5893 if (flags & ParseStatementFlags.scope_)
5894 s = new AST.ScopeStatement(loc, s, token.loc);
5895 break;
5897 if (tv == TOK.import_)
5899 AST.Dsymbols* imports = parseImport();
5900 s = new AST.ImportStatement(loc, imports);
5901 if (flags & ParseStatementFlags.scope_)
5902 s = new AST.ScopeStatement(loc, s, token.loc);
5903 break;
5905 goto Ldeclaration;
5907 case TOK.final_:
5908 if (peekNext() == TOK.switch_)
5910 nextToken();
5911 isfinal = true;
5912 goto Lswitch;
5914 goto Ldeclaration;
5916 case TOK.wchar_:
5917 case TOK.dchar_:
5918 case TOK.bool_:
5919 case TOK.char_:
5920 case TOK.int8:
5921 case TOK.uns8:
5922 case TOK.int16:
5923 case TOK.uns16:
5924 case TOK.int32:
5925 case TOK.uns32:
5926 case TOK.int64:
5927 case TOK.uns64:
5928 case TOK.int128:
5929 case TOK.uns128:
5930 case TOK.float32:
5931 case TOK.float64:
5932 case TOK.float80:
5933 case TOK.imaginary32:
5934 case TOK.imaginary64:
5935 case TOK.imaginary80:
5936 case TOK.complex32:
5937 case TOK.complex64:
5938 case TOK.complex80:
5939 case TOK.void_:
5940 // bug 7773: int.max is always a part of expression
5941 if (peekNext() == TOK.dot)
5942 goto Lexp;
5943 if (peekNext() == TOK.leftParenthesis)
5944 goto Lexp;
5945 goto case;
5947 case TOK.alias_:
5948 case TOK.const_:
5949 case TOK.auto_:
5950 case TOK.abstract_:
5951 case TOK.extern_:
5952 case TOK.align_:
5953 case TOK.immutable_:
5954 case TOK.shared_:
5955 case TOK.inout_:
5956 case TOK.deprecated_:
5957 case TOK.nothrow_:
5958 case TOK.pure_:
5959 case TOK.ref_:
5960 case TOK.gshared:
5961 case TOK.at:
5962 case TOK.struct_:
5963 case TOK.union_:
5964 case TOK.class_:
5965 case TOK.interface_:
5966 Ldeclaration:
5968 AST.Dsymbols* a = parseDeclarations(false, null, null);
5969 if (a.length > 1)
5971 auto as = new AST.Statements();
5972 as.reserve(a.length);
5973 foreach (i; 0 .. a.length)
5975 AST.Dsymbol d = (*a)[i];
5976 s = new AST.ExpStatement(loc, d);
5977 as.push(s);
5979 s = new AST.CompoundDeclarationStatement(loc, as);
5981 else if (a.length == 1)
5983 AST.Dsymbol d = (*a)[0];
5984 s = new AST.ExpStatement(loc, d);
5986 else
5987 s = new AST.ExpStatement(loc, cast(AST.Expression)null);
5988 if (flags & ParseStatementFlags.scope_)
5989 s = new AST.ScopeStatement(loc, s, token.loc);
5990 break;
5992 case TOK.enum_:
5994 /* Determine if this is a manifest constant declaration,
5995 * or a conventional enum.
5997 AST.Dsymbol d;
5998 const tv = peekNext();
5999 if (tv == TOK.leftCurly || tv == TOK.colon)
6000 d = parseEnum();
6001 else if (tv != TOK.identifier)
6002 goto Ldeclaration;
6003 else
6005 const nextv = peekNext2();
6006 if (nextv == TOK.leftCurly || nextv == TOK.colon || nextv == TOK.semicolon)
6007 d = parseEnum();
6008 else
6009 goto Ldeclaration;
6011 s = new AST.ExpStatement(loc, d);
6012 if (flags & ParseStatementFlags.scope_)
6013 s = new AST.ScopeStatement(loc, s, token.loc);
6014 break;
6016 case TOK.mixin_:
6018 if (isDeclaration(&token, NeedDeclaratorId.mustIfDstyle, TOK.reserved, null))
6019 goto Ldeclaration;
6020 const tv = peekNext();
6021 if (tv == TOK.leftParenthesis)
6023 // mixin(string)
6024 AST.Expression e = parseAssignExp();
6025 check(TOK.semicolon, "mixin");
6026 if (e.op == EXP.mixin_)
6028 AST.MixinExp cpe = cast(AST.MixinExp)e;
6029 s = new AST.MixinStatement(loc, cpe.exps);
6031 else
6033 s = new AST.ExpStatement(loc, e);
6035 break;
6037 else if (tv == TOK.template_)
6039 // mixin template
6040 nextToken();
6041 AST.Dsymbol d = parseTemplateDeclaration(true);
6042 s = new AST.ExpStatement(loc, d);
6043 break;
6045 AST.Dsymbol d = parseMixin();
6046 s = new AST.ExpStatement(loc, d);
6047 if (flags & ParseStatementFlags.scope_)
6048 s = new AST.ScopeStatement(loc, s, token.loc);
6049 break;
6051 case TOK.leftCurly:
6053 const lcLoc = token.loc;
6054 const lookingForElseSave = lookingForElse;
6055 lookingForElse = Loc.initial;
6057 nextToken();
6058 //if (token.value == TOK.semicolon)
6059 // error("use `{ }` for an empty statement, not `;`");
6060 auto statements = new AST.Statements();
6061 while (token.value != TOK.rightCurly && token.value != TOK.endOfFile)
6063 statements.push(parseStatement(ParseStatementFlags.curlyScope | ParseStatementFlags.semiOk));
6065 if (endPtr)
6066 *endPtr = token.ptr;
6067 endloc = token.loc;
6068 if (pEndloc)
6070 *pEndloc = token.loc;
6071 pEndloc = null; // don't set it again
6073 s = new AST.CompoundStatement(loc, statements);
6074 if (flags & (ParseStatementFlags.scope_ | ParseStatementFlags.curlyScope))
6075 s = new AST.ScopeStatement(loc, s, token.loc);
6076 if (token.value != TOK.rightCurly)
6078 error(token.loc, "matching `}` expected following compound statement, not `%s`",
6079 token.toChars());
6080 eSink.errorSupplemental(lcLoc, "unmatched `{`");
6082 else
6083 nextToken();
6084 lookingForElse = lookingForElseSave;
6085 break;
6087 case TOK.while_:
6089 nextToken();
6090 check(TOK.leftParenthesis);
6091 auto param = parseAssignCondition();
6092 auto condition = parseExpression();
6093 closeCondition("while", param, condition);
6095 Loc endloc;
6096 AST.Statement _body = parseStatement(ParseStatementFlags.scope_, null, &endloc);
6097 s = new AST.WhileStatement(loc, condition, _body, endloc, param);
6098 break;
6100 case TOK.semicolon:
6101 if (!(flags & ParseStatementFlags.semiOk))
6103 error("use `{ }` for an empty statement, not `;`");
6105 nextToken();
6106 s = new AST.ExpStatement(loc, cast(AST.Expression)null);
6107 break;
6109 case TOK.do_:
6111 AST.Statement _body;
6113 nextToken();
6114 const lookingForElseSave = lookingForElse;
6115 lookingForElse = Loc.initial;
6116 _body = parseStatement(ParseStatementFlags.scope_);
6117 lookingForElse = lookingForElseSave;
6118 check(TOK.while_);
6119 check(TOK.leftParenthesis);
6120 auto condition = parseExpression();
6121 closeCondition("do .. while", null, condition);
6122 if (token.value == TOK.semicolon)
6123 nextToken();
6124 else
6125 error("terminating `;` required after do-while statement");
6126 s = new AST.DoStatement(loc, _body, condition, token.loc);
6127 break;
6129 case TOK.for_:
6131 AST.Statement _init;
6132 AST.Expression condition;
6133 AST.Expression increment;
6135 nextToken();
6136 check(TOK.leftParenthesis);
6137 if (token.value == TOK.semicolon)
6139 _init = null;
6140 nextToken();
6142 else
6144 const lookingForElseSave = lookingForElse;
6145 lookingForElse = Loc.initial;
6146 _init = parseStatement(0);
6147 lookingForElse = lookingForElseSave;
6149 if (token.value == TOK.semicolon)
6151 condition = null;
6152 nextToken();
6154 else
6156 condition = parseExpression();
6157 check(TOK.semicolon, "`for` condition");
6159 if (token.value == TOK.rightParenthesis)
6161 increment = null;
6162 nextToken();
6164 else
6166 increment = parseExpression();
6167 check(TOK.rightParenthesis);
6169 Loc endloc;
6170 AST.Statement _body = parseStatement(ParseStatementFlags.scope_, null, &endloc);
6171 s = new AST.ForStatement(loc, _init, condition, increment, _body, endloc);
6172 break;
6174 case TOK.foreach_:
6175 case TOK.foreach_reverse_:
6177 s = parseForeach!(AST.Statement)(loc, null);
6178 break;
6180 case TOK.if_:
6182 nextToken();
6183 check(TOK.leftParenthesis);
6184 auto param = parseAssignCondition();
6185 auto condition = parseExpression();
6186 closeCondition("if", param, condition);
6189 const lookingForElseSave = lookingForElse;
6190 lookingForElse = loc;
6191 ifbody = parseStatement(ParseStatementFlags.scope_);
6192 lookingForElse = lookingForElseSave;
6194 if (token.value == TOK.else_)
6196 const elseloc = token.loc;
6197 nextToken();
6198 elsebody = parseStatement(ParseStatementFlags.scope_);
6199 checkDanglingElse(elseloc);
6201 else
6202 elsebody = null;
6203 if (condition && ifbody)
6204 s = new AST.IfStatement(loc, param, condition, ifbody, elsebody, token.loc);
6205 else
6206 s = null; // don't propagate parsing errors
6207 break;
6210 case TOK.else_:
6211 error("found `else` without a corresponding `if`, `version` or `debug` statement");
6212 goto Lerror;
6214 case TOK.scope_:
6215 if (peekNext() != TOK.leftParenthesis)
6216 goto Ldeclaration; // scope used as storage class
6217 nextToken();
6218 check(TOK.leftParenthesis);
6219 if (token.value != TOK.identifier)
6221 error("scope identifier expected");
6222 goto Lerror;
6224 else
6226 TOK t = TOK.onScopeExit;
6227 Identifier id = token.ident;
6228 if (id == Id.exit)
6229 t = TOK.onScopeExit;
6230 else if (id == Id.failure)
6231 t = TOK.onScopeFailure;
6232 else if (id == Id.success)
6233 t = TOK.onScopeSuccess;
6234 else
6235 error("valid scope identifiers are `exit`, `failure`, or `success`, not `%s`", id.toChars());
6236 nextToken();
6237 check(TOK.rightParenthesis);
6238 AST.Statement st = parseStatement(ParseStatementFlags.scope_);
6239 s = new AST.ScopeGuardStatement(loc, t, st);
6240 break;
6243 case TOK.debug_:
6244 nextToken();
6245 if (token.value == TOK.assign)
6247 if (auto ds = parseDebugSpecification())
6249 if (ds.ident)
6250 eSink.error(ds.loc, "%s `%s` declaration must be at module level", ds.kind, ds.toPrettyChars);
6251 else
6252 eSink.error(ds.loc, "%s `%s` level declaration must be at module level", ds.kind, ds.toPrettyChars);
6254 break;
6256 cond = parseDebugCondition();
6257 goto Lcondition;
6259 case TOK.version_:
6260 nextToken();
6261 if (token.value == TOK.assign)
6263 if (auto vs = parseVersionSpecification())
6265 if (vs.ident)
6266 eSink.error(vs.loc, "%s `%s` declaration must be at module level", vs.kind, vs.toPrettyChars);
6267 else
6268 eSink.error(vs.loc, "%s `%s` level declaration must be at module level", vs.kind, vs.toPrettyChars);
6270 break;
6272 cond = parseVersionCondition();
6273 goto Lcondition;
6275 Lcondition:
6277 const lookingForElseSave = lookingForElse;
6278 lookingForElse = loc;
6279 ifbody = parseStatement(0);
6280 lookingForElse = lookingForElseSave;
6282 elsebody = null;
6283 if (token.value == TOK.else_)
6285 const elseloc = token.loc;
6286 nextToken();
6287 elsebody = parseStatement(0);
6288 checkDanglingElse(elseloc);
6290 s = new AST.ConditionalStatement(loc, cond, ifbody, elsebody);
6291 if (flags & ParseStatementFlags.scope_)
6292 s = new AST.ScopeStatement(loc, s, token.loc);
6293 break;
6295 case TOK.pragma_:
6297 Identifier ident;
6298 AST.Expressions* args = null;
6299 AST.Statement _body;
6301 nextToken();
6302 check(TOK.leftParenthesis);
6303 if (token.value != TOK.identifier)
6305 error("`pragma(identifier)` expected");
6306 goto Lerror;
6308 ident = token.ident;
6309 nextToken();
6310 if (token.value == TOK.comma && peekNext() != TOK.rightParenthesis)
6311 args = parseArguments(); // pragma(identifier, args...);
6312 else
6313 check(TOK.rightParenthesis); // pragma(identifier);
6314 if (token.value == TOK.semicolon)
6316 nextToken();
6317 _body = null;
6319 else
6320 _body = parseStatement(0);
6321 s = new AST.PragmaStatement(loc, ident, args, _body);
6322 break;
6324 case TOK.switch_:
6325 isfinal = false;
6326 goto Lswitch;
6328 Lswitch:
6330 nextToken();
6331 check(TOK.leftParenthesis);
6332 auto param = parseAssignCondition();
6333 AST.Expression condition = parseExpression();
6334 closeCondition("switch", null, condition);
6335 AST.Statement _body = parseStatement(ParseStatementFlags.scope_);
6336 s = new AST.SwitchStatement(loc, param, condition, _body, isfinal, token.loc);
6337 break;
6339 case TOK.case_:
6341 AST.Expression exp;
6342 AST.Expressions cases; // array of Expression's
6343 AST.Expression last = null;
6345 nextToken();
6348 exp = parseAssignExp();
6349 cases.push(exp);
6350 if (token.value != TOK.comma)
6351 break;
6352 nextToken(); //comma
6354 while (token.value != TOK.colon && token.value != TOK.endOfFile);
6355 check(TOK.colon);
6357 /* case exp: .. case last:
6359 if (token.value == TOK.slice)
6361 if (cases.length > 1)
6362 error("only one `case` allowed for start of case range");
6363 nextToken();
6364 check(TOK.case_);
6365 last = parseAssignExp();
6366 check(TOK.colon);
6369 if (flags & ParseStatementFlags.curlyScope)
6371 auto statements = new AST.Statements();
6372 while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
6374 auto cur = parseStatement(ParseStatementFlags.curlyScope);
6375 statements.push(cur);
6377 // https://issues.dlang.org/show_bug.cgi?id=21739
6378 // Stop at the last break s.t. the following non-case statements are
6379 // not merged into the current case. This can happen for
6380 // case 1: ... break;
6381 // debug { case 2: ... }
6382 if (cur && cur.isBreakStatement())
6383 break;
6385 s = new AST.CompoundStatement(loc, statements);
6387 else
6389 s = parseStatement(0);
6391 s = new AST.ScopeStatement(loc, s, token.loc);
6393 if (last)
6395 s = new AST.CaseRangeStatement(loc, exp, last, s);
6397 else
6399 // Keep cases in order by building the case statements backwards
6400 for (size_t i = cases.length; i; i--)
6402 exp = cases[i - 1];
6403 s = new AST.CaseStatement(loc, exp, s);
6406 break;
6408 case TOK.default_:
6410 nextToken();
6411 check(TOK.colon);
6413 if (flags & ParseStatementFlags.curlyScope)
6415 auto statements = new AST.Statements();
6416 while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
6418 statements.push(parseStatement(ParseStatementFlags.curlyScope));
6420 s = new AST.CompoundStatement(loc, statements);
6422 else
6423 s = parseStatement(0);
6424 s = new AST.ScopeStatement(loc, s, token.loc);
6425 s = new AST.DefaultStatement(loc, s);
6426 break;
6428 case TOK.return_:
6430 AST.Expression exp;
6431 nextToken();
6432 exp = token.value == TOK.semicolon ? null : parseExpression();
6433 check(TOK.semicolon, "`return` statement");
6434 s = new AST.ReturnStatement(loc, exp);
6435 break;
6437 case TOK.break_:
6439 Identifier ident;
6440 nextToken();
6441 ident = null;
6442 if (token.value == TOK.identifier)
6444 ident = token.ident;
6445 nextToken();
6447 check(TOK.semicolon, "`break` statement");
6448 s = new AST.BreakStatement(loc, ident);
6449 break;
6451 case TOK.continue_:
6453 Identifier ident;
6454 nextToken();
6455 ident = null;
6456 if (token.value == TOK.identifier)
6458 ident = token.ident;
6459 nextToken();
6461 check(TOK.semicolon, "`continue` statement");
6462 s = new AST.ContinueStatement(loc, ident);
6463 break;
6465 case TOK.goto_:
6467 Identifier ident;
6468 nextToken();
6469 if (token.value == TOK.default_)
6471 nextToken();
6472 s = new AST.GotoDefaultStatement(loc);
6474 else if (token.value == TOK.case_)
6476 AST.Expression exp = null;
6477 nextToken();
6478 if (token.value != TOK.semicolon)
6479 exp = parseExpression();
6480 s = new AST.GotoCaseStatement(loc, exp);
6482 else
6484 if (token.value != TOK.identifier)
6486 error("identifier expected following `goto`");
6487 ident = null;
6489 else
6491 ident = token.ident;
6492 nextToken();
6494 s = new AST.GotoStatement(loc, ident);
6496 check(TOK.semicolon, "`goto` statement");
6497 break;
6499 case TOK.synchronized_:
6501 AST.Expression exp;
6502 AST.Statement _body;
6504 Token* t = peek(&token);
6505 if (skipAttributes(t, &t) && t.value == TOK.class_)
6506 goto Ldeclaration;
6508 nextToken();
6509 if (token.value == TOK.leftParenthesis)
6511 nextToken();
6512 exp = parseExpression();
6513 closeCondition("synchronized", null, exp);
6515 else
6516 exp = null;
6517 _body = parseStatement(ParseStatementFlags.scope_);
6518 s = new AST.SynchronizedStatement(loc, exp, _body);
6519 break;
6521 case TOK.with_:
6523 AST.Expression exp;
6524 AST.Statement _body;
6525 Loc endloc = loc;
6527 nextToken();
6528 check(TOK.leftParenthesis);
6529 exp = parseExpression();
6530 closeCondition("with", null, exp);
6531 _body = parseStatement(ParseStatementFlags.scope_, null, &endloc);
6532 s = new AST.WithStatement(loc, exp, _body, endloc);
6533 break;
6535 case TOK.try_:
6537 AST.Statement _body;
6538 AST.Catches* catches = null;
6539 AST.Statement finalbody = null;
6541 nextToken();
6542 const lookingForElseSave = lookingForElse;
6543 lookingForElse = Loc.initial;
6544 _body = parseStatement(ParseStatementFlags.scope_);
6545 lookingForElse = lookingForElseSave;
6546 while (token.value == TOK.catch_)
6548 AST.Statement handler;
6549 AST.Catch c;
6550 AST.Type t;
6551 Identifier id;
6552 const catchloc = token.loc;
6554 nextToken();
6555 if (token.value != TOK.leftParenthesis)
6557 deprecation("`catch` statement without an exception specification is deprecated");
6558 deprecationSupplemental("use `catch(Throwable)` for old behavior");
6559 t = null;
6560 id = null;
6562 else
6564 check(TOK.leftParenthesis);
6565 id = null;
6566 t = parseType(&id);
6567 check(TOK.rightParenthesis);
6569 handler = parseStatement(0);
6570 c = new AST.Catch(catchloc, t, id, handler);
6571 if (!catches)
6572 catches = new AST.Catches();
6573 catches.push(c);
6576 if (token.value == TOK.finally_)
6578 nextToken();
6579 finalbody = parseStatement(ParseStatementFlags.scope_);
6582 s = _body;
6583 if (!catches && !finalbody)
6584 error("`catch` or `finally` expected following `try`");
6585 else
6587 if (catches)
6588 s = new AST.TryCatchStatement(loc, _body, catches);
6589 if (finalbody)
6590 s = new AST.TryFinallyStatement(loc, s, finalbody);
6592 break;
6594 case TOK.throw_:
6596 AST.Expression exp;
6597 nextToken();
6598 exp = parseExpression();
6599 check(TOK.semicolon, "`throw` statement");
6600 s = new AST.ThrowStatement(loc, exp);
6601 break;
6604 case TOK.asm_:
6605 s = parseAsm(false);
6606 break;
6608 case TOK.import_:
6610 /* https://issues.dlang.org/show_bug.cgi?id=16088
6612 * At this point it can either be an
6613 * https://dlang.org/spec/grammar.html#ImportExpression
6614 * or an
6615 * https://dlang.org/spec/grammar.html#ImportDeclaration.
6616 * See if the next token after `import` is a `(`; if so,
6617 * then it is an import expression.
6619 if (peekNext() == TOK.leftParenthesis)
6621 AST.Expression e = parseExpression();
6622 check(TOK.semicolon, "`import` Expression");
6623 s = new AST.ExpStatement(loc, e);
6625 else
6627 AST.Dsymbols* imports = parseImport();
6628 s = new AST.ImportStatement(loc, imports);
6629 if (flags & ParseStatementFlags.scope_)
6630 s = new AST.ScopeStatement(loc, s, token.loc);
6632 break;
6634 case TOK.template_:
6636 AST.Dsymbol d = parseTemplateDeclaration();
6637 s = new AST.ExpStatement(loc, d);
6638 break;
6640 default:
6641 error("found `%s` instead of statement", token.toChars());
6642 goto Lerror;
6644 Lerror:
6645 while (token.value != TOK.rightCurly && token.value != TOK.semicolon && token.value != TOK.endOfFile)
6646 nextToken();
6647 if (token.value == TOK.semicolon)
6648 nextToken();
6649 s = new AST.ErrorStatement;
6650 break;
6652 if (pEndloc)
6653 *pEndloc = prevloc;
6654 return s;
6658 private AST.ExpInitializer parseExpInitializer(Loc loc)
6660 auto ae = parseAssignExp();
6661 return new AST.ExpInitializer(loc, ae);
6664 private AST.Initializer parseStructInitializer(Loc loc)
6666 /* Scan ahead to discern between a struct initializer and
6667 * parameterless function literal.
6669 * We'll scan the topmost curly bracket level for statement-related
6670 * tokens, thereby ruling out a struct initializer. (A struct
6671 * initializer which itself contains function literals may have
6672 * statements at nested curly bracket levels.)
6674 * It's important that this function literal check not be
6675 * pendantic, otherwise a function having the slightest syntax
6676 * error would emit confusing errors when we proceed to parse it
6677 * as a struct initializer.
6679 * The following two ambiguous cases will be treated as a struct
6680 * initializer (best we can do without type info):
6681 * {}
6682 * {{statements...}} - i.e. it could be struct initializer
6683 * with one function literal, or function literal having an
6684 * extra level of curly brackets
6685 * If a function literal is intended in these cases (unlikely),
6686 * source can use a more explicit function literal syntax
6687 * (e.g. prefix with "()" for empty parameter list).
6689 int braces = 1;
6690 int parens = 0;
6691 for (auto t = peek(&token); 1; t = peek(t))
6693 switch (t.value)
6695 case TOK.leftParenthesis:
6696 parens++;
6697 continue;
6698 case TOK.rightParenthesis:
6699 parens--;
6700 continue;
6701 // https://issues.dlang.org/show_bug.cgi?id=21163
6702 // lambda params can have the `scope` storage class, e.g
6703 // `S s = { (scope Type Id){} }`
6704 case TOK.scope_:
6705 if (!parens) goto case;
6706 continue;
6707 /* Look for a semicolon or keyword of statements which don't
6708 * require a semicolon (typically containing BlockStatement).
6709 * Tokens like "else", "catch", etc. are omitted where the
6710 * leading token of the statement is sufficient.
6712 case TOK.asm_:
6713 case TOK.class_:
6714 case TOK.debug_:
6715 case TOK.enum_:
6716 case TOK.if_:
6717 case TOK.interface_:
6718 case TOK.pragma_:
6719 case TOK.semicolon:
6720 case TOK.struct_:
6721 case TOK.switch_:
6722 case TOK.synchronized_:
6723 case TOK.try_:
6724 case TOK.union_:
6725 case TOK.version_:
6726 case TOK.while_:
6727 case TOK.with_:
6728 if (braces == 1)
6729 return parseExpInitializer(loc);
6730 continue;
6732 case TOK.leftCurly:
6733 braces++;
6734 continue;
6736 case TOK.rightCurly:
6737 if (--braces == 0)
6738 break;
6739 continue;
6741 case TOK.endOfFile:
6742 break;
6744 default:
6745 continue;
6747 break;
6750 auto _is = new AST.StructInitializer(loc);
6751 bool commaExpected = false;
6752 nextToken();
6753 while (1)
6755 switch (token.value)
6757 case TOK.identifier:
6760 if (commaExpected)
6761 error("comma expected separating field initializers");
6762 const t = peek(&token);
6763 Identifier id;
6764 if (t.value == TOK.colon)
6766 id = token.ident;
6767 nextToken();
6768 nextToken(); // skip over ':'
6770 auto value = parseInitializer();
6771 _is.addInit(id, value);
6772 commaExpected = true;
6773 continue;
6775 case TOK.comma:
6776 if (!commaExpected)
6777 error("expression expected, not `,`");
6778 nextToken();
6779 commaExpected = false;
6780 continue;
6782 case TOK.rightCurly: // allow trailing comma's
6783 nextToken();
6784 break;
6786 case TOK.endOfFile:
6787 error("found end of file instead of initializer");
6788 break;
6790 default:
6791 if (commaExpected)
6792 error("comma expected separating field initializers");
6793 auto value = parseInitializer();
6794 _is.addInit(null, value);
6795 commaExpected = true;
6796 continue;
6798 break;
6800 return _is;
6804 /*****************************************
6805 * Parse initializer for variable declaration.
6807 private AST.Initializer parseInitializer()
6809 const loc = token.loc;
6811 switch (token.value)
6813 case TOK.leftCurly:
6814 return parseStructInitializer(loc);
6816 case TOK.leftBracket:
6817 /* Scan ahead to see if it is an array initializer or
6818 * an expression.
6819 * If it ends with a ';' ',' or ']', it is an array initializer.
6821 int brackets = 1;
6822 for (auto t = peek(&token); 1; t = peek(t))
6824 switch (t.value)
6826 case TOK.leftBracket:
6827 brackets++;
6828 continue;
6830 case TOK.rightBracket:
6831 if (--brackets == 0)
6833 t = peek(t);
6834 if (t.value != TOK.semicolon && t.value != TOK.comma && t.value != TOK.rightBracket && t.value != TOK.rightCurly)
6835 return parseExpInitializer(loc);
6836 break;
6838 continue;
6840 case TOK.endOfFile:
6841 break;
6843 default:
6844 continue;
6846 break;
6849 auto ia = new AST.ArrayInitializer(loc);
6850 bool commaExpected = false;
6852 nextToken();
6853 while (1)
6855 switch (token.value)
6857 default:
6858 if (commaExpected)
6860 error("comma expected separating array initializers, not `%s`", token.toChars());
6861 nextToken();
6862 break;
6864 auto e = parseAssignExp();
6865 if (!e)
6866 break;
6868 AST.Initializer value;
6869 if (token.value == TOK.colon)
6871 nextToken();
6872 value = parseInitializer();
6874 else
6876 value = new AST.ExpInitializer(e.loc, e);
6877 e = null;
6879 ia.addInit(e, value);
6880 commaExpected = true;
6881 continue;
6883 case TOK.leftCurly:
6884 case TOK.leftBracket:
6885 if (commaExpected)
6886 error("comma expected separating array initializers, not `%s`", token.toChars());
6887 auto value = parseInitializer();
6888 AST.Expression e;
6890 if (token.value == TOK.colon)
6892 nextToken();
6893 if (auto ei = value.isExpInitializer())
6895 e = ei.exp;
6896 value = parseInitializer();
6898 else
6899 error("initializer expression expected following colon, not `%s`", token.toChars());
6901 ia.addInit(e, value);
6902 commaExpected = true;
6903 continue;
6905 case TOK.comma:
6906 if (!commaExpected)
6907 error("expression expected, not `,`");
6908 nextToken();
6909 commaExpected = false;
6910 continue;
6912 case TOK.rightBracket: // allow trailing comma's
6913 nextToken();
6914 break;
6916 case TOK.endOfFile:
6917 error("found `%s` instead of array initializer", token.toChars());
6918 break;
6920 break;
6922 return ia;
6924 case TOK.void_:
6925 const tv = peekNext();
6926 if (tv == TOK.semicolon || tv == TOK.comma)
6928 nextToken();
6929 return new AST.VoidInitializer(loc);
6931 return parseExpInitializer(loc);
6933 default:
6934 return parseExpInitializer(loc);
6938 /*****************************************
6939 * Parses default argument initializer expression that is an assign expression,
6940 * with special handling for __FILE__, __FILE_DIR__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__.
6942 private AST.Expression parseDefaultInitExp()
6944 AST.Expression e = null;
6945 const tv = peekNext();
6946 if (tv == TOK.comma || tv == TOK.rightParenthesis)
6948 switch (token.value)
6950 case TOK.file: e = new AST.FileInitExp(token.loc, EXP.file); break;
6951 case TOK.fileFullPath: e = new AST.FileInitExp(token.loc, EXP.fileFullPath); break;
6952 case TOK.line: e = new AST.LineInitExp(token.loc); break;
6953 case TOK.moduleString: e = new AST.ModuleInitExp(token.loc); break;
6954 case TOK.functionString: e = new AST.FuncInitExp(token.loc); break;
6955 case TOK.prettyFunction: e = new AST.PrettyFuncInitExp(token.loc); break;
6956 default: goto LExp;
6958 nextToken();
6959 return e;
6961 LExp:
6962 return parseAssignExp();
6965 /********************
6966 * Parse inline assembler block.
6967 * Enters with token on the `asm`.
6968 * https://dlang.org/spec/iasm.html
6970 * AsmStatement:
6971 * asm FunctionAttributes(opt) { AsmInstructionListopt }
6972 * AsmInstructionList:
6973 * AsmInstruction ;
6974 * AsmInstruction ; AsmInstruction
6976 * Params:
6977 * endOfLine = true if EOL means end of asm statement
6978 * Returns:
6979 * inline assembler block as a Statement
6981 AST.Statement parseAsm(bool endOfLine)
6983 // Parse the asm block into a sequence of AsmStatements,
6984 // each AsmStatement is one instruction.
6985 // Separate out labels.
6986 // Defer parsing of AsmStatements until semantic processing.
6988 const loc = token.loc;
6989 Loc labelloc;
6991 nextToken();
6992 StorageClass stc = parsePostfix(STC.undefined_, null); // optional FunctionAttributes
6993 if (stc & (STC.const_ | STC.immutable_ | STC.shared_ | STC.wild))
6994 error("`const`/`immutable`/`shared`/`inout` attributes are not allowed on `asm` blocks");
6996 check(TOK.leftCurly);
6997 Token* toklist = null;
6998 Token** ptoklist = &toklist;
6999 Identifier label = null;
7000 auto statements = new AST.Statements();
7001 size_t nestlevel = 0;
7002 while (1)
7004 if (endOfLine)
7005 nextDefineLine();
7006 switch (token.value)
7008 case TOK.identifier:
7009 if (!toklist)
7011 // Look ahead to see if it is a label
7012 if (peekNext() == TOK.colon)
7014 // It's a label
7015 label = token.ident;
7016 labelloc = token.loc;
7017 nextToken();
7018 nextToken();
7019 continue;
7022 goto default;
7024 case TOK.leftCurly:
7025 ++nestlevel;
7026 goto default;
7028 case TOK.rightCurly:
7029 if (nestlevel > 0)
7031 --nestlevel;
7032 goto default;
7034 if (toklist || label)
7036 error("`asm` statements must end in `;`");
7038 break;
7040 case TOK.endOfLine:
7041 nextDefineLine();
7042 goto case;
7044 case TOK.semicolon:
7045 if (nestlevel != 0)
7046 error("mismatched number of curly brackets");
7048 if (toklist || label)
7050 // Create AsmStatement from list of tokens we've saved
7051 AST.AsmStatement as = new AST.AsmStatement(token.loc, toklist);
7052 as.caseSensitive = !endOfLine;
7053 AST.Statement s = as;
7054 toklist = null;
7055 ptoklist = &toklist;
7056 if (label)
7058 s = new AST.LabelStatement(labelloc, label, s);
7059 label = null;
7061 statements.push(s);
7063 nextToken();
7064 continue;
7066 case TOK.endOfFile:
7067 /* { */
7068 error("matching `}` expected, not end of file");
7069 break;
7071 case TOK.colonColon: // treat as two separate : tokens for iasmgcc
7072 *ptoklist = allocateToken();
7073 memcpy(*ptoklist, &token, Token.sizeof);
7074 (*ptoklist).value = TOK.colon;
7075 ptoklist = &(*ptoklist).next;
7077 *ptoklist = allocateToken();
7078 memcpy(*ptoklist, &token, Token.sizeof);
7079 (*ptoklist).value = TOK.colon;
7080 ptoklist = &(*ptoklist).next;
7082 *ptoklist = null;
7083 nextToken();
7084 continue;
7086 default:
7087 *ptoklist = allocateToken();
7088 memcpy(*ptoklist, &token, Token.sizeof);
7089 ptoklist = &(*ptoklist).next;
7090 *ptoklist = null;
7091 nextToken();
7092 continue;
7094 break;
7096 nextToken();
7097 if (token.value == TOK.endOfLine)
7098 nextToken();
7099 auto s = new AST.CompoundAsmStatement(loc, statements, stc);
7100 return s;
7103 /**********************************
7104 * Issue error if the current token is not `value`,
7105 * advance to next token.
7106 * Params:
7107 * loc = location for error message
7108 * value = token value to compare with
7110 void check(Loc loc, TOK value)
7112 if (token.value != value)
7113 error(loc, "found `%s` when expecting `%s`", token.toChars(), Token.toChars(value));
7114 nextToken();
7117 /**********************************
7118 * Issue error if the current token is not `value`,
7119 * advance to next token.
7120 * Params:
7121 * value = token value to compare with
7123 void check(TOK value)
7125 check(token.loc, value);
7128 /**********************************
7129 * Issue error if the current token is not `value`,
7130 * advance to next token.
7131 * Params:
7132 * value = token value to compare with
7133 * string = for error message
7135 void check(TOK value, const(char)* string)
7137 if (token.value != value)
7138 error(token.loc, "found `%s` when expecting `%s` following %s", token.toChars(), Token.toChars(value), string);
7139 nextToken();
7142 private void checkParens(TOK value, AST.Expression e)
7144 if (precedence[e.op] == PREC.rel)
7145 error(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`", e.toChars(), Token.toChars(value));
7149 enum NeedDeclaratorId
7151 no, // Declarator part must have no identifier
7152 opt, // Declarator part identifier is optional
7153 must, // Declarator part must have identifier
7154 mustIfDstyle, // Declarator part must have identifier, but don't recognize old C-style syntax
7157 /************************************
7158 * Determine if the scanner is sitting on the start of a declaration.
7159 * Params:
7160 * t = current token of the scanner
7161 * needId = flag with additional requirements for a declaration
7162 * endtok = ending token
7163 * pt = will be set ending token (if not null)
7164 * Output:
7165 * true if the token `t` is a declaration, false otherwise
7167 private bool isDeclaration(Token* t, NeedDeclaratorId needId, TOK endtok, Token** pt)
7169 //printf("isDeclaration(needId = %d)\n", needId);
7170 int haveId = 0;
7171 int haveTpl = 0;
7173 while (1)
7175 if ((t.value == TOK.const_ || t.value == TOK.immutable_ || t.value == TOK.inout_ || t.value == TOK.shared_) && peek(t).value != TOK.leftParenthesis)
7177 /* const type
7178 * immutable type
7179 * shared type
7180 * wild type
7182 t = peek(t);
7183 continue;
7185 break;
7188 if (!isBasicType(&t))
7190 goto Lisnot;
7192 if (!isDeclarator(&t, &haveId, &haveTpl, endtok, needId != NeedDeclaratorId.mustIfDstyle))
7193 goto Lisnot;
7194 if ((needId == NeedDeclaratorId.no && !haveId) ||
7195 (needId == NeedDeclaratorId.opt) ||
7196 (needId == NeedDeclaratorId.must && haveId) ||
7197 (needId == NeedDeclaratorId.mustIfDstyle && haveId))
7199 if (pt)
7200 *pt = t;
7201 goto Lis;
7203 goto Lisnot;
7205 Lis:
7206 //printf("\tis declaration, t = %s\n", t.toChars());
7207 return true;
7209 Lisnot:
7210 //printf("\tis not declaration\n");
7211 return false;
7214 private bool isBasicType(Token** pt)
7216 // This code parallels parseBasicType()
7217 Token* t = *pt;
7218 switch (t.value)
7220 case TOK.wchar_:
7221 case TOK.dchar_:
7222 case TOK.bool_:
7223 case TOK.char_:
7224 case TOK.int8:
7225 case TOK.uns8:
7226 case TOK.int16:
7227 case TOK.uns16:
7228 case TOK.int32:
7229 case TOK.uns32:
7230 case TOK.int64:
7231 case TOK.uns64:
7232 case TOK.int128:
7233 case TOK.uns128:
7234 case TOK.float32:
7235 case TOK.float64:
7236 case TOK.float80:
7237 case TOK.imaginary32:
7238 case TOK.imaginary64:
7239 case TOK.imaginary80:
7240 case TOK.complex32:
7241 case TOK.complex64:
7242 case TOK.complex80:
7243 case TOK.void_:
7244 t = peek(t);
7245 break;
7247 case TOK.identifier:
7249 t = peek(t);
7250 if (t.value == TOK.not)
7252 goto L4;
7254 goto L3;
7255 while (1)
7258 t = peek(t);
7260 if (t.value == TOK.dot)
7262 Ldot:
7263 t = peek(t);
7264 if (t.value != TOK.identifier)
7265 goto Lfalse;
7266 t = peek(t);
7267 if (t.value != TOK.not)
7268 goto L3;
7270 /* Seen a !
7271 * Look for:
7272 * !( args ), !identifier, etc.
7274 t = peek(t);
7275 switch (t.value)
7277 case TOK.identifier:
7278 goto L5;
7280 case TOK.leftParenthesis:
7281 if (!skipParens(t, &t))
7282 goto Lfalse;
7283 goto L3;
7285 case TOK.wchar_:
7286 case TOK.dchar_:
7287 case TOK.bool_:
7288 case TOK.char_:
7289 case TOK.int8:
7290 case TOK.uns8:
7291 case TOK.int16:
7292 case TOK.uns16:
7293 case TOK.int32:
7294 case TOK.uns32:
7295 case TOK.int64:
7296 case TOK.uns64:
7297 case TOK.int128:
7298 case TOK.uns128:
7299 case TOK.float32:
7300 case TOK.float64:
7301 case TOK.float80:
7302 case TOK.imaginary32:
7303 case TOK.imaginary64:
7304 case TOK.imaginary80:
7305 case TOK.complex32:
7306 case TOK.complex64:
7307 case TOK.complex80:
7308 case TOK.void_:
7309 case TOK.int32Literal:
7310 case TOK.uns32Literal:
7311 case TOK.int64Literal:
7312 case TOK.uns64Literal:
7313 case TOK.int128Literal:
7314 case TOK.uns128Literal:
7315 case TOK.float32Literal:
7316 case TOK.float64Literal:
7317 case TOK.float80Literal:
7318 case TOK.imaginary32Literal:
7319 case TOK.imaginary64Literal:
7320 case TOK.imaginary80Literal:
7321 case TOK.null_:
7322 case TOK.true_:
7323 case TOK.false_:
7324 case TOK.charLiteral:
7325 case TOK.wcharLiteral:
7326 case TOK.dcharLiteral:
7327 case TOK.string_:
7328 case TOK.hexadecimalString:
7329 case TOK.file:
7330 case TOK.fileFullPath:
7331 case TOK.line:
7332 case TOK.moduleString:
7333 case TOK.functionString:
7334 case TOK.prettyFunction:
7335 goto L2;
7337 default:
7338 goto Lfalse;
7341 break;
7343 break;
7345 case TOK.dot:
7346 goto Ldot;
7348 case TOK.typeof_:
7349 case TOK.vector:
7350 case TOK.mixin_:
7351 /* typeof(exp).identifier...
7353 t = peek(t);
7354 if (!skipParens(t, &t))
7355 goto Lfalse;
7356 goto L3;
7358 case TOK.traits:
7359 // __traits(getMember
7360 t = peek(t);
7361 if (t.value != TOK.leftParenthesis)
7362 goto Lfalse;
7363 auto lp = t;
7364 t = peek(t);
7365 if (t.value != TOK.identifier || t.ident != Id.getMember)
7366 goto Lfalse;
7367 if (!skipParens(lp, &lp))
7368 goto Lfalse;
7369 // we are in a lookup for decl VS statement
7370 // so we expect a declarator following __trait if it's a type.
7371 // other usages wont be ambiguous (alias, template instance, type qual, etc.)
7372 if (lp.value != TOK.identifier)
7373 goto Lfalse;
7375 break;
7377 case TOK.const_:
7378 case TOK.immutable_:
7379 case TOK.shared_:
7380 case TOK.inout_:
7381 // const(type) or immutable(type) or shared(type) or wild(type)
7382 t = peek(t);
7383 if (t.value != TOK.leftParenthesis)
7384 goto Lfalse;
7385 t = peek(t);
7386 if (!isDeclaration(t, NeedDeclaratorId.no, TOK.rightParenthesis, &t))
7388 goto Lfalse;
7390 t = peek(t);
7391 break;
7393 default:
7394 goto Lfalse;
7396 *pt = t;
7397 //printf("is\n");
7398 return true;
7400 Lfalse:
7401 //printf("is not\n");
7402 return false;
7405 private bool isDeclarator(Token** pt, int* haveId, int* haveTpl, TOK endtok, bool allowAltSyntax = true)
7407 // This code parallels parseDeclarator()
7408 Token* t = *pt;
7409 bool parens;
7411 //printf("Parser::isDeclarator() %s\n", t.toChars());
7412 if (t.value == TOK.assign)
7413 return false;
7415 while (1)
7417 parens = false;
7418 switch (t.value)
7420 case TOK.mul:
7421 //case TOK.and:
7422 t = peek(t);
7423 continue;
7425 case TOK.leftBracket:
7426 t = peek(t);
7427 if (t.value == TOK.rightBracket)
7429 t = peek(t);
7431 else if (isDeclaration(t, NeedDeclaratorId.no, TOK.rightBracket, &t))
7433 // It's an associative array declaration
7434 t = peek(t);
7436 // ...[type].ident
7437 if (t.value == TOK.dot && peek(t).value == TOK.identifier)
7439 t = peek(t);
7440 t = peek(t);
7443 else
7445 // [ expression ]
7446 // [ expression .. expression ]
7447 if (!isExpression(&t))
7448 return false;
7449 if (t.value == TOK.slice)
7451 t = peek(t);
7452 if (!isExpression(&t))
7453 return false;
7454 if (t.value != TOK.rightBracket)
7455 return false;
7456 t = peek(t);
7458 else
7460 if (t.value != TOK.rightBracket)
7461 return false;
7462 t = peek(t);
7463 // ...[index].ident
7464 if (t.value == TOK.dot && peek(t).value == TOK.identifier)
7466 t = peek(t);
7467 t = peek(t);
7471 continue;
7473 case TOK.identifier:
7474 if (*haveId)
7475 return false;
7476 *haveId = true;
7477 t = peek(t);
7478 break;
7480 case TOK.leftParenthesis:
7481 if (!allowAltSyntax)
7482 return false; // Do not recognize C-style declarations.
7484 t = peek(t);
7485 if (t.value == TOK.rightParenthesis)
7486 return false; // () is not a declarator
7488 /* Regard ( identifier ) as not a declarator
7489 * BUG: what about ( *identifier ) in
7490 * f(*p)(x);
7491 * where f is a class instance with overloaded () ?
7492 * Should we just disallow C-style function pointer declarations?
7494 if (t.value == TOK.identifier)
7496 Token* t2 = peek(t);
7497 if (t2.value == TOK.rightParenthesis)
7498 return false;
7501 if (!isDeclarator(&t, haveId, null, TOK.rightParenthesis))
7502 return false;
7503 t = peek(t);
7504 parens = true;
7505 break;
7507 case TOK.delegate_:
7508 case TOK.function_:
7509 t = peek(t);
7510 if (!isParameters(&t))
7511 return false;
7512 skipAttributes(t, &t);
7513 continue;
7515 default:
7516 break;
7518 break;
7521 while (1)
7523 switch (t.value)
7525 static if (CARRAYDECL)
7527 case TOK.leftBracket:
7528 parens = false;
7529 t = peek(t);
7530 if (t.value == TOK.rightBracket)
7532 t = peek(t);
7534 else if (isDeclaration(t, NeedDeclaratorId.no, TOK.rightBracket, &t))
7536 // It's an associative array declaration
7537 t = peek(t);
7539 else
7541 // [ expression ]
7542 if (!isExpression(&t))
7543 return false;
7544 if (t.value != TOK.rightBracket)
7545 return false;
7546 t = peek(t);
7548 continue;
7551 case TOK.leftParenthesis:
7552 parens = false;
7553 if (Token* tk = peekPastParen(t))
7555 if (tk.value == TOK.leftParenthesis)
7557 if (!haveTpl)
7558 return false;
7559 *haveTpl = 1;
7560 t = tk;
7562 else if (tk.value == TOK.assign)
7564 if (!haveTpl)
7565 return false;
7566 *haveTpl = 1;
7567 *pt = tk;
7568 return true;
7571 if (!isParameters(&t))
7572 return false;
7573 while (1)
7575 switch (t.value)
7577 case TOK.const_:
7578 case TOK.immutable_:
7579 case TOK.shared_:
7580 case TOK.inout_:
7581 case TOK.pure_:
7582 case TOK.nothrow_:
7583 case TOK.return_:
7584 case TOK.scope_:
7585 t = peek(t);
7586 continue;
7588 case TOK.at:
7589 t = peek(t); // skip '@'
7590 t = peek(t); // skip identifier
7591 continue;
7593 default:
7594 break;
7596 break;
7598 continue;
7600 // Valid tokens that follow the start of a declaration
7601 case TOK.rightParenthesis:
7602 case TOK.rightBracket:
7603 case TOK.assign:
7604 case TOK.comma:
7605 case TOK.dotDotDot:
7606 case TOK.semicolon:
7607 case TOK.leftCurly:
7608 case TOK.in_:
7609 case TOK.out_:
7610 case TOK.do_:
7611 // The !parens is to disallow unnecessary parentheses
7612 if (!parens && (endtok == TOK.reserved || endtok == t.value))
7614 *pt = t;
7615 return true;
7617 return false;
7619 // To recognize the shortened function declaration syntax
7620 case TOK.goesTo:
7622 1. https://issues.dlang.org/show_bug.cgi?id=24088
7624 2. We need to make sure the would-be
7625 declarator has an identifier otherwise function literals
7626 are handled incorrectly. Some special treatment is required
7627 here, it turns out that a lot of code in the compiler relies
7628 on this mess (in the parser), i.e. having isDeclarator be more
7629 precise the parsing of other things go kaboom, so we do it in a
7630 separate case.
7632 if (*haveId)
7633 goto case TOK.do_;
7634 goto default;
7636 case TOK.identifier:
7637 if (t.ident == Id._body)
7639 usageOfBodyKeyword();
7640 goto case TOK.do_;
7642 goto default;
7644 case TOK.if_:
7645 return haveTpl ? true : false;
7647 // Used for mixin type parsing
7648 case TOK.endOfFile:
7649 if (endtok == TOK.endOfFile)
7650 goto case TOK.do_;
7651 return false;
7653 default:
7654 return false;
7657 assert(0);
7660 private bool isParameters(Token** pt)
7662 // This code parallels parseParameterList()
7663 Token* t = *pt;
7665 //printf("isParameters()\n");
7666 if (t.value != TOK.leftParenthesis)
7667 return false;
7669 t = peek(t);
7670 for (; 1; t = peek(t))
7673 switch (t.value)
7675 case TOK.rightParenthesis:
7676 break;
7678 case TOK.at:
7679 Token* pastAttr;
7680 if (skipAttributes(t, &pastAttr))
7682 t = pastAttr;
7683 goto default;
7685 break;
7687 case TOK.dotDotDot:
7688 t = peek(t);
7689 break;
7691 case TOK.in_:
7692 case TOK.out_:
7693 case TOK.ref_:
7694 case TOK.lazy_:
7695 case TOK.scope_:
7696 case TOK.final_:
7697 case TOK.auto_:
7698 case TOK.return_:
7699 continue;
7701 case TOK.const_:
7702 case TOK.immutable_:
7703 case TOK.shared_:
7704 case TOK.inout_:
7705 t = peek(t);
7706 if (t.value == TOK.leftParenthesis)
7708 t = peek(t);
7709 if (!isDeclaration(t, NeedDeclaratorId.no, TOK.rightParenthesis, &t))
7710 return false;
7711 t = peek(t); // skip past closing ')'
7712 goto L2;
7714 goto L1;
7716 default:
7718 if (!isBasicType(&t))
7719 return false;
7721 int tmp = false;
7722 if (t.value != TOK.dotDotDot && !isDeclarator(&t, &tmp, null, TOK.reserved))
7723 return false;
7724 if (t.value == TOK.assign)
7726 t = peek(t);
7727 if (!isExpression(&t))
7728 return false;
7730 if (t.value == TOK.dotDotDot)
7732 t = peek(t);
7733 break;
7736 if (t.value == TOK.comma)
7738 continue;
7740 break;
7742 break;
7744 if (t.value != TOK.rightParenthesis)
7745 return false;
7746 t = peek(t);
7747 *pt = t;
7748 return true;
7751 private bool isExpression(Token** pt)
7753 // This is supposed to determine if something is an expression.
7754 // What it actually does is scan until a closing right bracket
7755 // is found.
7757 Token* t = *pt;
7758 int brnest = 0;
7759 int panest = 0;
7760 int curlynest = 0;
7762 for (;; t = peek(t))
7764 switch (t.value)
7766 case TOK.leftBracket:
7767 brnest++;
7768 continue;
7770 case TOK.rightBracket:
7771 if (--brnest >= 0)
7772 continue;
7773 break;
7775 case TOK.leftParenthesis:
7776 panest++;
7777 continue;
7779 case TOK.comma:
7780 if (brnest || panest)
7781 continue;
7782 break;
7784 case TOK.rightParenthesis:
7785 if (--panest >= 0)
7786 continue;
7787 break;
7789 case TOK.leftCurly:
7790 curlynest++;
7791 continue;
7793 case TOK.rightCurly:
7794 if (--curlynest >= 0)
7795 continue;
7796 return false;
7798 case TOK.slice:
7799 if (brnest)
7800 continue;
7801 break;
7803 case TOK.semicolon:
7804 if (curlynest)
7805 continue;
7806 return false;
7808 case TOK.endOfFile:
7809 return false;
7811 default:
7812 continue;
7814 break;
7817 *pt = t;
7818 return true;
7821 /*******************************************
7822 * Skip parentheses.
7823 * Params:
7824 * t = on opening $(LPAREN)
7825 * pt = *pt is set to token past '$(RPAREN)' on true
7826 * Returns:
7827 * true successful
7828 * false some parsing error
7830 bool skipParens(Token* t, Token** pt)
7832 if (t.value != TOK.leftParenthesis)
7833 return false;
7835 int parens = 0;
7837 while (1)
7839 switch (t.value)
7841 case TOK.leftParenthesis:
7842 parens++;
7843 break;
7845 case TOK.rightParenthesis:
7846 parens--;
7847 if (parens < 0)
7848 goto Lfalse;
7849 if (parens == 0)
7850 goto Ldone;
7851 break;
7853 case TOK.endOfFile:
7854 goto Lfalse;
7856 default:
7857 break;
7859 t = peek(t);
7861 Ldone:
7862 if (pt)
7863 *pt = peek(t); // skip found rparen
7864 return true;
7866 Lfalse:
7867 return false;
7870 private bool skipParensIf(Token* t, Token** pt)
7872 if (t.value != TOK.leftParenthesis)
7874 if (pt)
7875 *pt = t;
7876 return true;
7878 return skipParens(t, pt);
7881 //returns true if the next value (after optional matching parens) is expected
7882 private bool hasOptionalParensThen(Token* t, TOK expected)
7884 Token* tk;
7885 if (!skipParensIf(t, &tk))
7886 return false;
7887 return tk.value == expected;
7890 /*******************************************
7891 * Skip attributes.
7892 * Input:
7893 * t is on a candidate attribute
7894 * Output:
7895 * *pt is set to first non-attribute token on success
7896 * Returns:
7897 * true successful
7898 * false some parsing error
7900 private bool skipAttributes(Token* t, Token** pt)
7902 while (1)
7904 switch (t.value)
7906 case TOK.const_:
7907 case TOK.immutable_:
7908 case TOK.shared_:
7909 case TOK.inout_:
7910 case TOK.final_:
7911 case TOK.auto_:
7912 case TOK.scope_:
7913 case TOK.override_:
7914 case TOK.abstract_:
7915 case TOK.synchronized_:
7916 break;
7918 case TOK.deprecated_:
7919 if (peek(t).value == TOK.leftParenthesis)
7921 t = peek(t);
7922 if (!skipParens(t, &t))
7923 goto Lerror;
7924 // t is on the next of closing parenthesis
7925 continue;
7927 break;
7929 case TOK.nothrow_:
7930 case TOK.pure_:
7931 case TOK.ref_:
7932 case TOK.gshared:
7933 case TOK.return_:
7934 break;
7936 case TOK.at:
7937 t = peek(t);
7938 if (t.value == TOK.identifier)
7940 /* @identifier
7941 * @identifier!arg
7942 * @identifier!(arglist)
7943 * any of the above followed by (arglist)
7944 * @predefined_attribute
7946 if (isBuiltinAtAttribute(t.ident))
7947 break;
7948 t = peek(t);
7949 if (t.value == TOK.not)
7951 t = peek(t);
7952 if (t.value == TOK.leftParenthesis)
7954 // @identifier!(arglist)
7955 if (!skipParens(t, &t))
7956 goto Lerror;
7957 // t is on the next of closing parenthesis
7959 else
7961 // @identifier!arg
7962 // Do low rent skipTemplateArgument
7963 if (t.value == TOK.vector)
7965 // identifier!__vector(type)
7966 t = peek(t);
7967 if (!skipParens(t, &t))
7968 goto Lerror;
7970 else
7971 t = peek(t);
7974 if (t.value == TOK.leftParenthesis)
7976 if (!skipParens(t, &t))
7977 goto Lerror;
7978 // t is on the next of closing parenthesis
7979 continue;
7981 continue;
7983 if (t.value == TOK.leftParenthesis)
7985 // @( ArgumentList )
7986 if (!skipParens(t, &t))
7987 goto Lerror;
7988 // t is on the next of closing parenthesis
7989 continue;
7991 goto Lerror;
7993 default:
7994 goto Ldone;
7996 t = peek(t);
7998 Ldone:
7999 if (pt)
8000 *pt = t;
8001 return true;
8003 Lerror:
8004 return false;
8007 AST.Expression parseExpression()
8009 auto loc = token.loc;
8011 //printf("Parser::parseExpression() loc = %d\n", loc.linnum);
8012 auto e = parseAssignExp();
8013 while (token.value == TOK.comma)
8015 nextToken();
8016 auto e2 = parseAssignExp();
8017 e = new AST.CommaExp(loc, e, e2, false);
8018 loc = token.loc;
8020 return e;
8023 /********************************* Expression Parser ***************************/
8025 AST.Expression parsePrimaryExp()
8027 AST.Expression e;
8028 AST.Type t;
8029 Identifier id;
8030 const loc = token.loc;
8032 //printf("parsePrimaryExp(): loc = %d\n", loc.linnum);
8033 switch (token.value)
8035 case TOK.identifier:
8037 if (peekNext() == TOK.arrow)
8039 // skip `identifier ->`
8040 nextToken();
8041 nextToken();
8042 error("use `.` for member lookup, not `->`");
8043 goto Lerr;
8046 if (peekNext() == TOK.goesTo)
8047 goto case_delegate;
8049 id = token.ident;
8050 nextToken();
8051 TOK save;
8052 if (token.value == TOK.not && (save = peekNext()) != TOK.is_ && save != TOK.in_)
8054 // identifier!(template-argument-list)
8055 auto tempinst = new AST.TemplateInstance(loc, id, parseTemplateArguments());
8056 e = new AST.ScopeExp(loc, tempinst);
8058 else
8059 e = new AST.IdentifierExp(loc, id);
8060 break;
8062 case TOK.dollar:
8063 if (!inBrackets)
8064 error("`$` is valid only inside [] of index or slice");
8065 e = new AST.DollarExp(loc);
8066 nextToken();
8067 break;
8069 case TOK.dot:
8070 // Signal global scope '.' operator with "" identifier
8071 e = new AST.IdentifierExp(loc, Id.empty);
8072 break;
8074 case TOK.this_:
8075 e = new AST.ThisExp(loc);
8076 nextToken();
8077 break;
8079 case TOK.super_:
8080 e = new AST.SuperExp(loc);
8081 nextToken();
8082 break;
8084 case TOK.int32Literal:
8085 e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint32);
8086 nextToken();
8087 break;
8089 case TOK.uns32Literal:
8090 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns32);
8091 nextToken();
8092 break;
8094 case TOK.int64Literal:
8095 e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint64);
8096 nextToken();
8097 break;
8099 case TOK.uns64Literal:
8100 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns64);
8101 nextToken();
8102 break;
8104 case TOK.float32Literal:
8105 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat32);
8106 nextToken();
8107 break;
8109 case TOK.float64Literal:
8110 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat64);
8111 nextToken();
8112 break;
8114 case TOK.float80Literal:
8115 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat80);
8116 nextToken();
8117 break;
8119 case TOK.imaginary32Literal:
8120 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary32);
8121 nextToken();
8122 break;
8124 case TOK.imaginary64Literal:
8125 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary64);
8126 nextToken();
8127 break;
8129 case TOK.imaginary80Literal:
8130 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary80);
8131 nextToken();
8132 break;
8134 case TOK.null_:
8135 e = new AST.NullExp(loc);
8136 nextToken();
8137 break;
8139 case TOK.file:
8141 const(char)* s = loc.filename ? loc.filename : mod.ident.toChars();
8142 e = new AST.StringExp(loc, s.toDString());
8143 nextToken();
8144 break;
8146 case TOK.fileFullPath:
8148 assert(loc.isValid(), "__FILE_FULL_PATH__ does not work with an invalid location");
8149 const s = FileName.toAbsolute(loc.filename);
8150 e = new AST.StringExp(loc, s.toDString());
8151 nextToken();
8152 break;
8155 case TOK.line:
8156 e = new AST.IntegerExp(loc, loc.linnum, AST.Type.tint32);
8157 nextToken();
8158 break;
8160 case TOK.moduleString:
8162 const(char)* s = md ? md.toChars() : mod.toChars();
8163 e = new AST.StringExp(loc, s.toDString());
8164 nextToken();
8165 break;
8167 case TOK.functionString:
8168 e = new AST.FuncInitExp(loc);
8169 nextToken();
8170 break;
8172 case TOK.prettyFunction:
8173 e = new AST.PrettyFuncInitExp(loc);
8174 nextToken();
8175 break;
8177 case TOK.true_:
8178 e = new AST.IntegerExp(loc, 1, AST.Type.tbool);
8179 nextToken();
8180 break;
8182 case TOK.false_:
8183 e = new AST.IntegerExp(loc, 0, AST.Type.tbool);
8184 nextToken();
8185 break;
8187 case TOK.charLiteral:
8188 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tchar);
8189 nextToken();
8190 break;
8192 case TOK.wcharLiteral:
8193 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.twchar);
8194 nextToken();
8195 break;
8197 case TOK.dcharLiteral:
8198 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tdchar);
8199 nextToken();
8200 break;
8202 case TOK.string_:
8203 case TOK.hexadecimalString:
8204 const bool hexString = token.value == TOK.hexadecimalString;
8206 // cat adjacent strings
8207 auto s = token.ustring;
8208 auto len = token.len;
8209 auto postfix = token.postfix;
8210 while (1)
8212 const prev = token;
8213 nextToken();
8214 if (token.value == TOK.string_ || token.value == TOK.hexadecimalString)
8216 if (token.postfix)
8218 if (token.postfix != postfix)
8219 error(token.loc, "mismatched string literal postfixes `'%c'` and `'%c'`", postfix, token.postfix);
8220 postfix = token.postfix;
8223 error("implicit string concatenation is error-prone and disallowed in D");
8224 eSink.errorSupplemental(token.loc, "Use the explicit syntax instead " ~
8225 "(concatenating literals is `@nogc`): %s ~ %s",
8226 prev.toChars(), token.toChars());
8228 const len1 = len;
8229 const len2 = token.len;
8230 len = len1 + len2;
8231 auto s2 = cast(char*)mem.xmalloc_noscan(len * char.sizeof);
8232 memcpy(s2, s, len1 * char.sizeof);
8233 memcpy(s2 + len1, token.ustring, len2 * char.sizeof);
8234 s = s2;
8236 else
8237 break;
8239 e = new AST.StringExp(loc, s[0 .. len], len, 1, postfix);
8240 e.isStringExp().hexString = hexString;
8241 break;
8243 case TOK.void_:
8244 t = AST.Type.tvoid;
8245 goto LabelX;
8247 case TOK.int8:
8248 t = AST.Type.tint8;
8249 goto LabelX;
8251 case TOK.uns8:
8252 t = AST.Type.tuns8;
8253 goto LabelX;
8255 case TOK.int16:
8256 t = AST.Type.tint16;
8257 goto LabelX;
8259 case TOK.uns16:
8260 t = AST.Type.tuns16;
8261 goto LabelX;
8263 case TOK.int32:
8264 t = AST.Type.tint32;
8265 goto LabelX;
8267 case TOK.uns32:
8268 t = AST.Type.tuns32;
8269 goto LabelX;
8271 case TOK.int64:
8272 t = AST.Type.tint64;
8273 goto LabelX;
8275 case TOK.uns64:
8276 t = AST.Type.tuns64;
8277 goto LabelX;
8279 case TOK.int128:
8280 t = AST.Type.tint128;
8281 goto LabelX;
8283 case TOK.uns128:
8284 t = AST.Type.tuns128;
8285 goto LabelX;
8287 case TOK.float32:
8288 t = AST.Type.tfloat32;
8289 goto LabelX;
8291 case TOK.float64:
8292 t = AST.Type.tfloat64;
8293 goto LabelX;
8295 case TOK.float80:
8296 t = AST.Type.tfloat80;
8297 goto LabelX;
8299 case TOK.imaginary32:
8300 t = AST.Type.timaginary32;
8301 goto LabelX;
8303 case TOK.imaginary64:
8304 t = AST.Type.timaginary64;
8305 goto LabelX;
8307 case TOK.imaginary80:
8308 t = AST.Type.timaginary80;
8309 goto LabelX;
8311 case TOK.complex32:
8312 t = AST.Type.tcomplex32;
8313 goto LabelX;
8315 case TOK.complex64:
8316 t = AST.Type.tcomplex64;
8317 goto LabelX;
8319 case TOK.complex80:
8320 t = AST.Type.tcomplex80;
8321 goto LabelX;
8323 case TOK.bool_:
8324 t = AST.Type.tbool;
8325 goto LabelX;
8327 case TOK.char_:
8328 t = AST.Type.tchar;
8329 goto LabelX;
8331 case TOK.wchar_:
8332 t = AST.Type.twchar;
8333 goto LabelX;
8335 case TOK.dchar_:
8336 t = AST.Type.tdchar;
8337 goto LabelX;
8338 LabelX:
8339 const next = peekNext();
8340 if (next != TOK.leftParenthesis && next != TOK.dot)
8342 // defer error for better diagnostics
8343 e = new AST.TypeExp(loc, parseType);
8344 break;
8346 nextToken();
8347 if (token.value == TOK.leftParenthesis)
8349 e = new AST.TypeExp(loc, t);
8350 e = new AST.CallExp(loc, e, parseArguments());
8351 break;
8353 check(TOK.dot);
8354 if (token.value != TOK.identifier)
8356 error(token.loc, "found `%s` when expecting identifier following `%s`.", token.toChars(), t.toChars());
8357 goto Lerr;
8359 e = new AST.DotIdExp(loc, new AST.TypeExp(loc, t), token.ident);
8360 nextToken();
8361 break;
8363 case TOK.typeof_:
8365 t = parseTypeof();
8366 e = new AST.TypeExp(loc, t);
8367 break;
8369 case TOK.vector:
8371 t = parseVector();
8372 e = new AST.TypeExp(loc, t);
8373 break;
8375 case TOK.typeid_:
8377 nextToken();
8378 check(TOK.leftParenthesis, "`typeid`");
8379 RootObject o = parseTypeOrAssignExp();
8380 check(TOK.rightParenthesis);
8381 e = new AST.TypeidExp(loc, o);
8382 break;
8384 case TOK.traits:
8386 /* __traits(identifier, args...)
8388 Identifier ident;
8389 AST.Objects* args = null;
8391 nextToken();
8392 check(TOK.leftParenthesis);
8393 if (token.value != TOK.identifier)
8395 error("`__traits(identifier, args...)` expected");
8396 goto Lerr;
8398 ident = token.ident;
8399 nextToken();
8400 if (token.value == TOK.comma)
8401 args = parseTemplateArgumentList(); // __traits(identifier, args...)
8402 else
8403 check(TOK.rightParenthesis); // __traits(identifier)
8405 e = new AST.TraitsExp(loc, ident, args);
8406 break;
8408 case TOK.is_:
8410 AST.Type targ;
8411 Identifier ident = null;
8412 AST.Type tspec = null;
8413 TOK tok = TOK.reserved;
8414 TOK tok2 = TOK.reserved;
8415 AST.TemplateParameters* tpl = null;
8417 nextToken();
8418 if (token.value == TOK.leftParenthesis)
8420 nextToken();
8421 if (token.value == TOK.identifier && peekNext() == TOK.leftParenthesis)
8423 error(loc, "unexpected `(` after `%s`, inside `is` expression. Try enclosing the contents of `is` with a `typeof` expression", token.toChars());
8424 nextToken();
8425 Token* tempTok = peekPastParen(&token);
8426 memcpy(&token, tempTok, Token.sizeof);
8427 goto Lerr;
8429 targ = parseType(&ident);
8430 if (token.value == TOK.colon || token.value == TOK.equal)
8432 tok = token.value;
8433 nextToken();
8434 if (tok == TOK.equal && (token.value == TOK.struct_ || token.value == TOK.union_
8435 || token.value == TOK.class_ || token.value == TOK.super_ || token.value == TOK.enum_
8436 || token.value == TOK.interface_ || token.value == TOK.package_ || token.value == TOK.module_
8437 || token.value == TOK.argumentTypes || token.value == TOK.parameters
8438 || token.value == TOK.const_ && peekNext() == TOK.rightParenthesis
8439 || token.value == TOK.immutable_ && peekNext() == TOK.rightParenthesis
8440 || token.value == TOK.shared_ && peekNext() == TOK.rightParenthesis
8441 || token.value == TOK.inout_ && peekNext() == TOK.rightParenthesis || token.value == TOK.function_
8442 || token.value == TOK.delegate_ || token.value == TOK.return_
8443 || (token.value == TOK.vector && peekNext() == TOK.rightParenthesis)))
8445 tok2 = token.value;
8446 nextToken();
8448 else
8450 tspec = parseType();
8453 if (tspec)
8455 if (token.value == TOK.comma)
8456 tpl = parseTemplateParameterList(1);
8457 else
8459 tpl = new AST.TemplateParameters();
8460 check(TOK.rightParenthesis);
8463 else
8464 check(TOK.rightParenthesis);
8466 else
8468 error("`type identifier : specialization` expected following `is`");
8469 goto Lerr;
8471 e = new AST.IsExp(loc, targ, ident, tok, tspec, tok2, tpl);
8472 break;
8474 case TOK.assert_:
8476 // https://dlang.org/spec/expression.html#assert_expressions
8477 AST.Expression msg = null;
8479 nextToken();
8480 check(TOK.leftParenthesis, "`assert`");
8481 e = parseAssignExp();
8482 if (token.value == TOK.comma)
8484 nextToken();
8485 if (token.value != TOK.rightParenthesis)
8487 msg = parseAssignExp();
8488 if (token.value == TOK.comma)
8489 nextToken();
8492 check(TOK.rightParenthesis);
8493 e = new AST.AssertExp(loc, e, msg);
8494 break;
8496 case TOK.mixin_:
8498 // https://dlang.org/spec/expression.html#mixin_expressions
8499 nextToken();
8500 if (token.value != TOK.leftParenthesis)
8501 error(token.loc, "found `%s` when expecting `%s` following `mixin`", token.toChars(), Token.toChars(TOK.leftParenthesis));
8502 auto exps = parseArguments();
8503 e = new AST.MixinExp(loc, exps);
8504 break;
8506 case TOK.import_:
8508 nextToken();
8509 check(TOK.leftParenthesis, "`import`");
8510 e = parseAssignExp();
8511 check(TOK.rightParenthesis);
8512 e = new AST.ImportExp(loc, e);
8513 break;
8515 case TOK.new_:
8516 e = parseNewExp(null);
8517 break;
8519 case TOK.auto_:
8521 if (peekNext() == TOK.ref_ && peekNext2() == TOK.leftParenthesis)
8523 Token* tk = peekPastParen(peek(peek(&token)));
8524 if (skipAttributes(tk, &tk) && (tk.value == TOK.goesTo || tk.value == TOK.leftCurly))
8526 // auto ref (arguments) => expression
8527 // auto ref (arguments) { statements... }
8528 goto case_delegate;
8531 nextToken();
8532 error("found `%s` when expecting `ref` and function literal following `auto`", token.toChars());
8533 goto Lerr;
8535 case TOK.ref_:
8537 if (peekNext() == TOK.leftParenthesis)
8539 Token* tk = peekPastParen(peek(&token));
8540 if (skipAttributes(tk, &tk) && (tk.value == TOK.goesTo || tk.value == TOK.leftCurly))
8542 // ref (arguments) => expression
8543 // ref (arguments) { statements... }
8544 goto case_delegate;
8547 nextToken();
8548 error("found `%s` when expecting function literal following `ref`", token.toChars());
8549 goto Lerr;
8551 case TOK.leftParenthesis:
8553 Token* tk = peekPastParen(&token);
8554 if (skipAttributes(tk, &tk) && (tk.value == TOK.goesTo || tk.value == TOK.leftCurly))
8556 // (arguments) => expression
8557 // (arguments) { statements... }
8558 goto case_delegate;
8561 // ( expression )
8562 nextToken();
8563 e = parseExpression();
8564 check(loc, TOK.rightParenthesis);
8565 break;
8567 case TOK.leftBracket:
8569 /* Parse array literals and associative array literals:
8570 * [ value, value, value ... ]
8571 * [ key:value, key:value, key:value ... ]
8573 auto values = new AST.Expressions();
8574 AST.Expressions* keys = null;
8576 nextToken();
8577 while (token.value != TOK.rightBracket && token.value != TOK.endOfFile)
8579 e = parseAssignExp();
8580 if (token.value == TOK.colon && (keys || values.length == 0))
8582 nextToken();
8583 if (!keys)
8584 keys = new AST.Expressions();
8585 keys.push(e);
8586 e = parseAssignExp();
8588 else if (keys)
8590 error("`key:value` expected for associative array literal");
8591 keys = null;
8593 values.push(e);
8594 if (token.value == TOK.rightBracket)
8595 break;
8596 check(TOK.comma);
8598 check(loc, TOK.rightBracket);
8600 if (keys)
8601 e = new AST.AssocArrayLiteralExp(loc, keys, values);
8602 else
8603 e = new AST.ArrayLiteralExp(loc, null, values);
8604 break;
8606 case TOK.leftCurly:
8607 case TOK.function_:
8608 case TOK.delegate_:
8609 case_delegate:
8611 AST.Dsymbol s = parseFunctionLiteral();
8612 e = new AST.FuncExp(loc, s);
8613 break;
8616 default:
8617 error("expression expected, not `%s`", token.toChars());
8618 Lerr:
8619 // Anything for e, as long as it's not NULL
8620 e = new AST.IntegerExp(loc, 0, AST.Type.tint32);
8621 nextToken();
8622 break;
8624 return e;
8627 private AST.Expression parseUnaryExp()
8629 AST.Expression e;
8630 const loc = token.loc;
8632 switch (token.value)
8634 case TOK.and:
8635 nextToken();
8636 e = parseUnaryExp();
8637 e = new AST.AddrExp(loc, e);
8638 break;
8640 case TOK.plusPlus:
8641 nextToken();
8642 e = parseUnaryExp();
8643 //e = new AddAssignExp(loc, e, new IntegerExp(loc, 1, Type::tint32));
8644 e = new AST.PreExp(EXP.prePlusPlus, loc, e);
8645 break;
8647 case TOK.minusMinus:
8648 nextToken();
8649 e = parseUnaryExp();
8650 //e = new MinAssignExp(loc, e, new IntegerExp(loc, 1, Type::tint32));
8651 e = new AST.PreExp(EXP.preMinusMinus, loc, e);
8652 break;
8654 case TOK.mul:
8655 nextToken();
8656 e = parseUnaryExp();
8657 e = new AST.PtrExp(loc, e);
8658 break;
8660 case TOK.min:
8661 nextToken();
8662 e = parseUnaryExp();
8663 e = new AST.NegExp(loc, e);
8664 break;
8666 case TOK.add:
8667 nextToken();
8668 e = parseUnaryExp();
8669 e = new AST.UAddExp(loc, e);
8670 break;
8672 case TOK.not:
8673 nextToken();
8674 e = parseUnaryExp();
8675 e = new AST.NotExp(loc, e);
8676 break;
8678 case TOK.tilde:
8679 nextToken();
8680 e = parseUnaryExp();
8681 e = new AST.ComExp(loc, e);
8682 break;
8684 case TOK.delete_:
8685 // @@@DEPRECATED_2.109@@@
8686 // Use of `delete` keyword has been an error since 2.099.
8687 // Remove from the parser after 2.109.
8688 nextToken();
8689 e = parseUnaryExp();
8690 e = new AST.DeleteExp(loc, e, false);
8691 break;
8693 case TOK.cast_: // cast(type) expression
8695 nextToken();
8696 check(TOK.leftParenthesis);
8697 /* Look for cast(), cast(const), cast(immutable),
8698 * cast(shared), cast(shared const), cast(wild), cast(shared wild)
8700 ubyte m = 0;
8701 while (1)
8703 switch (token.value)
8705 case TOK.const_:
8706 if (peekNext() == TOK.leftParenthesis)
8707 break; // const as type constructor
8708 m |= MODFlags.const_; // const as storage class
8709 nextToken();
8710 continue;
8712 case TOK.immutable_:
8713 if (peekNext() == TOK.leftParenthesis)
8714 break;
8715 m |= MODFlags.immutable_;
8716 nextToken();
8717 continue;
8719 case TOK.shared_:
8720 if (peekNext() == TOK.leftParenthesis)
8721 break;
8722 m |= MODFlags.shared_;
8723 nextToken();
8724 continue;
8726 case TOK.inout_:
8727 if (peekNext() == TOK.leftParenthesis)
8728 break;
8729 m |= MODFlags.wild;
8730 nextToken();
8731 continue;
8733 default:
8734 break;
8736 break;
8738 if (token.value == TOK.rightParenthesis)
8740 nextToken();
8741 e = parseUnaryExp();
8742 e = new AST.CastExp(loc, e, m);
8744 else
8746 AST.Type t = parseType(); // cast( type )
8747 t = t.addMod(m); // cast( const type )
8748 check(TOK.rightParenthesis);
8749 e = parseUnaryExp();
8750 e = new AST.CastExp(loc, e, t);
8752 break;
8754 case TOK.inout_:
8755 case TOK.shared_:
8756 case TOK.const_:
8757 case TOK.immutable_: // immutable(type)(arguments) / immutable(type).init
8759 StorageClass stc = parseTypeCtor();
8761 AST.Type t = parseBasicType();
8762 t = t.addSTC(stc);
8764 if (stc == 0 && token.value == TOK.dot)
8766 nextToken();
8767 if (token.value != TOK.identifier)
8769 error("identifier expected following `%s.`, not `%s`",
8770 t.toChars(), token.toChars());
8771 return AST.ErrorExp.get();
8773 e = new AST.DotIdExp(loc, new AST.TypeExp(loc, t), token.ident);
8774 nextToken();
8775 e = parsePostExp(e);
8777 else
8779 e = new AST.TypeExp(loc, t);
8780 if (token.value != TOK.leftParenthesis)
8782 error("`(arguments)` expected following `%s`, not `%s`",
8783 t.toChars(), token.toChars());
8784 return e;
8786 e = new AST.CallExp(loc, e, parseArguments());
8788 break;
8790 case TOK.leftParenthesis:
8792 auto tk = peek(&token);
8793 static if (CCASTSYNTAX)
8795 // If cast
8796 if (isDeclaration(tk, NeedDeclaratorId.no, TOK.rightParenthesis, &tk))
8798 tk = peek(tk); // skip over right parenthesis
8799 switch (tk.value)
8801 case TOK.not:
8802 tk = peek(tk);
8803 if (tk.value == TOK.is_ || tk.value == TOK.in_) // !is or !in
8804 break;
8805 goto case;
8807 case TOK.dot:
8808 case TOK.plusPlus:
8809 case TOK.minusMinus:
8810 case TOK.delete_:
8811 case TOK.new_:
8812 case TOK.leftParenthesis:
8813 case TOK.identifier:
8814 case TOK.this_:
8815 case TOK.super_:
8816 case TOK.int32Literal:
8817 case TOK.uns32Literal:
8818 case TOK.int64Literal:
8819 case TOK.uns64Literal:
8820 case TOK.int128Literal:
8821 case TOK.uns128Literal:
8822 case TOK.float32Literal:
8823 case TOK.float64Literal:
8824 case TOK.float80Literal:
8825 case TOK.imaginary32Literal:
8826 case TOK.imaginary64Literal:
8827 case TOK.imaginary80Literal:
8828 case TOK.null_:
8829 case TOK.true_:
8830 case TOK.false_:
8831 case TOK.charLiteral:
8832 case TOK.wcharLiteral:
8833 case TOK.dcharLiteral:
8834 case TOK.string_:
8835 case TOK.function_:
8836 case TOK.delegate_:
8837 case TOK.typeof_:
8838 case TOK.traits:
8839 case TOK.vector:
8840 case TOK.file:
8841 case TOK.fileFullPath:
8842 case TOK.line:
8843 case TOK.moduleString:
8844 case TOK.functionString:
8845 case TOK.prettyFunction:
8846 case TOK.wchar_:
8847 case TOK.dchar_:
8848 case TOK.bool_:
8849 case TOK.char_:
8850 case TOK.int8:
8851 case TOK.uns8:
8852 case TOK.int16:
8853 case TOK.uns16:
8854 case TOK.int32:
8855 case TOK.uns32:
8856 case TOK.int64:
8857 case TOK.uns64:
8858 case TOK.int128:
8859 case TOK.uns128:
8860 case TOK.float32:
8861 case TOK.float64:
8862 case TOK.float80:
8863 case TOK.imaginary32:
8864 case TOK.imaginary64:
8865 case TOK.imaginary80:
8866 case TOK.complex32:
8867 case TOK.complex64:
8868 case TOK.complex80:
8869 case TOK.void_:
8871 // (type) una_exp
8872 nextToken();
8873 // Note: `t` may be an expression that looks like a type
8874 auto t = parseType();
8875 check(TOK.rightParenthesis);
8877 // if .identifier
8878 // or .identifier!( ... )
8879 if (token.value == TOK.dot)
8881 if (peekNext() != TOK.identifier && peekNext() != TOK.new_)
8883 error("identifier or new keyword expected following `(...)`.");
8884 nextToken();
8885 return AST.ErrorExp.get();
8887 auto te = new AST.TypeExp(loc, t);
8888 te.parens = true;
8889 e = parsePostExp(te);
8891 else if (token.value == TOK.leftParenthesis ||
8892 token.value == TOK.plusPlus || token.value == TOK.minusMinus)
8894 // (type)(expr)
8895 // (callable)(args)
8896 // (expr)++
8897 auto te = new AST.TypeExp(loc, t);
8898 te.parens = true;
8899 e = parsePostExp(te);
8901 else
8903 e = parseUnaryExp();
8904 e = new AST.CastExp(loc, e, t);
8905 error(loc, "C style cast illegal, use `%s`", e.toChars());
8907 return e;
8909 default:
8910 break;
8914 e = parsePrimaryExp();
8915 e = parsePostExp(e);
8916 break;
8918 case TOK.throw_:
8920 nextToken();
8921 // Deviation from the DIP:
8922 // Parse AssignExpression instead of Expression to avoid conflicts for comma
8923 // separated lists, e.g. function arguments
8924 AST.Expression exp = parseAssignExp();
8925 e = new AST.ThrowExp(loc, exp);
8926 break;
8929 default:
8930 e = parsePrimaryExp();
8931 e = parsePostExp(e);
8932 break;
8934 assert(e);
8936 // ^^ is right associative and has higher precedence than the unary operators
8937 while (token.value == TOK.pow)
8939 nextToken();
8940 AST.Expression e2 = parseUnaryExp();
8941 e = new AST.PowExp(loc, e, e2);
8944 return e;
8947 private AST.Expression parsePostExp(AST.Expression e)
8949 while (1)
8951 const loc = token.loc;
8952 switch (token.value)
8954 case TOK.dot:
8955 nextToken();
8956 if (token.value == TOK.identifier)
8958 Identifier id = token.ident;
8960 nextToken();
8961 if (token.value == TOK.not && peekNext() != TOK.is_ && peekNext() != TOK.in_)
8963 AST.Objects* tiargs = parseTemplateArguments();
8964 e = new AST.DotTemplateInstanceExp(loc, e, id, tiargs);
8966 else
8967 e = new AST.DotIdExp(loc, e, id);
8968 continue;
8970 if (token.value == TOK.new_)
8972 e = parseNewExp(e);
8973 continue;
8975 error("identifier or `new` expected following `.`, not `%s`", token.toChars());
8976 break;
8978 case TOK.plusPlus:
8979 e = new AST.PostExp(EXP.plusPlus, loc, e);
8980 break;
8982 case TOK.minusMinus:
8983 e = new AST.PostExp(EXP.minusMinus, loc, e);
8984 break;
8986 case TOK.leftParenthesis:
8987 AST.Expressions* args = new AST.Expressions();
8988 AST.Identifiers* names = new AST.Identifiers();
8989 parseNamedArguments(args, names);
8990 e = new AST.CallExp(loc, e, args, names);
8991 continue;
8993 case TOK.leftBracket:
8995 // array dereferences:
8996 // array[index]
8997 // array[]
8998 // array[lwr .. upr]
8999 AST.Expression index;
9000 AST.Expression upr;
9001 auto arguments = new AST.Expressions();
9003 inBrackets++;
9004 nextToken();
9005 while (token.value != TOK.rightBracket && token.value != TOK.endOfFile)
9007 index = parseAssignExp();
9008 if (token.value == TOK.slice)
9010 // array[..., lwr..upr, ...]
9011 nextToken();
9012 upr = parseAssignExp();
9013 arguments.push(new AST.IntervalExp(loc, index, upr));
9015 else
9016 arguments.push(index);
9017 if (token.value == TOK.rightBracket)
9018 break;
9019 check(TOK.comma);
9021 check(TOK.rightBracket);
9022 inBrackets--;
9023 e = new AST.ArrayExp(loc, e, arguments);
9024 continue;
9026 default:
9027 return e;
9029 nextToken();
9033 private AST.Expression parseMulExp()
9035 const loc = token.loc;
9036 auto e = parseUnaryExp();
9038 while (1)
9040 switch (token.value)
9042 case TOK.mul:
9043 nextToken();
9044 auto e2 = parseUnaryExp();
9045 e = new AST.MulExp(loc, e, e2);
9046 continue;
9048 case TOK.div:
9049 nextToken();
9050 auto e2 = parseUnaryExp();
9051 e = new AST.DivExp(loc, e, e2);
9052 continue;
9054 case TOK.mod:
9055 nextToken();
9056 auto e2 = parseUnaryExp();
9057 e = new AST.ModExp(loc, e, e2);
9058 continue;
9060 default:
9061 break;
9063 break;
9065 return e;
9068 private AST.Expression parseAddExp()
9070 const loc = token.loc;
9071 auto e = parseMulExp();
9073 while (1)
9075 switch (token.value)
9077 case TOK.add:
9078 nextToken();
9079 auto e2 = parseMulExp();
9080 e = new AST.AddExp(loc, e, e2);
9081 continue;
9083 case TOK.min:
9084 nextToken();
9085 auto e2 = parseMulExp();
9086 e = new AST.MinExp(loc, e, e2);
9087 continue;
9089 case TOK.tilde:
9090 nextToken();
9091 auto e2 = parseMulExp();
9092 e = new AST.CatExp(loc, e, e2);
9093 continue;
9095 default:
9096 break;
9098 break;
9100 return e;
9103 private AST.Expression parseShiftExp()
9105 const loc = token.loc;
9106 auto e = parseAddExp();
9108 while (1)
9110 switch (token.value)
9112 case TOK.leftShift:
9113 nextToken();
9114 auto e2 = parseAddExp();
9115 e = new AST.ShlExp(loc, e, e2);
9116 continue;
9118 case TOK.rightShift:
9119 nextToken();
9120 auto e2 = parseAddExp();
9121 e = new AST.ShrExp(loc, e, e2);
9122 continue;
9124 case TOK.unsignedRightShift:
9125 nextToken();
9126 auto e2 = parseAddExp();
9127 e = new AST.UshrExp(loc, e, e2);
9128 continue;
9130 default:
9131 break;
9133 break;
9135 return e;
9138 private AST.Expression parseCmpExp()
9140 const loc = token.loc;
9142 auto e = parseShiftExp();
9143 EXP op = EXP.reserved;
9145 switch (token.value)
9147 case TOK.equal: op = EXP.equal; goto Lequal;
9148 case TOK.notEqual: op = EXP.notEqual; goto Lequal;
9149 Lequal:
9150 nextToken();
9151 auto e2 = parseShiftExp();
9152 e = new AST.EqualExp(op, loc, e, e2);
9153 break;
9155 case TOK.not:
9157 // Attempt to identify '!is'
9158 const tv = peekNext();
9159 if (tv == TOK.in_)
9161 nextToken();
9162 nextToken();
9163 auto e2 = parseShiftExp();
9164 e = new AST.InExp(loc, e, e2);
9165 e = new AST.NotExp(loc, e);
9166 break;
9168 if (tv != TOK.is_)
9169 break;
9170 nextToken();
9171 op = EXP.notIdentity;
9172 goto Lidentity;
9174 case TOK.is_: op = EXP.identity; goto Lidentity;
9175 Lidentity:
9176 nextToken();
9177 auto e2 = parseShiftExp();
9178 e = new AST.IdentityExp(op, loc, e, e2);
9179 break;
9181 case TOK.lessThan: op = EXP.lessThan; goto Lcmp;
9182 case TOK.lessOrEqual: op = EXP.lessOrEqual; goto Lcmp;
9183 case TOK.greaterThan: op = EXP.greaterThan; goto Lcmp;
9184 case TOK.greaterOrEqual: op = EXP.greaterOrEqual; goto Lcmp;
9185 Lcmp:
9186 nextToken();
9187 auto e2 = parseShiftExp();
9188 e = new AST.CmpExp(op, loc, e, e2);
9189 break;
9191 case TOK.in_:
9192 nextToken();
9193 auto e2 = parseShiftExp();
9194 e = new AST.InExp(loc, e, e2);
9195 break;
9197 default:
9198 break;
9200 return e;
9203 private AST.Expression parseAndExp()
9205 Loc loc = token.loc;
9206 bool parens = token.value == TOK.leftParenthesis;
9207 auto e = parseCmpExp();
9208 while (token.value == TOK.and)
9210 if (!parens)
9211 checkParens(TOK.and, e);
9212 parens = nextToken() == TOK.leftParenthesis;
9213 auto e2 = parseCmpExp();
9214 if (!parens)
9215 checkParens(TOK.and, e2);
9216 e = new AST.AndExp(loc, e, e2);
9217 parens = true; // don't call checkParens() for And
9218 loc = token.loc;
9220 return e;
9223 private AST.Expression parseXorExp()
9225 Loc loc = token.loc;
9227 bool parens = token.value == TOK.leftParenthesis;
9228 auto e = parseAndExp();
9229 while (token.value == TOK.xor)
9231 if (!parens)
9232 checkParens(TOK.xor, e);
9233 parens = nextToken() == TOK.leftParenthesis;
9234 auto e2 = parseAndExp();
9235 if (!parens)
9236 checkParens(TOK.xor, e2);
9237 e = new AST.XorExp(loc, e, e2);
9238 parens = true;
9239 loc = token.loc;
9241 return e;
9244 private AST.Expression parseOrExp()
9246 Loc loc = token.loc;
9248 bool parens = token.value == TOK.leftParenthesis;
9249 auto e = parseXorExp();
9250 while (token.value == TOK.or)
9252 if (!parens)
9253 checkParens(TOK.or, e);
9254 parens = nextToken() == TOK.leftParenthesis;
9255 auto e2 = parseXorExp();
9256 if (!parens)
9257 checkParens(TOK.or, e2);
9258 e = new AST.OrExp(loc, e, e2);
9259 parens = true;
9260 loc = token.loc;
9262 return e;
9265 private AST.Expression parseAndAndExp()
9267 const loc = token.loc;
9269 auto e = parseOrExp();
9270 while (token.value == TOK.andAnd)
9272 nextToken();
9273 auto e2 = parseOrExp();
9274 e = new AST.LogicalExp(loc, EXP.andAnd, e, e2);
9276 return e;
9279 private AST.Expression parseOrOrExp()
9281 const loc = token.loc;
9283 auto e = parseAndAndExp();
9284 while (token.value == TOK.orOr)
9286 nextToken();
9287 auto e2 = parseAndAndExp();
9288 e = new AST.LogicalExp(loc, EXP.orOr, e, e2);
9290 return e;
9293 private AST.Expression parseCondExp()
9295 const loc = token.loc;
9297 auto e = parseOrOrExp();
9298 if (token.value == TOK.question)
9300 nextToken();
9301 auto e1 = parseExpression();
9302 check(TOK.colon);
9303 auto e2 = parseCondExp();
9304 e = new AST.CondExp(loc, e, e1, e2);
9306 return e;
9309 AST.Expression parseAssignExp()
9311 bool parens = token.value == TOK.leftParenthesis;
9312 AST.Expression e;
9313 e = parseCondExp();
9314 if (e is null)
9315 return e;
9317 // require parens for e.g. `t ? a = 1 : b = 2`
9318 void checkRequiredParens()
9320 if (e.op == EXP.question && !parens)
9321 eSink.error(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`",
9322 e.toChars(), Token.toChars(token.value));
9325 const loc = token.loc;
9326 switch (token.value)
9328 case TOK.assign:
9329 checkRequiredParens();
9330 nextToken();
9331 auto e2 = parseAssignExp();
9332 e = new AST.AssignExp(loc, e, e2);
9333 break;
9335 case TOK.addAssign:
9336 checkRequiredParens();
9337 nextToken();
9338 auto e2 = parseAssignExp();
9339 e = new AST.AddAssignExp(loc, e, e2);
9340 break;
9342 case TOK.minAssign:
9343 checkRequiredParens();
9344 nextToken();
9345 auto e2 = parseAssignExp();
9346 e = new AST.MinAssignExp(loc, e, e2);
9347 break;
9349 case TOK.mulAssign:
9350 checkRequiredParens();
9351 nextToken();
9352 auto e2 = parseAssignExp();
9353 e = new AST.MulAssignExp(loc, e, e2);
9354 break;
9356 case TOK.divAssign:
9357 checkRequiredParens();
9358 nextToken();
9359 auto e2 = parseAssignExp();
9360 e = new AST.DivAssignExp(loc, e, e2);
9361 break;
9363 case TOK.modAssign:
9364 checkRequiredParens();
9365 nextToken();
9366 auto e2 = parseAssignExp();
9367 e = new AST.ModAssignExp(loc, e, e2);
9368 break;
9370 case TOK.powAssign:
9371 checkRequiredParens();
9372 nextToken();
9373 auto e2 = parseAssignExp();
9374 e = new AST.PowAssignExp(loc, e, e2);
9375 break;
9377 case TOK.andAssign:
9378 checkRequiredParens();
9379 nextToken();
9380 auto e2 = parseAssignExp();
9381 e = new AST.AndAssignExp(loc, e, e2);
9382 break;
9384 case TOK.orAssign:
9385 checkRequiredParens();
9386 nextToken();
9387 auto e2 = parseAssignExp();
9388 e = new AST.OrAssignExp(loc, e, e2);
9389 break;
9391 case TOK.xorAssign:
9392 checkRequiredParens();
9393 nextToken();
9394 auto e2 = parseAssignExp();
9395 e = new AST.XorAssignExp(loc, e, e2);
9396 break;
9398 case TOK.leftShiftAssign:
9399 checkRequiredParens();
9400 nextToken();
9401 auto e2 = parseAssignExp();
9402 e = new AST.ShlAssignExp(loc, e, e2);
9403 break;
9405 case TOK.rightShiftAssign:
9406 checkRequiredParens();
9407 nextToken();
9408 auto e2 = parseAssignExp();
9409 e = new AST.ShrAssignExp(loc, e, e2);
9410 break;
9412 case TOK.unsignedRightShiftAssign:
9413 checkRequiredParens();
9414 nextToken();
9415 auto e2 = parseAssignExp();
9416 e = new AST.UshrAssignExp(loc, e, e2);
9417 break;
9419 case TOK.concatenateAssign:
9420 checkRequiredParens();
9421 nextToken();
9422 auto e2 = parseAssignExp();
9423 e = new AST.CatAssignExp(loc, e, e2);
9424 break;
9426 default:
9427 break;
9430 return e;
9433 /*************************
9434 * Collect argument list.
9435 * Assume current token is ',', '$(LPAREN)' or '['.
9437 private AST.Expressions* parseArguments()
9439 // function call
9440 AST.Expressions* arguments = new AST.Expressions();
9441 parseNamedArguments(arguments, null);
9442 return arguments;
9445 /*************************
9446 * Collect argument list.
9447 * Assume current token is ',', '$(LPAREN)' or '['.
9449 private void parseNamedArguments(AST.Expressions* arguments, AST.Identifiers* names)
9451 assert(arguments);
9453 const endtok = token.value == TOK.leftBracket ? TOK.rightBracket : TOK.rightParenthesis;
9455 nextToken();
9457 while (token.value != endtok && token.value != TOK.endOfFile)
9459 if (peekNext() == TOK.colon)
9461 // Named argument `name: exp`
9462 auto loc = token.loc;
9463 auto ident = token.ident;
9464 check(TOK.identifier);
9465 check(TOK.colon);
9466 if (names)
9467 names.push(ident);
9468 else
9469 error(loc, "named arguments not allowed here");
9471 else
9473 if (names)
9474 names.push(null);
9477 auto arg = parseAssignExp();
9478 arguments.push(arg);
9480 if (token.value != TOK.comma)
9481 break;
9483 nextToken(); //comma
9485 check(endtok);
9488 /*******************************************
9490 private AST.Expression parseNewExp(AST.Expression thisexp)
9492 const loc = token.loc;
9494 nextToken();
9495 AST.Expressions* arguments = null;
9496 AST.Identifiers* names = null;
9498 // An anonymous nested class starts with "class"
9499 if (token.value == TOK.class_)
9501 nextToken();
9502 if (token.value == TOK.leftParenthesis)
9504 arguments = new AST.Expressions();
9505 names = new AST.Identifiers();
9506 parseNamedArguments(arguments, names);
9509 AST.BaseClasses* baseclasses = null;
9510 if (token.value != TOK.leftCurly)
9511 baseclasses = parseBaseClasses();
9513 Identifier id = null;
9514 AST.Dsymbols* members = null;
9516 if (token.value != TOK.leftCurly)
9518 error("`{ members }` expected for anonymous class");
9520 else
9522 nextToken();
9523 members = parseDeclDefs(0);
9524 if (token.value != TOK.rightCurly)
9525 error("class member expected");
9526 nextToken();
9529 auto cd = new AST.ClassDeclaration(loc, id, baseclasses, members, false);
9530 auto e = new AST.NewAnonClassExp(loc, thisexp, cd, arguments);
9531 return e;
9534 const stc = parseTypeCtor();
9535 auto t = parseBasicType(true);
9536 t = parseTypeSuffixes(t);
9537 t = t.addSTC(stc);
9538 if (t.ty == Taarray)
9540 AST.TypeAArray taa = cast(AST.TypeAArray)t;
9541 AST.Type index = taa.index;
9542 // `new Type[expr]` is a static array
9543 auto edim = AST.typeToExpression(index);
9544 if (edim)
9545 t = new AST.TypeSArray(taa.next, edim);
9547 else if (token.value == TOK.leftParenthesis && t.ty != Tsarray)
9549 arguments = new AST.Expressions();
9550 names = new AST.Identifiers();
9551 parseNamedArguments(arguments, names);
9554 auto e = new AST.NewExp(loc, thisexp, t, arguments, names);
9555 return e;
9558 /**********************************************
9560 private void addComment(AST.Dsymbol s, const(char)* blockComment)
9562 if (s !is null)
9563 this.addComment(s, blockComment.toDString());
9566 private void addComment(AST.Dsymbol s, const(char)[] blockComment)
9568 if (s !is null)
9570 s.addComment(combineComments(blockComment, token.lineComment, true));
9571 token.lineComment = null;
9575 /**********************************************
9576 * Recognize builtin @ attributes
9577 * Params:
9578 * ident = identifier
9579 * Returns:
9580 * storage class for attribute, 0 if not
9582 static StorageClass isBuiltinAtAttribute(Identifier ident)
9584 return (ident == Id.property) ? STC.property :
9585 (ident == Id.nogc) ? STC.nogc :
9586 (ident == Id.safe) ? STC.safe :
9587 (ident == Id.trusted) ? STC.trusted :
9588 (ident == Id.system) ? STC.system :
9589 (ident == Id.live) ? STC.live :
9590 (ident == Id.future) ? STC.future :
9591 (ident == Id.disable) ? STC.disable :
9595 enum StorageClass atAttrGroup =
9596 STC.property |
9597 STC.nogc |
9598 STC.safe |
9599 STC.trusted |
9600 STC.system |
9601 STC.live |
9602 /*STC.future |*/ // probably should be included
9603 STC.disable;
9605 void usageOfBodyKeyword()
9607 version (none) // disable obsolete warning
9609 eSink.warning(token.loc, "usage of identifer `body` as a keyword is obsolete. Use `do` instead.");
9614 enum PREC : int
9616 zero,
9617 expr,
9618 assign,
9619 cond,
9620 oror,
9621 andand,
9623 xor,
9624 and,
9625 equal,
9626 rel,
9627 shift,
9628 add,
9629 mul,
9630 pow,
9631 unary,
9632 primary,
9635 /**********************************
9636 * Set operator precedence for each operator.
9638 * Used by hdrgen
9640 immutable PREC[EXP.max + 1] precedence =
9642 EXP.type : PREC.expr,
9643 EXP.error : PREC.expr,
9644 EXP.objcClassReference : PREC.expr, // Objective-C class reference, same as EXP.type
9646 EXP.mixin_ : PREC.primary,
9648 EXP.import_ : PREC.primary,
9649 EXP.dotVariable : PREC.primary,
9650 EXP.scope_ : PREC.primary,
9651 EXP.identifier : PREC.primary,
9652 EXP.this_ : PREC.primary,
9653 EXP.super_ : PREC.primary,
9654 EXP.int64 : PREC.primary,
9655 EXP.float64 : PREC.primary,
9656 EXP.complex80 : PREC.primary,
9657 EXP.null_ : PREC.primary,
9658 EXP.string_ : PREC.primary,
9659 EXP.arrayLiteral : PREC.primary,
9660 EXP.assocArrayLiteral : PREC.primary,
9661 EXP.classReference : PREC.primary,
9662 EXP.file : PREC.primary,
9663 EXP.fileFullPath : PREC.primary,
9664 EXP.line : PREC.primary,
9665 EXP.moduleString : PREC.primary,
9666 EXP.functionString : PREC.primary,
9667 EXP.prettyFunction : PREC.primary,
9668 EXP.typeid_ : PREC.primary,
9669 EXP.is_ : PREC.primary,
9670 EXP.assert_ : PREC.primary,
9671 EXP.halt : PREC.primary,
9672 EXP.template_ : PREC.primary,
9673 EXP.dSymbol : PREC.primary,
9674 EXP.function_ : PREC.primary,
9675 EXP.variable : PREC.primary,
9676 EXP.symbolOffset : PREC.primary,
9677 EXP.structLiteral : PREC.primary,
9678 EXP.compoundLiteral : PREC.primary,
9679 EXP.arrayLength : PREC.primary,
9680 EXP.delegatePointer : PREC.primary,
9681 EXP.delegateFunctionPointer : PREC.primary,
9682 EXP.remove : PREC.primary,
9683 EXP.tuple : PREC.primary,
9684 EXP.traits : PREC.primary,
9685 EXP.overloadSet : PREC.primary,
9686 EXP.void_ : PREC.primary,
9687 EXP.vectorArray : PREC.primary,
9688 EXP._Generic : PREC.primary,
9690 // post
9691 EXP.dotTemplateInstance : PREC.primary,
9692 EXP.dotIdentifier : PREC.primary,
9693 EXP.dotTemplateDeclaration : PREC.primary,
9694 EXP.dot : PREC.primary,
9695 EXP.dotType : PREC.primary,
9696 EXP.plusPlus : PREC.primary,
9697 EXP.minusMinus : PREC.primary,
9698 EXP.prePlusPlus : PREC.primary,
9699 EXP.preMinusMinus : PREC.primary,
9700 EXP.call : PREC.primary,
9701 EXP.slice : PREC.primary,
9702 EXP.array : PREC.primary,
9703 EXP.index : PREC.primary,
9705 EXP.delegate_ : PREC.unary,
9706 EXP.address : PREC.unary,
9707 EXP.star : PREC.unary,
9708 EXP.negate : PREC.unary,
9709 EXP.uadd : PREC.unary,
9710 EXP.not : PREC.unary,
9711 EXP.tilde : PREC.unary,
9712 EXP.delete_ : PREC.unary,
9713 EXP.new_ : PREC.unary,
9714 EXP.newAnonymousClass : PREC.unary,
9715 EXP.cast_ : PREC.unary,
9716 EXP.throw_ : PREC.unary,
9718 EXP.vector : PREC.unary,
9719 EXP.pow : PREC.pow,
9721 EXP.mul : PREC.mul,
9722 EXP.div : PREC.mul,
9723 EXP.mod : PREC.mul,
9725 EXP.add : PREC.add,
9726 EXP.min : PREC.add,
9727 EXP.concatenate : PREC.add,
9729 EXP.leftShift : PREC.shift,
9730 EXP.rightShift : PREC.shift,
9731 EXP.unsignedRightShift : PREC.shift,
9733 EXP.lessThan : PREC.rel,
9734 EXP.lessOrEqual : PREC.rel,
9735 EXP.greaterThan : PREC.rel,
9736 EXP.greaterOrEqual : PREC.rel,
9737 EXP.in_ : PREC.rel,
9739 /* Note that we changed precedence, so that < and != have the same
9740 * precedence. This change is in the parser, too.
9742 EXP.equal : PREC.rel,
9743 EXP.notEqual : PREC.rel,
9744 EXP.identity : PREC.rel,
9745 EXP.notIdentity : PREC.rel,
9747 EXP.and : PREC.and,
9748 EXP.xor : PREC.xor,
9749 EXP.or : PREC.or,
9751 EXP.andAnd : PREC.andand,
9752 EXP.orOr : PREC.oror,
9754 EXP.question : PREC.cond,
9756 EXP.assign : PREC.assign,
9757 EXP.construct : PREC.assign,
9758 EXP.blit : PREC.assign,
9759 EXP.addAssign : PREC.assign,
9760 EXP.minAssign : PREC.assign,
9761 EXP.concatenateAssign : PREC.assign,
9762 EXP.concatenateElemAssign : PREC.assign,
9763 EXP.concatenateDcharAssign : PREC.assign,
9764 EXP.mulAssign : PREC.assign,
9765 EXP.divAssign : PREC.assign,
9766 EXP.modAssign : PREC.assign,
9767 EXP.powAssign : PREC.assign,
9768 EXP.leftShiftAssign : PREC.assign,
9769 EXP.rightShiftAssign : PREC.assign,
9770 EXP.unsignedRightShiftAssign : PREC.assign,
9771 EXP.andAssign : PREC.assign,
9772 EXP.orAssign : PREC.assign,
9773 EXP.xorAssign : PREC.assign,
9775 EXP.comma : PREC.expr,
9776 EXP.declaration : PREC.expr,
9778 EXP.interval : PREC.assign,
9781 enum ParseStatementFlags : int
9783 scope_ = 2, // start a new scope
9784 curly = 4, // { } statement is required
9785 curlyScope = 8, // { } starts a new scope
9786 semiOk = 0x10, // empty ';' are really ok
9789 struct PrefixAttributes(AST)
9791 StorageClass storageClass;
9792 AST.Expression depmsg;
9793 LINK link;
9794 AST.Visibility visibility;
9795 bool setAlignment;
9796 AST.Expression ealign;
9797 AST.Expressions* udas;
9798 const(char)* comment;
9801 /// The result of the `ParseLinkage` function
9802 struct ParsedLinkage(AST)
9804 /// What linkage was specified
9805 LINK link;
9806 /// If `extern(C++, class|struct)`, contains the `class|struct`
9807 CPPMANGLE cppmangle;
9808 /// If `extern(C++, some.identifier)`, will be the identifiers
9809 AST.Identifiers* idents;
9810 /// If `extern(C++, (some_tuple_expression)|"string"), will be the expressions
9811 AST.Expressions* identExps;
9815 /*********************************** Private *************************************/
9817 /***********************
9818 * How multiple declarations are parsed.
9819 * If 1, treat as C.
9820 * If 0, treat:
9821 * int *p, i;
9822 * as:
9823 * int* p;
9824 * int* i;
9826 private enum CDECLSYNTAX = 0;
9828 /*****
9829 * Support C cast syntax:
9830 * (type)(expression)
9832 private enum CCASTSYNTAX = 1;
9834 /*****
9835 * Support postfix C array declarations, such as
9836 * int a[3][4];
9838 private enum CARRAYDECL = 1;
9840 /*****************************
9841 * Destructively extract storage class from pAttrs.
9843 private StorageClass getStorageClass(AST)(PrefixAttributes!(AST)* pAttrs)
9845 StorageClass stc = STC.undefined_;
9846 if (pAttrs)
9848 stc = pAttrs.storageClass;
9849 pAttrs.storageClass = STC.undefined_;
9851 return stc;