d: Merge upstream dmd 3982604c5, druntime bc58b1e9, phobos 12329adb6.
[official-gcc.git] / gcc / d / dmd / parse.d
blob2229e7853db3861f428d3aa838cbf624a82208f3
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-2021 by The D Language Foundation, All Rights Reserved
7 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
8 * License: $(LINK2 http://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;
18 import dmd.astenums;
19 import dmd.globals;
20 import dmd.id;
21 import dmd.identifier;
22 import dmd.lexer;
23 import dmd.errors;
24 import dmd.root.filename;
25 import dmd.common.outbuffer;
26 import dmd.root.rmem;
27 import dmd.root.rootobject;
28 import dmd.root.string;
29 import dmd.tokens;
31 // How multiple declarations are parsed.
32 // If 1, treat as C.
33 // If 0, treat:
34 // int *p, i;
35 // as:
36 // int* p;
37 // int* i;
38 private enum CDECLSYNTAX = 0;
40 // Support C cast syntax:
41 // (type)(expression)
42 private enum CCASTSYNTAX = 1;
44 // Support postfix C array declarations, such as
45 // int a[3][4];
46 private enum CARRAYDECL = 1;
48 /**********************************
49 * Set operator precedence for each operator.
51 * Used by hdrgen
53 immutable PREC[EXP.max + 1] precedence =
55 EXP.type : PREC.expr,
56 EXP.error : PREC.expr,
57 EXP.objcClassReference : PREC.expr, // Objective-C class reference, same as EXP.type
59 EXP.typeof_ : PREC.primary,
60 EXP.mixin_ : PREC.primary,
62 EXP.import_ : PREC.primary,
63 EXP.dotVariable : PREC.primary,
64 EXP.scope_ : PREC.primary,
65 EXP.identifier : PREC.primary,
66 EXP.this_ : PREC.primary,
67 EXP.super_ : PREC.primary,
68 EXP.int64 : PREC.primary,
69 EXP.float64 : PREC.primary,
70 EXP.complex80 : PREC.primary,
71 EXP.null_ : PREC.primary,
72 EXP.string_ : PREC.primary,
73 EXP.arrayLiteral : PREC.primary,
74 EXP.assocArrayLiteral : PREC.primary,
75 EXP.classReference : PREC.primary,
76 EXP.file : PREC.primary,
77 EXP.fileFullPath : PREC.primary,
78 EXP.line : PREC.primary,
79 EXP.moduleString : PREC.primary,
80 EXP.functionString : PREC.primary,
81 EXP.prettyFunction : PREC.primary,
82 EXP.typeid_ : PREC.primary,
83 EXP.is_ : PREC.primary,
84 EXP.assert_ : PREC.primary,
85 EXP.halt : PREC.primary,
86 EXP.template_ : PREC.primary,
87 EXP.dSymbol : PREC.primary,
88 EXP.function_ : PREC.primary,
89 EXP.variable : PREC.primary,
90 EXP.symbolOffset : PREC.primary,
91 EXP.structLiteral : PREC.primary,
92 EXP.compoundLiteral : PREC.primary,
93 EXP.arrayLength : PREC.primary,
94 EXP.delegatePointer : PREC.primary,
95 EXP.delegateFunctionPointer : PREC.primary,
96 EXP.remove : PREC.primary,
97 EXP.tuple : PREC.primary,
98 EXP.traits : PREC.primary,
99 EXP.default_ : PREC.primary,
100 EXP.overloadSet : PREC.primary,
101 EXP.void_ : PREC.primary,
102 EXP.vectorArray : PREC.primary,
103 EXP._Generic : PREC.primary,
105 // post
106 EXP.dotTemplateInstance : PREC.primary,
107 EXP.dotIdentifier : PREC.primary,
108 EXP.dotTemplateDeclaration : PREC.primary,
109 EXP.dot : PREC.primary,
110 EXP.dotType : PREC.primary,
111 EXP.plusPlus : PREC.primary,
112 EXP.minusMinus : PREC.primary,
113 EXP.prePlusPlus : PREC.primary,
114 EXP.preMinusMinus : PREC.primary,
115 EXP.call : PREC.primary,
116 EXP.slice : PREC.primary,
117 EXP.array : PREC.primary,
118 EXP.index : PREC.primary,
120 EXP.delegate_ : PREC.unary,
121 EXP.address : PREC.unary,
122 EXP.star : PREC.unary,
123 EXP.negate : PREC.unary,
124 EXP.uadd : PREC.unary,
125 EXP.not : PREC.unary,
126 EXP.tilde : PREC.unary,
127 EXP.delete_ : PREC.unary,
128 EXP.new_ : PREC.unary,
129 EXP.newAnonymousClass : PREC.unary,
130 EXP.cast_ : PREC.unary,
132 EXP.vector : PREC.unary,
133 EXP.pow : PREC.pow,
135 EXP.mul : PREC.mul,
136 EXP.div : PREC.mul,
137 EXP.mod : PREC.mul,
139 EXP.add : PREC.add,
140 EXP.min : PREC.add,
141 EXP.concatenate : PREC.add,
143 EXP.leftShift : PREC.shift,
144 EXP.rightShift : PREC.shift,
145 EXP.unsignedRightShift : PREC.shift,
147 EXP.lessThan : PREC.rel,
148 EXP.lessOrEqual : PREC.rel,
149 EXP.greaterThan : PREC.rel,
150 EXP.greaterOrEqual : PREC.rel,
151 EXP.in_ : PREC.rel,
153 /* Note that we changed precedence, so that < and != have the same
154 * precedence. This change is in the parser, too.
156 EXP.equal : PREC.rel,
157 EXP.notEqual : PREC.rel,
158 EXP.identity : PREC.rel,
159 EXP.notIdentity : PREC.rel,
161 EXP.and : PREC.and,
162 EXP.xor : PREC.xor,
163 EXP.or : PREC.or,
165 EXP.andAnd : PREC.andand,
166 EXP.orOr : PREC.oror,
168 EXP.question : PREC.cond,
170 EXP.assign : PREC.assign,
171 EXP.construct : PREC.assign,
172 EXP.blit : PREC.assign,
173 EXP.addAssign : PREC.assign,
174 EXP.minAssign : PREC.assign,
175 EXP.concatenateAssign : PREC.assign,
176 EXP.concatenateElemAssign : PREC.assign,
177 EXP.concatenateDcharAssign : PREC.assign,
178 EXP.mulAssign : PREC.assign,
179 EXP.divAssign : PREC.assign,
180 EXP.modAssign : PREC.assign,
181 EXP.powAssign : PREC.assign,
182 EXP.leftShiftAssign : PREC.assign,
183 EXP.rightShiftAssign : PREC.assign,
184 EXP.unsignedRightShiftAssign : PREC.assign,
185 EXP.andAssign : PREC.assign,
186 EXP.orAssign : PREC.assign,
187 EXP.xorAssign : PREC.assign,
189 EXP.comma : PREC.expr,
190 EXP.declaration : PREC.expr,
192 EXP.interval : PREC.assign,
195 enum ParseStatementFlags : int
197 semi = 1, // empty ';' statements are allowed, but deprecated
198 scope_ = 2, // start a new scope
199 curly = 4, // { } statement is required
200 curlyScope = 8, // { } starts a new scope
201 semiOk = 0x10, // empty ';' are really ok
204 struct PrefixAttributes(AST)
206 StorageClass storageClass;
207 AST.Expression depmsg;
208 LINK link;
209 AST.Visibility visibility;
210 bool setAlignment;
211 AST.Expression ealign;
212 AST.Expressions* udas;
213 const(char)* comment;
216 /// The result of the `ParseLinkage` function
217 struct ParsedLinkage(AST)
219 /// What linkage was specified
220 LINK link;
221 /// If `extern(C++, class|struct)`, contains the `class|struct`
222 CPPMANGLE cppmangle;
223 /// If `extern(C++, some.identifier)`, will be the identifiers
224 AST.Identifiers* idents;
225 /// If `extern(C++, (some_tuple_expression)|"string"), will be the expressions
226 AST.Expressions* identExps;
229 /*****************************
230 * Destructively extract storage class from pAttrs.
232 private StorageClass getStorageClass(AST)(PrefixAttributes!(AST)* pAttrs)
234 StorageClass stc = STC.undefined_;
235 if (pAttrs)
237 stc = pAttrs.storageClass;
238 pAttrs.storageClass = STC.undefined_;
240 return stc;
243 /**************************************
244 * dump mixin expansion to file for better debugging
246 private bool writeMixin(const(char)[] s, ref Loc loc)
248 if (!global.params.mixinOut)
249 return false;
251 OutBuffer* ob = global.params.mixinOut;
253 ob.writestring("// expansion at ");
254 ob.writestring(loc.toChars());
255 ob.writenl();
257 global.params.mixinLines++;
259 loc = Loc(global.params.mixinFile, global.params.mixinLines + 1, loc.charnum);
261 // write by line to create consistent line endings
262 size_t lastpos = 0;
263 for (size_t i = 0; i < s.length; ++i)
265 // detect LF and CRLF
266 const c = s[i];
267 if (c == '\n' || (c == '\r' && i+1 < s.length && s[i+1] == '\n'))
269 ob.writestring(s[lastpos .. i]);
270 ob.writenl();
271 global.params.mixinLines++;
272 if (c == '\r')
273 ++i;
274 lastpos = i + 1;
278 if(lastpos < s.length)
279 ob.writestring(s[lastpos .. $]);
281 if (s.length == 0 || s[$-1] != '\n')
283 ob.writenl(); // ensure empty line after expansion
284 global.params.mixinLines++;
286 ob.writenl();
287 global.params.mixinLines++;
289 return true;
292 /***********************************************************
294 class Parser(AST) : Lexer
296 AST.ModuleDeclaration* md;
298 protected
300 AST.Module mod;
301 LINK linkage;
302 Loc linkLoc;
303 CPPMANGLE cppmangle;
304 Loc endloc; // set to location of last right curly
305 int inBrackets; // inside [] of array index or slice
306 Loc lookingForElse; // location of lonely if looking for an else
309 /*********************
310 * Use this constructor for string mixins.
311 * Input:
312 * loc location in source file of mixin
314 extern (D) this(const ref Loc loc, AST.Module _module, const(char)[] input, bool doDocComment)
316 super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false);
318 //printf("Parser::Parser()\n");
319 scanloc = loc;
321 if (!writeMixin(input, scanloc) && loc.filename)
323 /* Create a pseudo-filename for the mixin string, as it may not even exist
324 * in the source file.
326 char* filename = cast(char*)mem.xmalloc(strlen(loc.filename) + 7 + (loc.linnum).sizeof * 3 + 1);
327 sprintf(filename, "%s-mixin-%d", loc.filename, cast(int)loc.linnum);
328 scanloc.filename = filename;
331 mod = _module;
332 linkage = LINK.d;
333 //nextToken(); // start up the scanner
336 extern (D) this(AST.Module _module, const(char)[] input, bool doDocComment)
338 super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false);
340 //printf("Parser::Parser()\n");
341 mod = _module;
342 linkage = LINK.d;
343 //nextToken(); // start up the scanner
346 AST.Dsymbols* parseModule()
348 const comment = token.blockComment;
349 bool isdeprecated = false;
350 AST.Expression msg = null;
351 AST.Expressions* udas = null;
352 AST.Dsymbols* decldefs;
353 AST.Dsymbol lastDecl = mod; // for attaching ddoc unittests to module decl
355 Token* tk;
356 if (skipAttributes(&token, &tk) && tk.value == TOK.module_)
358 while (token.value != TOK.module_)
360 switch (token.value)
362 case TOK.deprecated_:
364 // deprecated (...) module ...
365 if (isdeprecated)
366 error("there is only one deprecation attribute allowed for module declaration");
367 isdeprecated = true;
368 nextToken();
369 if (token.value == TOK.leftParenthesis)
371 check(TOK.leftParenthesis);
372 msg = parseAssignExp();
373 check(TOK.rightParenthesis);
375 break;
377 case TOK.at:
379 AST.Expressions* exps = null;
380 const stc = parseAttribute(exps);
381 if (stc & atAttrGroup)
383 error("`@%s` attribute for module declaration is not supported", token.toChars());
385 else
387 udas = AST.UserAttributeDeclaration.concat(udas, exps);
389 if (stc)
390 nextToken();
391 break;
393 default:
395 error("`module` expected instead of `%s`", token.toChars());
396 nextToken();
397 break;
403 if (udas)
405 auto a = new AST.Dsymbols();
406 auto udad = new AST.UserAttributeDeclaration(udas, a);
407 mod.userAttribDecl = udad;
410 // ModuleDeclation leads off
411 if (token.value == TOK.module_)
413 const loc = token.loc;
415 nextToken();
416 if (token.value != TOK.identifier)
418 error("identifier expected following `module`");
419 goto Lerr;
422 Identifier[] a;
423 Identifier id = token.ident;
425 while (nextToken() == TOK.dot)
427 a ~= id;
428 nextToken();
429 if (token.value != TOK.identifier)
431 error("identifier expected following `package`");
432 goto Lerr;
434 id = token.ident;
437 md = new AST.ModuleDeclaration(loc, a, id, msg, isdeprecated);
439 if (token.value != TOK.semicolon)
440 error("`;` expected following module declaration instead of `%s`", token.toChars());
441 nextToken();
442 addComment(mod, comment);
445 decldefs = parseDeclDefs(0, &lastDecl);
446 if (token.value != TOK.endOfFile)
448 error(token.loc, "unrecognized declaration");
449 goto Lerr;
451 return decldefs;
453 Lerr:
454 while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
455 nextToken();
456 nextToken();
457 return new AST.Dsymbols();
460 final:
463 * Parses a `deprecated` declaration
465 * Params:
466 * msg = Deprecated message, if any.
467 * Used to support overriding a deprecated storage class with
468 * a deprecated declaration with a message, but to error
469 * if both declaration have a message.
471 * Returns:
472 * Whether the deprecated declaration has a message
474 private bool parseDeprecatedAttribute(ref AST.Expression msg)
476 if (peekNext() != TOK.leftParenthesis)
477 return false;
479 nextToken();
480 check(TOK.leftParenthesis);
481 AST.Expression e = parseAssignExp();
482 check(TOK.rightParenthesis);
483 if (msg)
485 error("conflicting storage class `deprecated(%s)` and `deprecated(%s)`", msg.toChars(), e.toChars());
487 msg = e;
488 return true;
491 AST.Dsymbols* parseDeclDefs(int once, AST.Dsymbol* pLastDecl = null, PrefixAttributes!AST* pAttrs = null)
493 AST.Dsymbol lastDecl = null; // used to link unittest to its previous declaration
494 if (!pLastDecl)
495 pLastDecl = &lastDecl;
497 const linksave = linkage; // save global state
499 //printf("Parser::parseDeclDefs()\n");
500 auto decldefs = new AST.Dsymbols();
503 // parse result
504 AST.Dsymbol s = null;
505 AST.Dsymbols* a = null;
507 PrefixAttributes!AST attrs;
508 if (!once || !pAttrs)
510 pAttrs = &attrs;
511 pAttrs.comment = token.blockComment.ptr;
513 AST.Visibility.Kind prot;
514 StorageClass stc;
515 AST.Condition condition;
517 linkage = linksave;
519 Loc startloc;
521 switch (token.value)
523 case TOK.enum_:
525 /* Determine if this is a manifest constant declaration,
526 * or a conventional enum.
528 const tv = peekNext();
529 if (tv == TOK.leftCurly || tv == TOK.colon)
530 s = parseEnum();
531 else if (tv != TOK.identifier)
532 goto Ldeclaration;
533 else
535 const nextv = peekNext2();
536 if (nextv == TOK.leftCurly || nextv == TOK.colon || nextv == TOK.semicolon)
537 s = parseEnum();
538 else
539 goto Ldeclaration;
541 break;
543 case TOK.import_:
544 a = parseImport();
545 // keep pLastDecl
546 break;
548 case TOK.template_:
549 s = cast(AST.Dsymbol)parseTemplateDeclaration();
550 break;
552 case TOK.mixin_:
554 const loc = token.loc;
555 switch (peekNext())
557 case TOK.leftParenthesis:
559 // MixinType
560 if (isDeclaration(&token, NeedDeclaratorId.mustIfDstyle, TOK.reserved, null))
561 goto Ldeclaration;
562 // mixin(string)
563 nextToken();
564 auto exps = parseArguments();
565 check(TOK.semicolon);
566 s = new AST.CompileDeclaration(loc, exps);
567 break;
569 case TOK.template_:
570 // mixin template
571 nextToken();
572 s = cast(AST.Dsymbol)parseTemplateDeclaration(true);
573 break;
575 default:
576 s = parseMixin();
577 break;
579 break;
581 case TOK.wchar_:
582 case TOK.dchar_:
583 case TOK.bool_:
584 case TOK.char_:
585 case TOK.int8:
586 case TOK.uns8:
587 case TOK.int16:
588 case TOK.uns16:
589 case TOK.int32:
590 case TOK.uns32:
591 case TOK.int64:
592 case TOK.uns64:
593 case TOK.int128:
594 case TOK.uns128:
595 case TOK.float32:
596 case TOK.float64:
597 case TOK.float80:
598 case TOK.imaginary32:
599 case TOK.imaginary64:
600 case TOK.imaginary80:
601 case TOK.complex32:
602 case TOK.complex64:
603 case TOK.complex80:
604 case TOK.void_:
605 case TOK.alias_:
606 case TOK.identifier:
607 case TOK.super_:
608 case TOK.typeof_:
609 case TOK.dot:
610 case TOK.vector:
611 case TOK.struct_:
612 case TOK.union_:
613 case TOK.class_:
614 case TOK.interface_:
615 case TOK.traits:
616 Ldeclaration:
617 a = parseDeclarations(false, pAttrs, pAttrs.comment);
618 if (a && a.dim)
619 *pLastDecl = (*a)[a.dim - 1];
620 break;
622 case TOK.this_:
623 if (peekNext() == TOK.dot)
624 goto Ldeclaration;
625 s = parseCtor(pAttrs);
626 break;
628 case TOK.tilde:
629 s = parseDtor(pAttrs);
630 break;
632 case TOK.invariant_:
633 const tv = peekNext();
634 if (tv == TOK.leftParenthesis || tv == TOK.leftCurly)
636 // invariant { statements... }
637 // invariant() { statements... }
638 // invariant (expression);
639 s = parseInvariant(pAttrs);
640 break;
642 error("invariant body expected, not `%s`", token.toChars());
643 goto Lerror;
645 case TOK.unittest_:
646 if (global.params.useUnitTests || global.params.doDocComments || global.params.doHdrGeneration)
648 s = parseUnitTest(pAttrs);
649 if (*pLastDecl)
650 (*pLastDecl).ddocUnittest = cast(AST.UnitTestDeclaration)s;
652 else
654 // Skip over unittest block by counting { }
655 Loc loc = token.loc;
656 int braces = 0;
657 while (1)
659 nextToken();
660 switch (token.value)
662 case TOK.leftCurly:
663 ++braces;
664 continue;
666 case TOK.rightCurly:
667 if (--braces)
668 continue;
669 nextToken();
670 break;
672 case TOK.endOfFile:
673 /* { */
674 error(loc, "closing `}` of unittest not found before end of file");
675 goto Lerror;
677 default:
678 continue;
680 break;
682 // Workaround 14894. Add an empty unittest declaration to keep
683 // the number of symbols in this scope independent of -unittest.
684 s = new AST.UnitTestDeclaration(loc, token.loc, STC.undefined_, null);
686 break;
688 case TOK.new_:
689 s = parseNew(pAttrs);
690 break;
692 case TOK.colon:
693 case TOK.leftCurly:
694 error("declaration expected, not `%s`", token.toChars());
695 goto Lerror;
697 case TOK.rightCurly:
698 case TOK.endOfFile:
699 if (once)
700 error("declaration expected, not `%s`", token.toChars());
701 return decldefs;
703 case TOK.static_:
705 const next = peekNext();
706 if (next == TOK.this_)
707 s = parseStaticCtor(pAttrs);
708 else if (next == TOK.tilde)
709 s = parseStaticDtor(pAttrs);
710 else if (next == TOK.assert_)
711 s = parseStaticAssert();
712 else if (next == TOK.if_)
714 const Loc loc = token.loc;
715 condition = parseStaticIfCondition();
716 AST.Dsymbols* athen;
717 if (token.value == TOK.colon)
718 athen = parseBlock(pLastDecl);
719 else
721 const lookingForElseSave = lookingForElse;
722 lookingForElse = token.loc;
723 athen = parseBlock(pLastDecl);
724 lookingForElse = lookingForElseSave;
726 AST.Dsymbols* aelse = null;
727 if (token.value == TOK.else_)
729 const elseloc = token.loc;
730 nextToken();
731 aelse = parseBlock(pLastDecl);
732 checkDanglingElse(elseloc);
734 s = new AST.StaticIfDeclaration(loc, condition, athen, aelse);
736 else if (next == TOK.import_)
738 a = parseImport();
739 // keep pLastDecl
741 else if (next == TOK.foreach_ || next == TOK.foreach_reverse_)
743 s = parseForeach!(AST.StaticForeachDeclaration)(token.loc, pLastDecl);
745 else
747 stc = STC.static_;
748 goto Lstc;
750 break;
752 case TOK.const_:
753 if (peekNext() == TOK.leftParenthesis)
754 goto Ldeclaration;
755 stc = STC.const_;
756 goto Lstc;
758 case TOK.immutable_:
759 if (peekNext() == TOK.leftParenthesis)
760 goto Ldeclaration;
761 stc = STC.immutable_;
762 goto Lstc;
764 case TOK.shared_:
766 const next = peekNext();
767 if (next == TOK.leftParenthesis)
768 goto Ldeclaration;
769 if (next == TOK.static_)
771 TOK next2 = peekNext2();
772 if (next2 == TOK.this_)
774 s = parseSharedStaticCtor(pAttrs);
775 break;
777 if (next2 == TOK.tilde)
779 s = parseSharedStaticDtor(pAttrs);
780 break;
783 stc = STC.shared_;
784 goto Lstc;
786 case TOK.inout_:
787 if (peekNext() == TOK.leftParenthesis)
788 goto Ldeclaration;
789 stc = STC.wild;
790 goto Lstc;
792 case TOK.final_:
793 stc = STC.final_;
794 goto Lstc;
796 case TOK.auto_:
797 stc = STC.auto_;
798 goto Lstc;
800 case TOK.scope_:
801 stc = STC.scope_;
802 goto Lstc;
804 case TOK.override_:
805 stc = STC.override_;
806 goto Lstc;
808 case TOK.abstract_:
809 stc = STC.abstract_;
810 goto Lstc;
812 case TOK.synchronized_:
813 stc = STC.synchronized_;
814 goto Lstc;
816 case TOK.nothrow_:
817 stc = STC.nothrow_;
818 goto Lstc;
820 case TOK.pure_:
821 stc = STC.pure_;
822 goto Lstc;
824 case TOK.ref_:
825 stc = STC.ref_;
826 goto Lstc;
828 case TOK.gshared:
829 stc = STC.gshared;
830 goto Lstc;
832 case TOK.at:
834 AST.Expressions* exps = null;
835 stc = parseAttribute(exps);
836 if (stc)
837 goto Lstc; // it's a predefined attribute
838 // no redundant/conflicting check for UDAs
839 pAttrs.udas = AST.UserAttributeDeclaration.concat(pAttrs.udas, exps);
840 goto Lautodecl;
842 Lstc:
843 pAttrs.storageClass = appendStorageClass(pAttrs.storageClass, stc);
844 nextToken();
846 Lautodecl:
848 /* Look for auto initializers:
849 * storage_class identifier = initializer;
850 * storage_class identifier(...) = initializer;
852 if (token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign))
854 a = parseAutoDeclarations(getStorageClass!AST(pAttrs), pAttrs.comment);
855 if (a && a.dim)
856 *pLastDecl = (*a)[a.dim - 1];
857 if (pAttrs.udas)
859 s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
860 pAttrs.udas = null;
862 break;
865 /* Look for return type inference for template functions.
867 Token* tk;
868 if (token.value == TOK.identifier && skipParens(peek(&token), &tk) && skipAttributes(tk, &tk) &&
869 (tk.value == TOK.leftParenthesis || tk.value == TOK.leftCurly || tk.value == TOK.in_ ||
870 tk.value == TOK.out_ || tk.value == TOK.do_ || tk.value == TOK.goesTo ||
871 tk.value == TOK.identifier && tk.ident == Id._body))
873 // @@@DEPRECATED@@@
874 // https://github.com/dlang/DIPs/blob/1f5959abe482b1f9094f6484a7d0a3ade77fc2fc/DIPs/accepted/DIP1003.md
875 // Deprecated in 2.097 - Can be removed from 2.117
876 // The deprecation period is longer than usual as `body`
877 // was quite widely used.
878 if (tk.value == TOK.identifier && tk.ident == Id._body)
879 deprecation("Usage of the `body` keyword is deprecated. Use `do` instead.");
881 a = parseDeclarations(true, pAttrs, pAttrs.comment);
882 if (a && a.dim)
883 *pLastDecl = (*a)[a.dim - 1];
884 if (pAttrs.udas)
886 s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
887 pAttrs.udas = null;
889 break;
892 a = parseBlock(pLastDecl, pAttrs);
893 auto stc2 = getStorageClass!AST(pAttrs);
894 if (stc2 != STC.undefined_)
896 s = new AST.StorageClassDeclaration(stc2, a);
898 if (pAttrs.udas)
900 if (s)
902 a = new AST.Dsymbols();
903 a.push(s);
905 s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
906 pAttrs.udas = null;
908 break;
910 case TOK.deprecated_:
912 stc |= STC.deprecated_;
913 if (!parseDeprecatedAttribute(pAttrs.depmsg))
914 goto Lstc;
916 a = parseBlock(pLastDecl, pAttrs);
917 s = new AST.DeprecatedDeclaration(pAttrs.depmsg, a);
918 pAttrs.depmsg = null;
919 break;
921 case TOK.leftBracket:
923 if (peekNext() == TOK.rightBracket)
924 error("empty attribute list is not allowed");
925 error("use `@(attributes)` instead of `[attributes]`");
926 AST.Expressions* exps = parseArguments();
927 // no redundant/conflicting check for UDAs
929 pAttrs.udas = AST.UserAttributeDeclaration.concat(pAttrs.udas, exps);
930 a = parseBlock(pLastDecl, pAttrs);
931 if (pAttrs.udas)
933 s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
934 pAttrs.udas = null;
936 break;
938 case TOK.extern_:
940 if (peekNext() != TOK.leftParenthesis)
942 stc = STC.extern_;
943 goto Lstc;
946 const linkLoc = token.loc;
947 auto res = parseLinkage();
948 if (pAttrs.link != LINK.default_)
950 if (pAttrs.link != res.link)
952 error("conflicting linkage `extern (%s)` and `extern (%s)`", AST.linkageToChars(pAttrs.link), AST.linkageToChars(res.link));
954 else if (res.idents || res.identExps || res.cppmangle != CPPMANGLE.def)
956 // Allow:
957 // extern(C++, foo) extern(C++, bar) void foo();
958 // to be equivalent with:
959 // extern(C++, foo.bar) void foo();
960 // Allow also:
961 // extern(C++, "ns") extern(C++, class) struct test {}
962 // extern(C++, class) extern(C++, "ns") struct test {}
964 else
965 error("redundant linkage `extern (%s)`", AST.linkageToChars(pAttrs.link));
967 pAttrs.link = res.link;
968 this.linkage = res.link;
969 this.linkLoc = linkLoc;
970 a = parseBlock(pLastDecl, pAttrs);
971 if (res.idents)
973 assert(res.link == LINK.cpp);
974 assert(res.idents.dim);
975 for (size_t i = res.idents.dim; i;)
977 Identifier id = (*res.idents)[--i];
978 if (s)
980 a = new AST.Dsymbols();
981 a.push(s);
983 s = new AST.Nspace(linkLoc, id, null, a);
985 pAttrs.link = LINK.default_;
987 else if (res.identExps)
989 assert(res.link == LINK.cpp);
990 assert(res.identExps.dim);
991 for (size_t i = res.identExps.dim; i;)
993 AST.Expression exp = (*res.identExps)[--i];
994 if (s)
996 a = new AST.Dsymbols();
997 a.push(s);
999 s = new AST.CPPNamespaceDeclaration(linkLoc, exp, a);
1001 pAttrs.link = LINK.default_;
1003 else if (res.cppmangle != CPPMANGLE.def)
1005 assert(res.link == LINK.cpp);
1006 s = new AST.CPPMangleDeclaration(linkLoc, res.cppmangle, a);
1008 else if (pAttrs.link != LINK.default_)
1010 s = new AST.LinkDeclaration(linkLoc, pAttrs.link, a);
1011 pAttrs.link = LINK.default_;
1013 break;
1016 case TOK.private_:
1017 prot = AST.Visibility.Kind.private_;
1018 goto Lprot;
1020 case TOK.package_:
1021 prot = AST.Visibility.Kind.package_;
1022 goto Lprot;
1024 case TOK.protected_:
1025 prot = AST.Visibility.Kind.protected_;
1026 goto Lprot;
1028 case TOK.public_:
1029 prot = AST.Visibility.Kind.public_;
1030 goto Lprot;
1032 case TOK.export_:
1033 prot = AST.Visibility.Kind.export_;
1034 goto Lprot;
1035 Lprot:
1037 if (pAttrs.visibility.kind != AST.Visibility.Kind.undefined)
1039 if (pAttrs.visibility.kind != prot)
1040 error("conflicting visibility attribute `%s` and `%s`", AST.visibilityToChars(pAttrs.visibility.kind), AST.visibilityToChars(prot));
1041 else
1042 error("redundant visibility attribute `%s`", AST.visibilityToChars(prot));
1044 pAttrs.visibility.kind = prot;
1046 nextToken();
1048 // optional qualified package identifier to bind
1049 // visibility to
1050 Identifier[] pkg_prot_idents;
1051 if (pAttrs.visibility.kind == AST.Visibility.Kind.package_ && token.value == TOK.leftParenthesis)
1053 pkg_prot_idents = parseQualifiedIdentifier("protection package");
1054 if (pkg_prot_idents)
1055 check(TOK.rightParenthesis);
1056 else
1058 while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
1059 nextToken();
1060 nextToken();
1061 break;
1065 const attrloc = token.loc;
1066 a = parseBlock(pLastDecl, pAttrs);
1067 if (pAttrs.visibility.kind != AST.Visibility.Kind.undefined)
1069 if (pAttrs.visibility.kind == AST.Visibility.Kind.package_ && pkg_prot_idents)
1070 s = new AST.VisibilityDeclaration(attrloc, pkg_prot_idents, a);
1071 else
1072 s = new AST.VisibilityDeclaration(attrloc, pAttrs.visibility, a);
1074 pAttrs.visibility = AST.Visibility(AST.Visibility.Kind.undefined);
1076 break;
1078 case TOK.align_:
1080 const attrLoc = token.loc;
1082 nextToken();
1084 AST.Expression e = null; // default
1085 if (token.value == TOK.leftParenthesis)
1087 nextToken();
1088 e = parseAssignExp();
1089 check(TOK.rightParenthesis);
1092 if (pAttrs.setAlignment)
1094 if (e)
1095 error("redundant alignment attribute `align(%s)`", e.toChars());
1096 else
1097 error("redundant alignment attribute `align`");
1100 pAttrs.setAlignment = true;
1101 pAttrs.ealign = e;
1102 a = parseBlock(pLastDecl, pAttrs);
1103 if (pAttrs.setAlignment)
1105 s = new AST.AlignDeclaration(attrLoc, pAttrs.ealign, a);
1106 pAttrs.setAlignment = false;
1107 pAttrs.ealign = null;
1109 break;
1111 case TOK.pragma_:
1113 AST.Expressions* args = null;
1114 const loc = token.loc;
1116 nextToken();
1117 check(TOK.leftParenthesis);
1118 if (token.value != TOK.identifier)
1120 error("`pragma(identifier)` expected");
1121 goto Lerror;
1123 Identifier ident = token.ident;
1124 nextToken();
1125 if (token.value == TOK.comma && peekNext() != TOK.rightParenthesis)
1126 args = parseArguments(); // pragma(identifier, args...)
1127 else
1128 check(TOK.rightParenthesis); // pragma(identifier)
1130 AST.Dsymbols* a2 = null;
1131 if (token.value == TOK.semicolon)
1133 /* https://issues.dlang.org/show_bug.cgi?id=2354
1134 * Accept single semicolon as an empty
1135 * DeclarationBlock following attribute.
1137 * Attribute DeclarationBlock
1138 * Pragma DeclDef
1141 nextToken();
1143 else
1144 a2 = parseBlock(pLastDecl);
1145 s = new AST.PragmaDeclaration(loc, ident, args, a2);
1146 break;
1148 case TOK.debug_:
1149 startloc = token.loc;
1150 nextToken();
1151 if (token.value == TOK.assign)
1153 nextToken();
1154 if (token.value == TOK.identifier)
1155 s = new AST.DebugSymbol(token.loc, token.ident);
1156 else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
1157 s = new AST.DebugSymbol(token.loc, cast(uint)token.unsvalue);
1158 else
1160 error("identifier or integer expected, not `%s`", token.toChars());
1161 s = null;
1163 nextToken();
1164 if (token.value != TOK.semicolon)
1165 error("semicolon expected");
1166 nextToken();
1167 break;
1170 condition = parseDebugCondition();
1171 goto Lcondition;
1173 case TOK.version_:
1174 startloc = token.loc;
1175 nextToken();
1176 if (token.value == TOK.assign)
1178 nextToken();
1179 if (token.value == TOK.identifier)
1180 s = new AST.VersionSymbol(token.loc, token.ident);
1181 else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
1182 s = new AST.VersionSymbol(token.loc, cast(uint)token.unsvalue);
1183 else
1185 error("identifier or integer expected, not `%s`", token.toChars());
1186 s = null;
1188 nextToken();
1189 if (token.value != TOK.semicolon)
1190 error("semicolon expected");
1191 nextToken();
1192 break;
1194 condition = parseVersionCondition();
1195 goto Lcondition;
1197 Lcondition:
1199 AST.Dsymbols* athen;
1200 if (token.value == TOK.colon)
1201 athen = parseBlock(pLastDecl);
1202 else
1204 const lookingForElseSave = lookingForElse;
1205 lookingForElse = token.loc;
1206 athen = parseBlock(pLastDecl);
1207 lookingForElse = lookingForElseSave;
1209 AST.Dsymbols* aelse = null;
1210 if (token.value == TOK.else_)
1212 const elseloc = token.loc;
1213 nextToken();
1214 aelse = parseBlock(pLastDecl);
1215 checkDanglingElse(elseloc);
1217 s = new AST.ConditionalDeclaration(startloc, condition, athen, aelse);
1218 break;
1220 case TOK.semicolon:
1221 // empty declaration
1222 //error("empty declaration");
1223 nextToken();
1224 continue;
1226 default:
1227 error("declaration expected, not `%s`", token.toChars());
1228 Lerror:
1229 while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
1230 nextToken();
1231 nextToken();
1232 s = null;
1233 continue;
1236 if (s)
1238 if (!s.isAttribDeclaration())
1239 *pLastDecl = s;
1240 decldefs.push(s);
1241 addComment(s, pAttrs.comment);
1243 else if (a && a.dim)
1245 decldefs.append(a);
1248 while (!once);
1250 linkage = linksave;
1252 return decldefs;
1255 /*****************************************
1256 * Parse auto declarations of the form:
1257 * storageClass ident = init, ident = init, ... ;
1258 * and return the array of them.
1259 * Starts with token on the first ident.
1260 * Ends with scanner past closing ';'
1262 private AST.Dsymbols* parseAutoDeclarations(StorageClass storageClass, const(char)* comment)
1264 //printf("parseAutoDeclarations\n");
1265 auto a = new AST.Dsymbols();
1267 while (1)
1269 const loc = token.loc;
1270 Identifier ident = token.ident;
1271 nextToken(); // skip over ident
1273 AST.TemplateParameters* tpl = null;
1274 if (token.value == TOK.leftParenthesis)
1275 tpl = parseTemplateParameterList();
1277 check(TOK.assign); // skip over '='
1278 AST.Initializer _init = parseInitializer();
1279 auto v = new AST.VarDeclaration(loc, null, ident, _init, storageClass);
1281 AST.Dsymbol s = v;
1282 if (tpl)
1284 auto a2 = new AST.Dsymbols();
1285 a2.push(v);
1286 auto tempdecl = new AST.TemplateDeclaration(loc, ident, tpl, null, a2, 0);
1287 s = tempdecl;
1289 a.push(s);
1290 switch (token.value)
1292 case TOK.semicolon:
1293 nextToken();
1294 addComment(s, comment);
1295 break;
1297 case TOK.comma:
1298 nextToken();
1299 if (!(token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign)))
1301 error("identifier expected following comma");
1302 break;
1304 addComment(s, comment);
1305 continue;
1307 default:
1308 error("semicolon expected following auto declaration, not `%s`", token.toChars());
1309 break;
1311 break;
1313 return a;
1316 /********************************************
1317 * Parse declarations after an align, visibility, or extern decl.
1319 private AST.Dsymbols* parseBlock(AST.Dsymbol* pLastDecl, PrefixAttributes!AST* pAttrs = null)
1321 AST.Dsymbols* a = null;
1323 //printf("parseBlock()\n");
1324 switch (token.value)
1326 case TOK.semicolon:
1327 error("declaration expected following attribute, not `;`");
1328 nextToken();
1329 break;
1331 case TOK.endOfFile:
1332 error("declaration expected following attribute, not end of file");
1333 break;
1335 case TOK.leftCurly:
1337 const lookingForElseSave = lookingForElse;
1338 lookingForElse = Loc();
1340 nextToken();
1341 a = parseDeclDefs(0, pLastDecl);
1342 if (token.value != TOK.rightCurly)
1344 /* { */
1345 error("matching `}` expected, not `%s`", token.toChars());
1347 else
1348 nextToken();
1349 lookingForElse = lookingForElseSave;
1350 break;
1352 case TOK.colon:
1353 nextToken();
1354 a = parseDeclDefs(0, pLastDecl); // grab declarations up to closing curly bracket
1355 break;
1357 default:
1358 a = parseDeclDefs(1, pLastDecl, pAttrs);
1359 break;
1361 return a;
1365 * Provide an error message if `added` contains storage classes which are
1366 * redundant with those in `orig`; otherwise, return the combination.
1368 * Params:
1369 * orig = The already applied storage class.
1370 * added = The new storage class to add to `orig`.
1372 * Returns:
1373 * The combination of both storage classes (`orig | added`).
1375 private StorageClass appendStorageClass(StorageClass orig, StorageClass added)
1377 if (orig & added)
1379 OutBuffer buf;
1380 AST.stcToBuffer(&buf, added);
1381 error("redundant attribute `%s`", buf.peekChars());
1382 return orig | added;
1385 const Redundant = (STC.const_ | STC.scope_ |
1386 (global.params.previewIn ? STC.ref_ : 0));
1387 orig |= added;
1389 if ((orig & STC.in_) && (added & Redundant))
1391 if (added & STC.const_)
1392 error("attribute `const` is redundant with previously-applied `in`");
1393 else if (global.params.previewIn)
1395 error("attribute `%s` is redundant with previously-applied `in`",
1396 (orig & STC.scope_) ? "scope".ptr : "ref".ptr);
1398 else
1399 error("attribute `scope` cannot be applied with `in`, use `-preview=in` instead");
1400 return orig;
1403 if ((added & STC.in_) && (orig & Redundant))
1405 if (orig & STC.const_)
1406 error("attribute `in` cannot be added after `const`: remove `const`");
1407 else if (global.params.previewIn)
1409 // Windows `printf` does not support `%1$s`
1410 const(char*) stc_str = (orig & STC.scope_) ? "scope".ptr : "ref".ptr;
1411 error("attribute `in` cannot be added after `%s`: remove `%s`",
1412 stc_str, stc_str);
1414 else
1415 error("attribute `in` cannot be added after `scope`: remove `scope` and use `-preview=in`");
1416 return orig;
1419 if (added & (STC.const_ | STC.immutable_ | STC.manifest))
1421 StorageClass u = orig & (STC.const_ | STC.immutable_ | STC.manifest);
1422 if (u & (u - 1))
1423 error("conflicting attribute `%s`", Token.toChars(token.value));
1425 if (added & (STC.gshared | STC.shared_ | STC.tls))
1427 StorageClass u = orig & (STC.gshared | STC.shared_ | STC.tls);
1428 if (u & (u - 1))
1429 error("conflicting attribute `%s`", Token.toChars(token.value));
1431 if (added & STC.safeGroup)
1433 StorageClass u = orig & STC.safeGroup;
1434 if (u & (u - 1))
1435 error("conflicting attribute `@%s`", token.toChars());
1438 return orig;
1441 /***********************************************
1442 * Parse attribute(s), lexer is on '@'.
1444 * Attributes can be builtin (e.g. `@safe`, `@nogc`, etc...),
1445 * or be user-defined (UDAs). In the former case, we return the storage
1446 * class via the return value, while in thelater case we return `0`
1447 * and set `pudas`.
1449 * Params:
1450 * pudas = An array of UDAs to append to
1452 * Returns:
1453 * If the attribute is builtin, the return value will be non-zero.
1454 * Otherwise, 0 is returned, and `pudas` will be appended to.
1456 private StorageClass parseAttribute(ref AST.Expressions* udas)
1458 nextToken();
1459 if (token.value == TOK.identifier)
1461 // If we find a builtin attribute, we're done, return immediately.
1462 if (StorageClass stc = isBuiltinAtAttribute(token.ident))
1463 return stc;
1465 // Allow identifier, template instantiation, or function call
1466 // for `@Argument` (single UDA) form.
1467 AST.Expression exp = parsePrimaryExp();
1468 if (token.value == TOK.leftParenthesis)
1470 const loc = token.loc;
1471 exp = new AST.CallExp(loc, exp, parseArguments());
1474 if (udas is null)
1475 udas = new AST.Expressions();
1476 udas.push(exp);
1477 return 0;
1480 if (token.value == TOK.leftParenthesis)
1482 // Multi-UDAs ( `@( ArgumentList )`) form, concatenate with existing
1483 if (peekNext() == TOK.rightParenthesis)
1484 error("empty attribute list is not allowed");
1485 udas = AST.UserAttributeDeclaration.concat(udas, parseArguments());
1486 return 0;
1489 if (token.isKeyword())
1490 error("`%s` is a keyword, not an `@` attribute", token.toChars());
1491 else
1492 error("`@identifier` or `@(ArgumentList)` expected, not `@%s`", token.toChars());
1494 return 0;
1497 /***********************************************
1498 * Parse const/immutable/shared/inout/nothrow/pure postfix
1500 private StorageClass parsePostfix(StorageClass storageClass, AST.Expressions** pudas)
1502 while (1)
1504 StorageClass stc;
1505 switch (token.value)
1507 case TOK.const_:
1508 stc = STC.const_;
1509 break;
1511 case TOK.immutable_:
1512 stc = STC.immutable_;
1513 break;
1515 case TOK.shared_:
1516 stc = STC.shared_;
1517 break;
1519 case TOK.inout_:
1520 stc = STC.wild;
1521 break;
1523 case TOK.nothrow_:
1524 stc = STC.nothrow_;
1525 break;
1527 case TOK.pure_:
1528 stc = STC.pure_;
1529 break;
1531 case TOK.return_:
1532 stc = STC.return_;
1533 break;
1535 case TOK.scope_:
1536 stc = STC.scope_;
1537 break;
1539 case TOK.at:
1541 AST.Expressions* udas = null;
1542 stc = parseAttribute(udas);
1543 if (udas)
1545 if (pudas)
1546 *pudas = AST.UserAttributeDeclaration.concat(*pudas, udas);
1547 else
1549 // Disallow:
1550 // void function() @uda fp;
1551 // () @uda { return 1; }
1552 error("user-defined attributes cannot appear as postfixes");
1554 continue;
1556 break;
1558 default:
1559 return storageClass;
1561 storageClass = appendStorageClass(storageClass, stc);
1562 nextToken();
1566 private StorageClass parseTypeCtor()
1568 StorageClass storageClass = STC.undefined_;
1570 while (1)
1572 if (peekNext() == TOK.leftParenthesis)
1573 return storageClass;
1575 StorageClass stc;
1576 switch (token.value)
1578 case TOK.const_:
1579 stc = STC.const_;
1580 break;
1582 case TOK.immutable_:
1583 stc = STC.immutable_;
1584 break;
1586 case TOK.shared_:
1587 stc = STC.shared_;
1588 break;
1590 case TOK.inout_:
1591 stc = STC.wild;
1592 break;
1594 default:
1595 return storageClass;
1597 storageClass = appendStorageClass(storageClass, stc);
1598 nextToken();
1602 /**************************************
1603 * Parse constraint.
1604 * Constraint is of the form:
1605 * if ( ConstraintExpression )
1607 private AST.Expression parseConstraint()
1609 AST.Expression e = null;
1610 if (token.value == TOK.if_)
1612 nextToken(); // skip over 'if'
1613 check(TOK.leftParenthesis);
1614 e = parseExpression();
1615 check(TOK.rightParenthesis);
1617 return e;
1620 /**************************************
1621 * Parse a TemplateDeclaration.
1623 private AST.TemplateDeclaration parseTemplateDeclaration(bool ismixin = false)
1625 AST.TemplateDeclaration tempdecl;
1626 Identifier id;
1627 AST.TemplateParameters* tpl;
1628 AST.Dsymbols* decldefs;
1629 AST.Expression constraint = null;
1630 const loc = token.loc;
1632 nextToken();
1633 if (token.value != TOK.identifier)
1635 error("identifier expected following `template`");
1636 goto Lerr;
1638 id = token.ident;
1639 nextToken();
1640 tpl = parseTemplateParameterList();
1641 if (!tpl)
1642 goto Lerr;
1644 constraint = parseConstraint();
1646 if (token.value != TOK.leftCurly)
1648 error("members of template declaration expected");
1649 goto Lerr;
1651 decldefs = parseBlock(null);
1653 tempdecl = new AST.TemplateDeclaration(loc, id, tpl, constraint, decldefs, ismixin);
1654 return tempdecl;
1656 Lerr:
1657 return null;
1660 /******************************************
1661 * Parse template parameter list.
1662 * Input:
1663 * flag 0: parsing "( list )"
1664 * 1: parsing non-empty "list $(RPAREN)"
1666 private AST.TemplateParameters* parseTemplateParameterList(int flag = 0)
1668 auto tpl = new AST.TemplateParameters();
1670 if (!flag && token.value != TOK.leftParenthesis)
1672 error("parenthesized template parameter list expected following template identifier");
1673 goto Lerr;
1675 nextToken();
1677 // Get array of TemplateParameters
1678 if (flag || token.value != TOK.rightParenthesis)
1680 while (token.value != TOK.rightParenthesis)
1682 AST.TemplateParameter tp;
1683 Loc loc;
1684 Identifier tp_ident = null;
1685 AST.Type tp_spectype = null;
1686 AST.Type tp_valtype = null;
1687 AST.Type tp_defaulttype = null;
1688 AST.Expression tp_specvalue = null;
1689 AST.Expression tp_defaultvalue = null;
1691 // Get TemplateParameter
1693 // First, look ahead to see if it is a TypeParameter or a ValueParameter
1694 const tv = peekNext();
1695 if (token.value == TOK.alias_)
1697 // AliasParameter
1698 nextToken();
1699 loc = token.loc; // todo
1700 AST.Type spectype = null;
1701 if (isDeclaration(&token, NeedDeclaratorId.must, TOK.reserved, null))
1703 spectype = parseType(&tp_ident);
1705 else
1707 if (token.value != TOK.identifier)
1709 error("identifier expected for template `alias` parameter");
1710 goto Lerr;
1712 tp_ident = token.ident;
1713 nextToken();
1715 RootObject spec = null;
1716 if (token.value == TOK.colon) // : Type
1718 nextToken();
1719 if (isDeclaration(&token, NeedDeclaratorId.no, TOK.reserved, null))
1720 spec = parseType();
1721 else
1722 spec = parseCondExp();
1724 RootObject def = null;
1725 if (token.value == TOK.assign) // = Type
1727 nextToken();
1728 if (isDeclaration(&token, NeedDeclaratorId.no, TOK.reserved, null))
1729 def = parseType();
1730 else
1731 def = parseCondExp();
1733 tp = new AST.TemplateAliasParameter(loc, tp_ident, spectype, spec, def);
1735 else if (tv == TOK.colon || tv == TOK.assign || tv == TOK.comma || tv == TOK.rightParenthesis)
1737 // TypeParameter
1738 if (token.value != TOK.identifier)
1740 error("identifier expected for template type parameter");
1741 goto Lerr;
1743 loc = token.loc;
1744 tp_ident = token.ident;
1745 nextToken();
1746 if (token.value == TOK.colon) // : Type
1748 nextToken();
1749 tp_spectype = parseType();
1751 if (token.value == TOK.assign) // = Type
1753 nextToken();
1754 tp_defaulttype = parseType();
1756 tp = new AST.TemplateTypeParameter(loc, tp_ident, tp_spectype, tp_defaulttype);
1758 else if (token.value == TOK.identifier && tv == TOK.dotDotDot)
1760 // ident...
1761 loc = token.loc;
1762 tp_ident = token.ident;
1763 nextToken();
1764 nextToken();
1765 tp = new AST.TemplateTupleParameter(loc, tp_ident);
1767 else if (token.value == TOK.this_)
1769 // ThisParameter
1770 nextToken();
1771 if (token.value != TOK.identifier)
1773 error("identifier expected for template `this` parameter");
1774 goto Lerr;
1776 loc = token.loc;
1777 tp_ident = token.ident;
1778 nextToken();
1779 if (token.value == TOK.colon) // : Type
1781 nextToken();
1782 tp_spectype = parseType();
1784 if (token.value == TOK.assign) // = Type
1786 nextToken();
1787 tp_defaulttype = parseType();
1789 tp = new AST.TemplateThisParameter(loc, tp_ident, tp_spectype, tp_defaulttype);
1791 else
1793 // ValueParameter
1794 loc = token.loc; // todo
1795 tp_valtype = parseType(&tp_ident);
1796 if (!tp_ident)
1798 error("identifier expected for template value parameter");
1799 tp_ident = Identifier.idPool("error");
1801 if (token.value == TOK.colon) // : CondExpression
1803 nextToken();
1804 tp_specvalue = parseCondExp();
1806 if (token.value == TOK.assign) // = CondExpression
1808 nextToken();
1809 tp_defaultvalue = parseDefaultInitExp();
1811 tp = new AST.TemplateValueParameter(loc, tp_ident, tp_valtype, tp_specvalue, tp_defaultvalue);
1813 tpl.push(tp);
1814 if (token.value != TOK.comma)
1815 break;
1816 nextToken();
1819 check(TOK.rightParenthesis);
1821 Lerr:
1822 return tpl;
1825 /******************************************
1826 * Parse template mixin.
1827 * mixin Foo;
1828 * mixin Foo!(args);
1829 * mixin a.b.c!(args).Foo!(args);
1830 * mixin Foo!(args) identifier;
1831 * mixin typeof(expr).identifier!(args);
1833 private AST.Dsymbol parseMixin()
1835 AST.TemplateMixin tm;
1836 Identifier id;
1837 AST.Objects* tiargs;
1839 //printf("parseMixin()\n");
1840 const locMixin = token.loc;
1841 nextToken(); // skip 'mixin'
1843 auto loc = token.loc;
1844 AST.TypeQualified tqual = null;
1845 if (token.value == TOK.dot)
1847 id = Id.empty;
1849 else
1851 if (token.value == TOK.typeof_)
1853 tqual = parseTypeof();
1854 check(TOK.dot);
1856 if (token.value != TOK.identifier)
1858 error("identifier expected, not `%s`", token.toChars());
1859 id = Id.empty;
1861 else
1862 id = token.ident;
1863 nextToken();
1866 while (1)
1868 tiargs = null;
1869 if (token.value == TOK.not)
1871 tiargs = parseTemplateArguments();
1874 if (tiargs && token.value == TOK.dot)
1876 auto tempinst = new AST.TemplateInstance(loc, id, tiargs);
1877 if (!tqual)
1878 tqual = new AST.TypeInstance(loc, tempinst);
1879 else
1880 tqual.addInst(tempinst);
1881 tiargs = null;
1883 else
1885 if (!tqual)
1886 tqual = new AST.TypeIdentifier(loc, id);
1887 else
1888 tqual.addIdent(id);
1891 if (token.value != TOK.dot)
1892 break;
1894 nextToken();
1895 if (token.value != TOK.identifier)
1897 error("identifier expected following `.` instead of `%s`", token.toChars());
1898 break;
1900 loc = token.loc;
1901 id = token.ident;
1902 nextToken();
1905 id = null;
1906 if (token.value == TOK.identifier)
1908 id = token.ident;
1909 nextToken();
1912 tm = new AST.TemplateMixin(locMixin, id, tqual, tiargs);
1913 if (token.value != TOK.semicolon)
1914 error("`;` expected after `mixin`");
1915 nextToken();
1917 return tm;
1920 /******************************************
1921 * Parse template arguments.
1922 * Input:
1923 * current token is opening '!'
1924 * Output:
1925 * current token is one after closing '$(RPAREN)'
1927 private AST.Objects* parseTemplateArguments()
1929 AST.Objects* tiargs;
1931 nextToken();
1932 if (token.value == TOK.leftParenthesis)
1934 // ident!(template_arguments)
1935 tiargs = parseTemplateArgumentList();
1937 else
1939 // ident!template_argument
1940 tiargs = parseTemplateSingleArgument();
1942 if (token.value == TOK.not)
1944 TOK tok = peekNext();
1945 if (tok != TOK.is_ && tok != TOK.in_)
1947 error("multiple ! arguments are not allowed");
1948 Lagain:
1949 nextToken();
1950 if (token.value == TOK.leftParenthesis)
1951 parseTemplateArgumentList();
1952 else
1953 parseTemplateSingleArgument();
1954 if (token.value == TOK.not && (tok = peekNext()) != TOK.is_ && tok != TOK.in_)
1955 goto Lagain;
1958 return tiargs;
1961 /******************************************
1962 * Parse template argument list.
1963 * Input:
1964 * current token is opening '$(LPAREN)',
1965 * or ',' for __traits
1966 * Output:
1967 * current token is one after closing '$(RPAREN)'
1969 private AST.Objects* parseTemplateArgumentList()
1971 //printf("Parser::parseTemplateArgumentList()\n");
1972 auto tiargs = new AST.Objects();
1973 TOK endtok = TOK.rightParenthesis;
1974 assert(token.value == TOK.leftParenthesis || token.value == TOK.comma);
1975 nextToken();
1977 // Get TemplateArgumentList
1978 while (token.value != endtok)
1980 tiargs.push(parseTypeOrAssignExp());
1981 if (token.value != TOK.comma)
1982 break;
1983 nextToken();
1985 check(endtok, "template argument list");
1986 return tiargs;
1989 /***************************************
1990 * Parse a Type or an Expression
1991 * Returns:
1992 * RootObject representing the AST
1994 RootObject parseTypeOrAssignExp(TOK endtoken = TOK.reserved)
1996 return isDeclaration(&token, NeedDeclaratorId.no, endtoken, null)
1997 ? parseType() // argument is a type
1998 : parseAssignExp(); // argument is an expression
2001 /*****************************
2002 * Parse single template argument, to support the syntax:
2003 * foo!arg
2004 * Input:
2005 * current token is the arg
2007 private AST.Objects* parseTemplateSingleArgument()
2009 //printf("parseTemplateSingleArgument()\n");
2010 auto tiargs = new AST.Objects();
2011 AST.Type ta;
2012 switch (token.value)
2014 case TOK.identifier:
2015 ta = new AST.TypeIdentifier(token.loc, token.ident);
2016 goto LabelX;
2018 case TOK.vector:
2019 ta = parseVector();
2020 goto LabelX;
2022 case TOK.void_:
2023 ta = AST.Type.tvoid;
2024 goto LabelX;
2026 case TOK.int8:
2027 ta = AST.Type.tint8;
2028 goto LabelX;
2030 case TOK.uns8:
2031 ta = AST.Type.tuns8;
2032 goto LabelX;
2034 case TOK.int16:
2035 ta = AST.Type.tint16;
2036 goto LabelX;
2038 case TOK.uns16:
2039 ta = AST.Type.tuns16;
2040 goto LabelX;
2042 case TOK.int32:
2043 ta = AST.Type.tint32;
2044 goto LabelX;
2046 case TOK.uns32:
2047 ta = AST.Type.tuns32;
2048 goto LabelX;
2050 case TOK.int64:
2051 ta = AST.Type.tint64;
2052 goto LabelX;
2054 case TOK.uns64:
2055 ta = AST.Type.tuns64;
2056 goto LabelX;
2058 case TOK.int128:
2059 ta = AST.Type.tint128;
2060 goto LabelX;
2062 case TOK.uns128:
2063 ta = AST.Type.tuns128;
2064 goto LabelX;
2066 case TOK.float32:
2067 ta = AST.Type.tfloat32;
2068 goto LabelX;
2070 case TOK.float64:
2071 ta = AST.Type.tfloat64;
2072 goto LabelX;
2074 case TOK.float80:
2075 ta = AST.Type.tfloat80;
2076 goto LabelX;
2078 case TOK.imaginary32:
2079 ta = AST.Type.timaginary32;
2080 goto LabelX;
2082 case TOK.imaginary64:
2083 ta = AST.Type.timaginary64;
2084 goto LabelX;
2086 case TOK.imaginary80:
2087 ta = AST.Type.timaginary80;
2088 goto LabelX;
2090 case TOK.complex32:
2091 ta = AST.Type.tcomplex32;
2092 goto LabelX;
2094 case TOK.complex64:
2095 ta = AST.Type.tcomplex64;
2096 goto LabelX;
2098 case TOK.complex80:
2099 ta = AST.Type.tcomplex80;
2100 goto LabelX;
2102 case TOK.bool_:
2103 ta = AST.Type.tbool;
2104 goto LabelX;
2106 case TOK.char_:
2107 ta = AST.Type.tchar;
2108 goto LabelX;
2110 case TOK.wchar_:
2111 ta = AST.Type.twchar;
2112 goto LabelX;
2114 case TOK.dchar_:
2115 ta = AST.Type.tdchar;
2116 goto LabelX;
2117 LabelX:
2118 tiargs.push(ta);
2119 nextToken();
2120 break;
2122 case TOK.int32Literal:
2123 case TOK.uns32Literal:
2124 case TOK.int64Literal:
2125 case TOK.uns64Literal:
2126 case TOK.int128Literal:
2127 case TOK.uns128Literal:
2128 case TOK.float32Literal:
2129 case TOK.float64Literal:
2130 case TOK.float80Literal:
2131 case TOK.imaginary32Literal:
2132 case TOK.imaginary64Literal:
2133 case TOK.imaginary80Literal:
2134 case TOK.null_:
2135 case TOK.true_:
2136 case TOK.false_:
2137 case TOK.charLiteral:
2138 case TOK.wcharLiteral:
2139 case TOK.dcharLiteral:
2140 case TOK.string_:
2141 case TOK.hexadecimalString:
2142 case TOK.file:
2143 case TOK.fileFullPath:
2144 case TOK.line:
2145 case TOK.moduleString:
2146 case TOK.functionString:
2147 case TOK.prettyFunction:
2148 case TOK.this_:
2150 // Template argument is an expression
2151 AST.Expression ea = parsePrimaryExp();
2152 tiargs.push(ea);
2153 break;
2155 default:
2156 error("template argument expected following `!`");
2157 break;
2159 return tiargs;
2162 /**********************************
2163 * Parse a static assertion.
2164 * Current token is 'static'.
2166 private AST.StaticAssert parseStaticAssert()
2168 const loc = token.loc;
2169 AST.Expression exp;
2170 AST.Expression msg = null;
2172 //printf("parseStaticAssert()\n");
2173 nextToken();
2174 nextToken();
2175 check(TOK.leftParenthesis);
2176 exp = parseAssignExp();
2177 if (token.value == TOK.comma)
2179 nextToken();
2180 if (token.value != TOK.rightParenthesis)
2182 msg = parseAssignExp();
2183 if (token.value == TOK.comma)
2184 nextToken();
2187 check(TOK.rightParenthesis);
2188 check(TOK.semicolon);
2189 return new AST.StaticAssert(loc, exp, msg);
2192 /***********************************
2193 * Parse typeof(expression).
2194 * Current token is on the 'typeof'.
2196 private AST.TypeQualified parseTypeof()
2198 AST.TypeQualified t;
2199 const loc = token.loc;
2201 nextToken();
2202 check(TOK.leftParenthesis);
2203 if (token.value == TOK.return_) // typeof(return)
2205 nextToken();
2206 t = new AST.TypeReturn(loc);
2208 else
2210 AST.Expression exp = parseExpression(); // typeof(expression)
2211 t = new AST.TypeTypeof(loc, exp);
2213 check(TOK.rightParenthesis);
2214 return t;
2217 /***********************************
2218 * Parse __vector(type).
2219 * Current token is on the '__vector'.
2221 private AST.Type parseVector()
2223 nextToken();
2224 check(TOK.leftParenthesis);
2225 AST.Type tb = parseType();
2226 check(TOK.rightParenthesis);
2227 return new AST.TypeVector(tb);
2230 /***********************************
2231 * Parse:
2232 * extern (linkage)
2233 * extern (C++, namespaces)
2234 * extern (C++, "namespace", "namespaces", ...)
2235 * extern (C++, (StringExp))
2236 * The parser is on the 'extern' token.
2238 private ParsedLinkage!(AST) parseLinkage()
2240 ParsedLinkage!(AST) result;
2241 nextToken();
2242 assert(token.value == TOK.leftParenthesis);
2243 nextToken();
2244 ParsedLinkage!(AST) returnLinkage(LINK link)
2246 check(TOK.rightParenthesis);
2247 result.link = link;
2248 return result;
2250 ParsedLinkage!(AST) invalidLinkage()
2252 error("valid linkage identifiers are `D`, `C`, `C++`, `Objective-C`, `Windows`, `System`");
2253 return returnLinkage(LINK.d);
2256 if (token.value != TOK.identifier)
2257 return returnLinkage(LINK.d);
2259 Identifier id = token.ident;
2260 nextToken();
2261 if (id == Id.Windows)
2262 return returnLinkage(LINK.windows);
2263 else if (id == Id.D)
2264 return returnLinkage(LINK.d);
2265 else if (id == Id.System)
2266 return returnLinkage(LINK.system);
2267 else if (id == Id.Objective) // Looking for tokens "Objective-C"
2269 if (token.value != TOK.min)
2270 return invalidLinkage();
2272 nextToken();
2273 if (token.ident != Id.C)
2274 return invalidLinkage();
2276 nextToken();
2277 return returnLinkage(LINK.objc);
2279 else if (id != Id.C)
2280 return invalidLinkage();
2282 if (token.value != TOK.plusPlus)
2283 return returnLinkage(LINK.c);
2285 nextToken();
2286 if (token.value != TOK.comma) // , namespaces or class or struct
2287 return returnLinkage(LINK.cpp);
2289 nextToken();
2291 if (token.value == TOK.rightParenthesis)
2292 return returnLinkage(LINK.cpp); // extern(C++,)
2294 if (token.value == TOK.class_ || token.value == TOK.struct_)
2296 result.cppmangle = token.value == TOK.class_ ? CPPMANGLE.asClass : CPPMANGLE.asStruct;
2297 nextToken();
2299 else if (token.value == TOK.identifier) // named scope namespace
2301 result.idents = new AST.Identifiers();
2302 while (1)
2304 Identifier idn = token.ident;
2305 result.idents.push(idn);
2306 nextToken();
2307 if (token.value == TOK.dot)
2309 nextToken();
2310 if (token.value == TOK.identifier)
2311 continue;
2312 error("identifier expected for C++ namespace");
2313 result.idents = null; // error occurred, invalidate list of elements.
2315 break;
2318 else // non-scoped StringExp namespace
2320 result.identExps = new AST.Expressions();
2321 while (1)
2323 result.identExps.push(parseCondExp());
2324 if (token.value != TOK.comma)
2325 break;
2326 nextToken();
2327 // Allow trailing commas as done for argument lists, arrays, ...
2328 if (token.value == TOK.rightParenthesis)
2329 break;
2332 return returnLinkage(LINK.cpp);
2335 /***********************************
2336 * Parse ident1.ident2.ident3
2338 * Params:
2339 * entity = what qualified identifier is expected to resolve into.
2340 * Used only for better error message
2342 * Returns:
2343 * array of identifiers with actual qualified one stored last
2345 private Identifier[] parseQualifiedIdentifier(const(char)* entity)
2347 Identifier[] qualified;
2351 nextToken();
2352 if (token.value != TOK.identifier)
2354 error("`%s` expected as dot-separated identifiers, got `%s`", entity, token.toChars());
2355 return qualified;
2358 Identifier id = token.ident;
2359 qualified ~= id;
2361 nextToken();
2363 while (token.value == TOK.dot);
2365 return qualified;
2368 /**************************************
2369 * Parse a debug conditional
2371 private AST.Condition parseDebugCondition()
2373 uint level = 1;
2374 Identifier id = null;
2375 Loc loc = token.loc;
2377 if (token.value == TOK.leftParenthesis)
2379 nextToken();
2381 if (token.value == TOK.identifier)
2382 id = token.ident;
2383 else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
2384 level = cast(uint)token.unsvalue;
2385 else
2386 error("identifier or integer expected inside `debug(...)`, not `%s`", token.toChars());
2387 loc = token.loc;
2388 nextToken();
2389 check(TOK.rightParenthesis);
2391 return new AST.DebugCondition(loc, mod, level, id);
2394 /**************************************
2395 * Parse a version conditional
2397 private AST.Condition parseVersionCondition()
2399 uint level = 1;
2400 Identifier id = null;
2401 Loc loc;
2403 if (token.value == TOK.leftParenthesis)
2405 nextToken();
2406 /* Allow:
2407 * version (unittest)
2408 * version (assert)
2409 * even though they are keywords
2411 loc = token.loc;
2412 if (token.value == TOK.identifier)
2413 id = token.ident;
2414 else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
2415 level = cast(uint)token.unsvalue;
2416 else if (token.value == TOK.unittest_)
2417 id = Identifier.idPool(Token.toString(TOK.unittest_));
2418 else if (token.value == TOK.assert_)
2419 id = Identifier.idPool(Token.toString(TOK.assert_));
2420 else
2421 error("identifier or integer expected inside `version(...)`, not `%s`", token.toChars());
2422 nextToken();
2423 check(TOK.rightParenthesis);
2425 else
2426 error("(condition) expected following `version`");
2427 return new AST.VersionCondition(loc, mod, level, id);
2430 /***********************************************
2431 * static if (expression)
2432 * body
2433 * else
2434 * body
2435 * Current token is 'static'.
2437 private AST.Condition parseStaticIfCondition()
2439 AST.Expression exp;
2440 AST.Condition condition;
2441 const loc = token.loc;
2443 nextToken();
2444 nextToken();
2445 if (token.value == TOK.leftParenthesis)
2447 nextToken();
2448 exp = parseAssignExp();
2449 check(TOK.rightParenthesis);
2451 else
2453 error("(expression) expected following `static if`");
2454 exp = null;
2456 condition = new AST.StaticIfCondition(loc, exp);
2457 return condition;
2460 /*****************************************
2461 * Parse a constructor definition:
2462 * this(parameters) { body }
2463 * or postblit:
2464 * this(this) { body }
2465 * or constructor template:
2466 * this(templateparameters)(parameters) { body }
2467 * Current token is 'this'.
2469 private AST.Dsymbol parseCtor(PrefixAttributes!AST* pAttrs)
2471 AST.Expressions* udas = null;
2472 const loc = token.loc;
2473 StorageClass stc = getStorageClass!AST(pAttrs);
2475 nextToken();
2476 if (token.value == TOK.leftParenthesis && peekNext() == TOK.this_ && peekNext2() == TOK.rightParenthesis)
2478 // this(this) { ... }
2479 nextToken();
2480 nextToken();
2481 check(TOK.rightParenthesis);
2483 stc = parsePostfix(stc, &udas);
2484 if (stc & STC.immutable_)
2485 deprecation("`immutable` postblit is deprecated. Please use an unqualified postblit.");
2486 if (stc & STC.shared_)
2487 deprecation("`shared` postblit is deprecated. Please use an unqualified postblit.");
2488 if (stc & STC.const_)
2489 deprecation("`const` postblit is deprecated. Please use an unqualified postblit.");
2490 if (stc & STC.static_)
2491 error(loc, "postblit cannot be `static`");
2493 auto f = new AST.PostBlitDeclaration(loc, Loc.initial, stc, Id.postblit);
2494 AST.Dsymbol s = parseContracts(f);
2495 if (udas)
2497 auto a = new AST.Dsymbols();
2498 a.push(f);
2499 s = new AST.UserAttributeDeclaration(udas, a);
2501 return s;
2504 /* Look ahead to see if:
2505 * this(...)(...)
2506 * which is a constructor template
2508 AST.TemplateParameters* tpl = null;
2509 if (token.value == TOK.leftParenthesis && peekPastParen(&token).value == TOK.leftParenthesis)
2511 tpl = parseTemplateParameterList();
2514 /* Just a regular constructor
2516 auto parameterList = parseParameterList(null);
2517 stc = parsePostfix(stc, &udas);
2519 if (parameterList.varargs != VarArg.none || AST.Parameter.dim(parameterList.parameters) != 0)
2521 if (stc & STC.static_)
2522 error(loc, "constructor cannot be static");
2524 else if (StorageClass ss = stc & (STC.shared_ | STC.static_)) // this()
2526 if (ss == STC.static_)
2527 error(loc, "use `static this()` to declare a static constructor");
2528 else if (ss == (STC.shared_ | STC.static_))
2529 error(loc, "use `shared static this()` to declare a shared static constructor");
2532 AST.Expression constraint = tpl ? parseConstraint() : null;
2534 AST.Type tf = new AST.TypeFunction(parameterList, null, linkage, stc); // RetrunType -> auto
2535 tf = tf.addSTC(stc);
2537 auto f = new AST.CtorDeclaration(loc, Loc.initial, stc, tf);
2538 AST.Dsymbol s = parseContracts(f);
2539 if (udas)
2541 auto a = new AST.Dsymbols();
2542 a.push(f);
2543 s = new AST.UserAttributeDeclaration(udas, a);
2546 if (tpl)
2548 // Wrap a template around it
2549 auto decldefs = new AST.Dsymbols();
2550 decldefs.push(s);
2551 s = new AST.TemplateDeclaration(loc, f.ident, tpl, constraint, decldefs);
2554 return s;
2557 /*****************************************
2558 * Parse a destructor definition:
2559 * ~this() { body }
2560 * Current token is '~'.
2562 private AST.Dsymbol parseDtor(PrefixAttributes!AST* pAttrs)
2564 AST.Expressions* udas = null;
2565 const loc = token.loc;
2566 StorageClass stc = getStorageClass!AST(pAttrs);
2568 nextToken();
2569 check(TOK.this_);
2570 check(TOK.leftParenthesis);
2571 check(TOK.rightParenthesis);
2573 stc = parsePostfix(stc, &udas);
2574 if (StorageClass ss = stc & (STC.shared_ | STC.static_))
2576 if (ss == STC.static_)
2577 error(loc, "use `static ~this()` to declare a static destructor");
2578 else if (ss == (STC.shared_ | STC.static_))
2579 error(loc, "use `shared static ~this()` to declare a shared static destructor");
2582 auto f = new AST.DtorDeclaration(loc, Loc.initial, stc, Id.dtor);
2583 AST.Dsymbol s = parseContracts(f);
2584 if (udas)
2586 auto a = new AST.Dsymbols();
2587 a.push(f);
2588 s = new AST.UserAttributeDeclaration(udas, a);
2590 return s;
2593 /*****************************************
2594 * Parse a static constructor definition:
2595 * static this() { body }
2596 * Current token is 'static'.
2598 private AST.Dsymbol parseStaticCtor(PrefixAttributes!AST* pAttrs)
2600 //Expressions *udas = NULL;
2601 const loc = token.loc;
2602 StorageClass stc = getStorageClass!AST(pAttrs);
2604 nextToken();
2605 nextToken();
2606 check(TOK.leftParenthesis);
2607 check(TOK.rightParenthesis);
2609 stc = parsePostfix(stc & ~STC.TYPECTOR, null) | stc;
2610 if (stc & STC.shared_)
2611 error(loc, "use `shared static this()` to declare a shared static constructor");
2612 else if (stc & STC.static_)
2613 appendStorageClass(stc, STC.static_); // complaint for the redundancy
2614 else if (StorageClass modStc = stc & STC.TYPECTOR)
2616 OutBuffer buf;
2617 AST.stcToBuffer(&buf, modStc);
2618 error(loc, "static constructor cannot be `%s`", buf.peekChars());
2620 stc &= ~(STC.static_ | STC.TYPECTOR);
2622 auto f = new AST.StaticCtorDeclaration(loc, Loc.initial, stc);
2623 AST.Dsymbol s = parseContracts(f);
2624 return s;
2627 /*****************************************
2628 * Parse a static destructor definition:
2629 * static ~this() { body }
2630 * Current token is 'static'.
2632 private AST.Dsymbol parseStaticDtor(PrefixAttributes!AST* pAttrs)
2634 AST.Expressions* udas = null;
2635 const loc = token.loc;
2636 StorageClass stc = getStorageClass!AST(pAttrs);
2638 nextToken();
2639 nextToken();
2640 check(TOK.this_);
2641 check(TOK.leftParenthesis);
2642 check(TOK.rightParenthesis);
2644 stc = parsePostfix(stc & ~STC.TYPECTOR, &udas) | stc;
2645 if (stc & STC.shared_)
2646 error(loc, "use `shared static ~this()` to declare a shared static destructor");
2647 else if (stc & STC.static_)
2648 appendStorageClass(stc, STC.static_); // complaint for the redundancy
2649 else if (StorageClass modStc = stc & STC.TYPECTOR)
2651 OutBuffer buf;
2652 AST.stcToBuffer(&buf, modStc);
2653 error(loc, "static destructor cannot be `%s`", buf.peekChars());
2655 stc &= ~(STC.static_ | STC.TYPECTOR);
2657 auto f = new AST.StaticDtorDeclaration(loc, Loc.initial, stc);
2658 AST.Dsymbol s = parseContracts(f);
2659 if (udas)
2661 auto a = new AST.Dsymbols();
2662 a.push(f);
2663 s = new AST.UserAttributeDeclaration(udas, a);
2665 return s;
2668 /*****************************************
2669 * Parse a shared static constructor definition:
2670 * shared static this() { body }
2671 * Current token is 'shared'.
2673 private AST.Dsymbol parseSharedStaticCtor(PrefixAttributes!AST* pAttrs)
2675 //Expressions *udas = NULL;
2676 const loc = token.loc;
2677 StorageClass stc = getStorageClass!AST(pAttrs);
2679 nextToken();
2680 nextToken();
2681 nextToken();
2682 check(TOK.leftParenthesis);
2683 check(TOK.rightParenthesis);
2685 stc = parsePostfix(stc & ~STC.TYPECTOR, null) | stc;
2686 if (StorageClass ss = stc & (STC.shared_ | STC.static_))
2687 appendStorageClass(stc, ss); // complaint for the redundancy
2688 else if (StorageClass modStc = stc & STC.TYPECTOR)
2690 OutBuffer buf;
2691 AST.stcToBuffer(&buf, modStc);
2692 error(loc, "shared static constructor cannot be `%s`", buf.peekChars());
2694 stc &= ~(STC.static_ | STC.TYPECTOR);
2696 auto f = new AST.SharedStaticCtorDeclaration(loc, Loc.initial, stc);
2697 AST.Dsymbol s = parseContracts(f);
2698 return s;
2701 /*****************************************
2702 * Parse a shared static destructor definition:
2703 * shared static ~this() { body }
2704 * Current token is 'shared'.
2706 private AST.Dsymbol parseSharedStaticDtor(PrefixAttributes!AST* pAttrs)
2708 AST.Expressions* udas = null;
2709 const loc = token.loc;
2710 StorageClass stc = getStorageClass!AST(pAttrs);
2712 nextToken();
2713 nextToken();
2714 nextToken();
2715 check(TOK.this_);
2716 check(TOK.leftParenthesis);
2717 check(TOK.rightParenthesis);
2719 stc = parsePostfix(stc & ~STC.TYPECTOR, &udas) | stc;
2720 if (StorageClass ss = stc & (STC.shared_ | STC.static_))
2721 appendStorageClass(stc, ss); // complaint for the redundancy
2722 else if (StorageClass modStc = stc & STC.TYPECTOR)
2724 OutBuffer buf;
2725 AST.stcToBuffer(&buf, modStc);
2726 error(loc, "shared static destructor cannot be `%s`", buf.peekChars());
2728 stc &= ~(STC.static_ | STC.TYPECTOR);
2730 auto f = new AST.SharedStaticDtorDeclaration(loc, Loc.initial, stc);
2731 AST.Dsymbol s = parseContracts(f);
2732 if (udas)
2734 auto a = new AST.Dsymbols();
2735 a.push(f);
2736 s = new AST.UserAttributeDeclaration(udas, a);
2738 return s;
2741 /*****************************************
2742 * Parse an invariant definition:
2743 * invariant { statements... }
2744 * invariant() { statements... }
2745 * invariant (expression);
2746 * Current token is 'invariant'.
2748 private AST.Dsymbol parseInvariant(PrefixAttributes!AST* pAttrs)
2750 const loc = token.loc;
2751 StorageClass stc = getStorageClass!AST(pAttrs);
2753 nextToken();
2754 if (token.value == TOK.leftParenthesis) // optional () or invariant (expression);
2756 nextToken();
2757 if (token.value != TOK.rightParenthesis) // invariant (expression);
2759 AST.Expression e = parseAssignExp(), msg = null;
2760 if (token.value == TOK.comma)
2762 nextToken();
2763 if (token.value != TOK.rightParenthesis)
2765 msg = parseAssignExp();
2766 if (token.value == TOK.comma)
2767 nextToken();
2770 check(TOK.rightParenthesis);
2771 check(TOK.semicolon);
2772 e = new AST.AssertExp(loc, e, msg);
2773 auto fbody = new AST.ExpStatement(loc, e);
2774 auto f = new AST.InvariantDeclaration(loc, token.loc, stc, null, fbody);
2775 return f;
2777 nextToken();
2780 auto fbody = parseStatement(ParseStatementFlags.curly);
2781 auto f = new AST.InvariantDeclaration(loc, token.loc, stc, null, fbody);
2782 return f;
2785 /*****************************************
2786 * Parse a unittest definition:
2787 * unittest { body }
2788 * Current token is 'unittest'.
2790 private AST.Dsymbol parseUnitTest(PrefixAttributes!AST* pAttrs)
2792 const loc = token.loc;
2793 StorageClass stc = getStorageClass!AST(pAttrs);
2795 nextToken();
2797 const(char)* begPtr = token.ptr + 1; // skip '{'
2798 const(char)* endPtr = null;
2799 AST.Statement sbody = parseStatement(ParseStatementFlags.curly, &endPtr);
2801 /** Extract unittest body as a string. Must be done eagerly since memory
2802 will be released by the lexer before doc gen. */
2803 char* docline = null;
2804 if (global.params.doDocComments && endPtr > begPtr)
2806 /* Remove trailing whitespaces */
2807 for (const(char)* p = endPtr - 1; begPtr <= p && (*p == ' ' || *p == '\r' || *p == '\n' || *p == '\t'); --p)
2809 endPtr = p;
2812 size_t len = endPtr - begPtr;
2813 if (len > 0)
2815 docline = cast(char*)mem.xmalloc_noscan(len + 2);
2816 memcpy(docline, begPtr, len);
2817 docline[len] = '\n'; // Terminate all lines by LF
2818 docline[len + 1] = '\0';
2822 auto f = new AST.UnitTestDeclaration(loc, token.loc, stc, docline);
2823 f.fbody = sbody;
2824 return f;
2827 /*****************************************
2828 * Parse a new definition:
2829 * @disable new();
2830 * Current token is 'new'.
2832 private AST.Dsymbol parseNew(PrefixAttributes!AST* pAttrs)
2834 const loc = token.loc;
2835 StorageClass stc = getStorageClass!AST(pAttrs);
2836 if (!(stc & STC.disable))
2838 error("`new` allocator must be annotated with `@disabled`");
2840 nextToken();
2842 /* @@@DEPRECATED_2.098@@@
2843 * After deprecation period (2.108), remove all code in the version(all) block.
2845 version (all)
2847 auto parameterList = parseParameterList(null); // parameterList ignored
2848 if (parameterList.parameters.length > 0 || parameterList.varargs != VarArg.none)
2849 deprecation("`new` allocator with non-empty parameter list is deprecated");
2850 auto f = new AST.NewDeclaration(loc, stc);
2851 if (token.value != TOK.semicolon)
2853 deprecation("`new` allocator with function definition is deprecated");
2854 parseContracts(f); // body ignored
2855 f.fbody = null;
2856 f.fensures = null;
2857 f.frequires = null;
2859 else
2860 nextToken();
2861 return f;
2863 else
2865 check(TOK.leftParenthesis);
2866 check(TOK.rightParenthesis);
2867 check(TOK.semicolon);
2868 return new AST.NewDeclaration(loc, stc);
2872 /**********************************************
2873 * Parse parameter list.
2875 private AST.ParameterList parseParameterList(AST.TemplateParameters** tpl)
2877 auto parameters = new AST.Parameters();
2878 VarArg varargs = VarArg.none;
2879 int hasdefault = 0;
2880 StorageClass varargsStc;
2882 // Attributes allowed for ...
2883 enum VarArgsStc = STC.const_ | STC.immutable_ | STC.shared_ | STC.scope_ | STC.return_;
2885 check(TOK.leftParenthesis);
2886 while (1)
2888 Identifier ai = null;
2889 AST.Type at;
2890 StorageClass storageClass = 0;
2891 StorageClass stc;
2892 AST.Expression ae;
2893 AST.Expressions* udas = null;
2894 for (; 1; nextToken())
2897 switch (token.value)
2899 case TOK.rightParenthesis:
2900 if (storageClass != 0 || udas !is null)
2901 error("basic type expected, not `)`");
2902 break;
2904 case TOK.dotDotDot:
2905 varargs = VarArg.variadic;
2906 varargsStc = storageClass;
2907 if (varargsStc & ~VarArgsStc)
2909 OutBuffer buf;
2910 AST.stcToBuffer(&buf, varargsStc & ~VarArgsStc);
2911 error("variadic parameter cannot have attributes `%s`", buf.peekChars());
2912 varargsStc &= VarArgsStc;
2914 nextToken();
2915 break;
2917 case TOK.const_:
2918 if (peekNext() == TOK.leftParenthesis)
2919 goto default;
2920 stc = STC.const_;
2921 goto L2;
2923 case TOK.immutable_:
2924 if (peekNext() == TOK.leftParenthesis)
2925 goto default;
2926 stc = STC.immutable_;
2927 goto L2;
2929 case TOK.shared_:
2930 if (peekNext() == TOK.leftParenthesis)
2931 goto default;
2932 stc = STC.shared_;
2933 goto L2;
2935 case TOK.inout_:
2936 if (peekNext() == TOK.leftParenthesis)
2937 goto default;
2938 stc = STC.wild;
2939 goto L2;
2940 case TOK.at:
2942 AST.Expressions* exps = null;
2943 StorageClass stc2 = parseAttribute(exps);
2944 if (stc2 & atAttrGroup)
2946 error("`@%s` attribute for function parameter is not supported", token.toChars());
2948 else
2950 udas = AST.UserAttributeDeclaration.concat(udas, exps);
2952 if (token.value == TOK.dotDotDot)
2953 error("variadic parameter cannot have user-defined attributes");
2954 if (stc2)
2955 nextToken();
2956 goto L3;
2957 // Don't call nextToken again.
2959 case TOK.in_:
2960 if (global.params.vin)
2961 message(scanloc, "Usage of 'in' on parameter");
2962 stc = STC.in_;
2963 goto L2;
2965 case TOK.out_:
2966 stc = STC.out_;
2967 goto L2;
2969 case TOK.ref_:
2970 stc = STC.ref_;
2971 goto L2;
2973 case TOK.lazy_:
2974 stc = STC.lazy_;
2975 goto L2;
2977 case TOK.scope_:
2978 stc = STC.scope_;
2979 goto L2;
2981 case TOK.final_:
2982 stc = STC.final_;
2983 goto L2;
2985 case TOK.auto_:
2986 stc = STC.auto_;
2987 goto L2;
2989 case TOK.return_:
2990 stc = STC.return_;
2991 goto L2;
2993 storageClass = appendStorageClass(storageClass, stc);
2994 continue;
2996 version (none)
2998 case TOK.static_:
2999 stc = STC.static_;
3000 goto L2;
3002 case TOK.auto_:
3003 storageClass = STC.auto_;
3004 goto L4;
3006 case TOK.alias_:
3007 storageClass = STC.alias_;
3008 goto L4;
3010 nextToken();
3011 ai = null;
3012 if (token.value == TOK.identifier)
3014 ai = token.ident;
3015 nextToken();
3018 at = null; // no type
3019 ae = null; // no default argument
3020 if (token.value == TOK.assign) // = defaultArg
3022 nextToken();
3023 ae = parseDefaultInitExp();
3024 hasdefault = 1;
3026 else
3028 if (hasdefault)
3029 error("default argument expected for `alias %s`", ai ? ai.toChars() : "");
3031 goto L3;
3033 default:
3035 stc = storageClass & (STC.IOR | STC.lazy_);
3036 // if stc is not a power of 2
3037 if (stc & (stc - 1) && !(stc == (STC.in_ | STC.ref_)))
3038 error("incompatible parameter storage classes");
3039 //if ((storageClass & STC.scope_) && (storageClass & (STC.ref_ | STC.out_)))
3040 //error("scope cannot be ref or out");
3042 if (tpl && token.value == TOK.identifier)
3044 const tv = peekNext();
3045 if (tv == TOK.comma || tv == TOK.rightParenthesis || tv == TOK.dotDotDot)
3047 Identifier id = Identifier.generateId("__T");
3048 const loc = token.loc;
3049 at = new AST.TypeIdentifier(loc, id);
3050 if (!*tpl)
3051 *tpl = new AST.TemplateParameters();
3052 AST.TemplateParameter tp = new AST.TemplateTypeParameter(loc, id, null, null);
3053 (*tpl).push(tp);
3055 ai = token.ident;
3056 nextToken();
3058 else goto _else;
3060 else
3062 _else:
3063 at = parseType(&ai);
3065 ae = null;
3066 if (token.value == TOK.assign) // = defaultArg
3068 nextToken();
3069 ae = parseDefaultInitExp();
3070 hasdefault = 1;
3072 else
3074 if (hasdefault)
3075 error("default argument expected for `%s`", ai ? ai.toChars() : at.toChars());
3077 auto param = new AST.Parameter(storageClass | STC.parameter, at, ai, ae, null);
3078 if (udas)
3080 auto a = new AST.Dsymbols();
3081 auto udad = new AST.UserAttributeDeclaration(udas, a);
3082 param.userAttribDecl = udad;
3084 if (token.value == TOK.at)
3086 AST.Expressions* exps = null;
3087 StorageClass stc2 = parseAttribute(exps);
3088 if (stc2 & atAttrGroup)
3090 error("`@%s` attribute for function parameter is not supported", token.toChars());
3092 else
3094 error("user-defined attributes cannot appear as postfixes", token.toChars());
3096 if (stc2)
3097 nextToken();
3099 if (token.value == TOK.dotDotDot)
3101 /* This is:
3102 * at ai ...
3104 if (storageClass & (STC.out_ | STC.ref_))
3105 error("variadic argument cannot be `out` or `ref`");
3106 varargs = VarArg.typesafe;
3107 parameters.push(param);
3108 nextToken();
3109 break;
3111 parameters.push(param);
3112 if (token.value == TOK.comma)
3114 nextToken();
3115 goto L1;
3117 break;
3120 break;
3122 break;
3126 check(TOK.rightParenthesis);
3127 return AST.ParameterList(parameters, varargs, varargsStc);
3130 /*************************************
3132 private AST.EnumDeclaration parseEnum()
3134 AST.EnumDeclaration e;
3135 Identifier id;
3136 AST.Type memtype;
3137 auto loc = token.loc;
3139 // printf("Parser::parseEnum()\n");
3140 nextToken();
3141 id = null;
3142 if (token.value == TOK.identifier)
3144 id = token.ident;
3145 nextToken();
3148 memtype = null;
3149 if (token.value == TOK.colon)
3151 nextToken();
3152 int alt = 0;
3153 const typeLoc = token.loc;
3154 memtype = parseBasicType();
3155 memtype = parseDeclarator(memtype, alt, null);
3156 checkCstyleTypeSyntax(typeLoc, memtype, alt, null);
3159 e = new AST.EnumDeclaration(loc, id, memtype);
3160 if (token.value == TOK.semicolon && id)
3161 nextToken();
3162 else if (token.value == TOK.leftCurly)
3164 bool isAnonymousEnum = !id;
3165 TOK prevTOK;
3167 //printf("enum definition\n");
3168 e.members = new AST.Dsymbols();
3169 nextToken();
3170 const(char)[] comment = token.blockComment;
3171 while (token.value != TOK.rightCurly)
3173 /* Can take the following forms...
3174 * 1. ident
3175 * 2. ident = value
3176 * 3. type ident = value
3177 * ... prefixed by valid attributes
3179 loc = token.loc;
3181 AST.Type type = null;
3182 Identifier ident = null;
3184 AST.Expressions* udas;
3185 StorageClass stc;
3186 AST.Expression deprecationMessage;
3187 enum attributeErrorMessage = "`%s` is not a valid attribute for enum members";
3188 while(token.value != TOK.rightCurly
3189 && token.value != TOK.comma
3190 && token.value != TOK.assign)
3192 switch(token.value)
3194 case TOK.at:
3195 if (StorageClass _stc = parseAttribute(udas))
3197 if (_stc == STC.disable)
3198 stc |= _stc;
3199 else
3201 OutBuffer buf;
3202 AST.stcToBuffer(&buf, _stc);
3203 error(attributeErrorMessage, buf.peekChars());
3205 prevTOK = token.value;
3206 nextToken();
3208 break;
3209 case TOK.deprecated_:
3210 stc |= STC.deprecated_;
3211 if (!parseDeprecatedAttribute(deprecationMessage))
3213 prevTOK = token.value;
3214 nextToken();
3216 break;
3217 case TOK.identifier:
3218 const tv = peekNext();
3219 if (tv == TOK.assign || tv == TOK.comma || tv == TOK.rightCurly)
3221 ident = token.ident;
3222 type = null;
3223 prevTOK = token.value;
3224 nextToken();
3226 else
3228 goto default;
3230 break;
3231 default:
3232 if (isAnonymousEnum)
3234 type = parseType(&ident, null);
3235 if (type == AST.Type.terror)
3237 type = null;
3238 prevTOK = token.value;
3239 nextToken();
3241 else
3243 prevTOK = TOK.identifier;
3246 else
3248 error(attributeErrorMessage, token.toChars());
3249 prevTOK = token.value;
3250 nextToken();
3252 break;
3254 if (token.value == TOK.comma)
3256 prevTOK = token.value;
3260 if (type && type != AST.Type.terror)
3262 if (!ident)
3263 error("no identifier for declarator `%s`", type.toChars());
3264 if (!isAnonymousEnum)
3265 error("type only allowed if anonymous enum and no enum type");
3267 AST.Expression value;
3268 if (token.value == TOK.assign)
3270 if (prevTOK == TOK.identifier)
3272 nextToken();
3273 value = parseAssignExp();
3275 else
3277 error("assignment must be preceded by an identifier");
3278 nextToken();
3281 else
3283 value = null;
3284 if (type && type != AST.Type.terror && isAnonymousEnum)
3285 error("if type, there must be an initializer");
3288 AST.DeprecatedDeclaration dd;
3289 if (deprecationMessage)
3291 dd = new AST.DeprecatedDeclaration(deprecationMessage, null);
3292 stc |= STC.deprecated_;
3295 auto em = new AST.EnumMember(loc, ident, value, type, stc, null, dd);
3296 e.members.push(em);
3298 if (udas)
3300 auto s = new AST.Dsymbols();
3301 s.push(em);
3302 auto uad = new AST.UserAttributeDeclaration(udas, s);
3303 em.userAttribDecl = uad;
3306 if (token.value == TOK.rightCurly)
3309 else
3311 addComment(em, comment);
3312 comment = null;
3313 check(TOK.comma);
3315 addComment(em, comment);
3316 comment = token.blockComment;
3318 if (token.value == TOK.endOfFile)
3320 error("premature end of file");
3321 break;
3324 nextToken();
3326 else
3327 error("enum declaration is invalid");
3329 //printf("-parseEnum() %s\n", e.toChars());
3330 return e;
3333 /********************************
3334 * Parse struct, union, interface, class.
3336 private AST.Dsymbol parseAggregate()
3338 AST.TemplateParameters* tpl = null;
3339 AST.Expression constraint;
3340 const loc = token.loc;
3341 TOK tok = token.value;
3343 //printf("Parser::parseAggregate()\n");
3344 nextToken();
3345 Identifier id;
3346 if (token.value != TOK.identifier)
3348 id = null;
3350 else
3352 id = token.ident;
3353 nextToken();
3355 if (token.value == TOK.leftParenthesis)
3357 // struct/class template declaration.
3358 tpl = parseTemplateParameterList();
3359 constraint = parseConstraint();
3363 // Collect base class(es)
3364 AST.BaseClasses* baseclasses = null;
3365 if (token.value == TOK.colon)
3367 if (tok != TOK.interface_ && tok != TOK.class_)
3368 error("base classes are not allowed for `%s`, did you mean `;`?", Token.toChars(tok));
3369 nextToken();
3370 baseclasses = parseBaseClasses();
3373 if (token.value == TOK.if_)
3375 if (constraint)
3376 error("template constraints appear both before and after BaseClassList, put them before");
3377 constraint = parseConstraint();
3379 if (constraint)
3381 if (!id)
3382 error("template constraints not allowed for anonymous `%s`", Token.toChars(tok));
3383 if (!tpl)
3384 error("template constraints only allowed for templates");
3387 AST.Dsymbols* members = null;
3388 if (token.value == TOK.leftCurly)
3390 //printf("aggregate definition\n");
3391 const lookingForElseSave = lookingForElse;
3392 lookingForElse = Loc();
3393 nextToken();
3394 members = parseDeclDefs(0);
3395 lookingForElse = lookingForElseSave;
3396 if (token.value != TOK.rightCurly)
3398 /* { */
3399 error("`}` expected following members in `%s` declaration at %s",
3400 Token.toChars(tok), loc.toChars());
3402 nextToken();
3404 else if (token.value == TOK.semicolon && id)
3406 if (baseclasses || constraint)
3407 error("members expected");
3408 nextToken();
3410 else
3412 error("{ } expected following `%s` declaration", Token.toChars(tok));
3415 AST.AggregateDeclaration a;
3416 switch (tok)
3418 case TOK.interface_:
3419 if (!id)
3420 error(loc, "anonymous interfaces not allowed");
3421 a = new AST.InterfaceDeclaration(loc, id, baseclasses);
3422 a.members = members;
3423 break;
3425 case TOK.class_:
3426 if (!id)
3427 error(loc, "anonymous classes not allowed");
3428 bool inObject = md && !md.packages && md.id == Id.object;
3429 a = new AST.ClassDeclaration(loc, id, baseclasses, members, inObject);
3430 break;
3432 case TOK.struct_:
3433 if (id)
3435 bool inObject = md && !md.packages && md.id == Id.object;
3436 a = new AST.StructDeclaration(loc, id, inObject);
3437 a.members = members;
3439 else
3441 /* Anonymous structs/unions are more like attributes.
3443 assert(!tpl);
3444 return new AST.AnonDeclaration(loc, false, members);
3446 break;
3448 case TOK.union_:
3449 if (id)
3451 a = new AST.UnionDeclaration(loc, id);
3452 a.members = members;
3454 else
3456 /* Anonymous structs/unions are more like attributes.
3458 assert(!tpl);
3459 return new AST.AnonDeclaration(loc, true, members);
3461 break;
3463 default:
3464 assert(0);
3467 if (tpl)
3469 // Wrap a template around the aggregate declaration
3470 auto decldefs = new AST.Dsymbols();
3471 decldefs.push(a);
3472 auto tempdecl = new AST.TemplateDeclaration(loc, id, tpl, constraint, decldefs);
3473 return tempdecl;
3475 return a;
3478 /*******************************************
3480 private AST.BaseClasses* parseBaseClasses()
3482 auto baseclasses = new AST.BaseClasses();
3484 for (; 1; nextToken())
3486 auto b = new AST.BaseClass(parseBasicType());
3487 baseclasses.push(b);
3488 if (token.value != TOK.comma)
3489 break;
3491 return baseclasses;
3494 private AST.Dsymbols* parseImport()
3496 auto decldefs = new AST.Dsymbols();
3497 Identifier aliasid = null;
3499 int isstatic = token.value == TOK.static_;
3500 if (isstatic)
3501 nextToken();
3503 //printf("Parser::parseImport()\n");
3507 nextToken();
3508 if (token.value != TOK.identifier)
3510 error("identifier expected following `import`");
3511 break;
3514 const loc = token.loc;
3515 Identifier id = token.ident;
3516 Identifier[] a;
3517 nextToken();
3518 if (!aliasid && token.value == TOK.assign)
3520 aliasid = id;
3521 goto L1;
3523 while (token.value == TOK.dot)
3525 a ~= id;
3526 nextToken();
3527 if (token.value != TOK.identifier)
3529 error("identifier expected following `package`");
3530 break;
3532 id = token.ident;
3533 nextToken();
3536 auto s = new AST.Import(loc, a, id, aliasid, isstatic);
3537 decldefs.push(s);
3539 /* Look for
3540 * : alias=name, alias=name;
3541 * syntax.
3543 if (token.value == TOK.colon)
3547 nextToken();
3548 if (token.value != TOK.identifier)
3550 error("identifier expected following `:`");
3551 break;
3553 Identifier _alias = token.ident;
3554 Identifier name;
3555 nextToken();
3556 if (token.value == TOK.assign)
3558 nextToken();
3559 if (token.value != TOK.identifier)
3561 error("identifier expected following `%s=`", _alias.toChars());
3562 break;
3564 name = token.ident;
3565 nextToken();
3567 else
3569 name = _alias;
3570 _alias = null;
3572 s.addAlias(name, _alias);
3574 while (token.value == TOK.comma);
3575 break; // no comma-separated imports of this form
3577 aliasid = null;
3579 while (token.value == TOK.comma);
3581 if (token.value == TOK.semicolon)
3582 nextToken();
3583 else
3585 error("`;` expected");
3586 nextToken();
3589 return decldefs;
3592 AST.Type parseType(Identifier* pident = null, AST.TemplateParameters** ptpl = null)
3594 /* Take care of the storage class prefixes that
3595 * serve as type attributes:
3596 * const type
3597 * immutable type
3598 * shared type
3599 * inout type
3600 * inout const type
3601 * shared const type
3602 * shared inout type
3603 * shared inout const type
3605 StorageClass stc = 0;
3606 while (1)
3608 switch (token.value)
3610 case TOK.const_:
3611 if (peekNext() == TOK.leftParenthesis)
3612 break; // const as type constructor
3613 stc |= STC.const_; // const as storage class
3614 nextToken();
3615 continue;
3617 case TOK.immutable_:
3618 if (peekNext() == TOK.leftParenthesis)
3619 break;
3620 stc |= STC.immutable_;
3621 nextToken();
3622 continue;
3624 case TOK.shared_:
3625 if (peekNext() == TOK.leftParenthesis)
3626 break;
3627 stc |= STC.shared_;
3628 nextToken();
3629 continue;
3631 case TOK.inout_:
3632 if (peekNext() == TOK.leftParenthesis)
3633 break;
3634 stc |= STC.wild;
3635 nextToken();
3636 continue;
3638 default:
3639 break;
3641 break;
3644 const typeLoc = token.loc;
3646 AST.Type t;
3647 t = parseBasicType();
3649 int alt = 0;
3650 t = parseDeclarator(t, alt, pident, ptpl);
3651 checkCstyleTypeSyntax(typeLoc, t, alt, pident ? *pident : null);
3653 t = t.addSTC(stc);
3654 return t;
3657 private AST.Type parseBasicType(bool dontLookDotIdents = false)
3659 AST.Type t;
3660 Loc loc;
3661 Identifier id;
3662 //printf("parseBasicType()\n");
3663 switch (token.value)
3665 case TOK.void_:
3666 t = AST.Type.tvoid;
3667 goto LabelX;
3669 case TOK.int8:
3670 t = AST.Type.tint8;
3671 goto LabelX;
3673 case TOK.uns8:
3674 t = AST.Type.tuns8;
3675 goto LabelX;
3677 case TOK.int16:
3678 t = AST.Type.tint16;
3679 goto LabelX;
3681 case TOK.uns16:
3682 t = AST.Type.tuns16;
3683 goto LabelX;
3685 case TOK.int32:
3686 t = AST.Type.tint32;
3687 goto LabelX;
3689 case TOK.uns32:
3690 t = AST.Type.tuns32;
3691 goto LabelX;
3693 case TOK.int64:
3694 t = AST.Type.tint64;
3695 nextToken();
3696 if (token.value == TOK.int64) // if `long long`
3698 error("use `long` for a 64 bit integer instead of `long long`");
3699 nextToken();
3701 else if (token.value == TOK.float64) // if `long double`
3703 error("use `real` instead of `long double`");
3704 t = AST.Type.tfloat80;
3705 nextToken();
3707 break;
3709 case TOK.uns64:
3710 t = AST.Type.tuns64;
3711 goto LabelX;
3713 case TOK.int128:
3714 t = AST.Type.tint128;
3715 goto LabelX;
3717 case TOK.uns128:
3718 t = AST.Type.tuns128;
3719 goto LabelX;
3721 case TOK.float32:
3722 t = AST.Type.tfloat32;
3723 goto LabelX;
3725 case TOK.float64:
3726 t = AST.Type.tfloat64;
3727 goto LabelX;
3729 case TOK.float80:
3730 t = AST.Type.tfloat80;
3731 goto LabelX;
3733 case TOK.imaginary32:
3734 t = AST.Type.timaginary32;
3735 goto LabelX;
3737 case TOK.imaginary64:
3738 t = AST.Type.timaginary64;
3739 goto LabelX;
3741 case TOK.imaginary80:
3742 t = AST.Type.timaginary80;
3743 goto LabelX;
3745 case TOK.complex32:
3746 t = AST.Type.tcomplex32;
3747 goto LabelX;
3749 case TOK.complex64:
3750 t = AST.Type.tcomplex64;
3751 goto LabelX;
3753 case TOK.complex80:
3754 t = AST.Type.tcomplex80;
3755 goto LabelX;
3757 case TOK.bool_:
3758 t = AST.Type.tbool;
3759 goto LabelX;
3761 case TOK.char_:
3762 t = AST.Type.tchar;
3763 goto LabelX;
3765 case TOK.wchar_:
3766 t = AST.Type.twchar;
3767 goto LabelX;
3769 case TOK.dchar_:
3770 t = AST.Type.tdchar;
3771 goto LabelX;
3772 LabelX:
3773 nextToken();
3774 break;
3776 case TOK.this_:
3777 case TOK.super_:
3778 case TOK.identifier:
3779 loc = token.loc;
3780 id = token.ident;
3781 nextToken();
3782 if (token.value == TOK.not)
3784 // ident!(template_arguments)
3785 auto tempinst = new AST.TemplateInstance(loc, id, parseTemplateArguments());
3786 t = parseBasicTypeStartingAt(new AST.TypeInstance(loc, tempinst), dontLookDotIdents);
3788 else
3790 t = parseBasicTypeStartingAt(new AST.TypeIdentifier(loc, id), dontLookDotIdents);
3792 break;
3794 case TOK.mixin_:
3795 // https://dlang.org/spec/expression.html#mixin_types
3796 loc = token.loc;
3797 nextToken();
3798 if (token.value != TOK.leftParenthesis)
3799 error("found `%s` when expecting `%s` following `mixin`", token.toChars(), Token.toChars(TOK.leftParenthesis));
3800 auto exps = parseArguments();
3801 t = new AST.TypeMixin(loc, exps);
3802 break;
3804 case TOK.dot:
3805 // Leading . as in .foo
3806 t = parseBasicTypeStartingAt(new AST.TypeIdentifier(token.loc, Id.empty), dontLookDotIdents);
3807 break;
3809 case TOK.typeof_:
3810 // typeof(expression)
3811 t = parseBasicTypeStartingAt(parseTypeof(), dontLookDotIdents);
3812 break;
3814 case TOK.vector:
3815 t = parseVector();
3816 break;
3818 case TOK.traits:
3819 if (AST.TraitsExp te = cast(AST.TraitsExp) parsePrimaryExp())
3820 if (te.ident && te.args)
3822 t = new AST.TypeTraits(token.loc, te);
3823 break;
3825 t = new AST.TypeError;
3826 break;
3828 case TOK.const_:
3829 // const(type)
3830 nextToken();
3831 check(TOK.leftParenthesis);
3832 t = parseType().addSTC(STC.const_);
3833 check(TOK.rightParenthesis);
3834 break;
3836 case TOK.immutable_:
3837 // immutable(type)
3838 nextToken();
3839 check(TOK.leftParenthesis);
3840 t = parseType().addSTC(STC.immutable_);
3841 check(TOK.rightParenthesis);
3842 break;
3844 case TOK.shared_:
3845 // shared(type)
3846 nextToken();
3847 check(TOK.leftParenthesis);
3848 t = parseType().addSTC(STC.shared_);
3849 check(TOK.rightParenthesis);
3850 break;
3852 case TOK.inout_:
3853 // wild(type)
3854 nextToken();
3855 check(TOK.leftParenthesis);
3856 t = parseType().addSTC(STC.wild);
3857 check(TOK.rightParenthesis);
3858 break;
3860 default:
3861 error("basic type expected, not `%s`", token.toChars());
3862 if (token.value == TOK.else_)
3863 errorSupplemental(token.loc, "There's no `static else`, use `else` instead.");
3864 t = AST.Type.terror;
3865 break;
3867 return t;
3870 private AST.Type parseBasicTypeStartingAt(AST.TypeQualified tid, bool dontLookDotIdents)
3872 AST.Type maybeArray = null;
3873 // See https://issues.dlang.org/show_bug.cgi?id=1215
3874 // A basic type can look like MyType (typical case), but also:
3875 // MyType.T -> A type
3876 // MyType[expr] -> Either a static array of MyType or a type (iif MyType is a Ttuple)
3877 // MyType[expr].T -> A type.
3878 // MyType[expr].T[expr] -> Either a static array of MyType[expr].T or a type
3879 // (iif MyType[expr].T is a Ttuple)
3880 while (1)
3882 switch (token.value)
3884 case TOK.dot:
3886 nextToken();
3887 if (token.value != TOK.identifier)
3889 error("identifier expected following `.` instead of `%s`", token.toChars());
3890 break;
3892 if (maybeArray)
3894 // This is actually a TypeTuple index, not an {a/s}array.
3895 // We need to have a while loop to unwind all index taking:
3896 // T[e1][e2].U -> T, addIndex(e1), addIndex(e2)
3897 AST.Objects dimStack;
3898 AST.Type t = maybeArray;
3899 while (true)
3901 if (t.ty == Tsarray)
3903 // The index expression is an Expression.
3904 AST.TypeSArray a = cast(AST.TypeSArray)t;
3905 dimStack.push(a.dim.syntaxCopy());
3906 t = a.next.syntaxCopy();
3908 else if (t.ty == Taarray)
3910 // The index expression is a Type. It will be interpreted as an expression at semantic time.
3911 AST.TypeAArray a = cast(AST.TypeAArray)t;
3912 dimStack.push(a.index.syntaxCopy());
3913 t = a.next.syntaxCopy();
3915 else
3917 break;
3920 assert(dimStack.dim > 0);
3921 // We're good. Replay indices in the reverse order.
3922 tid = cast(AST.TypeQualified)t;
3923 while (dimStack.dim)
3925 tid.addIndex(dimStack.pop());
3927 maybeArray = null;
3929 const loc = token.loc;
3930 Identifier id = token.ident;
3931 nextToken();
3932 if (token.value == TOK.not)
3934 auto tempinst = new AST.TemplateInstance(loc, id, parseTemplateArguments());
3935 tid.addInst(tempinst);
3937 else
3938 tid.addIdent(id);
3939 continue;
3941 case TOK.leftBracket:
3943 if (dontLookDotIdents) // workaround for https://issues.dlang.org/show_bug.cgi?id=14911
3944 goto Lend;
3946 nextToken();
3947 AST.Type t = maybeArray ? maybeArray : cast(AST.Type)tid;
3948 if (token.value == TOK.rightBracket)
3950 // It's a dynamic array, and we're done:
3951 // T[].U does not make sense.
3952 t = new AST.TypeDArray(t);
3953 nextToken();
3954 return t;
3956 else if (isDeclaration(&token, NeedDeclaratorId.no, TOK.rightBracket, null))
3958 // This can be one of two things:
3959 // 1 - an associative array declaration, T[type]
3960 // 2 - an associative array declaration, T[expr]
3961 // These can only be disambiguated later.
3962 AST.Type index = parseType(); // [ type ]
3963 maybeArray = new AST.TypeAArray(t, index);
3964 check(TOK.rightBracket);
3966 else
3968 // This can be one of three things:
3969 // 1 - an static array declaration, T[expr]
3970 // 2 - a slice, T[expr .. expr]
3971 // 3 - a template parameter pack index expression, T[expr].U
3972 // 1 and 3 can only be disambiguated later.
3973 //printf("it's type[expression]\n");
3974 inBrackets++;
3975 AST.Expression e = parseAssignExp(); // [ expression ]
3976 if (token.value == TOK.slice)
3978 // It's a slice, and we're done.
3979 nextToken();
3980 AST.Expression e2 = parseAssignExp(); // [ exp .. exp ]
3981 t = new AST.TypeSlice(t, e, e2);
3982 inBrackets--;
3983 check(TOK.rightBracket);
3984 return t;
3986 else
3988 maybeArray = new AST.TypeSArray(t, e);
3989 inBrackets--;
3990 check(TOK.rightBracket);
3991 continue;
3994 break;
3996 default:
3997 goto Lend;
4000 Lend:
4001 return maybeArray ? maybeArray : cast(AST.Type)tid;
4004 /******************************************
4005 * Parse suffixes to type t.
4007 * []
4008 * [AssignExpression]
4009 * [AssignExpression .. AssignExpression]
4010 * [Type]
4011 * delegate Parameters MemberFunctionAttributes(opt)
4012 * function Parameters FunctionAttributes(opt)
4013 * Params:
4014 * t = the already parsed type
4015 * Returns:
4016 * t with the suffixes added
4017 * See_Also:
4018 * https://dlang.org/spec/declaration.html#TypeSuffixes
4020 private AST.Type parseTypeSuffixes(AST.Type t)
4022 //printf("parseTypeSuffixes()\n");
4023 while (1)
4025 switch (token.value)
4027 case TOK.mul:
4028 t = new AST.TypePointer(t);
4029 nextToken();
4030 continue;
4032 case TOK.leftBracket:
4033 // Handle []. Make sure things like
4034 // int[3][1] a;
4035 // is (array[1] of array[3] of int)
4036 nextToken();
4037 if (token.value == TOK.rightBracket)
4039 t = new AST.TypeDArray(t); // []
4040 nextToken();
4042 else if (isDeclaration(&token, NeedDeclaratorId.no, TOK.rightBracket, null))
4044 // It's an associative array declaration
4045 //printf("it's an associative array\n");
4046 AST.Type index = parseType(); // [ type ]
4047 t = new AST.TypeAArray(t, index);
4048 check(TOK.rightBracket);
4050 else
4052 //printf("it's type[expression]\n");
4053 inBrackets++;
4054 AST.Expression e = parseAssignExp(); // [ expression ]
4055 if (!e)
4057 inBrackets--;
4058 check(TOK.rightBracket);
4059 continue;
4061 if (token.value == TOK.slice)
4063 nextToken();
4064 AST.Expression e2 = parseAssignExp(); // [ exp .. exp ]
4065 t = new AST.TypeSlice(t, e, e2);
4067 else
4069 t = new AST.TypeSArray(t, e);
4071 inBrackets--;
4072 check(TOK.rightBracket);
4074 continue;
4076 case TOK.delegate_:
4077 case TOK.function_:
4079 // Handle delegate declaration:
4080 // t delegate(parameter list) nothrow pure
4081 // t function(parameter list) nothrow pure
4082 const save = token.value;
4083 nextToken();
4085 auto parameterList = parseParameterList(null);
4087 StorageClass stc = parsePostfix(STC.undefined_, null);
4088 auto tf = new AST.TypeFunction(parameterList, t, linkage, stc);
4089 if (stc & (STC.const_ | STC.immutable_ | STC.shared_ | STC.wild | STC.return_))
4091 if (save == TOK.function_)
4092 error("`const`/`immutable`/`shared`/`inout`/`return` attributes are only valid for non-static member functions");
4093 else
4094 tf = cast(AST.TypeFunction)tf.addSTC(stc);
4096 t = save == TOK.delegate_ ? new AST.TypeDelegate(tf) : new AST.TypePointer(tf); // pointer to function
4097 continue;
4099 default:
4100 return t;
4102 assert(0);
4104 assert(0);
4107 /**********************
4108 * Parse Declarator
4109 * Params:
4110 * t = base type to start with
4111 * palt = OR in 1 for C-style function pointer declaration syntax,
4112 * 2 for C-style array declaration syntax, otherwise don't modify
4113 * pident = set to Identifier if there is one, null if not
4114 * tpl = if !null, then set to TemplateParameterList
4115 * storageClass = any storage classes seen so far
4116 * pdisable = set to true if @disable seen
4117 * pudas = any user defined attributes seen so far. Merged with any more found
4118 * Returns:
4119 * type declared
4120 * Reference: https://dlang.org/spec/declaration.html#Declarator
4122 private AST.Type parseDeclarator(AST.Type t, ref int palt, Identifier* pident,
4123 AST.TemplateParameters** tpl = null, StorageClass storageClass = 0,
4124 bool* pdisable = null, AST.Expressions** pudas = null)
4126 //printf("parseDeclarator(tpl = %p)\n", tpl);
4127 t = parseTypeSuffixes(t);
4128 AST.Type ts;
4129 switch (token.value)
4131 case TOK.identifier:
4132 if (pident)
4133 *pident = token.ident;
4134 else
4135 error("unexpected identifier `%s` in declarator", token.ident.toChars());
4136 ts = t;
4137 nextToken();
4138 break;
4140 case TOK.leftParenthesis:
4142 // like: T (*fp)();
4143 // like: T ((*fp))();
4144 if (peekNext() == TOK.mul || peekNext() == TOK.leftParenthesis)
4146 /* Parse things with parentheses around the identifier, like:
4147 * int (*ident[3])[]
4148 * although the D style would be:
4149 * int[]*[3] ident
4151 palt |= 1;
4152 nextToken();
4153 ts = parseDeclarator(t, palt, pident);
4154 check(TOK.rightParenthesis);
4155 break;
4157 ts = t;
4159 Token* peekt = &token;
4160 /* Completely disallow C-style things like:
4161 * T (a);
4162 * Improve error messages for the common bug of a missing return type
4163 * by looking to see if (a) looks like a parameter list.
4165 if (isParameters(&peekt))
4167 error("function declaration without return type. (Note that constructors are always named `this`)");
4169 else
4170 error("unexpected `(` in declarator");
4171 break;
4173 default:
4174 ts = t;
4175 break;
4178 // parse DeclaratorSuffixes
4179 while (1)
4181 switch (token.value)
4183 static if (CARRAYDECL)
4185 /* Support C style array syntax:
4186 * int ident[]
4187 * as opposed to D-style:
4188 * int[] ident
4190 case TOK.leftBracket:
4192 // This is the old C-style post [] syntax.
4193 AST.TypeNext ta;
4194 nextToken();
4195 if (token.value == TOK.rightBracket)
4197 // It's a dynamic array
4198 ta = new AST.TypeDArray(t); // []
4199 nextToken();
4200 palt |= 2;
4202 else if (isDeclaration(&token, NeedDeclaratorId.no, TOK.rightBracket, null))
4204 // It's an associative array
4205 //printf("it's an associative array\n");
4206 AST.Type index = parseType(); // [ type ]
4207 check(TOK.rightBracket);
4208 ta = new AST.TypeAArray(t, index);
4209 palt |= 2;
4211 else
4213 //printf("It's a static array\n");
4214 AST.Expression e = parseAssignExp(); // [ expression ]
4215 ta = new AST.TypeSArray(t, e);
4216 check(TOK.rightBracket);
4217 palt |= 2;
4220 /* Insert ta into
4221 * ts -> ... -> t
4222 * so that
4223 * ts -> ... -> ta -> t
4225 AST.Type* pt;
4226 for (pt = &ts; *pt != t; pt = &(cast(AST.TypeNext)*pt).next)
4229 *pt = ta;
4230 continue;
4233 case TOK.leftParenthesis:
4235 if (tpl)
4237 Token* tk = peekPastParen(&token);
4238 if (tk.value == TOK.leftParenthesis)
4240 /* Look ahead to see if this is (...)(...),
4241 * i.e. a function template declaration
4243 //printf("function template declaration\n");
4245 // Gather template parameter list
4246 *tpl = parseTemplateParameterList();
4248 else if (tk.value == TOK.assign)
4250 /* or (...) =,
4251 * i.e. a variable template declaration
4253 //printf("variable template declaration\n");
4254 *tpl = parseTemplateParameterList();
4255 break;
4259 auto parameterList = parseParameterList(null);
4261 /* Parse const/immutable/shared/inout/nothrow/pure/return postfix
4263 // merge prefix storage classes
4264 StorageClass stc = parsePostfix(storageClass, pudas);
4266 AST.Type tf = new AST.TypeFunction(parameterList, t, linkage, stc);
4267 tf = tf.addSTC(stc);
4268 if (pdisable)
4269 *pdisable = stc & STC.disable ? true : false;
4271 /* Insert tf into
4272 * ts -> ... -> t
4273 * so that
4274 * ts -> ... -> tf -> t
4276 AST.Type* pt;
4277 for (pt = &ts; *pt != t; pt = &(cast(AST.TypeNext)*pt).next)
4280 *pt = tf;
4281 break;
4283 default:
4284 break;
4286 break;
4288 return ts;
4291 private void parseStorageClasses(ref StorageClass storage_class, ref LINK link,
4292 ref bool setAlignment, ref AST.Expression ealign, ref AST.Expressions* udas,
4293 out Loc linkloc)
4295 StorageClass stc;
4296 bool sawLinkage = false; // seen a linkage declaration
4298 linkloc = Loc.initial;
4300 while (1)
4302 switch (token.value)
4304 case TOK.const_:
4305 if (peekNext() == TOK.leftParenthesis)
4306 break; // const as type constructor
4307 stc = STC.const_; // const as storage class
4308 goto L1;
4310 case TOK.immutable_:
4311 if (peekNext() == TOK.leftParenthesis)
4312 break;
4313 stc = STC.immutable_;
4314 goto L1;
4316 case TOK.shared_:
4317 if (peekNext() == TOK.leftParenthesis)
4318 break;
4319 stc = STC.shared_;
4320 goto L1;
4322 case TOK.inout_:
4323 if (peekNext() == TOK.leftParenthesis)
4324 break;
4325 stc = STC.wild;
4326 goto L1;
4328 case TOK.static_:
4329 stc = STC.static_;
4330 goto L1;
4332 case TOK.final_:
4333 stc = STC.final_;
4334 goto L1;
4336 case TOK.auto_:
4337 stc = STC.auto_;
4338 goto L1;
4340 case TOK.scope_:
4341 stc = STC.scope_;
4342 goto L1;
4344 case TOK.override_:
4345 stc = STC.override_;
4346 goto L1;
4348 case TOK.abstract_:
4349 stc = STC.abstract_;
4350 goto L1;
4352 case TOK.synchronized_:
4353 stc = STC.synchronized_;
4354 goto L1;
4356 case TOK.deprecated_:
4357 stc = STC.deprecated_;
4358 goto L1;
4360 case TOK.nothrow_:
4361 stc = STC.nothrow_;
4362 goto L1;
4364 case TOK.pure_:
4365 stc = STC.pure_;
4366 goto L1;
4368 case TOK.ref_:
4369 stc = STC.ref_;
4370 goto L1;
4372 case TOK.gshared:
4373 stc = STC.gshared;
4374 goto L1;
4376 case TOK.enum_:
4378 const tv = peekNext();
4379 if (tv == TOK.leftCurly || tv == TOK.colon)
4380 break;
4381 if (tv == TOK.identifier)
4383 const nextv = peekNext2();
4384 if (nextv == TOK.leftCurly || nextv == TOK.colon || nextv == TOK.semicolon)
4385 break;
4387 stc = STC.manifest;
4388 goto L1;
4391 case TOK.at:
4393 stc = parseAttribute(udas);
4394 if (stc)
4395 goto L1;
4396 continue;
4399 storage_class = appendStorageClass(storage_class, stc);
4400 nextToken();
4401 continue;
4403 case TOK.extern_:
4405 if (peekNext() != TOK.leftParenthesis)
4407 stc = STC.extern_;
4408 goto L1;
4411 if (sawLinkage)
4412 error("redundant linkage declaration");
4413 sawLinkage = true;
4414 linkloc = token.loc;
4415 auto res = parseLinkage();
4416 link = res.link;
4417 if (res.idents || res.identExps)
4419 error("C++ name spaces not allowed here");
4421 if (res.cppmangle != CPPMANGLE.def)
4423 error("C++ mangle declaration not allowed here");
4425 continue;
4427 case TOK.align_:
4429 nextToken();
4430 setAlignment = true;
4431 if (token.value == TOK.leftParenthesis)
4433 nextToken();
4434 ealign = parseExpression();
4435 check(TOK.rightParenthesis);
4437 continue;
4439 default:
4440 break;
4442 break;
4446 /**********************************
4447 * Parse Declarations.
4448 * These can be:
4449 * 1. declarations at global/class level
4450 * 2. declarations at statement level
4451 * Return array of Declaration *'s.
4453 private AST.Dsymbols* parseDeclarations(bool autodecl, PrefixAttributes!AST* pAttrs, const(char)* comment)
4455 StorageClass storage_class = STC.undefined_;
4456 TOK tok = TOK.reserved;
4457 LINK link = linkage;
4458 Loc linkloc = this.linkLoc;
4459 bool setAlignment = false;
4460 AST.Expression ealign;
4461 AST.Expressions* udas = null;
4463 //printf("parseDeclarations() %s\n", token.toChars());
4464 if (!comment)
4465 comment = token.blockComment.ptr;
4467 /* Look for AliasAssignment:
4468 * identifier = type;
4470 if (token.value == TOK.identifier && peekNext() == TOK.assign)
4472 const loc = token.loc;
4473 auto ident = token.ident;
4474 nextToken();
4475 nextToken(); // advance past =
4476 auto t = parseType();
4477 AST.Dsymbol s = new AST.AliasAssign(loc, ident, t, null);
4478 check(TOK.semicolon);
4479 addComment(s, comment);
4480 auto a = new AST.Dsymbols();
4481 a.push(s);
4482 return a;
4485 if (token.value == TOK.alias_)
4487 const loc = token.loc;
4488 tok = token.value;
4489 nextToken();
4491 /* Look for:
4492 * alias identifier this;
4494 if (token.value == TOK.identifier && peekNext() == TOK.this_)
4496 auto s = new AST.AliasThis(loc, token.ident);
4497 nextToken();
4498 check(TOK.this_);
4499 check(TOK.semicolon);
4500 auto a = new AST.Dsymbols();
4501 a.push(s);
4502 addComment(s, comment);
4503 return a;
4505 version (none)
4507 /* Look for:
4508 * alias this = identifier;
4510 if (token.value == TOK.this_ && peekNext() == TOK.assign && peekNext2() == TOK.identifier)
4512 check(TOK.this_);
4513 check(TOK.assign);
4514 auto s = new AliasThis(loc, token.ident);
4515 nextToken();
4516 check(TOK.semicolon);
4517 auto a = new Dsymbols();
4518 a.push(s);
4519 addComment(s, comment);
4520 return a;
4523 /* Look for:
4524 * alias identifier = type;
4525 * alias identifier(...) = type;
4527 if (token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign))
4529 auto a = new AST.Dsymbols();
4530 while (1)
4532 auto ident = token.ident;
4533 nextToken();
4534 AST.TemplateParameters* tpl = null;
4535 if (token.value == TOK.leftParenthesis)
4536 tpl = parseTemplateParameterList();
4537 check(TOK.assign);
4539 bool hasParsedAttributes;
4540 void parseAttributes()
4542 if (hasParsedAttributes) // only parse once
4543 return;
4544 hasParsedAttributes = true;
4545 udas = null;
4546 storage_class = STC.undefined_;
4547 link = linkage;
4548 linkloc = this.linkLoc;
4549 setAlignment = false;
4550 ealign = null;
4551 parseStorageClasses(storage_class, link, setAlignment, ealign, udas, linkloc);
4554 if (token.value == TOK.at)
4555 parseAttributes;
4557 AST.Declaration v;
4558 AST.Dsymbol s;
4560 // try to parse function type:
4561 // TypeCtors? BasicType ( Parameters ) MemberFunctionAttributes
4562 bool attributesAppended;
4563 const StorageClass funcStc = parseTypeCtor();
4564 Token* tlu = &token;
4565 Token* tk;
4566 if (token.value != TOK.function_ &&
4567 token.value != TOK.delegate_ &&
4568 isBasicType(&tlu) && tlu &&
4569 tlu.value == TOK.leftParenthesis)
4571 AST.Type tret = parseBasicType();
4572 auto parameterList = parseParameterList(null);
4574 parseAttributes();
4575 if (udas)
4576 error("user-defined attributes not allowed for `alias` declarations");
4578 attributesAppended = true;
4579 storage_class = appendStorageClass(storage_class, funcStc);
4580 AST.Type tf = new AST.TypeFunction(parameterList, tret, link, storage_class);
4581 v = new AST.AliasDeclaration(loc, ident, tf);
4583 else if (token.value == TOK.function_ ||
4584 token.value == TOK.delegate_ ||
4585 token.value == TOK.leftParenthesis &&
4586 skipAttributes(peekPastParen(&token), &tk) &&
4587 (tk.value == TOK.goesTo || tk.value == TOK.leftCurly) ||
4588 token.value == TOK.leftCurly ||
4589 token.value == TOK.identifier && peekNext() == TOK.goesTo ||
4590 token.value == TOK.ref_ && peekNext() == TOK.leftParenthesis &&
4591 skipAttributes(peekPastParen(peek(&token)), &tk) &&
4592 (tk.value == TOK.goesTo || tk.value == TOK.leftCurly)
4595 // function (parameters) { statements... }
4596 // delegate (parameters) { statements... }
4597 // (parameters) { statements... }
4598 // (parameters) => expression
4599 // { statements... }
4600 // identifier => expression
4601 // ref (parameters) { statements... }
4602 // ref (parameters) => expression
4604 s = parseFunctionLiteral();
4606 if (udas !is null)
4608 if (storage_class != 0)
4609 error("Cannot put a storage-class in an alias declaration.");
4610 // parseAttributes shouldn't have set these variables
4611 assert(link == linkage && !setAlignment && ealign is null);
4612 auto tpl_ = cast(AST.TemplateDeclaration) s;
4613 assert(tpl_ !is null && tpl_.members.dim == 1);
4614 auto fd = cast(AST.FuncLiteralDeclaration) (*tpl_.members)[0];
4615 auto tf = cast(AST.TypeFunction) fd.type;
4616 assert(tf.parameterList.parameters.dim > 0);
4617 auto as = new AST.Dsymbols();
4618 (*tf.parameterList.parameters)[0].userAttribDecl = new AST.UserAttributeDeclaration(udas, as);
4621 v = new AST.AliasDeclaration(loc, ident, s);
4623 else
4625 parseAttributes();
4626 // type
4627 if (udas)
4628 error("user-defined attributes not allowed for `%s` declarations", Token.toChars(tok));
4630 auto t = parseType();
4632 // Disallow meaningless storage classes on type aliases
4633 if (storage_class)
4635 // Don't raise errors for STC that are part of a function/delegate type, e.g.
4636 // `alias F = ref pure nothrow @nogc @safe int function();`
4637 auto tp = t.isTypePointer;
4638 const isFuncType = (tp && tp.next.isTypeFunction) || t.isTypeDelegate;
4639 const remStc = isFuncType ? (storage_class & ~STC.FUNCATTR) : storage_class;
4641 if (remStc)
4643 OutBuffer buf;
4644 AST.stcToBuffer(&buf, remStc);
4645 // @@@DEPRECATED_2.093@@@
4646 // Deprecated in 2020-07, can be made an error in 2.103
4647 deprecation("storage class `%s` has no effect in type aliases", buf.peekChars());
4651 v = new AST.AliasDeclaration(loc, ident, t);
4653 if (!attributesAppended)
4654 storage_class = appendStorageClass(storage_class, funcStc);
4655 v.storage_class = storage_class;
4657 s = v;
4658 if (tpl)
4660 auto a2 = new AST.Dsymbols();
4661 a2.push(s);
4662 auto tempdecl = new AST.TemplateDeclaration(loc, ident, tpl, null, a2);
4663 s = tempdecl;
4665 if (link != linkage)
4667 auto a2 = new AST.Dsymbols();
4668 a2.push(s);
4669 s = new AST.LinkDeclaration(linkloc, link, a2);
4671 a.push(s);
4673 switch (token.value)
4675 case TOK.semicolon:
4676 nextToken();
4677 addComment(s, comment);
4678 break;
4680 case TOK.comma:
4681 nextToken();
4682 addComment(s, comment);
4683 if (token.value != TOK.identifier)
4685 error("identifier expected following comma, not `%s`", token.toChars());
4686 break;
4688 if (peekNext() != TOK.assign && peekNext() != TOK.leftParenthesis)
4690 error("`=` expected following identifier");
4691 nextToken();
4692 break;
4694 continue;
4696 default:
4697 error("semicolon expected to close `%s` declaration", Token.toChars(tok));
4698 break;
4700 break;
4702 return a;
4705 // alias StorageClasses type ident;
4708 AST.Type ts;
4710 if (!autodecl)
4712 parseStorageClasses(storage_class, link, setAlignment, ealign, udas, linkloc);
4714 if (token.value == TOK.enum_)
4716 AST.Dsymbol d = parseEnum();
4717 auto a = new AST.Dsymbols();
4718 a.push(d);
4720 if (udas)
4722 d = new AST.UserAttributeDeclaration(udas, a);
4723 a = new AST.Dsymbols();
4724 a.push(d);
4727 addComment(d, comment);
4728 return a;
4730 if (token.value == TOK.struct_ ||
4731 token.value == TOK.union_ ||
4732 token.value == TOK.class_ ||
4733 token.value == TOK.interface_)
4735 AST.Dsymbol s = parseAggregate();
4736 auto a = new AST.Dsymbols();
4737 a.push(s);
4739 if (storage_class)
4741 s = new AST.StorageClassDeclaration(storage_class, a);
4742 a = new AST.Dsymbols();
4743 a.push(s);
4745 if (setAlignment)
4747 s = new AST.AlignDeclaration(s.loc, ealign, a);
4748 a = new AST.Dsymbols();
4749 a.push(s);
4751 if (link != linkage)
4753 s = new AST.LinkDeclaration(linkloc, link, a);
4754 a = new AST.Dsymbols();
4755 a.push(s);
4757 if (udas)
4759 s = new AST.UserAttributeDeclaration(udas, a);
4760 a = new AST.Dsymbols();
4761 a.push(s);
4764 addComment(s, comment);
4765 return a;
4768 /* Look for auto initializers:
4769 * storage_class identifier = initializer;
4770 * storage_class identifier(...) = initializer;
4772 if ((storage_class || udas) && token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign))
4774 AST.Dsymbols* a = parseAutoDeclarations(storage_class, comment);
4775 if (udas)
4777 AST.Dsymbol s = new AST.UserAttributeDeclaration(udas, a);
4778 a = new AST.Dsymbols();
4779 a.push(s);
4781 return a;
4784 /* Look for return type inference for template functions.
4787 Token* tk;
4788 if ((storage_class || udas) && token.value == TOK.identifier && skipParens(peek(&token), &tk) &&
4789 skipAttributes(tk, &tk) &&
4790 (tk.value == TOK.leftParenthesis || tk.value == TOK.leftCurly || tk.value == TOK.in_ || tk.value == TOK.out_ || tk.value == TOK.goesTo ||
4791 tk.value == TOK.do_ || tk.value == TOK.identifier && tk.ident == Id._body))
4793 // @@@DEPRECATED@@@
4794 // https://github.com/dlang/DIPs/blob/1f5959abe482b1f9094f6484a7d0a3ade77fc2fc/DIPs/accepted/DIP1003.md
4795 // Deprecated in 2.097 - Can be removed from 2.117
4796 // The deprecation period is longer than usual as `body`
4797 // was quite widely used.
4798 if (tk.value == TOK.identifier && tk.ident == Id._body)
4799 deprecation("Usage of the `body` keyword is deprecated. Use `do` instead.");
4801 ts = null;
4803 else
4805 ts = parseBasicType();
4806 ts = parseTypeSuffixes(ts);
4811 if (pAttrs)
4813 storage_class |= pAttrs.storageClass;
4814 //pAttrs.storageClass = STC.undefined_;
4817 AST.Type tfirst = null;
4818 auto a = new AST.Dsymbols();
4820 while (1)
4822 AST.TemplateParameters* tpl = null;
4823 bool disable;
4824 int alt = 0;
4826 const loc = token.loc;
4827 Identifier ident;
4829 auto t = parseDeclarator(ts, alt, &ident, &tpl, storage_class, &disable, &udas);
4830 assert(t);
4831 if (!tfirst)
4832 tfirst = t;
4833 else if (t != tfirst)
4834 error("multiple declarations must have the same type, not `%s` and `%s`", tfirst.toChars(), t.toChars());
4836 bool isThis = (t.ty == Tident && (cast(AST.TypeIdentifier)t).ident == Id.This && token.value == TOK.assign);
4837 if (ident)
4838 checkCstyleTypeSyntax(loc, t, alt, ident);
4839 else if (!isThis && (t != AST.Type.terror))
4840 error("no identifier for declarator `%s`", t.toChars());
4842 if (tok == TOK.alias_)
4844 AST.Declaration v;
4845 AST.Initializer _init = null;
4847 /* Aliases can no longer have multiple declarators, storage classes,
4848 * linkages, or auto declarations.
4849 * These never made any sense, anyway.
4850 * The code below needs to be fixed to reject them.
4851 * The grammar has already been fixed to preclude them.
4854 if (udas)
4855 error("user-defined attributes not allowed for `%s` declarations", Token.toChars(tok));
4857 if (token.value == TOK.assign)
4859 nextToken();
4860 _init = parseInitializer();
4862 if (_init)
4864 if (isThis)
4865 error("cannot use syntax `alias this = %s`, use `alias %s this` instead", _init.toChars(), _init.toChars());
4866 else
4867 error("alias cannot have initializer");
4869 v = new AST.AliasDeclaration(loc, ident, t);
4871 v.storage_class = storage_class;
4872 if (pAttrs)
4874 /* AliasDeclaration distinguish @safe, @system, @trusted attributes
4875 * on prefix and postfix.
4876 * @safe alias void function() FP1;
4877 * alias @safe void function() FP2; // FP2 is not @safe
4878 * alias void function() @safe FP3;
4880 pAttrs.storageClass &= STC.safeGroup;
4882 AST.Dsymbol s = v;
4884 if (link != linkage)
4886 auto ax = new AST.Dsymbols();
4887 ax.push(v);
4888 s = new AST.LinkDeclaration(linkloc, link, ax);
4890 a.push(s);
4891 switch (token.value)
4893 case TOK.semicolon:
4894 nextToken();
4895 addComment(s, comment);
4896 break;
4898 case TOK.comma:
4899 nextToken();
4900 addComment(s, comment);
4901 continue;
4903 default:
4904 error("semicolon expected to close `%s` declaration", Token.toChars(tok));
4905 break;
4908 else if (t.ty == Tfunction)
4910 AST.Expression constraint = null;
4911 //printf("%s funcdecl t = %s, storage_class = x%lx\n", loc.toChars(), t.toChars(), storage_class);
4912 auto f = new AST.FuncDeclaration(loc, Loc.initial, ident, storage_class | (disable ? STC.disable : 0), t);
4913 if (pAttrs)
4914 pAttrs.storageClass = STC.undefined_;
4915 if (tpl)
4916 constraint = parseConstraint();
4917 AST.Dsymbol s = parseContracts(f);
4918 auto tplIdent = s.ident;
4920 if (link != linkage)
4922 auto ax = new AST.Dsymbols();
4923 ax.push(s);
4924 s = new AST.LinkDeclaration(linkloc, link, ax);
4926 if (udas)
4928 auto ax = new AST.Dsymbols();
4929 ax.push(s);
4930 s = new AST.UserAttributeDeclaration(udas, ax);
4933 /* A template parameter list means it's a function template
4935 if (tpl)
4937 // Wrap a template around the function declaration
4938 auto decldefs = new AST.Dsymbols();
4939 decldefs.push(s);
4940 auto tempdecl = new AST.TemplateDeclaration(loc, tplIdent, tpl, constraint, decldefs);
4941 s = tempdecl;
4943 StorageClass stc2 = STC.undefined_;
4944 if (storage_class & STC.static_)
4946 assert(f.storage_class & STC.static_);
4947 f.storage_class &= ~STC.static_;
4948 stc2 |= STC.static_;
4950 if (storage_class & STC.deprecated_)
4952 assert(f.storage_class & STC.deprecated_);
4953 f.storage_class &= ~STC.deprecated_;
4954 stc2 |= STC.deprecated_;
4956 if (stc2 != STC.undefined_)
4958 auto ax = new AST.Dsymbols();
4959 ax.push(s);
4960 s = new AST.StorageClassDeclaration(stc2, ax);
4963 a.push(s);
4964 addComment(s, comment);
4966 else if (ident)
4968 AST.Initializer _init = null;
4969 if (token.value == TOK.assign)
4971 nextToken();
4972 _init = parseInitializer();
4975 auto v = new AST.VarDeclaration(loc, t, ident, _init);
4976 v.storage_class = storage_class;
4977 if (pAttrs)
4978 pAttrs.storageClass = STC.undefined_;
4980 AST.Dsymbol s = v;
4982 if (tpl && _init)
4984 auto a2 = new AST.Dsymbols();
4985 a2.push(s);
4986 auto tempdecl = new AST.TemplateDeclaration(loc, ident, tpl, null, a2, 0);
4987 s = tempdecl;
4989 if (setAlignment)
4991 auto ax = new AST.Dsymbols();
4992 ax.push(s);
4993 s = new AST.AlignDeclaration(v.loc, ealign, ax);
4995 if (link != linkage)
4997 auto ax = new AST.Dsymbols();
4998 ax.push(s);
4999 s = new AST.LinkDeclaration(linkloc, link, ax);
5001 if (udas)
5003 auto ax = new AST.Dsymbols();
5004 ax.push(s);
5005 s = new AST.UserAttributeDeclaration(udas, ax);
5007 a.push(s);
5008 switch (token.value)
5010 case TOK.semicolon:
5011 nextToken();
5012 addComment(s, comment);
5013 break;
5015 case TOK.comma:
5016 nextToken();
5017 addComment(s, comment);
5018 continue;
5020 default:
5021 error("semicolon expected, not `%s`", token.toChars());
5022 break;
5025 break;
5027 return a;
5030 private AST.Dsymbol parseFunctionLiteral()
5032 const loc = token.loc;
5033 AST.TemplateParameters* tpl = null;
5034 AST.ParameterList parameterList;
5035 AST.Type tret = null;
5036 StorageClass stc = 0;
5037 TOK save = TOK.reserved;
5039 switch (token.value)
5041 case TOK.function_:
5042 case TOK.delegate_:
5043 save = token.value;
5044 nextToken();
5045 if (token.value == TOK.ref_)
5047 // function ref (parameters) { statements... }
5048 // delegate ref (parameters) { statements... }
5049 stc = STC.ref_;
5050 nextToken();
5052 if (token.value != TOK.leftParenthesis && token.value != TOK.leftCurly)
5054 // function type (parameters) { statements... }
5055 // delegate type (parameters) { statements... }
5056 tret = parseBasicType();
5057 tret = parseTypeSuffixes(tret); // function return type
5060 if (token.value == TOK.leftParenthesis)
5062 // function (parameters) { statements... }
5063 // delegate (parameters) { statements... }
5065 else
5067 // function { statements... }
5068 // delegate { statements... }
5069 break;
5071 goto case TOK.leftParenthesis;
5073 case TOK.ref_:
5075 // ref (parameters) => expression
5076 // ref (parameters) { statements... }
5077 stc = STC.ref_;
5078 nextToken();
5079 goto case TOK.leftParenthesis;
5081 case TOK.leftParenthesis:
5083 // (parameters) => expression
5084 // (parameters) { statements... }
5085 parameterList = parseParameterList(&tpl);
5086 stc = parsePostfix(stc, null);
5087 if (StorageClass modStc = stc & STC.TYPECTOR)
5089 if (save == TOK.function_)
5091 OutBuffer buf;
5092 AST.stcToBuffer(&buf, modStc);
5093 error("function literal cannot be `%s`", buf.peekChars());
5095 else
5096 save = TOK.delegate_;
5098 break;
5100 case TOK.leftCurly:
5101 // { statements... }
5102 break;
5104 case TOK.identifier:
5106 // identifier => expression
5107 parameterList.parameters = new AST.Parameters();
5108 Identifier id = Identifier.generateId("__T");
5109 AST.Type t = new AST.TypeIdentifier(loc, id);
5110 parameterList.parameters.push(new AST.Parameter(STC.parameter, t, token.ident, null, null));
5112 tpl = new AST.TemplateParameters();
5113 AST.TemplateParameter tp = new AST.TemplateTypeParameter(loc, id, null, null);
5114 tpl.push(tp);
5116 nextToken();
5117 break;
5119 default:
5120 assert(0);
5123 auto tf = new AST.TypeFunction(parameterList, tret, linkage, stc);
5124 tf = cast(AST.TypeFunction)tf.addSTC(stc);
5125 auto fd = new AST.FuncLiteralDeclaration(loc, Loc.initial, tf, save, null);
5127 if (token.value == TOK.goesTo)
5129 check(TOK.goesTo);
5130 if (token.value == TOK.leftCurly)
5132 deprecation("Using `(args) => { ... }` to create a delegate that returns a delegate is error-prone.");
5133 deprecationSupplemental(token.loc, "Use `(args) { ... }` for a multi-statement function literal or use `(args) => () { }` if you intended for the lambda to return a delegate.");
5135 const returnloc = token.loc;
5136 AST.Expression ae = parseAssignExp();
5137 fd.fbody = new AST.ReturnStatement(returnloc, ae);
5138 fd.endloc = token.loc;
5140 else
5142 parseContracts(fd);
5145 if (tpl)
5147 // Wrap a template around function fd
5148 auto decldefs = new AST.Dsymbols();
5149 decldefs.push(fd);
5150 return new AST.TemplateDeclaration(fd.loc, fd.ident, tpl, null, decldefs, false, true);
5152 return fd;
5155 /*****************************************
5156 * Parse contracts following function declaration.
5158 private AST.FuncDeclaration parseContracts(AST.FuncDeclaration f)
5160 LINK linksave = linkage;
5162 bool literal = f.isFuncLiteralDeclaration() !is null;
5164 // The following is irrelevant, as it is overridden by sc.linkage in
5165 // TypeFunction::semantic
5166 linkage = LINK.d; // nested functions have D linkage
5167 bool requireDo = false;
5169 switch (token.value)
5171 case TOK.goesTo:
5172 if (requireDo)
5173 error("missing `do { ... }` after `in` or `out`");
5174 if (!global.params.shortenedMethods)
5175 error("=> shortened method not enabled, compile with compiler switch `-preview=shortenedMethods`");
5176 const returnloc = token.loc;
5177 nextToken();
5178 f.fbody = new AST.ReturnStatement(returnloc, parseExpression());
5179 f.endloc = token.loc;
5180 check(TOK.semicolon);
5181 break;
5183 case TOK.leftCurly:
5184 if (requireDo)
5185 error("missing `do { ... }` after `in` or `out`");
5186 f.fbody = parseStatement(ParseStatementFlags.semi);
5187 f.endloc = endloc;
5188 break;
5190 case TOK.identifier:
5191 if (token.ident == Id._body)
5193 // @@@DEPRECATED@@@
5194 // https://github.com/dlang/DIPs/blob/1f5959abe482b1f9094f6484a7d0a3ade77fc2fc/DIPs/accepted/DIP1003.md
5195 // Deprecated in 2.097 - Can be removed from 2.117
5196 // The deprecation period is longer than usual as `body`
5197 // was quite widely used.
5198 deprecation("Usage of the `body` keyword is deprecated. Use `do` instead.");
5199 goto case TOK.do_;
5201 goto default;
5203 case TOK.do_:
5204 nextToken();
5205 f.fbody = parseStatement(ParseStatementFlags.curly);
5206 f.endloc = endloc;
5207 break;
5209 version (none)
5211 // Do we want this for function declarations, so we can do:
5212 // int x, y, foo(), z;
5213 case TOK.comma:
5214 nextToken();
5215 continue;
5218 case TOK.in_:
5219 // in { statements... }
5220 // in (expression)
5221 auto loc = token.loc;
5222 nextToken();
5223 if (!f.frequires)
5225 f.frequires = new AST.Statements;
5227 if (token.value == TOK.leftParenthesis)
5229 nextToken();
5230 AST.Expression e = parseAssignExp(), msg = null;
5231 if (token.value == TOK.comma)
5233 nextToken();
5234 if (token.value != TOK.rightParenthesis)
5236 msg = parseAssignExp();
5237 if (token.value == TOK.comma)
5238 nextToken();
5241 check(TOK.rightParenthesis);
5242 e = new AST.AssertExp(loc, e, msg);
5243 f.frequires.push(new AST.ExpStatement(loc, e));
5244 requireDo = false;
5246 else
5248 f.frequires.push(parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_));
5249 requireDo = true;
5251 goto L1;
5253 case TOK.out_:
5254 // out { statements... }
5255 // out (; expression)
5256 // out (identifier) { statements... }
5257 // out (identifier; expression)
5258 auto loc = token.loc;
5259 nextToken();
5260 if (!f.fensures)
5262 f.fensures = new AST.Ensures;
5264 Identifier id = null;
5265 if (token.value != TOK.leftCurly)
5267 check(TOK.leftParenthesis);
5268 if (token.value != TOK.identifier && token.value != TOK.semicolon)
5269 error("`(identifier) { ... }` or `(identifier; expression)` following `out` expected, not `%s`", token.toChars());
5270 if (token.value != TOK.semicolon)
5272 id = token.ident;
5273 nextToken();
5275 if (token.value == TOK.semicolon)
5277 nextToken();
5278 AST.Expression e = parseAssignExp(), msg = null;
5279 if (token.value == TOK.comma)
5281 nextToken();
5282 if (token.value != TOK.rightParenthesis)
5284 msg = parseAssignExp();
5285 if (token.value == TOK.comma)
5286 nextToken();
5289 check(TOK.rightParenthesis);
5290 e = new AST.AssertExp(loc, e, msg);
5291 f.fensures.push(AST.Ensure(id, new AST.ExpStatement(loc, e)));
5292 requireDo = false;
5293 goto L1;
5295 check(TOK.rightParenthesis);
5297 f.fensures.push(AST.Ensure(id, parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_)));
5298 requireDo = true;
5299 goto L1;
5301 case TOK.semicolon:
5302 if (!literal)
5304 // https://issues.dlang.org/show_bug.cgi?id=15799
5305 // Semicolon becomes a part of function declaration
5306 // only when 'do' is not required
5307 if (!requireDo)
5308 nextToken();
5309 break;
5311 goto default;
5313 default:
5314 if (literal)
5316 const(char)* sbody = requireDo ? "do " : "";
5317 error("missing `%s{ ... }` for function literal", sbody);
5319 else if (!requireDo) // allow contracts even with no body
5321 TOK t = token.value;
5322 if (t == TOK.const_ || t == TOK.immutable_ || t == TOK.inout_ || t == TOK.return_ ||
5323 t == TOK.shared_ || t == TOK.nothrow_ || t == TOK.pure_)
5324 error("'%s' cannot be placed after a template constraint", token.toChars);
5325 else if (t == TOK.at)
5326 error("attributes cannot be placed after a template constraint");
5327 else if (t == TOK.if_)
5328 error("cannot use function constraints for non-template functions. Use `static if` instead");
5329 else
5330 error("semicolon expected following function declaration");
5332 break;
5334 if (literal && !f.fbody)
5336 // Set empty function body for error recovery
5337 f.fbody = new AST.CompoundStatement(Loc.initial, cast(AST.Statement)null);
5340 linkage = linksave;
5342 return f;
5345 /*****************************************
5347 private void checkDanglingElse(Loc elseloc)
5349 if (token.value != TOK.else_ && token.value != TOK.catch_ && token.value != TOK.finally_ && lookingForElse.linnum != 0)
5351 warning(elseloc, "else is dangling, add { } after condition at %s", lookingForElse.toChars());
5355 /* *************************
5356 * Issue errors if C-style syntax
5357 * Params:
5358 * alt = !=0 for C-style syntax
5360 private void checkCstyleTypeSyntax(Loc loc, AST.Type t, int alt, Identifier ident)
5362 if (!alt)
5363 return;
5365 const(char)* sp = !ident ? "" : " ";
5366 const(char)* s = !ident ? "" : ident.toChars();
5367 error(loc, "instead of C-style syntax, use D-style `%s%s%s`", t.toChars(), sp, s);
5370 /*****************************************
5371 * Parses `foreach` statements, `static foreach` statements and
5372 * `static foreach` declarations.
5373 * Params:
5374 * Foreach = one of Statement, StaticForeachStatement, StaticForeachDeclaration
5375 * loc = location of foreach
5376 * pLastDecl = non-null for StaticForeachDeclaration
5377 * Returns:
5378 * the Foreach generated
5380 private Foreach parseForeach(alias Foreach)(Loc loc, AST.Dsymbol* pLastDecl)
5382 static if (is(Foreach == AST.StaticForeachStatement) || is(Foreach == AST.StaticForeachDeclaration))
5384 nextToken();
5387 TOK op = token.value;
5389 nextToken();
5390 check(TOK.leftParenthesis);
5392 auto parameters = new AST.Parameters();
5393 while (1)
5395 Identifier ai = null;
5396 AST.Type at;
5398 StorageClass storageClass = 0;
5399 StorageClass stc = 0;
5400 Lagain:
5401 if (stc)
5403 storageClass = appendStorageClass(storageClass, stc);
5404 nextToken();
5406 switch (token.value)
5408 case TOK.ref_:
5409 stc = STC.ref_;
5410 goto Lagain;
5412 case TOK.scope_:
5413 stc = STC.scope_;
5414 goto Lagain;
5416 case TOK.out_:
5417 error("cannot declare `out` loop variable, use `ref` instead");
5418 stc = STC.out_;
5419 goto Lagain;
5421 case TOK.enum_:
5422 stc = STC.manifest;
5423 goto Lagain;
5425 case TOK.alias_:
5426 storageClass = appendStorageClass(storageClass, STC.alias_);
5427 nextToken();
5428 break;
5430 case TOK.const_:
5431 if (peekNext() != TOK.leftParenthesis)
5433 stc = STC.const_;
5434 goto Lagain;
5436 break;
5438 case TOK.immutable_:
5439 if (peekNext() != TOK.leftParenthesis)
5441 stc = STC.immutable_;
5442 goto Lagain;
5444 break;
5446 case TOK.shared_:
5447 if (peekNext() != TOK.leftParenthesis)
5449 stc = STC.shared_;
5450 goto Lagain;
5452 break;
5454 case TOK.inout_:
5455 if (peekNext() != TOK.leftParenthesis)
5457 stc = STC.wild;
5458 goto Lagain;
5460 break;
5462 default:
5463 break;
5465 if (token.value == TOK.identifier)
5467 const tv = peekNext();
5468 if (tv == TOK.comma || tv == TOK.semicolon)
5470 ai = token.ident;
5471 at = null; // infer argument type
5472 nextToken();
5473 goto Larg;
5476 at = parseType(&ai);
5477 if (!ai)
5478 error("no identifier for declarator `%s`", at.toChars());
5479 Larg:
5480 auto p = new AST.Parameter(storageClass, at, ai, null, null);
5481 parameters.push(p);
5482 if (token.value == TOK.comma)
5484 nextToken();
5485 continue;
5487 break;
5489 check(TOK.semicolon);
5491 AST.Expression aggr = parseExpression();
5492 if (token.value == TOK.slice && parameters.dim == 1)
5494 AST.Parameter p = (*parameters)[0];
5495 nextToken();
5496 AST.Expression upr = parseExpression();
5497 check(TOK.rightParenthesis);
5498 Loc endloc;
5499 static if (is(Foreach == AST.Statement) || is(Foreach == AST.StaticForeachStatement))
5501 AST.Statement _body = parseStatement(0, null, &endloc);
5503 else
5505 AST.Statement _body = null;
5507 auto rangefe = new AST.ForeachRangeStatement(loc, op, p, aggr, upr, _body, endloc);
5508 static if (is(Foreach == AST.Statement))
5510 return rangefe;
5512 else static if(is(Foreach == AST.StaticForeachDeclaration))
5514 return new AST.StaticForeachDeclaration(new AST.StaticForeach(loc, null, rangefe), parseBlock(pLastDecl));
5516 else static if (is(Foreach == AST.StaticForeachStatement))
5518 return new AST.StaticForeachStatement(loc, new AST.StaticForeach(loc, null, rangefe));
5521 else
5523 check(TOK.rightParenthesis);
5524 Loc endloc;
5525 static if (is(Foreach == AST.Statement) || is(Foreach == AST.StaticForeachStatement))
5527 AST.Statement _body = parseStatement(0, null, &endloc);
5529 else
5531 AST.Statement _body = null;
5533 auto aggrfe = new AST.ForeachStatement(loc, op, parameters, aggr, _body, endloc);
5534 static if (is(Foreach == AST.Statement))
5536 return aggrfe;
5538 else static if(is(Foreach == AST.StaticForeachDeclaration))
5540 return new AST.StaticForeachDeclaration(new AST.StaticForeach(loc, aggrfe, null), parseBlock(pLastDecl));
5542 else static if (is(Foreach == AST.StaticForeachStatement))
5544 return new AST.StaticForeachStatement(loc, new AST.StaticForeach(loc, aggrfe, null));
5550 /***
5551 * Parse an assignment condition for if or while statements.
5553 * Returns:
5554 * The variable that is declared inside the condition
5556 AST.Parameter parseAssignCondition()
5558 AST.Parameter param = null;
5559 StorageClass storageClass = 0;
5560 StorageClass stc = 0;
5561 LagainStc:
5562 if (stc)
5564 storageClass = appendStorageClass(storageClass, stc);
5565 nextToken();
5567 switch (token.value)
5569 case TOK.ref_:
5570 stc = STC.ref_;
5571 goto LagainStc;
5573 case TOK.scope_:
5574 stc = STC.scope_;
5575 goto LagainStc;
5577 case TOK.auto_:
5578 stc = STC.auto_;
5579 goto LagainStc;
5581 case TOK.const_:
5582 if (peekNext() != TOK.leftParenthesis)
5584 stc = STC.const_;
5585 goto LagainStc;
5587 break;
5589 case TOK.immutable_:
5590 if (peekNext() != TOK.leftParenthesis)
5592 stc = STC.immutable_;
5593 goto LagainStc;
5595 break;
5597 case TOK.shared_:
5598 if (peekNext() != TOK.leftParenthesis)
5600 stc = STC.shared_;
5601 goto LagainStc;
5603 break;
5605 case TOK.inout_:
5606 if (peekNext() != TOK.leftParenthesis)
5608 stc = STC.wild;
5609 goto LagainStc;
5611 break;
5613 default:
5614 break;
5616 auto n = peek(&token);
5617 if (storageClass != 0 && token.value == TOK.identifier && n.value == TOK.assign)
5619 Identifier ai = token.ident;
5620 AST.Type at = null; // infer parameter type
5621 nextToken();
5622 check(TOK.assign);
5623 param = new AST.Parameter(storageClass, at, ai, null, null);
5625 else if (isDeclaration(&token, NeedDeclaratorId.must, TOK.assign, null))
5627 Identifier ai;
5628 AST.Type at = parseType(&ai);
5629 check(TOK.assign);
5630 param = new AST.Parameter(storageClass, at, ai, null, null);
5632 else if (storageClass != 0)
5633 error("found `%s` while expecting `=` or identifier", n.toChars());
5635 return param;
5638 /*****************************************
5639 * Input:
5640 * flags PSxxxx
5641 * Output:
5642 * pEndloc if { ... statements ... }, store location of closing brace, otherwise loc of last token of statement
5644 AST.Statement parseStatement(int flags, const(char)** endPtr = null, Loc* pEndloc = null)
5646 AST.Statement s;
5647 AST.Condition cond;
5648 AST.Statement ifbody;
5649 AST.Statement elsebody;
5650 bool isfinal;
5651 const loc = token.loc;
5653 //printf("parseStatement()\n");
5654 if (flags & ParseStatementFlags.curly && token.value != TOK.leftCurly)
5655 error("statement expected to be `{ }`, not `%s`", token.toChars());
5657 switch (token.value)
5659 case TOK.identifier:
5661 /* A leading identifier can be a declaration, label, or expression.
5662 * The easiest case to check first is label:
5664 if (peekNext() == TOK.colonColon)
5666 // skip ident::
5667 nextToken();
5668 nextToken();
5669 error("use `.` for member lookup, not `::`");
5670 break;
5673 if (peekNext() == TOK.colon)
5675 // It's a label
5676 Identifier ident = token.ident;
5677 nextToken();
5678 nextToken();
5679 if (token.value == TOK.rightCurly)
5680 s = null;
5681 else if (token.value == TOK.leftCurly)
5682 s = parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_);
5683 else
5684 s = parseStatement(ParseStatementFlags.semiOk);
5685 s = new AST.LabelStatement(loc, ident, s);
5686 break;
5688 goto case TOK.dot;
5690 case TOK.dot:
5691 case TOK.typeof_:
5692 case TOK.vector:
5693 case TOK.traits:
5694 /* https://issues.dlang.org/show_bug.cgi?id=15163
5695 * If tokens can be handled as
5696 * old C-style declaration or D expression, prefer the latter.
5698 if (isDeclaration(&token, NeedDeclaratorId.mustIfDstyle, TOK.reserved, null))
5699 goto Ldeclaration;
5700 goto Lexp;
5702 case TOK.assert_:
5703 case TOK.this_:
5704 case TOK.super_:
5705 case TOK.int32Literal:
5706 case TOK.uns32Literal:
5707 case TOK.int64Literal:
5708 case TOK.uns64Literal:
5709 case TOK.int128Literal:
5710 case TOK.uns128Literal:
5711 case TOK.float32Literal:
5712 case TOK.float64Literal:
5713 case TOK.float80Literal:
5714 case TOK.imaginary32Literal:
5715 case TOK.imaginary64Literal:
5716 case TOK.imaginary80Literal:
5717 case TOK.charLiteral:
5718 case TOK.wcharLiteral:
5719 case TOK.dcharLiteral:
5720 case TOK.null_:
5721 case TOK.true_:
5722 case TOK.false_:
5723 case TOK.string_:
5724 case TOK.hexadecimalString:
5725 case TOK.leftParenthesis:
5726 case TOK.cast_:
5727 case TOK.mul:
5728 case TOK.min:
5729 case TOK.add:
5730 case TOK.tilde:
5731 case TOK.not:
5732 case TOK.plusPlus:
5733 case TOK.minusMinus:
5734 case TOK.new_:
5735 case TOK.delete_:
5736 case TOK.delegate_:
5737 case TOK.function_:
5738 case TOK.typeid_:
5739 case TOK.is_:
5740 case TOK.leftBracket:
5741 case TOK.file:
5742 case TOK.fileFullPath:
5743 case TOK.line:
5744 case TOK.moduleString:
5745 case TOK.functionString:
5746 case TOK.prettyFunction:
5747 Lexp:
5749 AST.Expression exp = parseExpression();
5750 /* https://issues.dlang.org/show_bug.cgi?id=15103
5751 * Improve declaration / initialization syntax error message
5752 * Error: found 'foo' when expecting ';' following statement
5753 * becomes Error: found `(` when expecting `;` or `=`, did you mean `Foo foo = 42`?
5755 if (token.value == TOK.identifier && exp.op == EXP.identifier)
5757 error("found `%s` when expecting `;` or `=`, did you mean `%s %s = %s`?", peek(&token).toChars(), exp.toChars(), token.toChars(), peek(peek(&token)).toChars());
5758 nextToken();
5760 else
5761 check(TOK.semicolon, "statement");
5762 s = new AST.ExpStatement(loc, exp);
5763 break;
5765 case TOK.static_:
5767 // Look ahead to see if it's static assert() or static if()
5768 const tv = peekNext();
5769 if (tv == TOK.assert_)
5771 s = new AST.StaticAssertStatement(parseStaticAssert());
5772 break;
5774 if (tv == TOK.if_)
5776 cond = parseStaticIfCondition();
5777 goto Lcondition;
5779 if (tv == TOK.foreach_ || tv == TOK.foreach_reverse_)
5781 s = parseForeach!(AST.StaticForeachStatement)(loc, null);
5782 if (flags & ParseStatementFlags.scope_)
5783 s = new AST.ScopeStatement(loc, s, token.loc);
5784 break;
5786 if (tv == TOK.import_)
5788 AST.Dsymbols* imports = parseImport();
5789 s = new AST.ImportStatement(loc, imports);
5790 if (flags & ParseStatementFlags.scope_)
5791 s = new AST.ScopeStatement(loc, s, token.loc);
5792 break;
5794 goto Ldeclaration;
5796 case TOK.final_:
5797 if (peekNext() == TOK.switch_)
5799 nextToken();
5800 isfinal = true;
5801 goto Lswitch;
5803 goto Ldeclaration;
5805 case TOK.wchar_:
5806 case TOK.dchar_:
5807 case TOK.bool_:
5808 case TOK.char_:
5809 case TOK.int8:
5810 case TOK.uns8:
5811 case TOK.int16:
5812 case TOK.uns16:
5813 case TOK.int32:
5814 case TOK.uns32:
5815 case TOK.int64:
5816 case TOK.uns64:
5817 case TOK.int128:
5818 case TOK.uns128:
5819 case TOK.float32:
5820 case TOK.float64:
5821 case TOK.float80:
5822 case TOK.imaginary32:
5823 case TOK.imaginary64:
5824 case TOK.imaginary80:
5825 case TOK.complex32:
5826 case TOK.complex64:
5827 case TOK.complex80:
5828 case TOK.void_:
5829 // bug 7773: int.max is always a part of expression
5830 if (peekNext() == TOK.dot)
5831 goto Lexp;
5832 if (peekNext() == TOK.leftParenthesis)
5833 goto Lexp;
5834 goto case;
5836 case TOK.alias_:
5837 case TOK.const_:
5838 case TOK.auto_:
5839 case TOK.abstract_:
5840 case TOK.extern_:
5841 case TOK.align_:
5842 case TOK.immutable_:
5843 case TOK.shared_:
5844 case TOK.inout_:
5845 case TOK.deprecated_:
5846 case TOK.nothrow_:
5847 case TOK.pure_:
5848 case TOK.ref_:
5849 case TOK.gshared:
5850 case TOK.at:
5851 case TOK.struct_:
5852 case TOK.union_:
5853 case TOK.class_:
5854 case TOK.interface_:
5855 Ldeclaration:
5857 AST.Dsymbols* a = parseDeclarations(false, null, null);
5858 if (a.dim > 1)
5860 auto as = new AST.Statements();
5861 as.reserve(a.dim);
5862 foreach (i; 0 .. a.dim)
5864 AST.Dsymbol d = (*a)[i];
5865 s = new AST.ExpStatement(loc, d);
5866 as.push(s);
5868 s = new AST.CompoundDeclarationStatement(loc, as);
5870 else if (a.dim == 1)
5872 AST.Dsymbol d = (*a)[0];
5873 s = new AST.ExpStatement(loc, d);
5875 else
5876 s = new AST.ExpStatement(loc, cast(AST.Expression)null);
5877 if (flags & ParseStatementFlags.scope_)
5878 s = new AST.ScopeStatement(loc, s, token.loc);
5879 break;
5881 case TOK.enum_:
5883 /* Determine if this is a manifest constant declaration,
5884 * or a conventional enum.
5886 AST.Dsymbol d;
5887 const tv = peekNext();
5888 if (tv == TOK.leftCurly || tv == TOK.colon)
5889 d = parseEnum();
5890 else if (tv != TOK.identifier)
5891 goto Ldeclaration;
5892 else
5894 const nextv = peekNext2();
5895 if (nextv == TOK.leftCurly || nextv == TOK.colon || nextv == TOK.semicolon)
5896 d = parseEnum();
5897 else
5898 goto Ldeclaration;
5900 s = new AST.ExpStatement(loc, d);
5901 if (flags & ParseStatementFlags.scope_)
5902 s = new AST.ScopeStatement(loc, s, token.loc);
5903 break;
5905 case TOK.mixin_:
5907 if (isDeclaration(&token, NeedDeclaratorId.mustIfDstyle, TOK.reserved, null))
5908 goto Ldeclaration;
5909 if (peekNext() == TOK.leftParenthesis)
5911 // mixin(string)
5912 AST.Expression e = parseAssignExp();
5913 check(TOK.semicolon);
5914 if (e.op == EXP.mixin_)
5916 AST.MixinExp cpe = cast(AST.MixinExp)e;
5917 s = new AST.CompileStatement(loc, cpe.exps);
5919 else
5921 s = new AST.ExpStatement(loc, e);
5923 break;
5925 AST.Dsymbol d = parseMixin();
5926 s = new AST.ExpStatement(loc, d);
5927 if (flags & ParseStatementFlags.scope_)
5928 s = new AST.ScopeStatement(loc, s, token.loc);
5929 break;
5931 case TOK.leftCurly:
5933 const lookingForElseSave = lookingForElse;
5934 lookingForElse = Loc.initial;
5936 nextToken();
5937 //if (token.value == TOK.semicolon)
5938 // error("use `{ }` for an empty statement, not `;`");
5939 auto statements = new AST.Statements();
5940 while (token.value != TOK.rightCurly && token.value != TOK.endOfFile)
5942 statements.push(parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope));
5944 if (endPtr)
5945 *endPtr = token.ptr;
5946 endloc = token.loc;
5947 if (pEndloc)
5949 *pEndloc = token.loc;
5950 pEndloc = null; // don't set it again
5952 s = new AST.CompoundStatement(loc, statements);
5953 if (flags & (ParseStatementFlags.scope_ | ParseStatementFlags.curlyScope))
5954 s = new AST.ScopeStatement(loc, s, token.loc);
5955 check(TOK.rightCurly, "compound statement");
5956 lookingForElse = lookingForElseSave;
5957 break;
5959 case TOK.while_:
5961 AST.Parameter param = null;
5962 nextToken();
5963 check(TOK.leftParenthesis);
5964 param = parseAssignCondition();
5965 AST.Expression condition = parseExpression();
5966 check(TOK.rightParenthesis);
5967 Loc endloc;
5968 AST.Statement _body = parseStatement(ParseStatementFlags.scope_, null, &endloc);
5969 s = new AST.WhileStatement(loc, condition, _body, endloc, param);
5970 break;
5972 case TOK.semicolon:
5973 if (!(flags & ParseStatementFlags.semiOk))
5975 if (flags & ParseStatementFlags.semi)
5976 deprecation("use `{ }` for an empty statement, not `;`");
5977 else
5978 error("use `{ }` for an empty statement, not `;`");
5980 nextToken();
5981 s = new AST.ExpStatement(loc, cast(AST.Expression)null);
5982 break;
5984 case TOK.do_:
5986 AST.Statement _body;
5987 AST.Expression condition;
5989 nextToken();
5990 const lookingForElseSave = lookingForElse;
5991 lookingForElse = Loc.initial;
5992 _body = parseStatement(ParseStatementFlags.scope_);
5993 lookingForElse = lookingForElseSave;
5994 check(TOK.while_);
5995 check(TOK.leftParenthesis);
5996 condition = parseExpression();
5997 check(TOK.rightParenthesis);
5998 if (token.value == TOK.semicolon)
5999 nextToken();
6000 else
6001 error("terminating `;` required after do-while statement");
6002 s = new AST.DoStatement(loc, _body, condition, token.loc);
6003 break;
6005 case TOK.for_:
6007 AST.Statement _init;
6008 AST.Expression condition;
6009 AST.Expression increment;
6011 nextToken();
6012 check(TOK.leftParenthesis);
6013 if (token.value == TOK.semicolon)
6015 _init = null;
6016 nextToken();
6018 else
6020 const lookingForElseSave = lookingForElse;
6021 lookingForElse = Loc.initial;
6022 _init = parseStatement(0);
6023 lookingForElse = lookingForElseSave;
6025 if (token.value == TOK.semicolon)
6027 condition = null;
6028 nextToken();
6030 else
6032 condition = parseExpression();
6033 check(TOK.semicolon, "`for` condition");
6035 if (token.value == TOK.rightParenthesis)
6037 increment = null;
6038 nextToken();
6040 else
6042 increment = parseExpression();
6043 check(TOK.rightParenthesis);
6045 Loc endloc;
6046 AST.Statement _body = parseStatement(ParseStatementFlags.scope_, null, &endloc);
6047 s = new AST.ForStatement(loc, _init, condition, increment, _body, endloc);
6048 break;
6050 case TOK.foreach_:
6051 case TOK.foreach_reverse_:
6053 s = parseForeach!(AST.Statement)(loc, null);
6054 break;
6056 case TOK.if_:
6058 AST.Parameter param = null;
6059 AST.Expression condition;
6061 nextToken();
6062 check(TOK.leftParenthesis);
6063 param = parseAssignCondition();
6064 condition = parseExpression();
6065 check(TOK.rightParenthesis);
6067 const lookingForElseSave = lookingForElse;
6068 lookingForElse = loc;
6069 ifbody = parseStatement(ParseStatementFlags.scope_);
6070 lookingForElse = lookingForElseSave;
6072 if (token.value == TOK.else_)
6074 const elseloc = token.loc;
6075 nextToken();
6076 elsebody = parseStatement(ParseStatementFlags.scope_);
6077 checkDanglingElse(elseloc);
6079 else
6080 elsebody = null;
6081 if (condition && ifbody)
6082 s = new AST.IfStatement(loc, param, condition, ifbody, elsebody, token.loc);
6083 else
6084 s = null; // don't propagate parsing errors
6085 break;
6088 case TOK.else_:
6089 error("found `else` without a corresponding `if`, `version` or `debug` statement");
6090 goto Lerror;
6092 case TOK.scope_:
6093 if (peekNext() != TOK.leftParenthesis)
6094 goto Ldeclaration; // scope used as storage class
6095 nextToken();
6096 check(TOK.leftParenthesis);
6097 if (token.value != TOK.identifier)
6099 error("scope identifier expected");
6100 goto Lerror;
6102 else
6104 TOK t = TOK.onScopeExit;
6105 Identifier id = token.ident;
6106 if (id == Id.exit)
6107 t = TOK.onScopeExit;
6108 else if (id == Id.failure)
6109 t = TOK.onScopeFailure;
6110 else if (id == Id.success)
6111 t = TOK.onScopeSuccess;
6112 else
6113 error("valid scope identifiers are `exit`, `failure`, or `success`, not `%s`", id.toChars());
6114 nextToken();
6115 check(TOK.rightParenthesis);
6116 AST.Statement st = parseStatement(ParseStatementFlags.scope_);
6117 s = new AST.ScopeGuardStatement(loc, t, st);
6118 break;
6121 case TOK.debug_:
6122 nextToken();
6123 if (token.value == TOK.assign)
6125 error("debug conditions can only be declared at module scope");
6126 nextToken();
6127 nextToken();
6128 goto Lerror;
6130 cond = parseDebugCondition();
6131 goto Lcondition;
6133 case TOK.version_:
6134 nextToken();
6135 if (token.value == TOK.assign)
6137 error("version conditions can only be declared at module scope");
6138 nextToken();
6139 nextToken();
6140 goto Lerror;
6142 cond = parseVersionCondition();
6143 goto Lcondition;
6145 Lcondition:
6147 const lookingForElseSave = lookingForElse;
6148 lookingForElse = loc;
6149 ifbody = parseStatement(0);
6150 lookingForElse = lookingForElseSave;
6152 elsebody = null;
6153 if (token.value == TOK.else_)
6155 const elseloc = token.loc;
6156 nextToken();
6157 elsebody = parseStatement(0);
6158 checkDanglingElse(elseloc);
6160 s = new AST.ConditionalStatement(loc, cond, ifbody, elsebody);
6161 if (flags & ParseStatementFlags.scope_)
6162 s = new AST.ScopeStatement(loc, s, token.loc);
6163 break;
6165 case TOK.pragma_:
6167 Identifier ident;
6168 AST.Expressions* args = null;
6169 AST.Statement _body;
6171 nextToken();
6172 check(TOK.leftParenthesis);
6173 if (token.value != TOK.identifier)
6175 error("`pragma(identifier)` expected");
6176 goto Lerror;
6178 ident = token.ident;
6179 nextToken();
6180 if (token.value == TOK.comma && peekNext() != TOK.rightParenthesis)
6181 args = parseArguments(); // pragma(identifier, args...);
6182 else
6183 check(TOK.rightParenthesis); // pragma(identifier);
6184 if (token.value == TOK.semicolon)
6186 nextToken();
6187 _body = null;
6189 else
6190 _body = parseStatement(ParseStatementFlags.semi);
6191 s = new AST.PragmaStatement(loc, ident, args, _body);
6192 break;
6194 case TOK.switch_:
6195 isfinal = false;
6196 goto Lswitch;
6198 Lswitch:
6200 nextToken();
6201 check(TOK.leftParenthesis);
6202 AST.Expression condition = parseExpression();
6203 check(TOK.rightParenthesis);
6204 AST.Statement _body = parseStatement(ParseStatementFlags.scope_);
6205 s = new AST.SwitchStatement(loc, condition, _body, isfinal);
6206 break;
6208 case TOK.case_:
6210 AST.Expression exp;
6211 AST.Expressions cases; // array of Expression's
6212 AST.Expression last = null;
6214 nextToken();
6217 exp = parseAssignExp();
6218 cases.push(exp);
6219 if (token.value != TOK.comma)
6220 break;
6221 nextToken(); //comma
6223 while (token.value != TOK.colon && token.value != TOK.endOfFile);
6224 check(TOK.colon);
6226 /* case exp: .. case last:
6228 if (token.value == TOK.slice)
6230 if (cases.dim > 1)
6231 error("only one `case` allowed for start of case range");
6232 nextToken();
6233 check(TOK.case_);
6234 last = parseAssignExp();
6235 check(TOK.colon);
6238 if (flags & ParseStatementFlags.curlyScope)
6240 auto statements = new AST.Statements();
6241 while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
6243 auto cur = parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope);
6244 statements.push(cur);
6246 // https://issues.dlang.org/show_bug.cgi?id=21739
6247 // Stop at the last break s.t. the following non-case statements are
6248 // not merged into the current case. This can happen for
6249 // case 1: ... break;
6250 // debug { case 2: ... }
6251 if (cur && cur.isBreakStatement())
6252 break;
6254 s = new AST.CompoundStatement(loc, statements);
6256 else
6258 s = parseStatement(ParseStatementFlags.semi);
6260 s = new AST.ScopeStatement(loc, s, token.loc);
6262 if (last)
6264 s = new AST.CaseRangeStatement(loc, exp, last, s);
6266 else
6268 // Keep cases in order by building the case statements backwards
6269 for (size_t i = cases.dim; i; i--)
6271 exp = cases[i - 1];
6272 s = new AST.CaseStatement(loc, exp, s);
6275 break;
6277 case TOK.default_:
6279 nextToken();
6280 check(TOK.colon);
6282 if (flags & ParseStatementFlags.curlyScope)
6284 auto statements = new AST.Statements();
6285 while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
6287 statements.push(parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope));
6289 s = new AST.CompoundStatement(loc, statements);
6291 else
6292 s = parseStatement(ParseStatementFlags.semi);
6293 s = new AST.ScopeStatement(loc, s, token.loc);
6294 s = new AST.DefaultStatement(loc, s);
6295 break;
6297 case TOK.return_:
6299 AST.Expression exp;
6300 nextToken();
6301 exp = token.value == TOK.semicolon ? null : parseExpression();
6302 check(TOK.semicolon, "`return` statement");
6303 s = new AST.ReturnStatement(loc, exp);
6304 break;
6306 case TOK.break_:
6308 Identifier ident;
6309 nextToken();
6310 ident = null;
6311 if (token.value == TOK.identifier)
6313 ident = token.ident;
6314 nextToken();
6316 check(TOK.semicolon, "`break` statement");
6317 s = new AST.BreakStatement(loc, ident);
6318 break;
6320 case TOK.continue_:
6322 Identifier ident;
6323 nextToken();
6324 ident = null;
6325 if (token.value == TOK.identifier)
6327 ident = token.ident;
6328 nextToken();
6330 check(TOK.semicolon, "`continue` statement");
6331 s = new AST.ContinueStatement(loc, ident);
6332 break;
6334 case TOK.goto_:
6336 Identifier ident;
6337 nextToken();
6338 if (token.value == TOK.default_)
6340 nextToken();
6341 s = new AST.GotoDefaultStatement(loc);
6343 else if (token.value == TOK.case_)
6345 AST.Expression exp = null;
6346 nextToken();
6347 if (token.value != TOK.semicolon)
6348 exp = parseExpression();
6349 s = new AST.GotoCaseStatement(loc, exp);
6351 else
6353 if (token.value != TOK.identifier)
6355 error("identifier expected following `goto`");
6356 ident = null;
6358 else
6360 ident = token.ident;
6361 nextToken();
6363 s = new AST.GotoStatement(loc, ident);
6365 check(TOK.semicolon, "`goto` statement");
6366 break;
6368 case TOK.synchronized_:
6370 AST.Expression exp;
6371 AST.Statement _body;
6373 Token* t = peek(&token);
6374 if (skipAttributes(t, &t) && t.value == TOK.class_)
6375 goto Ldeclaration;
6377 nextToken();
6378 if (token.value == TOK.leftParenthesis)
6380 nextToken();
6381 exp = parseExpression();
6382 check(TOK.rightParenthesis);
6384 else
6385 exp = null;
6386 _body = parseStatement(ParseStatementFlags.scope_);
6387 s = new AST.SynchronizedStatement(loc, exp, _body);
6388 break;
6390 case TOK.with_:
6392 AST.Expression exp;
6393 AST.Statement _body;
6394 Loc endloc = loc;
6396 nextToken();
6397 check(TOK.leftParenthesis);
6398 exp = parseExpression();
6399 check(TOK.rightParenthesis);
6400 _body = parseStatement(ParseStatementFlags.scope_, null, &endloc);
6401 s = new AST.WithStatement(loc, exp, _body, endloc);
6402 break;
6404 case TOK.try_:
6406 AST.Statement _body;
6407 AST.Catches* catches = null;
6408 AST.Statement finalbody = null;
6410 nextToken();
6411 const lookingForElseSave = lookingForElse;
6412 lookingForElse = Loc.initial;
6413 _body = parseStatement(ParseStatementFlags.scope_);
6414 lookingForElse = lookingForElseSave;
6415 while (token.value == TOK.catch_)
6417 AST.Statement handler;
6418 AST.Catch c;
6419 AST.Type t;
6420 Identifier id;
6421 const catchloc = token.loc;
6423 nextToken();
6424 if (token.value != TOK.leftParenthesis)
6426 deprecation("`catch` statement without an exception specification is deprecated");
6427 deprecationSupplemental(token.loc, "use `catch(Throwable)` for old behavior");
6428 t = null;
6429 id = null;
6431 else
6433 check(TOK.leftParenthesis);
6434 id = null;
6435 t = parseType(&id);
6436 check(TOK.rightParenthesis);
6438 handler = parseStatement(0);
6439 c = new AST.Catch(catchloc, t, id, handler);
6440 if (!catches)
6441 catches = new AST.Catches();
6442 catches.push(c);
6445 if (token.value == TOK.finally_)
6447 nextToken();
6448 finalbody = parseStatement(ParseStatementFlags.scope_);
6451 s = _body;
6452 if (!catches && !finalbody)
6453 error("`catch` or `finally` expected following `try`");
6454 else
6456 if (catches)
6457 s = new AST.TryCatchStatement(loc, _body, catches);
6458 if (finalbody)
6459 s = new AST.TryFinallyStatement(loc, s, finalbody);
6461 break;
6463 case TOK.throw_:
6465 AST.Expression exp;
6466 nextToken();
6467 exp = parseExpression();
6468 check(TOK.semicolon, "`throw` statement");
6469 s = new AST.ThrowStatement(loc, exp);
6470 break;
6473 case TOK.asm_:
6474 s = parseAsm();
6475 break;
6477 case TOK.import_:
6479 /* https://issues.dlang.org/show_bug.cgi?id=16088
6481 * At this point it can either be an
6482 * https://dlang.org/spec/grammar.html#ImportExpression
6483 * or an
6484 * https://dlang.org/spec/grammar.html#ImportDeclaration.
6485 * See if the next token after `import` is a `(`; if so,
6486 * then it is an import expression.
6488 if (peekNext() == TOK.leftParenthesis)
6490 AST.Expression e = parseExpression();
6491 check(TOK.semicolon);
6492 s = new AST.ExpStatement(loc, e);
6494 else
6496 AST.Dsymbols* imports = parseImport();
6497 s = new AST.ImportStatement(loc, imports);
6498 if (flags & ParseStatementFlags.scope_)
6499 s = new AST.ScopeStatement(loc, s, token.loc);
6501 break;
6503 case TOK.template_:
6505 AST.Dsymbol d = parseTemplateDeclaration();
6506 s = new AST.ExpStatement(loc, d);
6507 break;
6509 default:
6510 error("found `%s` instead of statement", token.toChars());
6511 goto Lerror;
6513 Lerror:
6514 while (token.value != TOK.rightCurly && token.value != TOK.semicolon && token.value != TOK.endOfFile)
6515 nextToken();
6516 if (token.value == TOK.semicolon)
6517 nextToken();
6518 s = null;
6519 break;
6521 if (pEndloc)
6522 *pEndloc = prevloc;
6523 return s;
6527 private AST.ExpInitializer parseExpInitializer(Loc loc)
6529 auto ae = parseAssignExp();
6530 return new AST.ExpInitializer(loc, ae);
6533 private AST.Initializer parseStructInitializer(Loc loc)
6535 /* Scan ahead to discern between a struct initializer and
6536 * parameterless function literal.
6538 * We'll scan the topmost curly bracket level for statement-related
6539 * tokens, thereby ruling out a struct initializer. (A struct
6540 * initializer which itself contains function literals may have
6541 * statements at nested curly bracket levels.)
6543 * It's important that this function literal check not be
6544 * pendantic, otherwise a function having the slightest syntax
6545 * error would emit confusing errors when we proceed to parse it
6546 * as a struct initializer.
6548 * The following two ambiguous cases will be treated as a struct
6549 * initializer (best we can do without type info):
6550 * {}
6551 * {{statements...}} - i.e. it could be struct initializer
6552 * with one function literal, or function literal having an
6553 * extra level of curly brackets
6554 * If a function literal is intended in these cases (unlikely),
6555 * source can use a more explicit function literal syntax
6556 * (e.g. prefix with "()" for empty parameter list).
6558 int braces = 1;
6559 int parens = 0;
6560 for (auto t = peek(&token); 1; t = peek(t))
6562 switch (t.value)
6564 case TOK.leftParenthesis:
6565 parens++;
6566 continue;
6567 case TOK.rightParenthesis:
6568 parens--;
6569 continue;
6570 // https://issues.dlang.org/show_bug.cgi?id=21163
6571 // lambda params can have the `scope` storage class, e.g
6572 // `S s = { (scope Type Id){} }`
6573 case TOK.scope_:
6574 if (!parens) goto case;
6575 continue;
6576 /* Look for a semicolon or keyword of statements which don't
6577 * require a semicolon (typically containing BlockStatement).
6578 * Tokens like "else", "catch", etc. are omitted where the
6579 * leading token of the statement is sufficient.
6581 case TOK.asm_:
6582 case TOK.class_:
6583 case TOK.debug_:
6584 case TOK.enum_:
6585 case TOK.if_:
6586 case TOK.interface_:
6587 case TOK.pragma_:
6588 case TOK.semicolon:
6589 case TOK.struct_:
6590 case TOK.switch_:
6591 case TOK.synchronized_:
6592 case TOK.try_:
6593 case TOK.union_:
6594 case TOK.version_:
6595 case TOK.while_:
6596 case TOK.with_:
6597 if (braces == 1)
6598 return parseExpInitializer(loc);
6599 continue;
6601 case TOK.leftCurly:
6602 braces++;
6603 continue;
6605 case TOK.rightCurly:
6606 if (--braces == 0)
6607 break;
6608 continue;
6610 case TOK.endOfFile:
6611 break;
6613 default:
6614 continue;
6616 break;
6619 auto _is = new AST.StructInitializer(loc);
6620 bool commaExpected = false;
6621 nextToken();
6622 while (1)
6624 switch (token.value)
6626 case TOK.identifier:
6629 if (commaExpected)
6630 error("comma expected separating field initializers");
6631 const t = peek(&token);
6632 Identifier id;
6633 if (t.value == TOK.colon)
6635 id = token.ident;
6636 nextToken();
6637 nextToken(); // skip over ':'
6639 auto value = parseInitializer();
6640 _is.addInit(id, value);
6641 commaExpected = true;
6642 continue;
6644 case TOK.comma:
6645 if (!commaExpected)
6646 error("expression expected, not `,`");
6647 nextToken();
6648 commaExpected = false;
6649 continue;
6651 case TOK.rightCurly: // allow trailing comma's
6652 nextToken();
6653 break;
6655 case TOK.endOfFile:
6656 error("found end of file instead of initializer");
6657 break;
6659 default:
6660 if (commaExpected)
6661 error("comma expected separating field initializers");
6662 auto value = parseInitializer();
6663 _is.addInit(null, value);
6664 commaExpected = true;
6665 continue;
6667 break;
6669 return _is;
6673 /*****************************************
6674 * Parse initializer for variable declaration.
6676 private AST.Initializer parseInitializer()
6678 const loc = token.loc;
6680 switch (token.value)
6682 case TOK.leftCurly:
6683 return parseStructInitializer(loc);
6685 case TOK.leftBracket:
6686 /* Scan ahead to see if it is an array initializer or
6687 * an expression.
6688 * If it ends with a ';' ',' or '}', it is an array initializer.
6690 int brackets = 1;
6691 for (auto t = peek(&token); 1; t = peek(t))
6693 switch (t.value)
6695 case TOK.leftBracket:
6696 brackets++;
6697 continue;
6699 case TOK.rightBracket:
6700 if (--brackets == 0)
6702 t = peek(t);
6703 if (t.value != TOK.semicolon && t.value != TOK.comma && t.value != TOK.rightBracket && t.value != TOK.rightCurly)
6704 return parseExpInitializer(loc);
6705 break;
6707 continue;
6709 case TOK.endOfFile:
6710 break;
6712 default:
6713 continue;
6715 break;
6718 auto ia = new AST.ArrayInitializer(loc);
6719 bool commaExpected = false;
6721 nextToken();
6722 while (1)
6724 switch (token.value)
6726 default:
6727 if (commaExpected)
6729 error("comma expected separating array initializers, not `%s`", token.toChars());
6730 nextToken();
6731 break;
6733 auto e = parseAssignExp();
6734 if (!e)
6735 break;
6737 AST.Initializer value;
6738 if (token.value == TOK.colon)
6740 nextToken();
6741 value = parseInitializer();
6743 else
6745 value = new AST.ExpInitializer(e.loc, e);
6746 e = null;
6748 ia.addInit(e, value);
6749 commaExpected = true;
6750 continue;
6752 case TOK.leftCurly:
6753 case TOK.leftBracket:
6754 if (commaExpected)
6755 error("comma expected separating array initializers, not `%s`", token.toChars());
6756 auto value = parseInitializer();
6757 AST.Expression e;
6759 if (token.value == TOK.colon)
6761 nextToken();
6762 if (auto ei = value.isExpInitializer())
6764 e = ei.exp;
6765 value = parseInitializer();
6767 else
6768 error("initializer expression expected following colon, not `%s`", token.toChars());
6770 ia.addInit(e, value);
6771 commaExpected = true;
6772 continue;
6774 case TOK.comma:
6775 if (!commaExpected)
6776 error("expression expected, not `,`");
6777 nextToken();
6778 commaExpected = false;
6779 continue;
6781 case TOK.rightBracket: // allow trailing comma's
6782 nextToken();
6783 break;
6785 case TOK.endOfFile:
6786 error("found `%s` instead of array initializer", token.toChars());
6787 break;
6789 break;
6791 return ia;
6793 case TOK.void_:
6794 const tv = peekNext();
6795 if (tv == TOK.semicolon || tv == TOK.comma)
6797 nextToken();
6798 return new AST.VoidInitializer(loc);
6800 return parseExpInitializer(loc);
6802 default:
6803 return parseExpInitializer(loc);
6807 /*****************************************
6808 * Parses default argument initializer expression that is an assign expression,
6809 * with special handling for __FILE__, __FILE_DIR__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__.
6811 private AST.Expression parseDefaultInitExp()
6813 AST.Expression e = null;
6814 const tv = peekNext();
6815 if (tv == TOK.comma || tv == TOK.rightParenthesis)
6817 switch (token.value)
6819 case TOK.file: e = new AST.FileInitExp(token.loc, EXP.file); break;
6820 case TOK.fileFullPath: e = new AST.FileInitExp(token.loc, EXP.fileFullPath); break;
6821 case TOK.line: e = new AST.LineInitExp(token.loc); break;
6822 case TOK.moduleString: e = new AST.ModuleInitExp(token.loc); break;
6823 case TOK.functionString: e = new AST.FuncInitExp(token.loc); break;
6824 case TOK.prettyFunction: e = new AST.PrettyFuncInitExp(token.loc); break;
6825 default: goto LExp;
6827 nextToken();
6828 return e;
6830 LExp:
6831 return parseAssignExp();
6834 /********************
6835 * Parse inline assembler block.
6836 * Returns:
6837 * inline assembler block as a Statement
6839 AST.Statement parseAsm()
6841 // Parse the asm block into a sequence of AsmStatements,
6842 // each AsmStatement is one instruction.
6843 // Separate out labels.
6844 // Defer parsing of AsmStatements until semantic processing.
6846 const loc = token.loc;
6847 Loc labelloc;
6849 nextToken();
6850 StorageClass stc = parsePostfix(STC.undefined_, null);
6851 if (stc & (STC.const_ | STC.immutable_ | STC.shared_ | STC.wild))
6852 error("`const`/`immutable`/`shared`/`inout` attributes are not allowed on `asm` blocks");
6854 check(TOK.leftCurly);
6855 Token* toklist = null;
6856 Token** ptoklist = &toklist;
6857 Identifier label = null;
6858 auto statements = new AST.Statements();
6859 size_t nestlevel = 0;
6860 while (1)
6862 switch (token.value)
6864 case TOK.identifier:
6865 if (!toklist)
6867 // Look ahead to see if it is a label
6868 if (peekNext() == TOK.colon)
6870 // It's a label
6871 label = token.ident;
6872 labelloc = token.loc;
6873 nextToken();
6874 nextToken();
6875 continue;
6878 goto default;
6880 case TOK.leftCurly:
6881 ++nestlevel;
6882 goto default;
6884 case TOK.rightCurly:
6885 if (nestlevel > 0)
6887 --nestlevel;
6888 goto default;
6890 if (toklist || label)
6892 error("`asm` statements must end in `;`");
6894 break;
6896 case TOK.semicolon:
6897 if (nestlevel != 0)
6898 error("mismatched number of curly brackets");
6900 if (toklist || label)
6902 // Create AsmStatement from list of tokens we've saved
6903 AST.Statement s = new AST.AsmStatement(token.loc, toklist);
6904 toklist = null;
6905 ptoklist = &toklist;
6906 if (label)
6908 s = new AST.LabelStatement(labelloc, label, s);
6909 label = null;
6911 statements.push(s);
6913 nextToken();
6914 continue;
6916 case TOK.endOfFile:
6917 /* { */
6918 error("matching `}` expected, not end of file");
6919 break;
6921 case TOK.colonColon: // treat as two separate : tokens for iasmgcc
6922 *ptoklist = allocateToken();
6923 memcpy(*ptoklist, &token, Token.sizeof);
6924 (*ptoklist).value = TOK.colon;
6925 ptoklist = &(*ptoklist).next;
6927 *ptoklist = allocateToken();
6928 memcpy(*ptoklist, &token, Token.sizeof);
6929 (*ptoklist).value = TOK.colon;
6930 ptoklist = &(*ptoklist).next;
6932 *ptoklist = null;
6933 nextToken();
6934 continue;
6936 default:
6937 *ptoklist = allocateToken();
6938 memcpy(*ptoklist, &token, Token.sizeof);
6939 ptoklist = &(*ptoklist).next;
6940 *ptoklist = null;
6941 nextToken();
6942 continue;
6944 break;
6946 nextToken();
6947 auto s = new AST.CompoundAsmStatement(loc, statements, stc);
6948 return s;
6951 /**********************************
6952 * Issue error if the current token is not `value`,
6953 * advance to next token.
6954 * Params:
6955 * loc = location for error message
6956 * value = token value to compare with
6958 void check(Loc loc, TOK value)
6960 if (token.value != value)
6961 error(loc, "found `%s` when expecting `%s`", token.toChars(), Token.toChars(value));
6962 nextToken();
6965 /**********************************
6966 * Issue error if the current token is not `value`,
6967 * advance to next token.
6968 * Params:
6969 * value = token value to compare with
6971 void check(TOK value)
6973 check(token.loc, value);
6976 /**********************************
6977 * Issue error if the current token is not `value`,
6978 * advance to next token.
6979 * Params:
6980 * value = token value to compare with
6981 * string = for error message
6983 void check(TOK value, const(char)* string)
6985 if (token.value != value)
6986 error("found `%s` when expecting `%s` following %s", token.toChars(), Token.toChars(value), string);
6987 nextToken();
6990 private void checkParens(TOK value, AST.Expression e)
6992 if (precedence[e.op] == PREC.rel && !e.parens)
6993 error(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`", e.toChars(), Token.toChars(value));
6997 enum NeedDeclaratorId
6999 no, // Declarator part must have no identifier
7000 opt, // Declarator part identifier is optional
7001 must, // Declarator part must have identifier
7002 mustIfDstyle, // Declarator part must have identifier, but don't recognize old C-style syntax
7005 /************************************
7006 * Determine if the scanner is sitting on the start of a declaration.
7007 * Params:
7008 * t = current token of the scanner
7009 * needId = flag with additional requirements for a declaration
7010 * endtok = ending token
7011 * pt = will be set ending token (if not null)
7012 * Output:
7013 * true if the token `t` is a declaration, false otherwise
7015 private bool isDeclaration(Token* t, NeedDeclaratorId needId, TOK endtok, Token** pt)
7017 //printf("isDeclaration(needId = %d)\n", needId);
7018 int haveId = 0;
7019 int haveTpl = 0;
7021 while (1)
7023 if ((t.value == TOK.const_ || t.value == TOK.immutable_ || t.value == TOK.inout_ || t.value == TOK.shared_) && peek(t).value != TOK.leftParenthesis)
7025 /* const type
7026 * immutable type
7027 * shared type
7028 * wild type
7030 t = peek(t);
7031 continue;
7033 break;
7036 if (!isBasicType(&t))
7038 goto Lisnot;
7040 if (!isDeclarator(&t, &haveId, &haveTpl, endtok, needId != NeedDeclaratorId.mustIfDstyle))
7041 goto Lisnot;
7042 if ((needId == NeedDeclaratorId.no && !haveId) ||
7043 (needId == NeedDeclaratorId.opt) ||
7044 (needId == NeedDeclaratorId.must && haveId) ||
7045 (needId == NeedDeclaratorId.mustIfDstyle && haveId))
7047 if (pt)
7048 *pt = t;
7049 goto Lis;
7051 goto Lisnot;
7053 Lis:
7054 //printf("\tis declaration, t = %s\n", t.toChars());
7055 return true;
7057 Lisnot:
7058 //printf("\tis not declaration\n");
7059 return false;
7062 private bool isBasicType(Token** pt)
7064 // This code parallels parseBasicType()
7065 Token* t = *pt;
7066 switch (t.value)
7068 case TOK.wchar_:
7069 case TOK.dchar_:
7070 case TOK.bool_:
7071 case TOK.char_:
7072 case TOK.int8:
7073 case TOK.uns8:
7074 case TOK.int16:
7075 case TOK.uns16:
7076 case TOK.int32:
7077 case TOK.uns32:
7078 case TOK.int64:
7079 case TOK.uns64:
7080 case TOK.int128:
7081 case TOK.uns128:
7082 case TOK.float32:
7083 case TOK.float64:
7084 case TOK.float80:
7085 case TOK.imaginary32:
7086 case TOK.imaginary64:
7087 case TOK.imaginary80:
7088 case TOK.complex32:
7089 case TOK.complex64:
7090 case TOK.complex80:
7091 case TOK.void_:
7092 t = peek(t);
7093 break;
7095 case TOK.identifier:
7097 t = peek(t);
7098 if (t.value == TOK.not)
7100 goto L4;
7102 goto L3;
7103 while (1)
7106 t = peek(t);
7108 if (t.value == TOK.dot)
7110 Ldot:
7111 t = peek(t);
7112 if (t.value != TOK.identifier)
7113 goto Lfalse;
7114 t = peek(t);
7115 if (t.value != TOK.not)
7116 goto L3;
7118 /* Seen a !
7119 * Look for:
7120 * !( args ), !identifier, etc.
7122 t = peek(t);
7123 switch (t.value)
7125 case TOK.identifier:
7126 goto L5;
7128 case TOK.leftParenthesis:
7129 if (!skipParens(t, &t))
7130 goto Lfalse;
7131 goto L3;
7133 case TOK.wchar_:
7134 case TOK.dchar_:
7135 case TOK.bool_:
7136 case TOK.char_:
7137 case TOK.int8:
7138 case TOK.uns8:
7139 case TOK.int16:
7140 case TOK.uns16:
7141 case TOK.int32:
7142 case TOK.uns32:
7143 case TOK.int64:
7144 case TOK.uns64:
7145 case TOK.int128:
7146 case TOK.uns128:
7147 case TOK.float32:
7148 case TOK.float64:
7149 case TOK.float80:
7150 case TOK.imaginary32:
7151 case TOK.imaginary64:
7152 case TOK.imaginary80:
7153 case TOK.complex32:
7154 case TOK.complex64:
7155 case TOK.complex80:
7156 case TOK.void_:
7157 case TOK.int32Literal:
7158 case TOK.uns32Literal:
7159 case TOK.int64Literal:
7160 case TOK.uns64Literal:
7161 case TOK.int128Literal:
7162 case TOK.uns128Literal:
7163 case TOK.float32Literal:
7164 case TOK.float64Literal:
7165 case TOK.float80Literal:
7166 case TOK.imaginary32Literal:
7167 case TOK.imaginary64Literal:
7168 case TOK.imaginary80Literal:
7169 case TOK.null_:
7170 case TOK.true_:
7171 case TOK.false_:
7172 case TOK.charLiteral:
7173 case TOK.wcharLiteral:
7174 case TOK.dcharLiteral:
7175 case TOK.string_:
7176 case TOK.hexadecimalString:
7177 case TOK.file:
7178 case TOK.fileFullPath:
7179 case TOK.line:
7180 case TOK.moduleString:
7181 case TOK.functionString:
7182 case TOK.prettyFunction:
7183 goto L2;
7185 default:
7186 goto Lfalse;
7189 break;
7191 break;
7193 case TOK.dot:
7194 goto Ldot;
7196 case TOK.typeof_:
7197 case TOK.vector:
7198 case TOK.mixin_:
7199 /* typeof(exp).identifier...
7201 t = peek(t);
7202 if (!skipParens(t, &t))
7203 goto Lfalse;
7204 goto L3;
7206 case TOK.traits:
7207 // __traits(getMember
7208 t = peek(t);
7209 if (t.value != TOK.leftParenthesis)
7210 goto Lfalse;
7211 auto lp = t;
7212 t = peek(t);
7213 if (t.value != TOK.identifier || t.ident != Id.getMember)
7214 goto Lfalse;
7215 if (!skipParens(lp, &lp))
7216 goto Lfalse;
7217 // we are in a lookup for decl VS statement
7218 // so we expect a declarator following __trait if it's a type.
7219 // other usages wont be ambiguous (alias, template instance, type qual, etc.)
7220 if (lp.value != TOK.identifier)
7221 goto Lfalse;
7223 break;
7225 case TOK.const_:
7226 case TOK.immutable_:
7227 case TOK.shared_:
7228 case TOK.inout_:
7229 // const(type) or immutable(type) or shared(type) or wild(type)
7230 t = peek(t);
7231 if (t.value != TOK.leftParenthesis)
7232 goto Lfalse;
7233 t = peek(t);
7234 if (!isDeclaration(t, NeedDeclaratorId.no, TOK.rightParenthesis, &t))
7236 goto Lfalse;
7238 t = peek(t);
7239 break;
7241 default:
7242 goto Lfalse;
7244 *pt = t;
7245 //printf("is\n");
7246 return true;
7248 Lfalse:
7249 //printf("is not\n");
7250 return false;
7253 private bool isDeclarator(Token** pt, int* haveId, int* haveTpl, TOK endtok, bool allowAltSyntax = true)
7255 // This code parallels parseDeclarator()
7256 Token* t = *pt;
7257 int parens;
7259 //printf("Parser::isDeclarator() %s\n", t.toChars());
7260 if (t.value == TOK.assign)
7261 return false;
7263 while (1)
7265 parens = false;
7266 switch (t.value)
7268 case TOK.mul:
7269 //case TOK.and:
7270 t = peek(t);
7271 continue;
7273 case TOK.leftBracket:
7274 t = peek(t);
7275 if (t.value == TOK.rightBracket)
7277 t = peek(t);
7279 else if (isDeclaration(t, NeedDeclaratorId.no, TOK.rightBracket, &t))
7281 // It's an associative array declaration
7282 t = peek(t);
7284 // ...[type].ident
7285 if (t.value == TOK.dot && peek(t).value == TOK.identifier)
7287 t = peek(t);
7288 t = peek(t);
7291 else
7293 // [ expression ]
7294 // [ expression .. expression ]
7295 if (!isExpression(&t))
7296 return false;
7297 if (t.value == TOK.slice)
7299 t = peek(t);
7300 if (!isExpression(&t))
7301 return false;
7302 if (t.value != TOK.rightBracket)
7303 return false;
7304 t = peek(t);
7306 else
7308 if (t.value != TOK.rightBracket)
7309 return false;
7310 t = peek(t);
7311 // ...[index].ident
7312 if (t.value == TOK.dot && peek(t).value == TOK.identifier)
7314 t = peek(t);
7315 t = peek(t);
7319 continue;
7321 case TOK.identifier:
7322 if (*haveId)
7323 return false;
7324 *haveId = true;
7325 t = peek(t);
7326 break;
7328 case TOK.leftParenthesis:
7329 if (!allowAltSyntax)
7330 return false; // Do not recognize C-style declarations.
7332 t = peek(t);
7333 if (t.value == TOK.rightParenthesis)
7334 return false; // () is not a declarator
7336 /* Regard ( identifier ) as not a declarator
7337 * BUG: what about ( *identifier ) in
7338 * f(*p)(x);
7339 * where f is a class instance with overloaded () ?
7340 * Should we just disallow C-style function pointer declarations?
7342 if (t.value == TOK.identifier)
7344 Token* t2 = peek(t);
7345 if (t2.value == TOK.rightParenthesis)
7346 return false;
7349 if (!isDeclarator(&t, haveId, null, TOK.rightParenthesis))
7350 return false;
7351 t = peek(t);
7352 parens = true;
7353 break;
7355 case TOK.delegate_:
7356 case TOK.function_:
7357 t = peek(t);
7358 if (!isParameters(&t))
7359 return false;
7360 skipAttributes(t, &t);
7361 continue;
7363 default:
7364 break;
7366 break;
7369 while (1)
7371 switch (t.value)
7373 static if (CARRAYDECL)
7375 case TOK.leftBracket:
7376 parens = false;
7377 t = peek(t);
7378 if (t.value == TOK.rightBracket)
7380 t = peek(t);
7382 else if (isDeclaration(t, NeedDeclaratorId.no, TOK.rightBracket, &t))
7384 // It's an associative array declaration
7385 t = peek(t);
7387 else
7389 // [ expression ]
7390 if (!isExpression(&t))
7391 return false;
7392 if (t.value != TOK.rightBracket)
7393 return false;
7394 t = peek(t);
7396 continue;
7399 case TOK.leftParenthesis:
7400 parens = false;
7401 if (Token* tk = peekPastParen(t))
7403 if (tk.value == TOK.leftParenthesis)
7405 if (!haveTpl)
7406 return false;
7407 *haveTpl = 1;
7408 t = tk;
7410 else if (tk.value == TOK.assign)
7412 if (!haveTpl)
7413 return false;
7414 *haveTpl = 1;
7415 *pt = tk;
7416 return true;
7419 if (!isParameters(&t))
7420 return false;
7421 while (1)
7423 switch (t.value)
7425 case TOK.const_:
7426 case TOK.immutable_:
7427 case TOK.shared_:
7428 case TOK.inout_:
7429 case TOK.pure_:
7430 case TOK.nothrow_:
7431 case TOK.return_:
7432 case TOK.scope_:
7433 t = peek(t);
7434 continue;
7436 case TOK.at:
7437 t = peek(t); // skip '@'
7438 t = peek(t); // skip identifier
7439 continue;
7441 default:
7442 break;
7444 break;
7446 continue;
7448 // Valid tokens that follow a declaration
7449 case TOK.rightParenthesis:
7450 case TOK.rightBracket:
7451 case TOK.assign:
7452 case TOK.comma:
7453 case TOK.dotDotDot:
7454 case TOK.semicolon:
7455 case TOK.leftCurly:
7456 case TOK.in_:
7457 case TOK.out_:
7458 case TOK.do_:
7459 // The !parens is to disallow unnecessary parentheses
7460 if (!parens && (endtok == TOK.reserved || endtok == t.value))
7462 *pt = t;
7463 return true;
7465 return false;
7467 case TOK.identifier:
7468 if (t.ident == Id._body)
7470 // @@@DEPRECATED@@@
7471 // https://github.com/dlang/DIPs/blob/1f5959abe482b1f9094f6484a7d0a3ade77fc2fc/DIPs/accepted/DIP1003.md
7472 // Deprecated in 2.097 - Can be removed from 2.117
7473 // The deprecation period is longer than usual as `body`
7474 // was quite widely used.
7475 deprecation("Usage of the `body` keyword is deprecated. Use `do` instead.");
7476 goto case TOK.do_;
7478 goto default;
7480 case TOK.if_:
7481 return haveTpl ? true : false;
7483 // Used for mixin type parsing
7484 case TOK.endOfFile:
7485 if (endtok == TOK.endOfFile)
7486 goto case TOK.do_;
7487 return false;
7489 default:
7490 return false;
7493 assert(0);
7496 private bool isParameters(Token** pt)
7498 // This code parallels parseParameterList()
7499 Token* t = *pt;
7501 //printf("isParameters()\n");
7502 if (t.value != TOK.leftParenthesis)
7503 return false;
7505 t = peek(t);
7506 for (; 1; t = peek(t))
7509 switch (t.value)
7511 case TOK.rightParenthesis:
7512 break;
7514 case TOK.at:
7515 Token* pastAttr;
7516 if (skipAttributes(t, &pastAttr))
7518 t = pastAttr;
7519 goto default;
7521 break;
7523 case TOK.dotDotDot:
7524 t = peek(t);
7525 break;
7527 case TOK.in_:
7528 case TOK.out_:
7529 case TOK.ref_:
7530 case TOK.lazy_:
7531 case TOK.scope_:
7532 case TOK.final_:
7533 case TOK.auto_:
7534 case TOK.return_:
7535 continue;
7537 case TOK.const_:
7538 case TOK.immutable_:
7539 case TOK.shared_:
7540 case TOK.inout_:
7541 t = peek(t);
7542 if (t.value == TOK.leftParenthesis)
7544 t = peek(t);
7545 if (!isDeclaration(t, NeedDeclaratorId.no, TOK.rightParenthesis, &t))
7546 return false;
7547 t = peek(t); // skip past closing ')'
7548 goto L2;
7550 goto L1;
7552 version (none)
7554 case TOK.static_:
7555 continue;
7556 case TOK.auto_:
7557 case TOK.alias_:
7558 t = peek(t);
7559 if (t.value == TOK.identifier)
7560 t = peek(t);
7561 if (t.value == TOK.assign)
7563 t = peek(t);
7564 if (!isExpression(&t))
7565 return false;
7567 goto L3;
7570 default:
7572 if (!isBasicType(&t))
7573 return false;
7575 int tmp = false;
7576 if (t.value != TOK.dotDotDot && !isDeclarator(&t, &tmp, null, TOK.reserved))
7577 return false;
7578 if (t.value == TOK.assign)
7580 t = peek(t);
7581 if (!isExpression(&t))
7582 return false;
7584 if (t.value == TOK.dotDotDot)
7586 t = peek(t);
7587 break;
7590 if (t.value == TOK.comma)
7592 continue;
7594 break;
7596 break;
7598 if (t.value != TOK.rightParenthesis)
7599 return false;
7600 t = peek(t);
7601 *pt = t;
7602 return true;
7605 private bool isExpression(Token** pt)
7607 // This is supposed to determine if something is an expression.
7608 // What it actually does is scan until a closing right bracket
7609 // is found.
7611 Token* t = *pt;
7612 int brnest = 0;
7613 int panest = 0;
7614 int curlynest = 0;
7616 for (;; t = peek(t))
7618 switch (t.value)
7620 case TOK.leftBracket:
7621 brnest++;
7622 continue;
7624 case TOK.rightBracket:
7625 if (--brnest >= 0)
7626 continue;
7627 break;
7629 case TOK.leftParenthesis:
7630 panest++;
7631 continue;
7633 case TOK.comma:
7634 if (brnest || panest)
7635 continue;
7636 break;
7638 case TOK.rightParenthesis:
7639 if (--panest >= 0)
7640 continue;
7641 break;
7643 case TOK.leftCurly:
7644 curlynest++;
7645 continue;
7647 case TOK.rightCurly:
7648 if (--curlynest >= 0)
7649 continue;
7650 return false;
7652 case TOK.slice:
7653 if (brnest)
7654 continue;
7655 break;
7657 case TOK.semicolon:
7658 if (curlynest)
7659 continue;
7660 return false;
7662 case TOK.endOfFile:
7663 return false;
7665 default:
7666 continue;
7668 break;
7671 *pt = t;
7672 return true;
7675 /*******************************************
7676 * Skip parentheses.
7677 * Params:
7678 * t = on opening $(LPAREN)
7679 * pt = *pt is set to token past '$(RPAREN)' on true
7680 * Returns:
7681 * true successful
7682 * false some parsing error
7684 bool skipParens(Token* t, Token** pt)
7686 if (t.value != TOK.leftParenthesis)
7687 return false;
7689 int parens = 0;
7691 while (1)
7693 switch (t.value)
7695 case TOK.leftParenthesis:
7696 parens++;
7697 break;
7699 case TOK.rightParenthesis:
7700 parens--;
7701 if (parens < 0)
7702 goto Lfalse;
7703 if (parens == 0)
7704 goto Ldone;
7705 break;
7707 case TOK.endOfFile:
7708 goto Lfalse;
7710 default:
7711 break;
7713 t = peek(t);
7715 Ldone:
7716 if (pt)
7717 *pt = peek(t); // skip found rparen
7718 return true;
7720 Lfalse:
7721 return false;
7724 private bool skipParensIf(Token* t, Token** pt)
7726 if (t.value != TOK.leftParenthesis)
7728 if (pt)
7729 *pt = t;
7730 return true;
7732 return skipParens(t, pt);
7735 //returns true if the next value (after optional matching parens) is expected
7736 private bool hasOptionalParensThen(Token* t, TOK expected)
7738 Token* tk;
7739 if (!skipParensIf(t, &tk))
7740 return false;
7741 return tk.value == expected;
7744 /*******************************************
7745 * Skip attributes.
7746 * Input:
7747 * t is on a candidate attribute
7748 * Output:
7749 * *pt is set to first non-attribute token on success
7750 * Returns:
7751 * true successful
7752 * false some parsing error
7754 private bool skipAttributes(Token* t, Token** pt)
7756 while (1)
7758 switch (t.value)
7760 case TOK.const_:
7761 case TOK.immutable_:
7762 case TOK.shared_:
7763 case TOK.inout_:
7764 case TOK.final_:
7765 case TOK.auto_:
7766 case TOK.scope_:
7767 case TOK.override_:
7768 case TOK.abstract_:
7769 case TOK.synchronized_:
7770 break;
7772 case TOK.deprecated_:
7773 if (peek(t).value == TOK.leftParenthesis)
7775 t = peek(t);
7776 if (!skipParens(t, &t))
7777 goto Lerror;
7778 // t is on the next of closing parenthesis
7779 continue;
7781 break;
7783 case TOK.nothrow_:
7784 case TOK.pure_:
7785 case TOK.ref_:
7786 case TOK.gshared:
7787 case TOK.return_:
7788 break;
7790 case TOK.at:
7791 t = peek(t);
7792 if (t.value == TOK.identifier)
7794 /* @identifier
7795 * @identifier!arg
7796 * @identifier!(arglist)
7797 * any of the above followed by (arglist)
7798 * @predefined_attribute
7800 if (isBuiltinAtAttribute(t.ident))
7801 break;
7802 t = peek(t);
7803 if (t.value == TOK.not)
7805 t = peek(t);
7806 if (t.value == TOK.leftParenthesis)
7808 // @identifier!(arglist)
7809 if (!skipParens(t, &t))
7810 goto Lerror;
7811 // t is on the next of closing parenthesis
7813 else
7815 // @identifier!arg
7816 // Do low rent skipTemplateArgument
7817 if (t.value == TOK.vector)
7819 // identifier!__vector(type)
7820 t = peek(t);
7821 if (!skipParens(t, &t))
7822 goto Lerror;
7824 else
7825 t = peek(t);
7828 if (t.value == TOK.leftParenthesis)
7830 if (!skipParens(t, &t))
7831 goto Lerror;
7832 // t is on the next of closing parenthesis
7833 continue;
7835 continue;
7837 if (t.value == TOK.leftParenthesis)
7839 // @( ArgumentList )
7840 if (!skipParens(t, &t))
7841 goto Lerror;
7842 // t is on the next of closing parenthesis
7843 continue;
7845 goto Lerror;
7847 default:
7848 goto Ldone;
7850 t = peek(t);
7852 Ldone:
7853 if (pt)
7854 *pt = t;
7855 return true;
7857 Lerror:
7858 return false;
7861 AST.Expression parseExpression()
7863 auto loc = token.loc;
7865 //printf("Parser::parseExpression() loc = %d\n", loc.linnum);
7866 auto e = parseAssignExp();
7867 while (token.value == TOK.comma)
7869 nextToken();
7870 auto e2 = parseAssignExp();
7871 e = new AST.CommaExp(loc, e, e2, false);
7872 loc = token.loc;
7874 return e;
7877 /********************************* Expression Parser ***************************/
7879 AST.Expression parsePrimaryExp()
7881 AST.Expression e;
7882 AST.Type t;
7883 Identifier id;
7884 const loc = token.loc;
7886 //printf("parsePrimaryExp(): loc = %d\n", loc.linnum);
7887 switch (token.value)
7889 case TOK.identifier:
7891 if (peekNext() == TOK.arrow)
7893 // skip `identifier ->`
7894 nextToken();
7895 nextToken();
7896 error("use `.` for member lookup, not `->`");
7897 goto Lerr;
7900 if (peekNext() == TOK.goesTo)
7901 goto case_delegate;
7903 id = token.ident;
7904 nextToken();
7905 TOK save;
7906 if (token.value == TOK.not && (save = peekNext()) != TOK.is_ && save != TOK.in_)
7908 // identifier!(template-argument-list)
7909 auto tempinst = new AST.TemplateInstance(loc, id, parseTemplateArguments());
7910 e = new AST.ScopeExp(loc, tempinst);
7912 else
7913 e = new AST.IdentifierExp(loc, id);
7914 break;
7916 case TOK.dollar:
7917 if (!inBrackets)
7918 error("`$` is valid only inside [] of index or slice");
7919 e = new AST.DollarExp(loc);
7920 nextToken();
7921 break;
7923 case TOK.dot:
7924 // Signal global scope '.' operator with "" identifier
7925 e = new AST.IdentifierExp(loc, Id.empty);
7926 break;
7928 case TOK.this_:
7929 e = new AST.ThisExp(loc);
7930 nextToken();
7931 break;
7933 case TOK.super_:
7934 e = new AST.SuperExp(loc);
7935 nextToken();
7936 break;
7938 case TOK.int32Literal:
7939 e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint32);
7940 nextToken();
7941 break;
7943 case TOK.uns32Literal:
7944 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns32);
7945 nextToken();
7946 break;
7948 case TOK.int64Literal:
7949 e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint64);
7950 nextToken();
7951 break;
7953 case TOK.uns64Literal:
7954 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns64);
7955 nextToken();
7956 break;
7958 case TOK.float32Literal:
7959 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat32);
7960 nextToken();
7961 break;
7963 case TOK.float64Literal:
7964 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat64);
7965 nextToken();
7966 break;
7968 case TOK.float80Literal:
7969 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat80);
7970 nextToken();
7971 break;
7973 case TOK.imaginary32Literal:
7974 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary32);
7975 nextToken();
7976 break;
7978 case TOK.imaginary64Literal:
7979 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary64);
7980 nextToken();
7981 break;
7983 case TOK.imaginary80Literal:
7984 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary80);
7985 nextToken();
7986 break;
7988 case TOK.null_:
7989 e = new AST.NullExp(loc);
7990 nextToken();
7991 break;
7993 case TOK.file:
7995 const(char)* s = loc.filename ? loc.filename : mod.ident.toChars();
7996 e = new AST.StringExp(loc, s.toDString());
7997 nextToken();
7998 break;
8000 case TOK.fileFullPath:
8002 assert(loc.isValid(), "__FILE_FULL_PATH__ does not work with an invalid location");
8003 const s = FileName.toAbsolute(loc.filename);
8004 e = new AST.StringExp(loc, s.toDString());
8005 nextToken();
8006 break;
8009 case TOK.line:
8010 e = new AST.IntegerExp(loc, loc.linnum, AST.Type.tint32);
8011 nextToken();
8012 break;
8014 case TOK.moduleString:
8016 const(char)* s = md ? md.toChars() : mod.toChars();
8017 e = new AST.StringExp(loc, s.toDString());
8018 nextToken();
8019 break;
8021 case TOK.functionString:
8022 e = new AST.FuncInitExp(loc);
8023 nextToken();
8024 break;
8026 case TOK.prettyFunction:
8027 e = new AST.PrettyFuncInitExp(loc);
8028 nextToken();
8029 break;
8031 case TOK.true_:
8032 e = new AST.IntegerExp(loc, 1, AST.Type.tbool);
8033 nextToken();
8034 break;
8036 case TOK.false_:
8037 e = new AST.IntegerExp(loc, 0, AST.Type.tbool);
8038 nextToken();
8039 break;
8041 case TOK.charLiteral:
8042 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tchar);
8043 nextToken();
8044 break;
8046 case TOK.wcharLiteral:
8047 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.twchar);
8048 nextToken();
8049 break;
8051 case TOK.dcharLiteral:
8052 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tdchar);
8053 nextToken();
8054 break;
8056 case TOK.string_:
8057 case TOK.hexadecimalString:
8059 // cat adjacent strings
8060 auto s = token.ustring;
8061 auto len = token.len;
8062 auto postfix = token.postfix;
8063 while (1)
8065 const prev = token;
8066 nextToken();
8067 if (token.value == TOK.string_ || token.value == TOK.hexadecimalString)
8069 if (token.postfix)
8071 if (token.postfix != postfix)
8072 error("mismatched string literal postfixes `'%c'` and `'%c'`", postfix, token.postfix);
8073 postfix = token.postfix;
8076 error("Implicit string concatenation is error-prone and disallowed in D");
8077 errorSupplemental(token.loc, "Use the explicit syntax instead " ~
8078 "(concatenating literals is `@nogc`): %s ~ %s",
8079 prev.toChars(), token.toChars());
8081 const len1 = len;
8082 const len2 = token.len;
8083 len = len1 + len2;
8084 auto s2 = cast(char*)mem.xmalloc_noscan(len * char.sizeof);
8085 memcpy(s2, s, len1 * char.sizeof);
8086 memcpy(s2 + len1, token.ustring, len2 * char.sizeof);
8087 s = s2;
8089 else
8090 break;
8092 e = new AST.StringExp(loc, s[0 .. len], len, 1, postfix);
8093 break;
8095 case TOK.void_:
8096 t = AST.Type.tvoid;
8097 goto LabelX;
8099 case TOK.int8:
8100 t = AST.Type.tint8;
8101 goto LabelX;
8103 case TOK.uns8:
8104 t = AST.Type.tuns8;
8105 goto LabelX;
8107 case TOK.int16:
8108 t = AST.Type.tint16;
8109 goto LabelX;
8111 case TOK.uns16:
8112 t = AST.Type.tuns16;
8113 goto LabelX;
8115 case TOK.int32:
8116 t = AST.Type.tint32;
8117 goto LabelX;
8119 case TOK.uns32:
8120 t = AST.Type.tuns32;
8121 goto LabelX;
8123 case TOK.int64:
8124 t = AST.Type.tint64;
8125 goto LabelX;
8127 case TOK.uns64:
8128 t = AST.Type.tuns64;
8129 goto LabelX;
8131 case TOK.int128:
8132 t = AST.Type.tint128;
8133 goto LabelX;
8135 case TOK.uns128:
8136 t = AST.Type.tuns128;
8137 goto LabelX;
8139 case TOK.float32:
8140 t = AST.Type.tfloat32;
8141 goto LabelX;
8143 case TOK.float64:
8144 t = AST.Type.tfloat64;
8145 goto LabelX;
8147 case TOK.float80:
8148 t = AST.Type.tfloat80;
8149 goto LabelX;
8151 case TOK.imaginary32:
8152 t = AST.Type.timaginary32;
8153 goto LabelX;
8155 case TOK.imaginary64:
8156 t = AST.Type.timaginary64;
8157 goto LabelX;
8159 case TOK.imaginary80:
8160 t = AST.Type.timaginary80;
8161 goto LabelX;
8163 case TOK.complex32:
8164 t = AST.Type.tcomplex32;
8165 goto LabelX;
8167 case TOK.complex64:
8168 t = AST.Type.tcomplex64;
8169 goto LabelX;
8171 case TOK.complex80:
8172 t = AST.Type.tcomplex80;
8173 goto LabelX;
8175 case TOK.bool_:
8176 t = AST.Type.tbool;
8177 goto LabelX;
8179 case TOK.char_:
8180 t = AST.Type.tchar;
8181 goto LabelX;
8183 case TOK.wchar_:
8184 t = AST.Type.twchar;
8185 goto LabelX;
8187 case TOK.dchar_:
8188 t = AST.Type.tdchar;
8189 goto LabelX;
8190 LabelX:
8191 nextToken();
8192 if (token.value == TOK.leftParenthesis)
8194 e = new AST.TypeExp(loc, t);
8195 e = new AST.CallExp(loc, e, parseArguments());
8196 break;
8198 check(TOK.dot, t.toChars());
8199 if (token.value != TOK.identifier)
8201 error("found `%s` when expecting identifier following `%s`.", token.toChars(), t.toChars());
8202 goto Lerr;
8204 e = new AST.DotIdExp(loc, new AST.TypeExp(loc, t), token.ident);
8205 nextToken();
8206 break;
8208 case TOK.typeof_:
8210 t = parseTypeof();
8211 e = new AST.TypeExp(loc, t);
8212 break;
8214 case TOK.vector:
8216 t = parseVector();
8217 e = new AST.TypeExp(loc, t);
8218 break;
8220 case TOK.typeid_:
8222 nextToken();
8223 check(TOK.leftParenthesis, "`typeid`");
8224 RootObject o = parseTypeOrAssignExp();
8225 check(TOK.rightParenthesis);
8226 e = new AST.TypeidExp(loc, o);
8227 break;
8229 case TOK.traits:
8231 /* __traits(identifier, args...)
8233 Identifier ident;
8234 AST.Objects* args = null;
8236 nextToken();
8237 check(TOK.leftParenthesis);
8238 if (token.value != TOK.identifier)
8240 error("`__traits(identifier, args...)` expected");
8241 goto Lerr;
8243 ident = token.ident;
8244 nextToken();
8245 if (token.value == TOK.comma)
8246 args = parseTemplateArgumentList(); // __traits(identifier, args...)
8247 else
8248 check(TOK.rightParenthesis); // __traits(identifier)
8250 e = new AST.TraitsExp(loc, ident, args);
8251 break;
8253 case TOK.is_:
8255 AST.Type targ;
8256 Identifier ident = null;
8257 AST.Type tspec = null;
8258 TOK tok = TOK.reserved;
8259 TOK tok2 = TOK.reserved;
8260 AST.TemplateParameters* tpl = null;
8262 nextToken();
8263 if (token.value == TOK.leftParenthesis)
8265 nextToken();
8266 if (token.value == TOK.identifier && peekNext() == TOK.leftParenthesis)
8268 error(loc, "unexpected `(` after `%s`, inside `is` expression. Try enclosing the contents of `is` with a `typeof` expression", token.toChars());
8269 nextToken();
8270 Token* tempTok = peekPastParen(&token);
8271 memcpy(&token, tempTok, Token.sizeof);
8272 goto Lerr;
8274 targ = parseType(&ident);
8275 if (token.value == TOK.colon || token.value == TOK.equal)
8277 tok = token.value;
8278 nextToken();
8279 if (tok == TOK.equal && (token.value == TOK.struct_ || token.value == TOK.union_
8280 || token.value == TOK.class_ || token.value == TOK.super_ || token.value == TOK.enum_
8281 || token.value == TOK.interface_ || token.value == TOK.package_ || token.value == TOK.module_
8282 || token.value == TOK.argumentTypes || token.value == TOK.parameters
8283 || token.value == TOK.const_ && peekNext() == TOK.rightParenthesis
8284 || token.value == TOK.immutable_ && peekNext() == TOK.rightParenthesis
8285 || token.value == TOK.shared_ && peekNext() == TOK.rightParenthesis
8286 || token.value == TOK.inout_ && peekNext() == TOK.rightParenthesis || token.value == TOK.function_
8287 || token.value == TOK.delegate_ || token.value == TOK.return_
8288 || (token.value == TOK.vector && peekNext() == TOK.rightParenthesis)))
8290 tok2 = token.value;
8291 nextToken();
8293 else
8295 tspec = parseType();
8298 if (tspec)
8300 if (token.value == TOK.comma)
8301 tpl = parseTemplateParameterList(1);
8302 else
8304 tpl = new AST.TemplateParameters();
8305 check(TOK.rightParenthesis);
8308 else
8309 check(TOK.rightParenthesis);
8311 else
8313 error("`type identifier : specialization` expected following `is`");
8314 goto Lerr;
8316 e = new AST.IsExp(loc, targ, ident, tok, tspec, tok2, tpl);
8317 break;
8319 case TOK.assert_:
8321 // https://dlang.org/spec/expression.html#assert_expressions
8322 AST.Expression msg = null;
8324 nextToken();
8325 check(TOK.leftParenthesis, "`assert`");
8326 e = parseAssignExp();
8327 if (token.value == TOK.comma)
8329 nextToken();
8330 if (token.value != TOK.rightParenthesis)
8332 msg = parseAssignExp();
8333 if (token.value == TOK.comma)
8334 nextToken();
8337 check(TOK.rightParenthesis);
8338 e = new AST.AssertExp(loc, e, msg);
8339 break;
8341 case TOK.mixin_:
8343 // https://dlang.org/spec/expression.html#mixin_expressions
8344 nextToken();
8345 if (token.value != TOK.leftParenthesis)
8346 error("found `%s` when expecting `%s` following `mixin`", token.toChars(), Token.toChars(TOK.leftParenthesis));
8347 auto exps = parseArguments();
8348 e = new AST.MixinExp(loc, exps);
8349 break;
8351 case TOK.import_:
8353 nextToken();
8354 check(TOK.leftParenthesis, "`import`");
8355 e = parseAssignExp();
8356 check(TOK.rightParenthesis);
8357 e = new AST.ImportExp(loc, e);
8358 break;
8360 case TOK.new_:
8361 e = parseNewExp(null);
8362 break;
8364 case TOK.ref_:
8366 if (peekNext() == TOK.leftParenthesis)
8368 Token* tk = peekPastParen(peek(&token));
8369 if (skipAttributes(tk, &tk) && (tk.value == TOK.goesTo || tk.value == TOK.leftCurly))
8371 // ref (arguments) => expression
8372 // ref (arguments) { statements... }
8373 goto case_delegate;
8376 nextToken();
8377 error("found `%s` when expecting function literal following `ref`", token.toChars());
8378 goto Lerr;
8380 case TOK.leftParenthesis:
8382 Token* tk = peekPastParen(&token);
8383 if (skipAttributes(tk, &tk) && (tk.value == TOK.goesTo || tk.value == TOK.leftCurly))
8385 // (arguments) => expression
8386 // (arguments) { statements... }
8387 goto case_delegate;
8390 // ( expression )
8391 nextToken();
8392 e = parseExpression();
8393 e.parens = 1;
8394 check(loc, TOK.rightParenthesis);
8395 break;
8397 case TOK.leftBracket:
8399 /* Parse array literals and associative array literals:
8400 * [ value, value, value ... ]
8401 * [ key:value, key:value, key:value ... ]
8403 auto values = new AST.Expressions();
8404 AST.Expressions* keys = null;
8406 nextToken();
8407 while (token.value != TOK.rightBracket && token.value != TOK.endOfFile)
8409 e = parseAssignExp();
8410 if (token.value == TOK.colon && (keys || values.dim == 0))
8412 nextToken();
8413 if (!keys)
8414 keys = new AST.Expressions();
8415 keys.push(e);
8416 e = parseAssignExp();
8418 else if (keys)
8420 error("`key:value` expected for associative array literal");
8421 keys = null;
8423 values.push(e);
8424 if (token.value == TOK.rightBracket)
8425 break;
8426 check(TOK.comma);
8428 check(loc, TOK.rightBracket);
8430 if (keys)
8431 e = new AST.AssocArrayLiteralExp(loc, keys, values);
8432 else
8433 e = new AST.ArrayLiteralExp(loc, null, values);
8434 break;
8436 case TOK.leftCurly:
8437 case TOK.function_:
8438 case TOK.delegate_:
8439 case_delegate:
8441 AST.Dsymbol s = parseFunctionLiteral();
8442 e = new AST.FuncExp(loc, s);
8443 break;
8445 default:
8446 error("expression expected, not `%s`", token.toChars());
8447 Lerr:
8448 // Anything for e, as long as it's not NULL
8449 e = new AST.IntegerExp(loc, 0, AST.Type.tint32);
8450 nextToken();
8451 break;
8453 return e;
8456 private AST.Expression parseUnaryExp()
8458 AST.Expression e;
8459 const loc = token.loc;
8461 switch (token.value)
8463 case TOK.and:
8464 nextToken();
8465 e = parseUnaryExp();
8466 e = new AST.AddrExp(loc, e);
8467 break;
8469 case TOK.plusPlus:
8470 nextToken();
8471 e = parseUnaryExp();
8472 //e = new AddAssignExp(loc, e, new IntegerExp(loc, 1, Type::tint32));
8473 e = new AST.PreExp(EXP.prePlusPlus, loc, e);
8474 break;
8476 case TOK.minusMinus:
8477 nextToken();
8478 e = parseUnaryExp();
8479 //e = new MinAssignExp(loc, e, new IntegerExp(loc, 1, Type::tint32));
8480 e = new AST.PreExp(EXP.preMinusMinus, loc, e);
8481 break;
8483 case TOK.mul:
8484 nextToken();
8485 e = parseUnaryExp();
8486 e = new AST.PtrExp(loc, e);
8487 break;
8489 case TOK.min:
8490 nextToken();
8491 e = parseUnaryExp();
8492 e = new AST.NegExp(loc, e);
8493 break;
8495 case TOK.add:
8496 nextToken();
8497 e = parseUnaryExp();
8498 e = new AST.UAddExp(loc, e);
8499 break;
8501 case TOK.not:
8502 nextToken();
8503 e = parseUnaryExp();
8504 e = new AST.NotExp(loc, e);
8505 break;
8507 case TOK.tilde:
8508 nextToken();
8509 e = parseUnaryExp();
8510 e = new AST.ComExp(loc, e);
8511 break;
8513 case TOK.delete_:
8514 nextToken();
8515 e = parseUnaryExp();
8516 e = new AST.DeleteExp(loc, e, false);
8517 break;
8519 case TOK.cast_: // cast(type) expression
8521 nextToken();
8522 check(TOK.leftParenthesis);
8523 /* Look for cast(), cast(const), cast(immutable),
8524 * cast(shared), cast(shared const), cast(wild), cast(shared wild)
8526 ubyte m = 0;
8527 while (1)
8529 switch (token.value)
8531 case TOK.const_:
8532 if (peekNext() == TOK.leftParenthesis)
8533 break; // const as type constructor
8534 m |= MODFlags.const_; // const as storage class
8535 nextToken();
8536 continue;
8538 case TOK.immutable_:
8539 if (peekNext() == TOK.leftParenthesis)
8540 break;
8541 m |= MODFlags.immutable_;
8542 nextToken();
8543 continue;
8545 case TOK.shared_:
8546 if (peekNext() == TOK.leftParenthesis)
8547 break;
8548 m |= MODFlags.shared_;
8549 nextToken();
8550 continue;
8552 case TOK.inout_:
8553 if (peekNext() == TOK.leftParenthesis)
8554 break;
8555 m |= MODFlags.wild;
8556 nextToken();
8557 continue;
8559 default:
8560 break;
8562 break;
8564 if (token.value == TOK.rightParenthesis)
8566 nextToken();
8567 e = parseUnaryExp();
8568 e = new AST.CastExp(loc, e, m);
8570 else
8572 AST.Type t = parseType(); // cast( type )
8573 t = t.addMod(m); // cast( const type )
8574 check(TOK.rightParenthesis);
8575 e = parseUnaryExp();
8576 e = new AST.CastExp(loc, e, t);
8578 break;
8580 case TOK.inout_:
8581 case TOK.shared_:
8582 case TOK.const_:
8583 case TOK.immutable_: // immutable(type)(arguments) / immutable(type).init
8585 StorageClass stc = parseTypeCtor();
8587 AST.Type t = parseBasicType();
8588 t = t.addSTC(stc);
8590 if (stc == 0 && token.value == TOK.dot)
8592 nextToken();
8593 if (token.value != TOK.identifier)
8595 error("identifier expected following `(type)`.");
8596 return null;
8598 e = new AST.DotIdExp(loc, new AST.TypeExp(loc, t), token.ident);
8599 nextToken();
8600 e = parsePostExp(e);
8602 else
8604 e = new AST.TypeExp(loc, t);
8605 if (token.value != TOK.leftParenthesis)
8607 error("`(arguments)` expected following `%s`", t.toChars());
8608 return e;
8610 e = new AST.CallExp(loc, e, parseArguments());
8612 break;
8614 case TOK.leftParenthesis:
8616 auto tk = peek(&token);
8617 static if (CCASTSYNTAX)
8619 // If cast
8620 if (isDeclaration(tk, NeedDeclaratorId.no, TOK.rightParenthesis, &tk))
8622 tk = peek(tk); // skip over right parenthesis
8623 switch (tk.value)
8625 case TOK.not:
8626 tk = peek(tk);
8627 if (tk.value == TOK.is_ || tk.value == TOK.in_) // !is or !in
8628 break;
8629 goto case;
8631 case TOK.dot:
8632 case TOK.plusPlus:
8633 case TOK.minusMinus:
8634 case TOK.delete_:
8635 case TOK.new_:
8636 case TOK.leftParenthesis:
8637 case TOK.identifier:
8638 case TOK.this_:
8639 case TOK.super_:
8640 case TOK.int32Literal:
8641 case TOK.uns32Literal:
8642 case TOK.int64Literal:
8643 case TOK.uns64Literal:
8644 case TOK.int128Literal:
8645 case TOK.uns128Literal:
8646 case TOK.float32Literal:
8647 case TOK.float64Literal:
8648 case TOK.float80Literal:
8649 case TOK.imaginary32Literal:
8650 case TOK.imaginary64Literal:
8651 case TOK.imaginary80Literal:
8652 case TOK.null_:
8653 case TOK.true_:
8654 case TOK.false_:
8655 case TOK.charLiteral:
8656 case TOK.wcharLiteral:
8657 case TOK.dcharLiteral:
8658 case TOK.string_:
8659 version (none)
8661 case TOK.tilde:
8662 case TOK.and:
8663 case TOK.mul:
8664 case TOK.min:
8665 case TOK.add:
8667 case TOK.function_:
8668 case TOK.delegate_:
8669 case TOK.typeof_:
8670 case TOK.traits:
8671 case TOK.vector:
8672 case TOK.file:
8673 case TOK.fileFullPath:
8674 case TOK.line:
8675 case TOK.moduleString:
8676 case TOK.functionString:
8677 case TOK.prettyFunction:
8678 case TOK.wchar_:
8679 case TOK.dchar_:
8680 case TOK.bool_:
8681 case TOK.char_:
8682 case TOK.int8:
8683 case TOK.uns8:
8684 case TOK.int16:
8685 case TOK.uns16:
8686 case TOK.int32:
8687 case TOK.uns32:
8688 case TOK.int64:
8689 case TOK.uns64:
8690 case TOK.int128:
8691 case TOK.uns128:
8692 case TOK.float32:
8693 case TOK.float64:
8694 case TOK.float80:
8695 case TOK.imaginary32:
8696 case TOK.imaginary64:
8697 case TOK.imaginary80:
8698 case TOK.complex32:
8699 case TOK.complex64:
8700 case TOK.complex80:
8701 case TOK.void_:
8703 // (type) una_exp
8704 nextToken();
8705 auto t = parseType();
8706 check(TOK.rightParenthesis);
8708 // if .identifier
8709 // or .identifier!( ... )
8710 if (token.value == TOK.dot)
8712 if (peekNext() != TOK.identifier && peekNext() != TOK.new_)
8714 error("identifier or new keyword expected following `(...)`.");
8715 return null;
8717 e = new AST.TypeExp(loc, t);
8718 e.parens = true;
8719 e = parsePostExp(e);
8721 else
8723 e = parseUnaryExp();
8724 e = new AST.CastExp(loc, e, t);
8725 error("C style cast illegal, use `%s`", e.toChars());
8727 return e;
8729 default:
8730 break;
8734 e = parsePrimaryExp();
8735 e = parsePostExp(e);
8736 break;
8738 default:
8739 e = parsePrimaryExp();
8740 e = parsePostExp(e);
8741 break;
8743 assert(e);
8745 // ^^ is right associative and has higher precedence than the unary operators
8746 while (token.value == TOK.pow)
8748 nextToken();
8749 AST.Expression e2 = parseUnaryExp();
8750 e = new AST.PowExp(loc, e, e2);
8753 return e;
8756 private AST.Expression parsePostExp(AST.Expression e)
8758 while (1)
8760 const loc = token.loc;
8761 switch (token.value)
8763 case TOK.dot:
8764 nextToken();
8765 if (token.value == TOK.identifier)
8767 Identifier id = token.ident;
8769 nextToken();
8770 if (token.value == TOK.not && peekNext() != TOK.is_ && peekNext() != TOK.in_)
8772 AST.Objects* tiargs = parseTemplateArguments();
8773 e = new AST.DotTemplateInstanceExp(loc, e, id, tiargs);
8775 else
8776 e = new AST.DotIdExp(loc, e, id);
8777 continue;
8779 if (token.value == TOK.new_)
8781 e = parseNewExp(e);
8782 continue;
8784 error("identifier or `new` expected following `.`, not `%s`", token.toChars());
8785 break;
8787 case TOK.plusPlus:
8788 e = new AST.PostExp(EXP.plusPlus, loc, e);
8789 break;
8791 case TOK.minusMinus:
8792 e = new AST.PostExp(EXP.minusMinus, loc, e);
8793 break;
8795 case TOK.leftParenthesis:
8796 e = new AST.CallExp(loc, e, parseArguments());
8797 continue;
8799 case TOK.leftBracket:
8801 // array dereferences:
8802 // array[index]
8803 // array[]
8804 // array[lwr .. upr]
8805 AST.Expression index;
8806 AST.Expression upr;
8807 auto arguments = new AST.Expressions();
8809 inBrackets++;
8810 nextToken();
8811 while (token.value != TOK.rightBracket && token.value != TOK.endOfFile)
8813 index = parseAssignExp();
8814 if (token.value == TOK.slice)
8816 // array[..., lwr..upr, ...]
8817 nextToken();
8818 upr = parseAssignExp();
8819 arguments.push(new AST.IntervalExp(loc, index, upr));
8821 else
8822 arguments.push(index);
8823 if (token.value == TOK.rightBracket)
8824 break;
8825 check(TOK.comma);
8827 check(TOK.rightBracket);
8828 inBrackets--;
8829 e = new AST.ArrayExp(loc, e, arguments);
8830 continue;
8832 default:
8833 return e;
8835 nextToken();
8839 private AST.Expression parseMulExp()
8841 const loc = token.loc;
8842 auto e = parseUnaryExp();
8844 while (1)
8846 switch (token.value)
8848 case TOK.mul:
8849 nextToken();
8850 auto e2 = parseUnaryExp();
8851 e = new AST.MulExp(loc, e, e2);
8852 continue;
8854 case TOK.div:
8855 nextToken();
8856 auto e2 = parseUnaryExp();
8857 e = new AST.DivExp(loc, e, e2);
8858 continue;
8860 case TOK.mod:
8861 nextToken();
8862 auto e2 = parseUnaryExp();
8863 e = new AST.ModExp(loc, e, e2);
8864 continue;
8866 default:
8867 break;
8869 break;
8871 return e;
8874 private AST.Expression parseAddExp()
8876 const loc = token.loc;
8877 auto e = parseMulExp();
8879 while (1)
8881 switch (token.value)
8883 case TOK.add:
8884 nextToken();
8885 auto e2 = parseMulExp();
8886 e = new AST.AddExp(loc, e, e2);
8887 continue;
8889 case TOK.min:
8890 nextToken();
8891 auto e2 = parseMulExp();
8892 e = new AST.MinExp(loc, e, e2);
8893 continue;
8895 case TOK.tilde:
8896 nextToken();
8897 auto e2 = parseMulExp();
8898 e = new AST.CatExp(loc, e, e2);
8899 continue;
8901 default:
8902 break;
8904 break;
8906 return e;
8909 private AST.Expression parseShiftExp()
8911 const loc = token.loc;
8912 auto e = parseAddExp();
8914 while (1)
8916 switch (token.value)
8918 case TOK.leftShift:
8919 nextToken();
8920 auto e2 = parseAddExp();
8921 e = new AST.ShlExp(loc, e, e2);
8922 continue;
8924 case TOK.rightShift:
8925 nextToken();
8926 auto e2 = parseAddExp();
8927 e = new AST.ShrExp(loc, e, e2);
8928 continue;
8930 case TOK.unsignedRightShift:
8931 nextToken();
8932 auto e2 = parseAddExp();
8933 e = new AST.UshrExp(loc, e, e2);
8934 continue;
8936 default:
8937 break;
8939 break;
8941 return e;
8944 private AST.Expression parseCmpExp()
8946 const loc = token.loc;
8948 auto e = parseShiftExp();
8949 EXP op = EXP.reserved;
8951 switch (token.value)
8953 case TOK.equal: op = EXP.equal; goto Lequal;
8954 case TOK.notEqual: op = EXP.notEqual; goto Lequal;
8955 Lequal:
8956 nextToken();
8957 auto e2 = parseShiftExp();
8958 e = new AST.EqualExp(op, loc, e, e2);
8959 break;
8961 case TOK.not:
8963 // Attempt to identify '!is'
8964 const tv = peekNext();
8965 if (tv == TOK.in_)
8967 nextToken();
8968 nextToken();
8969 auto e2 = parseShiftExp();
8970 e = new AST.InExp(loc, e, e2);
8971 e = new AST.NotExp(loc, e);
8972 break;
8974 if (tv != TOK.is_)
8975 break;
8976 nextToken();
8977 op = EXP.notIdentity;
8978 goto Lidentity;
8980 case TOK.is_: op = EXP.identity; goto Lidentity;
8981 Lidentity:
8982 nextToken();
8983 auto e2 = parseShiftExp();
8984 e = new AST.IdentityExp(op, loc, e, e2);
8985 break;
8987 case TOK.lessThan: op = EXP.lessThan; goto Lcmp;
8988 case TOK.lessOrEqual: op = EXP.lessOrEqual; goto Lcmp;
8989 case TOK.greaterThan: op = EXP.greaterThan; goto Lcmp;
8990 case TOK.greaterOrEqual: op = EXP.greaterOrEqual; goto Lcmp;
8991 Lcmp:
8992 nextToken();
8993 auto e2 = parseShiftExp();
8994 e = new AST.CmpExp(op, loc, e, e2);
8995 break;
8997 case TOK.in_:
8998 nextToken();
8999 auto e2 = parseShiftExp();
9000 e = new AST.InExp(loc, e, e2);
9001 break;
9003 default:
9004 break;
9006 return e;
9009 private AST.Expression parseAndExp()
9011 Loc loc = token.loc;
9012 auto e = parseCmpExp();
9013 while (token.value == TOK.and)
9015 checkParens(TOK.and, e);
9016 nextToken();
9017 auto e2 = parseCmpExp();
9018 checkParens(TOK.and, e2);
9019 e = new AST.AndExp(loc, e, e2);
9020 loc = token.loc;
9022 return e;
9025 private AST.Expression parseXorExp()
9027 const loc = token.loc;
9029 auto e = parseAndExp();
9030 while (token.value == TOK.xor)
9032 checkParens(TOK.xor, e);
9033 nextToken();
9034 auto e2 = parseAndExp();
9035 checkParens(TOK.xor, e2);
9036 e = new AST.XorExp(loc, e, e2);
9038 return e;
9041 private AST.Expression parseOrExp()
9043 const loc = token.loc;
9045 auto e = parseXorExp();
9046 while (token.value == TOK.or)
9048 checkParens(TOK.or, e);
9049 nextToken();
9050 auto e2 = parseXorExp();
9051 checkParens(TOK.or, e2);
9052 e = new AST.OrExp(loc, e, e2);
9054 return e;
9057 private AST.Expression parseAndAndExp()
9059 const loc = token.loc;
9061 auto e = parseOrExp();
9062 while (token.value == TOK.andAnd)
9064 nextToken();
9065 auto e2 = parseOrExp();
9066 e = new AST.LogicalExp(loc, EXP.andAnd, e, e2);
9068 return e;
9071 private AST.Expression parseOrOrExp()
9073 const loc = token.loc;
9075 auto e = parseAndAndExp();
9076 while (token.value == TOK.orOr)
9078 nextToken();
9079 auto e2 = parseAndAndExp();
9080 e = new AST.LogicalExp(loc, EXP.orOr, e, e2);
9082 return e;
9085 private AST.Expression parseCondExp()
9087 const loc = token.loc;
9089 auto e = parseOrOrExp();
9090 if (token.value == TOK.question)
9092 nextToken();
9093 auto e1 = parseExpression();
9094 check(TOK.colon);
9095 auto e2 = parseCondExp();
9096 e = new AST.CondExp(loc, e, e1, e2);
9098 return e;
9101 AST.Expression parseAssignExp()
9103 AST.Expression e;
9104 e = parseCondExp();
9105 if (e is null)
9106 return e;
9108 // require parens for e.g. `t ? a = 1 : b = 2`
9109 void checkRequiredParens()
9111 if (e.op == EXP.question && !e.parens)
9112 dmd.errors.error(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`",
9113 e.toChars(), Token.toChars(token.value));
9116 const loc = token.loc;
9117 switch (token.value)
9119 case TOK.assign:
9120 checkRequiredParens();
9121 nextToken();
9122 auto e2 = parseAssignExp();
9123 e = new AST.AssignExp(loc, e, e2);
9124 break;
9126 case TOK.addAssign:
9127 checkRequiredParens();
9128 nextToken();
9129 auto e2 = parseAssignExp();
9130 e = new AST.AddAssignExp(loc, e, e2);
9131 break;
9133 case TOK.minAssign:
9134 checkRequiredParens();
9135 nextToken();
9136 auto e2 = parseAssignExp();
9137 e = new AST.MinAssignExp(loc, e, e2);
9138 break;
9140 case TOK.mulAssign:
9141 checkRequiredParens();
9142 nextToken();
9143 auto e2 = parseAssignExp();
9144 e = new AST.MulAssignExp(loc, e, e2);
9145 break;
9147 case TOK.divAssign:
9148 checkRequiredParens();
9149 nextToken();
9150 auto e2 = parseAssignExp();
9151 e = new AST.DivAssignExp(loc, e, e2);
9152 break;
9154 case TOK.modAssign:
9155 checkRequiredParens();
9156 nextToken();
9157 auto e2 = parseAssignExp();
9158 e = new AST.ModAssignExp(loc, e, e2);
9159 break;
9161 case TOK.powAssign:
9162 checkRequiredParens();
9163 nextToken();
9164 auto e2 = parseAssignExp();
9165 e = new AST.PowAssignExp(loc, e, e2);
9166 break;
9168 case TOK.andAssign:
9169 checkRequiredParens();
9170 nextToken();
9171 auto e2 = parseAssignExp();
9172 e = new AST.AndAssignExp(loc, e, e2);
9173 break;
9175 case TOK.orAssign:
9176 checkRequiredParens();
9177 nextToken();
9178 auto e2 = parseAssignExp();
9179 e = new AST.OrAssignExp(loc, e, e2);
9180 break;
9182 case TOK.xorAssign:
9183 checkRequiredParens();
9184 nextToken();
9185 auto e2 = parseAssignExp();
9186 e = new AST.XorAssignExp(loc, e, e2);
9187 break;
9189 case TOK.leftShiftAssign:
9190 checkRequiredParens();
9191 nextToken();
9192 auto e2 = parseAssignExp();
9193 e = new AST.ShlAssignExp(loc, e, e2);
9194 break;
9196 case TOK.rightShiftAssign:
9197 checkRequiredParens();
9198 nextToken();
9199 auto e2 = parseAssignExp();
9200 e = new AST.ShrAssignExp(loc, e, e2);
9201 break;
9203 case TOK.unsignedRightShiftAssign:
9204 checkRequiredParens();
9205 nextToken();
9206 auto e2 = parseAssignExp();
9207 e = new AST.UshrAssignExp(loc, e, e2);
9208 break;
9210 case TOK.concatenateAssign:
9211 checkRequiredParens();
9212 nextToken();
9213 auto e2 = parseAssignExp();
9214 e = new AST.CatAssignExp(loc, e, e2);
9215 break;
9217 default:
9218 break;
9221 return e;
9224 /*************************
9225 * Collect argument list.
9226 * Assume current token is ',', '$(LPAREN)' or '['.
9228 private AST.Expressions* parseArguments()
9230 // function call
9231 AST.Expressions* arguments;
9233 arguments = new AST.Expressions();
9234 const endtok = token.value == TOK.leftBracket ? TOK.rightBracket : TOK.rightParenthesis;
9236 nextToken();
9238 while (token.value != endtok && token.value != TOK.endOfFile)
9240 auto arg = parseAssignExp();
9241 arguments.push(arg);
9242 if (token.value != TOK.comma)
9243 break;
9245 nextToken(); //comma
9248 check(endtok);
9250 return arguments;
9253 /*******************************************
9255 private AST.Expression parseNewExp(AST.Expression thisexp)
9257 const loc = token.loc;
9259 nextToken();
9260 AST.Expressions* newargs = null;
9261 AST.Expressions* arguments = null;
9262 if (token.value == TOK.leftParenthesis)
9264 newargs = parseArguments();
9267 // An anonymous nested class starts with "class"
9268 if (token.value == TOK.class_)
9270 nextToken();
9271 if (token.value == TOK.leftParenthesis)
9272 arguments = parseArguments();
9274 AST.BaseClasses* baseclasses = null;
9275 if (token.value != TOK.leftCurly)
9276 baseclasses = parseBaseClasses();
9278 Identifier id = null;
9279 AST.Dsymbols* members = null;
9281 if (token.value != TOK.leftCurly)
9283 error("`{ members }` expected for anonymous class");
9285 else
9287 nextToken();
9288 members = parseDeclDefs(0);
9289 if (token.value != TOK.rightCurly)
9290 error("class member expected");
9291 nextToken();
9294 auto cd = new AST.ClassDeclaration(loc, id, baseclasses, members, false);
9295 auto e = new AST.NewAnonClassExp(loc, thisexp, newargs, cd, arguments);
9296 return e;
9299 const stc = parseTypeCtor();
9300 auto t = parseBasicType(true);
9301 t = parseTypeSuffixes(t);
9302 t = t.addSTC(stc);
9303 if (t.ty == Taarray)
9305 AST.TypeAArray taa = cast(AST.TypeAArray)t;
9306 AST.Type index = taa.index;
9307 auto edim = AST.typeToExpression(index);
9308 if (!edim)
9310 error("cannot create a `%s` with `new`", t.toChars);
9311 return new AST.NullExp(loc);
9313 t = new AST.TypeSArray(taa.next, edim);
9315 else if (token.value == TOK.leftParenthesis && t.ty != Tsarray)
9317 arguments = parseArguments();
9320 auto e = new AST.NewExp(loc, thisexp, newargs, t, arguments);
9321 return e;
9324 /**********************************************
9326 private void addComment(AST.Dsymbol s, const(char)* blockComment)
9328 if (s !is null)
9329 this.addComment(s, blockComment.toDString());
9332 private void addComment(AST.Dsymbol s, const(char)[] blockComment)
9334 if (s !is null)
9336 s.addComment(combineComments(blockComment, token.lineComment, true));
9337 token.lineComment = null;
9341 /**********************************************
9342 * Recognize builtin @ attributes
9343 * Params:
9344 * ident = identifier
9345 * Returns:
9346 * storage class for attribute, 0 if not
9348 static StorageClass isBuiltinAtAttribute(Identifier ident)
9350 return (ident == Id.property) ? STC.property :
9351 (ident == Id.nogc) ? STC.nogc :
9352 (ident == Id.safe) ? STC.safe :
9353 (ident == Id.trusted) ? STC.trusted :
9354 (ident == Id.system) ? STC.system :
9355 (ident == Id.live) ? STC.live :
9356 (ident == Id.future) ? STC.future :
9357 (ident == Id.disable) ? STC.disable :
9361 enum StorageClass atAttrGroup =
9362 STC.property |
9363 STC.nogc |
9364 STC.safe |
9365 STC.trusted |
9366 STC.system |
9367 STC.live |
9368 /*STC.future |*/ // probably should be included
9369 STC.disable;
9372 enum PREC : int
9374 zero,
9375 expr,
9376 assign,
9377 cond,
9378 oror,
9379 andand,
9381 xor,
9382 and,
9383 equal,
9384 rel,
9385 shift,
9386 add,
9387 mul,
9388 pow,
9389 unary,
9390 primary,