d: Merge dmd. druntime e770945277, phobos 6d6e0b9b9
[official-gcc.git] / gcc / d / dmd / parse.d
blob0dc54ffe762410a339fe6603312d64913414790a
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-2024 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.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 using `in ref` for legacy compatibility
1228 else
1230 version (IN_GCC)
1231 error("attribute `scope` cannot be applied with `in`, use `-fpreview=in` instead");
1232 else
1233 error("attribute `scope` cannot be applied with `in`, use `-preview=in` instead");
1235 return orig;
1238 if ((added & STC.in_) && (orig & Redundant))
1240 if (orig & STC.const_)
1241 error("attribute `in` cannot be added after `const`: remove `const`");
1242 else if (compileEnv.previewIn)
1244 // Windows `printf` does not support `%1$s`
1245 const(char*) stc_str = (orig & STC.scope_) ? "scope".ptr : "ref".ptr;
1246 error(token.loc, "attribute `in` cannot be added after `%s`: remove `%s`",
1247 stc_str, stc_str);
1249 else if (orig & STC.ref_)
1251 // accept using `in ref` for legacy compatibility
1253 else
1255 version (IN_GCC)
1256 error("attribute `in` cannot be added after `scope`: remove `scope` and use `-fpreview=in`");
1257 else
1258 error("attribute `in` cannot be added after `scope`: remove `scope` and use `-preview=in`");
1260 return orig;
1263 checkConflictSTCGroup(STC.const_ | STC.immutable_ | STC.manifest);
1264 checkConflictSTCGroup(STC.gshared | STC.shared_);
1265 checkConflictSTCGroup!true(STC.safeGroup);
1267 return orig;
1270 /***********************************************
1271 * Parse attribute(s), lexer is on '@'.
1273 * Attributes can be builtin (e.g. `@safe`, `@nogc`, etc...),
1274 * or be user-defined (UDAs). In the former case, we return the storage
1275 * class via the return value, while in thelater case we return `0`
1276 * and set `pudas`.
1278 * Params:
1279 * pudas = An array of UDAs to append to
1281 * Returns:
1282 * If the attribute is builtin, the return value will be non-zero.
1283 * Otherwise, 0 is returned, and `pudas` will be appended to.
1285 private StorageClass parseAttribute(ref AST.Expressions* udas)
1287 nextToken();
1288 if (token.value == TOK.identifier)
1290 // If we find a builtin attribute, we're done, return immediately.
1291 if (StorageClass stc = isBuiltinAtAttribute(token.ident))
1292 return stc;
1294 // Allow identifier, template instantiation, or function call
1295 // for `@Argument` (single UDA) form.
1296 AST.Expression exp = parsePrimaryExp();
1297 if (token.value == TOK.leftParenthesis)
1299 const loc = token.loc;
1300 AST.Expressions* args = new AST.Expressions();
1301 AST.Identifiers* names = new AST.Identifiers();
1302 parseNamedArguments(args, names);
1303 exp = new AST.CallExp(loc, exp, args, names);
1306 if (udas is null)
1307 udas = new AST.Expressions();
1308 udas.push(exp);
1309 return 0;
1312 AST.Expression templateArgToExp(RootObject o, const ref Loc loc)
1314 switch (o.dyncast)
1316 case DYNCAST.expression:
1317 return cast(AST.Expression) o;
1318 case DYNCAST.type:
1319 return new AST.TypeExp(loc, cast(AST.Type)o);
1320 default:
1321 assert(0);
1325 if (token.value == TOK.leftParenthesis)
1327 // Multi-UDAs ( `@( ArgumentList )`) form, concatenate with existing
1328 if (peekNext() == TOK.rightParenthesis)
1329 error("empty attribute list is not allowed");
1331 if (udas is null)
1332 udas = new AST.Expressions();
1333 auto args = parseTemplateArgumentList();
1334 foreach (arg; *args)
1335 udas.push(templateArgToExp(arg, token.loc));
1336 return 0;
1339 if (auto o = parseTemplateSingleArgument())
1341 if (udas is null)
1342 udas = new AST.Expressions();
1343 udas.push(templateArgToExp(o, token.loc));
1344 return 0;
1347 if (token.isKeyword())
1348 error("`%s` is a keyword, not an `@` attribute", token.toChars());
1349 else
1350 error("`@identifier` or `@(ArgumentList)` expected, not `@%s`", token.toChars());
1352 return 0;
1355 /***********************************************
1356 * Parse const/immutable/shared/inout/nothrow/pure postfix
1358 private StorageClass parsePostfix(StorageClass storageClass, AST.Expressions** pudas)
1360 while (1)
1362 StorageClass stc;
1363 switch (token.value)
1365 case TOK.const_:
1366 stc = STC.const_;
1367 break;
1369 case TOK.immutable_:
1370 stc = STC.immutable_;
1371 break;
1373 case TOK.shared_:
1374 stc = STC.shared_;
1375 break;
1377 case TOK.inout_:
1378 stc = STC.wild;
1379 break;
1381 case TOK.nothrow_:
1382 stc = STC.nothrow_;
1383 break;
1385 case TOK.pure_:
1386 stc = STC.pure_;
1387 break;
1389 case TOK.return_:
1390 stc = STC.return_;
1391 if (peekNext() == TOK.scope_)
1392 stc |= STC.returnScope; // recognize `return scope`
1393 break;
1395 case TOK.scope_:
1396 stc = STC.scope_;
1397 break;
1399 case TOK.at:
1401 AST.Expressions* udas = null;
1402 stc = parseAttribute(udas);
1403 if (udas)
1405 if (pudas)
1406 *pudas = AST.UserAttributeDeclaration.concat(*pudas, udas);
1407 else
1409 // Disallow:
1410 // void function() @uda fp;
1411 // () @uda { return 1; }
1412 error("user-defined attributes cannot appear as postfixes");
1414 continue;
1416 break;
1418 default:
1419 Token* tk;
1420 if (skipAttributes(&token, &tk) && tk.ptr != token.ptr ||
1421 token.value == TOK.static_ || token.value == TOK.extern_)
1423 error("`%s` token is not allowed in postfix position",
1424 Token.toChars(token.value));
1425 nextToken();
1426 continue;
1428 return storageClass;
1430 storageClass = appendStorageClass(storageClass, stc);
1431 nextToken();
1435 private StorageClass parseTypeCtor()
1437 StorageClass storageClass = STC.undefined_;
1439 while (1)
1441 if (peekNext() == TOK.leftParenthesis)
1442 return storageClass;
1444 StorageClass stc;
1445 switch (token.value)
1447 case TOK.const_:
1448 stc = STC.const_;
1449 break;
1451 case TOK.immutable_:
1452 stc = STC.immutable_;
1453 break;
1455 case TOK.shared_:
1456 stc = STC.shared_;
1457 break;
1459 case TOK.inout_:
1460 stc = STC.wild;
1461 break;
1463 default:
1464 return storageClass;
1466 storageClass = appendStorageClass(storageClass, stc);
1467 nextToken();
1471 /**************************************
1472 * Parse constraint.
1473 * Constraint is of the form:
1474 * if ( ConstraintExpression )
1476 private AST.Expression parseConstraint()
1478 AST.Expression e = null;
1479 if (token.value == TOK.if_)
1481 nextToken(); // skip over 'if'
1482 check(TOK.leftParenthesis);
1483 e = parseExpression();
1484 check(TOK.rightParenthesis);
1486 return e;
1489 /**************************************
1490 * Parse a TemplateDeclaration.
1492 private AST.TemplateDeclaration parseTemplateDeclaration(bool ismixin = false)
1494 AST.TemplateDeclaration tempdecl;
1495 Identifier id;
1496 AST.TemplateParameters* tpl;
1497 AST.Dsymbols* decldefs;
1498 AST.Expression constraint = null;
1499 const loc = token.loc;
1501 nextToken();
1502 if (token.value != TOK.identifier)
1504 error("identifier expected following `template`");
1505 goto Lerr;
1507 id = token.ident;
1508 nextToken();
1509 tpl = parseTemplateParameterList();
1510 if (!tpl)
1511 goto Lerr;
1513 constraint = parseConstraint();
1515 if (token.value != TOK.leftCurly)
1517 error("`{` expected after template parameter list, not `%s`", token.toChars()); /* } */
1518 goto Lerr;
1520 decldefs = parseBlock(null);
1522 tempdecl = new AST.TemplateDeclaration(loc, id, tpl, constraint, decldefs, ismixin);
1523 return tempdecl;
1525 Lerr:
1526 return null;
1529 /******************************************
1530 * Parse template parameter list.
1531 * Input:
1532 * flag 0: parsing "( list )"
1533 * 1: parsing non-empty "list $(RPAREN)"
1535 private AST.TemplateParameters* parseTemplateParameterList(int flag = 0)
1537 auto tpl = new AST.TemplateParameters();
1539 if (!flag && token.value != TOK.leftParenthesis)
1541 error("parenthesized template parameter list expected following template identifier");
1542 goto Lerr;
1544 nextToken();
1546 // Get array of TemplateParameters
1547 if (flag || token.value != TOK.rightParenthesis)
1549 while (token.value != TOK.rightParenthesis)
1551 AST.TemplateParameter tp;
1552 Loc loc;
1553 Identifier tp_ident = null;
1554 AST.Type tp_spectype = null;
1555 AST.Type tp_valtype = null;
1556 AST.Type tp_defaulttype = null;
1557 AST.Expression tp_specvalue = null;
1558 AST.Expression tp_defaultvalue = null;
1560 // Get TemplateParameter
1562 // First, look ahead to see if it is a TypeParameter or a ValueParameter
1563 const tv = peekNext();
1564 if (token.value == TOK.alias_)
1566 // AliasParameter
1567 nextToken();
1568 loc = token.loc; // todo
1569 AST.Type spectype = null;
1570 if (isDeclaration(&token, NeedDeclaratorId.must, TOK.reserved, null))
1572 spectype = parseType(&tp_ident);
1574 else
1576 if (token.value != TOK.identifier)
1578 error("identifier expected for template `alias` parameter");
1579 goto Lerr;
1581 tp_ident = token.ident;
1582 nextToken();
1584 RootObject spec = null;
1585 if (token.value == TOK.colon) // : Type
1587 nextToken();
1588 if (isDeclaration(&token, NeedDeclaratorId.no, TOK.reserved, null))
1589 spec = parseType();
1590 else
1591 spec = parseCondExp();
1593 RootObject def = null;
1594 if (token.value == TOK.assign) // = Type
1596 nextToken();
1597 if (isDeclaration(&token, NeedDeclaratorId.no, TOK.reserved, null))
1598 def = parseType();
1599 else
1600 def = parseCondExp();
1602 tp = new AST.TemplateAliasParameter(loc, tp_ident, spectype, spec, def);
1604 else if (tv == TOK.colon || tv == TOK.assign || tv == TOK.comma || tv == TOK.rightParenthesis)
1606 // TypeParameter
1607 if (token.value != TOK.identifier)
1609 error("identifier expected for template type parameter");
1610 goto Lerr;
1612 loc = token.loc;
1613 tp_ident = token.ident;
1614 nextToken();
1615 if (token.value == TOK.colon) // : Type
1617 nextToken();
1618 tp_spectype = parseType();
1620 if (token.value == TOK.assign) // = Type
1622 nextToken();
1623 tp_defaulttype = parseType();
1625 tp = new AST.TemplateTypeParameter(loc, tp_ident, tp_spectype, tp_defaulttype);
1627 else if (token.value == TOK.identifier && tv == TOK.dotDotDot)
1629 // ident...
1630 loc = token.loc;
1631 tp_ident = token.ident;
1632 nextToken();
1633 nextToken();
1634 tp = new AST.TemplateTupleParameter(loc, tp_ident);
1636 else if (token.value == TOK.this_)
1638 // ThisParameter
1639 nextToken();
1640 if (token.value != TOK.identifier)
1642 error("identifier expected for template `this` parameter");
1643 goto Lerr;
1645 loc = token.loc;
1646 tp_ident = token.ident;
1647 nextToken();
1648 if (token.value == TOK.colon) // : Type
1650 nextToken();
1651 tp_spectype = parseType();
1653 if (token.value == TOK.assign) // = Type
1655 nextToken();
1656 tp_defaulttype = parseType();
1658 tp = new AST.TemplateThisParameter(loc, tp_ident, tp_spectype, tp_defaulttype);
1660 else
1662 // ValueParameter
1663 loc = token.loc; // todo
1664 tp_valtype = parseType(&tp_ident);
1665 if (!tp_ident)
1667 error("identifier expected for template value parameter");
1668 tp_ident = Identifier.idPool("error");
1670 if (token.value == TOK.colon) // : CondExpression
1672 nextToken();
1673 tp_specvalue = parseCondExp();
1675 if (token.value == TOK.assign) // = CondExpression
1677 nextToken();
1678 tp_defaultvalue = parseAssignExp();
1680 tp = new AST.TemplateValueParameter(loc, tp_ident, tp_valtype, tp_specvalue, tp_defaultvalue);
1682 tpl.push(tp);
1683 if (token.value != TOK.comma)
1684 break;
1685 nextToken();
1688 check(TOK.rightParenthesis);
1690 Lerr:
1691 return tpl;
1694 /******************************************
1695 * Parse template mixin.
1696 * mixin Foo;
1697 * mixin Foo!(args);
1698 * mixin a.b.c!(args).Foo!(args);
1699 * mixin Foo!(args) identifier;
1700 * mixin typeof(expr).identifier!(args);
1702 private AST.Dsymbol parseMixin()
1704 AST.TemplateMixin tm;
1705 Identifier id;
1706 AST.Objects* tiargs;
1708 //printf("parseMixin()\n");
1709 const locMixin = token.loc;
1710 nextToken(); // skip 'mixin'
1712 auto loc = token.loc;
1713 AST.TypeQualified tqual = null;
1714 if (token.value == TOK.dot)
1716 id = Id.empty;
1718 else
1720 if (token.value == TOK.typeof_)
1722 tqual = parseTypeof();
1723 check(TOK.dot);
1725 if (token.value != TOK.identifier)
1727 error("identifier expected, not `%s`", token.toChars());
1728 id = Id.empty;
1730 else
1731 id = token.ident;
1732 nextToken();
1735 while (1)
1737 tiargs = null;
1738 if (token.value == TOK.not)
1740 tiargs = parseTemplateArguments();
1743 if (tiargs && token.value == TOK.dot)
1745 auto tempinst = new AST.TemplateInstance(loc, id, tiargs);
1746 if (!tqual)
1747 tqual = new AST.TypeInstance(loc, tempinst);
1748 else
1749 tqual.addInst(tempinst);
1750 tiargs = null;
1752 else
1754 if (!tqual)
1755 tqual = new AST.TypeIdentifier(loc, id);
1756 else
1757 tqual.addIdent(id);
1760 if (token.value != TOK.dot)
1761 break;
1763 nextToken();
1764 if (token.value != TOK.identifier)
1766 error("identifier expected following `.` instead of `%s`", token.toChars());
1767 break;
1769 loc = token.loc;
1770 id = token.ident;
1771 nextToken();
1774 id = null;
1775 if (token.value == TOK.identifier)
1777 id = token.ident;
1778 nextToken();
1781 tm = new AST.TemplateMixin(locMixin, id, tqual, tiargs);
1782 if (token.value != TOK.semicolon)
1783 error("`;` expected after `mixin`");
1784 nextToken();
1786 return tm;
1789 /******************************************
1790 * Parse template arguments.
1791 * Input:
1792 * current token is opening '!'
1793 * Output:
1794 * current token is one after closing '$(RPAREN)'
1796 private AST.Objects* parseTemplateArguments()
1798 AST.Objects* tiargs;
1800 nextToken();
1801 if (token.value == TOK.leftParenthesis)
1803 // ident!(template_arguments)
1804 tiargs = parseTemplateArgumentList();
1806 else
1808 // ident!template_argument
1809 RootObject o = parseTemplateSingleArgument();
1810 if (!o)
1812 error("template argument expected following `!`");
1814 else
1816 tiargs = new AST.Objects();
1817 tiargs.push(o);
1820 if (token.value == TOK.not)
1822 TOK tok = peekNext();
1823 if (tok != TOK.is_ && tok != TOK.in_)
1825 error("multiple ! arguments are not allowed");
1826 Lagain:
1827 nextToken();
1828 if (token.value == TOK.leftParenthesis)
1829 parseTemplateArgumentList();
1830 else
1831 parseTemplateSingleArgument();
1832 if (token.value == TOK.not && (tok = peekNext()) != TOK.is_ && tok != TOK.in_)
1833 goto Lagain;
1836 return tiargs;
1839 /******************************************
1840 * Parse template argument list.
1841 * Input:
1842 * current token is opening '$(LPAREN)',
1843 * or ',' for __traits
1844 * Output:
1845 * current token is one after closing '$(RPAREN)'
1847 private AST.Objects* parseTemplateArgumentList()
1849 //printf("Parser::parseTemplateArgumentList()\n");
1850 auto tiargs = new AST.Objects();
1851 TOK endtok = TOK.rightParenthesis;
1852 assert(token.value == TOK.leftParenthesis || token.value == TOK.comma);
1853 nextToken();
1855 // Get TemplateArgumentList
1856 while (token.value != endtok)
1858 tiargs.push(parseTypeOrAssignExp());
1859 if (token.value != TOK.comma)
1860 break;
1861 nextToken();
1863 check(endtok, "template argument list");
1864 return tiargs;
1867 /***************************************
1868 * Parse a Type or an Expression
1869 * Returns:
1870 * RootObject representing the AST
1872 RootObject parseTypeOrAssignExp(TOK endtoken = TOK.reserved)
1874 return isDeclaration(&token, NeedDeclaratorId.no, endtoken, null)
1875 ? parseType() // argument is a type
1876 : parseAssignExp(); // argument is an expression
1879 /*****************************
1880 * Parse single template argument, to support the syntax:
1881 * foo!arg
1882 * Input:
1883 * current token is the arg
1884 * Returns: An AST.Type, AST.Expression, or `null` on error
1886 private RootObject parseTemplateSingleArgument()
1888 //printf("parseTemplateSingleArgument()\n");
1889 AST.Type ta;
1890 switch (token.value)
1892 case TOK.identifier:
1893 ta = new AST.TypeIdentifier(token.loc, token.ident);
1894 goto LabelX;
1896 case TOK.vector:
1897 ta = parseVector();
1898 goto LabelX;
1900 case TOK.void_:
1901 ta = AST.Type.tvoid;
1902 goto LabelX;
1904 case TOK.int8:
1905 ta = AST.Type.tint8;
1906 goto LabelX;
1908 case TOK.uns8:
1909 ta = AST.Type.tuns8;
1910 goto LabelX;
1912 case TOK.int16:
1913 ta = AST.Type.tint16;
1914 goto LabelX;
1916 case TOK.uns16:
1917 ta = AST.Type.tuns16;
1918 goto LabelX;
1920 case TOK.int32:
1921 ta = AST.Type.tint32;
1922 goto LabelX;
1924 case TOK.uns32:
1925 ta = AST.Type.tuns32;
1926 goto LabelX;
1928 case TOK.int64:
1929 ta = AST.Type.tint64;
1930 goto LabelX;
1932 case TOK.uns64:
1933 ta = AST.Type.tuns64;
1934 goto LabelX;
1936 case TOK.int128:
1937 ta = AST.Type.tint128;
1938 goto LabelX;
1940 case TOK.uns128:
1941 ta = AST.Type.tuns128;
1942 goto LabelX;
1944 case TOK.float32:
1945 ta = AST.Type.tfloat32;
1946 goto LabelX;
1948 case TOK.float64:
1949 ta = AST.Type.tfloat64;
1950 goto LabelX;
1952 case TOK.float80:
1953 ta = AST.Type.tfloat80;
1954 goto LabelX;
1956 case TOK.imaginary32:
1957 ta = AST.Type.timaginary32;
1958 goto LabelX;
1960 case TOK.imaginary64:
1961 ta = AST.Type.timaginary64;
1962 goto LabelX;
1964 case TOK.imaginary80:
1965 ta = AST.Type.timaginary80;
1966 goto LabelX;
1968 case TOK.complex32:
1969 ta = AST.Type.tcomplex32;
1970 goto LabelX;
1972 case TOK.complex64:
1973 ta = AST.Type.tcomplex64;
1974 goto LabelX;
1976 case TOK.complex80:
1977 ta = AST.Type.tcomplex80;
1978 goto LabelX;
1980 case TOK.bool_:
1981 ta = AST.Type.tbool;
1982 goto LabelX;
1984 case TOK.char_:
1985 ta = AST.Type.tchar;
1986 goto LabelX;
1988 case TOK.wchar_:
1989 ta = AST.Type.twchar;
1990 goto LabelX;
1992 case TOK.dchar_:
1993 ta = AST.Type.tdchar;
1994 goto LabelX;
1995 LabelX:
1996 nextToken();
1997 return ta;
1999 case TOK.int32Literal:
2000 case TOK.uns32Literal:
2001 case TOK.int64Literal:
2002 case TOK.uns64Literal:
2003 case TOK.int128Literal:
2004 case TOK.uns128Literal:
2005 case TOK.float32Literal:
2006 case TOK.float64Literal:
2007 case TOK.float80Literal:
2008 case TOK.imaginary32Literal:
2009 case TOK.imaginary64Literal:
2010 case TOK.imaginary80Literal:
2011 case TOK.null_:
2012 case TOK.true_:
2013 case TOK.false_:
2014 case TOK.charLiteral:
2015 case TOK.wcharLiteral:
2016 case TOK.dcharLiteral:
2017 case TOK.string_:
2018 case TOK.interpolated:
2019 case TOK.hexadecimalString:
2020 case TOK.file:
2021 case TOK.fileFullPath:
2022 case TOK.line:
2023 case TOK.moduleString:
2024 case TOK.functionString:
2025 case TOK.prettyFunction:
2026 case TOK.this_:
2028 // Template argument is an expression
2029 return parsePrimaryExp();
2031 default:
2032 return null;
2036 /**********************************
2037 * Parse a static assertion.
2038 * Current token is 'static'.
2040 private AST.StaticAssert parseStaticAssert()
2042 const loc = token.loc;
2043 AST.Expression exp;
2044 AST.Expressions* msg = null;
2046 //printf("parseStaticAssert()\n");
2047 nextToken();
2048 nextToken();
2049 check(TOK.leftParenthesis);
2050 exp = parseAssignExp();
2051 if (token.value == TOK.comma)
2053 if (peekNext() == TOK.rightParenthesis)
2055 nextToken(); // consume `,`
2056 nextToken(); // consume `)`
2058 else
2059 msg = parseArguments();
2061 else
2062 check(TOK.rightParenthesis);
2063 check(TOK.semicolon, "static assert");
2064 return new AST.StaticAssert(loc, exp, msg);
2067 /***********************************
2068 * Parse typeof(expression).
2069 * Current token is on the 'typeof'.
2071 private AST.TypeQualified parseTypeof()
2073 AST.TypeQualified t;
2074 const loc = token.loc;
2076 nextToken();
2077 check(TOK.leftParenthesis);
2078 if (token.value == TOK.return_) // typeof(return)
2080 nextToken();
2081 t = new AST.TypeReturn(loc);
2083 else
2085 AST.Expression exp = parseExpression(); // typeof(expression)
2086 t = new AST.TypeTypeof(loc, exp);
2088 check(TOK.rightParenthesis);
2089 return t;
2092 /***********************************
2093 * Parse __vector(type).
2094 * Current token is on the '__vector'.
2096 private AST.Type parseVector()
2098 nextToken();
2099 check(TOK.leftParenthesis);
2100 AST.Type tb = parseType();
2101 check(TOK.rightParenthesis);
2102 return new AST.TypeVector(tb);
2105 /***********************************
2106 * Parse:
2107 * extern (linkage)
2108 * extern (C++, namespaces)
2109 * extern (C++, "namespace", "namespaces", ...)
2110 * extern (C++, (StringExp))
2111 * The parser is on the 'extern' token.
2113 private ParsedLinkage!(AST) parseLinkage()
2115 ParsedLinkage!(AST) result;
2116 nextToken();
2117 assert(token.value == TOK.leftParenthesis);
2118 nextToken();
2119 ParsedLinkage!(AST) returnLinkage(LINK link)
2121 check(TOK.rightParenthesis);
2122 result.link = link;
2123 return result;
2125 ParsedLinkage!(AST) invalidLinkage()
2127 error("valid linkage identifiers are `D`, `C`, `C++`, `Objective-C`, `Windows`, `System`");
2128 return returnLinkage(LINK.d);
2131 if (token.value != TOK.identifier)
2132 return returnLinkage(LINK.d);
2134 Identifier id = token.ident;
2135 nextToken();
2136 if (id == Id.Windows)
2137 return returnLinkage(LINK.windows);
2138 else if (id == Id.D)
2139 return returnLinkage(LINK.d);
2140 else if (id == Id.System)
2141 return returnLinkage(LINK.system);
2142 else if (id == Id.Objective) // Looking for tokens "Objective-C"
2144 if (token.value != TOK.min)
2145 return invalidLinkage();
2147 nextToken();
2148 if (token.ident != Id.C)
2149 return invalidLinkage();
2151 nextToken();
2152 return returnLinkage(LINK.objc);
2154 else if (id != Id.C)
2155 return invalidLinkage();
2157 if (token.value != TOK.plusPlus)
2158 return returnLinkage(LINK.c);
2160 nextToken();
2161 if (token.value != TOK.comma) // , namespaces or class or struct
2162 return returnLinkage(LINK.cpp);
2164 nextToken();
2166 if (token.value == TOK.rightParenthesis)
2167 return returnLinkage(LINK.cpp); // extern(C++,)
2169 if (token.value == TOK.class_ || token.value == TOK.struct_)
2171 result.cppmangle = token.value == TOK.class_ ? CPPMANGLE.asClass : CPPMANGLE.asStruct;
2172 nextToken();
2174 else if (token.value == TOK.identifier) // named scope namespace
2176 result.idents = new AST.Identifiers();
2177 while (1)
2179 Identifier idn = token.ident;
2180 result.idents.push(idn);
2181 nextToken();
2182 if (token.value == TOK.dot)
2184 nextToken();
2185 if (token.value == TOK.identifier)
2186 continue;
2187 error("identifier expected for C++ namespace");
2188 result.idents = null; // error occurred, invalidate list of elements.
2190 break;
2193 else // non-scoped StringExp namespace
2195 result.identExps = new AST.Expressions();
2196 while (1)
2198 result.identExps.push(parseCondExp());
2199 if (token.value != TOK.comma)
2200 break;
2201 nextToken();
2202 // Allow trailing commas as done for argument lists, arrays, ...
2203 if (token.value == TOK.rightParenthesis)
2204 break;
2207 return returnLinkage(LINK.cpp);
2210 /***********************************
2211 * Parse ident1.ident2.ident3
2213 * Params:
2214 * entity = what qualified identifier is expected to resolve into.
2215 * Used only for better error message
2217 * Returns:
2218 * array of identifiers with actual qualified one stored last
2220 private Identifier[] parseQualifiedIdentifier(const(char)* entity)
2222 Identifier[] qualified;
2226 nextToken();
2227 if (token.value != TOK.identifier)
2229 error(token.loc, "`%s` expected as dot-separated identifiers, got `%s`", entity, token.toChars());
2230 return qualified;
2233 Identifier id = token.ident;
2234 qualified ~= id;
2236 nextToken();
2238 while (token.value == TOK.dot);
2240 return qualified;
2243 private AST.DebugSymbol parseDebugSpecification()
2245 AST.DebugSymbol s;
2246 nextToken();
2247 if (token.value == TOK.identifier)
2248 s = new AST.DebugSymbol(token.loc, token.ident);
2249 else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
2251 // @@@DEPRECATED_2.111@@@
2252 // Deprecated in 2.101, remove in 2.111
2253 deprecation("`debug = <integer>` is deprecated, use debug identifiers instead");
2255 s = new AST.DebugSymbol(token.loc, cast(uint)token.unsvalue);
2257 else
2259 error("identifier or integer expected, not `%s`", token.toChars());
2260 s = null;
2262 nextToken();
2263 if (token.value != TOK.semicolon)
2264 error("semicolon expected");
2265 nextToken();
2266 return s;
2269 /**************************************
2270 * Parse a debug conditional
2272 private AST.Condition parseDebugCondition()
2274 uint level = 1;
2275 Identifier id = null;
2276 Loc loc = token.loc;
2278 if (token.value == TOK.leftParenthesis)
2280 nextToken();
2282 if (token.value == TOK.identifier)
2283 id = token.ident;
2284 else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
2286 // @@@DEPRECATED_2.111@@@
2287 // Deprecated in 2.101, remove in 2.111
2288 deprecation("`debug( <integer> )` is deprecated, use debug identifiers instead");
2290 level = cast(uint)token.unsvalue;
2292 else
2293 error("identifier or integer expected inside `debug(...)`, not `%s`", token.toChars());
2294 loc = token.loc;
2295 nextToken();
2296 check(TOK.rightParenthesis);
2298 return new AST.DebugCondition(loc, mod, level, id);
2301 /**************************************
2302 * Parse a version specification
2304 private AST.VersionSymbol parseVersionSpecification()
2306 AST.VersionSymbol s;
2307 nextToken();
2308 if (token.value == TOK.identifier)
2309 s = new AST.VersionSymbol(token.loc, token.ident);
2310 else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
2312 // @@@DEPRECATED_2.111@@@
2313 // Deprecated in 2.101, remove in 2.111
2314 deprecation("`version = <integer>` is deprecated, use version identifiers instead");
2315 s = new AST.VersionSymbol(token.loc, cast(uint)token.unsvalue);
2317 else
2319 error("identifier or integer expected, not `%s`", token.toChars());
2320 s = null;
2322 nextToken();
2323 if (token.value != TOK.semicolon)
2324 error("semicolon expected");
2325 nextToken();
2326 return s;
2329 /**************************************
2330 * Parse a version conditional
2332 private AST.Condition parseVersionCondition()
2334 uint level = 1;
2335 Identifier id = null;
2336 Loc loc;
2338 if (token.value == TOK.leftParenthesis)
2340 nextToken();
2341 /* Allow:
2342 * version (unittest)
2343 * version (assert)
2344 * even though they are keywords
2346 loc = token.loc;
2347 if (token.value == TOK.identifier)
2348 id = token.ident;
2349 else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
2351 // @@@DEPRECATED_2.111@@@
2352 // Deprecated in 2.101, remove in 2.111
2353 deprecation("`version( <integer> )` is deprecated, use version identifiers instead");
2355 level = cast(uint)token.unsvalue;
2357 else if (token.value == TOK.unittest_)
2358 id = Identifier.idPool(Token.toString(TOK.unittest_));
2359 else if (token.value == TOK.assert_)
2360 id = Identifier.idPool(Token.toString(TOK.assert_));
2361 else
2362 error("identifier or integer expected inside `version(...)`, not `%s`", token.toChars());
2363 nextToken();
2364 check(TOK.rightParenthesis);
2366 else
2367 error("(condition) expected following `version`");
2368 return new AST.VersionCondition(loc, mod, level, id);
2371 /***********************************************
2372 * static if (expression)
2373 * body
2374 * else
2375 * body
2376 * Current token is 'static'.
2378 private AST.Condition parseStaticIfCondition()
2380 AST.Expression exp;
2381 AST.Condition condition;
2382 const loc = token.loc;
2384 nextToken();
2385 nextToken();
2386 if (token.value == TOK.leftParenthesis)
2388 nextToken();
2389 exp = parseAssignExp();
2390 check(TOK.rightParenthesis);
2392 else
2394 error("(expression) expected following `static if`");
2395 exp = null;
2397 condition = new AST.StaticIfCondition(loc, exp);
2398 return condition;
2401 /*****************************************
2402 * Parse a constructor definition:
2403 * this(parameters) { body }
2404 * or postblit:
2405 * this(this) { body }
2406 * or constructor template:
2407 * this(templateparameters)(parameters) { body }
2408 * Current token is 'this'.
2410 private AST.Dsymbol parseCtor(PrefixAttributes!AST* pAttrs)
2412 AST.Expressions* udas = null;
2413 const loc = token.loc;
2414 StorageClass stc = getStorageClass!AST(pAttrs);
2416 nextToken();
2417 if (token.value == TOK.leftParenthesis && peekNext() == TOK.this_ && peekNext2() == TOK.rightParenthesis)
2419 // this(this) { ... }
2420 nextToken();
2421 nextToken();
2422 check(TOK.rightParenthesis);
2424 stc = parsePostfix(stc, &udas);
2425 if (stc & STC.immutable_)
2426 deprecation("`immutable` postblit is deprecated. Please use an unqualified postblit.");
2427 if (stc & STC.shared_)
2428 deprecation("`shared` postblit is deprecated. Please use an unqualified postblit.");
2429 if (stc & STC.const_)
2430 deprecation("`const` postblit is deprecated. Please use an unqualified postblit.");
2431 if (stc & STC.static_)
2432 error(loc, "postblit cannot be `static`");
2434 auto f = new AST.PostBlitDeclaration(loc, Loc.initial, stc, Id.postblit);
2435 AST.Dsymbol s = parseContracts(f);
2436 if (udas)
2438 auto a = new AST.Dsymbols();
2439 a.push(f);
2440 s = new AST.UserAttributeDeclaration(udas, a);
2442 return s;
2445 /* Look ahead to see if:
2446 * this(...)(...)
2447 * which is a constructor template
2449 AST.TemplateParameters* tpl = null;
2450 if (token.value == TOK.leftParenthesis && peekPastParen(&token).value == TOK.leftParenthesis)
2452 tpl = parseTemplateParameterList();
2455 /* Just a regular constructor
2457 auto parameterList = parseParameterList(null);
2458 stc = parsePostfix(stc, &udas);
2460 if (parameterList.varargs != VarArg.none || AST.Parameter.dim(parameterList.parameters) != 0)
2462 if (stc & STC.static_)
2463 error(loc, "constructor cannot be static");
2465 else if (StorageClass ss = stc & (STC.shared_ | STC.static_)) // this()
2467 if (ss == STC.static_)
2468 error(loc, "use `static this()` to declare a static constructor");
2469 else if (ss == (STC.shared_ | STC.static_))
2470 error(loc, "use `shared static this()` to declare a shared static constructor");
2473 AST.Expression constraint = tpl ? parseConstraint() : null;
2475 AST.Type tf = new AST.TypeFunction(parameterList, null, linkage, stc); // RetrunType -> auto
2476 tf = tf.addSTC(stc);
2478 auto f = new AST.CtorDeclaration(loc, Loc.initial, stc, tf);
2479 AST.Dsymbol s = parseContracts(f, !!tpl);
2480 if (udas)
2482 auto a = new AST.Dsymbols();
2483 a.push(f);
2484 s = new AST.UserAttributeDeclaration(udas, a);
2487 if (tpl)
2489 // Wrap a template around it
2490 auto decldefs = new AST.Dsymbols();
2491 decldefs.push(s);
2492 s = new AST.TemplateDeclaration(loc, f.ident, tpl, constraint, decldefs);
2495 return s;
2498 /*****************************************
2499 * Parse a destructor definition:
2500 * ~this() { body }
2501 * Current token is '~'.
2503 private AST.Dsymbol parseDtor(PrefixAttributes!AST* pAttrs)
2505 AST.Expressions* udas = null;
2506 const loc = token.loc;
2507 StorageClass stc = getStorageClass!AST(pAttrs);
2509 nextToken();
2510 check(TOK.this_);
2511 check(TOK.leftParenthesis);
2512 check(TOK.rightParenthesis);
2514 stc = parsePostfix(stc, &udas);
2515 if (StorageClass ss = stc & (STC.shared_ | STC.static_))
2517 if (ss == STC.static_)
2518 error(loc, "use `static ~this()` to declare a static destructor");
2519 else if (ss == (STC.shared_ | STC.static_))
2520 error(loc, "use `shared static ~this()` to declare a shared static destructor");
2523 auto f = new AST.DtorDeclaration(loc, Loc.initial, stc, Id.dtor);
2524 AST.Dsymbol s = parseContracts(f);
2525 if (udas)
2527 auto a = new AST.Dsymbols();
2528 a.push(f);
2529 s = new AST.UserAttributeDeclaration(udas, a);
2531 return s;
2534 /*****************************************
2535 * Parse a static constructor definition:
2536 * static this() { body }
2537 * Current token is 'static'.
2539 private AST.Dsymbol parseStaticCtor(PrefixAttributes!AST* pAttrs)
2541 //Expressions *udas = NULL;
2542 const loc = token.loc;
2543 StorageClass stc = getStorageClass!AST(pAttrs);
2545 nextToken();
2546 nextToken();
2547 check(TOK.leftParenthesis);
2548 check(TOK.rightParenthesis);
2550 stc = parsePostfix(stc & ~STC.TYPECTOR, null) | stc;
2551 if (stc & STC.shared_)
2552 error(loc, "use `shared static this()` to declare a shared static constructor");
2553 else if (stc & STC.static_)
2554 appendStorageClass(stc, STC.static_); // complaint for the redundancy
2555 else if (StorageClass modStc = stc & STC.TYPECTOR)
2557 OutBuffer buf;
2558 AST.stcToBuffer(buf, modStc);
2559 error(loc, "static constructor cannot be `%s`", buf.peekChars());
2561 stc &= ~(STC.static_ | STC.TYPECTOR);
2563 auto f = new AST.StaticCtorDeclaration(loc, Loc.initial, stc);
2564 AST.Dsymbol s = parseContracts(f);
2565 return s;
2568 /*****************************************
2569 * Parse a static destructor definition:
2570 * static ~this() { body }
2571 * Current token is 'static'.
2573 private AST.Dsymbol parseStaticDtor(PrefixAttributes!AST* pAttrs)
2575 AST.Expressions* udas = null;
2576 const loc = token.loc;
2577 StorageClass stc = getStorageClass!AST(pAttrs);
2579 nextToken();
2580 nextToken();
2581 check(TOK.this_);
2582 check(TOK.leftParenthesis);
2583 check(TOK.rightParenthesis);
2585 stc = parsePostfix(stc & ~STC.TYPECTOR, &udas) | stc;
2586 if (stc & STC.shared_)
2587 error(loc, "use `shared static ~this()` to declare a shared static destructor");
2588 else if (stc & STC.static_)
2589 appendStorageClass(stc, STC.static_); // complaint for the redundancy
2590 else if (StorageClass modStc = stc & STC.TYPECTOR)
2592 OutBuffer buf;
2593 AST.stcToBuffer(buf, modStc);
2594 error(loc, "static destructor cannot be `%s`", buf.peekChars());
2596 stc &= ~(STC.static_ | STC.TYPECTOR);
2598 auto f = new AST.StaticDtorDeclaration(loc, Loc.initial, stc);
2599 AST.Dsymbol s = parseContracts(f);
2600 if (udas)
2602 auto a = new AST.Dsymbols();
2603 a.push(f);
2604 s = new AST.UserAttributeDeclaration(udas, a);
2606 return s;
2609 /*****************************************
2610 * Parse a shared static constructor definition:
2611 * shared static this() { body }
2612 * Current token is 'shared'.
2614 private AST.Dsymbol parseSharedStaticCtor(PrefixAttributes!AST* pAttrs)
2616 //Expressions *udas = NULL;
2617 const loc = token.loc;
2618 StorageClass stc = getStorageClass!AST(pAttrs);
2620 nextToken();
2621 nextToken();
2622 nextToken();
2623 check(TOK.leftParenthesis);
2624 check(TOK.rightParenthesis);
2626 stc = parsePostfix(stc & ~STC.TYPECTOR, null) | stc;
2627 if (StorageClass ss = stc & (STC.shared_ | STC.static_))
2628 appendStorageClass(stc, ss); // complaint for the redundancy
2629 else if (StorageClass modStc = stc & STC.TYPECTOR)
2631 OutBuffer buf;
2632 AST.stcToBuffer(buf, modStc);
2633 error(loc, "shared static constructor cannot be `%s`", buf.peekChars());
2635 stc &= ~(STC.static_ | STC.TYPECTOR);
2637 auto f = new AST.SharedStaticCtorDeclaration(loc, Loc.initial, stc);
2638 AST.Dsymbol s = parseContracts(f);
2639 return s;
2642 /*****************************************
2643 * Parse a shared static destructor definition:
2644 * shared static ~this() { body }
2645 * Current token is 'shared'.
2647 private AST.Dsymbol parseSharedStaticDtor(PrefixAttributes!AST* pAttrs)
2649 AST.Expressions* udas = null;
2650 const loc = token.loc;
2651 StorageClass stc = getStorageClass!AST(pAttrs);
2653 nextToken();
2654 nextToken();
2655 nextToken();
2656 check(TOK.this_);
2657 check(TOK.leftParenthesis);
2658 check(TOK.rightParenthesis);
2660 stc = parsePostfix(stc & ~STC.TYPECTOR, &udas) | stc;
2661 if (StorageClass ss = stc & (STC.shared_ | STC.static_))
2662 appendStorageClass(stc, ss); // complaint for the redundancy
2663 else if (StorageClass modStc = stc & STC.TYPECTOR)
2665 OutBuffer buf;
2666 AST.stcToBuffer(buf, modStc);
2667 error(loc, "shared static destructor cannot be `%s`", buf.peekChars());
2669 stc &= ~(STC.static_ | STC.TYPECTOR);
2671 auto f = new AST.SharedStaticDtorDeclaration(loc, Loc.initial, stc);
2672 AST.Dsymbol s = parseContracts(f);
2673 if (udas)
2675 auto a = new AST.Dsymbols();
2676 a.push(f);
2677 s = new AST.UserAttributeDeclaration(udas, a);
2679 return s;
2682 /*****************************************
2683 * Parse an invariant definition:
2684 * invariant { statements... }
2685 * invariant() { statements... }
2686 * invariant (expression);
2687 * Current token is 'invariant'.
2689 private AST.Dsymbol parseInvariant(PrefixAttributes!AST* pAttrs)
2691 const loc = token.loc;
2692 StorageClass stc = getStorageClass!AST(pAttrs);
2694 nextToken();
2695 if (token.value == TOK.leftParenthesis) // optional () or invariant (expression);
2697 nextToken();
2698 if (token.value != TOK.rightParenthesis) // invariant (expression);
2700 AST.Expression e = parseAssignExp(), msg = null;
2701 if (token.value == TOK.comma)
2703 nextToken();
2704 if (token.value != TOK.rightParenthesis)
2706 msg = parseAssignExp();
2707 if (token.value == TOK.comma)
2708 nextToken();
2711 check(TOK.rightParenthesis);
2712 check(TOK.semicolon, "invariant");
2713 e = new AST.AssertExp(loc, e, msg);
2714 auto fbody = new AST.ExpStatement(loc, e);
2715 auto f = new AST.InvariantDeclaration(loc, token.loc, stc, null, fbody);
2716 return f;
2718 nextToken();
2721 auto fbody = parseStatement(ParseStatementFlags.curly);
2722 auto f = new AST.InvariantDeclaration(loc, token.loc, stc, null, fbody);
2723 return f;
2726 /*****************************************
2727 * Parse a unittest definition:
2728 * unittest { body }
2729 * Current token is 'unittest'.
2731 private AST.Dsymbol parseUnitTest(PrefixAttributes!AST* pAttrs)
2733 const loc = token.loc;
2734 StorageClass stc = getStorageClass!AST(pAttrs);
2736 nextToken();
2738 const(char)* begPtr = token.ptr + 1; // skip left curly brace
2739 const(char)* endPtr = null;
2740 AST.Statement sbody = parseStatement(ParseStatementFlags.curly, &endPtr);
2742 /** Extract unittest body as a string. Must be done eagerly since memory
2743 will be released by the lexer before doc gen. */
2744 char* docline = null;
2745 if (compileEnv.ddocOutput && endPtr > begPtr)
2747 /* Remove trailing whitespaces */
2748 for (const(char)* p = endPtr - 1; begPtr <= p && (*p == ' ' || *p == '\r' || *p == '\n' || *p == '\t'); --p)
2750 endPtr = p;
2753 size_t len = endPtr - begPtr;
2754 if (len > 0)
2756 docline = cast(char*)mem.xmalloc_noscan(len + 2);
2757 memcpy(docline, begPtr, len);
2758 docline[len] = '\n'; // Terminate all lines by LF
2759 docline[len + 1] = '\0';
2763 auto f = new AST.UnitTestDeclaration(loc, token.loc, stc, docline);
2764 f.fbody = sbody;
2765 return f;
2768 /*****************************************
2769 * Parse a new definition:
2770 * @disable new();
2771 * Current token is 'new'.
2773 private AST.Dsymbol parseNew(PrefixAttributes!AST* pAttrs)
2775 const loc = token.loc;
2776 StorageClass stc = getStorageClass!AST(pAttrs);
2777 if (!(stc & STC.disable))
2779 error("`new` allocator must be annotated with `@disabled`");
2781 nextToken();
2783 /* @@@DEPRECATED_2.108@@@
2784 * After deprecation period (2.108), remove all code in the version(all) block.
2786 version (all)
2788 auto parameterList = parseParameterList(null); // parameterList ignored
2789 if (parameterList.parameters.length > 0 || parameterList.varargs != VarArg.none)
2790 deprecation("`new` allocator with non-empty parameter list is deprecated");
2791 auto f = new AST.NewDeclaration(loc, stc);
2792 if (token.value != TOK.semicolon)
2794 deprecation("`new` allocator with function definition is deprecated");
2795 parseContracts(f); // body ignored
2796 f.fbody = null;
2797 f.fensures = null;
2798 f.frequires = null;
2800 else
2801 nextToken();
2802 return f;
2804 else
2806 check(TOK.leftParenthesis);
2807 check(TOK.rightParenthesis);
2808 check(TOK.semicolon);
2809 return new AST.NewDeclaration(loc, stc);
2813 /**********************************************
2814 * Parse parameter list.
2816 private AST.ParameterList parseParameterList(AST.TemplateParameters** tpl)
2818 auto parameters = new AST.Parameters();
2819 VarArg varargs = VarArg.none;
2820 StorageClass varargsStc;
2822 // Attributes allowed for ...
2823 enum VarArgsStc = STC.const_ | STC.immutable_ | STC.shared_ | STC.scope_ | STC.return_ | STC.returnScope;
2825 check(TOK.leftParenthesis);
2826 while (1)
2828 Identifier ai = null;
2829 AST.Type at;
2830 StorageClass storageClass = 0;
2831 StorageClass stc;
2832 AST.Expression ae;
2833 AST.Expressions* udas = null;
2834 for (; 1; nextToken())
2837 switch (token.value)
2839 case TOK.rightParenthesis:
2840 if (storageClass != 0 || udas !is null)
2841 error("basic type expected, not `)`");
2842 break;
2844 case TOK.dotDotDot:
2845 varargs = VarArg.variadic;
2846 varargsStc = storageClass;
2847 if (varargsStc & ~VarArgsStc)
2849 OutBuffer buf;
2850 AST.stcToBuffer(buf, varargsStc & ~VarArgsStc);
2851 error("variadic parameter cannot have attributes `%s`", buf.peekChars());
2852 varargsStc &= VarArgsStc;
2854 nextToken();
2855 break;
2857 case TOK.const_:
2858 if (peekNext() == TOK.leftParenthesis)
2859 goto default;
2860 stc = STC.const_;
2861 goto L2;
2863 case TOK.immutable_:
2864 if (peekNext() == TOK.leftParenthesis)
2865 goto default;
2866 stc = STC.immutable_;
2867 goto L2;
2869 case TOK.shared_:
2870 if (peekNext() == TOK.leftParenthesis)
2871 goto default;
2872 stc = STC.shared_;
2873 goto L2;
2875 case TOK.inout_:
2876 if (peekNext() == TOK.leftParenthesis)
2877 goto default;
2878 stc = STC.wild;
2879 goto L2;
2880 case TOK.at:
2882 AST.Expressions* exps = null;
2883 StorageClass stc2 = parseAttribute(exps);
2884 if (stc2 & atAttrGroup)
2886 error("`@%s` attribute for function parameter is not supported", token.toChars());
2888 else
2890 udas = AST.UserAttributeDeclaration.concat(udas, exps);
2892 if (token.value == TOK.dotDotDot)
2893 error("variadic parameter cannot have user-defined attributes");
2894 if (stc2)
2895 nextToken();
2896 goto L3;
2897 // Don't call nextToken again.
2899 case TOK.in_:
2900 if (transitionIn)
2901 eSink.message(scanloc, "Usage of 'in' on parameter");
2902 stc = STC.in_;
2903 if (compileEnv.previewIn)
2904 stc |= STC.constscoperef;
2905 goto L2;
2907 case TOK.out_:
2908 stc = STC.out_;
2909 goto L2;
2911 case TOK.ref_:
2912 stc = STC.ref_;
2913 goto L2;
2915 case TOK.lazy_:
2916 stc = STC.lazy_;
2917 goto L2;
2919 case TOK.scope_:
2920 stc = STC.scope_;
2921 goto L2;
2923 case TOK.final_:
2924 stc = STC.final_;
2925 goto L2;
2927 case TOK.auto_:
2928 stc = STC.auto_;
2929 goto L2;
2931 case TOK.return_:
2932 stc = STC.return_;
2933 if (peekNext() == TOK.scope_)
2934 stc |= STC.returnScope;
2935 goto L2;
2937 storageClass = appendStorageClass(storageClass, stc);
2938 continue;
2940 default:
2942 const stcx = storageClass & (STC.in_ | STC.ref_ | STC.out_ | STC.lazy_);
2943 // if stcx is not a power of 2
2944 if (stcx & (stcx - 1) && !(stcx == (STC.in_ | STC.ref_)))
2945 error("incompatible parameter storage classes");
2946 //if ((storageClass & STC.scope_) && (storageClass & (STC.ref_ | STC.out_)))
2947 //error("scope cannot be ref or out");
2949 const tv = peekNext();
2950 Loc loc;
2951 if (tpl && token.value == TOK.identifier &&
2952 (tv == TOK.comma || tv == TOK.rightParenthesis || tv == TOK.dotDotDot))
2954 Identifier id = Identifier.generateId("__T");
2955 loc = token.loc;
2956 at = new AST.TypeIdentifier(loc, id);
2957 if (!*tpl)
2958 *tpl = new AST.TemplateParameters();
2959 AST.TemplateParameter tp = new AST.TemplateTypeParameter(loc, id, null, null);
2960 (*tpl).push(tp);
2962 ai = token.ident;
2963 nextToken();
2965 else
2967 at = parseType(&ai, null, &loc);
2969 ae = null;
2970 if (token.value == TOK.assign) // = defaultArg
2972 nextToken();
2973 ae = parseAssignExp();
2975 auto param = new AST.Parameter(loc, storageClass | STC.parameter, at, ai, ae, null);
2976 if (udas)
2978 auto a = new AST.Dsymbols();
2979 auto udad = new AST.UserAttributeDeclaration(udas, a);
2980 param.userAttribDecl = udad;
2982 if (token.value == TOK.at)
2984 AST.Expressions* exps = null;
2985 StorageClass stc2 = parseAttribute(exps);
2986 if (stc2 & atAttrGroup)
2988 error("`@%s` attribute for function parameter is not supported", token.toChars());
2990 else
2992 error("user-defined attributes cannot appear as postfixes", token.toChars());
2994 if (stc2)
2995 nextToken();
2997 if (token.value == TOK.dotDotDot)
2999 /* This is:
3000 * at ai ...
3002 if (storageClass & (STC.out_ | STC.ref_))
3003 error("variadic argument cannot be `out` or `ref`");
3004 varargs = VarArg.typesafe;
3005 parameters.push(param);
3006 nextToken();
3007 break;
3009 parameters.push(param);
3010 if (token.value == TOK.comma)
3012 nextToken();
3013 goto L1;
3015 break;
3018 break;
3020 break;
3024 check(TOK.rightParenthesis);
3025 return AST.ParameterList(parameters, varargs, varargsStc);
3028 /*************************************
3030 private AST.EnumDeclaration parseEnum()
3032 AST.EnumDeclaration e;
3033 Identifier id;
3034 AST.Type memtype;
3035 auto loc = token.loc;
3037 // printf("Parser::parseEnum()\n");
3038 nextToken();
3039 id = null;
3040 if (token.value == TOK.identifier)
3042 id = token.ident;
3043 nextToken();
3046 memtype = null;
3047 if (token.value == TOK.colon)
3049 nextToken();
3050 int alt = 0;
3051 const typeLoc = token.loc;
3052 memtype = parseBasicType();
3053 memtype = parseDeclarator(memtype, alt, null);
3054 checkCstyleTypeSyntax(typeLoc, memtype, alt, null);
3057 e = new AST.EnumDeclaration(loc, id, memtype);
3058 // opaque type
3059 if (token.value == TOK.semicolon && id)
3060 nextToken();
3061 else if (token.value == TOK.leftCurly)
3063 bool isAnonymousEnum = !id;
3065 //printf("enum definition\n");
3066 e.members = new AST.Dsymbols();
3067 nextToken();
3068 const(char)[] comment = token.blockComment;
3069 while (token.value != TOK.rightCurly)
3071 /* Can take the following forms...
3072 * 1. ident
3073 * 2. ident = value
3074 * 3. type ident = value
3075 * ... prefixed by valid attributes
3077 loc = token.loc;
3079 AST.Type type = null;
3080 Identifier ident = null;
3082 AST.Expressions* udas;
3083 StorageClass stc;
3084 AST.Expression deprecationMessage;
3085 enum attributeErrorMessage = "`%s` is not a valid attribute for enum members";
3086 Lattrs:
3087 while (1)
3089 switch (token.value)
3091 case TOK.at:
3092 if (StorageClass _stc = parseAttribute(udas))
3094 if (_stc == STC.disable)
3095 stc |= _stc;
3096 else
3098 OutBuffer buf;
3099 AST.stcToBuffer(buf, _stc);
3100 error(attributeErrorMessage, buf.peekChars());
3102 nextToken();
3104 break;
3105 case TOK.deprecated_:
3106 stc |= STC.deprecated_;
3107 if (!parseDeprecatedAttribute(deprecationMessage))
3109 nextToken();
3111 break;
3112 default:
3113 break Lattrs;
3116 if (token.value == TOK.identifier)
3118 const tv = peekNext();
3119 if (tv == TOK.assign || tv == TOK.comma || tv == TOK.rightCurly)
3121 ident = token.ident;
3122 type = null;
3123 nextToken();
3125 else
3127 if (isAnonymousEnum)
3128 goto Ltype;
3130 nextToken();
3131 error("expected `,` or `=` after identifier, not `%s`", token.toChars());
3134 else
3136 if (isAnonymousEnum)
3138 Ltype:
3139 // Type identifier
3140 type = parseType(&ident, null);
3141 if (type == AST.Type.terror)
3143 type = null;
3144 nextToken();
3146 else if (!ident)
3148 error("no identifier for declarator `%s`", type.toChars());
3149 type = null;
3151 else
3153 const tv = token.value;
3154 if (tv != TOK.assign && tv != TOK.comma && tv != TOK.rightCurly)
3156 error("expected `,` or `=` after identifier, not `%s`", token.toChars());
3157 nextToken();
3161 else
3163 Token* t = &token;
3164 if (isBasicType(&t))
3166 error("named enum cannot declare member with type", (*t).toChars());
3167 nextToken();
3169 else
3170 check(TOK.identifier);
3172 // avoid extra error messages
3173 const tv = token.value;
3174 if (tv != TOK.assign && tv != TOK.comma && tv != TOK.rightCurly && tv != TOK.endOfFile)
3175 continue;
3179 AST.Expression value;
3180 if (token.value == TOK.assign)
3182 nextToken();
3183 value = parseAssignExp();
3185 else
3187 value = null;
3188 if (type && isAnonymousEnum)
3189 error("initializer required after `%s` when type is specified", ident.toChars());
3192 AST.DeprecatedDeclaration dd;
3193 if (deprecationMessage)
3195 dd = new AST.DeprecatedDeclaration(deprecationMessage, null);
3196 stc |= STC.deprecated_;
3199 auto em = new AST.EnumMember(loc, ident, value, type, stc, null, dd);
3200 e.members.push(em);
3202 if (udas)
3204 auto uad = new AST.UserAttributeDeclaration(udas, new AST.Dsymbols());
3205 em.userAttribDecl = uad;
3208 if (token.value != TOK.rightCurly)
3210 addComment(em, comment);
3211 comment = null;
3212 check(TOK.comma);
3214 addComment(em, comment);
3215 comment = token.blockComment;
3217 if (token.value == TOK.endOfFile)
3219 error("premature end of file");
3220 break;
3223 nextToken();
3225 else
3227 nextToken();
3228 error("expected `{`, not `%s` for enum declaration", token.toChars());
3230 //printf("-parseEnum() %s\n", e.toChars());
3231 return e;
3234 /********************************
3235 * Parse struct, union, interface, class.
3237 private AST.Dsymbol parseAggregate()
3239 AST.TemplateParameters* tpl = null;
3240 AST.Expression constraint;
3241 const loc = token.loc;
3242 TOK tok = token.value;
3244 //printf("Parser::parseAggregate()\n");
3245 nextToken();
3246 Identifier id;
3247 if (token.value != TOK.identifier)
3249 id = null;
3251 else
3253 id = token.ident;
3254 nextToken();
3256 if (token.value == TOK.leftParenthesis)
3258 // struct/class template declaration.
3259 tpl = parseTemplateParameterList();
3260 constraint = parseConstraint();
3264 // Collect base class(es)
3265 AST.BaseClasses* baseclasses = null;
3266 if (token.value == TOK.colon)
3268 if (tok != TOK.interface_ && tok != TOK.class_)
3269 error("base classes are not allowed for `%s`, did you mean `;`?", Token.toChars(tok));
3270 nextToken();
3271 baseclasses = parseBaseClasses();
3274 if (token.value == TOK.if_)
3276 if (constraint)
3277 error("template constraints appear both before and after BaseClassList, put them before");
3278 constraint = parseConstraint();
3280 if (constraint)
3282 if (!id)
3283 error("template constraints not allowed for anonymous `%s`", Token.toChars(tok));
3284 if (!tpl)
3285 error("template constraints only allowed for templates");
3288 AST.Dsymbols* members = null;
3289 if (token.value == TOK.leftCurly)
3291 //printf("aggregate definition\n");
3292 const lookingForElseSave = lookingForElse;
3293 lookingForElse = Loc();
3294 nextToken();
3295 members = parseDeclDefs(0);
3296 lookingForElse = lookingForElseSave;
3297 if (token.value != TOK.rightCurly)
3299 /* { */
3300 error(token.loc, "`}` expected following members in `%s` declaration",
3301 Token.toChars(tok));
3302 if (id)
3303 eSink.errorSupplemental(loc, "%s `%s` starts here",
3304 Token.toChars(tok), id.toChars());
3305 else
3306 eSink.errorSupplemental(loc, "%s starts here",
3307 Token.toChars(tok));
3309 nextToken();
3311 else if (token.value == TOK.semicolon && id)
3313 if (baseclasses || constraint)
3314 error("members expected");
3315 nextToken();
3317 else
3319 error(token.loc, "{ } expected following `%s` declaration", Token.toChars(tok));
3322 AST.AggregateDeclaration a;
3323 switch (tok)
3325 case TOK.interface_:
3326 if (!id)
3327 error(loc, "anonymous interfaces not allowed");
3328 a = new AST.InterfaceDeclaration(loc, id, baseclasses);
3329 a.members = members;
3330 break;
3332 case TOK.class_:
3333 if (!id)
3334 error(loc, "anonymous classes not allowed");
3335 bool inObject = md && !md.packages && md.id == Id.object;
3336 a = new AST.ClassDeclaration(loc, id, baseclasses, members, inObject);
3337 break;
3339 case TOK.struct_:
3340 if (id)
3342 bool inObject = md && !md.packages && md.id == Id.object;
3343 a = new AST.StructDeclaration(loc, id, inObject);
3344 a.members = members;
3346 else
3348 /* Anonymous structs/unions are more like attributes.
3350 assert(!tpl);
3351 return new AST.AnonDeclaration(loc, false, members);
3353 break;
3355 case TOK.union_:
3356 if (id)
3358 a = new AST.UnionDeclaration(loc, id);
3359 a.members = members;
3361 else
3363 /* Anonymous structs/unions are more like attributes.
3365 assert(!tpl);
3366 return new AST.AnonDeclaration(loc, true, members);
3368 break;
3370 default:
3371 assert(0);
3374 if (tpl)
3376 // Wrap a template around the aggregate declaration
3377 auto decldefs = new AST.Dsymbols();
3378 decldefs.push(a);
3379 auto tempdecl = new AST.TemplateDeclaration(loc, id, tpl, constraint, decldefs);
3380 return tempdecl;
3382 return a;
3385 /*******************************************
3387 private AST.BaseClasses* parseBaseClasses()
3389 auto baseclasses = new AST.BaseClasses();
3391 for (; 1; nextToken())
3393 auto b = new AST.BaseClass(parseBasicType());
3394 baseclasses.push(b);
3395 if (token.value != TOK.comma)
3396 break;
3398 return baseclasses;
3401 AST.Dsymbols* parseImport()
3403 auto decldefs = new AST.Dsymbols();
3404 Identifier aliasid = null;
3406 int isstatic = token.value == TOK.static_;
3407 if (isstatic)
3408 nextToken();
3410 //printf("Parser::parseImport()\n");
3414 nextToken();
3415 if (token.value != TOK.identifier)
3417 error("identifier expected following `import`");
3418 break;
3421 const loc = token.loc;
3422 Identifier id = token.ident;
3423 Identifier[] a;
3424 nextToken();
3425 if (!aliasid && token.value == TOK.assign)
3427 aliasid = id;
3428 goto L1;
3430 while (token.value == TOK.dot)
3432 a ~= id;
3433 nextToken();
3434 if (token.value != TOK.identifier)
3436 error("identifier expected following `package`");
3437 break;
3439 id = token.ident;
3440 nextToken();
3443 auto s = new AST.Import(loc, a, id, aliasid, isstatic);
3444 decldefs.push(s);
3446 /* Look for
3447 * : alias=name, alias=name;
3448 * syntax.
3450 if (token.value == TOK.colon)
3454 nextToken();
3455 if (token.value != TOK.identifier)
3457 error("identifier expected following `:`");
3458 break;
3460 Identifier _alias = token.ident;
3461 Identifier name;
3462 nextToken();
3463 if (token.value == TOK.assign)
3465 nextToken();
3466 if (token.value != TOK.identifier)
3468 error("identifier expected following `%s=`", _alias.toChars());
3469 break;
3471 name = token.ident;
3472 nextToken();
3474 else
3476 name = _alias;
3477 _alias = null;
3479 s.addAlias(name, _alias);
3481 while (token.value == TOK.comma);
3482 break; // no comma-separated imports of this form
3484 aliasid = null;
3486 while (token.value == TOK.comma);
3488 if (token.value == TOK.semicolon)
3489 nextToken();
3490 else
3492 error("`;` expected");
3493 nextToken();
3496 return decldefs;
3499 /* Parse a type and optional identifier
3500 * Params:
3501 * pident = set to Identifier if there is one, null if not
3502 * ptpl = if !null, then set to TemplateParameterList
3503 * pdeclLoc = if !null, then set to location of the declarator
3505 AST.Type parseType(Identifier* pident = null, AST.TemplateParameters** ptpl = null, Loc* pdeclLoc = null)
3507 /* Take care of the storage class prefixes that
3508 * serve as type attributes:
3509 * const type
3510 * immutable type
3511 * shared type
3512 * inout type
3513 * inout const type
3514 * shared const type
3515 * shared inout type
3516 * shared inout const type
3518 StorageClass stc = 0;
3519 while (1)
3521 switch (token.value)
3523 case TOK.const_:
3524 if (peekNext() == TOK.leftParenthesis)
3525 break; // const as type constructor
3526 stc |= STC.const_; // const as storage class
3527 nextToken();
3528 continue;
3530 case TOK.immutable_:
3531 if (peekNext() == TOK.leftParenthesis)
3532 break;
3533 stc |= STC.immutable_;
3534 nextToken();
3535 continue;
3537 case TOK.shared_:
3538 if (peekNext() == TOK.leftParenthesis)
3539 break;
3540 stc |= STC.shared_;
3541 nextToken();
3542 continue;
3544 case TOK.inout_:
3545 if (peekNext() == TOK.leftParenthesis)
3546 break;
3547 stc |= STC.wild;
3548 nextToken();
3549 continue;
3551 default:
3552 break;
3554 break;
3557 const typeLoc = token.loc;
3559 AST.Type t;
3560 t = parseBasicType();
3562 if (pdeclLoc)
3563 *pdeclLoc = token.loc;
3564 int alt = 0;
3565 t = parseDeclarator(t, alt, pident, ptpl);
3566 checkCstyleTypeSyntax(typeLoc, t, alt, pident ? *pident : null);
3568 t = t.addSTC(stc);
3569 return t;
3572 private AST.Type parseBasicType(bool dontLookDotIdents = false)
3574 AST.Type t;
3575 Loc loc;
3576 Identifier id;
3577 //printf("parseBasicType()\n");
3578 switch (token.value)
3580 case TOK.void_:
3581 t = AST.Type.tvoid;
3582 goto LabelX;
3584 case TOK.int8:
3585 t = AST.Type.tint8;
3586 goto LabelX;
3588 case TOK.uns8:
3589 t = AST.Type.tuns8;
3590 goto LabelX;
3592 case TOK.int16:
3593 t = AST.Type.tint16;
3594 goto LabelX;
3596 case TOK.uns16:
3597 t = AST.Type.tuns16;
3598 goto LabelX;
3600 case TOK.int32:
3601 t = AST.Type.tint32;
3602 goto LabelX;
3604 case TOK.uns32:
3605 t = AST.Type.tuns32;
3606 goto LabelX;
3608 case TOK.int64:
3609 t = AST.Type.tint64;
3610 nextToken();
3611 if (token.value == TOK.int64) // if `long long`
3613 error("use `long` for a 64 bit integer instead of `long long`");
3614 nextToken();
3616 else if (token.value == TOK.float64) // if `long double`
3618 error("use `real` instead of `long double`");
3619 t = AST.Type.tfloat80;
3620 nextToken();
3622 break;
3624 case TOK.uns64:
3625 t = AST.Type.tuns64;
3626 goto LabelX;
3628 case TOK.int128:
3629 t = AST.Type.tint128;
3630 goto LabelX;
3632 case TOK.uns128:
3633 t = AST.Type.tuns128;
3634 goto LabelX;
3636 case TOK.float32:
3637 t = AST.Type.tfloat32;
3638 goto LabelX;
3640 case TOK.float64:
3641 t = AST.Type.tfloat64;
3642 goto LabelX;
3644 case TOK.float80:
3645 t = AST.Type.tfloat80;
3646 goto LabelX;
3648 case TOK.imaginary32:
3649 t = AST.Type.timaginary32;
3650 goto LabelX;
3652 case TOK.imaginary64:
3653 t = AST.Type.timaginary64;
3654 goto LabelX;
3656 case TOK.imaginary80:
3657 t = AST.Type.timaginary80;
3658 goto LabelX;
3660 case TOK.complex32:
3661 t = AST.Type.tcomplex32;
3662 goto LabelX;
3664 case TOK.complex64:
3665 t = AST.Type.tcomplex64;
3666 goto LabelX;
3668 case TOK.complex80:
3669 t = AST.Type.tcomplex80;
3670 goto LabelX;
3672 case TOK.bool_:
3673 t = AST.Type.tbool;
3674 goto LabelX;
3676 case TOK.char_:
3677 t = AST.Type.tchar;
3678 goto LabelX;
3680 case TOK.wchar_:
3681 t = AST.Type.twchar;
3682 goto LabelX;
3684 case TOK.dchar_:
3685 t = AST.Type.tdchar;
3686 goto LabelX;
3687 LabelX:
3688 nextToken();
3689 break;
3691 case TOK.this_:
3692 case TOK.super_:
3693 case TOK.identifier:
3694 loc = token.loc;
3695 id = token.ident;
3696 nextToken();
3697 if (token.value == TOK.not)
3699 // ident!(template_arguments)
3700 auto tempinst = new AST.TemplateInstance(loc, id, parseTemplateArguments());
3701 t = parseBasicTypeStartingAt(new AST.TypeInstance(loc, tempinst), dontLookDotIdents);
3703 else
3705 t = parseBasicTypeStartingAt(new AST.TypeIdentifier(loc, id), dontLookDotIdents);
3707 break;
3709 case TOK.mixin_:
3710 // https://dlang.org/spec/expression.html#mixin_types
3711 loc = token.loc;
3712 nextToken();
3713 if (token.value != TOK.leftParenthesis)
3714 error(token.loc, "found `%s` when expecting `%s` following `mixin`", token.toChars(), Token.toChars(TOK.leftParenthesis));
3715 auto exps = parseArguments();
3716 t = new AST.TypeMixin(loc, exps);
3717 break;
3719 case TOK.dot:
3720 // Leading . as in .foo
3721 t = parseBasicTypeStartingAt(new AST.TypeIdentifier(token.loc, Id.empty), dontLookDotIdents);
3722 break;
3724 case TOK.typeof_:
3725 // typeof(expression)
3726 t = parseBasicTypeStartingAt(parseTypeof(), dontLookDotIdents);
3727 break;
3729 case TOK.vector:
3730 t = parseVector();
3731 break;
3733 case TOK.traits:
3734 if (AST.TraitsExp te = cast(AST.TraitsExp) parsePrimaryExp())
3735 if (te.ident)
3737 t = new AST.TypeTraits(token.loc, te);
3738 break;
3740 t = new AST.TypeError;
3741 break;
3743 case TOK.const_:
3744 // const(type)
3745 nextToken();
3746 check(TOK.leftParenthesis);
3747 t = parseType().addSTC(STC.const_);
3748 check(TOK.rightParenthesis);
3749 break;
3751 case TOK.immutable_:
3752 // immutable(type)
3753 nextToken();
3754 check(TOK.leftParenthesis);
3755 t = parseType().addSTC(STC.immutable_);
3756 check(TOK.rightParenthesis);
3757 break;
3759 case TOK.shared_:
3760 // shared(type)
3761 nextToken();
3762 check(TOK.leftParenthesis);
3763 t = parseType().addSTC(STC.shared_);
3764 check(TOK.rightParenthesis);
3765 break;
3767 case TOK.inout_:
3768 // wild(type)
3769 nextToken();
3770 check(TOK.leftParenthesis);
3771 t = parseType().addSTC(STC.wild);
3772 check(TOK.rightParenthesis);
3773 break;
3775 default:
3776 error("basic type expected, not `%s`", token.toChars());
3777 if (token.value == TOK.else_)
3778 eSink.errorSupplemental(token.loc, "There's no `static else`, use `else` instead.");
3779 t = AST.Type.terror;
3780 break;
3782 return t;
3785 private AST.Type parseBasicTypeStartingAt(AST.TypeQualified tid, bool dontLookDotIdents)
3787 AST.Type maybeArray = null;
3788 // See https://issues.dlang.org/show_bug.cgi?id=1215
3789 // A basic type can look like MyType (typical case), but also:
3790 // MyType.T -> A type
3791 // MyType[expr] -> Either a static array of MyType or a type (iif MyType is a Ttuple)
3792 // MyType[expr].T -> A type.
3793 // MyType[expr].T[expr] -> Either a static array of MyType[expr].T or a type
3794 // (iif MyType[expr].T is a Ttuple)
3795 while (1)
3797 switch (token.value)
3799 case TOK.dot:
3801 nextToken();
3802 if (token.value != TOK.identifier)
3804 error("identifier expected following `.` instead of `%s`", token.toChars());
3805 break;
3807 if (maybeArray)
3809 // This is actually a TypeTuple index, not an {a/s}array.
3810 // We need to have a while loop to unwind all index taking:
3811 // T[e1][e2].U -> T, addIndex(e1), addIndex(e2)
3812 AST.Objects dimStack;
3813 AST.Type t = maybeArray;
3814 while (true)
3816 if (t.ty == Tsarray)
3818 // The index expression is an Expression.
3819 AST.TypeSArray a = cast(AST.TypeSArray)t;
3820 dimStack.push(a.dim.syntaxCopy());
3821 t = a.next.syntaxCopy();
3823 else if (t.ty == Taarray)
3825 // The index expression is a Type. It will be interpreted as an expression at semantic time.
3826 AST.TypeAArray a = cast(AST.TypeAArray)t;
3827 dimStack.push(a.index.syntaxCopy());
3828 t = a.next.syntaxCopy();
3830 else
3832 break;
3835 assert(dimStack.length > 0);
3836 // We're good. Replay indices in the reverse order.
3837 tid = cast(AST.TypeQualified)t;
3838 while (dimStack.length)
3840 tid.addIndex(dimStack.pop());
3842 maybeArray = null;
3844 const loc = token.loc;
3845 Identifier id = token.ident;
3846 nextToken();
3847 if (token.value == TOK.not)
3849 auto tempinst = new AST.TemplateInstance(loc, id, parseTemplateArguments());
3850 tid.addInst(tempinst);
3852 else
3853 tid.addIdent(id);
3854 continue;
3856 case TOK.leftBracket:
3858 if (dontLookDotIdents) // workaround for https://issues.dlang.org/show_bug.cgi?id=14911
3859 goto Lend;
3861 nextToken();
3862 AST.Type t = maybeArray ? maybeArray : cast(AST.Type)tid;
3863 if (token.value == TOK.rightBracket)
3865 // It's a dynamic array, and we're done:
3866 // T[].U does not make sense.
3867 t = new AST.TypeDArray(t);
3868 nextToken();
3869 return t;
3871 else if (isDeclaration(&token, NeedDeclaratorId.no, TOK.rightBracket, null))
3873 // This can be one of two things:
3874 // 1 - an associative array declaration, T[type]
3875 // 2 - an associative array declaration, T[expr]
3876 // These can only be disambiguated later.
3877 AST.Type index = parseType(); // [ type ]
3878 maybeArray = new AST.TypeAArray(t, index);
3879 check(TOK.rightBracket);
3881 else
3883 // This can be one of three things:
3884 // 1 - an static array declaration, T[expr]
3885 // 2 - a slice, T[expr .. expr]
3886 // 3 - a template parameter pack index expression, T[expr].U
3887 // 1 and 3 can only be disambiguated later.
3888 //printf("it's type[expression]\n");
3889 inBrackets++;
3890 AST.Expression e = parseAssignExp(); // [ expression ]
3891 if (token.value == TOK.slice)
3893 // It's a slice, and we're done.
3894 nextToken();
3895 AST.Expression e2 = parseAssignExp(); // [ exp .. exp ]
3896 t = new AST.TypeSlice(t, e, e2);
3897 inBrackets--;
3898 check(TOK.rightBracket);
3899 return t;
3901 else
3903 maybeArray = new AST.TypeSArray(t, e);
3904 inBrackets--;
3905 check(TOK.rightBracket);
3906 continue;
3909 break;
3911 default:
3912 goto Lend;
3915 Lend:
3916 return maybeArray ? maybeArray : cast(AST.Type)tid;
3919 /******************************************
3920 * Parse suffixes to type t.
3922 * []
3923 * [AssignExpression]
3924 * [AssignExpression .. AssignExpression]
3925 * [Type]
3926 * delegate Parameters MemberFunctionAttributes(opt)
3927 * function Parameters FunctionAttributes(opt)
3928 * Params:
3929 * t = the already parsed type
3930 * Returns:
3931 * t with the suffixes added
3932 * See_Also:
3933 * https://dlang.org/spec/declaration.html#TypeSuffixes
3935 private AST.Type parseTypeSuffixes(AST.Type t)
3937 //printf("parseTypeSuffixes()\n");
3938 while (1)
3940 switch (token.value)
3942 case TOK.mul:
3943 t = new AST.TypePointer(t);
3944 nextToken();
3945 continue;
3947 case TOK.leftBracket:
3948 // Handle []. Make sure things like
3949 // int[3][1] a;
3950 // is (array[1] of array[3] of int)
3951 nextToken();
3952 if (token.value == TOK.rightBracket)
3954 t = new AST.TypeDArray(t); // []
3955 nextToken();
3957 else if (isDeclaration(&token, NeedDeclaratorId.no, TOK.rightBracket, null))
3959 // It's an associative array declaration
3960 //printf("it's an associative array\n");
3961 AST.Type index = parseType(); // [ type ]
3962 t = new AST.TypeAArray(t, index);
3963 check(TOK.rightBracket);
3965 else
3967 //printf("it's type[expression]\n");
3968 inBrackets++;
3969 AST.Expression e = parseAssignExp(); // [ expression ]
3970 if (!e)
3972 inBrackets--;
3973 check(TOK.rightBracket);
3974 continue;
3976 if (token.value == TOK.slice)
3978 nextToken();
3979 AST.Expression e2 = parseAssignExp(); // [ exp .. exp ]
3980 t = new AST.TypeSlice(t, e, e2);
3982 else
3984 t = new AST.TypeSArray(t, e);
3986 inBrackets--;
3987 check(TOK.rightBracket);
3989 continue;
3991 case TOK.delegate_:
3992 case TOK.function_:
3994 // Handle delegate declaration:
3995 // t delegate(parameter list) nothrow pure
3996 // t function(parameter list) nothrow pure
3997 const save = token.value;
3998 nextToken();
4000 auto parameterList = parseParameterList(null);
4002 StorageClass stc = parsePostfix(STC.undefined_, null);
4003 auto tf = new AST.TypeFunction(parameterList, t, linkage, stc);
4004 if (stc & (STC.const_ | STC.immutable_ | STC.shared_ | STC.wild | STC.return_))
4006 if (save == TOK.function_)
4007 error("`const`/`immutable`/`shared`/`inout`/`return` attributes are only valid for non-static member functions");
4008 else
4009 tf = cast(AST.TypeFunction)tf.addSTC(stc);
4011 t = save == TOK.delegate_ ? new AST.TypeDelegate(tf) : new AST.TypePointer(tf); // pointer to function
4012 continue;
4014 default:
4015 return t;
4017 assert(0);
4019 assert(0);
4022 /**********************
4023 * Parse Declarator
4024 * Params:
4025 * t = base type to start with
4026 * palt = OR in 1 for C-style function pointer declaration syntax,
4027 * 2 for C-style array declaration syntax, otherwise don't modify
4028 * pident = set to Identifier if there is one, null if not
4029 * tpl = if !null, then set to TemplateParameterList
4030 * storageClass = any storage classes seen so far
4031 * pdisable = set to true if @disable seen
4032 * pudas = any user defined attributes seen so far. Merged with any more found
4033 * Returns:
4034 * type declared
4035 * Reference: https://dlang.org/spec/declaration.html#Declarator
4037 private AST.Type parseDeclarator(AST.Type t, ref int palt, Identifier* pident,
4038 AST.TemplateParameters** tpl = null, StorageClass storageClass = 0,
4039 bool* pdisable = null, AST.Expressions** pudas = null)
4041 //printf("parseDeclarator(tpl = %p)\n", tpl);
4042 t = parseTypeSuffixes(t);
4043 AST.Type ts;
4044 switch (token.value)
4046 case TOK.identifier:
4047 if (pident)
4048 *pident = token.ident;
4049 else
4050 error("unexpected identifier `%s` in declarator", token.ident.toChars());
4051 ts = t;
4052 nextToken();
4053 break;
4055 case TOK.leftParenthesis:
4057 // like: T (*fp)();
4058 // like: T ((*fp))();
4059 if (peekNext() == TOK.mul || peekNext() == TOK.leftParenthesis)
4061 /* Parse things with parentheses around the identifier, like:
4062 * int (*ident[3])[]
4063 * although the D style would be:
4064 * int[]*[3] ident
4066 palt |= 1;
4067 nextToken();
4068 ts = parseDeclarator(t, palt, pident);
4069 check(TOK.rightParenthesis);
4070 break;
4072 ts = t;
4074 Token* peekt = &token;
4075 /* Completely disallow C-style things like:
4076 * T (a);
4077 * Improve error messages for the common bug of a missing return type
4078 * by looking to see if (a) looks like a parameter list.
4080 if (isParameters(&peekt))
4082 error("function declaration without return type. (Note that constructors are always named `this`)");
4084 else
4085 error("unexpected `(` in declarator");
4086 break;
4088 default:
4089 ts = t;
4090 break;
4093 // parse DeclaratorSuffixes
4094 while (1)
4096 switch (token.value)
4098 static if (CARRAYDECL)
4100 /* Support C style array syntax:
4101 * int ident[]
4102 * as opposed to D-style:
4103 * int[] ident
4105 case TOK.leftBracket:
4107 // This is the old C-style post [] syntax.
4108 AST.TypeNext ta;
4109 nextToken();
4110 if (token.value == TOK.rightBracket)
4112 // It's a dynamic array
4113 ta = new AST.TypeDArray(t); // []
4114 nextToken();
4115 palt |= 2;
4117 else if (isDeclaration(&token, NeedDeclaratorId.no, TOK.rightBracket, null))
4119 // It's an associative array
4120 //printf("it's an associative array\n");
4121 AST.Type index = parseType(); // [ type ]
4122 check(TOK.rightBracket);
4123 ta = new AST.TypeAArray(t, index);
4124 palt |= 2;
4126 else
4128 //printf("It's a static array\n");
4129 AST.Expression e = parseAssignExp(); // [ expression ]
4130 ta = new AST.TypeSArray(t, e);
4131 check(TOK.rightBracket);
4132 palt |= 2;
4135 /* Insert ta into
4136 * ts -> ... -> t
4137 * so that
4138 * ts -> ... -> ta -> t
4140 AST.Type* pt;
4141 for (pt = &ts; *pt != t; pt = &(cast(AST.TypeNext)*pt).next)
4144 *pt = ta;
4145 continue;
4148 case TOK.leftParenthesis:
4150 if (tpl)
4152 Token* tk = peekPastParen(&token);
4153 if (tk.value == TOK.leftParenthesis)
4155 /* Look ahead to see if this is (...)(...),
4156 * i.e. a function template declaration
4158 //printf("function template declaration\n");
4160 // Gather template parameter list
4161 *tpl = parseTemplateParameterList();
4163 else if (tk.value == TOK.assign)
4165 /* or (...) =,
4166 * i.e. a variable template declaration
4168 //printf("variable template declaration\n");
4169 *tpl = parseTemplateParameterList();
4170 break;
4174 auto parameterList = parseParameterList(null);
4176 /* Parse const/immutable/shared/inout/nothrow/pure/return postfix
4178 // merge prefix storage classes
4179 StorageClass stc = parsePostfix(storageClass, pudas);
4181 AST.Type tf = new AST.TypeFunction(parameterList, t, linkage, stc);
4182 tf = tf.addSTC(stc);
4183 if (pdisable)
4184 *pdisable = stc & STC.disable ? true : false;
4186 /* Insert tf into
4187 * ts -> ... -> t
4188 * so that
4189 * ts -> ... -> tf -> t
4191 AST.Type* pt;
4192 for (pt = &ts; *pt != t; pt = &(cast(AST.TypeNext)*pt).next)
4195 *pt = tf;
4196 break;
4198 default:
4199 break;
4201 break;
4203 return ts;
4206 private void parseStorageClasses(ref StorageClass storage_class, ref LINK link,
4207 ref bool setAlignment, ref AST.Expression ealign, ref AST.Expressions* udas,
4208 out Loc linkloc)
4210 StorageClass stc;
4211 bool sawLinkage = false; // seen a linkage declaration
4213 linkloc = Loc.initial;
4215 while (1)
4217 switch (token.value)
4219 case TOK.const_:
4220 if (peekNext() == TOK.leftParenthesis)
4221 break; // const as type constructor
4222 stc = STC.const_; // const as storage class
4223 goto L1;
4225 case TOK.immutable_:
4226 if (peekNext() == TOK.leftParenthesis)
4227 break;
4228 stc = STC.immutable_;
4229 goto L1;
4231 case TOK.shared_:
4232 if (peekNext() == TOK.leftParenthesis)
4233 break;
4234 stc = STC.shared_;
4235 goto L1;
4237 case TOK.inout_:
4238 if (peekNext() == TOK.leftParenthesis)
4239 break;
4240 stc = STC.wild;
4241 goto L1;
4243 case TOK.static_:
4244 stc = STC.static_;
4245 goto L1;
4247 case TOK.final_:
4248 stc = STC.final_;
4249 goto L1;
4251 case TOK.auto_:
4252 stc = STC.auto_;
4253 goto L1;
4255 case TOK.scope_:
4256 stc = STC.scope_;
4257 goto L1;
4259 case TOK.override_:
4260 stc = STC.override_;
4261 goto L1;
4263 case TOK.abstract_:
4264 stc = STC.abstract_;
4265 goto L1;
4267 case TOK.synchronized_:
4268 stc = STC.synchronized_;
4269 goto L1;
4271 case TOK.deprecated_:
4272 stc = STC.deprecated_;
4273 goto L1;
4275 case TOK.nothrow_:
4276 stc = STC.nothrow_;
4277 goto L1;
4279 case TOK.pure_:
4280 stc = STC.pure_;
4281 goto L1;
4283 case TOK.ref_:
4284 stc = STC.ref_;
4285 goto L1;
4287 case TOK.gshared:
4288 stc = STC.gshared;
4289 goto L1;
4291 case TOK.enum_:
4293 const tv = peekNext();
4294 if (tv == TOK.leftCurly || tv == TOK.colon)
4295 break;
4296 if (tv == TOK.identifier)
4298 const nextv = peekNext2();
4299 if (nextv == TOK.leftCurly || nextv == TOK.colon || nextv == TOK.semicolon)
4300 break;
4302 stc = STC.manifest;
4303 goto L1;
4306 case TOK.at:
4308 stc = parseAttribute(udas);
4309 if (stc)
4310 goto L1;
4311 continue;
4314 storage_class = appendStorageClass(storage_class, stc);
4315 nextToken();
4316 continue;
4318 case TOK.extern_:
4320 if (peekNext() != TOK.leftParenthesis)
4322 stc = STC.extern_;
4323 goto L1;
4326 if (sawLinkage)
4327 error("redundant linkage declaration");
4328 sawLinkage = true;
4329 linkloc = token.loc;
4330 auto res = parseLinkage();
4331 link = res.link;
4332 if (res.idents || res.identExps)
4334 error("C++ name spaces not allowed here");
4336 if (res.cppmangle != CPPMANGLE.def)
4338 error("C++ mangle declaration not allowed here");
4340 continue;
4342 case TOK.align_:
4344 nextToken();
4345 setAlignment = true;
4346 if (token.value == TOK.leftParenthesis)
4348 nextToken();
4349 ealign = parseExpression();
4350 check(TOK.rightParenthesis);
4352 continue;
4354 default:
4355 break;
4357 break;
4361 /**********************************
4362 * Parse Declarations.
4363 * These can be:
4364 * 1. declarations at global/class level
4365 * 2. declarations at statement level
4366 * Returns:
4367 * array of Declarations.
4369 private AST.Dsymbols* parseDeclarations(bool autodecl, PrefixAttributes!AST* pAttrs, const(char)* comment)
4371 StorageClass storage_class = STC.undefined_;
4372 LINK link = linkage;
4373 Loc linkloc = this.linkLoc;
4374 bool setAlignment = false;
4375 AST.Expression ealign;
4376 AST.Expressions* udas = null;
4378 //printf("parseDeclarations() %s\n", token.toChars());
4379 if (!comment)
4380 comment = token.blockComment.ptr;
4382 /* Look for AliasReassignment
4384 if (token.value == TOK.identifier && peekNext() == TOK.assign)
4385 return parseAliasReassignment(comment);
4387 /* Declarations that start with `alias`
4389 bool isAliasDeclaration = false;
4390 auto aliasLoc = token.loc;
4391 if (token.value == TOK.alias_)
4393 if (auto a = parseAliasDeclarations(comment))
4394 return a;
4395 /* Handle these later:
4396 * alias StorageClasses type ident;
4398 isAliasDeclaration = true;
4401 AST.Type ts;
4403 if (!autodecl)
4405 parseStorageClasses(storage_class, link, setAlignment, ealign, udas, linkloc);
4407 if (token.value == TOK.enum_)
4409 AST.Dsymbol d = parseEnum();
4410 auto a = new AST.Dsymbols();
4411 a.push(d);
4413 if (udas)
4415 d = new AST.UserAttributeDeclaration(udas, a);
4416 a = new AST.Dsymbols();
4417 a.push(d);
4420 addComment(d, comment);
4421 return a;
4423 if (token.value == TOK.struct_ ||
4424 token.value == TOK.union_ ||
4425 token.value == TOK.class_ ||
4426 token.value == TOK.interface_)
4428 AST.Dsymbol s = parseAggregate();
4429 auto a = new AST.Dsymbols();
4430 a.push(s);
4432 if (storage_class)
4434 s = new AST.StorageClassDeclaration(storage_class, a);
4435 a = new AST.Dsymbols();
4436 a.push(s);
4438 if (setAlignment)
4440 s = new AST.AlignDeclaration(s.loc, ealign, a);
4441 a = new AST.Dsymbols();
4442 a.push(s);
4444 if (link != linkage)
4446 s = new AST.LinkDeclaration(linkloc, link, a);
4447 a = new AST.Dsymbols();
4448 a.push(s);
4450 if (udas)
4452 s = new AST.UserAttributeDeclaration(udas, a);
4453 a = new AST.Dsymbols();
4454 a.push(s);
4457 addComment(s, comment);
4458 return a;
4461 /* Look for auto initializers:
4462 * storage_class identifier = initializer;
4463 * storage_class identifier(...) = initializer;
4465 if ((storage_class || udas) && token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign))
4467 AST.Dsymbols* a = parseAutoDeclarations(storage_class, comment);
4468 if (udas)
4470 AST.Dsymbol s = new AST.UserAttributeDeclaration(udas, a);
4471 a = new AST.Dsymbols();
4472 a.push(s);
4474 return a;
4477 /* Look for return type inference for template functions.
4480 Token* tk;
4481 if ((storage_class || udas) && token.value == TOK.identifier && skipParens(peek(&token), &tk) &&
4482 skipAttributes(tk, &tk) &&
4483 (tk.value == TOK.leftParenthesis || tk.value == TOK.leftCurly || tk.value == TOK.in_ || tk.value == TOK.out_ || tk.value == TOK.goesTo ||
4484 tk.value == TOK.do_ || tk.value == TOK.identifier && tk.ident == Id._body))
4486 if (tk.value == TOK.identifier && tk.ident == Id._body)
4487 usageOfBodyKeyword();
4489 ts = null;
4491 else
4493 ts = parseBasicType();
4494 ts = parseTypeSuffixes(ts);
4499 if (pAttrs)
4501 storage_class |= pAttrs.storageClass;
4502 //pAttrs.storageClass = STC.undefined_;
4505 AST.Type tfirst = null;
4506 auto a = new AST.Dsymbols();
4508 while (1)
4510 AST.TemplateParameters* tpl = null;
4511 bool disable;
4512 int alt = 0;
4514 const loc = token.loc;
4515 Identifier ident;
4516 auto t = parseDeclarator(ts, alt, &ident, &tpl, storage_class, &disable, &udas);
4517 assert(t);
4518 if (!tfirst)
4519 tfirst = t;
4520 else if (t != tfirst)
4521 error(token.loc, "multiple declarations must have the same type, not `%s` and `%s`", tfirst.toChars(), t.toChars());
4523 if (token.value == TOK.colon && !ident && t.ty != Tfunction)
4525 // Unnamed bit field
4526 ident = Identifier.generateAnonymousId("BitField");
4529 bool isThis = (t.ty == Tident && (cast(AST.TypeIdentifier)t).ident == Id.This && token.value == TOK.assign);
4530 if (ident)
4531 checkCstyleTypeSyntax(loc, t, alt, ident);
4532 else if (!isThis && (t != AST.Type.terror))
4533 noIdentifierForDeclarator(t);
4535 if (isAliasDeclaration)
4537 AST.Declaration v;
4538 AST.Initializer _init = null;
4540 /* Aliases can no longer have multiple declarators, storage classes,
4541 * linkages, or auto declarations.
4542 * These never made any sense, anyway.
4543 * The code below needs to be fixed to reject them.
4544 * The grammar has already been fixed to preclude them.
4547 if (udas)
4548 error("user-defined attributes not allowed for `alias` declarations");
4550 if (token.value == TOK.assign)
4552 nextToken();
4553 _init = parseInitializer();
4555 if (_init)
4557 error("alias cannot have initializer");
4559 v = new AST.AliasDeclaration(aliasLoc, ident, t);
4561 v.storage_class = storage_class;
4562 if (pAttrs)
4564 /* AliasDeclaration distinguish @safe, @system, @trusted attributes
4565 * on prefix and postfix.
4566 * @safe alias void function() FP1;
4567 * alias @safe void function() FP2; // FP2 is not @safe
4568 * alias void function() @safe FP3;
4570 pAttrs.storageClass &= STC.safeGroup;
4572 AST.Dsymbol s = v;
4574 if (link != linkage)
4576 auto ax = new AST.Dsymbols();
4577 ax.push(v);
4578 s = new AST.LinkDeclaration(linkloc, link, ax);
4580 a.push(s);
4581 switch (token.value)
4583 case TOK.semicolon:
4584 nextToken();
4585 addComment(s, comment);
4586 break;
4588 case TOK.comma:
4589 nextToken();
4590 addComment(s, comment);
4591 continue;
4593 default:
4594 error("semicolon expected to close `alias` declaration, not `%s`", token.toChars());
4595 break;
4598 else if (t.ty == Tfunction)
4600 /* @@@DEPRECATED_2.115@@@
4601 * change to error, deprecated in 2.105.1 */
4602 if (storage_class & STC.manifest)
4603 deprecation("function cannot have enum storage class");
4605 AST.Expression constraint = null;
4606 //printf("%s funcdecl t = %s, storage_class = x%lx\n", loc.toChars(), t.toChars(), storage_class);
4607 auto f = new AST.FuncDeclaration(loc, Loc.initial, ident, storage_class | (disable ? STC.disable : 0), t);
4608 if (pAttrs)
4609 pAttrs.storageClass = STC.undefined_;
4610 if (tpl)
4611 constraint = parseConstraint();
4612 AST.Dsymbol s = parseContracts(f, !!tpl);
4613 auto tplIdent = s.ident;
4615 if (link != linkage)
4617 auto ax = new AST.Dsymbols();
4618 ax.push(s);
4619 s = new AST.LinkDeclaration(linkloc, link, ax);
4621 if (udas)
4623 auto ax = new AST.Dsymbols();
4624 ax.push(s);
4625 s = new AST.UserAttributeDeclaration(udas, ax);
4628 /* A template parameter list means it's a function template
4630 if (tpl)
4632 // @@@DEPRECATED_2.114@@@
4633 // Both deprecated in 2.104, change to error
4634 if (storage_class & STC.override_)
4635 deprecation(loc, "a function template is not virtual so cannot be marked `override`");
4636 else if (storage_class & STC.abstract_)
4637 deprecation(loc, "a function template is not virtual so cannot be marked `abstract`");
4639 // Wrap a template around the function declaration
4640 auto decldefs = new AST.Dsymbols();
4641 decldefs.push(s);
4642 auto tempdecl = new AST.TemplateDeclaration(loc, tplIdent, tpl, constraint, decldefs);
4643 s = tempdecl;
4645 StorageClass stc2 = STC.undefined_;
4646 if (storage_class & STC.static_)
4648 assert(f.storage_class & STC.static_);
4649 f.storage_class &= ~STC.static_;
4650 stc2 |= STC.static_;
4652 if (storage_class & STC.deprecated_)
4654 assert(f.storage_class & STC.deprecated_);
4655 f.storage_class &= ~STC.deprecated_;
4656 stc2 |= STC.deprecated_;
4658 if (stc2 != STC.undefined_)
4660 auto ax = new AST.Dsymbols();
4661 ax.push(s);
4662 s = new AST.StorageClassDeclaration(stc2, ax);
4665 a.push(s);
4666 addComment(s, comment);
4668 else if (ident)
4670 AST.Expression width;
4671 if (token.value == TOK.colon)
4673 nextToken();
4674 width = parseCondExp();
4677 AST.Initializer _init = null;
4678 if (token.value == TOK.assign)
4680 nextToken();
4681 _init = parseInitializer();
4684 AST.Dsymbol s;
4685 if (width)
4687 if (_init)
4688 error("initializer not allowed for bit-field declaration");
4689 if (storage_class)
4690 error("storage class not allowed for bit-field declaration");
4691 s = new AST.BitFieldDeclaration(width.loc, t, ident, width);
4693 else
4695 auto v = new AST.VarDeclaration(loc, t, ident, _init);
4696 v.storage_class = storage_class;
4697 if (pAttrs)
4698 pAttrs.storageClass = STC.undefined_;
4699 s = v;
4702 if (tpl && _init)
4704 auto a2 = new AST.Dsymbols();
4705 a2.push(s);
4706 auto tempdecl = new AST.TemplateDeclaration(loc, ident, tpl, null, a2, 0);
4707 s = tempdecl;
4709 if (setAlignment)
4711 auto ax = new AST.Dsymbols();
4712 ax.push(s);
4713 s = new AST.AlignDeclaration(s.loc, ealign, ax);
4715 if (link != linkage)
4717 auto ax = new AST.Dsymbols();
4718 ax.push(s);
4719 s = new AST.LinkDeclaration(linkloc, link, ax);
4721 if (udas)
4723 auto ax = new AST.Dsymbols();
4724 ax.push(s);
4725 s = new AST.UserAttributeDeclaration(udas, ax);
4727 a.push(s);
4728 switch (token.value)
4730 case TOK.semicolon:
4731 nextToken();
4732 addComment(s, comment);
4733 break;
4735 case TOK.comma:
4736 nextToken();
4737 addComment(s, comment);
4738 continue;
4740 default:
4741 if (loc.linnum != token.loc.linnum)
4743 error(token.loc, "semicolon needed to end declaration of `%s`, instead of `%s`", s.toChars(), token.toChars());
4744 eSink.errorSupplemental(loc, "`%s` declared here", s.toChars());
4746 else
4748 error(token.loc, "semicolon needed to end declaration of `%s` instead of `%s`", s.toChars(), token.toChars());
4750 break;
4753 break;
4755 return a;
4758 /// Report an error that a declaration of type `t` is missing an identifier
4759 /// The parser is expected to sit on the next token after the type.
4760 private void noIdentifierForDeclarator(AST.Type t)
4762 error("no identifier for declarator `%s`", t.toChars());
4763 // A common mistake is to use a reserved keyword as an identifier, e.g. `in` or `out`
4764 if (token.isKeyword)
4766 eSink.errorSupplemental(token.loc, "`%s` is a keyword, perhaps append `_` to make it an identifier", token.toChars());
4767 nextToken();
4771 /********************************
4772 * Parse AliasReassignment:
4773 * identifier = type;
4774 * Parser is sitting on the identifier.
4775 * https://dlang.org/spec/declaration.html#alias-reassignment
4776 * Params:
4777 * comment = if not null, comment to attach to symbol
4778 * Returns:
4779 * array of symbols
4781 private AST.Dsymbols* parseAliasReassignment(const(char)* comment)
4783 const loc = token.loc;
4784 auto ident = token.ident;
4785 nextToken();
4786 nextToken(); // advance past =
4787 auto t = parseType();
4788 AST.Dsymbol s = new AST.AliasAssign(loc, ident, t, null);
4789 check(TOK.semicolon, "alias reassignment");
4790 addComment(s, comment);
4791 auto a = new AST.Dsymbols();
4792 a.push(s);
4793 return a;
4796 /********************************
4797 * Parse declarations that start with `alias`
4798 * Parser is sitting on the `alias`.
4799 * https://dlang.org/spec/declaration.html#alias
4800 * Params:
4801 * comment = if not null, comment to attach to symbol
4802 * Returns:
4803 * array of symbols
4805 private AST.Dsymbols* parseAliasDeclarations(const(char)* comment)
4807 const loc = token.loc;
4808 nextToken();
4809 Loc linkloc = this.linkLoc;
4810 AST.Expressions* udas;
4811 LINK link = linkage;
4812 StorageClass storage_class = STC.undefined_;
4813 AST.Expression ealign;
4814 bool setAlignment = false;
4816 /* Look for:
4817 * alias Identifier this;
4818 * https://dlang.org/spec/class.html#alias-this
4820 if (token.value == TOK.identifier && peekNext() == TOK.this_)
4822 auto s = new AST.AliasThis(loc, token.ident);
4823 nextToken();
4824 check(TOK.this_);
4825 check(TOK.semicolon, "`alias Identifier this`");
4826 auto a = new AST.Dsymbols();
4827 a.push(s);
4828 addComment(s, comment);
4829 return a;
4831 /* Look for:
4832 * alias this = identifier;
4834 if (token.value == TOK.this_ && peekNext() == TOK.assign && peekNext2() == TOK.identifier)
4836 check(TOK.this_);
4837 check(TOK.assign);
4838 auto s = new AST.AliasThis(loc, token.ident);
4839 nextToken();
4840 check(TOK.semicolon, "`alias this = Identifier`");
4841 auto a = new AST.Dsymbols();
4842 a.push(s);
4843 addComment(s, comment);
4844 return a;
4846 /* Look for:
4847 * alias identifier = type;
4848 * alias identifier(...) = type;
4849 * https://dlang.org/spec/declaration.html#alias
4851 if (token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign))
4853 auto a = new AST.Dsymbols();
4854 while (1)
4856 auto ident = token.ident;
4857 nextToken();
4858 AST.TemplateParameters* tpl = null;
4859 if (token.value == TOK.leftParenthesis)
4860 tpl = parseTemplateParameterList();
4861 check(TOK.assign);
4863 bool hasParsedAttributes;
4864 void parseAttributes()
4866 if (hasParsedAttributes) // only parse once
4867 return;
4868 hasParsedAttributes = true;
4869 udas = null;
4870 storage_class = STC.undefined_;
4871 link = linkage;
4872 linkloc = this.linkLoc;
4873 setAlignment = false;
4874 ealign = null;
4875 parseStorageClasses(storage_class, link, setAlignment, ealign, udas, linkloc);
4878 if (token.value == TOK.at)
4879 parseAttributes;
4881 AST.Declaration v;
4882 AST.Dsymbol s;
4884 bool attributesAppended;
4885 const StorageClass funcStc = parseTypeCtor();
4886 Token* tk;
4887 // function literal?
4888 if (token.value == TOK.function_ ||
4889 token.value == TOK.delegate_ ||
4890 token.value == TOK.leftParenthesis &&
4891 skipAttributes(peekPastParen(&token), &tk) &&
4892 (tk.value == TOK.goesTo || tk.value == TOK.leftCurly) ||
4893 token.value == TOK.leftCurly ||
4894 token.value == TOK.identifier && peekNext() == TOK.goesTo ||
4895 token.value == TOK.ref_ && peekNext() == TOK.leftParenthesis &&
4896 skipAttributes(peekPastParen(peek(&token)), &tk) &&
4897 (tk.value == TOK.goesTo || tk.value == TOK.leftCurly) ||
4898 token.value == TOK.auto_ &&
4899 (peekNext() == TOK.leftParenthesis || // for better error
4900 peekNext() == TOK.ref_ &&
4901 peekNext2() == TOK.leftParenthesis)
4904 // function (parameters) { statements... }
4905 // delegate (parameters) { statements... }
4906 // (parameters) { statements... }
4907 // (parameters) => expression
4908 // { statements... }
4909 // identifier => expression
4910 // ref (parameters) { statements... }
4911 // ref (parameters) => expression
4912 // auto ref (parameters) { statements... }
4913 // auto ref (parameters) => expression
4915 s = parseFunctionLiteral();
4917 if (udas !is null)
4919 if (storage_class != 0)
4920 error("cannot put a storage-class in an `alias` declaration.");
4921 // parseAttributes shouldn't have set these variables
4922 assert(link == linkage && !setAlignment && ealign is null);
4923 auto tpl_ = cast(AST.TemplateDeclaration) s;
4924 if (tpl_ is null || tpl_.members.length != 1)
4926 error("user-defined attributes are not allowed on `alias` declarations");
4928 else
4930 auto fd = cast(AST.FuncLiteralDeclaration) (*tpl_.members)[0];
4931 auto tf = cast(AST.TypeFunction) fd.type;
4932 assert(tf.parameterList.parameters.length > 0);
4933 auto as = new AST.Dsymbols();
4934 (*tf.parameterList.parameters)[0].userAttribDecl = new AST.UserAttributeDeclaration(udas, as);
4938 v = new AST.AliasDeclaration(loc, ident, s);
4940 else
4942 // type
4943 parseAttributes();
4944 if (udas)
4945 error("user-defined attributes not allowed for `alias` declarations");
4947 auto t = parseBasicType();
4948 t = parseTypeSuffixes(t);
4949 if (token.value == TOK.identifier)
4951 error("unexpected identifier `%s` after `%s`",
4952 token.ident.toChars(), t.toChars());
4953 nextToken();
4955 else if (token.value == TOK.leftParenthesis)
4957 // function type:
4958 // StorageClasses Type ( Parameters ) MemberFunctionAttributes
4959 auto parameterList = parseParameterList(null);
4960 udas = null;
4961 parseStorageClasses(storage_class, link, setAlignment, ealign, udas, linkloc);
4962 if (udas)
4963 error("user-defined attributes not allowed for `alias` declarations");
4965 attributesAppended = true;
4966 // Note: method types can have a TypeCtor attribute
4967 storage_class = appendStorageClass(storage_class, funcStc);
4968 t = new AST.TypeFunction(parameterList, t, link, storage_class);
4971 // Disallow meaningless storage classes on type aliases
4972 if (storage_class)
4974 // Don't raise errors for STC that are part of a function/delegate type, e.g.
4975 // `alias F = ref pure nothrow @nogc @safe int function();`
4976 const remStc = t.isTypeFunction ?
4977 storage_class & ~(STC.FUNCATTR | STC.TYPECTOR) : {
4978 auto tp = t.isTypePointer;
4979 const isFuncType = (tp && tp.next.isTypeFunction) || t.isTypeDelegate;
4980 return isFuncType ? (storage_class & ~STC.FUNCATTR) : storage_class;
4981 }();
4983 if (remStc)
4985 OutBuffer buf;
4986 AST.stcToBuffer(buf, remStc);
4987 // @@@DEPRECATED_2.103@@@
4988 // Deprecated in 2020-07, can be made an error in 2.103
4989 eSink.deprecation(token.loc, "storage class `%s` has no effect in type aliases", buf.peekChars());
4993 v = new AST.AliasDeclaration(loc, ident, t);
4995 if (!attributesAppended)
4996 storage_class = appendStorageClass(storage_class, funcStc);
4997 v.storage_class = storage_class;
4999 s = v;
5000 if (tpl)
5002 auto a2 = new AST.Dsymbols();
5003 a2.push(s);
5004 auto tempdecl = new AST.TemplateDeclaration(loc, ident, tpl, null, a2);
5005 s = tempdecl;
5007 if (link != linkage)
5009 auto a2 = new AST.Dsymbols();
5010 a2.push(s);
5011 s = new AST.LinkDeclaration(linkloc, link, a2);
5013 a.push(s);
5015 switch (token.value)
5017 case TOK.semicolon:
5018 nextToken();
5019 addComment(s, comment);
5020 break;
5022 case TOK.comma:
5023 nextToken();
5024 addComment(s, comment);
5025 if (token.value != TOK.identifier)
5027 error("identifier expected following comma, not `%s`", token.toChars());
5028 break;
5030 if (peekNext() != TOK.assign && peekNext() != TOK.leftParenthesis)
5032 error("`=` expected following identifier");
5033 nextToken();
5034 break;
5036 continue;
5038 default:
5039 error("semicolon expected to close `alias` declaration, not `%s`", token.toChars());
5040 break;
5042 break;
5044 return a;
5047 // alias StorageClasses type ident;
5048 return null;
5051 private AST.Dsymbol parseFunctionLiteral()
5053 const loc = token.loc;
5054 AST.TemplateParameters* tpl = null;
5055 AST.ParameterList parameterList;
5056 AST.Type tret = null;
5057 StorageClass stc = 0;
5058 TOK save = TOK.reserved;
5060 switch (token.value)
5062 case TOK.function_:
5063 case TOK.delegate_:
5064 save = token.value;
5065 nextToken();
5066 if (token.value == TOK.auto_)
5068 nextToken();
5069 if (token.value == TOK.ref_)
5071 // function auto ref (parameters) { statements... }
5072 // delegate auto ref (parameters) { statements... }
5073 stc = STC.auto_ | STC.ref_;
5074 nextToken();
5076 else
5077 error("`auto` can only be used as part of `auto ref` for function literal return values");
5079 else if (token.value == TOK.ref_)
5081 // function ref (parameters) { statements... }
5082 // delegate ref (parameters) { statements... }
5083 stc = STC.ref_;
5084 nextToken();
5086 if (token.value != TOK.leftParenthesis && token.value != TOK.leftCurly &&
5087 token.value != TOK.goesTo)
5089 // function type (parameters) { statements... }
5090 // delegate type (parameters) { statements... }
5091 tret = parseBasicType();
5092 tret = parseTypeSuffixes(tret); // function return type
5095 if (token.value == TOK.leftParenthesis)
5097 // function (parameters) { statements... }
5098 // delegate (parameters) { statements... }
5100 else
5102 // function { statements... }
5103 // delegate { statements... }
5104 break;
5106 goto case TOK.leftParenthesis;
5108 case TOK.auto_:
5110 nextToken();
5111 if (token.value == TOK.ref_)
5113 // auto ref (parameters) => expression
5114 // auto ref (parameters) { statements... }
5115 stc = STC.auto_ | STC.ref_;
5116 nextToken();
5118 else
5119 error("`auto` can only be used as part of `auto ref` for function literal return values");
5120 goto case TOK.leftParenthesis;
5122 case TOK.ref_:
5124 // ref (parameters) => expression
5125 // ref (parameters) { statements... }
5126 stc = STC.ref_;
5127 nextToken();
5128 goto case TOK.leftParenthesis;
5130 case TOK.leftParenthesis:
5132 // (parameters) => expression
5133 // (parameters) { statements... }
5134 parameterList = parseParameterList(&tpl);
5135 stc = parsePostfix(stc, null);
5136 if (StorageClass modStc = stc & STC.TYPECTOR)
5138 if (save == TOK.function_)
5140 OutBuffer buf;
5141 AST.stcToBuffer(buf, modStc);
5142 error("function literal cannot be `%s`", buf.peekChars());
5144 else
5145 save = TOK.delegate_;
5147 break;
5149 case TOK.leftCurly:
5150 // { statements... }
5151 break;
5153 case TOK.identifier:
5155 // identifier => expression
5156 parameterList.parameters = new AST.Parameters();
5157 Identifier id = Identifier.generateId("__T");
5158 AST.Type t = new AST.TypeIdentifier(loc, id);
5159 parameterList.parameters.push(new AST.Parameter(loc, STC.parameter, t, token.ident, null, null));
5161 tpl = new AST.TemplateParameters();
5162 AST.TemplateParameter tp = new AST.TemplateTypeParameter(loc, id, null, null);
5163 tpl.push(tp);
5165 nextToken();
5166 break;
5168 default:
5169 assert(0);
5172 auto tf = new AST.TypeFunction(parameterList, tret, linkage, stc);
5173 tf = cast(AST.TypeFunction)tf.addSTC(stc);
5174 auto fd = new AST.FuncLiteralDeclaration(loc, Loc.initial, tf, save, null, null, stc & STC.auto_);
5176 if (token.value == TOK.goesTo)
5178 check(TOK.goesTo);
5179 if (token.value == TOK.leftCurly)
5181 deprecation("using `(args) => { ... }` to create a delegate that returns a delegate is error-prone.");
5182 deprecationSupplemental("Use `(args) { ... }` for a multi-statement function literal or use `(args) => () { }` if you intended for the lambda to return a delegate.");
5184 const returnloc = token.loc;
5185 AST.Expression ae = parseAssignExp();
5186 fd.fbody = new AST.ReturnStatement(returnloc, ae);
5187 fd.endloc = token.loc;
5189 else
5191 parseContracts(fd);
5194 if (tpl)
5196 // Wrap a template around function fd
5197 auto decldefs = new AST.Dsymbols();
5198 decldefs.push(fd);
5199 return new AST.TemplateDeclaration(fd.loc, fd.ident, tpl, null, decldefs, false, true);
5201 return fd;
5204 /*****************************************
5205 * Parse contracts following function declaration.
5207 private AST.FuncDeclaration parseContracts(AST.FuncDeclaration f, bool isTemplateFunction = false)
5209 LINK linksave = linkage;
5211 bool literal = f.isFuncLiteralDeclaration() !is null;
5213 // The following is irrelevant, as it is overridden by sc.linkage in
5214 // TypeFunction::semantic
5215 linkage = LINK.d; // nested functions have D linkage
5216 bool requireDo = false;
5218 switch (token.value)
5220 case TOK.goesTo:
5221 if (requireDo)
5222 error("missing `do { ... }` after `in` or `out`");
5223 const returnloc = token.loc;
5224 nextToken();
5225 f.fbody = new AST.ReturnStatement(returnloc, parseExpression());
5226 f.endloc = token.loc;
5227 check(TOK.semicolon);
5228 break;
5230 case TOK.leftCurly:
5231 if (requireDo)
5232 error("missing `do { ... }` after `in` or `out`");
5233 f.fbody = parseStatement(0);
5234 f.endloc = endloc;
5235 break;
5237 case TOK.identifier:
5238 if (token.ident == Id._body)
5240 usageOfBodyKeyword();
5241 goto case TOK.do_;
5243 goto default;
5245 case TOK.do_:
5246 nextToken();
5247 f.fbody = parseStatement(ParseStatementFlags.curly);
5248 f.endloc = endloc;
5249 break;
5251 version (none)
5253 // Do we want this for function declarations, so we can do:
5254 // int x, y, foo(), z;
5255 case TOK.comma:
5256 nextToken();
5257 continue;
5260 case TOK.in_:
5261 // in { statements... }
5262 // in (expression)
5263 auto loc = token.loc;
5264 nextToken();
5265 if (!f.frequires)
5267 f.frequires = new AST.Statements;
5269 if (token.value == TOK.leftParenthesis)
5271 nextToken();
5272 AST.Expression e = parseAssignExp(), msg = null;
5273 if (token.value == TOK.comma)
5275 nextToken();
5276 if (token.value != TOK.rightParenthesis)
5278 msg = parseAssignExp();
5279 if (token.value == TOK.comma)
5280 nextToken();
5283 check(TOK.rightParenthesis);
5284 e = new AST.AssertExp(loc, e, msg);
5285 f.frequires.push(new AST.ExpStatement(loc, e));
5286 requireDo = false;
5288 else
5290 auto ret = parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_);
5291 assert(ret);
5292 f.frequires.push(ret);
5293 requireDo = true;
5295 goto L1;
5297 case TOK.out_:
5298 // out { statements... }
5299 // out (; expression)
5300 // out (identifier) { statements... }
5301 // out (identifier; expression)
5302 auto loc = token.loc;
5303 nextToken();
5304 if (!f.fensures)
5306 f.fensures = new AST.Ensures;
5308 Identifier id = null;
5309 if (token.value != TOK.leftCurly)
5311 check(TOK.leftParenthesis);
5312 if (token.value != TOK.identifier && token.value != TOK.semicolon)
5313 error("`(identifier) { ... }` or `(identifier; expression)` following `out` expected, not `%s`", token.toChars());
5314 if (token.value != TOK.semicolon)
5316 id = token.ident;
5317 nextToken();
5319 if (token.value == TOK.semicolon)
5321 nextToken();
5322 AST.Expression e = parseAssignExp(), msg = null;
5323 if (token.value == TOK.comma)
5325 nextToken();
5326 if (token.value != TOK.rightParenthesis)
5328 msg = parseAssignExp();
5329 if (token.value == TOK.comma)
5330 nextToken();
5333 check(TOK.rightParenthesis);
5334 e = new AST.AssertExp(loc, e, msg);
5335 f.fensures.push(AST.Ensure(id, new AST.ExpStatement(loc, e)));
5336 requireDo = false;
5337 goto L1;
5339 check(TOK.rightParenthesis);
5341 f.fensures.push(AST.Ensure(id, parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_)));
5342 requireDo = true;
5343 goto L1;
5345 case TOK.semicolon:
5346 if (!literal)
5348 // https://issues.dlang.org/show_bug.cgi?id=15799
5349 // Semicolon becomes a part of function declaration
5350 // only when 'do' is not required
5351 if (!requireDo)
5352 nextToken();
5353 break;
5355 goto default;
5357 default:
5358 if (literal)
5360 const(char)* sbody = requireDo ? "do " : "";
5361 error("missing `%s{ ... }` for function literal", sbody);
5363 else if (!requireDo) // allow contracts even with no body
5365 TOK t = token.value;
5366 if (t == TOK.const_ || t == TOK.immutable_ || t == TOK.inout_ || t == TOK.return_ ||
5367 t == TOK.shared_ || t == TOK.nothrow_ || t == TOK.pure_)
5368 error("'%s' cannot be placed after a template constraint", token.toChars);
5369 else if (t == TOK.at)
5370 error("attributes cannot be placed after a template constraint");
5371 else if (t == TOK.if_)
5373 if (isTemplateFunction)
5374 error("template constraint must follow parameter lists and attributes");
5375 else
5376 error("cannot use function constraints for non-template functions. Use `static if` instead");
5378 else
5379 error("semicolon expected following function declaration, not `%s`", token.toChars());
5381 break;
5383 if (literal && !f.fbody)
5385 // Set empty function body for error recovery
5386 f.fbody = new AST.CompoundStatement(Loc.initial, cast(AST.Statement)null);
5389 linkage = linksave;
5391 return f;
5394 /*****************************************
5396 private void checkDanglingElse(Loc elseloc)
5398 if (token.value != TOK.else_ && token.value != TOK.catch_ && token.value != TOK.finally_ && lookingForElse.linnum != 0)
5400 eSink.warning(elseloc, "else is dangling, add { } after condition at %s", lookingForElse.toChars());
5404 /* *************************
5405 * Issue errors if C-style syntax
5406 * Params:
5407 * alt = !=0 for C-style syntax
5409 private void checkCstyleTypeSyntax(Loc loc, AST.Type t, int alt, Identifier ident)
5411 if (!alt)
5412 return;
5414 const(char)* sp = !ident ? "" : " ";
5415 const(char)* s = !ident ? "" : ident.toChars();
5416 error(loc, "instead of C-style syntax, use D-style `%s%s%s`", t.toChars(), sp, s);
5419 /*****************************
5420 * Ad-hoc error message for missing or extra parens that close a condition.
5421 * Params:
5422 * start = "if", "while", etc. Must be 0 terminated.
5423 * param = if the condition is a declaration, this will be non-null
5424 * condition = if param is null, then this is the conditional Expression. If condition is null,
5425 * then an error in the condition was already reported.
5427 private void closeCondition(string start, AST.Parameter param, AST.Expression condition)
5429 string format;
5430 if (token.value != TOK.rightParenthesis && condition)
5432 format = "missing closing `)` after `%s (%s`";
5434 else
5435 check(TOK.rightParenthesis);
5436 if (token.value == TOK.rightParenthesis)
5438 if (condition) // if not an error in condition
5439 format = "extra `)` after `%s (%s)`";
5440 nextToken();
5442 if (format)
5443 error(token.loc, format.ptr, start.ptr, param ? "declaration".ptr : condition.toChars());
5446 /*****************************************
5447 * Parses `foreach` statements, `static foreach` statements and
5448 * `static foreach` declarations.
5449 * Params:
5450 * Foreach = one of Statement, StaticForeachStatement, StaticForeachDeclaration
5451 * loc = location of foreach
5452 * pLastDecl = non-null for StaticForeachDeclaration
5453 * Returns:
5454 * the Foreach generated
5456 private Foreach parseForeach(alias Foreach)(Loc loc, AST.Dsymbol* pLastDecl)
5458 static if (is(Foreach == AST.StaticForeachStatement) || is(Foreach == AST.StaticForeachDeclaration))
5460 nextToken();
5463 TOK op = token.value;
5465 nextToken();
5466 check(TOK.leftParenthesis);
5468 auto parameters = new AST.Parameters();
5469 Identifier lastai;
5470 while (1)
5472 Identifier ai = null;
5473 AST.Type at;
5474 Loc aloc;
5476 StorageClass storageClass = 0;
5477 StorageClass stc = 0;
5478 Lagain:
5479 if (stc)
5481 storageClass = appendStorageClass(storageClass, stc);
5482 nextToken();
5484 switch (token.value)
5486 case TOK.ref_:
5487 stc = STC.ref_;
5488 goto Lagain;
5490 case TOK.scope_:
5491 stc = STC.scope_;
5492 goto Lagain;
5494 case TOK.out_:
5495 error("cannot declare `out` loop variable, use `ref` instead");
5496 stc = STC.out_;
5497 goto Lagain;
5499 case TOK.auto_:
5500 error("cannot declare `auto` loop variable, omit `auto` to still get type inference");
5501 stc = STC.auto_;
5502 goto Lagain;
5504 case TOK.enum_:
5505 stc = STC.manifest;
5506 goto Lagain;
5508 case TOK.alias_:
5509 storageClass = appendStorageClass(storageClass, STC.alias_);
5510 nextToken();
5511 break;
5513 case TOK.const_:
5514 if (peekNext() != TOK.leftParenthesis)
5516 stc = STC.const_;
5517 goto Lagain;
5519 break;
5521 case TOK.immutable_:
5522 if (peekNext() != TOK.leftParenthesis)
5524 stc = STC.immutable_;
5525 goto Lagain;
5527 break;
5529 case TOK.shared_:
5530 if (peekNext() != TOK.leftParenthesis)
5532 stc = STC.shared_;
5533 goto Lagain;
5535 break;
5537 case TOK.inout_:
5538 if (peekNext() != TOK.leftParenthesis)
5540 stc = STC.wild;
5541 goto Lagain;
5543 break;
5545 default:
5546 break;
5548 if (token.value == TOK.identifier)
5550 const tv = peekNext();
5551 if (tv == TOK.comma || tv == TOK.semicolon || tv == TOK.rightParenthesis)
5553 lastai = token.ident;
5554 ai = token.ident;
5555 at = null; // infer argument type
5556 aloc = token.loc;
5557 nextToken();
5558 goto Larg;
5561 at = parseType(&ai);
5562 if (!ai)
5563 noIdentifierForDeclarator(at);
5564 Larg:
5565 auto p = new AST.Parameter(aloc, storageClass, at, ai, null, null);
5566 parameters.push(p);
5567 if (token.value == TOK.comma)
5569 nextToken();
5570 continue;
5572 break;
5574 if (token.value != TOK.semicolon)
5576 error("missing `; expression` before `)` of `foreach`");
5577 nextToken();
5578 if (lastai && parameters.length >= 2)
5580 eSink.errorSupplemental(loc, "perhaps the `;` goes before `%s`", lastai.toChars());
5582 return null;
5584 nextToken();
5586 AST.Expression aggr = parseExpression();
5587 if (token.value == TOK.slice && parameters.length == 1)
5589 AST.Parameter p = (*parameters)[0];
5590 nextToken();
5591 AST.Expression upr = parseExpression();
5592 check(TOK.rightParenthesis);
5593 Loc endloc;
5594 static if (is(Foreach == AST.Statement) || is(Foreach == AST.StaticForeachStatement))
5596 AST.Statement _body = parseStatement(0, null, &endloc);
5598 else
5600 AST.Statement _body = null;
5602 auto rangefe = new AST.ForeachRangeStatement(loc, op, p, aggr, upr, _body, endloc);
5603 static if (is(Foreach == AST.Statement))
5605 return rangefe;
5607 else static if(is(Foreach == AST.StaticForeachDeclaration))
5609 return new AST.StaticForeachDeclaration(new AST.StaticForeach(loc, null, rangefe), parseBlock(pLastDecl));
5611 else static if (is(Foreach == AST.StaticForeachStatement))
5613 return new AST.StaticForeachStatement(loc, new AST.StaticForeach(loc, null, rangefe));
5616 else
5618 check(TOK.rightParenthesis);
5619 Loc endloc;
5620 static if (is(Foreach == AST.Statement) || is(Foreach == AST.StaticForeachStatement))
5622 AST.Statement _body = parseStatement(0, null, &endloc);
5624 else
5626 AST.Statement _body = null;
5628 auto aggrfe = new AST.ForeachStatement(loc, op, parameters, aggr, _body, endloc);
5629 static if (is(Foreach == AST.Statement))
5631 return aggrfe;
5633 else static if(is(Foreach == AST.StaticForeachDeclaration))
5635 return new AST.StaticForeachDeclaration(new AST.StaticForeach(loc, aggrfe, null), parseBlock(pLastDecl));
5637 else static if (is(Foreach == AST.StaticForeachStatement))
5639 return new AST.StaticForeachStatement(loc, new AST.StaticForeach(loc, aggrfe, null));
5645 /***
5646 * Parse an assignment condition for if or while statements.
5648 * Returns:
5649 * The variable that is declared inside the condition
5651 AST.Parameter parseAssignCondition()
5653 AST.Parameter param = null;
5654 StorageClass storageClass = 0;
5655 StorageClass stc = 0;
5656 Lwhile:
5657 while (1)
5659 switch (token.value)
5661 // parse ref for better error
5662 case TOK.ref_:
5663 stc = STC.ref_;
5664 break;
5666 case TOK.scope_:
5667 stc = STC.scope_;
5668 break;
5670 case TOK.auto_:
5671 stc = STC.auto_;
5672 break;
5674 case TOK.const_:
5675 if (peekNext() != TOK.leftParenthesis)
5677 stc = STC.const_;
5678 break;
5680 goto default;
5682 case TOK.immutable_:
5683 if (peekNext() != TOK.leftParenthesis)
5685 stc = STC.immutable_;
5686 break;
5688 goto default;
5690 case TOK.shared_:
5691 if (peekNext() != TOK.leftParenthesis)
5693 stc = STC.shared_;
5694 break;
5696 goto default;
5698 case TOK.inout_:
5699 if (peekNext() != TOK.leftParenthesis)
5701 stc = STC.wild;
5702 break;
5704 goto default;
5706 default:
5707 break Lwhile;
5709 storageClass = appendStorageClass(storageClass, stc);
5710 nextToken();
5712 auto n = peek(&token);
5713 if (storageClass != 0 && token.value == TOK.identifier && n.value == TOK.assign)
5715 Identifier ai = token.ident;
5716 AST.Type at = null; // infer parameter type
5717 const aloc = token.loc;
5718 nextToken();
5719 check(TOK.assign);
5720 param = new AST.Parameter(aloc, storageClass, at, ai, null, null);
5722 else if (isDeclaration(&token, NeedDeclaratorId.must, TOK.assign, null))
5724 Identifier ai;
5725 const aloc = token.loc;
5726 AST.Type at = parseType(&ai);
5727 check(TOK.assign);
5728 param = new AST.Parameter(aloc, storageClass, at, ai, null, null);
5730 else if (storageClass != 0)
5731 error("found `%s` while expecting `=` or identifier", n.toChars());
5733 return param;
5736 /*****************************************
5737 * Input:
5738 * flags PSxxxx
5739 * Output:
5740 * pEndloc if { ... statements ... }, store location of closing brace, otherwise loc of last token of statement
5742 AST.Statement parseStatement(int flags, const(char)** endPtr = null, Loc* pEndloc = null)
5744 AST.Statement s;
5745 AST.Condition cond;
5746 AST.Statement ifbody;
5747 AST.Statement elsebody;
5748 bool isfinal;
5749 const loc = token.loc;
5751 //printf("parseStatement()\n");
5752 if (flags & ParseStatementFlags.curly && token.value != TOK.leftCurly)
5753 error("statement expected to be `{ }`, not `%s`", token.toChars());
5755 switch (token.value)
5757 case TOK.identifier:
5759 /* A leading identifier can be a declaration, label, or expression.
5760 * The easiest case to check first is label:
5762 if (peekNext() == TOK.colonColon)
5764 // skip ident::
5765 nextToken();
5766 nextToken();
5767 error("use `.` for member lookup, not `::`");
5768 break;
5771 if (peekNext() == TOK.colon)
5773 // It's a label
5774 Identifier ident = token.ident;
5775 nextToken();
5776 nextToken();
5777 if (token.value == TOK.rightCurly)
5778 s = null;
5779 else if (token.value == TOK.leftCurly)
5780 s = parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_);
5781 else if (flags & ParseStatementFlags.curlyScope)
5782 s = parseStatement(ParseStatementFlags.semiOk | ParseStatementFlags.curlyScope);
5783 else
5784 s = parseStatement(ParseStatementFlags.semiOk);
5785 s = new AST.LabelStatement(loc, ident, s);
5786 break;
5788 goto case TOK.dot;
5790 case TOK.dot:
5791 case TOK.typeof_:
5792 case TOK.vector:
5793 case TOK.traits:
5794 /* https://issues.dlang.org/show_bug.cgi?id=15163
5795 * If tokens can be handled as
5796 * old C-style declaration or D expression, prefer the latter.
5798 if (isDeclaration(&token, NeedDeclaratorId.mustIfDstyle, TOK.reserved, null))
5799 goto Ldeclaration;
5800 goto Lexp;
5802 case TOK.assert_:
5803 case TOK.this_:
5804 case TOK.super_:
5805 case TOK.int32Literal:
5806 case TOK.uns32Literal:
5807 case TOK.int64Literal:
5808 case TOK.uns64Literal:
5809 case TOK.int128Literal:
5810 case TOK.uns128Literal:
5811 case TOK.float32Literal:
5812 case TOK.float64Literal:
5813 case TOK.float80Literal:
5814 case TOK.imaginary32Literal:
5815 case TOK.imaginary64Literal:
5816 case TOK.imaginary80Literal:
5817 case TOK.charLiteral:
5818 case TOK.wcharLiteral:
5819 case TOK.dcharLiteral:
5820 case TOK.null_:
5821 case TOK.true_:
5822 case TOK.false_:
5823 case TOK.string_:
5824 case TOK.interpolated:
5825 case TOK.hexadecimalString:
5826 case TOK.leftParenthesis:
5827 case TOK.cast_:
5828 case TOK.mul:
5829 case TOK.min:
5830 case TOK.add:
5831 case TOK.tilde:
5832 case TOK.not:
5833 case TOK.plusPlus:
5834 case TOK.minusMinus:
5835 case TOK.new_:
5836 case TOK.delete_:
5837 case TOK.delegate_:
5838 case TOK.function_:
5839 case TOK.typeid_:
5840 case TOK.is_:
5841 case TOK.leftBracket:
5842 case TOK.file:
5843 case TOK.fileFullPath:
5844 case TOK.line:
5845 case TOK.moduleString:
5846 case TOK.functionString:
5847 case TOK.prettyFunction:
5848 Lexp:
5850 AST.Expression exp = parseExpression();
5851 /* https://issues.dlang.org/show_bug.cgi?id=15103
5852 * Improve declaration / initialization syntax error message
5853 * Error: found 'foo' when expecting ';' following expression
5854 * becomes Error: found `(` when expecting `;` or `=`, did you mean `Foo foo = 42`?
5856 if (token.value == TOK.identifier && exp.op == EXP.identifier)
5858 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());
5859 nextToken();
5861 else
5864 * https://issues.dlang.org/show_bug.cgi?id=22529
5865 * Avoid empty declaration error in case of missing semicolon
5866 * followed by another token and another semicolon. E.g.:
5868 * foo()
5869 * return;
5871 * When the missing `;` error is emitted, token is sitting on return.
5872 * If we simply use `check` to emit the error, the token is advanced
5873 * to `;` and the empty statement error would follow. To avoid that,
5874 * we check if the next token is a semicolon and simply output the error,
5875 * otherwise we fall back on the old path (advancing the token).
5877 if (token.value != TOK.semicolon && peek(&token).value == TOK.semicolon)
5878 error("found `%s` when expecting `;` following expression", token.toChars());
5879 else
5881 if (token.value != TOK.semicolon)
5883 error("found `%s` when expecting `;` following expression", token.toChars());
5884 eSink.errorSupplemental(exp.loc, "expression: `%s`", exp.toChars());
5886 nextToken();
5889 s = new AST.ExpStatement(loc, exp);
5890 break;
5892 case TOK.static_:
5894 // Look ahead to see if it's static assert() or static if()
5895 const tv = peekNext();
5896 if (tv == TOK.assert_)
5898 s = new AST.StaticAssertStatement(parseStaticAssert());
5899 break;
5901 if (tv == TOK.if_)
5903 cond = parseStaticIfCondition();
5904 goto Lcondition;
5906 if (tv == TOK.foreach_ || tv == TOK.foreach_reverse_)
5908 s = parseForeach!(AST.StaticForeachStatement)(loc, null);
5909 if (flags & ParseStatementFlags.scope_)
5910 s = new AST.ScopeStatement(loc, s, token.loc);
5911 break;
5913 if (tv == TOK.import_)
5915 AST.Dsymbols* imports = parseImport();
5916 s = new AST.ImportStatement(loc, imports);
5917 if (flags & ParseStatementFlags.scope_)
5918 s = new AST.ScopeStatement(loc, s, token.loc);
5919 break;
5921 goto Ldeclaration;
5923 case TOK.final_:
5924 if (peekNext() == TOK.switch_)
5926 nextToken();
5927 isfinal = true;
5928 goto Lswitch;
5930 goto Ldeclaration;
5932 case TOK.wchar_:
5933 case TOK.dchar_:
5934 case TOK.bool_:
5935 case TOK.char_:
5936 case TOK.int8:
5937 case TOK.uns8:
5938 case TOK.int16:
5939 case TOK.uns16:
5940 case TOK.int32:
5941 case TOK.uns32:
5942 case TOK.int64:
5943 case TOK.uns64:
5944 case TOK.int128:
5945 case TOK.uns128:
5946 case TOK.float32:
5947 case TOK.float64:
5948 case TOK.float80:
5949 case TOK.imaginary32:
5950 case TOK.imaginary64:
5951 case TOK.imaginary80:
5952 case TOK.complex32:
5953 case TOK.complex64:
5954 case TOK.complex80:
5955 case TOK.void_:
5956 // bug 7773: int.max is always a part of expression
5957 if (peekNext() == TOK.dot)
5958 goto Lexp;
5959 if (peekNext() == TOK.leftParenthesis)
5960 goto Lexp;
5961 goto case;
5963 case TOK.alias_:
5964 case TOK.const_:
5965 case TOK.auto_:
5966 case TOK.abstract_:
5967 case TOK.extern_:
5968 case TOK.align_:
5969 case TOK.immutable_:
5970 case TOK.shared_:
5971 case TOK.inout_:
5972 case TOK.deprecated_:
5973 case TOK.nothrow_:
5974 case TOK.pure_:
5975 case TOK.ref_:
5976 case TOK.gshared:
5977 case TOK.at:
5978 case TOK.struct_:
5979 case TOK.union_:
5980 case TOK.class_:
5981 case TOK.interface_:
5982 Ldeclaration:
5984 AST.Dsymbols* a = parseDeclarations(false, null, null);
5985 if (a.length > 1)
5987 auto as = new AST.Statements();
5988 as.reserve(a.length);
5989 foreach (i; 0 .. a.length)
5991 AST.Dsymbol d = (*a)[i];
5992 s = new AST.ExpStatement(loc, d);
5993 as.push(s);
5995 s = new AST.CompoundDeclarationStatement(loc, as);
5997 else if (a.length == 1)
5999 AST.Dsymbol d = (*a)[0];
6000 s = new AST.ExpStatement(loc, d);
6002 else
6003 s = new AST.ExpStatement(loc, cast(AST.Expression)null);
6004 if (flags & ParseStatementFlags.scope_)
6005 s = new AST.ScopeStatement(loc, s, token.loc);
6006 break;
6008 case TOK.enum_:
6010 /* Determine if this is a manifest constant declaration,
6011 * or a conventional enum.
6013 AST.Dsymbol d;
6014 const tv = peekNext();
6015 if (tv == TOK.leftCurly || tv == TOK.colon)
6016 d = parseEnum();
6017 else if (tv != TOK.identifier)
6018 goto Ldeclaration;
6019 else
6021 const nextv = peekNext2();
6022 if (nextv == TOK.leftCurly || nextv == TOK.colon || nextv == TOK.semicolon)
6023 d = parseEnum();
6024 else
6025 goto Ldeclaration;
6027 s = new AST.ExpStatement(loc, d);
6028 if (flags & ParseStatementFlags.scope_)
6029 s = new AST.ScopeStatement(loc, s, token.loc);
6030 break;
6032 case TOK.mixin_:
6034 if (isDeclaration(&token, NeedDeclaratorId.mustIfDstyle, TOK.reserved, null))
6035 goto Ldeclaration;
6036 const tv = peekNext();
6037 if (tv == TOK.leftParenthesis)
6039 // mixin(string)
6040 AST.Expression e = parseAssignExp();
6041 check(TOK.semicolon, "mixin");
6042 if (e.op == EXP.mixin_)
6044 AST.MixinExp cpe = cast(AST.MixinExp)e;
6045 s = new AST.MixinStatement(loc, cpe.exps);
6047 else
6049 s = new AST.ExpStatement(loc, e);
6051 break;
6053 else if (tv == TOK.template_)
6055 // mixin template
6056 nextToken();
6057 AST.Dsymbol d = parseTemplateDeclaration(true);
6058 s = new AST.ExpStatement(loc, d);
6059 break;
6061 AST.Dsymbol d = parseMixin();
6062 s = new AST.ExpStatement(loc, d);
6063 if (flags & ParseStatementFlags.scope_)
6064 s = new AST.ScopeStatement(loc, s, token.loc);
6065 break;
6067 case TOK.leftCurly:
6069 const lcLoc = token.loc;
6070 const lookingForElseSave = lookingForElse;
6071 lookingForElse = Loc.initial;
6073 nextToken();
6074 //if (token.value == TOK.semicolon)
6075 // error("use `{ }` for an empty statement, not `;`");
6076 auto statements = new AST.Statements();
6077 while (token.value != TOK.rightCurly && token.value != TOK.endOfFile)
6079 statements.push(parseStatement(ParseStatementFlags.curlyScope | ParseStatementFlags.semiOk));
6081 if (endPtr)
6082 *endPtr = token.ptr;
6083 endloc = token.loc;
6084 if (pEndloc)
6086 *pEndloc = token.loc;
6087 pEndloc = null; // don't set it again
6089 s = new AST.CompoundStatement(loc, statements);
6090 if (flags & (ParseStatementFlags.scope_ | ParseStatementFlags.curlyScope))
6091 s = new AST.ScopeStatement(loc, s, token.loc);
6092 if (token.value != TOK.rightCurly)
6094 error(token.loc, "matching `}` expected following compound statement, not `%s`",
6095 token.toChars());
6096 eSink.errorSupplemental(lcLoc, "unmatched `{`");
6098 else
6099 nextToken();
6100 lookingForElse = lookingForElseSave;
6101 break;
6103 case TOK.while_:
6105 nextToken();
6106 check(TOK.leftParenthesis);
6107 auto param = parseAssignCondition();
6108 auto condition = parseExpression();
6109 closeCondition("while", param, condition);
6111 Loc endloc;
6112 AST.Statement _body = parseStatement(ParseStatementFlags.scope_, null, &endloc);
6113 s = new AST.WhileStatement(loc, condition, _body, endloc, param);
6114 break;
6116 case TOK.semicolon:
6117 if (!(flags & ParseStatementFlags.semiOk))
6119 error("use `{ }` for an empty statement, not `;`");
6121 nextToken();
6122 s = new AST.ExpStatement(loc, cast(AST.Expression)null);
6123 break;
6125 case TOK.do_:
6127 AST.Statement _body;
6129 nextToken();
6130 const lookingForElseSave = lookingForElse;
6131 lookingForElse = Loc.initial;
6132 _body = parseStatement(ParseStatementFlags.scope_);
6133 lookingForElse = lookingForElseSave;
6134 check(TOK.while_);
6135 check(TOK.leftParenthesis);
6136 auto condition = parseExpression();
6137 closeCondition("do .. while", null, condition);
6138 if (token.value == TOK.semicolon)
6139 nextToken();
6140 else
6141 error("terminating `;` required after do-while statement");
6142 s = new AST.DoStatement(loc, _body, condition, token.loc);
6143 break;
6145 case TOK.for_:
6147 AST.Statement _init;
6148 AST.Expression condition;
6149 AST.Expression increment;
6151 nextToken();
6152 check(TOK.leftParenthesis);
6153 if (token.value == TOK.semicolon)
6155 _init = null;
6156 nextToken();
6158 else
6160 const lookingForElseSave = lookingForElse;
6161 lookingForElse = Loc.initial;
6162 _init = parseStatement(0);
6163 lookingForElse = lookingForElseSave;
6165 if (token.value == TOK.semicolon)
6167 condition = null;
6168 nextToken();
6170 else
6172 condition = parseExpression();
6173 check(TOK.semicolon, "`for` condition");
6175 if (token.value == TOK.rightParenthesis)
6177 increment = null;
6178 nextToken();
6180 else
6182 increment = parseExpression();
6183 check(TOK.rightParenthesis);
6185 Loc endloc;
6186 AST.Statement _body = parseStatement(ParseStatementFlags.scope_, null, &endloc);
6187 s = new AST.ForStatement(loc, _init, condition, increment, _body, endloc);
6188 break;
6190 case TOK.foreach_:
6191 case TOK.foreach_reverse_:
6193 s = parseForeach!(AST.Statement)(loc, null);
6194 break;
6196 case TOK.if_:
6198 nextToken();
6199 check(TOK.leftParenthesis);
6200 auto param = parseAssignCondition();
6201 auto condition = parseExpression();
6202 closeCondition("if", param, condition);
6205 const lookingForElseSave = lookingForElse;
6206 lookingForElse = loc;
6207 ifbody = parseStatement(ParseStatementFlags.scope_);
6208 lookingForElse = lookingForElseSave;
6210 if (token.value == TOK.else_)
6212 const elseloc = token.loc;
6213 nextToken();
6214 elsebody = parseStatement(ParseStatementFlags.scope_);
6215 checkDanglingElse(elseloc);
6217 else
6218 elsebody = null;
6219 if (condition && ifbody)
6220 s = new AST.IfStatement(loc, param, condition, ifbody, elsebody, token.loc);
6221 else
6222 s = null; // don't propagate parsing errors
6223 break;
6226 case TOK.else_:
6227 error("found `else` without a corresponding `if`, `version` or `debug` statement");
6228 goto Lerror;
6230 case TOK.scope_:
6231 if (peekNext() != TOK.leftParenthesis)
6232 goto Ldeclaration; // scope used as storage class
6233 nextToken();
6234 check(TOK.leftParenthesis);
6235 if (token.value != TOK.identifier)
6237 error("scope identifier expected");
6238 goto Lerror;
6240 else
6242 TOK t = TOK.onScopeExit;
6243 Identifier id = token.ident;
6244 if (id == Id.exit)
6245 t = TOK.onScopeExit;
6246 else if (id == Id.failure)
6247 t = TOK.onScopeFailure;
6248 else if (id == Id.success)
6249 t = TOK.onScopeSuccess;
6250 else
6251 error("valid scope identifiers are `exit`, `failure`, or `success`, not `%s`", id.toChars());
6252 nextToken();
6253 check(TOK.rightParenthesis);
6254 AST.Statement st = parseStatement(ParseStatementFlags.scope_);
6255 s = new AST.ScopeGuardStatement(loc, t, st);
6256 break;
6259 case TOK.debug_:
6260 nextToken();
6261 if (token.value == TOK.assign)
6263 if (auto ds = parseDebugSpecification())
6265 if (ds.ident)
6266 eSink.error(ds.loc, "%s `%s` declaration must be at module level", ds.kind, ds.toPrettyChars);
6267 else
6268 eSink.error(ds.loc, "%s `%s` level declaration must be at module level", ds.kind, ds.toPrettyChars);
6270 break;
6272 cond = parseDebugCondition();
6273 goto Lcondition;
6275 case TOK.version_:
6276 nextToken();
6277 if (token.value == TOK.assign)
6279 if (auto vs = parseVersionSpecification())
6281 if (vs.ident)
6282 eSink.error(vs.loc, "%s `%s` declaration must be at module level", vs.kind, vs.toPrettyChars);
6283 else
6284 eSink.error(vs.loc, "%s `%s` level declaration must be at module level", vs.kind, vs.toPrettyChars);
6286 break;
6288 cond = parseVersionCondition();
6289 goto Lcondition;
6291 Lcondition:
6293 const lookingForElseSave = lookingForElse;
6294 lookingForElse = loc;
6295 ifbody = parseStatement(0);
6296 lookingForElse = lookingForElseSave;
6298 elsebody = null;
6299 if (token.value == TOK.else_)
6301 const elseloc = token.loc;
6302 nextToken();
6303 elsebody = parseStatement(0);
6304 checkDanglingElse(elseloc);
6306 s = new AST.ConditionalStatement(loc, cond, ifbody, elsebody);
6307 if (flags & ParseStatementFlags.scope_)
6308 s = new AST.ScopeStatement(loc, s, token.loc);
6309 break;
6311 case TOK.pragma_:
6313 Identifier ident;
6314 AST.Expressions* args = null;
6315 AST.Statement _body;
6317 nextToken();
6318 check(TOK.leftParenthesis);
6319 if (token.value != TOK.identifier)
6321 error("`pragma(identifier)` expected");
6322 goto Lerror;
6324 ident = token.ident;
6325 nextToken();
6326 if (token.value == TOK.comma && peekNext() != TOK.rightParenthesis)
6327 args = parseArguments(); // pragma(identifier, args...);
6328 else
6329 check(TOK.rightParenthesis); // pragma(identifier);
6330 if (token.value == TOK.semicolon)
6332 nextToken();
6333 _body = null;
6335 else
6336 _body = parseStatement(0);
6337 s = new AST.PragmaStatement(loc, ident, args, _body);
6338 break;
6340 case TOK.switch_:
6341 isfinal = false;
6342 goto Lswitch;
6344 Lswitch:
6346 nextToken();
6347 check(TOK.leftParenthesis);
6348 auto param = parseAssignCondition();
6349 AST.Expression condition = parseExpression();
6350 closeCondition("switch", null, condition);
6351 AST.Statement _body = parseStatement(ParseStatementFlags.scope_);
6352 s = new AST.SwitchStatement(loc, param, condition, _body, isfinal, token.loc);
6353 break;
6355 case TOK.case_:
6357 AST.Expression exp;
6358 AST.Expressions cases; // array of Expression's
6359 AST.Expression last = null;
6361 nextToken();
6364 exp = parseAssignExp();
6365 cases.push(exp);
6366 if (token.value != TOK.comma)
6367 break;
6368 nextToken(); //comma
6370 while (token.value != TOK.colon && token.value != TOK.endOfFile);
6371 check(TOK.colon);
6373 /* case exp: .. case last:
6375 if (token.value == TOK.slice)
6377 if (cases.length > 1)
6378 error("only one `case` allowed for start of case range");
6379 nextToken();
6380 check(TOK.case_);
6381 last = parseAssignExp();
6382 check(TOK.colon);
6385 if (flags & ParseStatementFlags.curlyScope)
6387 auto statements = new AST.Statements();
6388 while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
6390 auto cur = parseStatement(ParseStatementFlags.curlyScope);
6391 statements.push(cur);
6393 // https://issues.dlang.org/show_bug.cgi?id=21739
6394 // Stop at the last break s.t. the following non-case statements are
6395 // not merged into the current case. This can happen for
6396 // case 1: ... break;
6397 // debug { case 2: ... }
6398 if (cur && cur.isBreakStatement())
6399 break;
6401 s = new AST.CompoundStatement(loc, statements);
6403 else
6405 s = parseStatement(0);
6407 s = new AST.ScopeStatement(loc, s, token.loc);
6409 if (last)
6411 s = new AST.CaseRangeStatement(loc, exp, last, s);
6413 else
6415 // Keep cases in order by building the case statements backwards
6416 for (size_t i = cases.length; i; i--)
6418 exp = cases[i - 1];
6419 s = new AST.CaseStatement(loc, exp, s);
6422 break;
6424 case TOK.default_:
6426 nextToken();
6427 check(TOK.colon);
6429 if (flags & ParseStatementFlags.curlyScope)
6431 auto statements = new AST.Statements();
6432 while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
6434 statements.push(parseStatement(ParseStatementFlags.curlyScope));
6436 s = new AST.CompoundStatement(loc, statements);
6438 else
6439 s = parseStatement(0);
6440 s = new AST.ScopeStatement(loc, s, token.loc);
6441 s = new AST.DefaultStatement(loc, s);
6442 break;
6444 case TOK.return_:
6446 AST.Expression exp;
6447 nextToken();
6448 exp = token.value == TOK.semicolon ? null : parseExpression();
6449 check(TOK.semicolon, "`return` statement");
6450 s = new AST.ReturnStatement(loc, exp);
6451 break;
6453 case TOK.break_:
6455 Identifier ident;
6456 nextToken();
6457 ident = null;
6458 if (token.value == TOK.identifier)
6460 ident = token.ident;
6461 nextToken();
6463 check(TOK.semicolon, "`break` statement");
6464 s = new AST.BreakStatement(loc, ident);
6465 break;
6467 case TOK.continue_:
6469 Identifier ident;
6470 nextToken();
6471 ident = null;
6472 if (token.value == TOK.identifier)
6474 ident = token.ident;
6475 nextToken();
6477 check(TOK.semicolon, "`continue` statement");
6478 s = new AST.ContinueStatement(loc, ident);
6479 break;
6481 case TOK.goto_:
6483 Identifier ident;
6484 nextToken();
6485 if (token.value == TOK.default_)
6487 nextToken();
6488 s = new AST.GotoDefaultStatement(loc);
6490 else if (token.value == TOK.case_)
6492 AST.Expression exp = null;
6493 nextToken();
6494 if (token.value != TOK.semicolon)
6495 exp = parseExpression();
6496 s = new AST.GotoCaseStatement(loc, exp);
6498 else
6500 if (token.value != TOK.identifier)
6502 error("identifier expected following `goto`");
6503 ident = null;
6505 else
6507 ident = token.ident;
6508 nextToken();
6510 s = new AST.GotoStatement(loc, ident);
6512 check(TOK.semicolon, "`goto` statement");
6513 break;
6515 case TOK.synchronized_:
6517 AST.Expression exp;
6518 AST.Statement _body;
6520 Token* t = peek(&token);
6521 if (skipAttributes(t, &t) && t.value == TOK.class_)
6522 goto Ldeclaration;
6524 nextToken();
6525 if (token.value == TOK.leftParenthesis)
6527 nextToken();
6528 exp = parseExpression();
6529 closeCondition("synchronized", null, exp);
6531 else
6532 exp = null;
6533 _body = parseStatement(ParseStatementFlags.scope_);
6534 s = new AST.SynchronizedStatement(loc, exp, _body);
6535 break;
6537 case TOK.with_:
6539 AST.Expression exp;
6540 AST.Statement _body;
6541 Loc endloc = loc;
6543 nextToken();
6544 check(TOK.leftParenthesis);
6545 exp = parseExpression();
6546 closeCondition("with", null, exp);
6547 _body = parseStatement(ParseStatementFlags.scope_, null, &endloc);
6548 s = new AST.WithStatement(loc, exp, _body, endloc);
6549 break;
6551 case TOK.try_:
6553 AST.Statement _body;
6554 AST.Catches* catches = null;
6555 AST.Statement finalbody = null;
6557 nextToken();
6558 const lookingForElseSave = lookingForElse;
6559 lookingForElse = Loc.initial;
6560 _body = parseStatement(ParseStatementFlags.scope_);
6561 lookingForElse = lookingForElseSave;
6562 while (token.value == TOK.catch_)
6564 AST.Statement handler;
6565 AST.Catch c;
6566 AST.Type t;
6567 Identifier id;
6568 const catchloc = token.loc;
6570 nextToken();
6571 if (token.value != TOK.leftParenthesis)
6573 deprecation("`catch` statement without an exception specification is deprecated");
6574 deprecationSupplemental("use `catch(Throwable)` for old behavior");
6575 t = null;
6576 id = null;
6578 else
6580 check(TOK.leftParenthesis);
6581 id = null;
6582 t = parseType(&id);
6583 check(TOK.rightParenthesis);
6585 handler = parseStatement(0);
6586 c = new AST.Catch(catchloc, t, id, handler);
6587 if (!catches)
6588 catches = new AST.Catches();
6589 catches.push(c);
6592 if (token.value == TOK.finally_)
6594 nextToken();
6595 finalbody = parseStatement(ParseStatementFlags.scope_);
6598 s = _body;
6599 if (!catches && !finalbody)
6600 error("`catch` or `finally` expected following `try`");
6601 else
6603 if (catches)
6604 s = new AST.TryCatchStatement(loc, _body, catches);
6605 if (finalbody)
6606 s = new AST.TryFinallyStatement(loc, s, finalbody);
6608 break;
6610 case TOK.throw_:
6612 AST.Expression exp;
6613 nextToken();
6614 exp = parseExpression();
6615 check(TOK.semicolon, "`throw` statement");
6616 s = new AST.ThrowStatement(loc, exp);
6617 break;
6620 case TOK.asm_:
6621 s = parseAsm(false);
6622 break;
6624 case TOK.import_:
6626 /* https://issues.dlang.org/show_bug.cgi?id=16088
6628 * At this point it can either be an
6629 * https://dlang.org/spec/grammar.html#ImportExpression
6630 * or an
6631 * https://dlang.org/spec/grammar.html#ImportDeclaration.
6632 * See if the next token after `import` is a `(`; if so,
6633 * then it is an import expression.
6635 if (peekNext() == TOK.leftParenthesis)
6637 AST.Expression e = parseExpression();
6638 check(TOK.semicolon, "`import` Expression");
6639 s = new AST.ExpStatement(loc, e);
6641 else
6643 AST.Dsymbols* imports = parseImport();
6644 s = new AST.ImportStatement(loc, imports);
6645 if (flags & ParseStatementFlags.scope_)
6646 s = new AST.ScopeStatement(loc, s, token.loc);
6648 break;
6650 case TOK.template_:
6652 AST.Dsymbol d = parseTemplateDeclaration();
6653 s = new AST.ExpStatement(loc, d);
6654 break;
6656 default:
6657 error("found `%s` instead of statement", token.toChars());
6658 goto Lerror;
6660 Lerror:
6661 while (token.value != TOK.rightCurly && token.value != TOK.semicolon && token.value != TOK.endOfFile)
6662 nextToken();
6663 if (token.value == TOK.semicolon)
6664 nextToken();
6665 s = new AST.ErrorStatement;
6666 break;
6668 if (pEndloc)
6669 *pEndloc = prevloc;
6670 return s;
6674 private AST.ExpInitializer parseExpInitializer(Loc loc)
6676 auto ae = parseAssignExp();
6677 return new AST.ExpInitializer(loc, ae);
6680 private AST.Initializer parseStructInitializer(Loc loc)
6682 /* Scan ahead to discern between a struct initializer and
6683 * parameterless function literal.
6685 * We'll scan the topmost curly bracket level for statement-related
6686 * tokens, thereby ruling out a struct initializer. (A struct
6687 * initializer which itself contains function literals may have
6688 * statements at nested curly bracket levels.)
6690 * It's important that this function literal check not be
6691 * pendantic, otherwise a function having the slightest syntax
6692 * error would emit confusing errors when we proceed to parse it
6693 * as a struct initializer.
6695 * The following two ambiguous cases will be treated as a struct
6696 * initializer (best we can do without type info):
6697 * {}
6698 * {{statements...}} - i.e. it could be struct initializer
6699 * with one function literal, or function literal having an
6700 * extra level of curly brackets
6701 * If a function literal is intended in these cases (unlikely),
6702 * source can use a more explicit function literal syntax
6703 * (e.g. prefix with "()" for empty parameter list).
6705 int braces = 1;
6706 int parens = 0;
6707 for (auto t = peek(&token); 1; t = peek(t))
6709 switch (t.value)
6711 case TOK.leftParenthesis:
6712 parens++;
6713 continue;
6714 case TOK.rightParenthesis:
6715 parens--;
6716 continue;
6717 // https://issues.dlang.org/show_bug.cgi?id=21163
6718 // lambda params can have the `scope` storage class, e.g
6719 // `S s = { (scope Type Id){} }`
6720 case TOK.scope_:
6721 if (!parens) goto case;
6722 continue;
6723 /* Look for a semicolon or keyword of statements which don't
6724 * require a semicolon (typically containing BlockStatement).
6725 * Tokens like "else", "catch", etc. are omitted where the
6726 * leading token of the statement is sufficient.
6728 case TOK.asm_:
6729 case TOK.class_:
6730 case TOK.debug_:
6731 case TOK.enum_:
6732 case TOK.if_:
6733 case TOK.interface_:
6734 case TOK.pragma_:
6735 case TOK.semicolon:
6736 case TOK.struct_:
6737 case TOK.switch_:
6738 case TOK.synchronized_:
6739 case TOK.try_:
6740 case TOK.union_:
6741 case TOK.version_:
6742 case TOK.while_:
6743 case TOK.with_:
6744 if (braces == 1)
6745 return parseExpInitializer(loc);
6746 continue;
6748 case TOK.leftCurly:
6749 braces++;
6750 continue;
6752 case TOK.rightCurly:
6753 if (--braces == 0)
6754 break;
6755 continue;
6757 case TOK.endOfFile:
6758 break;
6760 default:
6761 continue;
6763 break;
6766 auto _is = new AST.StructInitializer(loc);
6767 bool commaExpected = false;
6768 nextToken();
6769 while (1)
6771 switch (token.value)
6773 case TOK.identifier:
6776 if (commaExpected)
6777 error("comma expected separating field initializers");
6778 const t = peek(&token);
6779 Identifier id;
6780 if (t.value == TOK.colon)
6782 id = token.ident;
6783 nextToken();
6784 nextToken(); // skip over ':'
6786 auto value = parseInitializer();
6787 _is.addInit(id, value);
6788 commaExpected = true;
6789 continue;
6791 case TOK.comma:
6792 if (!commaExpected)
6793 error("expression expected, not `,`");
6794 nextToken();
6795 commaExpected = false;
6796 continue;
6798 case TOK.rightCurly: // allow trailing comma's
6799 nextToken();
6800 break;
6802 case TOK.endOfFile:
6803 error("found end of file instead of initializer");
6804 break;
6806 default:
6807 if (commaExpected)
6808 error("comma expected separating field initializers");
6809 auto value = parseInitializer();
6810 _is.addInit(null, value);
6811 commaExpected = true;
6812 continue;
6814 break;
6816 return _is;
6820 /*****************************************
6821 * Parse initializer for variable declaration.
6823 private AST.Initializer parseInitializer()
6825 const loc = token.loc;
6827 switch (token.value)
6829 case TOK.leftCurly:
6830 return parseStructInitializer(loc);
6832 case TOK.leftBracket:
6833 /* Scan ahead to see if it is an array initializer or
6834 * an expression.
6835 * If it ends with a ';' ',' or ']', it is an array initializer.
6837 int brackets = 1;
6838 for (auto t = peek(&token); 1; t = peek(t))
6840 switch (t.value)
6842 case TOK.leftBracket:
6843 brackets++;
6844 continue;
6846 case TOK.rightBracket:
6847 if (--brackets == 0)
6849 t = peek(t);
6850 if (t.value != TOK.semicolon && t.value != TOK.comma && t.value != TOK.rightBracket && t.value != TOK.rightCurly)
6851 return parseExpInitializer(loc);
6852 break;
6854 continue;
6856 case TOK.endOfFile:
6857 break;
6859 default:
6860 continue;
6862 break;
6865 auto ia = new AST.ArrayInitializer(loc);
6866 bool commaExpected = false;
6868 nextToken();
6869 while (1)
6871 switch (token.value)
6873 default:
6874 if (commaExpected)
6876 error("comma expected separating array initializers, not `%s`", token.toChars());
6877 nextToken();
6878 break;
6880 auto e = parseAssignExp();
6881 if (!e)
6882 break;
6884 AST.Initializer value;
6885 if (token.value == TOK.colon)
6887 nextToken();
6888 value = parseInitializer();
6890 else
6892 value = new AST.ExpInitializer(e.loc, e);
6893 e = null;
6895 ia.addInit(e, value);
6896 commaExpected = true;
6897 continue;
6899 case TOK.leftCurly:
6900 case TOK.leftBracket:
6901 if (commaExpected)
6902 error("comma expected separating array initializers, not `%s`", token.toChars());
6903 auto value = parseInitializer();
6904 AST.Expression e;
6906 if (token.value == TOK.colon)
6908 nextToken();
6909 if (auto ei = value.isExpInitializer())
6911 e = ei.exp;
6912 value = parseInitializer();
6914 else
6915 error("initializer expression expected following colon, not `%s`", token.toChars());
6917 ia.addInit(e, value);
6918 commaExpected = true;
6919 continue;
6921 case TOK.comma:
6922 if (!commaExpected)
6923 error("expression expected, not `,`");
6924 nextToken();
6925 commaExpected = false;
6926 continue;
6928 case TOK.rightBracket: // allow trailing comma's
6929 nextToken();
6930 break;
6932 case TOK.endOfFile:
6933 error("found `%s` instead of array initializer", token.toChars());
6934 break;
6936 break;
6938 return ia;
6940 case TOK.void_:
6941 const tv = peekNext();
6942 if (tv == TOK.semicolon || tv == TOK.comma)
6944 nextToken();
6945 return new AST.VoidInitializer(loc);
6947 return parseExpInitializer(loc);
6949 default:
6950 return parseExpInitializer(loc);
6954 /********************
6955 * Parse inline assembler block.
6956 * Enters with token on the `asm`.
6957 * https://dlang.org/spec/iasm.html
6959 * AsmStatement:
6960 * asm FunctionAttributes(opt) { AsmInstructionListopt }
6961 * AsmInstructionList:
6962 * AsmInstruction ;
6963 * AsmInstruction ; AsmInstruction
6965 * Params:
6966 * endOfLine = true if EOL means end of asm statement
6967 * Returns:
6968 * inline assembler block as a Statement
6970 AST.Statement parseAsm(bool endOfLine)
6972 // Parse the asm block into a sequence of AsmStatements,
6973 // each AsmStatement is one instruction.
6974 // Separate out labels.
6975 // Defer parsing of AsmStatements until semantic processing.
6977 const loc = token.loc;
6978 Loc labelloc;
6980 nextToken();
6981 StorageClass stc = parsePostfix(STC.undefined_, null); // optional FunctionAttributes
6982 if (stc & (STC.const_ | STC.immutable_ | STC.shared_ | STC.wild))
6983 error("`const`/`immutable`/`shared`/`inout` attributes are not allowed on `asm` blocks");
6985 check(TOK.leftCurly);
6986 Token* toklist = null;
6987 Token** ptoklist = &toklist;
6988 Identifier label = null;
6989 auto statements = new AST.Statements();
6990 size_t nestlevel = 0;
6991 while (1)
6993 if (endOfLine)
6994 nextDefineLine();
6995 switch (token.value)
6997 case TOK.identifier:
6998 if (!toklist)
7000 // Look ahead to see if it is a label
7001 if (peekNext() == TOK.colon)
7003 // It's a label
7004 label = token.ident;
7005 labelloc = token.loc;
7006 nextToken();
7007 nextToken();
7008 continue;
7011 goto default;
7013 case TOK.leftCurly:
7014 ++nestlevel;
7015 goto default;
7017 case TOK.rightCurly:
7018 if (nestlevel > 0)
7020 --nestlevel;
7021 goto default;
7023 if (toklist || label)
7025 error("`asm` statements must end in `;`");
7027 break;
7029 case TOK.endOfLine:
7030 nextDefineLine();
7031 goto case;
7033 case TOK.semicolon:
7034 if (nestlevel != 0)
7035 error("mismatched number of curly brackets");
7037 if (toklist || label)
7039 // Create AsmStatement from list of tokens we've saved
7040 AST.AsmStatement as = new AST.AsmStatement(token.loc, toklist);
7041 as.caseSensitive = !endOfLine;
7042 AST.Statement s = as;
7043 toklist = null;
7044 ptoklist = &toklist;
7045 if (label)
7047 s = new AST.LabelStatement(labelloc, label, s);
7048 label = null;
7050 statements.push(s);
7052 nextToken();
7053 continue;
7055 case TOK.endOfFile:
7056 /* { */
7057 error("matching `}` expected, not end of file");
7058 break;
7060 case TOK.colonColon: // treat as two separate : tokens for iasmgcc
7061 *ptoklist = allocateToken();
7062 memcpy(*ptoklist, &token, Token.sizeof);
7063 (*ptoklist).value = TOK.colon;
7064 ptoklist = &(*ptoklist).next;
7066 *ptoklist = allocateToken();
7067 memcpy(*ptoklist, &token, Token.sizeof);
7068 (*ptoklist).value = TOK.colon;
7069 ptoklist = &(*ptoklist).next;
7071 *ptoklist = null;
7072 nextToken();
7073 continue;
7075 default:
7076 *ptoklist = allocateToken();
7077 memcpy(*ptoklist, &token, Token.sizeof);
7078 ptoklist = &(*ptoklist).next;
7079 *ptoklist = null;
7080 nextToken();
7081 continue;
7083 break;
7085 nextToken();
7086 if (token.value == TOK.endOfLine)
7087 nextToken();
7088 auto s = new AST.CompoundAsmStatement(loc, statements, stc);
7089 return s;
7092 /**********************************
7093 * Issue error if the current token is not `value`,
7094 * advance to next token.
7095 * Params:
7096 * loc = location for error message
7097 * value = token value to compare with
7099 void check(Loc loc, TOK value)
7101 if (token.value != value)
7102 error(loc, "found `%s` when expecting `%s`", token.toChars(), Token.toChars(value));
7103 nextToken();
7106 /**********************************
7107 * Issue error if the current token is not `value`,
7108 * advance to next token.
7109 * Params:
7110 * value = token value to compare with
7112 void check(TOK value)
7114 check(token.loc, value);
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
7122 * string = for error message
7124 void check(TOK value, const(char)* string)
7126 if (token.value != value)
7127 error(token.loc, "found `%s` when expecting `%s` following %s", token.toChars(), Token.toChars(value), string);
7128 nextToken();
7131 private void checkParens(TOK value, AST.Expression e)
7133 if (precedence[e.op] == PREC.rel)
7134 error(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`", e.toChars(), Token.toChars(value));
7138 enum NeedDeclaratorId
7140 no, // Declarator part must have no identifier
7141 opt, // Declarator part identifier is optional
7142 must, // Declarator part must have identifier
7143 mustIfDstyle, // Declarator part must have identifier, but don't recognize old C-style syntax
7146 /************************************
7147 * Determine if the scanner is sitting on the start of a declaration.
7148 * Params:
7149 * t = current token of the scanner
7150 * needId = flag with additional requirements for a declaration
7151 * endtok = ending token
7152 * pt = will be set ending token (if not null)
7153 * Output:
7154 * true if the token `t` is a declaration, false otherwise
7156 private bool isDeclaration(Token* t, NeedDeclaratorId needId, TOK endtok, Token** pt)
7158 //printf("isDeclaration(needId = %d)\n", needId);
7159 int haveId = 0;
7160 int haveTpl = 0;
7162 while (1)
7164 if ((t.value == TOK.const_ || t.value == TOK.immutable_ || t.value == TOK.inout_ || t.value == TOK.shared_) && peek(t).value != TOK.leftParenthesis)
7166 /* const type
7167 * immutable type
7168 * shared type
7169 * wild type
7171 t = peek(t);
7172 continue;
7174 break;
7177 if (!isBasicType(&t))
7179 goto Lisnot;
7181 if (!isDeclarator(&t, &haveId, &haveTpl, endtok, needId != NeedDeclaratorId.mustIfDstyle))
7182 goto Lisnot;
7183 if ((needId == NeedDeclaratorId.no && !haveId) ||
7184 (needId == NeedDeclaratorId.opt) ||
7185 (needId == NeedDeclaratorId.must && haveId) ||
7186 (needId == NeedDeclaratorId.mustIfDstyle && haveId))
7188 if (pt)
7189 *pt = t;
7190 goto Lis;
7192 goto Lisnot;
7194 Lis:
7195 //printf("\tis declaration, t = %s\n", t.toChars());
7196 return true;
7198 Lisnot:
7199 //printf("\tis not declaration\n");
7200 return false;
7203 // pt = test token. If found, pt is set to the token after BasicType
7204 private bool isBasicType(Token** pt)
7206 // This code parallels parseBasicType()
7207 Token* t = *pt;
7208 switch (t.value)
7210 case TOK.wchar_:
7211 case TOK.dchar_:
7212 case TOK.bool_:
7213 case TOK.char_:
7214 case TOK.int8:
7215 case TOK.uns8:
7216 case TOK.int16:
7217 case TOK.uns16:
7218 case TOK.int32:
7219 case TOK.uns32:
7220 case TOK.int64:
7221 case TOK.uns64:
7222 case TOK.int128:
7223 case TOK.uns128:
7224 case TOK.float32:
7225 case TOK.float64:
7226 case TOK.float80:
7227 case TOK.imaginary32:
7228 case TOK.imaginary64:
7229 case TOK.imaginary80:
7230 case TOK.complex32:
7231 case TOK.complex64:
7232 case TOK.complex80:
7233 case TOK.void_:
7234 t = peek(t);
7235 break;
7237 case TOK.identifier:
7239 t = peek(t);
7240 if (t.value == TOK.not)
7242 goto L4;
7244 goto L3;
7245 while (1)
7248 t = peek(t);
7250 if (t.value == TOK.dot)
7252 Ldot:
7253 t = peek(t);
7254 if (t.value != TOK.identifier)
7255 goto Lfalse;
7256 t = peek(t);
7257 if (t.value != TOK.not)
7258 goto L3;
7260 /* Seen a !
7261 * Look for:
7262 * !( args ), !identifier, etc.
7264 t = peek(t);
7265 switch (t.value)
7267 case TOK.identifier:
7268 goto L5;
7270 case TOK.leftParenthesis:
7271 if (!skipParens(t, &t))
7272 goto Lfalse;
7273 goto L3;
7275 case TOK.wchar_:
7276 case TOK.dchar_:
7277 case TOK.bool_:
7278 case TOK.char_:
7279 case TOK.int8:
7280 case TOK.uns8:
7281 case TOK.int16:
7282 case TOK.uns16:
7283 case TOK.int32:
7284 case TOK.uns32:
7285 case TOK.int64:
7286 case TOK.uns64:
7287 case TOK.int128:
7288 case TOK.uns128:
7289 case TOK.float32:
7290 case TOK.float64:
7291 case TOK.float80:
7292 case TOK.imaginary32:
7293 case TOK.imaginary64:
7294 case TOK.imaginary80:
7295 case TOK.complex32:
7296 case TOK.complex64:
7297 case TOK.complex80:
7298 case TOK.void_:
7299 case TOK.int32Literal:
7300 case TOK.uns32Literal:
7301 case TOK.int64Literal:
7302 case TOK.uns64Literal:
7303 case TOK.int128Literal:
7304 case TOK.uns128Literal:
7305 case TOK.float32Literal:
7306 case TOK.float64Literal:
7307 case TOK.float80Literal:
7308 case TOK.imaginary32Literal:
7309 case TOK.imaginary64Literal:
7310 case TOK.imaginary80Literal:
7311 case TOK.null_:
7312 case TOK.true_:
7313 case TOK.false_:
7314 case TOK.charLiteral:
7315 case TOK.wcharLiteral:
7316 case TOK.dcharLiteral:
7317 case TOK.string_:
7318 case TOK.interpolated:
7319 case TOK.hexadecimalString:
7320 case TOK.file:
7321 case TOK.fileFullPath:
7322 case TOK.line:
7323 case TOK.moduleString:
7324 case TOK.functionString:
7325 case TOK.prettyFunction:
7326 goto L2;
7328 default:
7329 goto Lfalse;
7332 break;
7334 break;
7336 case TOK.dot:
7337 goto Ldot;
7339 case TOK.typeof_:
7340 case TOK.vector:
7341 case TOK.mixin_:
7342 /* typeof(exp).identifier...
7344 t = peek(t);
7345 if (!skipParens(t, &t))
7346 goto Lfalse;
7347 goto L3;
7349 case TOK.traits:
7350 // __traits(getMember
7351 t = peek(t);
7352 if (t.value != TOK.leftParenthesis)
7353 goto Lfalse;
7354 auto lp = t;
7355 t = peek(t);
7356 if (t.value != TOK.identifier || t.ident != Id.getMember)
7357 goto Lfalse;
7358 if (!skipParens(lp, &lp))
7359 goto Lfalse;
7360 // we are in a lookup for decl VS statement
7361 // so we expect a declarator following __trait if it's a type.
7362 // other usages wont be ambiguous (alias, template instance, type qual, etc.)
7363 if (lp.value != TOK.identifier)
7364 goto Lfalse;
7366 break;
7368 case TOK.const_:
7369 case TOK.immutable_:
7370 case TOK.shared_:
7371 case TOK.inout_:
7372 // const(type) or immutable(type) or shared(type) or wild(type)
7373 t = peek(t);
7374 if (t.value != TOK.leftParenthesis)
7375 goto Lfalse;
7376 t = peek(t);
7377 if (!isDeclaration(t, NeedDeclaratorId.no, TOK.rightParenthesis, &t))
7379 goto Lfalse;
7381 t = peek(t);
7382 break;
7384 default:
7385 goto Lfalse;
7387 *pt = t;
7388 //printf("is\n");
7389 return true;
7391 Lfalse:
7392 //printf("is not\n");
7393 return false;
7396 private bool isDeclarator(Token** pt, int* haveId, int* haveTpl, TOK endtok, bool allowAltSyntax = true)
7398 // This code parallels parseDeclarator()
7399 Token* t = *pt;
7400 bool parens;
7402 //printf("Parser::isDeclarator() %s\n", t.toChars());
7403 if (t.value == TOK.assign)
7404 return false;
7406 while (1)
7408 parens = false;
7409 switch (t.value)
7411 case TOK.mul:
7412 //case TOK.and:
7413 t = peek(t);
7414 continue;
7416 case TOK.leftBracket:
7417 t = peek(t);
7418 if (t.value == TOK.rightBracket)
7420 t = peek(t);
7422 else if (isDeclaration(t, NeedDeclaratorId.no, TOK.rightBracket, &t))
7424 // It's an associative array declaration
7425 t = peek(t);
7427 // ...[type].ident
7428 if (t.value == TOK.dot && peek(t).value == TOK.identifier)
7430 t = peek(t);
7431 t = peek(t);
7434 else
7436 // [ expression ]
7437 // [ expression .. expression ]
7438 if (!isExpression(&t))
7439 return false;
7440 if (t.value == TOK.slice)
7442 t = peek(t);
7443 if (!isExpression(&t))
7444 return false;
7445 if (t.value != TOK.rightBracket)
7446 return false;
7447 t = peek(t);
7449 else
7451 if (t.value != TOK.rightBracket)
7452 return false;
7453 t = peek(t);
7454 // ...[index].ident
7455 if (t.value == TOK.dot && peek(t).value == TOK.identifier)
7457 t = peek(t);
7458 t = peek(t);
7462 continue;
7464 case TOK.identifier:
7465 if (*haveId)
7466 return false;
7467 *haveId = true;
7468 t = peek(t);
7469 break;
7471 case TOK.leftParenthesis:
7472 if (!allowAltSyntax)
7473 return false; // Do not recognize C-style declarations.
7475 t = peek(t);
7476 if (t.value == TOK.rightParenthesis)
7477 return false; // () is not a declarator
7479 /* Regard ( identifier ) as not a declarator
7480 * BUG: what about ( *identifier ) in
7481 * f(*p)(x);
7482 * where f is a class instance with overloaded () ?
7483 * Should we just disallow C-style function pointer declarations?
7485 if (t.value == TOK.identifier)
7487 Token* t2 = peek(t);
7488 if (t2.value == TOK.rightParenthesis)
7489 return false;
7492 if (!isDeclarator(&t, haveId, null, TOK.rightParenthesis))
7493 return false;
7494 t = peek(t);
7495 parens = true;
7496 break;
7498 case TOK.delegate_:
7499 case TOK.function_:
7500 t = peek(t);
7501 if (!isParameters(&t))
7502 return false;
7503 skipAttributes(t, &t);
7504 continue;
7506 default:
7507 break;
7509 break;
7512 while (1)
7514 switch (t.value)
7516 static if (CARRAYDECL)
7518 case TOK.leftBracket:
7519 parens = false;
7520 t = peek(t);
7521 if (t.value == TOK.rightBracket)
7523 t = peek(t);
7525 else if (isDeclaration(t, NeedDeclaratorId.no, TOK.rightBracket, &t))
7527 // It's an associative array declaration
7528 t = peek(t);
7530 else
7532 // [ expression ]
7533 if (!isExpression(&t))
7534 return false;
7535 if (t.value != TOK.rightBracket)
7536 return false;
7537 t = peek(t);
7539 continue;
7542 case TOK.leftParenthesis:
7543 parens = false;
7544 if (Token* tk = peekPastParen(t))
7546 if (tk.value == TOK.leftParenthesis)
7548 if (!haveTpl)
7549 return false;
7550 *haveTpl = 1;
7551 t = tk;
7553 else if (tk.value == TOK.assign)
7555 if (!haveTpl)
7556 return false;
7557 *haveTpl = 1;
7558 *pt = tk;
7559 return true;
7562 if (!isParameters(&t))
7563 return false;
7564 while (1)
7566 switch (t.value)
7568 case TOK.const_:
7569 case TOK.immutable_:
7570 case TOK.shared_:
7571 case TOK.inout_:
7572 case TOK.pure_:
7573 case TOK.nothrow_:
7574 case TOK.return_:
7575 case TOK.scope_:
7576 t = peek(t);
7577 continue;
7579 case TOK.at:
7580 t = peek(t); // skip '@'
7581 t = peek(t); // skip identifier
7582 continue;
7584 default:
7585 break;
7587 break;
7589 continue;
7591 // Valid tokens that follow the start of a declaration
7592 case TOK.rightParenthesis:
7593 case TOK.rightBracket:
7594 case TOK.assign:
7595 case TOK.comma:
7596 case TOK.dotDotDot:
7597 case TOK.semicolon:
7598 case TOK.leftCurly:
7599 case TOK.in_:
7600 case TOK.out_:
7601 case TOK.do_:
7602 // The !parens is to disallow unnecessary parentheses
7603 if (!parens && (endtok == TOK.reserved || endtok == t.value))
7605 *pt = t;
7606 return true;
7608 return false;
7610 // To recognize the shortened function declaration syntax
7611 case TOK.goesTo:
7613 1. https://issues.dlang.org/show_bug.cgi?id=24088
7615 2. We need to make sure the would-be
7616 declarator has an identifier otherwise function literals
7617 are handled incorrectly. Some special treatment is required
7618 here, it turns out that a lot of code in the compiler relies
7619 on this mess (in the parser), i.e. having isDeclarator be more
7620 precise the parsing of other things go kaboom, so we do it in a
7621 separate case.
7623 if (*haveId)
7624 goto case TOK.do_;
7625 goto default;
7627 case TOK.identifier:
7628 if (t.ident == Id._body)
7630 usageOfBodyKeyword();
7631 goto case TOK.do_;
7633 goto default;
7635 case TOK.if_:
7636 return haveTpl ? true : false;
7638 // Used for mixin type parsing
7639 case TOK.endOfFile:
7640 if (endtok == TOK.endOfFile)
7641 goto case TOK.do_;
7642 return false;
7644 default:
7645 return false;
7648 assert(0);
7651 private bool isParameters(Token** pt)
7653 // This code parallels parseParameterList()
7654 Token* t = *pt;
7656 //printf("isParameters()\n");
7657 if (t.value != TOK.leftParenthesis)
7658 return false;
7660 t = peek(t);
7661 for (; 1; t = peek(t))
7664 switch (t.value)
7666 case TOK.rightParenthesis:
7667 break;
7669 case TOK.at:
7670 Token* pastAttr;
7671 if (skipAttributes(t, &pastAttr))
7673 t = pastAttr;
7674 goto default;
7676 break;
7678 case TOK.dotDotDot:
7679 t = peek(t);
7680 break;
7682 case TOK.in_:
7683 case TOK.out_:
7684 case TOK.ref_:
7685 case TOK.lazy_:
7686 case TOK.scope_:
7687 case TOK.final_:
7688 case TOK.auto_:
7689 case TOK.return_:
7690 continue;
7692 case TOK.const_:
7693 case TOK.immutable_:
7694 case TOK.shared_:
7695 case TOK.inout_:
7696 t = peek(t);
7697 if (t.value == TOK.leftParenthesis)
7699 t = peek(t);
7700 if (!isDeclaration(t, NeedDeclaratorId.no, TOK.rightParenthesis, &t))
7701 return false;
7702 t = peek(t); // skip past closing ')'
7703 goto L2;
7705 goto L1;
7707 default:
7709 if (!isBasicType(&t))
7710 return false;
7712 int tmp = false;
7713 if (t.value != TOK.dotDotDot && !isDeclarator(&t, &tmp, null, TOK.reserved))
7714 return false;
7715 if (t.value == TOK.assign)
7717 t = peek(t);
7718 if (!isExpression(&t))
7719 return false;
7721 if (t.value == TOK.dotDotDot)
7723 t = peek(t);
7724 break;
7727 if (t.value == TOK.comma)
7729 continue;
7731 break;
7733 break;
7735 if (t.value != TOK.rightParenthesis)
7736 return false;
7737 t = peek(t);
7738 *pt = t;
7739 return true;
7742 private bool isExpression(Token** pt)
7744 // This is supposed to determine if something is an expression.
7745 // What it actually does is scan until a closing right bracket
7746 // is found.
7748 Token* t = *pt;
7749 int brnest = 0;
7750 int panest = 0;
7751 int curlynest = 0;
7753 for (;; t = peek(t))
7755 switch (t.value)
7757 case TOK.leftBracket:
7758 brnest++;
7759 continue;
7761 case TOK.rightBracket:
7762 if (--brnest >= 0)
7763 continue;
7764 break;
7766 case TOK.leftParenthesis:
7767 panest++;
7768 continue;
7770 case TOK.comma:
7771 if (brnest || panest)
7772 continue;
7773 break;
7775 case TOK.rightParenthesis:
7776 if (--panest >= 0)
7777 continue;
7778 break;
7780 case TOK.leftCurly:
7781 curlynest++;
7782 continue;
7784 case TOK.rightCurly:
7785 if (--curlynest >= 0)
7786 continue;
7787 return false;
7789 case TOK.slice:
7790 if (brnest)
7791 continue;
7792 break;
7794 case TOK.semicolon:
7795 if (curlynest)
7796 continue;
7797 return false;
7799 case TOK.endOfFile:
7800 return false;
7802 default:
7803 continue;
7805 break;
7808 *pt = t;
7809 return true;
7812 /*******************************************
7813 * Skip parentheses.
7814 * Params:
7815 * t = on opening $(LPAREN)
7816 * pt = *pt is set to token past '$(RPAREN)' on true
7817 * Returns:
7818 * true successful
7819 * false some parsing error
7821 bool skipParens(Token* t, Token** pt)
7823 if (t.value != TOK.leftParenthesis)
7824 return false;
7826 int parens = 0;
7828 while (1)
7830 switch (t.value)
7832 case TOK.leftParenthesis:
7833 parens++;
7834 break;
7836 case TOK.rightParenthesis:
7837 parens--;
7838 if (parens < 0)
7839 goto Lfalse;
7840 if (parens == 0)
7841 goto Ldone;
7842 break;
7844 case TOK.endOfFile:
7845 goto Lfalse;
7847 default:
7848 break;
7850 t = peek(t);
7852 Ldone:
7853 if (pt)
7854 *pt = peek(t); // skip found rparen
7855 return true;
7857 Lfalse:
7858 return false;
7861 private bool skipParensIf(Token* t, Token** pt)
7863 if (t.value != TOK.leftParenthesis)
7865 if (pt)
7866 *pt = t;
7867 return true;
7869 return skipParens(t, pt);
7872 //returns true if the next value (after optional matching parens) is expected
7873 private bool hasOptionalParensThen(Token* t, TOK expected)
7875 Token* tk;
7876 if (!skipParensIf(t, &tk))
7877 return false;
7878 return tk.value == expected;
7881 /*******************************************
7882 * Skip attributes.
7883 * Input:
7884 * t is on a candidate attribute
7885 * Output:
7886 * *pt is set to first non-attribute token on success
7887 * Returns:
7888 * true successful
7889 * false some parsing error
7891 private bool skipAttributes(Token* t, Token** pt)
7893 while (1)
7895 switch (t.value)
7897 case TOK.const_:
7898 case TOK.immutable_:
7899 case TOK.shared_:
7900 case TOK.inout_:
7901 case TOK.final_:
7902 case TOK.auto_:
7903 case TOK.scope_:
7904 case TOK.override_:
7905 case TOK.abstract_:
7906 case TOK.synchronized_:
7907 break;
7909 case TOK.deprecated_:
7910 if (peek(t).value == TOK.leftParenthesis)
7912 t = peek(t);
7913 if (!skipParens(t, &t))
7914 goto Lerror;
7915 // t is on the next of closing parenthesis
7916 continue;
7918 break;
7920 case TOK.nothrow_:
7921 case TOK.pure_:
7922 case TOK.ref_:
7923 case TOK.gshared:
7924 case TOK.return_:
7925 break;
7927 case TOK.at:
7928 t = peek(t);
7929 if (t.value == TOK.identifier)
7931 /* @identifier
7932 * @identifier!arg
7933 * @identifier!(arglist)
7934 * any of the above followed by (arglist)
7935 * @predefined_attribute
7937 if (isBuiltinAtAttribute(t.ident))
7938 break;
7939 t = peek(t);
7940 if (t.value == TOK.not)
7942 t = peek(t);
7943 if (t.value == TOK.leftParenthesis)
7945 // @identifier!(arglist)
7946 if (!skipParens(t, &t))
7947 goto Lerror;
7948 // t is on the next of closing parenthesis
7950 else
7952 // @identifier!arg
7953 // Do low rent skipTemplateArgument
7954 if (t.value == TOK.vector)
7956 // identifier!__vector(type)
7957 t = peek(t);
7958 if (!skipParens(t, &t))
7959 goto Lerror;
7961 else
7962 t = peek(t);
7965 if (t.value == TOK.leftParenthesis)
7967 if (!skipParens(t, &t))
7968 goto Lerror;
7969 // t is on the next of closing parenthesis
7970 continue;
7972 continue;
7974 if (t.value == TOK.leftParenthesis)
7976 // @( ArgumentList )
7977 if (!skipParens(t, &t))
7978 goto Lerror;
7979 // t is on the next of closing parenthesis
7980 continue;
7982 goto Lerror;
7984 default:
7985 goto Ldone;
7987 t = peek(t);
7989 Ldone:
7990 if (pt)
7991 *pt = t;
7992 return true;
7994 Lerror:
7995 return false;
7998 AST.Expression parseExpression()
8000 auto loc = token.loc;
8002 //printf("Parser::parseExpression() loc = %d\n", loc.linnum);
8003 auto e = parseAssignExp();
8004 while (token.value == TOK.comma)
8006 nextToken();
8007 auto e2 = parseAssignExp();
8008 e = new AST.CommaExp(loc, e, e2, false);
8009 loc = token.loc;
8011 return e;
8014 /********************************* Expression Parser ***************************/
8016 AST.Expression parsePrimaryExp()
8018 AST.Expression e;
8019 AST.Type t;
8020 Identifier id;
8021 const loc = token.loc;
8023 //printf("parsePrimaryExp(): loc = %d\n", loc.linnum);
8024 switch (token.value)
8026 case TOK.identifier:
8028 if (peekNext() == TOK.arrow)
8030 // skip `identifier ->`
8031 nextToken();
8032 nextToken();
8033 error("use `.` for member lookup, not `->`");
8034 goto Lerr;
8037 if (peekNext() == TOK.goesTo)
8038 goto case_delegate;
8040 id = token.ident;
8041 nextToken();
8042 TOK save;
8043 if (token.value == TOK.not && (save = peekNext()) != TOK.is_ && save != TOK.in_)
8045 // identifier!(template-argument-list)
8046 auto tempinst = new AST.TemplateInstance(loc, id, parseTemplateArguments());
8047 e = new AST.ScopeExp(loc, tempinst);
8049 else
8050 e = new AST.IdentifierExp(loc, id);
8051 break;
8053 case TOK.dollar:
8054 if (!inBrackets)
8055 error("`$` is valid only inside [] of index or slice");
8056 e = new AST.DollarExp(loc);
8057 nextToken();
8058 break;
8060 case TOK.dot:
8061 // Signal global scope '.' operator with "" identifier
8062 e = new AST.IdentifierExp(loc, Id.empty);
8063 break;
8065 case TOK.this_:
8066 e = new AST.ThisExp(loc);
8067 nextToken();
8068 break;
8070 case TOK.super_:
8071 e = new AST.SuperExp(loc);
8072 nextToken();
8073 break;
8075 case TOK.int32Literal:
8076 e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint32);
8077 nextToken();
8078 break;
8080 case TOK.uns32Literal:
8081 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns32);
8082 nextToken();
8083 break;
8085 case TOK.int64Literal:
8086 e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint64);
8087 nextToken();
8088 break;
8090 case TOK.uns64Literal:
8091 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns64);
8092 nextToken();
8093 break;
8095 case TOK.float32Literal:
8096 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat32);
8097 nextToken();
8098 break;
8100 case TOK.float64Literal:
8101 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat64);
8102 nextToken();
8103 break;
8105 case TOK.float80Literal:
8106 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat80);
8107 nextToken();
8108 break;
8110 case TOK.imaginary32Literal:
8111 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary32);
8112 nextToken();
8113 break;
8115 case TOK.imaginary64Literal:
8116 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary64);
8117 nextToken();
8118 break;
8120 case TOK.imaginary80Literal:
8121 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary80);
8122 nextToken();
8123 break;
8125 case TOK.null_:
8126 e = new AST.NullExp(loc);
8127 nextToken();
8128 break;
8130 case TOK.file:
8131 e = new AST.FileInitExp(loc, EXP.file);
8132 nextToken();
8133 break;
8134 case TOK.fileFullPath:
8135 e = new AST.FileInitExp(loc, EXP.fileFullPath);
8136 nextToken();
8137 break;
8139 case TOK.line:
8140 e = new AST.LineInitExp(loc);
8141 nextToken();
8142 break;
8144 case TOK.moduleString:
8145 e = new AST.ModuleInitExp(loc);
8146 nextToken();
8147 break;
8148 case TOK.functionString:
8149 e = new AST.FuncInitExp(loc);
8150 nextToken();
8151 break;
8153 case TOK.prettyFunction:
8154 e = new AST.PrettyFuncInitExp(loc);
8155 nextToken();
8156 break;
8158 case TOK.true_:
8159 e = new AST.IntegerExp(loc, 1, AST.Type.tbool);
8160 nextToken();
8161 break;
8163 case TOK.false_:
8164 e = new AST.IntegerExp(loc, 0, AST.Type.tbool);
8165 nextToken();
8166 break;
8168 case TOK.charLiteral:
8169 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tchar);
8170 nextToken();
8171 break;
8173 case TOK.wcharLiteral:
8174 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.twchar);
8175 nextToken();
8176 break;
8178 case TOK.dcharLiteral:
8179 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tdchar);
8180 nextToken();
8181 break;
8183 case TOK.interpolated:
8184 e = new AST.InterpExp(loc, token.interpolatedSet, token.postfix);
8185 nextToken();
8186 break;
8188 case TOK.string_:
8189 case TOK.hexadecimalString:
8190 const bool hexString = token.value == TOK.hexadecimalString;
8192 // cat adjacent strings
8193 auto s = token.ustring;
8194 auto len = token.len;
8195 auto postfix = token.postfix;
8196 while (1)
8198 const prev = token;
8199 nextToken();
8200 if (token.value == TOK.string_ || token.value == TOK.hexadecimalString)
8202 if (token.postfix)
8204 if (token.postfix != postfix)
8205 error(token.loc, "mismatched string literal postfixes `'%c'` and `'%c'`", postfix, token.postfix);
8206 postfix = token.postfix;
8209 error("implicit string concatenation is error-prone and disallowed in D");
8210 eSink.errorSupplemental(token.loc, "Use the explicit syntax instead " ~
8211 "(concatenating literals is `@nogc`): %s ~ %s",
8212 prev.toChars(), token.toChars());
8214 const len1 = len;
8215 const len2 = token.len;
8216 len = len1 + len2;
8217 auto s2 = cast(char*)mem.xmalloc_noscan(len * char.sizeof);
8218 memcpy(s2, s, len1 * char.sizeof);
8219 memcpy(s2 + len1, token.ustring, len2 * char.sizeof);
8220 s = s2;
8222 else
8223 break;
8225 e = new AST.StringExp(loc, s[0 .. len], len, 1, postfix);
8226 e.isStringExp().hexString = hexString;
8227 break;
8229 case TOK.void_:
8230 t = AST.Type.tvoid;
8231 goto LabelX;
8233 case TOK.int8:
8234 t = AST.Type.tint8;
8235 goto LabelX;
8237 case TOK.uns8:
8238 t = AST.Type.tuns8;
8239 goto LabelX;
8241 case TOK.int16:
8242 t = AST.Type.tint16;
8243 goto LabelX;
8245 case TOK.uns16:
8246 t = AST.Type.tuns16;
8247 goto LabelX;
8249 case TOK.int32:
8250 t = AST.Type.tint32;
8251 goto LabelX;
8253 case TOK.uns32:
8254 t = AST.Type.tuns32;
8255 goto LabelX;
8257 case TOK.int64:
8258 t = AST.Type.tint64;
8259 goto LabelX;
8261 case TOK.uns64:
8262 t = AST.Type.tuns64;
8263 goto LabelX;
8265 case TOK.int128:
8266 t = AST.Type.tint128;
8267 goto LabelX;
8269 case TOK.uns128:
8270 t = AST.Type.tuns128;
8271 goto LabelX;
8273 case TOK.float32:
8274 t = AST.Type.tfloat32;
8275 goto LabelX;
8277 case TOK.float64:
8278 t = AST.Type.tfloat64;
8279 goto LabelX;
8281 case TOK.float80:
8282 t = AST.Type.tfloat80;
8283 goto LabelX;
8285 case TOK.imaginary32:
8286 t = AST.Type.timaginary32;
8287 goto LabelX;
8289 case TOK.imaginary64:
8290 t = AST.Type.timaginary64;
8291 goto LabelX;
8293 case TOK.imaginary80:
8294 t = AST.Type.timaginary80;
8295 goto LabelX;
8297 case TOK.complex32:
8298 t = AST.Type.tcomplex32;
8299 goto LabelX;
8301 case TOK.complex64:
8302 t = AST.Type.tcomplex64;
8303 goto LabelX;
8305 case TOK.complex80:
8306 t = AST.Type.tcomplex80;
8307 goto LabelX;
8309 case TOK.bool_:
8310 t = AST.Type.tbool;
8311 goto LabelX;
8313 case TOK.char_:
8314 t = AST.Type.tchar;
8315 goto LabelX;
8317 case TOK.wchar_:
8318 t = AST.Type.twchar;
8319 goto LabelX;
8321 case TOK.dchar_:
8322 t = AST.Type.tdchar;
8323 goto LabelX;
8324 LabelX:
8325 const next = peekNext();
8326 if (next != TOK.leftParenthesis && next != TOK.dot)
8328 // defer error for better diagnostics
8329 e = new AST.TypeExp(loc, parseType);
8330 break;
8332 nextToken();
8333 if (token.value == TOK.leftParenthesis)
8335 e = new AST.TypeExp(loc, t);
8336 e = new AST.CallExp(loc, e, parseArguments());
8337 break;
8339 check(TOK.dot);
8340 if (token.value != TOK.identifier)
8342 error(token.loc, "found `%s` when expecting identifier following `%s`.", token.toChars(), t.toChars());
8343 goto Lerr;
8345 e = new AST.DotIdExp(loc, new AST.TypeExp(loc, t), token.ident);
8346 nextToken();
8347 break;
8349 case TOK.typeof_:
8351 t = parseTypeof();
8352 e = new AST.TypeExp(loc, t);
8353 break;
8355 case TOK.vector:
8357 t = parseVector();
8358 e = new AST.TypeExp(loc, t);
8359 break;
8361 case TOK.typeid_:
8363 nextToken();
8364 check(TOK.leftParenthesis, "`typeid`");
8365 RootObject o = parseTypeOrAssignExp();
8366 check(TOK.rightParenthesis);
8367 e = new AST.TypeidExp(loc, o);
8368 break;
8370 case TOK.traits:
8372 /* __traits(identifier, args...)
8374 Identifier ident;
8375 AST.Objects* args = null;
8377 nextToken();
8378 check(TOK.leftParenthesis);
8379 if (token.value != TOK.identifier)
8381 error("`__traits(identifier, args...)` expected");
8382 goto Lerr;
8384 ident = token.ident;
8385 nextToken();
8386 if (token.value == TOK.comma)
8387 args = parseTemplateArgumentList(); // __traits(identifier, args...)
8388 else
8389 check(TOK.rightParenthesis); // __traits(identifier)
8391 e = new AST.TraitsExp(loc, ident, args);
8392 break;
8394 case TOK.is_:
8396 AST.Type targ;
8397 Identifier ident = null;
8398 AST.Type tspec = null;
8399 TOK tok = TOK.reserved;
8400 TOK tok2 = TOK.reserved;
8401 AST.TemplateParameters* tpl = null;
8403 nextToken();
8404 if (token.value != TOK.leftParenthesis)
8406 error("expected `(` following `is`, not `%s`", token.toChars());
8407 goto Lerr;
8409 else
8411 nextToken();
8412 if (token.value == TOK.identifier && peekNext() == TOK.leftParenthesis)
8414 error(loc, "unexpected `(` after `%s`, inside `is` expression. Try enclosing the contents of `is` with a `typeof` expression", token.toChars());
8415 nextToken();
8416 Token* tempTok = peekPastParen(&token);
8417 memcpy(&token, tempTok, Token.sizeof);
8418 goto Lerr;
8420 targ = parseType(&ident);
8421 if (token.value == TOK.colon || token.value == TOK.equal)
8423 tok = token.value;
8424 nextToken();
8425 if (tok == TOK.equal && (token.value == TOK.struct_ || token.value == TOK.union_
8426 || token.value == TOK.class_ || token.value == TOK.super_ || token.value == TOK.enum_
8427 || token.value == TOK.interface_ || token.value == TOK.package_ || token.value == TOK.module_
8428 || token.value == TOK.argumentTypes || token.value == TOK.parameters
8429 || token.value == TOK.const_ && peekNext() == TOK.rightParenthesis
8430 || token.value == TOK.immutable_ && peekNext() == TOK.rightParenthesis
8431 || token.value == TOK.shared_ && peekNext() == TOK.rightParenthesis
8432 || token.value == TOK.inout_ && peekNext() == TOK.rightParenthesis || token.value == TOK.function_
8433 || token.value == TOK.delegate_ || token.value == TOK.return_
8434 || (token.value == TOK.vector && peekNext() == TOK.rightParenthesis)))
8436 tok2 = token.value;
8437 nextToken();
8439 else
8441 tspec = parseType();
8444 if (tspec)
8446 if (token.value == TOK.comma)
8447 tpl = parseTemplateParameterList(1);
8448 else
8450 tpl = new AST.TemplateParameters();
8451 check(TOK.rightParenthesis);
8454 else
8455 check(TOK.rightParenthesis);
8457 e = new AST.IsExp(loc, targ, ident, tok, tspec, tok2, tpl);
8458 break;
8460 case TOK.assert_:
8462 // https://dlang.org/spec/expression.html#assert_expressions
8463 AST.Expression msg = null;
8465 nextToken();
8466 check(TOK.leftParenthesis, "`assert`");
8467 e = parseAssignExp();
8468 if (token.value == TOK.comma)
8470 nextToken();
8471 if (token.value != TOK.rightParenthesis)
8473 msg = parseAssignExp();
8474 if (token.value == TOK.comma)
8475 nextToken();
8478 check(TOK.rightParenthesis);
8479 e = new AST.AssertExp(loc, e, msg);
8480 break;
8482 case TOK.mixin_:
8484 // https://dlang.org/spec/expression.html#mixin_expressions
8485 nextToken();
8486 if (token.value != TOK.leftParenthesis)
8487 error(token.loc, "found `%s` when expecting `%s` following `mixin`", token.toChars(), Token.toChars(TOK.leftParenthesis));
8488 auto exps = parseArguments();
8489 e = new AST.MixinExp(loc, exps);
8490 break;
8492 case TOK.import_:
8494 nextToken();
8495 check(TOK.leftParenthesis, "`import`");
8496 e = parseAssignExp();
8497 check(TOK.rightParenthesis);
8498 e = new AST.ImportExp(loc, e);
8499 break;
8501 case TOK.new_:
8502 e = parseNewExp(null);
8503 break;
8505 case TOK.auto_:
8507 if (peekNext() == TOK.ref_ && peekNext2() == TOK.leftParenthesis)
8509 Token* tk = peekPastParen(peek(peek(&token)));
8510 if (skipAttributes(tk, &tk) && (tk.value == TOK.goesTo || tk.value == TOK.leftCurly))
8512 // auto ref (arguments) => expression
8513 // auto ref (arguments) { statements... }
8514 goto case_delegate;
8517 nextToken();
8518 error("found `%s` when expecting `ref` and function literal following `auto`", token.toChars());
8519 goto Lerr;
8521 case TOK.ref_:
8523 if (peekNext() == TOK.leftParenthesis)
8525 Token* tk = peekPastParen(peek(&token));
8526 if (skipAttributes(tk, &tk) && (tk.value == TOK.goesTo || tk.value == TOK.leftCurly))
8528 // ref (arguments) => expression
8529 // ref (arguments) { statements... }
8530 goto case_delegate;
8533 nextToken();
8534 error("found `%s` when expecting function literal following `ref`", token.toChars());
8535 goto Lerr;
8537 case TOK.leftParenthesis:
8539 Token* tk = peekPastParen(&token);
8540 if (skipAttributes(tk, &tk) && (tk.value == TOK.goesTo || tk.value == TOK.leftCurly))
8542 // (arguments) => expression
8543 // (arguments) { statements... }
8544 goto case_delegate;
8547 // ( expression )
8548 nextToken();
8549 e = parseExpression();
8550 check(loc, TOK.rightParenthesis);
8551 break;
8553 case TOK.leftBracket:
8555 /* Parse array literals and associative array literals:
8556 * [ value, value, value ... ]
8557 * [ key:value, key:value, key:value ... ]
8559 auto values = new AST.Expressions();
8560 AST.Expressions* keys = null;
8562 nextToken();
8563 while (token.value != TOK.rightBracket && token.value != TOK.endOfFile)
8565 e = parseAssignExp();
8566 if (token.value == TOK.colon && (keys || values.length == 0))
8568 nextToken();
8569 if (!keys)
8570 keys = new AST.Expressions();
8571 keys.push(e);
8572 e = parseAssignExp();
8574 else if (keys)
8576 error("`key:value` expected for associative array literal");
8577 keys = null;
8579 values.push(e);
8580 if (token.value == TOK.rightBracket)
8581 break;
8582 check(TOK.comma);
8584 check(loc, TOK.rightBracket);
8586 if (keys)
8587 e = new AST.AssocArrayLiteralExp(loc, keys, values);
8588 else
8589 e = new AST.ArrayLiteralExp(loc, null, values);
8590 break;
8592 case TOK.leftCurly:
8593 case TOK.function_:
8594 case TOK.delegate_:
8595 case_delegate:
8597 AST.Dsymbol s = parseFunctionLiteral();
8598 e = new AST.FuncExp(loc, s);
8599 break;
8602 default:
8603 error("expression expected, not `%s`", token.toChars());
8604 Lerr:
8605 // Anything for e, as long as it's not NULL
8606 e = new AST.IntegerExp(loc, 0, AST.Type.tint32);
8607 nextToken();
8608 break;
8610 return e;
8613 private AST.Expression parseUnaryExp()
8615 AST.Expression e;
8616 const loc = token.loc;
8618 switch (token.value)
8620 case TOK.and:
8621 nextToken();
8622 e = parseUnaryExp();
8623 e = new AST.AddrExp(loc, e);
8624 break;
8626 case TOK.plusPlus:
8627 nextToken();
8628 e = parseUnaryExp();
8629 //e = new AddAssignExp(loc, e, new IntegerExp(loc, 1, Type::tint32));
8630 e = new AST.PreExp(EXP.prePlusPlus, loc, e);
8631 break;
8633 case TOK.minusMinus:
8634 nextToken();
8635 e = parseUnaryExp();
8636 //e = new MinAssignExp(loc, e, new IntegerExp(loc, 1, Type::tint32));
8637 e = new AST.PreExp(EXP.preMinusMinus, loc, e);
8638 break;
8640 case TOK.mul:
8641 nextToken();
8642 e = parseUnaryExp();
8643 e = new AST.PtrExp(loc, e);
8644 break;
8646 case TOK.min:
8647 nextToken();
8648 e = parseUnaryExp();
8649 e = new AST.NegExp(loc, e);
8650 break;
8652 case TOK.add:
8653 nextToken();
8654 e = parseUnaryExp();
8655 e = new AST.UAddExp(loc, e);
8656 break;
8658 case TOK.not:
8659 nextToken();
8660 e = parseUnaryExp();
8661 e = new AST.NotExp(loc, e);
8662 break;
8664 case TOK.tilde:
8665 nextToken();
8666 e = parseUnaryExp();
8667 e = new AST.ComExp(loc, e);
8668 break;
8670 case TOK.delete_:
8671 // @@@DEPRECATED_2.109@@@
8672 // Use of `delete` keyword has been an error since 2.099.
8673 // Remove from the parser after 2.109.
8674 nextToken();
8675 e = parseUnaryExp();
8676 e = new AST.DeleteExp(loc, e, false);
8677 break;
8679 case TOK.cast_: // cast(type) expression
8681 nextToken();
8682 check(TOK.leftParenthesis);
8683 /* Look for cast(), cast(const), cast(immutable),
8684 * cast(shared), cast(shared const), cast(wild), cast(shared wild)
8686 ubyte m = 0;
8687 while (1)
8689 switch (token.value)
8691 case TOK.const_:
8692 if (peekNext() == TOK.leftParenthesis)
8693 break; // const as type constructor
8694 m |= MODFlags.const_; // const as storage class
8695 nextToken();
8696 continue;
8698 case TOK.immutable_:
8699 if (peekNext() == TOK.leftParenthesis)
8700 break;
8701 m |= MODFlags.immutable_;
8702 nextToken();
8703 continue;
8705 case TOK.shared_:
8706 if (peekNext() == TOK.leftParenthesis)
8707 break;
8708 m |= MODFlags.shared_;
8709 nextToken();
8710 continue;
8712 case TOK.inout_:
8713 if (peekNext() == TOK.leftParenthesis)
8714 break;
8715 m |= MODFlags.wild;
8716 nextToken();
8717 continue;
8719 default:
8720 break;
8722 break;
8724 if (token.value == TOK.rightParenthesis)
8726 nextToken();
8727 e = parseUnaryExp();
8728 e = new AST.CastExp(loc, e, m);
8730 else
8732 AST.Type t = parseType(); // cast( type )
8733 t = t.addMod(m); // cast( const type )
8734 check(TOK.rightParenthesis);
8735 e = parseUnaryExp();
8736 e = new AST.CastExp(loc, e, t);
8738 break;
8740 case TOK.inout_:
8741 case TOK.shared_:
8742 case TOK.const_:
8743 case TOK.immutable_: // immutable(type)(arguments) / immutable(type).init
8745 StorageClass stc = parseTypeCtor();
8747 AST.Type t = parseBasicType();
8748 t = t.addSTC(stc);
8750 if (stc == 0 && token.value == TOK.dot)
8752 nextToken();
8753 if (token.value != TOK.identifier)
8755 error("identifier expected following `%s.`, not `%s`",
8756 t.toChars(), token.toChars());
8757 return AST.ErrorExp.get();
8759 e = new AST.DotIdExp(loc, new AST.TypeExp(loc, t), token.ident);
8760 nextToken();
8761 e = parsePostExp(e);
8763 else
8765 e = new AST.TypeExp(loc, t);
8766 if (token.value != TOK.leftParenthesis)
8768 error("`(arguments)` expected following `%s`, not `%s`",
8769 t.toChars(), token.toChars());
8770 return e;
8772 e = new AST.CallExp(loc, e, parseArguments());
8774 break;
8776 case TOK.leftParenthesis:
8778 auto tk = peek(&token);
8779 static if (CCASTSYNTAX)
8781 // If cast
8782 if (isDeclaration(tk, NeedDeclaratorId.no, TOK.rightParenthesis, &tk))
8784 tk = peek(tk); // skip over right parenthesis
8785 switch (tk.value)
8787 case TOK.not:
8788 tk = peek(tk);
8789 if (tk.value == TOK.is_ || tk.value == TOK.in_) // !is or !in
8790 break;
8791 goto case;
8793 case TOK.dot:
8794 case TOK.plusPlus:
8795 case TOK.minusMinus:
8796 case TOK.delete_:
8797 case TOK.new_:
8798 case TOK.leftParenthesis:
8799 case TOK.identifier:
8800 case TOK.this_:
8801 case TOK.super_:
8802 case TOK.int32Literal:
8803 case TOK.uns32Literal:
8804 case TOK.int64Literal:
8805 case TOK.uns64Literal:
8806 case TOK.int128Literal:
8807 case TOK.uns128Literal:
8808 case TOK.float32Literal:
8809 case TOK.float64Literal:
8810 case TOK.float80Literal:
8811 case TOK.imaginary32Literal:
8812 case TOK.imaginary64Literal:
8813 case TOK.imaginary80Literal:
8814 case TOK.null_:
8815 case TOK.true_:
8816 case TOK.false_:
8817 case TOK.charLiteral:
8818 case TOK.wcharLiteral:
8819 case TOK.dcharLiteral:
8820 case TOK.string_:
8821 case TOK.interpolated:
8822 case TOK.function_:
8823 case TOK.delegate_:
8824 case TOK.typeof_:
8825 case TOK.traits:
8826 case TOK.vector:
8827 case TOK.file:
8828 case TOK.fileFullPath:
8829 case TOK.line:
8830 case TOK.moduleString:
8831 case TOK.functionString:
8832 case TOK.prettyFunction:
8833 case TOK.wchar_:
8834 case TOK.dchar_:
8835 case TOK.bool_:
8836 case TOK.char_:
8837 case TOK.int8:
8838 case TOK.uns8:
8839 case TOK.int16:
8840 case TOK.uns16:
8841 case TOK.int32:
8842 case TOK.uns32:
8843 case TOK.int64:
8844 case TOK.uns64:
8845 case TOK.int128:
8846 case TOK.uns128:
8847 case TOK.float32:
8848 case TOK.float64:
8849 case TOK.float80:
8850 case TOK.imaginary32:
8851 case TOK.imaginary64:
8852 case TOK.imaginary80:
8853 case TOK.complex32:
8854 case TOK.complex64:
8855 case TOK.complex80:
8856 case TOK.void_:
8858 // (type) una_exp
8859 nextToken();
8860 // Note: `t` may be an expression that looks like a type
8861 auto t = parseType();
8862 check(TOK.rightParenthesis);
8864 // if .identifier
8865 // or .identifier!( ... )
8866 if (token.value == TOK.dot)
8868 if (peekNext() != TOK.identifier && peekNext() != TOK.new_)
8870 error("identifier or new keyword expected following `(...)`.");
8871 nextToken();
8872 return AST.ErrorExp.get();
8874 auto te = new AST.TypeExp(loc, t);
8875 te.parens = true;
8876 e = parsePostExp(te);
8878 else if (token.value == TOK.leftParenthesis ||
8879 token.value == TOK.plusPlus || token.value == TOK.minusMinus)
8881 // (type)(expr)
8882 // (callable)(args)
8883 // (expr)++
8884 auto te = new AST.TypeExp(loc, t);
8885 te.parens = true;
8886 e = parsePostExp(te);
8888 else
8890 e = parseUnaryExp();
8891 e = new AST.CastExp(loc, e, t);
8892 error(loc, "C style cast illegal, use `%s`", e.toChars());
8894 return e;
8896 default:
8897 break;
8901 e = parsePrimaryExp();
8902 e = parsePostExp(e);
8903 break;
8905 case TOK.throw_:
8907 nextToken();
8908 // Deviation from the DIP:
8909 // Parse AssignExpression instead of Expression to avoid conflicts for comma
8910 // separated lists, e.g. function arguments
8911 AST.Expression exp = parseAssignExp();
8912 e = new AST.ThrowExp(loc, exp);
8913 break;
8916 default:
8917 e = parsePrimaryExp();
8918 e = parsePostExp(e);
8919 break;
8921 assert(e);
8923 // ^^ is right associative and has higher precedence than the unary operators
8924 while (token.value == TOK.pow)
8926 nextToken();
8927 AST.Expression e2 = parseUnaryExp();
8928 e = new AST.PowExp(loc, e, e2);
8931 return e;
8934 private AST.Expression parsePostExp(AST.Expression e)
8936 while (1)
8938 const loc = token.loc;
8939 switch (token.value)
8941 case TOK.dot:
8942 nextToken();
8943 if (token.value == TOK.identifier)
8945 Identifier id = token.ident;
8947 nextToken();
8948 if (token.value == TOK.not && peekNext() != TOK.is_ && peekNext() != TOK.in_)
8950 AST.Objects* tiargs = parseTemplateArguments();
8951 e = new AST.DotTemplateInstanceExp(loc, e, id, tiargs);
8953 else
8954 e = new AST.DotIdExp(loc, e, id);
8955 continue;
8957 if (token.value == TOK.new_)
8959 e = parseNewExp(e);
8960 continue;
8962 error("identifier or `new` expected following `.`, not `%s`", token.toChars());
8963 break;
8965 case TOK.plusPlus:
8966 e = new AST.PostExp(EXP.plusPlus, loc, e);
8967 break;
8969 case TOK.minusMinus:
8970 e = new AST.PostExp(EXP.minusMinus, loc, e);
8971 break;
8973 case TOK.leftParenthesis:
8974 AST.Expressions* args = new AST.Expressions();
8975 AST.Identifiers* names = new AST.Identifiers();
8976 parseNamedArguments(args, names);
8977 e = new AST.CallExp(loc, e, args, names);
8978 continue;
8980 case TOK.leftBracket:
8982 // array dereferences:
8983 // array[index]
8984 // array[]
8985 // array[lwr .. upr]
8986 AST.Expression index;
8987 AST.Expression upr;
8988 auto arguments = new AST.Expressions();
8990 inBrackets++;
8991 nextToken();
8992 while (token.value != TOK.rightBracket && token.value != TOK.endOfFile)
8994 index = parseAssignExp();
8995 if (token.value == TOK.slice)
8997 // array[..., lwr..upr, ...]
8998 nextToken();
8999 upr = parseAssignExp();
9000 arguments.push(new AST.IntervalExp(loc, index, upr));
9002 else
9003 arguments.push(index);
9004 if (token.value == TOK.rightBracket)
9005 break;
9006 check(TOK.comma);
9008 check(TOK.rightBracket);
9009 inBrackets--;
9010 e = new AST.ArrayExp(loc, e, arguments);
9011 continue;
9013 default:
9014 return e;
9016 nextToken();
9020 private AST.Expression parseMulExp()
9022 const loc = token.loc;
9023 auto e = parseUnaryExp();
9025 while (1)
9027 switch (token.value)
9029 case TOK.mul:
9030 nextToken();
9031 auto e2 = parseUnaryExp();
9032 e = new AST.MulExp(loc, e, e2);
9033 continue;
9035 case TOK.div:
9036 nextToken();
9037 auto e2 = parseUnaryExp();
9038 e = new AST.DivExp(loc, e, e2);
9039 continue;
9041 case TOK.mod:
9042 nextToken();
9043 auto e2 = parseUnaryExp();
9044 e = new AST.ModExp(loc, e, e2);
9045 continue;
9047 default:
9048 break;
9050 break;
9052 return e;
9055 private AST.Expression parseAddExp()
9057 const loc = token.loc;
9058 auto e = parseMulExp();
9060 while (1)
9062 switch (token.value)
9064 case TOK.add:
9065 nextToken();
9066 auto e2 = parseMulExp();
9067 e = new AST.AddExp(loc, e, e2);
9068 continue;
9070 case TOK.min:
9071 nextToken();
9072 auto e2 = parseMulExp();
9073 e = new AST.MinExp(loc, e, e2);
9074 continue;
9076 case TOK.tilde:
9077 nextToken();
9078 auto e2 = parseMulExp();
9079 e = new AST.CatExp(loc, e, e2);
9080 continue;
9082 default:
9083 break;
9085 break;
9087 return e;
9090 private AST.Expression parseShiftExp()
9092 const loc = token.loc;
9093 auto e = parseAddExp();
9095 while (1)
9097 switch (token.value)
9099 case TOK.leftShift:
9100 nextToken();
9101 auto e2 = parseAddExp();
9102 e = new AST.ShlExp(loc, e, e2);
9103 continue;
9105 case TOK.rightShift:
9106 nextToken();
9107 auto e2 = parseAddExp();
9108 e = new AST.ShrExp(loc, e, e2);
9109 continue;
9111 case TOK.unsignedRightShift:
9112 nextToken();
9113 auto e2 = parseAddExp();
9114 e = new AST.UshrExp(loc, e, e2);
9115 continue;
9117 default:
9118 break;
9120 break;
9122 return e;
9125 private AST.Expression parseCmpExp()
9127 const loc = token.loc;
9129 auto e = parseShiftExp();
9130 EXP op = EXP.reserved;
9132 switch (token.value)
9134 case TOK.equal: op = EXP.equal; goto Lequal;
9135 case TOK.notEqual: op = EXP.notEqual; goto Lequal;
9136 Lequal:
9137 nextToken();
9138 auto e2 = parseShiftExp();
9139 e = new AST.EqualExp(op, loc, e, e2);
9140 break;
9142 case TOK.not:
9144 // Attempt to identify '!is'
9145 const tv = peekNext();
9146 if (tv == TOK.in_)
9148 nextToken();
9149 nextToken();
9150 auto e2 = parseShiftExp();
9151 e = new AST.InExp(loc, e, e2);
9152 e = new AST.NotExp(loc, e);
9153 break;
9155 if (tv != TOK.is_)
9156 break;
9157 nextToken();
9158 op = EXP.notIdentity;
9159 goto Lidentity;
9161 case TOK.is_: op = EXP.identity; goto Lidentity;
9162 Lidentity:
9163 nextToken();
9164 auto e2 = parseShiftExp();
9165 e = new AST.IdentityExp(op, loc, e, e2);
9166 break;
9168 case TOK.lessThan: op = EXP.lessThan; goto Lcmp;
9169 case TOK.lessOrEqual: op = EXP.lessOrEqual; goto Lcmp;
9170 case TOK.greaterThan: op = EXP.greaterThan; goto Lcmp;
9171 case TOK.greaterOrEqual: op = EXP.greaterOrEqual; goto Lcmp;
9172 Lcmp:
9173 nextToken();
9174 auto e2 = parseShiftExp();
9175 e = new AST.CmpExp(op, loc, e, e2);
9176 break;
9178 case TOK.in_:
9179 nextToken();
9180 auto e2 = parseShiftExp();
9181 e = new AST.InExp(loc, e, e2);
9182 break;
9184 default:
9185 break;
9187 return e;
9190 private AST.Expression parseAndExp()
9192 Loc loc = token.loc;
9193 bool parens = token.value == TOK.leftParenthesis;
9194 auto e = parseCmpExp();
9195 while (token.value == TOK.and)
9197 if (!parens)
9198 checkParens(TOK.and, e);
9199 parens = nextToken() == TOK.leftParenthesis;
9200 auto e2 = parseCmpExp();
9201 if (!parens)
9202 checkParens(TOK.and, e2);
9203 e = new AST.AndExp(loc, e, e2);
9204 parens = true; // don't call checkParens() for And
9205 loc = token.loc;
9207 return e;
9210 private AST.Expression parseXorExp()
9212 Loc loc = token.loc;
9214 bool parens = token.value == TOK.leftParenthesis;
9215 auto e = parseAndExp();
9216 while (token.value == TOK.xor)
9218 if (!parens)
9219 checkParens(TOK.xor, e);
9220 parens = nextToken() == TOK.leftParenthesis;
9221 auto e2 = parseAndExp();
9222 if (!parens)
9223 checkParens(TOK.xor, e2);
9224 e = new AST.XorExp(loc, e, e2);
9225 parens = true;
9226 loc = token.loc;
9228 return e;
9231 private AST.Expression parseOrExp()
9233 Loc loc = token.loc;
9235 bool parens = token.value == TOK.leftParenthesis;
9236 auto e = parseXorExp();
9237 while (token.value == TOK.or)
9239 if (!parens)
9240 checkParens(TOK.or, e);
9241 parens = nextToken() == TOK.leftParenthesis;
9242 auto e2 = parseXorExp();
9243 if (!parens)
9244 checkParens(TOK.or, e2);
9245 e = new AST.OrExp(loc, e, e2);
9246 parens = true;
9247 loc = token.loc;
9249 return e;
9252 private AST.Expression parseAndAndExp()
9254 const loc = token.loc;
9256 auto e = parseOrExp();
9257 while (token.value == TOK.andAnd)
9259 nextToken();
9260 auto e2 = parseOrExp();
9261 e = new AST.LogicalExp(loc, EXP.andAnd, e, e2);
9263 return e;
9266 private AST.Expression parseOrOrExp()
9268 const loc = token.loc;
9270 auto e = parseAndAndExp();
9271 while (token.value == TOK.orOr)
9273 nextToken();
9274 auto e2 = parseAndAndExp();
9275 e = new AST.LogicalExp(loc, EXP.orOr, e, e2);
9277 return e;
9280 private AST.Expression parseCondExp()
9282 const loc = token.loc;
9284 auto e = parseOrOrExp();
9285 if (token.value == TOK.question)
9287 nextToken();
9288 auto e1 = parseExpression();
9289 check(TOK.colon);
9290 auto e2 = parseCondExp();
9291 e = new AST.CondExp(loc, e, e1, e2);
9293 return e;
9296 AST.Expression parseAssignExp()
9298 bool parens = token.value == TOK.leftParenthesis;
9299 AST.Expression e;
9300 e = parseCondExp();
9301 if (e is null)
9302 return e;
9304 // require parens for e.g. `t ? a = 1 : b = 2`
9305 void checkRequiredParens()
9307 if (e.op == EXP.question && !parens)
9308 eSink.error(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`",
9309 e.toChars(), Token.toChars(token.value));
9312 const loc = token.loc;
9313 switch (token.value)
9315 case TOK.assign:
9316 checkRequiredParens();
9317 nextToken();
9318 auto e2 = parseAssignExp();
9319 e = new AST.AssignExp(loc, e, e2);
9320 break;
9322 case TOK.addAssign:
9323 checkRequiredParens();
9324 nextToken();
9325 auto e2 = parseAssignExp();
9326 e = new AST.AddAssignExp(loc, e, e2);
9327 break;
9329 case TOK.minAssign:
9330 checkRequiredParens();
9331 nextToken();
9332 auto e2 = parseAssignExp();
9333 e = new AST.MinAssignExp(loc, e, e2);
9334 break;
9336 case TOK.mulAssign:
9337 checkRequiredParens();
9338 nextToken();
9339 auto e2 = parseAssignExp();
9340 e = new AST.MulAssignExp(loc, e, e2);
9341 break;
9343 case TOK.divAssign:
9344 checkRequiredParens();
9345 nextToken();
9346 auto e2 = parseAssignExp();
9347 e = new AST.DivAssignExp(loc, e, e2);
9348 break;
9350 case TOK.modAssign:
9351 checkRequiredParens();
9352 nextToken();
9353 auto e2 = parseAssignExp();
9354 e = new AST.ModAssignExp(loc, e, e2);
9355 break;
9357 case TOK.powAssign:
9358 checkRequiredParens();
9359 nextToken();
9360 auto e2 = parseAssignExp();
9361 e = new AST.PowAssignExp(loc, e, e2);
9362 break;
9364 case TOK.andAssign:
9365 checkRequiredParens();
9366 nextToken();
9367 auto e2 = parseAssignExp();
9368 e = new AST.AndAssignExp(loc, e, e2);
9369 break;
9371 case TOK.orAssign:
9372 checkRequiredParens();
9373 nextToken();
9374 auto e2 = parseAssignExp();
9375 e = new AST.OrAssignExp(loc, e, e2);
9376 break;
9378 case TOK.xorAssign:
9379 checkRequiredParens();
9380 nextToken();
9381 auto e2 = parseAssignExp();
9382 e = new AST.XorAssignExp(loc, e, e2);
9383 break;
9385 case TOK.leftShiftAssign:
9386 checkRequiredParens();
9387 nextToken();
9388 auto e2 = parseAssignExp();
9389 e = new AST.ShlAssignExp(loc, e, e2);
9390 break;
9392 case TOK.rightShiftAssign:
9393 checkRequiredParens();
9394 nextToken();
9395 auto e2 = parseAssignExp();
9396 e = new AST.ShrAssignExp(loc, e, e2);
9397 break;
9399 case TOK.unsignedRightShiftAssign:
9400 checkRequiredParens();
9401 nextToken();
9402 auto e2 = parseAssignExp();
9403 e = new AST.UshrAssignExp(loc, e, e2);
9404 break;
9406 case TOK.concatenateAssign:
9407 checkRequiredParens();
9408 nextToken();
9409 auto e2 = parseAssignExp();
9410 e = new AST.CatAssignExp(loc, e, e2);
9411 break;
9413 default:
9414 break;
9417 return e;
9420 /*************************
9421 * Collect argument list.
9422 * Assume current token is ',', '$(LPAREN)' or '['.
9424 private AST.Expressions* parseArguments()
9426 // function call
9427 AST.Expressions* arguments = new AST.Expressions();
9428 parseNamedArguments(arguments, null);
9429 return arguments;
9432 /*************************
9433 * Collect argument list.
9434 * Assume current token is ',', '$(LPAREN)' or '['.
9436 private void parseNamedArguments(AST.Expressions* arguments, AST.Identifiers* names)
9438 assert(arguments);
9440 const endtok = token.value == TOK.leftBracket ? TOK.rightBracket : TOK.rightParenthesis;
9442 nextToken();
9444 while (token.value != endtok && token.value != TOK.endOfFile)
9446 if (peekNext() == TOK.colon)
9448 // Named argument `name: exp`
9449 auto loc = token.loc;
9450 auto ident = token.ident;
9451 check(TOK.identifier);
9452 check(TOK.colon);
9453 if (names)
9454 names.push(ident);
9455 else
9456 error(loc, "named arguments not allowed here");
9458 else
9460 if (names)
9461 names.push(null);
9464 auto arg = parseAssignExp();
9465 arguments.push(arg);
9467 if (token.value != TOK.comma)
9468 break;
9470 nextToken(); //comma
9472 check(endtok);
9475 /*******************************************
9477 private AST.Expression parseNewExp(AST.Expression thisexp)
9479 const loc = token.loc;
9481 nextToken();
9482 AST.Expressions* arguments = null;
9483 AST.Identifiers* names = null;
9485 // An anonymous nested class starts with "class"
9486 if (token.value == TOK.class_)
9488 nextToken();
9489 if (token.value == TOK.leftParenthesis)
9491 arguments = new AST.Expressions();
9492 names = new AST.Identifiers();
9493 parseNamedArguments(arguments, names);
9496 AST.BaseClasses* baseclasses = null;
9497 if (token.value != TOK.leftCurly)
9498 baseclasses = parseBaseClasses();
9500 Identifier id = null;
9501 AST.Dsymbols* members = null;
9503 if (token.value != TOK.leftCurly)
9505 error("`{ members }` expected for anonymous class");
9507 else
9509 nextToken();
9510 members = parseDeclDefs(0);
9511 if (token.value != TOK.rightCurly)
9512 error("class member expected");
9513 nextToken();
9516 auto cd = new AST.ClassDeclaration(loc, id, baseclasses, members, false);
9517 auto e = new AST.NewAnonClassExp(loc, thisexp, cd, arguments);
9518 return e;
9521 const stc = parseTypeCtor();
9522 auto t = parseBasicType(true);
9523 t = parseTypeSuffixes(t);
9524 t = t.addSTC(stc);
9525 if (t.ty == Taarray)
9527 AST.TypeAArray taa = cast(AST.TypeAArray)t;
9528 AST.Type index = taa.index;
9529 // `new Type[expr]` is a static array
9530 auto edim = AST.typeToExpression(index);
9531 if (edim)
9532 t = new AST.TypeSArray(taa.next, edim);
9534 else if (token.value == TOK.leftParenthesis && t.ty != Tsarray)
9536 arguments = new AST.Expressions();
9537 names = new AST.Identifiers();
9538 parseNamedArguments(arguments, names);
9541 auto e = new AST.NewExp(loc, thisexp, t, arguments, names);
9542 return e;
9545 /**********************************************
9547 private void addComment(AST.Dsymbol s, const(char)* blockComment)
9549 if (s !is null)
9550 this.addComment(s, blockComment.toDString());
9553 private void addComment(AST.Dsymbol s, const(char)[] blockComment)
9555 if (s !is null)
9557 s.addComment(combineComments(blockComment, token.lineComment, true));
9558 token.lineComment = null;
9562 /**********************************************
9563 * Recognize builtin @ attributes
9564 * Params:
9565 * ident = identifier
9566 * Returns:
9567 * storage class for attribute, 0 if not
9569 static StorageClass isBuiltinAtAttribute(Identifier ident)
9571 return (ident == Id.property) ? STC.property :
9572 (ident == Id.nogc) ? STC.nogc :
9573 (ident == Id.safe) ? STC.safe :
9574 (ident == Id.trusted) ? STC.trusted :
9575 (ident == Id.system) ? STC.system :
9576 (ident == Id.live) ? STC.live :
9577 (ident == Id.future) ? STC.future :
9578 (ident == Id.disable) ? STC.disable :
9582 enum StorageClass atAttrGroup =
9583 STC.property |
9584 STC.nogc |
9585 STC.safe |
9586 STC.trusted |
9587 STC.system |
9588 STC.live |
9589 /*STC.future |*/ // probably should be included
9590 STC.disable;
9592 void usageOfBodyKeyword()
9594 version (none) // disable obsolete warning
9596 eSink.warning(token.loc, "usage of identifer `body` as a keyword is obsolete. Use `do` instead.");
9601 enum PREC : int
9603 zero,
9604 expr,
9605 assign,
9606 cond,
9607 oror,
9608 andand,
9610 xor,
9611 and,
9612 equal,
9613 rel,
9614 shift,
9615 add,
9616 mul,
9617 pow,
9618 unary,
9619 primary,
9622 /**********************************
9623 * Set operator precedence for each operator.
9625 * Used by hdrgen
9627 immutable PREC[EXP.max + 1] precedence =
9629 EXP.type : PREC.expr,
9630 EXP.error : PREC.expr,
9631 EXP.objcClassReference : PREC.expr, // Objective-C class reference, same as EXP.type
9633 EXP.mixin_ : PREC.primary,
9635 EXP.import_ : PREC.primary,
9636 EXP.dotVariable : PREC.primary,
9637 EXP.scope_ : PREC.primary,
9638 EXP.identifier : PREC.primary,
9639 EXP.this_ : PREC.primary,
9640 EXP.super_ : PREC.primary,
9641 EXP.int64 : PREC.primary,
9642 EXP.float64 : PREC.primary,
9643 EXP.complex80 : PREC.primary,
9644 EXP.null_ : PREC.primary,
9645 EXP.string_ : PREC.primary,
9646 EXP.arrayLiteral : PREC.primary,
9647 EXP.assocArrayLiteral : PREC.primary,
9648 EXP.classReference : PREC.primary,
9649 EXP.file : PREC.primary,
9650 EXP.fileFullPath : PREC.primary,
9651 EXP.line : PREC.primary,
9652 EXP.moduleString : PREC.primary,
9653 EXP.functionString : PREC.primary,
9654 EXP.prettyFunction : PREC.primary,
9655 EXP.typeid_ : PREC.primary,
9656 EXP.is_ : PREC.primary,
9657 EXP.assert_ : PREC.primary,
9658 EXP.halt : PREC.primary,
9659 EXP.template_ : PREC.primary,
9660 EXP.dSymbol : PREC.primary,
9661 EXP.function_ : PREC.primary,
9662 EXP.variable : PREC.primary,
9663 EXP.symbolOffset : PREC.primary,
9664 EXP.structLiteral : PREC.primary,
9665 EXP.compoundLiteral : PREC.primary,
9666 EXP.arrayLength : PREC.primary,
9667 EXP.delegatePointer : PREC.primary,
9668 EXP.delegateFunctionPointer : PREC.primary,
9669 EXP.remove : PREC.primary,
9670 EXP.tuple : PREC.primary,
9671 EXP.traits : PREC.primary,
9672 EXP.overloadSet : PREC.primary,
9673 EXP.void_ : PREC.primary,
9674 EXP.vectorArray : PREC.primary,
9675 EXP._Generic : PREC.primary,
9677 // post
9678 EXP.dotTemplateInstance : PREC.primary,
9679 EXP.dotIdentifier : PREC.primary,
9680 EXP.dotTemplateDeclaration : PREC.primary,
9681 EXP.dot : PREC.primary,
9682 EXP.dotType : PREC.primary,
9683 EXP.plusPlus : PREC.primary,
9684 EXP.minusMinus : PREC.primary,
9685 EXP.prePlusPlus : PREC.primary,
9686 EXP.preMinusMinus : PREC.primary,
9687 EXP.call : PREC.primary,
9688 EXP.slice : PREC.primary,
9689 EXP.array : PREC.primary,
9690 EXP.index : PREC.primary,
9692 EXP.delegate_ : PREC.unary,
9693 EXP.address : PREC.unary,
9694 EXP.star : PREC.unary,
9695 EXP.negate : PREC.unary,
9696 EXP.uadd : PREC.unary,
9697 EXP.not : PREC.unary,
9698 EXP.tilde : PREC.unary,
9699 EXP.delete_ : PREC.unary,
9700 EXP.new_ : PREC.unary,
9701 EXP.newAnonymousClass : PREC.unary,
9702 EXP.cast_ : PREC.unary,
9703 EXP.throw_ : PREC.unary,
9705 EXP.vector : PREC.unary,
9706 EXP.pow : PREC.pow,
9708 EXP.mul : PREC.mul,
9709 EXP.div : PREC.mul,
9710 EXP.mod : PREC.mul,
9712 EXP.add : PREC.add,
9713 EXP.min : PREC.add,
9714 EXP.concatenate : PREC.add,
9716 EXP.leftShift : PREC.shift,
9717 EXP.rightShift : PREC.shift,
9718 EXP.unsignedRightShift : PREC.shift,
9720 EXP.lessThan : PREC.rel,
9721 EXP.lessOrEqual : PREC.rel,
9722 EXP.greaterThan : PREC.rel,
9723 EXP.greaterOrEqual : PREC.rel,
9724 EXP.in_ : PREC.rel,
9726 /* Note that we changed precedence, so that < and != have the same
9727 * precedence. This change is in the parser, too.
9729 EXP.equal : PREC.rel,
9730 EXP.notEqual : PREC.rel,
9731 EXP.identity : PREC.rel,
9732 EXP.notIdentity : PREC.rel,
9734 EXP.and : PREC.and,
9735 EXP.xor : PREC.xor,
9736 EXP.or : PREC.or,
9738 EXP.andAnd : PREC.andand,
9739 EXP.orOr : PREC.oror,
9741 EXP.question : PREC.cond,
9743 EXP.assign : PREC.assign,
9744 EXP.construct : PREC.assign,
9745 EXP.blit : PREC.assign,
9746 EXP.addAssign : PREC.assign,
9747 EXP.minAssign : PREC.assign,
9748 EXP.concatenateAssign : PREC.assign,
9749 EXP.concatenateElemAssign : PREC.assign,
9750 EXP.concatenateDcharAssign : PREC.assign,
9751 EXP.mulAssign : PREC.assign,
9752 EXP.divAssign : PREC.assign,
9753 EXP.modAssign : PREC.assign,
9754 EXP.powAssign : PREC.assign,
9755 EXP.leftShiftAssign : PREC.assign,
9756 EXP.rightShiftAssign : PREC.assign,
9757 EXP.unsignedRightShiftAssign : PREC.assign,
9758 EXP.andAssign : PREC.assign,
9759 EXP.orAssign : PREC.assign,
9760 EXP.xorAssign : PREC.assign,
9762 EXP.comma : PREC.expr,
9763 EXP.declaration : PREC.expr,
9765 EXP.interval : PREC.assign,
9768 enum ParseStatementFlags : int
9770 scope_ = 2, // start a new scope
9771 curly = 4, // { } statement is required
9772 curlyScope = 8, // { } starts a new scope
9773 semiOk = 0x10, // empty ';' are really ok
9776 struct PrefixAttributes(AST)
9778 StorageClass storageClass;
9779 AST.Expression depmsg;
9780 LINK link;
9781 AST.Visibility visibility;
9782 bool setAlignment;
9783 AST.Expression ealign;
9784 AST.Expressions* udas;
9785 const(char)* comment;
9788 /// The result of the `ParseLinkage` function
9789 struct ParsedLinkage(AST)
9791 /// What linkage was specified
9792 LINK link;
9793 /// If `extern(C++, class|struct)`, contains the `class|struct`
9794 CPPMANGLE cppmangle;
9795 /// If `extern(C++, some.identifier)`, will be the identifiers
9796 AST.Identifiers* idents;
9797 /// If `extern(C++, (some_tuple_expression)|"string"), will be the expressions
9798 AST.Expressions* identExps;
9802 /*********************************** Private *************************************/
9804 /***********************
9805 * How multiple declarations are parsed.
9806 * If 1, treat as C.
9807 * If 0, treat:
9808 * int *p, i;
9809 * as:
9810 * int* p;
9811 * int* i;
9813 private enum CDECLSYNTAX = 0;
9815 /*****
9816 * Support C cast syntax:
9817 * (type)(expression)
9819 private enum CCASTSYNTAX = 1;
9821 /*****
9822 * Support postfix C array declarations, such as
9823 * int a[3][4];
9825 private enum CARRAYDECL = 1;
9827 /*****************************
9828 * Destructively extract storage class from pAttrs.
9830 private StorageClass getStorageClass(AST)(PrefixAttributes!(AST)* pAttrs)
9832 StorageClass stc = STC.undefined_;
9833 if (pAttrs)
9835 stc = pAttrs.storageClass;
9836 pAttrs.storageClass = STC.undefined_;
9838 return stc;