1 /**
2 * Takes a token stream from the lexer, and parses it into an abstract syntax tree.
3 *
4 * Specification: $(LINK2 https://dlang.org/spec/grammar.html, D Grammar)
5 *
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
12 */
13
14 module dmd.parse;
15
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.root.outbuffer;
26 import dmd.root.rmem;
27 import dmd.root.rootobject;
28 import dmd.root.string;
29 import dmd.tokens;
30
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;
39
40 // Support C cast syntax:
41 // (type)(expression)
42 private enum CCASTSYNTAX = 1;
43
44 // Support postfix C array declarations, such as
45 // int a[3][4];
46 private enum CARRAYDECL = 1;
47
48 /**********************************
49 * Set operator precedence for each operator.
50 *
51 * Used by hdrgen
52 */
53 immutable PREC[TOK.max + 1] precedence =
54 [
55 TOK.type : PREC.expr,
56 TOK.error : PREC.expr,
57 TOK.objcClassReference : PREC.expr, // Objective-C class reference, same as TOK.type
58
59 TOK.typeof_ : PREC.primary,
60 TOK.mixin_ : PREC.primary,
61
62 TOK.import_ : PREC.primary,
63 TOK.dotVariable : PREC.primary,
64 TOK.scope_ : PREC.primary,
65 TOK.identifier : PREC.primary,
66 TOK.this_ : PREC.primary,
67 TOK.super_ : PREC.primary,
68 TOK.int64 : PREC.primary,
69 TOK.float64 : PREC.primary,
70 TOK.complex80 : PREC.primary,
71 TOK.null_ : PREC.primary,
72 TOK.string_ : PREC.primary,
73 TOK.arrayLiteral : PREC.primary,
74 TOK.assocArrayLiteral : PREC.primary,
75 TOK.classReference : PREC.primary,
76 TOK.file : PREC.primary,
77 TOK.fileFullPath : PREC.primary,
78 TOK.line : PREC.primary,
79 TOK.moduleString : PREC.primary,
80 TOK.functionString : PREC.primary,
81 TOK.prettyFunction : PREC.primary,
82 TOK.typeid_ : PREC.primary,
83 TOK.is_ : PREC.primary,
84 TOK.assert_ : PREC.primary,
85 TOK.halt : PREC.primary,
86 TOK.template_ : PREC.primary,
87 TOK.dSymbol : PREC.primary,
88 TOK.function_ : PREC.primary,
89 TOK.variable : PREC.primary,
90 TOK.symbolOffset : PREC.primary,
91 TOK.structLiteral : PREC.primary,
92 TOK.compoundLiteral : PREC.primary,
93 TOK.arrayLength : PREC.primary,
94 TOK.delegatePointer : PREC.primary,
95 TOK.delegateFunctionPointer : PREC.primary,
96 TOK.remove : PREC.primary,
97 TOK.tuple : PREC.primary,
98 TOK.traits : PREC.primary,
99 TOK.default_ : PREC.primary,
100 TOK.overloadSet : PREC.primary,
101 TOK.void_ : PREC.primary,
102 TOK.vectorArray : PREC.primary,
103 TOK._Generic : PREC.primary,
104
105 // post
106 TOK.dotTemplateInstance : PREC.primary,
107 TOK.dotIdentifier : PREC.primary,
108 TOK.dotTemplateDeclaration : PREC.primary,
109 TOK.dot : PREC.primary,
110 TOK.dotType : PREC.primary,
111 TOK.plusPlus : PREC.primary,
112 TOK.minusMinus : PREC.primary,
113 TOK.prePlusPlus : PREC.primary,
114 TOK.preMinusMinus : PREC.primary,
115 TOK.call : PREC.primary,
116 TOK.slice : PREC.primary,
117 TOK.array : PREC.primary,
118 TOK.index : PREC.primary,
119
120 TOK.delegate_ : PREC.unary,
121 TOK.address : PREC.unary,
122 TOK.star : PREC.unary,
123 TOK.negate : PREC.unary,
124 TOK.uadd : PREC.unary,
125 TOK.not : PREC.unary,
126 TOK.tilde : PREC.unary,
127 TOK.delete_ : PREC.unary,
128 TOK.new_ : PREC.unary,
129 TOK.newAnonymousClass : PREC.unary,
130 TOK.cast_ : PREC.unary,
131
132 TOK.vector : PREC.unary,
133 TOK.pow : PREC.pow,
134
135 TOK.mul : PREC.mul,
136 TOK.div : PREC.mul,
137 TOK.mod : PREC.mul,
138
139 TOK.add : PREC.add,
140 TOK.min : PREC.add,
141 TOK.concatenate : PREC.add,
142
143 TOK.leftShift : PREC.shift,
144 TOK.rightShift : PREC.shift,
145 TOK.unsignedRightShift : PREC.shift,
146
147 TOK.lessThan : PREC.rel,
148 TOK.lessOrEqual : PREC.rel,
149 TOK.greaterThan : PREC.rel,
150 TOK.greaterOrEqual : PREC.rel,
151 TOK.in_ : PREC.rel,
152
153 /* Note that we changed precedence, so that < and != have the same
154 * precedence. This change is in the parser, too.
155 */
156 TOK.equal : PREC.rel,
157 TOK.notEqual : PREC.rel,
158 TOK.identity : PREC.rel,
159 TOK.notIdentity : PREC.rel,
160
161 TOK.and : PREC.and,
162 TOK.xor : PREC.xor,
163 TOK.or : PREC.or,
164
165 TOK.andAnd : PREC.andand,
166 TOK.orOr : PREC.oror,
167
168 TOK.question : PREC.cond,
169
170 TOK.assign : PREC.assign,
171 TOK.construct : PREC.assign,
172 TOK.blit : PREC.assign,
173 TOK.addAssign : PREC.assign,
174 TOK.minAssign : PREC.assign,
175 TOK.concatenateAssign : PREC.assign,
176 TOK.concatenateElemAssign : PREC.assign,
177 TOK.concatenateDcharAssign : PREC.assign,
178 TOK.mulAssign : PREC.assign,
179 TOK.divAssign : PREC.assign,
180 TOK.modAssign : PREC.assign,
181 TOK.powAssign : PREC.assign,
182 TOK.leftShiftAssign : PREC.assign,
183 TOK.rightShiftAssign : PREC.assign,
184 TOK.unsignedRightShiftAssign : PREC.assign,
185 TOK.andAssign : PREC.assign,
186 TOK.orAssign : PREC.assign,
187 TOK.xorAssign : PREC.assign,
188
189 TOK.comma : PREC.expr,
190 TOK.declaration : PREC.expr,
191
192 TOK.interval : PREC.assign,
193 ];
194
195 enum ParseStatementFlags : int
196 {
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
202 }
203
PrefixAttributes(AST)204 struct PrefixAttributes(AST)
205 {
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;
214 }
215
216 /// The result of the `ParseLinkage` function
ParsedLinkage(AST)217 struct ParsedLinkage(AST)
218 {
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;
227 }
228
229 /*****************************
230 * Destructively extract storage class from pAttrs.
231 */
getStorageClass(AST)232 private StorageClass getStorageClass(AST)(PrefixAttributes!(AST)* pAttrs)
233 {
234 StorageClass stc = STC.undefined_;
235 if (pAttrs)
236 {
237 stc = pAttrs.storageClass;
238 pAttrs.storageClass = STC.undefined_;
239 }
240 return stc;
241 }
242
243 /**************************************
244 * dump mixin expansion to file for better debugging
245 */
writeMixin(const (char)[]s,ref Loc loc)246 private bool writeMixin(const(char)[] s, ref Loc loc)
247 {
248 if (!global.params.mixinOut)
249 return false;
250
251 OutBuffer* ob = global.params.mixinOut;
252
253 ob.writestring("// expansion at ");
254 ob.writestring(loc.toChars());
255 ob.writenl();
256
257 global.params.mixinLines++;
258
259 loc = Loc(global.params.mixinFile, global.params.mixinLines + 1, loc.charnum);
260
261 // write by line to create consistent line endings
262 size_t lastpos = 0;
263 for (size_t i = 0; i < s.length; ++i)
264 {
265 // detect LF and CRLF
266 const c = s[i];
267 if (c == '\n' || (c == '\r' && i+1 < s.length && s[i+1] == '\n'))
268 {
269 ob.writestring(s[lastpos .. i]);
270 ob.writenl();
271 global.params.mixinLines++;
272 if (c == '\r')
273 ++i;
274 lastpos = i + 1;
275 }
276 }
277
278 if(lastpos < s.length)
279 ob.writestring(s[lastpos .. $]);
280
281 if (s.length == 0 || s[$-1] != '\n')
282 {
283 ob.writenl(); // ensure empty line after expansion
284 global.params.mixinLines++;
285 }
286 ob.writenl();
287 global.params.mixinLines++;
288
289 return true;
290 }
291
292 /***********************************************************
293 */
Parser(AST)294 class Parser(AST) : Lexer
295 {
296 AST.ModuleDeclaration* md;
297
298 protected
299 {
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
307 }
308
309 /*********************
310 * Use this constructor for string mixins.
311 * Input:
312 * loc location in source file of mixin
313 */
314 extern (D) this(const ref Loc loc, AST.Module _module, const(char)[] input, bool doDocComment)
315 {
316 super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false);
317
318 //printf("Parser::Parser()\n");
319 scanloc = loc;
320
321 if (!writeMixin(input, scanloc) && loc.filename)
322 {
323 /* Create a pseudo-filename for the mixin string, as it may not even exist
324 * in the source file.
325 */
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;
329 }
330
331 mod = _module;
332 linkage = LINK.d;
333 //nextToken(); // start up the scanner
334 }
335
336 extern (D) this(AST.Module _module, const(char)[] input, bool doDocComment)
337 {
338 super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false);
339
340 //printf("Parser::Parser()\n");
341 mod = _module;
342 linkage = LINK.d;
343 //nextToken(); // start up the scanner
344 }
345
346 AST.Dsymbols* parseModule()
347 {
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
354
355 Token* tk;
356 if (skipAttributes(&token, &tk) && tk.value == TOK.module_)
357 {
358 while (token.value != TOK.module_)
359 {
360 switch (token.value)
361 {
362 case TOK.deprecated_:
363 {
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)
370 {
371 check(TOK.leftParenthesis);
372 msg = parseAssignExp();
373 check(TOK.rightParenthesis);
374 }
375 break;
376 }
377 case TOK.at:
378 {
379 AST.Expressions* exps = null;
380 const stc = parseAttribute(exps);
381 if (stc & atAttrGroup)
382 {
383 error("`@%s` attribute for module declaration is not supported", token.toChars());
384 }
385 else
386 {
387 udas = AST.UserAttributeDeclaration.concat(udas, exps);
388 }
389 if (stc)
390 nextToken();
391 break;
392 }
393 default:
394 {
395 error("`module` expected instead of `%s`", token.toChars());
396 nextToken();
397 break;
398 }
399 }
400 }
401 }
402
403 if (udas)
404 {
405 auto a = new AST.Dsymbols();
406 auto udad = new AST.UserAttributeDeclaration(udas, a);
407 mod.userAttribDecl = udad;
408 }
409
410 // ModuleDeclation leads off
411 if (token.value == TOK.module_)
412 {
413 const loc = token.loc;
414
415 nextToken();
416 if (token.value != TOK.identifier)
417 {
418 error("identifier expected following `module`");
419 goto Lerr;
420 }
421
422 Identifier[] a;
423 Identifier id = token.ident;
424
425 while (nextToken() == TOK.dot)
426 {
427 a ~= id;
428 nextToken();
429 if (token.value != TOK.identifier)
430 {
431 error("identifier expected following `package`");
432 goto Lerr;
433 }
434 id = token.ident;
435 }
436
437 md = new AST.ModuleDeclaration(loc, a, id, msg, isdeprecated);
438
439 if (token.value != TOK.semicolon)
440 error("`;` expected following module declaration instead of `%s`", token.toChars());
441 nextToken();
442 addComment(mod, comment);
443 }
444
445 decldefs = parseDeclDefs(0, &lastDecl);
446 if (token.value != TOK.endOfFile)
447 {
448 error(token.loc, "unrecognized declaration");
449 goto Lerr;
450 }
451 return decldefs;
452
453 Lerr:
454 while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
455 nextToken();
456 nextToken();
457 return new AST.Dsymbols();
458 }
459
460 final:
461
462 /**
463 * Parses a `deprecated` declaration
464 *
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.
470 *
471 * Returns:
472 * Whether the deprecated declaration has a message
473 */
474 private bool parseDeprecatedAttribute(ref AST.Expression msg)
475 {
476 if (peekNext() != TOK.leftParenthesis)
477 return false;
478
479 nextToken();
480 check(TOK.leftParenthesis);
481 AST.Expression e = parseAssignExp();
482 check(TOK.rightParenthesis);
483 if (msg)
484 {
485 error("conflicting storage class `deprecated(%s)` and `deprecated(%s)`", msg.toChars(), e.toChars());
486 }
487 msg = e;
488 return true;
489 }
490
491 AST.Dsymbols* parseDeclDefs(int once, AST.Dsymbol* pLastDecl = null, PrefixAttributes!AST* pAttrs = null)
492 {
493 AST.Dsymbol lastDecl = null; // used to link unittest to its previous declaration
494 if (!pLastDecl)
495 pLastDecl = &lastDecl;
496
497 const linksave = linkage; // save global state
498
499 //printf("Parser::parseDeclDefs()\n");
500 auto decldefs = new AST.Dsymbols();
501 do
502 {
503 // parse result
504 AST.Dsymbol s = null;
505 AST.Dsymbols* a = null;
506
507 PrefixAttributes!AST attrs;
508 if (!once || !pAttrs)
509 {
510 pAttrs = &attrs;
511 pAttrs.comment = token.blockComment.ptr;
512 }
513 AST.Visibility.Kind prot;
514 StorageClass stc;
515 AST.Condition condition;
516
517 linkage = linksave;
518
519 Loc startloc;
520
521 switch (token.value)
522 {
523 case TOK.enum_:
524 {
525 /* Determine if this is a manifest constant declaration,
526 * or a conventional enum.
527 */
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
534 {
535 const nextv = peekNext2();
536 if (nextv == TOK.leftCurly || nextv == TOK.colon || nextv == TOK.semicolon)
537 s = parseEnum();
538 else
539 goto Ldeclaration;
540 }
541 break;
542 }
543 case TOK.import_:
544 a = parseImport();
545 // keep pLastDecl
546 break;
547
548 case TOK.template_:
549 s = cast(AST.Dsymbol)parseTemplateDeclaration();
550 break;
551
552 case TOK.mixin_:
553 {
554 const loc = token.loc;
555 switch (peekNext())
556 {
557 case TOK.leftParenthesis:
558 {
559 // mixin(string)
560 nextToken();
561 auto exps = parseArguments();
562 check(TOK.semicolon);
563 s = new AST.CompileDeclaration(loc, exps);
564 break;
565 }
566 case TOK.template_:
567 // mixin template
568 nextToken();
569 s = cast(AST.Dsymbol)parseTemplateDeclaration(true);
570 break;
571
572 default:
573 s = parseMixin();
574 break;
575 }
576 break;
577 }
578 case TOK.wchar_:
579 case TOK.dchar_:
580 case TOK.bool_:
581 case TOK.char_:
582 case TOK.int8:
583 case TOK.uns8:
584 case TOK.int16:
585 case TOK.uns16:
586 case TOK.int32:
587 case TOK.uns32:
588 case TOK.int64:
589 case TOK.uns64:
590 case TOK.int128:
591 case TOK.uns128:
592 case TOK.float32:
593 case TOK.float64:
594 case TOK.float80:
595 case TOK.imaginary32:
596 case TOK.imaginary64:
597 case TOK.imaginary80:
598 case TOK.complex32:
599 case TOK.complex64:
600 case TOK.complex80:
601 case TOK.void_:
602 case TOK.alias_:
603 case TOK.identifier:
604 case TOK.super_:
605 case TOK.typeof_:
606 case TOK.dot:
607 case TOK.vector:
608 case TOK.struct_:
609 case TOK.union_:
610 case TOK.class_:
611 case TOK.interface_:
612 case TOK.traits:
613 Ldeclaration:
614 a = parseDeclarations(false, pAttrs, pAttrs.comment);
615 if (a && a.dim)
616 *pLastDecl = (*a)[a.dim - 1];
617 break;
618
619 case TOK.this_:
620 if (peekNext() == TOK.dot)
621 goto Ldeclaration;
622 s = parseCtor(pAttrs);
623 break;
624
625 case TOK.tilde:
626 s = parseDtor(pAttrs);
627 break;
628
629 case TOK.invariant_:
630 const tv = peekNext();
631 if (tv == TOK.leftParenthesis || tv == TOK.leftCurly)
632 {
633 // invariant { statements... }
634 // invariant() { statements... }
635 // invariant (expression);
636 s = parseInvariant(pAttrs);
637 break;
638 }
639 error("invariant body expected, not `%s`", token.toChars());
640 goto Lerror;
641
642 case TOK.unittest_:
643 if (global.params.useUnitTests || global.params.doDocComments || global.params.doHdrGeneration)
644 {
645 s = parseUnitTest(pAttrs);
646 if (*pLastDecl)
647 (*pLastDecl).ddocUnittest = cast(AST.UnitTestDeclaration)s;
648 }
649 else
650 {
651 // Skip over unittest block by counting { }
652 Loc loc = token.loc;
653 int braces = 0;
654 while (1)
655 {
656 nextToken();
657 switch (token.value)
658 {
659 case TOK.leftCurly:
660 ++braces;
661 continue;
662
663 case TOK.rightCurly:
664 if (--braces)
665 continue;
666 nextToken();
667 break;
668
669 case TOK.endOfFile:
670 /* { */
671 error(loc, "closing `}` of unittest not found before end of file");
672 goto Lerror;
673
674 default:
675 continue;
676 }
677 break;
678 }
679 // Workaround 14894. Add an empty unittest declaration to keep
680 // the number of symbols in this scope independent of -unittest.
681 s = new AST.UnitTestDeclaration(loc, token.loc, STC.undefined_, null);
682 }
683 break;
684
685 case TOK.new_:
686 s = parseNew(pAttrs);
687 break;
688
689 case TOK.colon:
690 case TOK.leftCurly:
691 error("declaration expected, not `%s`", token.toChars());
692 goto Lerror;
693
694 case TOK.rightCurly:
695 case TOK.endOfFile:
696 if (once)
697 error("declaration expected, not `%s`", token.toChars());
698 return decldefs;
699
700 case TOK.static_:
701 {
702 const next = peekNext();
703 if (next == TOK.this_)
704 s = parseStaticCtor(pAttrs);
705 else if (next == TOK.tilde)
706 s = parseStaticDtor(pAttrs);
707 else if (next == TOK.assert_)
708 s = parseStaticAssert();
709 else if (next == TOK.if_)
710 {
711 const Loc loc = token.loc;
712 condition = parseStaticIfCondition();
713 AST.Dsymbols* athen;
714 if (token.value == TOK.colon)
715 athen = parseBlock(pLastDecl);
716 else
717 {
718 const lookingForElseSave = lookingForElse;
719 lookingForElse = token.loc;
720 athen = parseBlock(pLastDecl);
721 lookingForElse = lookingForElseSave;
722 }
723 AST.Dsymbols* aelse = null;
724 if (token.value == TOK.else_)
725 {
726 const elseloc = token.loc;
727 nextToken();
728 aelse = parseBlock(pLastDecl);
729 checkDanglingElse(elseloc);
730 }
731 s = new AST.StaticIfDeclaration(loc, condition, athen, aelse);
732 }
733 else if (next == TOK.import_)
734 {
735 a = parseImport();
736 // keep pLastDecl
737 }
738 else if (next == TOK.foreach_ || next == TOK.foreach_reverse_)
739 {
740 s = parseForeach!(AST.StaticForeachDeclaration)(token.loc, pLastDecl);
741 }
742 else
743 {
744 stc = STC.static_;
745 goto Lstc;
746 }
747 break;
748 }
749 case TOK.const_:
750 if (peekNext() == TOK.leftParenthesis)
751 goto Ldeclaration;
752 stc = STC.const_;
753 goto Lstc;
754
755 case TOK.immutable_:
756 if (peekNext() == TOK.leftParenthesis)
757 goto Ldeclaration;
758 stc = STC.immutable_;
759 goto Lstc;
760
761 case TOK.shared_:
762 {
763 const next = peekNext();
764 if (next == TOK.leftParenthesis)
765 goto Ldeclaration;
766 if (next == TOK.static_)
767 {
768 TOK next2 = peekNext2();
769 if (next2 == TOK.this_)
770 {
771 s = parseSharedStaticCtor(pAttrs);
772 break;
773 }
774 if (next2 == TOK.tilde)
775 {
776 s = parseSharedStaticDtor(pAttrs);
777 break;
778 }
779 }
780 stc = STC.shared_;
781 goto Lstc;
782 }
783 case TOK.inout_:
784 if (peekNext() == TOK.leftParenthesis)
785 goto Ldeclaration;
786 stc = STC.wild;
787 goto Lstc;
788
789 case TOK.final_:
790 stc = STC.final_;
791 goto Lstc;
792
793 case TOK.auto_:
794 stc = STC.auto_;
795 goto Lstc;
796
797 case TOK.scope_:
798 stc = STC.scope_;
799 goto Lstc;
800
801 case TOK.override_:
802 stc = STC.override_;
803 goto Lstc;
804
805 case TOK.abstract_:
806 stc = STC.abstract_;
807 goto Lstc;
808
809 case TOK.synchronized_:
810 stc = STC.synchronized_;
811 goto Lstc;
812
813 case TOK.nothrow_:
814 stc = STC.nothrow_;
815 goto Lstc;
816
817 case TOK.pure_:
818 stc = STC.pure_;
819 goto Lstc;
820
821 case TOK.ref_:
822 stc = STC.ref_;
823 goto Lstc;
824
825 case TOK.gshared:
826 stc = STC.gshared;
827 goto Lstc;
828
829 case TOK.at:
830 {
831 AST.Expressions* exps = null;
832 stc = parseAttribute(exps);
833 if (stc)
834 goto Lstc; // it's a predefined attribute
835 // no redundant/conflicting check for UDAs
836 pAttrs.udas = AST.UserAttributeDeclaration.concat(pAttrs.udas, exps);
837 goto Lautodecl;
838 }
839 Lstc:
840 pAttrs.storageClass = appendStorageClass(pAttrs.storageClass, stc);
841 nextToken();
842
843 Lautodecl:
844
845 /* Look for auto initializers:
846 * storage_class identifier = initializer;
847 * storage_class identifier(...) = initializer;
848 */
849 if (token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign))
850 {
851 a = parseAutoDeclarations(getStorageClass!AST(pAttrs), pAttrs.comment);
852 if (a && a.dim)
853 *pLastDecl = (*a)[a.dim - 1];
854 if (pAttrs.udas)
855 {
856 s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
857 pAttrs.udas = null;
858 }
859 break;
860 }
861
862 /* Look for return type inference for template functions.
863 */
864 Token* tk;
865 if (token.value == TOK.identifier && skipParens(peek(&token), &tk) && skipAttributes(tk, &tk) &&
866 (tk.value == TOK.leftParenthesis || tk.value == TOK.leftCurly || tk.value == TOK.in_ ||
867 tk.value == TOK.out_ || tk.value == TOK.do_ || tk.value == TOK.goesTo ||
868 tk.value == TOK.identifier && tk.ident == Id._body))
869 {
870 // @@@DEPRECATED@@@
871 // https://github.com/dlang/DIPs/blob/1f5959abe482b1f9094f6484a7d0a3ade77fc2fc/DIPs/accepted/DIP1003.md
872 // Deprecated in 2.097 - Can be removed from 2.117
873 // The deprecation period is longer than usual as `body`
874 // was quite widely used.
875 if (tk.value == TOK.identifier && tk.ident == Id._body)
876 deprecation("Usage of the `body` keyword is deprecated. Use `do` instead.");
877
878 a = parseDeclarations(true, pAttrs, pAttrs.comment);
879 if (a && a.dim)
880 *pLastDecl = (*a)[a.dim - 1];
881 if (pAttrs.udas)
882 {
883 s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
884 pAttrs.udas = null;
885 }
886 break;
887 }
888
889 a = parseBlock(pLastDecl, pAttrs);
890 auto stc2 = getStorageClass!AST(pAttrs);
891 if (stc2 != STC.undefined_)
892 {
893 s = new AST.StorageClassDeclaration(stc2, a);
894 }
895 if (pAttrs.udas)
896 {
897 if (s)
898 {
899 a = new AST.Dsymbols();
900 a.push(s);
901 }
902 s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
903 pAttrs.udas = null;
904 }
905 break;
906
907 case TOK.deprecated_:
908 {
909 stc |= STC.deprecated_;
910 if (!parseDeprecatedAttribute(pAttrs.depmsg))
911 goto Lstc;
912
913 a = parseBlock(pLastDecl, pAttrs);
914 s = new AST.DeprecatedDeclaration(pAttrs.depmsg, a);
915 pAttrs.depmsg = null;
916 break;
917 }
918 case TOK.leftBracket:
919 {
920 if (peekNext() == TOK.rightBracket)
921 error("empty attribute list is not allowed");
922 error("use `@(attributes)` instead of `[attributes]`");
923 AST.Expressions* exps = parseArguments();
924 // no redundant/conflicting check for UDAs
925
926 pAttrs.udas = AST.UserAttributeDeclaration.concat(pAttrs.udas, exps);
927 a = parseBlock(pLastDecl, pAttrs);
928 if (pAttrs.udas)
929 {
930 s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
931 pAttrs.udas = null;
932 }
933 break;
934 }
935 case TOK.extern_:
936 {
937 if (peekNext() != TOK.leftParenthesis)
938 {
939 stc = STC.extern_;
940 goto Lstc;
941 }
942
943 const linkLoc = token.loc;
944 auto res = parseLinkage();
945 if (pAttrs.link != LINK.default_)
946 {
947 if (pAttrs.link != res.link)
948 {
949 error("conflicting linkage `extern (%s)` and `extern (%s)`", AST.linkageToChars(pAttrs.link), AST.linkageToChars(res.link));
950 }
951 else if (res.idents || res.identExps || res.cppmangle != CPPMANGLE.def)
952 {
953 // Allow:
954 // extern(C++, foo) extern(C++, bar) void foo();
955 // to be equivalent with:
956 // extern(C++, foo.bar) void foo();
957 // Allow also:
958 // extern(C++, "ns") extern(C++, class) struct test {}
959 // extern(C++, class) extern(C++, "ns") struct test {}
960 }
961 else
962 error("redundant linkage `extern (%s)`", AST.linkageToChars(pAttrs.link));
963 }
964 pAttrs.link = res.link;
965 this.linkage = res.link;
966 this.linkLoc = linkLoc;
967 a = parseBlock(pLastDecl, pAttrs);
968 if (res.idents)
969 {
970 assert(res.link == LINK.cpp);
971 assert(res.idents.dim);
972 for (size_t i = res.idents.dim; i;)
973 {
974 Identifier id = (*res.idents)[--i];
975 if (s)
976 {
977 a = new AST.Dsymbols();
978 a.push(s);
979 }
980 s = new AST.Nspace(linkLoc, id, null, a);
981 }
982 pAttrs.link = LINK.default_;
983 }
984 else if (res.identExps)
985 {
986 assert(res.link == LINK.cpp);
987 assert(res.identExps.dim);
988 for (size_t i = res.identExps.dim; i;)
989 {
990 AST.Expression exp = (*res.identExps)[--i];
991 if (s)
992 {
993 a = new AST.Dsymbols();
994 a.push(s);
995 }
996 s = new AST.CPPNamespaceDeclaration(linkLoc, exp, a);
997 }
998 pAttrs.link = LINK.default_;
999 }
1000 else if (res.cppmangle != CPPMANGLE.def)
1001 {
1002 assert(res.link == LINK.cpp);
1003 s = new AST.CPPMangleDeclaration(linkLoc, res.cppmangle, a);
1004 }
1005 else if (pAttrs.link != LINK.default_)
1006 {
1007 s = new AST.LinkDeclaration(linkLoc, pAttrs.link, a);
1008 pAttrs.link = LINK.default_;
1009 }
1010 break;
1011 }
1012
1013 case TOK.private_:
1014 prot = AST.Visibility.Kind.private_;
1015 goto Lprot;
1016
1017 case TOK.package_:
1018 prot = AST.Visibility.Kind.package_;
1019 goto Lprot;
1020
1021 case TOK.protected_:
1022 prot = AST.Visibility.Kind.protected_;
1023 goto Lprot;
1024
1025 case TOK.public_:
1026 prot = AST.Visibility.Kind.public_;
1027 goto Lprot;
1028
1029 case TOK.export_:
1030 prot = AST.Visibility.Kind.export_;
1031 goto Lprot;
1032 Lprot:
1033 {
1034 if (pAttrs.visibility.kind != AST.Visibility.Kind.undefined)
1035 {
1036 if (pAttrs.visibility.kind != prot)
1037 error("conflicting visibility attribute `%s` and `%s`", AST.visibilityToChars(pAttrs.visibility.kind), AST.visibilityToChars(prot));
1038 else
1039 error("redundant visibility attribute `%s`", AST.visibilityToChars(prot));
1040 }
1041 pAttrs.visibility.kind = prot;
1042
1043 nextToken();
1044
1045 // optional qualified package identifier to bind
1046 // visibility to
1047 Identifier[] pkg_prot_idents;
1048 if (pAttrs.visibility.kind == AST.Visibility.Kind.package_ && token.value == TOK.leftParenthesis)
1049 {
1050 pkg_prot_idents = parseQualifiedIdentifier("protection package");
1051 if (pkg_prot_idents)
1052 check(TOK.rightParenthesis);
1053 else
1054 {
1055 while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
1056 nextToken();
1057 nextToken();
1058 break;
1059 }
1060 }
1061
1062 const attrloc = token.loc;
1063 a = parseBlock(pLastDecl, pAttrs);
1064 if (pAttrs.visibility.kind != AST.Visibility.Kind.undefined)
1065 {
1066 if (pAttrs.visibility.kind == AST.Visibility.Kind.package_ && pkg_prot_idents)
1067 s = new AST.VisibilityDeclaration(attrloc, pkg_prot_idents, a);
1068 else
1069 s = new AST.VisibilityDeclaration(attrloc, pAttrs.visibility, a);
1070
1071 pAttrs.visibility = AST.Visibility(AST.Visibility.Kind.undefined);
1072 }
1073 break;
1074 }
1075 case TOK.align_:
1076 {
1077 const attrLoc = token.loc;
1078
1079 nextToken();
1080
1081 AST.Expression e = null; // default
1082 if (token.value == TOK.leftParenthesis)
1083 {
1084 nextToken();
1085 e = parseAssignExp();
1086 check(TOK.rightParenthesis);
1087 }
1088
1089 if (pAttrs.setAlignment)
1090 {
1091 if (e)
1092 error("redundant alignment attribute `align(%s)`", e.toChars());
1093 else
1094 error("redundant alignment attribute `align`");
1095 }
1096
1097 pAttrs.setAlignment = true;
1098 pAttrs.ealign = e;
1099 a = parseBlock(pLastDecl, pAttrs);
1100 if (pAttrs.setAlignment)
1101 {
1102 s = new AST.AlignDeclaration(attrLoc, pAttrs.ealign, a);
1103 pAttrs.setAlignment = false;
1104 pAttrs.ealign = null;
1105 }
1106 break;
1107 }
1108 case TOK.pragma_:
1109 {
1110 AST.Expressions* args = null;
1111 const loc = token.loc;
1112
1113 nextToken();
1114 check(TOK.leftParenthesis);
1115 if (token.value != TOK.identifier)
1116 {
1117 error("`pragma(identifier)` expected");
1118 goto Lerror;
1119 }
1120 Identifier ident = token.ident;
1121 nextToken();
1122 if (token.value == TOK.comma && peekNext() != TOK.rightParenthesis)
1123 args = parseArguments(); // pragma(identifier, args...)
1124 else
1125 check(TOK.rightParenthesis); // pragma(identifier)
1126
1127 AST.Dsymbols* a2 = null;
1128 if (token.value == TOK.semicolon)
1129 {
1130 /* https://issues.dlang.org/show_bug.cgi?id=2354
1131 * Accept single semicolon as an empty
1132 * DeclarationBlock following attribute.
1133 *
1134 * Attribute DeclarationBlock
1135 * Pragma DeclDef
1136 * ;
1137 */
1138 nextToken();
1139 }
1140 else
1141 a2 = parseBlock(pLastDecl);
1142 s = new AST.PragmaDeclaration(loc, ident, args, a2);
1143 break;
1144 }
1145 case TOK.debug_:
1146 startloc = token.loc;
1147 nextToken();
1148 if (token.value == TOK.assign)
1149 {
1150 nextToken();
1151 if (token.value == TOK.identifier)
1152 s = new AST.DebugSymbol(token.loc, token.ident);
1153 else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
1154 s = new AST.DebugSymbol(token.loc, cast(uint)token.unsvalue);
1155 else
1156 {
1157 error("identifier or integer expected, not `%s`", token.toChars());
1158 s = null;
1159 }
1160 nextToken();
1161 if (token.value != TOK.semicolon)
1162 error("semicolon expected");
1163 nextToken();
1164 break;
1165 }
1166
1167 condition = parseDebugCondition();
1168 goto Lcondition;
1169
1170 case TOK.version_:
1171 startloc = token.loc;
1172 nextToken();
1173 if (token.value == TOK.assign)
1174 {
1175 nextToken();
1176 if (token.value == TOK.identifier)
1177 s = new AST.VersionSymbol(token.loc, token.ident);
1178 else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
1179 s = new AST.VersionSymbol(token.loc, cast(uint)token.unsvalue);
1180 else
1181 {
1182 error("identifier or integer expected, not `%s`", token.toChars());
1183 s = null;
1184 }
1185 nextToken();
1186 if (token.value != TOK.semicolon)
1187 error("semicolon expected");
1188 nextToken();
1189 break;
1190 }
1191 condition = parseVersionCondition();
1192 goto Lcondition;
1193
1194 Lcondition:
1195 {
1196 AST.Dsymbols* athen;
1197 if (token.value == TOK.colon)
1198 athen = parseBlock(pLastDecl);
1199 else
1200 {
1201 const lookingForElseSave = lookingForElse;
1202 lookingForElse = token.loc;
1203 athen = parseBlock(pLastDecl);
1204 lookingForElse = lookingForElseSave;
1205 }
1206 AST.Dsymbols* aelse = null;
1207 if (token.value == TOK.else_)
1208 {
1209 const elseloc = token.loc;
1210 nextToken();
1211 aelse = parseBlock(pLastDecl);
1212 checkDanglingElse(elseloc);
1213 }
1214 s = new AST.ConditionalDeclaration(startloc, condition, athen, aelse);
1215 break;
1216 }
1217 case TOK.semicolon:
1218 // empty declaration
1219 //error("empty declaration");
1220 nextToken();
1221 continue;
1222
1223 default:
1224 error("declaration expected, not `%s`", token.toChars());
1225 Lerror:
1226 while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
1227 nextToken();
1228 nextToken();
1229 s = null;
1230 continue;
1231 }
1232
1233 if (s)
1234 {
1235 if (!s.isAttribDeclaration())
1236 *pLastDecl = s;
1237 decldefs.push(s);
1238 addComment(s, pAttrs.comment);
1239 }
1240 else if (a && a.dim)
1241 {
1242 decldefs.append(a);
1243 }
1244 }
1245 while (!once);
1246
1247 linkage = linksave;
1248
1249 return decldefs;
1250 }
1251
1252 /*****************************************
1253 * Parse auto declarations of the form:
1254 * storageClass ident = init, ident = init, ... ;
1255 * and return the array of them.
1256 * Starts with token on the first ident.
1257 * Ends with scanner past closing ';'
1258 */
1259 private AST.Dsymbols* parseAutoDeclarations(StorageClass storageClass, const(char)* comment)
1260 {
1261 //printf("parseAutoDeclarations\n");
1262 auto a = new AST.Dsymbols();
1263
1264 while (1)
1265 {
1266 const loc = token.loc;
1267 Identifier ident = token.ident;
1268 nextToken(); // skip over ident
1269
1270 AST.TemplateParameters* tpl = null;
1271 if (token.value == TOK.leftParenthesis)
1272 tpl = parseTemplateParameterList();
1273
1274 check(TOK.assign); // skip over '='
1275 AST.Initializer _init = parseInitializer();
1276 auto v = new AST.VarDeclaration(loc, null, ident, _init, storageClass);
1277
1278 AST.Dsymbol s = v;
1279 if (tpl)
1280 {
1281 auto a2 = new AST.Dsymbols();
1282 a2.push(v);
1283 auto tempdecl = new AST.TemplateDeclaration(loc, ident, tpl, null, a2, 0);
1284 s = tempdecl;
1285 }
1286 a.push(s);
1287 switch (token.value)
1288 {
1289 case TOK.semicolon:
1290 nextToken();
1291 addComment(s, comment);
1292 break;
1293
1294 case TOK.comma:
1295 nextToken();
1296 if (!(token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign)))
1297 {
1298 error("identifier expected following comma");
1299 break;
1300 }
1301 addComment(s, comment);
1302 continue;
1303
1304 default:
1305 error("semicolon expected following auto declaration, not `%s`", token.toChars());
1306 break;
1307 }
1308 break;
1309 }
1310 return a;
1311 }
1312
1313 /********************************************
1314 * Parse declarations after an align, visibility, or extern decl.
1315 */
1316 private AST.Dsymbols* parseBlock(AST.Dsymbol* pLastDecl, PrefixAttributes!AST* pAttrs = null)
1317 {
1318 AST.Dsymbols* a = null;
1319
1320 //printf("parseBlock()\n");
1321 switch (token.value)
1322 {
1323 case TOK.semicolon:
1324 error("declaration expected following attribute, not `;`");
1325 nextToken();
1326 break;
1327
1328 case TOK.endOfFile:
1329 error("declaration expected following attribute, not end of file");
1330 break;
1331
1332 case TOK.leftCurly:
1333 {
1334 const lookingForElseSave = lookingForElse;
1335 lookingForElse = Loc();
1336
1337 nextToken();
1338 a = parseDeclDefs(0, pLastDecl);
1339 if (token.value != TOK.rightCurly)
1340 {
1341 /* { */
1342 error("matching `}` expected, not `%s`", token.toChars());
1343 }
1344 else
1345 nextToken();
1346 lookingForElse = lookingForElseSave;
1347 break;
1348 }
1349 case TOK.colon:
1350 nextToken();
1351 a = parseDeclDefs(0, pLastDecl); // grab declarations up to closing curly bracket
1352 break;
1353
1354 default:
1355 a = parseDeclDefs(1, pLastDecl, pAttrs);
1356 break;
1357 }
1358 return a;
1359 }
1360
1361 /**
1362 * Provide an error message if `added` contains storage classes which are
1363 * redundant with those in `orig`; otherwise, return the combination.
1364 *
1365 * Params:
1366 * orig = The already applied storage class.
1367 * added = The new storage class to add to `orig`.
1368 *
1369 * Returns:
1370 * The combination of both storage classes (`orig | added`).
1371 */
1372 private StorageClass appendStorageClass(StorageClass orig, StorageClass added)
1373 {
1374 if (orig & added)
1375 {
1376 OutBuffer buf;
1377 AST.stcToBuffer(&buf, added);
1378 error("redundant attribute `%s`", buf.peekChars());
1379 return orig | added;
1380 }
1381
1382 const Redundant = (STC.const_ | STC.scope_ |
1383 (global.params.previewIn ? STC.ref_ : 0));
1384 orig |= added;
1385
1386 if ((orig & STC.in_) && (added & Redundant))
1387 {
1388 if (added & STC.const_)
1389 error("attribute `const` is redundant with previously-applied `in`");
1390 else if (global.params.previewIn)
1391 {
1392 error("attribute `%s` is redundant with previously-applied `in`",
1393 (orig & STC.scope_) ? "scope".ptr : "ref".ptr);
1394 }
1395 else
1396 error("attribute `scope` cannot be applied with `in`, use `-preview=in` instead");
1397 return orig;
1398 }
1399
1400 if ((added & STC.in_) && (orig & Redundant))
1401 {
1402 if (orig & STC.const_)
1403 error("attribute `in` cannot be added after `const`: remove `const`");
1404 else if (global.params.previewIn)
1405 {
1406 // Windows `printf` does not support `%1$s`
1407 const(char*) stc_str = (orig & STC.scope_) ? "scope".ptr : "ref".ptr;
1408 error("attribute `in` cannot be added after `%s`: remove `%s`",
1409 stc_str, stc_str);
1410 }
1411 else
1412 error("attribute `in` cannot be added after `scope`: remove `scope` and use `-preview=in`");
1413 return orig;
1414 }
1415
1416 if (added & (STC.const_ | STC.immutable_ | STC.manifest))
1417 {
1418 StorageClass u = orig & (STC.const_ | STC.immutable_ | STC.manifest);
1419 if (u & (u - 1))
1420 error("conflicting attribute `%s`", Token.toChars(token.value));
1421 }
1422 if (added & (STC.gshared | STC.shared_ | STC.tls))
1423 {
1424 StorageClass u = orig & (STC.gshared | STC.shared_ | STC.tls);
1425 if (u & (u - 1))
1426 error("conflicting attribute `%s`", Token.toChars(token.value));
1427 }
1428 if (added & STC.safeGroup)
1429 {
1430 StorageClass u = orig & STC.safeGroup;
1431 if (u & (u - 1))
1432 error("conflicting attribute `@%s`", token.toChars());
1433 }
1434
1435 return orig;
1436 }
1437
1438 /***********************************************
1439 * Parse attribute(s), lexer is on '@'.
1440 *
1441 * Attributes can be builtin (e.g. `@safe`, `@nogc`, etc...),
1442 * or be user-defined (UDAs). In the former case, we return the storage
1443 * class via the return value, while in thelater case we return `0`
1444 * and set `pudas`.
1445 *
1446 * Params:
1447 * pudas = An array of UDAs to append to
1448 *
1449 * Returns:
1450 * If the attribute is builtin, the return value will be non-zero.
1451 * Otherwise, 0 is returned, and `pudas` will be appended to.
1452 */
1453 private StorageClass parseAttribute(ref AST.Expressions* udas)
1454 {
1455 nextToken();
1456 if (token.value == TOK.identifier)
1457 {
1458 // If we find a builtin attribute, we're done, return immediately.
1459 if (StorageClass stc = isBuiltinAtAttribute(token.ident))
1460 return stc;
1461
1462 // Allow identifier, template instantiation, or function call
1463 // for `@Argument` (single UDA) form.
1464 AST.Expression exp = parsePrimaryExp();
1465 if (token.value == TOK.leftParenthesis)
1466 {
1467 const loc = token.loc;
1468 exp = new AST.CallExp(loc, exp, parseArguments());
1469 }
1470
1471 if (udas is null)
1472 udas = new AST.Expressions();
1473 udas.push(exp);
1474 return 0;
1475 }
1476
1477 if (token.value == TOK.leftParenthesis)
1478 {
1479 // Multi-UDAs ( `@( ArgumentList )`) form, concatenate with existing
1480 if (peekNext() == TOK.rightParenthesis)
1481 error("empty attribute list is not allowed");
1482 udas = AST.UserAttributeDeclaration.concat(udas, parseArguments());
1483 return 0;
1484 }
1485
1486 if (token.isKeyword())
1487 error("`%s` is a keyword, not an `@` attribute", token.toChars());
1488 else
1489 error("`@identifier` or `@(ArgumentList)` expected, not `@%s`", token.toChars());
1490
1491 return 0;
1492 }
1493
1494 /***********************************************
1495 * Parse const/immutable/shared/inout/nothrow/pure postfix
1496 */
1497 private StorageClass parsePostfix(StorageClass storageClass, AST.Expressions** pudas)
1498 {
1499 while (1)
1500 {
1501 StorageClass stc;
1502 switch (token.value)
1503 {
1504 case TOK.const_:
1505 stc = STC.const_;
1506 break;
1507
1508 case TOK.immutable_:
1509 stc = STC.immutable_;
1510 break;
1511
1512 case TOK.shared_:
1513 stc = STC.shared_;
1514 break;
1515
1516 case TOK.inout_:
1517 stc = STC.wild;
1518 break;
1519
1520 case TOK.nothrow_:
1521 stc = STC.nothrow_;
1522 break;
1523
1524 case TOK.pure_:
1525 stc = STC.pure_;
1526 break;
1527
1528 case TOK.return_:
1529 stc = STC.return_;
1530 break;
1531
1532 case TOK.scope_:
1533 stc = STC.scope_;
1534 break;
1535
1536 case TOK.at:
1537 {
1538 AST.Expressions* udas = null;
1539 stc = parseAttribute(udas);
1540 if (udas)
1541 {
1542 if (pudas)
1543 *pudas = AST.UserAttributeDeclaration.concat(*pudas, udas);
1544 else
1545 {
1546 // Disallow:
1547 // void function() @uda fp;
1548 // () @uda { return 1; }
1549 error("user-defined attributes cannot appear as postfixes");
1550 }
1551 continue;
1552 }
1553 break;
1554 }
1555 default:
1556 return storageClass;
1557 }
1558 storageClass = appendStorageClass(storageClass, stc);
1559 nextToken();
1560 }
1561 }
1562
1563 private StorageClass parseTypeCtor()
1564 {
1565 StorageClass storageClass = STC.undefined_;
1566
1567 while (1)
1568 {
1569 if (peekNext() == TOK.leftParenthesis)
1570 return storageClass;
1571
1572 StorageClass stc;
1573 switch (token.value)
1574 {
1575 case TOK.const_:
1576 stc = STC.const_;
1577 break;
1578
1579 case TOK.immutable_:
1580 stc = STC.immutable_;
1581 break;
1582
1583 case TOK.shared_:
1584 stc = STC.shared_;
1585 break;
1586
1587 case TOK.inout_:
1588 stc = STC.wild;
1589 break;
1590
1591 default:
1592 return storageClass;
1593 }
1594 storageClass = appendStorageClass(storageClass, stc);
1595 nextToken();
1596 }
1597 }
1598
1599 /**************************************
1600 * Parse constraint.
1601 * Constraint is of the form:
1602 * if ( ConstraintExpression )
1603 */
1604 private AST.Expression parseConstraint()
1605 {
1606 AST.Expression e = null;
1607 if (token.value == TOK.if_)
1608 {
1609 nextToken(); // skip over 'if'
1610 check(TOK.leftParenthesis);
1611 e = parseExpression();
1612 check(TOK.rightParenthesis);
1613 }
1614 return e;
1615 }
1616
1617 /**************************************
1618 * Parse a TemplateDeclaration.
1619 */
1620 private AST.TemplateDeclaration parseTemplateDeclaration(bool ismixin = false)
1621 {
1622 AST.TemplateDeclaration tempdecl;
1623 Identifier id;
1624 AST.TemplateParameters* tpl;
1625 AST.Dsymbols* decldefs;
1626 AST.Expression constraint = null;
1627 const loc = token.loc;
1628
1629 nextToken();
1630 if (token.value != TOK.identifier)
1631 {
1632 error("identifier expected following `template`");
1633 goto Lerr;
1634 }
1635 id = token.ident;
1636 nextToken();
1637 tpl = parseTemplateParameterList();
1638 if (!tpl)
1639 goto Lerr;
1640
1641 constraint = parseConstraint();
1642
1643 if (token.value != TOK.leftCurly)
1644 {
1645 error("members of template declaration expected");
1646 goto Lerr;
1647 }
1648 decldefs = parseBlock(null);
1649
1650 tempdecl = new AST.TemplateDeclaration(loc, id, tpl, constraint, decldefs, ismixin);
1651 return tempdecl;
1652
1653 Lerr:
1654 return null;
1655 }
1656
1657 /******************************************
1658 * Parse template parameter list.
1659 * Input:
1660 * flag 0: parsing "( list )"
1661 * 1: parsing non-empty "list $(RPAREN)"
1662 */
1663 private AST.TemplateParameters* parseTemplateParameterList(int flag = 0)
1664 {
1665 auto tpl = new AST.TemplateParameters();
1666
1667 if (!flag && token.value != TOK.leftParenthesis)
1668 {
1669 error("parenthesized template parameter list expected following template identifier");
1670 goto Lerr;
1671 }
1672 nextToken();
1673
1674 // Get array of TemplateParameters
1675 if (flag || token.value != TOK.rightParenthesis)
1676 {
1677 while (token.value != TOK.rightParenthesis)
1678 {
1679 AST.TemplateParameter tp;
1680 Loc loc;
1681 Identifier tp_ident = null;
1682 AST.Type tp_spectype = null;
1683 AST.Type tp_valtype = null;
1684 AST.Type tp_defaulttype = null;
1685 AST.Expression tp_specvalue = null;
1686 AST.Expression tp_defaultvalue = null;
1687
1688 // Get TemplateParameter
1689
1690 // First, look ahead to see if it is a TypeParameter or a ValueParameter
1691 const tv = peekNext();
1692 if (token.value == TOK.alias_)
1693 {
1694 // AliasParameter
1695 nextToken();
1696 loc = token.loc; // todo
1697 AST.Type spectype = null;
1698 if (isDeclaration(&token, NeedDeclaratorId.must, TOK.reserved, null))
1699 {
1700 spectype = parseType(&tp_ident);
1701 }
1702 else
1703 {
1704 if (token.value != TOK.identifier)
1705 {
1706 error("identifier expected for template `alias` parameter");
1707 goto Lerr;
1708 }
1709 tp_ident = token.ident;
1710 nextToken();
1711 }
1712 RootObject spec = null;
1713 if (token.value == TOK.colon) // : Type
1714 {
1715 nextToken();
1716 if (isDeclaration(&token, NeedDeclaratorId.no, TOK.reserved, null))
1717 spec = parseType();
1718 else
1719 spec = parseCondExp();
1720 }
1721 RootObject def = null;
1722 if (token.value == TOK.assign) // = Type
1723 {
1724 nextToken();
1725 if (isDeclaration(&token, NeedDeclaratorId.no, TOK.reserved, null))
1726 def = parseType();
1727 else
1728 def = parseCondExp();
1729 }
1730 tp = new AST.TemplateAliasParameter(loc, tp_ident, spectype, spec, def);
1731 }
1732 else if (tv == TOK.colon || tv == TOK.assign || tv == TOK.comma || tv == TOK.rightParenthesis)
1733 {
1734 // TypeParameter
1735 if (token.value != TOK.identifier)
1736 {
1737 error("identifier expected for template type parameter");
1738 goto Lerr;
1739 }
1740 loc = token.loc;
1741 tp_ident = token.ident;
1742 nextToken();
1743 if (token.value == TOK.colon) // : Type
1744 {
1745 nextToken();
1746 tp_spectype = parseType();
1747 }
1748 if (token.value == TOK.assign) // = Type
1749 {
1750 nextToken();
1751 tp_defaulttype = parseType();
1752 }
1753 tp = new AST.TemplateTypeParameter(loc, tp_ident, tp_spectype, tp_defaulttype);
1754 }
1755 else if (token.value == TOK.identifier && tv == TOK.dotDotDot)
1756 {
1757 // ident...
1758 loc = token.loc;
1759 tp_ident = token.ident;
1760 nextToken();
1761 nextToken();
1762 tp = new AST.TemplateTupleParameter(loc, tp_ident);
1763 }
1764 else if (token.value == TOK.this_)
1765 {
1766 // ThisParameter
1767 nextToken();
1768 if (token.value != TOK.identifier)
1769 {
1770 error("identifier expected for template `this` parameter");
1771 goto Lerr;
1772 }
1773 loc = token.loc;
1774 tp_ident = token.ident;
1775 nextToken();
1776 if (token.value == TOK.colon) // : Type
1777 {
1778 nextToken();
1779 tp_spectype = parseType();
1780 }
1781 if (token.value == TOK.assign) // = Type
1782 {
1783 nextToken();
1784 tp_defaulttype = parseType();
1785 }
1786 tp = new AST.TemplateThisParameter(loc, tp_ident, tp_spectype, tp_defaulttype);
1787 }
1788 else
1789 {
1790 // ValueParameter
1791 loc = token.loc; // todo
1792 tp_valtype = parseType(&tp_ident);
1793 if (!tp_ident)
1794 {
1795 error("identifier expected for template value parameter");
1796 tp_ident = Identifier.idPool("error");
1797 }
1798 if (token.value == TOK.colon) // : CondExpression
1799 {
1800 nextToken();
1801 tp_specvalue = parseCondExp();
1802 }
1803 if (token.value == TOK.assign) // = CondExpression
1804 {
1805 nextToken();
1806 tp_defaultvalue = parseDefaultInitExp();
1807 }
1808 tp = new AST.TemplateValueParameter(loc, tp_ident, tp_valtype, tp_specvalue, tp_defaultvalue);
1809 }
1810 tpl.push(tp);
1811 if (token.value != TOK.comma)
1812 break;
1813 nextToken();
1814 }
1815 }
1816 check(TOK.rightParenthesis);
1817
1818 Lerr:
1819 return tpl;
1820 }
1821
1822 /******************************************
1823 * Parse template mixin.
1824 * mixin Foo;
1825 * mixin Foo!(args);
1826 * mixin a.b.c!(args).Foo!(args);
1827 * mixin Foo!(args) identifier;
1828 * mixin typeof(expr).identifier!(args);
1829 */
1830 private AST.Dsymbol parseMixin()
1831 {
1832 AST.TemplateMixin tm;
1833 Identifier id;
1834 AST.Objects* tiargs;
1835
1836 //printf("parseMixin()\n");
1837 const locMixin = token.loc;
1838 nextToken(); // skip 'mixin'
1839
1840 auto loc = token.loc;
1841 AST.TypeQualified tqual = null;
1842 if (token.value == TOK.dot)
1843 {
1844 id = Id.empty;
1845 }
1846 else
1847 {
1848 if (token.value == TOK.typeof_)
1849 {
1850 tqual = parseTypeof();
1851 check(TOK.dot);
1852 }
1853 if (token.value != TOK.identifier)
1854 {
1855 error("identifier expected, not `%s`", token.toChars());
1856 id = Id.empty;
1857 }
1858 else
1859 id = token.ident;
1860 nextToken();
1861 }
1862
1863 while (1)
1864 {
1865 tiargs = null;
1866 if (token.value == TOK.not)
1867 {
1868 tiargs = parseTemplateArguments();
1869 }
1870
1871 if (tiargs && token.value == TOK.dot)
1872 {
1873 auto tempinst = new AST.TemplateInstance(loc, id, tiargs);
1874 if (!tqual)
1875 tqual = new AST.TypeInstance(loc, tempinst);
1876 else
1877 tqual.addInst(tempinst);
1878 tiargs = null;
1879 }
1880 else
1881 {
1882 if (!tqual)
1883 tqual = new AST.TypeIdentifier(loc, id);
1884 else
1885 tqual.addIdent(id);
1886 }
1887
1888 if (token.value != TOK.dot)
1889 break;
1890
1891 nextToken();
1892 if (token.value != TOK.identifier)
1893 {
1894 error("identifier expected following `.` instead of `%s`", token.toChars());
1895 break;
1896 }
1897 loc = token.loc;
1898 id = token.ident;
1899 nextToken();
1900 }
1901
1902 id = null;
1903 if (token.value == TOK.identifier)
1904 {
1905 id = token.ident;
1906 nextToken();
1907 }
1908
1909 tm = new AST.TemplateMixin(locMixin, id, tqual, tiargs);
1910 if (token.value != TOK.semicolon)
1911 error("`;` expected after `mixin`");
1912 nextToken();
1913
1914 return tm;
1915 }
1916
1917 /******************************************
1918 * Parse template arguments.
1919 * Input:
1920 * current token is opening '!'
1921 * Output:
1922 * current token is one after closing '$(RPAREN)'
1923 */
1924 private AST.Objects* parseTemplateArguments()
1925 {
1926 AST.Objects* tiargs;
1927
1928 nextToken();
1929 if (token.value == TOK.leftParenthesis)
1930 {
1931 // ident!(template_arguments)
1932 tiargs = parseTemplateArgumentList();
1933 }
1934 else
1935 {
1936 // ident!template_argument
1937 tiargs = parseTemplateSingleArgument();
1938 }
1939 if (token.value == TOK.not)
1940 {
1941 TOK tok = peekNext();
1942 if (tok != TOK.is_ && tok != TOK.in_)
1943 {
1944 error("multiple ! arguments are not allowed");
1945 Lagain:
1946 nextToken();
1947 if (token.value == TOK.leftParenthesis)
1948 parseTemplateArgumentList();
1949 else
1950 parseTemplateSingleArgument();
1951 if (token.value == TOK.not && (tok = peekNext()) != TOK.is_ && tok != TOK.in_)
1952 goto Lagain;
1953 }
1954 }
1955 return tiargs;
1956 }
1957
1958 /******************************************
1959 * Parse template argument list.
1960 * Input:
1961 * current token is opening '$(LPAREN)',
1962 * or ',' for __traits
1963 * Output:
1964 * current token is one after closing '$(RPAREN)'
1965 */
1966 private AST.Objects* parseTemplateArgumentList()
1967 {
1968 //printf("Parser::parseTemplateArgumentList()\n");
1969 auto tiargs = new AST.Objects();
1970 TOK endtok = TOK.rightParenthesis;
1971 assert(token.value == TOK.leftParenthesis || token.value == TOK.comma);
1972 nextToken();
1973
1974 // Get TemplateArgumentList
1975 while (token.value != endtok)
1976 {
1977 tiargs.push(parseTypeOrAssignExp());
1978 if (token.value != TOK.comma)
1979 break;
1980 nextToken();
1981 }
1982 check(endtok, "template argument list");
1983 return tiargs;
1984 }
1985
1986 /***************************************
1987 * Parse a Type or an Expression
1988 * Returns:
1989 * RootObject representing the AST
1990 */
1991 RootObject parseTypeOrAssignExp(TOK endtoken = TOK.reserved)
1992 {
1993 return isDeclaration(&token, NeedDeclaratorId.no, endtoken, null)
1994 ? parseType() // argument is a type
1995 : parseAssignExp(); // argument is an expression
1996 }
1997
1998 /*****************************
1999 * Parse single template argument, to support the syntax:
2000 * foo!arg
2001 * Input:
2002 * current token is the arg
2003 */
2004 private AST.Objects* parseTemplateSingleArgument()
2005 {
2006 //printf("parseTemplateSingleArgument()\n");
2007 auto tiargs = new AST.Objects();
2008 AST.Type ta;
2009 switch (token.value)
2010 {
2011 case TOK.identifier:
2012 ta = new AST.TypeIdentifier(token.loc, token.ident);
2013 goto LabelX;
2014
2015 case TOK.vector:
2016 ta = parseVector();
2017 goto LabelX;
2018
2019 case TOK.void_:
2020 ta = AST.Type.tvoid;
2021 goto LabelX;
2022
2023 case TOK.int8:
2024 ta = AST.Type.tint8;
2025 goto LabelX;
2026
2027 case TOK.uns8:
2028 ta = AST.Type.tuns8;
2029 goto LabelX;
2030
2031 case TOK.int16:
2032 ta = AST.Type.tint16;
2033 goto LabelX;
2034
2035 case TOK.uns16:
2036 ta = AST.Type.tuns16;
2037 goto LabelX;
2038
2039 case TOK.int32:
2040 ta = AST.Type.tint32;
2041 goto LabelX;
2042
2043 case TOK.uns32:
2044 ta = AST.Type.tuns32;
2045 goto LabelX;
2046
2047 case TOK.int64:
2048 ta = AST.Type.tint64;
2049 goto LabelX;
2050
2051 case TOK.uns64:
2052 ta = AST.Type.tuns64;
2053 goto LabelX;
2054
2055 case TOK.int128:
2056 ta = AST.Type.tint128;
2057 goto LabelX;
2058
2059 case TOK.uns128:
2060 ta = AST.Type.tuns128;
2061 goto LabelX;
2062
2063 case TOK.float32:
2064 ta = AST.Type.tfloat32;
2065 goto LabelX;
2066
2067 case TOK.float64:
2068 ta = AST.Type.tfloat64;
2069 goto LabelX;
2070
2071 case TOK.float80:
2072 ta = AST.Type.tfloat80;
2073 goto LabelX;
2074
2075 case TOK.imaginary32:
2076 ta = AST.Type.timaginary32;
2077 goto LabelX;
2078
2079 case TOK.imaginary64:
2080 ta = AST.Type.timaginary64;
2081 goto LabelX;
2082
2083 case TOK.imaginary80:
2084 ta = AST.Type.timaginary80;
2085 goto LabelX;
2086
2087 case TOK.complex32:
2088 ta = AST.Type.tcomplex32;
2089 goto LabelX;
2090
2091 case TOK.complex64:
2092 ta = AST.Type.tcomplex64;
2093 goto LabelX;
2094
2095 case TOK.complex80:
2096 ta = AST.Type.tcomplex80;
2097 goto LabelX;
2098
2099 case TOK.bool_:
2100 ta = AST.Type.tbool;
2101 goto LabelX;
2102
2103 case TOK.char_:
2104 ta = AST.Type.tchar;
2105 goto LabelX;
2106
2107 case TOK.wchar_:
2108 ta = AST.Type.twchar;
2109 goto LabelX;
2110
2111 case TOK.dchar_:
2112 ta = AST.Type.tdchar;
2113 goto LabelX;
2114 LabelX:
2115 tiargs.push(ta);
2116 nextToken();
2117 break;
2118
2119 case TOK.int32Literal:
2120 case TOK.uns32Literal:
2121 case TOK.int64Literal:
2122 case TOK.uns64Literal:
2123 case TOK.int128Literal:
2124 case TOK.uns128Literal:
2125 case TOK.float32Literal:
2126 case TOK.float64Literal:
2127 case TOK.float80Literal:
2128 case TOK.imaginary32Literal:
2129 case TOK.imaginary64Literal:
2130 case TOK.imaginary80Literal:
2131 case TOK.null_:
2132 case TOK.true_:
2133 case TOK.false_:
2134 case TOK.charLiteral:
2135 case TOK.wcharLiteral:
2136 case TOK.dcharLiteral:
2137 case TOK.string_:
2138 case TOK.hexadecimalString:
2139 case TOK.file:
2140 case TOK.fileFullPath:
2141 case TOK.line:
2142 case TOK.moduleString:
2143 case TOK.functionString:
2144 case TOK.prettyFunction:
2145 case TOK.this_:
2146 {
2147 // Template argument is an expression
2148 AST.Expression ea = parsePrimaryExp();
2149 tiargs.push(ea);
2150 break;
2151 }
2152 default:
2153 error("template argument expected following `!`");
2154 break;
2155 }
2156 return tiargs;
2157 }
2158
2159 /**********************************
2160 * Parse a static assertion.
2161 * Current token is 'static'.
2162 */
2163 private AST.StaticAssert parseStaticAssert()
2164 {
2165 const loc = token.loc;
2166 AST.Expression exp;
2167 AST.Expression msg = null;
2168
2169 //printf("parseStaticAssert()\n");
2170 nextToken();
2171 nextToken();
2172 check(TOK.leftParenthesis);
2173 exp = parseAssignExp();
2174 if (token.value == TOK.comma)
2175 {
2176 nextToken();
2177 if (token.value != TOK.rightParenthesis)
2178 {
2179 msg = parseAssignExp();
2180 if (token.value == TOK.comma)
2181 nextToken();
2182 }
2183 }
2184 check(TOK.rightParenthesis);
2185 check(TOK.semicolon);
2186 return new AST.StaticAssert(loc, exp, msg);
2187 }
2188
2189 /***********************************
2190 * Parse typeof(expression).
2191 * Current token is on the 'typeof'.
2192 */
2193 private AST.TypeQualified parseTypeof()
2194 {
2195 AST.TypeQualified t;
2196 const loc = token.loc;
2197
2198 nextToken();
2199 check(TOK.leftParenthesis);
2200 if (token.value == TOK.return_) // typeof(return)
2201 {
2202 nextToken();
2203 t = new AST.TypeReturn(loc);
2204 }
2205 else
2206 {
2207 AST.Expression exp = parseExpression(); // typeof(expression)
2208 t = new AST.TypeTypeof(loc, exp);
2209 }
2210 check(TOK.rightParenthesis);
2211 return t;
2212 }
2213
2214 /***********************************
2215 * Parse __vector(type).
2216 * Current token is on the '__vector'.
2217 */
2218 private AST.Type parseVector()
2219 {
2220 nextToken();
2221 check(TOK.leftParenthesis);
2222 AST.Type tb = parseType();
2223 check(TOK.rightParenthesis);
2224 return new AST.TypeVector(tb);
2225 }
2226
2227 /***********************************
2228 * Parse:
2229 * extern (linkage)
2230 * extern (C++, namespaces)
2231 * extern (C++, "namespace", "namespaces", ...)
2232 * extern (C++, (StringExp))
2233 * The parser is on the 'extern' token.
2234 */
2235 private ParsedLinkage!(AST) parseLinkage()
2236 {
2237 ParsedLinkage!(AST) result;
2238 nextToken();
2239 assert(token.value == TOK.leftParenthesis);
2240 nextToken();
2241 ParsedLinkage!(AST) returnLinkage(LINK link)
2242 {
2243 check(TOK.rightParenthesis);
2244 result.link = link;
2245 return result;
2246 }
2247 ParsedLinkage!(AST) invalidLinkage()
2248 {
2249 error("valid linkage identifiers are `D`, `C`, `C++`, `Objective-C`, `Windows`, `System`");
2250 return returnLinkage(LINK.d);
2251 }
2252
2253 if (token.value != TOK.identifier)
2254 return returnLinkage(LINK.d);
2255
2256 Identifier id = token.ident;
2257 nextToken();
2258 if (id == Id.Windows)
2259 return returnLinkage(LINK.windows);
2260 else if (id == Id.D)
2261 return returnLinkage(LINK.d);
2262 else if (id == Id.System)
2263 return returnLinkage(LINK.system);
2264 else if (id == Id.Objective) // Looking for tokens "Objective-C"
2265 {
2266 if (token.value != TOK.min)
2267 return invalidLinkage();
2268
2269 nextToken();
2270 if (token.ident != Id.C)
2271 return invalidLinkage();
2272
2273 nextToken();
2274 return returnLinkage(LINK.objc);
2275 }
2276 else if (id != Id.C)
2277 return invalidLinkage();
2278
2279 if (token.value != TOK.plusPlus)
2280 return returnLinkage(LINK.c);
2281
2282 nextToken();
2283 if (token.value != TOK.comma) // , namespaces or class or struct
2284 return returnLinkage(LINK.cpp);
2285
2286 nextToken();
2287
2288 if (token.value == TOK.rightParenthesis)
2289 return returnLinkage(LINK.cpp); // extern(C++,)
2290
2291 if (token.value == TOK.class_ || token.value == TOK.struct_)
2292 {
2293 result.cppmangle = token.value == TOK.class_ ? CPPMANGLE.asClass : CPPMANGLE.asStruct;
2294 nextToken();
2295 }
2296 else if (token.value == TOK.identifier) // named scope namespace
2297 {
2298 result.idents = new AST.Identifiers();
2299 while (1)
2300 {
2301 Identifier idn = token.ident;
2302 result.idents.push(idn);
2303 nextToken();
2304 if (token.value == TOK.dot)
2305 {
2306 nextToken();
2307 if (token.value == TOK.identifier)
2308 continue;
2309 error("identifier expected for C++ namespace");
2310 result.idents = null; // error occurred, invalidate list of elements.
2311 }
2312 break;
2313 }
2314 }
2315 else // non-scoped StringExp namespace
2316 {
2317 result.identExps = new AST.Expressions();
2318 while (1)
2319 {
2320 result.identExps.push(parseCondExp());
2321 if (token.value != TOK.comma)
2322 break;
2323 nextToken();
2324 // Allow trailing commas as done for argument lists, arrays, ...
2325 if (token.value == TOK.rightParenthesis)
2326 break;
2327 }
2328 }
2329 return returnLinkage(LINK.cpp);
2330 }
2331
2332 /***********************************
2333 * Parse ident1.ident2.ident3
2334 *
2335 * Params:
2336 * entity = what qualified identifier is expected to resolve into.
2337 * Used only for better error message
2338 *
2339 * Returns:
2340 * array of identifiers with actual qualified one stored last
2341 */
2342 private Identifier[] parseQualifiedIdentifier(const(char)* entity)
2343 {
2344 Identifier[] qualified;
2345
2346 do
2347 {
2348 nextToken();
2349 if (token.value != TOK.identifier)
2350 {
2351 error("`%s` expected as dot-separated identifiers, got `%s`", entity, token.toChars());
2352 return qualified;
2353 }
2354
2355 Identifier id = token.ident;
2356 qualified ~= id;
2357
2358 nextToken();
2359 }
2360 while (token.value == TOK.dot);
2361
2362 return qualified;
2363 }
2364
2365 /**************************************
2366 * Parse a debug conditional
2367 */
2368 private AST.Condition parseDebugCondition()
2369 {
2370 uint level = 1;
2371 Identifier id = null;
2372 Loc loc = token.loc;
2373
2374 if (token.value == TOK.leftParenthesis)
2375 {
2376 nextToken();
2377
2378 if (token.value == TOK.identifier)
2379 id = token.ident;
2380 else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
2381 level = cast(uint)token.unsvalue;
2382 else
2383 error("identifier or integer expected inside `debug(...)`, not `%s`", token.toChars());
2384 loc = token.loc;
2385 nextToken();
2386 check(TOK.rightParenthesis);
2387 }
2388 return new AST.DebugCondition(loc, mod, level, id);
2389 }
2390
2391 /**************************************
2392 * Parse a version conditional
2393 */
2394 private AST.Condition parseVersionCondition()
2395 {
2396 uint level = 1;
2397 Identifier id = null;
2398 Loc loc;
2399
2400 if (token.value == TOK.leftParenthesis)
2401 {
2402 nextToken();
2403 /* Allow:
2404 * version (unittest)
2405 * version (assert)
2406 * even though they are keywords
2407 */
2408 loc = token.loc;
2409 if (token.value == TOK.identifier)
2410 id = token.ident;
2411 else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
2412 level = cast(uint)token.unsvalue;
2413 else if (token.value == TOK.unittest_)
2414 id = Identifier.idPool(Token.toString(TOK.unittest_));
2415 else if (token.value == TOK.assert_)
2416 id = Identifier.idPool(Token.toString(TOK.assert_));
2417 else
2418 error("identifier or integer expected inside `version(...)`, not `%s`", token.toChars());
2419 nextToken();
2420 check(TOK.rightParenthesis);
2421 }
2422 else
2423 error("(condition) expected following `version`");
2424 return new AST.VersionCondition(loc, mod, level, id);
2425 }
2426
2427 /***********************************************
2428 * static if (expression)
2429 * body
2430 * else
2431 * body
2432 * Current token is 'static'.
2433 */
2434 private AST.Condition parseStaticIfCondition()
2435 {
2436 AST.Expression exp;
2437 AST.Condition condition;
2438 const loc = token.loc;
2439
2440 nextToken();
2441 nextToken();
2442 if (token.value == TOK.leftParenthesis)
2443 {
2444 nextToken();
2445 exp = parseAssignExp();
2446 check(TOK.rightParenthesis);
2447 }
2448 else
2449 {
2450 error("(expression) expected following `static if`");
2451 exp = null;
2452 }
2453 condition = new AST.StaticIfCondition(loc, exp);
2454 return condition;
2455 }
2456
2457 /*****************************************
2458 * Parse a constructor definition:
2459 * this(parameters) { body }
2460 * or postblit:
2461 * this(this) { body }
2462 * or constructor template:
2463 * this(templateparameters)(parameters) { body }
2464 * Current token is 'this'.
2465 */
2466 private AST.Dsymbol parseCtor(PrefixAttributes!AST* pAttrs)
2467 {
2468 AST.Expressions* udas = null;
2469 const loc = token.loc;
2470 StorageClass stc = getStorageClass!AST(pAttrs);
2471
2472 nextToken();
2473 if (token.value == TOK.leftParenthesis && peekNext() == TOK.this_ && peekNext2() == TOK.rightParenthesis)
2474 {
2475 // this(this) { ... }
2476 nextToken();
2477 nextToken();
2478 check(TOK.rightParenthesis);
2479
2480 stc = parsePostfix(stc, &udas);
2481 if (stc & STC.immutable_)
2482 deprecation("`immutable` postblit is deprecated. Please use an unqualified postblit.");
2483 if (stc & STC.shared_)
2484 deprecation("`shared` postblit is deprecated. Please use an unqualified postblit.");
2485 if (stc & STC.const_)
2486 deprecation("`const` postblit is deprecated. Please use an unqualified postblit.");
2487 if (stc & STC.static_)
2488 error(loc, "postblit cannot be `static`");
2489
2490 auto f = new AST.PostBlitDeclaration(loc, Loc.initial, stc, Id.postblit);
2491 AST.Dsymbol s = parseContracts(f);
2492 if (udas)
2493 {
2494 auto a = new AST.Dsymbols();
2495 a.push(f);
2496 s = new AST.UserAttributeDeclaration(udas, a);
2497 }
2498 return s;
2499 }
2500
2501 /* Look ahead to see if:
2502 * this(...)(...)
2503 * which is a constructor template
2504 */
2505 AST.TemplateParameters* tpl = null;
2506 if (token.value == TOK.leftParenthesis && peekPastParen(&token).value == TOK.leftParenthesis)
2507 {
2508 tpl = parseTemplateParameterList();
2509 }
2510
2511 /* Just a regular constructor
2512 */
2513 auto parameterList = parseParameterList(null);
2514 stc = parsePostfix(stc, &udas);
2515
2516 if (parameterList.varargs != VarArg.none || AST.Parameter.dim(parameterList.parameters) != 0)
2517 {
2518 if (stc & STC.static_)
2519 error(loc, "constructor cannot be static");
2520 }
2521 else if (StorageClass ss = stc & (STC.shared_ | STC.static_)) // this()
2522 {
2523 if (ss == STC.static_)
2524 error(loc, "use `static this()` to declare a static constructor");
2525 else if (ss == (STC.shared_ | STC.static_))
2526 error(loc, "use `shared static this()` to declare a shared static constructor");
2527 }
2528
2529 AST.Expression constraint = tpl ? parseConstraint() : null;
2530
2531 AST.Type tf = new AST.TypeFunction(parameterList, null, linkage, stc); // RetrunType -> auto
2532 tf = tf.addSTC(stc);
2533
2534 auto f = new AST.CtorDeclaration(loc, Loc.initial, stc, tf);
2535 AST.Dsymbol s = parseContracts(f);
2536 if (udas)
2537 {
2538 auto a = new AST.Dsymbols();
2539 a.push(f);
2540 s = new AST.UserAttributeDeclaration(udas, a);
2541 }
2542
2543 if (tpl)
2544 {
2545 // Wrap a template around it
2546 auto decldefs = new AST.Dsymbols();
2547 decldefs.push(s);
2548 s = new AST.TemplateDeclaration(loc, f.ident, tpl, constraint, decldefs);
2549 }
2550
2551 return s;
2552 }
2553
2554 /*****************************************
2555 * Parse a destructor definition:
2556 * ~this() { body }
2557 * Current token is '~'.
2558 */
2559 private AST.Dsymbol parseDtor(PrefixAttributes!AST* pAttrs)
2560 {
2561 AST.Expressions* udas = null;
2562 const loc = token.loc;
2563 StorageClass stc = getStorageClass!AST(pAttrs);
2564
2565 nextToken();
2566 check(TOK.this_);
2567 check(TOK.leftParenthesis);
2568 check(TOK.rightParenthesis);
2569
2570 stc = parsePostfix(stc, &udas);
2571 if (StorageClass ss = stc & (STC.shared_ | STC.static_))
2572 {
2573 if (ss == STC.static_)
2574 error(loc, "use `static ~this()` to declare a static destructor");
2575 else if (ss == (STC.shared_ | STC.static_))
2576 error(loc, "use `shared static ~this()` to declare a shared static destructor");
2577 }
2578
2579 auto f = new AST.DtorDeclaration(loc, Loc.initial, stc, Id.dtor);
2580 AST.Dsymbol s = parseContracts(f);
2581 if (udas)
2582 {
2583 auto a = new AST.Dsymbols();
2584 a.push(f);
2585 s = new AST.UserAttributeDeclaration(udas, a);
2586 }
2587 return s;
2588 }
2589
2590 /*****************************************
2591 * Parse a static constructor definition:
2592 * static this() { body }
2593 * Current token is 'static'.
2594 */
2595 private AST.Dsymbol parseStaticCtor(PrefixAttributes!AST* pAttrs)
2596 {
2597 //Expressions *udas = NULL;
2598 const loc = token.loc;
2599 StorageClass stc = getStorageClass!AST(pAttrs);
2600
2601 nextToken();
2602 nextToken();
2603 check(TOK.leftParenthesis);
2604 check(TOK.rightParenthesis);
2605
2606 stc = parsePostfix(stc & ~STC.TYPECTOR, null) | stc;
2607 if (stc & STC.shared_)
2608 error(loc, "use `shared static this()` to declare a shared static constructor");
2609 else if (stc & STC.static_)
2610 appendStorageClass(stc, STC.static_); // complaint for the redundancy
2611 else if (StorageClass modStc = stc & STC.TYPECTOR)
2612 {
2613 OutBuffer buf;
2614 AST.stcToBuffer(&buf, modStc);
2615 error(loc, "static constructor cannot be `%s`", buf.peekChars());
2616 }
2617 stc &= ~(STC.static_ | STC.TYPECTOR);
2618
2619 auto f = new AST.StaticCtorDeclaration(loc, Loc.initial, stc);
2620 AST.Dsymbol s = parseContracts(f);
2621 return s;
2622 }
2623
2624 /*****************************************
2625 * Parse a static destructor definition:
2626 * static ~this() { body }
2627 * Current token is 'static'.
2628 */
2629 private AST.Dsymbol parseStaticDtor(PrefixAttributes!AST* pAttrs)
2630 {
2631 AST.Expressions* udas = null;
2632 const loc = token.loc;
2633 StorageClass stc = getStorageClass!AST(pAttrs);
2634
2635 nextToken();
2636 nextToken();
2637 check(TOK.this_);
2638 check(TOK.leftParenthesis);
2639 check(TOK.rightParenthesis);
2640
2641 stc = parsePostfix(stc & ~STC.TYPECTOR, &udas) | stc;
2642 if (stc & STC.shared_)
2643 error(loc, "use `shared static ~this()` to declare a shared static destructor");
2644 else if (stc & STC.static_)
2645 appendStorageClass(stc, STC.static_); // complaint for the redundancy
2646 else if (StorageClass modStc = stc & STC.TYPECTOR)
2647 {
2648 OutBuffer buf;
2649 AST.stcToBuffer(&buf, modStc);
2650 error(loc, "static destructor cannot be `%s`", buf.peekChars());
2651 }
2652 stc &= ~(STC.static_ | STC.TYPECTOR);
2653
2654 auto f = new AST.StaticDtorDeclaration(loc, Loc.initial, stc);
2655 AST.Dsymbol s = parseContracts(f);
2656 if (udas)
2657 {
2658 auto a = new AST.Dsymbols();
2659 a.push(f);
2660 s = new AST.UserAttributeDeclaration(udas, a);
2661 }
2662 return s;
2663 }
2664
2665 /*****************************************
2666 * Parse a shared static constructor definition:
2667 * shared static this() { body }
2668 * Current token is 'shared'.
2669 */
2670 private AST.Dsymbol parseSharedStaticCtor(PrefixAttributes!AST* pAttrs)
2671 {
2672 //Expressions *udas = NULL;
2673 const loc = token.loc;
2674 StorageClass stc = getStorageClass!AST(pAttrs);
2675
2676 nextToken();
2677 nextToken();
2678 nextToken();
2679 check(TOK.leftParenthesis);
2680 check(TOK.rightParenthesis);
2681
2682 stc = parsePostfix(stc & ~STC.TYPECTOR, null) | stc;
2683 if (StorageClass ss = stc & (STC.shared_ | STC.static_))
2684 appendStorageClass(stc, ss); // complaint for the redundancy
2685 else if (StorageClass modStc = stc & STC.TYPECTOR)
2686 {
2687 OutBuffer buf;
2688 AST.stcToBuffer(&buf, modStc);
2689 error(loc, "shared static constructor cannot be `%s`", buf.peekChars());
2690 }
2691 stc &= ~(STC.static_ | STC.TYPECTOR);
2692
2693 auto f = new AST.SharedStaticCtorDeclaration(loc, Loc.initial, stc);
2694 AST.Dsymbol s = parseContracts(f);
2695 return s;
2696 }
2697
2698 /*****************************************
2699 * Parse a shared static destructor definition:
2700 * shared static ~this() { body }
2701 * Current token is 'shared'.
2702 */
2703 private AST.Dsymbol parseSharedStaticDtor(PrefixAttributes!AST* pAttrs)
2704 {
2705 AST.Expressions* udas = null;
2706 const loc = token.loc;
2707 StorageClass stc = getStorageClass!AST(pAttrs);
2708
2709 nextToken();
2710 nextToken();
2711 nextToken();
2712 check(TOK.this_);
2713 check(TOK.leftParenthesis);
2714 check(TOK.rightParenthesis);
2715
2716 stc = parsePostfix(stc & ~STC.TYPECTOR, &udas) | stc;
2717 if (StorageClass ss = stc & (STC.shared_ | STC.static_))
2718 appendStorageClass(stc, ss); // complaint for the redundancy
2719 else if (StorageClass modStc = stc & STC.TYPECTOR)
2720 {
2721 OutBuffer buf;
2722 AST.stcToBuffer(&buf, modStc);
2723 error(loc, "shared static destructor cannot be `%s`", buf.peekChars());
2724 }
2725 stc &= ~(STC.static_ | STC.TYPECTOR);
2726
2727 auto f = new AST.SharedStaticDtorDeclaration(loc, Loc.initial, stc);
2728 AST.Dsymbol s = parseContracts(f);
2729 if (udas)
2730 {
2731 auto a = new AST.Dsymbols();
2732 a.push(f);
2733 s = new AST.UserAttributeDeclaration(udas, a);
2734 }
2735 return s;
2736 }
2737
2738 /*****************************************
2739 * Parse an invariant definition:
2740 * invariant { statements... }
2741 * invariant() { statements... }
2742 * invariant (expression);
2743 * Current token is 'invariant'.
2744 */
2745 private AST.Dsymbol parseInvariant(PrefixAttributes!AST* pAttrs)
2746 {
2747 const loc = token.loc;
2748 StorageClass stc = getStorageClass!AST(pAttrs);
2749
2750 nextToken();
2751 if (token.value == TOK.leftParenthesis) // optional () or invariant (expression);
2752 {
2753 nextToken();
2754 if (token.value != TOK.rightParenthesis) // invariant (expression);
2755 {
2756 AST.Expression e = parseAssignExp(), msg = null;
2757 if (token.value == TOK.comma)
2758 {
2759 nextToken();
2760 if (token.value != TOK.rightParenthesis)
2761 {
2762 msg = parseAssignExp();
2763 if (token.value == TOK.comma)
2764 nextToken();
2765 }
2766 }
2767 check(TOK.rightParenthesis);
2768 check(TOK.semicolon);
2769 e = new AST.AssertExp(loc, e, msg);
2770 auto fbody = new AST.ExpStatement(loc, e);
2771 auto f = new AST.InvariantDeclaration(loc, token.loc, stc, null, fbody);
2772 return f;
2773 }
2774 nextToken();
2775 }
2776
2777 auto fbody = parseStatement(ParseStatementFlags.curly);
2778 auto f = new AST.InvariantDeclaration(loc, token.loc, stc, null, fbody);
2779 return f;
2780 }
2781
2782 /*****************************************
2783 * Parse a unittest definition:
2784 * unittest { body }
2785 * Current token is 'unittest'.
2786 */
2787 private AST.Dsymbol parseUnitTest(PrefixAttributes!AST* pAttrs)
2788 {
2789 const loc = token.loc;
2790 StorageClass stc = getStorageClass!AST(pAttrs);
2791
2792 nextToken();
2793
2794 const(char)* begPtr = token.ptr + 1; // skip '{'
2795 const(char)* endPtr = null;
2796 AST.Statement sbody = parseStatement(ParseStatementFlags.curly, &endPtr);
2797
2798 /** Extract unittest body as a string. Must be done eagerly since memory
2799 will be released by the lexer before doc gen. */
2800 char* docline = null;
2801 if (global.params.doDocComments && endPtr > begPtr)
2802 {
2803 /* Remove trailing whitespaces */
2804 for (const(char)* p = endPtr - 1; begPtr <= p && (*p == ' ' || *p == '\r' || *p == '\n' || *p == '\t'); --p)
2805 {
2806 endPtr = p;
2807 }
2808
2809 size_t len = endPtr - begPtr;
2810 if (len > 0)
2811 {
2812 docline = cast(char*)mem.xmalloc_noscan(len + 2);
2813 memcpy(docline, begPtr, len);
2814 docline[len] = '\n'; // Terminate all lines by LF
2815 docline[len + 1] = '\0';
2816 }
2817 }
2818
2819 auto f = new AST.UnitTestDeclaration(loc, token.loc, stc, docline);
2820 f.fbody = sbody;
2821 return f;
2822 }
2823
2824 /*****************************************
2825 * Parse a new definition:
2826 * @disable new();
2827 * Current token is 'new'.
2828 */
2829 private AST.Dsymbol parseNew(PrefixAttributes!AST* pAttrs)
2830 {
2831 const loc = token.loc;
2832 StorageClass stc = getStorageClass!AST(pAttrs);
2833 if (!(stc & STC.disable))
2834 {
2835 error("`new` allocator must be annotated with `@disabled`");
2836 }
2837 nextToken();
2838
2839 /* @@@DEPRECATED_2.098@@@
2840 * After deprecation period (2.108), remove all code in the version(all) block.
2841 */
2842 version (all)
2843 {
2844 auto parameterList = parseParameterList(null); // parameterList ignored
2845 if (parameterList.parameters.length > 0 || parameterList.varargs != VarArg.none)
2846 deprecation("`new` allocator with non-empty parameter list is deprecated");
2847 auto f = new AST.NewDeclaration(loc, stc);
2848 if (token.value != TOK.semicolon)
2849 {
2850 deprecation("`new` allocator with function definition is deprecated");
2851 parseContracts(f); // body ignored
2852 f.fbody = null;
2853 f.fensures = null;
2854 f.frequires = null;
2855 }
2856 else
2857 nextToken();
2858 return f;
2859 }
2860 else
2861 {
2862 check(TOK.leftParenthesis);
2863 check(TOK.rightParenthesis);
2864 check(TOK.semicolon);
2865 return new AST.NewDeclaration(loc, stc);
2866 }
2867 }
2868
2869 /**********************************************
2870 * Parse parameter list.
2871 */
2872 private AST.ParameterList parseParameterList(AST.TemplateParameters** tpl)
2873 {
2874 auto parameters = new AST.Parameters();
2875 VarArg varargs = VarArg.none;
2876 int hasdefault = 0;
2877 StorageClass varargsStc;
2878
2879 // Attributes allowed for ...
2880 enum VarArgsStc = STC.const_ | STC.immutable_ | STC.shared_ | STC.scope_ | STC.return_;
2881
2882 check(TOK.leftParenthesis);
2883 while (1)
2884 {
2885 Identifier ai = null;
2886 AST.Type at;
2887 StorageClass storageClass = 0;
2888 StorageClass stc;
2889 AST.Expression ae;
2890 AST.Expressions* udas = null;
2891 for (; 1; nextToken())
2892 {
2893 L3:
2894 switch (token.value)
2895 {
2896 case TOK.rightParenthesis:
2897 if (storageClass != 0 || udas !is null)
2898 error("basic type expected, not `)`");
2899 break;
2900
2901 case TOK.dotDotDot:
2902 varargs = VarArg.variadic;
2903 varargsStc = storageClass;
2904 if (varargsStc & ~VarArgsStc)
2905 {
2906 OutBuffer buf;
2907 AST.stcToBuffer(&buf, varargsStc & ~VarArgsStc);
2908 error("variadic parameter cannot have attributes `%s`", buf.peekChars());
2909 varargsStc &= VarArgsStc;
2910 }
2911 nextToken();
2912 break;
2913
2914 case TOK.const_:
2915 if (peekNext() == TOK.leftParenthesis)
2916 goto default;
2917 stc = STC.const_;
2918 goto L2;
2919
2920 case TOK.immutable_:
2921 if (peekNext() == TOK.leftParenthesis)
2922 goto default;
2923 stc = STC.immutable_;
2924 goto L2;
2925
2926 case TOK.shared_:
2927 if (peekNext() == TOK.leftParenthesis)
2928 goto default;
2929 stc = STC.shared_;
2930 goto L2;
2931
2932 case TOK.inout_:
2933 if (peekNext() == TOK.leftParenthesis)
2934 goto default;
2935 stc = STC.wild;
2936 goto L2;
2937 case TOK.at:
2938 {
2939 AST.Expressions* exps = null;
2940 StorageClass stc2 = parseAttribute(exps);
2941 if (stc2 & atAttrGroup)
2942 {
2943 error("`@%s` attribute for function parameter is not supported", token.toChars());
2944 }
2945 else
2946 {
2947 udas = AST.UserAttributeDeclaration.concat(udas, exps);
2948 }
2949 if (token.value == TOK.dotDotDot)
2950 error("variadic parameter cannot have user-defined attributes");
2951 if (stc2)
2952 nextToken();
2953 goto L3;
2954 // Don't call nextToken again.
2955 }
2956 case TOK.in_:
2957 stc = STC.in_;
2958 goto L2;
2959
2960 case TOK.out_:
2961 stc = STC.out_;
2962 goto L2;
2963
2964 case TOK.ref_:
2965 stc = STC.ref_;
2966 goto L2;
2967
2968 case TOK.lazy_:
2969 stc = STC.lazy_;
2970 goto L2;
2971
2972 case TOK.scope_:
2973 stc = STC.scope_;
2974 goto L2;
2975
2976 case TOK.final_:
2977 stc = STC.final_;
2978 goto L2;
2979
2980 case TOK.auto_:
2981 stc = STC.auto_;
2982 goto L2;
2983
2984 case TOK.return_:
2985 stc = STC.return_;
2986 goto L2;
2987 L2:
2988 storageClass = appendStorageClass(storageClass, stc);
2989 continue;
2990
2991 version (none)
2992 {
2993 case TOK.static_:
2994 stc = STC.static_;
2995 goto L2;
2996
2997 case TOK.auto_:
2998 storageClass = STC.auto_;
2999 goto L4;
3000
3001 case TOK.alias_:
3002 storageClass = STC.alias_;
3003 goto L4;
3004 L4:
3005 nextToken();
3006 ai = null;
3007 if (token.value == TOK.identifier)
3008 {
3009 ai = token.ident;
3010 nextToken();
3011 }
3012
3013 at = null; // no type
3014 ae = null; // no default argument
3015 if (token.value == TOK.assign) // = defaultArg
3016 {
3017 nextToken();
3018 ae = parseDefaultInitExp();
3019 hasdefault = 1;
3020 }
3021 else
3022 {
3023 if (hasdefault)
3024 error("default argument expected for `alias %s`", ai ? ai.toChars() : "");
3025 }
3026 goto L3;
3027 }
3028 default:
3029 {
3030 stc = storageClass & (STC.IOR | STC.lazy_);
3031 // if stc is not a power of 2
3032 if (stc & (stc - 1) && !(stc == (STC.in_ | STC.ref_)))
3033 error("incompatible parameter storage classes");
3034 //if ((storageClass & STC.scope_) && (storageClass & (STC.ref_ | STC.out_)))
3035 //error("scope cannot be ref or out");
3036
3037 if (tpl && token.value == TOK.identifier)
3038 {
3039 const tv = peekNext();
3040 if (tv == TOK.comma || tv == TOK.rightParenthesis || tv == TOK.dotDotDot)
3041 {
3042 Identifier id = Identifier.generateId("__T");
3043 const loc = token.loc;
3044 at = new AST.TypeIdentifier(loc, id);
3045 if (!*tpl)
3046 *tpl = new AST.TemplateParameters();
3047 AST.TemplateParameter tp = new AST.TemplateTypeParameter(loc, id, null, null);
3048 (*tpl).push(tp);
3049
3050 ai = token.ident;
3051 nextToken();
3052 }
3053 else goto _else;
3054 }
3055 else
3056 {
3057 _else:
3058 at = parseType(&ai);
3059 }
3060 ae = null;
3061 if (token.value == TOK.assign) // = defaultArg
3062 {
3063 nextToken();
3064 ae = parseDefaultInitExp();
3065 hasdefault = 1;
3066 }
3067 else
3068 {
3069 if (hasdefault)
3070 error("default argument expected for `%s`", ai ? ai.toChars() : at.toChars());
3071 }
3072 auto param = new AST.Parameter(storageClass | STC.parameter, at, ai, ae, null);
3073 if (udas)
3074 {
3075 auto a = new AST.Dsymbols();
3076 auto udad = new AST.UserAttributeDeclaration(udas, a);
3077 param.userAttribDecl = udad;
3078 }
3079 if (token.value == TOK.at)
3080 {
3081 AST.Expressions* exps = null;
3082 StorageClass stc2 = parseAttribute(exps);
3083 if (stc2 & atAttrGroup)
3084 {
3085 error("`@%s` attribute for function parameter is not supported", token.toChars());
3086 }
3087 else
3088 {
3089 error("user-defined attributes cannot appear as postfixes", token.toChars());
3090 }
3091 if (stc2)
3092 nextToken();
3093 }
3094 if (token.value == TOK.dotDotDot)
3095 {
3096 /* This is:
3097 * at ai ...
3098 */
3099 if (storageClass & (STC.out_ | STC.ref_))
3100 error("variadic argument cannot be `out` or `ref`");
3101 varargs = VarArg.typesafe;
3102 parameters.push(param);
3103 nextToken();
3104 break;
3105 }
3106 parameters.push(param);
3107 if (token.value == TOK.comma)
3108 {
3109 nextToken();
3110 goto L1;
3111 }
3112 break;
3113 }
3114 }
3115 break;
3116 }
3117 break;
3118
3119 L1:
3120 }
3121 check(TOK.rightParenthesis);
3122 return AST.ParameterList(parameters, varargs, varargsStc);
3123 }
3124
3125 /*************************************
3126 */
3127 private AST.EnumDeclaration parseEnum()
3128 {
3129 AST.EnumDeclaration e;
3130 Identifier id;
3131 AST.Type memtype;
3132 auto loc = token.loc;
3133
3134 // printf("Parser::parseEnum()\n");
3135 nextToken();
3136 id = null;
3137 if (token.value == TOK.identifier)
3138 {
3139 id = token.ident;
3140 nextToken();
3141 }
3142
3143 memtype = null;
3144 if (token.value == TOK.colon)
3145 {
3146 nextToken();
3147 int alt = 0;
3148 const typeLoc = token.loc;
3149 memtype = parseBasicType();
3150 memtype = parseDeclarator(memtype, alt, null);
3151 checkCstyleTypeSyntax(typeLoc, memtype, alt, null);
3152 }
3153
3154 e = new AST.EnumDeclaration(loc, id, memtype);
3155 if (token.value == TOK.semicolon && id)
3156 nextToken();
3157 else if (token.value == TOK.leftCurly)
3158 {
3159 bool isAnonymousEnum = !id;
3160 TOK prevTOK;
3161
3162 //printf("enum definition\n");
3163 e.members = new AST.Dsymbols();
3164 nextToken();
3165 const(char)[] comment = token.blockComment;
3166 while (token.value != TOK.rightCurly)
3167 {
3168 /* Can take the following forms...
3169 * 1. ident
3170 * 2. ident = value
3171 * 3. type ident = value
3172 * ... prefixed by valid attributes
3173 */
3174 loc = token.loc;
3175
3176 AST.Type type = null;
3177 Identifier ident = null;
3178
3179 AST.Expressions* udas;
3180 StorageClass stc;
3181 AST.Expression deprecationMessage;
3182 enum attributeErrorMessage = "`%s` is not a valid attribute for enum members";
3183 while(token.value != TOK.rightCurly
3184 && token.value != TOK.comma
3185 && token.value != TOK.assign)
3186 {
3187 switch(token.value)
3188 {
3189 case TOK.at:
3190 if (StorageClass _stc = parseAttribute(udas))
3191 {
3192 if (_stc == STC.disable)
3193 stc |= _stc;
3194 else
3195 {
3196 OutBuffer buf;
3197 AST.stcToBuffer(&buf, _stc);
3198 error(attributeErrorMessage, buf.peekChars());
3199 }
3200 prevTOK = token.value;
3201 nextToken();
3202 }
3203 break;
3204 case TOK.deprecated_:
3205 stc |= STC.deprecated_;
3206 if (!parseDeprecatedAttribute(deprecationMessage))
3207 {
3208 prevTOK = token.value;
3209 nextToken();
3210 }
3211 break;
3212 case TOK.identifier:
3213 const tv = peekNext();
3214 if (tv == TOK.assign || tv == TOK.comma || tv == TOK.rightCurly)
3215 {
3216 ident = token.ident;
3217 type = null;
3218 prevTOK = token.value;
3219 nextToken();
3220 }
3221 else
3222 {
3223 goto default;
3224 }
3225 break;
3226 default:
3227 if (isAnonymousEnum)
3228 {
3229 type = parseType(&ident, null);
3230 if (type == AST.Type.terror)
3231 {
3232 type = null;
3233 prevTOK = token.value;
3234 nextToken();
3235 }
3236 else
3237 {
3238 prevTOK = TOK.identifier;
3239 }
3240 }
3241 else
3242 {
3243 error(attributeErrorMessage, token.toChars());
3244 prevTOK = token.value;
3245 nextToken();
3246 }
3247 break;
3248 }
3249 if (token.value == TOK.comma)
3250 {
3251 prevTOK = token.value;
3252 }
3253 }
3254
3255 if (type && type != AST.Type.terror)
3256 {
3257 if (!ident)
3258 error("no identifier for declarator `%s`", type.toChars());
3259 if (!isAnonymousEnum)
3260 error("type only allowed if anonymous enum and no enum type");
3261 }
3262 AST.Expression value;
3263 if (token.value == TOK.assign)
3264 {
3265 if (prevTOK == TOK.identifier)
3266 {
3267 nextToken();
3268 value = parseAssignExp();
3269 }
3270 else
3271 {
3272 error("assignment must be preceded by an identifier");
3273 nextToken();
3274 }
3275 }
3276 else
3277 {
3278 value = null;
3279 if (type && type != AST.Type.terror && isAnonymousEnum)
3280 error("if type, there must be an initializer");
3281 }
3282
3283 AST.DeprecatedDeclaration dd;
3284 if (deprecationMessage)
3285 {
3286 dd = new AST.DeprecatedDeclaration(deprecationMessage, null);
3287 stc |= STC.deprecated_;
3288 }
3289
3290 auto em = new AST.EnumMember(loc, ident, value, type, stc, null, dd);
3291 e.members.push(em);
3292
3293 if (udas)
3294 {
3295 auto s = new AST.Dsymbols();
3296 s.push(em);
3297 auto uad = new AST.UserAttributeDeclaration(udas, s);
3298 em.userAttribDecl = uad;
3299 }
3300
3301 if (token.value == TOK.rightCurly)
3302 {
3303 }
3304 else
3305 {
3306 addComment(em, comment);
3307 comment = null;
3308 check(TOK.comma);
3309 }
3310 addComment(em, comment);
3311 comment = token.blockComment;
3312
3313 if (token.value == TOK.endOfFile)
3314 {
3315 error("premature end of file");
3316 break;
3317 }
3318 }
3319 nextToken();
3320 }
3321 else
3322 error("enum declaration is invalid");
3323
3324 //printf("-parseEnum() %s\n", e.toChars());
3325 return e;
3326 }
3327
3328 /********************************
3329 * Parse struct, union, interface, class.
3330 */
3331 private AST.Dsymbol parseAggregate()
3332 {
3333 AST.TemplateParameters* tpl = null;
3334 AST.Expression constraint;
3335 const loc = token.loc;
3336 TOK tok = token.value;
3337
3338 //printf("Parser::parseAggregate()\n");
3339 nextToken();
3340 Identifier id;
3341 if (token.value != TOK.identifier)
3342 {
3343 id = null;
3344 }
3345 else
3346 {
3347 id = token.ident;
3348 nextToken();
3349
3350 if (token.value == TOK.leftParenthesis)
3351 {
3352 // struct/class template declaration.
3353 tpl = parseTemplateParameterList();
3354 constraint = parseConstraint();
3355 }
3356 }
3357
3358 // Collect base class(es)
3359 AST.BaseClasses* baseclasses = null;
3360 if (token.value == TOK.colon)
3361 {
3362 if (tok != TOK.interface_ && tok != TOK.class_)
3363 error("base classes are not allowed for `%s`, did you mean `;`?", Token.toChars(tok));
3364 nextToken();
3365 baseclasses = parseBaseClasses();
3366 }
3367
3368 if (token.value == TOK.if_)
3369 {
3370 if (constraint)
3371 error("template constraints appear both before and after BaseClassList, put them before");
3372 constraint = parseConstraint();
3373 }
3374 if (constraint)
3375 {
3376 if (!id)
3377 error("template constraints not allowed for anonymous `%s`", Token.toChars(tok));
3378 if (!tpl)
3379 error("template constraints only allowed for templates");
3380 }
3381
3382 AST.Dsymbols* members = null;
3383 if (token.value == TOK.leftCurly)
3384 {
3385 //printf("aggregate definition\n");
3386 const lookingForElseSave = lookingForElse;
3387 lookingForElse = Loc();
3388 nextToken();
3389 members = parseDeclDefs(0);
3390 lookingForElse = lookingForElseSave;
3391 if (token.value != TOK.rightCurly)
3392 {
3393 /* { */
3394 error("`}` expected following members in `%s` declaration at %s",
3395 Token.toChars(tok), loc.toChars());
3396 }
3397 nextToken();
3398 }
3399 else if (token.value == TOK.semicolon && id)
3400 {
3401 if (baseclasses || constraint)
3402 error("members expected");
3403 nextToken();
3404 }
3405 else
3406 {
3407 error("{ } expected following `%s` declaration", Token.toChars(tok));
3408 }
3409
3410 AST.AggregateDeclaration a;
3411 switch (tok)
3412 {
3413 case TOK.interface_:
3414 if (!id)
3415 error(loc, "anonymous interfaces not allowed");
3416 a = new AST.InterfaceDeclaration(loc, id, baseclasses);
3417 a.members = members;
3418 break;
3419
3420 case TOK.class_:
3421 if (!id)
3422 error(loc, "anonymous classes not allowed");
3423 bool inObject = md && !md.packages && md.id == Id.object;
3424 a = new AST.ClassDeclaration(loc, id, baseclasses, members, inObject);
3425 break;
3426
3427 case TOK.struct_:
3428 if (id)
3429 {
3430 bool inObject = md && !md.packages && md.id == Id.object;
3431 a = new AST.StructDeclaration(loc, id, inObject);
3432 a.members = members;
3433 }
3434 else
3435 {
3436 /* Anonymous structs/unions are more like attributes.
3437 */
3438 assert(!tpl);
3439 return new AST.AnonDeclaration(loc, false, members);
3440 }
3441 break;
3442
3443 case TOK.union_:
3444 if (id)
3445 {
3446 a = new AST.UnionDeclaration(loc, id);
3447 a.members = members;
3448 }
3449 else
3450 {
3451 /* Anonymous structs/unions are more like attributes.
3452 */
3453 assert(!tpl);
3454 return new AST.AnonDeclaration(loc, true, members);
3455 }
3456 break;
3457
3458 default:
3459 assert(0);
3460 }
3461
3462 if (tpl)
3463 {
3464 // Wrap a template around the aggregate declaration
3465 auto decldefs = new AST.Dsymbols();
3466 decldefs.push(a);
3467 auto tempdecl = new AST.TemplateDeclaration(loc, id, tpl, constraint, decldefs);
3468 return tempdecl;
3469 }
3470 return a;
3471 }
3472
3473 /*******************************************
3474 */
3475 private AST.BaseClasses* parseBaseClasses()
3476 {
3477 auto baseclasses = new AST.BaseClasses();
3478
3479 for (; 1; nextToken())
3480 {
3481 auto b = new AST.BaseClass(parseBasicType());
3482 baseclasses.push(b);
3483 if (token.value != TOK.comma)
3484 break;
3485 }
3486 return baseclasses;
3487 }
3488
3489 private AST.Dsymbols* parseImport()
3490 {
3491 auto decldefs = new AST.Dsymbols();
3492 Identifier aliasid = null;
3493
3494 int isstatic = token.value == TOK.static_;
3495 if (isstatic)
3496 nextToken();
3497
3498 //printf("Parser::parseImport()\n");
3499 do
3500 {
3501 L1:
3502 nextToken();
3503 if (token.value != TOK.identifier)
3504 {
3505 error("identifier expected following `import`");
3506 break;
3507 }
3508
3509 const loc = token.loc;
3510 Identifier id = token.ident;
3511 Identifier[] a;
3512 nextToken();
3513 if (!aliasid && token.value == TOK.assign)
3514 {
3515 aliasid = id;
3516 goto L1;
3517 }
3518 while (token.value == TOK.dot)
3519 {
3520 a ~= id;
3521 nextToken();
3522 if (token.value != TOK.identifier)
3523 {
3524 error("identifier expected following `package`");
3525 break;
3526 }
3527 id = token.ident;
3528 nextToken();
3529 }
3530
3531 auto s = new AST.Import(loc, a, id, aliasid, isstatic);
3532 decldefs.push(s);
3533
3534 /* Look for
3535 * : alias=name, alias=name;
3536 * syntax.
3537 */
3538 if (token.value == TOK.colon)
3539 {
3540 do
3541 {
3542 nextToken();
3543 if (token.value != TOK.identifier)
3544 {
3545 error("identifier expected following `:`");
3546 break;
3547 }
3548 Identifier _alias = token.ident;
3549 Identifier name;
3550 nextToken();
3551 if (token.value == TOK.assign)
3552 {
3553 nextToken();
3554 if (token.value != TOK.identifier)
3555 {
3556 error("identifier expected following `%s=`", _alias.toChars());
3557 break;
3558 }
3559 name = token.ident;
3560 nextToken();
3561 }
3562 else
3563 {
3564 name = _alias;
3565 _alias = null;
3566 }
3567 s.addAlias(name, _alias);
3568 }
3569 while (token.value == TOK.comma);
3570 break; // no comma-separated imports of this form
3571 }
3572 aliasid = null;
3573 }
3574 while (token.value == TOK.comma);
3575
3576 if (token.value == TOK.semicolon)
3577 nextToken();
3578 else
3579 {
3580 error("`;` expected");
3581 nextToken();
3582 }
3583
3584 return decldefs;
3585 }
3586
3587 AST.Type parseType(Identifier* pident = null, AST.TemplateParameters** ptpl = null)
3588 {
3589 /* Take care of the storage class prefixes that
3590 * serve as type attributes:
3591 * const type
3592 * immutable type
3593 * shared type
3594 * inout type
3595 * inout const type
3596 * shared const type
3597 * shared inout type
3598 * shared inout const type
3599 */
3600 StorageClass stc = 0;
3601 while (1)
3602 {
3603 switch (token.value)
3604 {
3605 case TOK.const_:
3606 if (peekNext() == TOK.leftParenthesis)
3607 break; // const as type constructor
3608 stc |= STC.const_; // const as storage class
3609 nextToken();
3610 continue;
3611
3612 case TOK.immutable_:
3613 if (peekNext() == TOK.leftParenthesis)
3614 break;
3615 stc |= STC.immutable_;
3616 nextToken();
3617 continue;
3618
3619 case TOK.shared_:
3620 if (peekNext() == TOK.leftParenthesis)
3621 break;
3622 stc |= STC.shared_;
3623 nextToken();
3624 continue;
3625
3626 case TOK.inout_:
3627 if (peekNext() == TOK.leftParenthesis)
3628 break;
3629 stc |= STC.wild;
3630 nextToken();
3631 continue;
3632
3633 default:
3634 break;
3635 }
3636 break;
3637 }
3638
3639 const typeLoc = token.loc;
3640
3641 AST.Type t;
3642 t = parseBasicType();
3643
3644 int alt = 0;
3645 t = parseDeclarator(t, alt, pident, ptpl);
3646 checkCstyleTypeSyntax(typeLoc, t, alt, pident ? *pident : null);
3647
3648 t = t.addSTC(stc);
3649 return t;
3650 }
3651
3652 private AST.Type parseBasicType(bool dontLookDotIdents = false)
3653 {
3654 AST.Type t;
3655 Loc loc;
3656 Identifier id;
3657 //printf("parseBasicType()\n");
3658 switch (token.value)
3659 {
3660 case TOK.void_:
3661 t = AST.Type.tvoid;
3662 goto LabelX;
3663
3664 case TOK.int8:
3665 t = AST.Type.tint8;
3666 goto LabelX;
3667
3668 case TOK.uns8:
3669 t = AST.Type.tuns8;
3670 goto LabelX;
3671
3672 case TOK.int16:
3673 t = AST.Type.tint16;
3674 goto LabelX;
3675
3676 case TOK.uns16:
3677 t = AST.Type.tuns16;
3678 goto LabelX;
3679
3680 case TOK.int32:
3681 t = AST.Type.tint32;
3682 goto LabelX;
3683
3684 case TOK.uns32:
3685 t = AST.Type.tuns32;
3686 goto LabelX;
3687
3688 case TOK.int64:
3689 t = AST.Type.tint64;
3690 nextToken();
3691 if (token.value == TOK.int64) // if `long long`
3692 {
3693 error("use `long` for a 64 bit integer instead of `long long`");
3694 nextToken();
3695 }
3696 else if (token.value == TOK.float64) // if `long double`
3697 {
3698 error("use `real` instead of `long double`");
3699 t = AST.Type.tfloat80;
3700 nextToken();
3701 }
3702 break;
3703
3704 case TOK.uns64:
3705 t = AST.Type.tuns64;
3706 goto LabelX;
3707
3708 case TOK.int128:
3709 t = AST.Type.tint128;
3710 goto LabelX;
3711
3712 case TOK.uns128:
3713 t = AST.Type.tuns128;
3714 goto LabelX;
3715
3716 case TOK.float32:
3717 t = AST.Type.tfloat32;
3718 goto LabelX;
3719
3720 case TOK.float64:
3721 t = AST.Type.tfloat64;
3722 goto LabelX;
3723
3724 case TOK.float80:
3725 t = AST.Type.tfloat80;
3726 goto LabelX;
3727
3728 case TOK.imaginary32:
3729 t = AST.Type.timaginary32;
3730 goto LabelX;
3731
3732 case TOK.imaginary64:
3733 t = AST.Type.timaginary64;
3734 goto LabelX;
3735
3736 case TOK.imaginary80:
3737 t = AST.Type.timaginary80;
3738 goto LabelX;
3739
3740 case TOK.complex32:
3741 t = AST.Type.tcomplex32;
3742 goto LabelX;
3743
3744 case TOK.complex64:
3745 t = AST.Type.tcomplex64;
3746 goto LabelX;
3747
3748 case TOK.complex80:
3749 t = AST.Type.tcomplex80;
3750 goto LabelX;
3751
3752 case TOK.bool_:
3753 t = AST.Type.tbool;
3754 goto LabelX;
3755
3756 case TOK.char_:
3757 t = AST.Type.tchar;
3758 goto LabelX;
3759
3760 case TOK.wchar_:
3761 t = AST.Type.twchar;
3762 goto LabelX;
3763
3764 case TOK.dchar_:
3765 t = AST.Type.tdchar;
3766 goto LabelX;
3767 LabelX:
3768 nextToken();
3769 break;
3770
3771 case TOK.this_:
3772 case TOK.super_:
3773 case TOK.identifier:
3774 loc = token.loc;
3775 id = token.ident;
3776 nextToken();
3777 if (token.value == TOK.not)
3778 {
3779 // ident!(template_arguments)
3780 auto tempinst = new AST.TemplateInstance(loc, id, parseTemplateArguments());
3781 t = parseBasicTypeStartingAt(new AST.TypeInstance(loc, tempinst), dontLookDotIdents);
3782 }
3783 else
3784 {
3785 t = parseBasicTypeStartingAt(new AST.TypeIdentifier(loc, id), dontLookDotIdents);
3786 }
3787 break;
3788
3789 case TOK.mixin_:
3790 // https://dlang.org/spec/expression.html#mixin_types
3791 loc = token.loc;
3792 nextToken();
3793 if (token.value != TOK.leftParenthesis)
3794 error("found `%s` when expecting `%s` following `mixin`", token.toChars(), Token.toChars(TOK.leftParenthesis));
3795 auto exps = parseArguments();
3796 t = new AST.TypeMixin(loc, exps);
3797 break;
3798
3799 case TOK.dot:
3800 // Leading . as in .foo
3801 t = parseBasicTypeStartingAt(new AST.TypeIdentifier(token.loc, Id.empty), dontLookDotIdents);
3802 break;
3803
3804 case TOK.typeof_:
3805 // typeof(expression)
3806 t = parseBasicTypeStartingAt(parseTypeof(), dontLookDotIdents);
3807 break;
3808
3809 case TOK.vector:
3810 t = parseVector();
3811 break;
3812
3813 case TOK.traits:
3814 if (AST.TraitsExp te = cast(AST.TraitsExp) parsePrimaryExp())
3815 if (te.ident && te.args)
3816 {
3817 t = new AST.TypeTraits(token.loc, te);
3818 break;
3819 }
3820 t = new AST.TypeError;
3821 break;
3822
3823 case TOK.const_:
3824 // const(type)
3825 nextToken();
3826 check(TOK.leftParenthesis);
3827 t = parseType().addSTC(STC.const_);
3828 check(TOK.rightParenthesis);
3829 break;
3830
3831 case TOK.immutable_:
3832 // immutable(type)
3833 nextToken();
3834 check(TOK.leftParenthesis);
3835 t = parseType().addSTC(STC.immutable_);
3836 check(TOK.rightParenthesis);
3837 break;
3838
3839 case TOK.shared_:
3840 // shared(type)
3841 nextToken();
3842 check(TOK.leftParenthesis);
3843 t = parseType().addSTC(STC.shared_);
3844 check(TOK.rightParenthesis);
3845 break;
3846
3847 case TOK.inout_:
3848 // wild(type)
3849 nextToken();
3850 check(TOK.leftParenthesis);
3851 t = parseType().addSTC(STC.wild);
3852 check(TOK.rightParenthesis);
3853 break;
3854
3855 default:
3856 error("basic type expected, not `%s`", token.toChars());
3857 if (token.value == TOK.else_)
3858 errorSupplemental(token.loc, "There's no `static else`, use `else` instead.");
3859 t = AST.Type.terror;
3860 break;
3861 }
3862 return t;
3863 }
3864
3865 private AST.Type parseBasicTypeStartingAt(AST.TypeQualified tid, bool dontLookDotIdents)
3866 {
3867 AST.Type maybeArray = null;
3868 // See https://issues.dlang.org/show_bug.cgi?id=1215
3869 // A basic type can look like MyType (typical case), but also:
3870 // MyType.T -> A type
3871 // MyType[expr] -> Either a static array of MyType or a type (iif MyType is a Ttuple)
3872 // MyType[expr].T -> A type.
3873 // MyType[expr].T[expr] -> Either a static array of MyType[expr].T or a type
3874 // (iif MyType[expr].T is a Ttuple)
3875 while (1)
3876 {
3877 switch (token.value)
3878 {
3879 case TOK.dot:
3880 {
3881 nextToken();
3882 if (token.value != TOK.identifier)
3883 {
3884 error("identifier expected following `.` instead of `%s`", token.toChars());
3885 break;
3886 }
3887 if (maybeArray)
3888 {
3889 // This is actually a TypeTuple index, not an {a/s}array.
3890 // We need to have a while loop to unwind all index taking:
3891 // T[e1][e2].U -> T, addIndex(e1), addIndex(e2)
3892 AST.Objects dimStack;
3893 AST.Type t = maybeArray;
3894 while (true)
3895 {
3896 if (t.ty == Tsarray)
3897 {
3898 // The index expression is an Expression.
3899 AST.TypeSArray a = cast(AST.TypeSArray)t;
3900 dimStack.push(a.dim.syntaxCopy());
3901 t = a.next.syntaxCopy();
3902 }
3903 else if (t.ty == Taarray)
3904 {
3905 // The index expression is a Type. It will be interpreted as an expression at semantic time.
3906 AST.TypeAArray a = cast(AST.TypeAArray)t;
3907 dimStack.push(a.index.syntaxCopy());
3908 t = a.next.syntaxCopy();
3909 }
3910 else
3911 {
3912 break;
3913 }
3914 }
3915 assert(dimStack.dim > 0);
3916 // We're good. Replay indices in the reverse order.
3917 tid = cast(AST.TypeQualified)t;
3918 while (dimStack.dim)
3919 {
3920 tid.addIndex(dimStack.pop());
3921 }
3922 maybeArray = null;
3923 }
3924 const loc = token.loc;
3925 Identifier id = token.ident;
3926 nextToken();
3927 if (token.value == TOK.not)
3928 {
3929 auto tempinst = new AST.TemplateInstance(loc, id, parseTemplateArguments());
3930 tid.addInst(tempinst);
3931 }
3932 else
3933 tid.addIdent(id);
3934 continue;
3935 }
3936 case TOK.leftBracket:
3937 {
3938 if (dontLookDotIdents) // workaround for https://issues.dlang.org/show_bug.cgi?id=14911
3939 goto Lend;
3940
3941 nextToken();
3942 AST.Type t = maybeArray ? maybeArray : cast(AST.Type)tid;
3943 if (token.value == TOK.rightBracket)
3944 {
3945 // It's a dynamic array, and we're done:
3946 // T[].U does not make sense.
3947 t = new AST.TypeDArray(t);
3948 nextToken();
3949 return t;
3950 }
3951 else if (isDeclaration(&token, NeedDeclaratorId.no, TOK.rightBracket, null))
3952 {
3953 // This can be one of two things:
3954 // 1 - an associative array declaration, T[type]
3955 // 2 - an associative array declaration, T[expr]
3956 // These can only be disambiguated later.
3957 AST.Type index = parseType(); // [ type ]
3958 maybeArray = new AST.TypeAArray(t, index);
3959 check(TOK.rightBracket);
3960 }
3961 else
3962 {
3963 // This can be one of three things:
3964 // 1 - an static array declaration, T[expr]
3965 // 2 - a slice, T[expr .. expr]
3966 // 3 - a template parameter pack index expression, T[expr].U
3967 // 1 and 3 can only be disambiguated later.
3968 //printf("it's type[expression]\n");
3969 inBrackets++;
3970 AST.Expression e = parseAssignExp(); // [ expression ]
3971 if (token.value == TOK.slice)
3972 {
3973 // It's a slice, and we're done.
3974 nextToken();
3975 AST.Expression e2 = parseAssignExp(); // [ exp .. exp ]
3976 t = new AST.TypeSlice(t, e, e2);
3977 inBrackets--;
3978 check(TOK.rightBracket);
3979 return t;
3980 }
3981 else
3982 {
3983 maybeArray = new AST.TypeSArray(t, e);
3984 inBrackets--;
3985 check(TOK.rightBracket);
3986 continue;
3987 }
3988 }
3989 break;
3990 }
3991 default:
3992 goto Lend;
3993 }
3994 }
3995 Lend:
3996 return maybeArray ? maybeArray : cast(AST.Type)tid;
3997 }
3998
3999 /******************************************
4000 * Parse suffixes to type t.
4001 * *
4002 * []
4003 * [AssignExpression]
4004 * [AssignExpression .. AssignExpression]
4005 * [Type]
4006 * delegate Parameters MemberFunctionAttributes(opt)
4007 * function Parameters FunctionAttributes(opt)
4008 * Params:
4009 * t = the already parsed type
4010 * Returns:
4011 * t with the suffixes added
4012 * See_Also:
4013 * https://dlang.org/spec/declaration.html#TypeSuffixes
4014 */
4015 private AST.Type parseTypeSuffixes(AST.Type t)
4016 {
4017 //printf("parseTypeSuffixes()\n");
4018 while (1)
4019 {
4020 switch (token.value)
4021 {
4022 case TOK.mul:
4023 t = new AST.TypePointer(t);
4024 nextToken();
4025 continue;
4026
4027 case TOK.leftBracket:
4028 // Handle []. Make sure things like
4029 // int[3][1] a;
4030 // is (array[1] of array[3] of int)
4031 nextToken();
4032 if (token.value == TOK.rightBracket)
4033 {
4034 t = new AST.TypeDArray(t); // []
4035 nextToken();
4036 }
4037 else if (isDeclaration(&token, NeedDeclaratorId.no, TOK.rightBracket, null))
4038 {
4039 // It's an associative array declaration
4040 //printf("it's an associative array\n");
4041 AST.Type index = parseType(); // [ type ]
4042 t = new AST.TypeAArray(t, index);
4043 check(TOK.rightBracket);
4044 }
4045 else
4046 {
4047 //printf("it's type[expression]\n");
4048 inBrackets++;
4049 AST.Expression e = parseAssignExp(); // [ expression ]
4050 if (!e)
4051 {
4052 inBrackets--;
4053 check(TOK.rightBracket);
4054 continue;
4055 }
4056 if (token.value == TOK.slice)
4057 {
4058 nextToken();
4059 AST.Expression e2 = parseAssignExp(); // [ exp .. exp ]
4060 t = new AST.TypeSlice(t, e, e2);
4061 }
4062 else
4063 {
4064 t = new AST.TypeSArray(t, e);
4065 }
4066 inBrackets--;
4067 check(TOK.rightBracket);
4068 }
4069 continue;
4070
4071 case TOK.delegate_:
4072 case TOK.function_:
4073 {
4074 // Handle delegate declaration:
4075 // t delegate(parameter list) nothrow pure
4076 // t function(parameter list) nothrow pure
4077 TOK save = token.value;
4078 nextToken();
4079
4080 auto parameterList = parseParameterList(null);
4081
4082 StorageClass stc = parsePostfix(STC.undefined_, null);
4083 auto tf = new AST.TypeFunction(parameterList, t, linkage, stc);
4084 if (stc & (STC.const_ | STC.immutable_ | STC.shared_ | STC.wild | STC.return_))
4085 {
4086 if (save == TOK.function_)
4087 error("`const`/`immutable`/`shared`/`inout`/`return` attributes are only valid for non-static member functions");
4088 else
4089 tf = cast(AST.TypeFunction)tf.addSTC(stc);
4090 }
4091 t = save == TOK.delegate_ ? new AST.TypeDelegate(tf) : new AST.TypePointer(tf); // pointer to function
4092 continue;
4093 }
4094 default:
4095 return t;
4096 }
4097 assert(0);
4098 }
4099 assert(0);
4100 }
4101
4102 /**********************
4103 * Parse Declarator
4104 * Params:
4105 * t = base type to start with
4106 * palt = OR in 1 for C-style function pointer declaration syntax,
4107 * 2 for C-style array declaration syntax, otherwise don't modify
4108 * pident = set to Identifier if there is one, null if not
4109 * tpl = if !null, then set to TemplateParameterList
4110 * storageClass = any storage classes seen so far
4111 * pdisable = set to true if @disable seen
4112 * pudas = any user defined attributes seen so far. Merged with any more found
4113 * Returns:
4114 * type declared
4115 * Reference: https://dlang.org/spec/declaration.html#Declarator
4116 */
4117 private AST.Type parseDeclarator(AST.Type t, ref int palt, Identifier* pident,
4118 AST.TemplateParameters** tpl = null, StorageClass storageClass = 0,
4119 bool* pdisable = null, AST.Expressions** pudas = null)
4120 {
4121 //printf("parseDeclarator(tpl = %p)\n", tpl);
4122 t = parseTypeSuffixes(t);
4123 AST.Type ts;
4124 switch (token.value)
4125 {
4126 case TOK.identifier:
4127 if (pident)
4128 *pident = token.ident;
4129 else
4130 error("unexpected identifier `%s` in declarator", token.ident.toChars());
4131 ts = t;
4132 nextToken();
4133 break;
4134
4135 case TOK.leftParenthesis:
4136 {
4137 // like: T (*fp)();
4138 // like: T ((*fp))();
4139 if (peekNext() == TOK.mul || peekNext() == TOK.leftParenthesis)
4140 {
4141 /* Parse things with parentheses around the identifier, like:
4142 * int (*ident[3])[]
4143 * although the D style would be:
4144 * int[]*[3] ident
4145 */
4146 palt |= 1;
4147 nextToken();
4148 ts = parseDeclarator(t, palt, pident);
4149 check(TOK.rightParenthesis);
4150 break;
4151 }
4152 ts = t;
4153
4154 Token* peekt = &token;
4155 /* Completely disallow C-style things like:
4156 * T (a);
4157 * Improve error messages for the common bug of a missing return type
4158 * by looking to see if (a) looks like a parameter list.
4159 */
4160 if (isParameters(&peekt))
4161 {
4162 error("function declaration without return type. (Note that constructors are always named `this`)");
4163 }
4164 else
4165 error("unexpected `(` in declarator");
4166 break;
4167 }
4168 default:
4169 ts = t;
4170 break;
4171 }
4172
4173 // parse DeclaratorSuffixes
4174 while (1)
4175 {
4176 switch (token.value)
4177 {
4178 static if (CARRAYDECL)
4179 {
4180 /* Support C style array syntax:
4181 * int ident[]
4182 * as opposed to D-style:
4183 * int[] ident
4184 */
4185 case TOK.leftBracket:
4186 {
4187 // This is the old C-style post [] syntax.
4188 AST.TypeNext ta;
4189 nextToken();
4190 if (token.value == TOK.rightBracket)
4191 {
4192 // It's a dynamic array
4193 ta = new AST.TypeDArray(t); // []
4194 nextToken();
4195 palt |= 2;
4196 }
4197 else if (isDeclaration(&token, NeedDeclaratorId.no, TOK.rightBracket, null))
4198 {
4199 // It's an associative array
4200 //printf("it's an associative array\n");
4201 AST.Type index = parseType(); // [ type ]
4202 check(TOK.rightBracket);
4203 ta = new AST.TypeAArray(t, index);
4204 palt |= 2;
4205 }
4206 else
4207 {
4208 //printf("It's a static array\n");
4209 AST.Expression e = parseAssignExp(); // [ expression ]
4210 ta = new AST.TypeSArray(t, e);
4211 check(TOK.rightBracket);
4212 palt |= 2;
4213 }
4214
4215 /* Insert ta into
4216 * ts -> ... -> t
4217 * so that
4218 * ts -> ... -> ta -> t
4219 */
4220 AST.Type* pt;
4221 for (pt = &ts; *pt != t; pt = &(cast(AST.TypeNext)*pt).next)
4222 {
4223 }
4224 *pt = ta;
4225 continue;
4226 }
4227 }
4228 case TOK.leftParenthesis:
4229 {
4230 if (tpl)
4231 {
4232 Token* tk = peekPastParen(&token);
4233 if (tk.value == TOK.leftParenthesis)
4234 {
4235 /* Look ahead to see if this is (...)(...),
4236 * i.e. a function template declaration
4237 */
4238 //printf("function template declaration\n");
4239
4240 // Gather template parameter list
4241 *tpl = parseTemplateParameterList();
4242 }
4243 else if (tk.value == TOK.assign)
4244 {
4245 /* or (...) =,
4246 * i.e. a variable template declaration
4247 */
4248 //printf("variable template declaration\n");
4249 *tpl = parseTemplateParameterList();
4250 break;
4251 }
4252 }
4253
4254 auto parameterList = parseParameterList(null);
4255
4256 /* Parse const/immutable/shared/inout/nothrow/pure/return postfix
4257 */
4258 // merge prefix storage classes
4259 StorageClass stc = parsePostfix(storageClass, pudas);
4260
4261 AST.Type tf = new AST.TypeFunction(parameterList, t, linkage, stc);
4262 tf = tf.addSTC(stc);
4263 if (pdisable)
4264 *pdisable = stc & STC.disable ? true : false;
4265
4266 /* Insert tf into
4267 * ts -> ... -> t
4268 * so that
4269 * ts -> ... -> tf -> t
4270 */
4271 AST.Type* pt;
4272 for (pt = &ts; *pt != t; pt = &(cast(AST.TypeNext)*pt).next)
4273 {
4274 }
4275 *pt = tf;
4276 break;
4277 }
4278 default:
4279 break;
4280 }
4281 break;
4282 }
4283 return ts;
4284 }
4285
4286 private void parseStorageClasses(ref StorageClass storage_class, ref LINK link,
4287 ref bool setAlignment, ref AST.Expression ealign, ref AST.Expressions* udas,
4288 out Loc linkloc)
4289 {
4290 StorageClass stc;
4291 bool sawLinkage = false; // seen a linkage declaration
4292
4293 linkloc = Loc.initial;
4294
4295 while (1)
4296 {
4297 switch (token.value)
4298 {
4299 case TOK.const_:
4300 if (peekNext() == TOK.leftParenthesis)
4301 break; // const as type constructor
4302 stc = STC.const_; // const as storage class
4303 goto L1;
4304
4305 case TOK.immutable_:
4306 if (peekNext() == TOK.leftParenthesis)
4307 break;
4308 stc = STC.immutable_;
4309 goto L1;
4310
4311 case TOK.shared_:
4312 if (peekNext() == TOK.leftParenthesis)
4313 break;
4314 stc = STC.shared_;
4315 goto L1;
4316
4317 case TOK.inout_:
4318 if (peekNext() == TOK.leftParenthesis)
4319 break;
4320 stc = STC.wild;
4321 goto L1;
4322
4323 case TOK.static_:
4324 stc = STC.static_;
4325 goto L1;
4326
4327 case TOK.final_:
4328 stc = STC.final_;
4329 goto L1;
4330
4331 case TOK.auto_:
4332 stc = STC.auto_;
4333 goto L1;
4334
4335 case TOK.scope_:
4336 stc = STC.scope_;
4337 goto L1;
4338
4339 case TOK.override_:
4340 stc = STC.override_;
4341 goto L1;
4342
4343 case TOK.abstract_:
4344 stc = STC.abstract_;
4345 goto L1;
4346
4347 case TOK.synchronized_:
4348 stc = STC.synchronized_;
4349 goto L1;
4350
4351 case TOK.deprecated_:
4352 stc = STC.deprecated_;
4353 goto L1;
4354
4355 case TOK.nothrow_:
4356 stc = STC.nothrow_;
4357 goto L1;
4358
4359 case TOK.pure_:
4360 stc = STC.pure_;
4361 goto L1;
4362
4363 case TOK.ref_:
4364 stc = STC.ref_;
4365 goto L1;
4366
4367 case TOK.gshared:
4368 stc = STC.gshared;
4369 goto L1;
4370
4371 case TOK.enum_:
4372 {
4373 const tv = peekNext();
4374 if (tv == TOK.leftCurly || tv == TOK.colon)
4375 break;
4376 if (tv == TOK.identifier)
4377 {
4378 const nextv = peekNext2();
4379 if (nextv == TOK.leftCurly || nextv == TOK.colon || nextv == TOK.semicolon)
4380 break;
4381 }
4382 stc = STC.manifest;
4383 goto L1;
4384 }
4385
4386 case TOK.at:
4387 {
4388 stc = parseAttribute(udas);
4389 if (stc)
4390 goto L1;
4391 continue;
4392 }
4393 L1:
4394 storage_class = appendStorageClass(storage_class, stc);
4395 nextToken();
4396 continue;
4397
4398 case TOK.extern_:
4399 {
4400 if (peekNext() != TOK.leftParenthesis)
4401 {
4402 stc = STC.extern_;
4403 goto L1;
4404 }
4405
4406 if (sawLinkage)
4407 error("redundant linkage declaration");
4408 sawLinkage = true;
4409 linkloc = token.loc;
4410 auto res = parseLinkage();
4411 link = res.link;
4412 if (res.idents || res.identExps)
4413 {
4414 error("C++ name spaces not allowed here");
4415 }
4416 if (res.cppmangle != CPPMANGLE.def)
4417 {
4418 error("C++ mangle declaration not allowed here");
4419 }
4420 continue;
4421 }
4422 case TOK.align_:
4423 {
4424 nextToken();
4425 setAlignment = true;
4426 if (token.value == TOK.leftParenthesis)
4427 {
4428 nextToken();
4429 ealign = parseExpression();
4430 check(TOK.rightParenthesis);
4431 }
4432 continue;
4433 }
4434 default:
4435 break;
4436 }
4437 break;
4438 }
4439 }
4440
4441 /**********************************
4442 * Parse Declarations.
4443 * These can be:
4444 * 1. declarations at global/class level
4445 * 2. declarations at statement level
4446 * Return array of Declaration *'s.
4447 */
4448 private AST.Dsymbols* parseDeclarations(bool autodecl, PrefixAttributes!AST* pAttrs, const(char)* comment)
4449 {
4450 StorageClass storage_class = STC.undefined_;
4451 TOK tok = TOK.reserved;
4452 LINK link = linkage;
4453 Loc linkloc = this.linkLoc;
4454 bool setAlignment = false;
4455 AST.Expression ealign;
4456 AST.Expressions* udas = null;
4457
4458 //printf("parseDeclarations() %s\n", token.toChars());
4459 if (!comment)
4460 comment = token.blockComment.ptr;
4461
4462 /* Look for AliasAssignment:
4463 * identifier = type;
4464 */
4465 if (token.value == TOK.identifier && peekNext() == TOK.assign)
4466 {
4467 const loc = token.loc;
4468 auto ident = token.ident;
4469 nextToken();
4470 nextToken(); // advance past =
4471 auto t = parseType();
4472 AST.Dsymbol s = new AST.AliasAssign(loc, ident, t, null);
4473 check(TOK.semicolon);
4474 addComment(s, comment);
4475 auto a = new AST.Dsymbols();
4476 a.push(s);
4477 return a;
4478 }
4479
4480 if (token.value == TOK.alias_)
4481 {
4482 const loc = token.loc;
4483 tok = token.value;
4484 nextToken();
4485
4486 /* Look for:
4487 * alias identifier this;
4488 */
4489 if (token.value == TOK.identifier && peekNext() == TOK.this_)
4490 {
4491 auto s = new AST.AliasThis(loc, token.ident);
4492 nextToken();
4493 check(TOK.this_);
4494 check(TOK.semicolon);
4495 auto a = new AST.Dsymbols();
4496 a.push(s);
4497 addComment(s, comment);
4498 return a;
4499 }
4500 version (none)
4501 {
4502 /* Look for:
4503 * alias this = identifier;
4504 */
4505 if (token.value == TOK.this_ && peekNext() == TOK.assign && peekNext2() == TOK.identifier)
4506 {
4507 check(TOK.this_);
4508 check(TOK.assign);
4509 auto s = new AliasThis(loc, token.ident);
4510 nextToken();
4511 check(TOK.semicolon);
4512 auto a = new Dsymbols();
4513 a.push(s);
4514 addComment(s, comment);
4515 return a;
4516 }
4517 }
4518 /* Look for:
4519 * alias identifier = type;
4520 * alias identifier(...) = type;
4521 */
4522 if (token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign))
4523 {
4524 auto a = new AST.Dsymbols();
4525 while (1)
4526 {
4527 auto ident = token.ident;
4528 nextToken();
4529 AST.TemplateParameters* tpl = null;
4530 if (token.value == TOK.leftParenthesis)
4531 tpl = parseTemplateParameterList();
4532 check(TOK.assign);
4533
4534 bool hasParsedAttributes;
4535 void parseAttributes()
4536 {
4537 if (hasParsedAttributes) // only parse once
4538 return;
4539 hasParsedAttributes = true;
4540 udas = null;
4541 storage_class = STC.undefined_;
4542 link = linkage;
4543 linkloc = this.linkLoc;
4544 setAlignment = false;
4545 ealign = null;
4546 parseStorageClasses(storage_class, link, setAlignment, ealign, udas, linkloc);
4547 }
4548
4549 if (token.value == TOK.at)
4550 parseAttributes;
4551
4552 AST.Declaration v;
4553 AST.Dsymbol s;
4554
4555 // try to parse function type:
4556 // TypeCtors? BasicType ( Parameters ) MemberFunctionAttributes
4557 bool attributesAppended;
4558 const StorageClass funcStc = parseTypeCtor();
4559 Token* tlu = &token;
4560 Token* tk;
4561 if (token.value != TOK.function_ &&
4562 token.value != TOK.delegate_ &&
4563 isBasicType(&tlu) && tlu &&
4564 tlu.value == TOK.leftParenthesis)
4565 {
4566 AST.Type tret = parseBasicType();
4567 auto parameterList = parseParameterList(null);
4568
4569 parseAttributes();
4570 if (udas)
4571 error("user-defined attributes not allowed for `alias` declarations");
4572
4573 attributesAppended = true;
4574 storage_class = appendStorageClass(storage_class, funcStc);
4575 AST.Type tf = new AST.TypeFunction(parameterList, tret, link, storage_class);
4576 v = new AST.AliasDeclaration(loc, ident, tf);
4577 }
4578 else if (token.value == TOK.function_ ||
4579 token.value == TOK.delegate_ ||
4580 token.value == TOK.leftParenthesis &&
4581 skipAttributes(peekPastParen(&token), &tk) &&
4582 (tk.value == TOK.goesTo || tk.value == TOK.leftCurly) ||
4583 token.value == TOK.leftCurly ||
4584 token.value == TOK.identifier && peekNext() == TOK.goesTo ||
4585 token.value == TOK.ref_ && peekNext() == TOK.leftParenthesis &&
4586 skipAttributes(peekPastParen(peek(&token)), &tk) &&
4587 (tk.value == TOK.goesTo || tk.value == TOK.leftCurly)
4588 )
4589 {
4590 // function (parameters) { statements... }
4591 // delegate (parameters) { statements... }
4592 // (parameters) { statements... }
4593 // (parameters) => expression
4594 // { statements... }
4595 // identifier => expression
4596 // ref (parameters) { statements... }
4597 // ref (parameters) => expression
4598
4599 s = parseFunctionLiteral();
4600
4601 if (udas !is null)
4602 {
4603 if (storage_class != 0)
4604 error("Cannot put a storage-class in an alias declaration.");
4605 // parseAttributes shouldn't have set these variables
4606 assert(link == linkage && !setAlignment && ealign is null);
4607 auto tpl_ = cast(AST.TemplateDeclaration) s;
4608 assert(tpl_ !is null && tpl_.members.dim == 1);
4609 auto fd = cast(AST.FuncLiteralDeclaration) (*tpl_.members)[0];
4610 auto tf = cast(AST.TypeFunction) fd.type;
4611 assert(tf.parameterList.parameters.dim > 0);
4612 auto as = new AST.Dsymbols();
4613 (*tf.parameterList.parameters)[0].userAttribDecl = new AST.UserAttributeDeclaration(udas, as);
4614 }
4615
4616 v = new AST.AliasDeclaration(loc, ident, s);
4617 }
4618 else
4619 {
4620 parseAttributes();
4621 // type
4622 if (udas)
4623 error("user-defined attributes not allowed for `%s` declarations", Token.toChars(tok));
4624
4625 auto t = parseType();
4626
4627 // Disallow meaningless storage classes on type aliases
4628 if (storage_class)
4629 {
4630 // Don't raise errors for STC that are part of a function/delegate type, e.g.
4631 // `alias F = ref pure nothrow @nogc @safe int function();`
4632 auto tp = t.isTypePointer;
4633 const isFuncType = (tp && tp.next.isTypeFunction) || t.isTypeDelegate;
4634 const remStc = isFuncType ? (storage_class & ~STC.FUNCATTR) : storage_class;
4635
4636 if (remStc)
4637 {
4638 OutBuffer buf;
4639 AST.stcToBuffer(&buf, remStc);
4640 // @@@DEPRECATED_2.093@@@
4641 // Deprecated in 2020-07, can be made an error in 2.103
4642 deprecation("storage class `%s` has no effect in type aliases", buf.peekChars());
4643 }
4644 }
4645
4646 v = new AST.AliasDeclaration(loc, ident, t);
4647 }
4648 if (!attributesAppended)
4649 storage_class = appendStorageClass(storage_class, funcStc);
4650 v.storage_class = storage_class;
4651
4652 s = v;
4653 if (tpl)
4654 {
4655 auto a2 = new AST.Dsymbols();
4656 a2.push(s);
4657 auto tempdecl = new AST.TemplateDeclaration(loc, ident, tpl, null, a2);
4658 s = tempdecl;
4659 }
4660 if (link != linkage)
4661 {
4662 auto a2 = new AST.Dsymbols();
4663 a2.push(s);
4664 s = new AST.LinkDeclaration(linkloc, link, a2);
4665 }
4666 a.push(s);
4667
4668 switch (token.value)
4669 {
4670 case TOK.semicolon:
4671 nextToken();
4672 addComment(s, comment);
4673 break;
4674
4675 case TOK.comma:
4676 nextToken();
4677 addComment(s, comment);
4678 if (token.value != TOK.identifier)
4679 {
4680 error("identifier expected following comma, not `%s`", token.toChars());
4681 break;
4682 }
4683 if (peekNext() != TOK.assign && peekNext() != TOK.leftParenthesis)
4684 {
4685 error("`=` expected following identifier");
4686 nextToken();
4687 break;
4688 }
4689 continue;
4690
4691 default:
4692 error("semicolon expected to close `%s` declaration", Token.toChars(tok));
4693 break;
4694 }
4695 break;
4696 }
4697 return a;
4698 }
4699
4700 // alias StorageClasses type ident;
4701 }
4702
4703 AST.Type ts;
4704
4705 if (!autodecl)
4706 {
4707 parseStorageClasses(storage_class, link, setAlignment, ealign, udas, linkloc);
4708
4709 if (token.value == TOK.enum_)
4710 {
4711 AST.Dsymbol d = parseEnum();
4712 auto a = new AST.Dsymbols();
4713 a.push(d);
4714
4715 if (udas)
4716 {
4717 d = new AST.UserAttributeDeclaration(udas, a);
4718 a = new AST.Dsymbols();
4719 a.push(d);
4720 }
4721
4722 addComment(d, comment);
4723 return a;
4724 }
4725 if (token.value == TOK.struct_ ||
4726 token.value == TOK.union_ ||
4727 token.value == TOK.class_ ||
4728 token.value == TOK.interface_)
4729 {
4730 AST.Dsymbol s = parseAggregate();
4731 auto a = new AST.Dsymbols();
4732 a.push(s);
4733
4734 if (storage_class)
4735 {
4736 s = new AST.StorageClassDeclaration(storage_class, a);
4737 a = new AST.Dsymbols();
4738 a.push(s);
4739 }
4740 if (setAlignment)
4741 {
4742 s = new AST.AlignDeclaration(s.loc, ealign, a);
4743 a = new AST.Dsymbols();
4744 a.push(s);
4745 }
4746 if (link != linkage)
4747 {
4748 s = new AST.LinkDeclaration(linkloc, link, a);
4749 a = new AST.Dsymbols();
4750 a.push(s);
4751 }
4752 if (udas)
4753 {
4754 s = new AST.UserAttributeDeclaration(udas, a);
4755 a = new AST.Dsymbols();
4756 a.push(s);
4757 }
4758
4759 addComment(s, comment);
4760 return a;
4761 }
4762
4763 /* Look for auto initializers:
4764 * storage_class identifier = initializer;
4765 * storage_class identifier(...) = initializer;
4766 */
4767 if ((storage_class || udas) && token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign))
4768 {
4769 AST.Dsymbols* a = parseAutoDeclarations(storage_class, comment);
4770 if (udas)
4771 {
4772 AST.Dsymbol s = new AST.UserAttributeDeclaration(udas, a);
4773 a = new AST.Dsymbols();
4774 a.push(s);
4775 }
4776 return a;
4777 }
4778
4779 /* Look for return type inference for template functions.
4780 */
4781 {
4782 Token* tk;
4783 if ((storage_class || udas) && token.value == TOK.identifier && skipParens(peek(&token), &tk) &&
4784 skipAttributes(tk, &tk) &&
4785 (tk.value == TOK.leftParenthesis || tk.value == TOK.leftCurly || tk.value == TOK.in_ || tk.value == TOK.out_ || tk.value == TOK.goesTo ||
4786 tk.value == TOK.do_ || tk.value == TOK.identifier && tk.ident == Id._body))
4787 {
4788 // @@@DEPRECATED@@@
4789 // https://github.com/dlang/DIPs/blob/1f5959abe482b1f9094f6484a7d0a3ade77fc2fc/DIPs/accepted/DIP1003.md
4790 // Deprecated in 2.097 - Can be removed from 2.117
4791 // The deprecation period is longer than usual as `body`
4792 // was quite widely used.
4793 if (tk.value == TOK.identifier && tk.ident == Id._body)
4794 deprecation("Usage of the `body` keyword is deprecated. Use `do` instead.");
4795
4796 ts = null;
4797 }
4798 else
4799 {
4800 ts = parseBasicType();
4801 ts = parseTypeSuffixes(ts);
4802 }
4803 }
4804 }
4805
4806 if (pAttrs)
4807 {
4808 storage_class |= pAttrs.storageClass;
4809 //pAttrs.storageClass = STC.undefined_;
4810 }
4811
4812 AST.Type tfirst = null;
4813 auto a = new AST.Dsymbols();
4814
4815 while (1)
4816 {
4817 AST.TemplateParameters* tpl = null;
4818 bool disable;
4819 int alt = 0;
4820
4821 const loc = token.loc;
4822 Identifier ident;
4823
4824 auto t = parseDeclarator(ts, alt, &ident, &tpl, storage_class, &disable, &udas);
4825 assert(t);
4826 if (!tfirst)
4827 tfirst = t;
4828 else if (t != tfirst)
4829 error("multiple declarations must have the same type, not `%s` and `%s`", tfirst.toChars(), t.toChars());
4830
4831 bool isThis = (t.ty == Tident && (cast(AST.TypeIdentifier)t).ident == Id.This && token.value == TOK.assign);
4832 if (ident)
4833 checkCstyleTypeSyntax(loc, t, alt, ident);
4834 else if (!isThis && (t != AST.Type.terror))
4835 error("no identifier for declarator `%s`", t.toChars());
4836
4837 if (tok == TOK.alias_)
4838 {
4839 AST.Declaration v;
4840 AST.Initializer _init = null;
4841
4842 /* Aliases can no longer have multiple declarators, storage classes,
4843 * linkages, or auto declarations.
4844 * These never made any sense, anyway.
4845 * The code below needs to be fixed to reject them.
4846 * The grammar has already been fixed to preclude them.
4847 */
4848
4849 if (udas)
4850 error("user-defined attributes not allowed for `%s` declarations", Token.toChars(tok));
4851
4852 if (token.value == TOK.assign)
4853 {
4854 nextToken();
4855 _init = parseInitializer();
4856 }
4857 if (_init)
4858 {
4859 if (isThis)
4860 error("cannot use syntax `alias this = %s`, use `alias %s this` instead", _init.toChars(), _init.toChars());
4861 else
4862 error("alias cannot have initializer");
4863 }
4864 v = new AST.AliasDeclaration(loc, ident, t);
4865
4866 v.storage_class = storage_class;
4867 if (pAttrs)
4868 {
4869 /* AliasDeclaration distinguish @safe, @system, @trusted attributes
4870 * on prefix and postfix.
4871 * @safe alias void function() FP1;
4872 * alias @safe void function() FP2; // FP2 is not @safe
4873 * alias void function() @safe FP3;
4874 */
4875 pAttrs.storageClass &= STC.safeGroup;
4876 }
4877 AST.Dsymbol s = v;
4878
4879 if (link != linkage)
4880 {
4881 auto ax = new AST.Dsymbols();
4882 ax.push(v);
4883 s = new AST.LinkDeclaration(linkloc, link, ax);
4884 }
4885 a.push(s);
4886 switch (token.value)
4887 {
4888 case TOK.semicolon:
4889 nextToken();
4890 addComment(s, comment);
4891 break;
4892
4893 case TOK.comma:
4894 nextToken();
4895 addComment(s, comment);
4896 continue;
4897
4898 default:
4899 error("semicolon expected to close `%s` declaration", Token.toChars(tok));
4900 break;
4901 }
4902 }
4903 else if (t.ty == Tfunction)
4904 {
4905 AST.Expression constraint = null;
4906 //printf("%s funcdecl t = %s, storage_class = x%lx\n", loc.toChars(), t.toChars(), storage_class);
4907 auto f = new AST.FuncDeclaration(loc, Loc.initial, ident, storage_class | (disable ? STC.disable : 0), t);
4908 if (pAttrs)
4909 pAttrs.storageClass = STC.undefined_;
4910 if (tpl)
4911 constraint = parseConstraint();
4912 AST.Dsymbol s = parseContracts(f);
4913 auto tplIdent = s.ident;
4914
4915 if (link != linkage)
4916 {
4917 auto ax = new AST.Dsymbols();
4918 ax.push(s);
4919 s = new AST.LinkDeclaration(linkloc, link, ax);
4920 }
4921 if (udas)
4922 {
4923 auto ax = new AST.Dsymbols();
4924 ax.push(s);
4925 s = new AST.UserAttributeDeclaration(udas, ax);
4926 }
4927
4928 /* A template parameter list means it's a function template
4929 */
4930 if (tpl)
4931 {
4932 // Wrap a template around the function declaration
4933 auto decldefs = new AST.Dsymbols();
4934 decldefs.push(s);
4935 auto tempdecl = new AST.TemplateDeclaration(loc, tplIdent, tpl, constraint, decldefs);
4936 s = tempdecl;
4937
4938 StorageClass stc2 = STC.undefined_;
4939 if (storage_class & STC.static_)
4940 {
4941 assert(f.storage_class & STC.static_);
4942 f.storage_class &= ~STC.static_;
4943 stc2 |= STC.static_;
4944 }
4945 if (storage_class & STC.deprecated_)
4946 {
4947 assert(f.storage_class & STC.deprecated_);
4948 f.storage_class &= ~STC.deprecated_;
4949 stc2 |= STC.deprecated_;
4950 }
4951 if (stc2 != STC.undefined_)
4952 {
4953 auto ax = new AST.Dsymbols();
4954 ax.push(s);
4955 s = new AST.StorageClassDeclaration(stc2, ax);
4956 }
4957 }
4958 a.push(s);
4959 addComment(s, comment);
4960 }
4961 else if (ident)
4962 {
4963 AST.Initializer _init = null;
4964 if (token.value == TOK.assign)
4965 {
4966 nextToken();
4967 _init = parseInitializer();
4968 }
4969
4970 auto v = new AST.VarDeclaration(loc, t, ident, _init);
4971 v.storage_class = storage_class;
4972 if (pAttrs)
4973 pAttrs.storageClass = STC.undefined_;
4974
4975 AST.Dsymbol s = v;
4976
4977 if (tpl && _init)
4978 {
4979 auto a2 = new AST.Dsymbols();
4980 a2.push(s);
4981 auto tempdecl = new AST.TemplateDeclaration(loc, ident, tpl, null, a2, 0);
4982 s = tempdecl;
4983 }
4984 if (setAlignment)
4985 {
4986 auto ax = new AST.Dsymbols();
4987 ax.push(s);
4988 s = new AST.AlignDeclaration(v.loc, ealign, ax);
4989 }
4990 if (link != linkage)
4991 {
4992 auto ax = new AST.Dsymbols();
4993 ax.push(s);
4994 s = new AST.LinkDeclaration(linkloc, link, ax);
4995 }
4996 if (udas)
4997 {
4998 auto ax = new AST.Dsymbols();
4999 ax.push(s);
5000 s = new AST.UserAttributeDeclaration(udas, ax);
5001 }
5002 a.push(s);
5003 switch (token.value)
5004 {
5005 case TOK.semicolon:
5006 nextToken();
5007 addComment(s, comment);
5008 break;
5009
5010 case TOK.comma:
5011 nextToken();
5012 addComment(s, comment);
5013 continue;
5014
5015 default:
5016 error("semicolon expected, not `%s`", token.toChars());
5017 break;
5018 }
5019 }
5020 break;
5021 }
5022 return a;
5023 }
5024
5025 private AST.Dsymbol parseFunctionLiteral()
5026 {
5027 const loc = token.loc;
5028 AST.TemplateParameters* tpl = null;
5029 AST.ParameterList parameterList;
5030 AST.Type tret = null;
5031 StorageClass stc = 0;
5032 TOK save = TOK.reserved;
5033
5034 switch (token.value)
5035 {
5036 case TOK.function_:
5037 case TOK.delegate_:
5038 save = token.value;
5039 nextToken();
5040 if (token.value == TOK.ref_)
5041 {
5042 // function ref (parameters) { statements... }
5043 // delegate ref (parameters) { statements... }
5044 stc = STC.ref_;
5045 nextToken();
5046 }
5047 if (token.value != TOK.leftParenthesis && token.value != TOK.leftCurly)
5048 {
5049 // function type (parameters) { statements... }
5050 // delegate type (parameters) { statements... }
5051 tret = parseBasicType();
5052 tret = parseTypeSuffixes(tret); // function return type
5053 }
5054
5055 if (token.value == TOK.leftParenthesis)
5056 {
5057 // function (parameters) { statements... }
5058 // delegate (parameters) { statements... }
5059 }
5060 else
5061 {
5062 // function { statements... }
5063 // delegate { statements... }
5064 break;
5065 }
5066 goto case TOK.leftParenthesis;
5067
5068 case TOK.ref_:
5069 {
5070 // ref (parameters) => expression
5071 // ref (parameters) { statements... }
5072 stc = STC.ref_;
5073 nextToken();
5074 goto case TOK.leftParenthesis;
5075 }
5076 case TOK.leftParenthesis:
5077 {
5078 // (parameters) => expression
5079 // (parameters) { statements... }
5080 parameterList = parseParameterList(&tpl);
5081 stc = parsePostfix(stc, null);
5082 if (StorageClass modStc = stc & STC.TYPECTOR)
5083 {
5084 if (save == TOK.function_)
5085 {
5086 OutBuffer buf;
5087 AST.stcToBuffer(&buf, modStc);
5088 error("function literal cannot be `%s`", buf.peekChars());
5089 }
5090 else
5091 save = TOK.delegate_;
5092 }
5093 break;
5094 }
5095 case TOK.leftCurly:
5096 // { statements... }
5097 break;
5098
5099 case TOK.identifier:
5100 {
5101 // identifier => expression
5102 parameterList.parameters = new AST.Parameters();
5103 Identifier id = Identifier.generateId("__T");
5104 AST.Type t = new AST.TypeIdentifier(loc, id);
5105 parameterList.parameters.push(new AST.Parameter(STC.parameter, t, token.ident, null, null));
5106
5107 tpl = new AST.TemplateParameters();
5108 AST.TemplateParameter tp = new AST.TemplateTypeParameter(loc, id, null, null);
5109 tpl.push(tp);
5110
5111 nextToken();
5112 break;
5113 }
5114 default:
5115 assert(0);
5116 }
5117
5118 auto tf = new AST.TypeFunction(parameterList, tret, linkage, stc);
5119 tf = cast(AST.TypeFunction)tf.addSTC(stc);
5120 auto fd = new AST.FuncLiteralDeclaration(loc, Loc.initial, tf, save, null);
5121
5122 if (token.value == TOK.goesTo)
5123 {
5124 check(TOK.goesTo);
5125 if (token.value == TOK.leftCurly)
5126 {
5127 deprecation("Using `(args) => { ... }` to create a delegate that returns a delegate is error-prone.");
5128 deprecationSupplemental(token.loc, "Use `(args) { ... }` for a multi-statement function literal or use `(args) => () { }` if you intended for the lambda to return a delegate.");
5129 }
5130 const returnloc = token.loc;
5131 AST.Expression ae = parseAssignExp();
5132 fd.fbody = new AST.ReturnStatement(returnloc, ae);
5133 fd.endloc = token.loc;
5134 }
5135 else
5136 {
5137 parseContracts(fd);
5138 }
5139
5140 if (tpl)
5141 {
5142 // Wrap a template around function fd
5143 auto decldefs = new AST.Dsymbols();
5144 decldefs.push(fd);
5145 return new AST.TemplateDeclaration(fd.loc, fd.ident, tpl, null, decldefs, false, true);
5146 }
5147 return fd;
5148 }
5149
5150 /*****************************************
5151 * Parse contracts following function declaration.
5152 */
5153 private AST.FuncDeclaration parseContracts(AST.FuncDeclaration f)
5154 {
5155 LINK linksave = linkage;
5156
5157 bool literal = f.isFuncLiteralDeclaration() !is null;
5158
5159 // The following is irrelevant, as it is overridden by sc.linkage in
5160 // TypeFunction::semantic
5161 linkage = LINK.d; // nested functions have D linkage
5162 bool requireDo = false;
5163 L1:
5164 switch (token.value)
5165 {
5166 case TOK.goesTo:
5167 if (requireDo)
5168 error("missing `do { ... }` after `in` or `out`");
5169 if (!global.params.shortenedMethods)
5170 error("=> shortened method not enabled, compile with compiler switch `-preview=shortenedMethods`");
5171 const returnloc = token.loc;
5172 nextToken();
5173 f.fbody = new AST.ReturnStatement(returnloc, parseExpression());
5174 f.endloc = token.loc;
5175 check(TOK.semicolon);
5176 break;
5177
5178 case TOK.leftCurly:
5179 if (requireDo)
5180 error("missing `do { ... }` after `in` or `out`");
5181 f.fbody = parseStatement(ParseStatementFlags.semi);
5182 f.endloc = endloc;
5183 break;
5184
5185 case TOK.identifier:
5186 if (token.ident == Id._body)
5187 {
5188 // @@@DEPRECATED@@@
5189 // https://github.com/dlang/DIPs/blob/1f5959abe482b1f9094f6484a7d0a3ade77fc2fc/DIPs/accepted/DIP1003.md
5190 // Deprecated in 2.097 - Can be removed from 2.117
5191 // The deprecation period is longer than usual as `body`
5192 // was quite widely used.
5193 deprecation("Usage of the `body` keyword is deprecated. Use `do` instead.");
5194 goto case TOK.do_;
5195 }
5196 goto default;
5197
5198 case TOK.do_:
5199 nextToken();
5200 f.fbody = parseStatement(ParseStatementFlags.curly);
5201 f.endloc = endloc;
5202 break;
5203
5204 version (none)
5205 {
5206 // Do we want this for function declarations, so we can do:
5207 // int x, y, foo(), z;
5208 case TOK.comma:
5209 nextToken();
5210 continue;
5211 }
5212
5213 case TOK.in_:
5214 // in { statements... }
5215 // in (expression)
5216 auto loc = token.loc;
5217 nextToken();
5218 if (!f.frequires)
5219 {
5220 f.frequires = new AST.Statements;
5221 }
5222 if (token.value == TOK.leftParenthesis)
5223 {
5224 nextToken();
5225 AST.Expression e = parseAssignExp(), msg = null;
5226 if (token.value == TOK.comma)
5227 {
5228 nextToken();
5229 if (token.value != TOK.rightParenthesis)
5230 {
5231 msg = parseAssignExp();
5232 if (token.value == TOK.comma)
5233 nextToken();
5234 }
5235 }
5236 check(TOK.rightParenthesis);
5237 e = new AST.AssertExp(loc, e, msg);
5238 f.frequires.push(new AST.ExpStatement(loc, e));
5239 requireDo = false;
5240 }
5241 else
5242 {
5243 f.frequires.push(parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_));
5244 requireDo = true;
5245 }
5246 goto L1;
5247
5248 case TOK.out_:
5249 // out { statements... }
5250 // out (; expression)
5251 // out (identifier) { statements... }
5252 // out (identifier; expression)
5253 auto loc = token.loc;
5254 nextToken();
5255 if (!f.fensures)
5256 {
5257 f.fensures = new AST.Ensures;
5258 }
5259 Identifier id = null;
5260 if (token.value != TOK.leftCurly)
5261 {
5262 check(TOK.leftParenthesis);
5263 if (token.value != TOK.identifier && token.value != TOK.semicolon)
5264 error("`(identifier) { ... }` or `(identifier; expression)` following `out` expected, not `%s`", token.toChars());
5265 if (token.value != TOK.semicolon)
5266 {
5267 id = token.ident;
5268 nextToken();
5269 }
5270 if (token.value == TOK.semicolon)
5271 {
5272 nextToken();
5273 AST.Expression e = parseAssignExp(), msg = null;
5274 if (token.value == TOK.comma)
5275 {
5276 nextToken();
5277 if (token.value != TOK.rightParenthesis)
5278 {
5279 msg = parseAssignExp();
5280 if (token.value == TOK.comma)
5281 nextToken();
5282 }
5283 }
5284 check(TOK.rightParenthesis);
5285 e = new AST.AssertExp(loc, e, msg);
5286 f.fensures.push(AST.Ensure(id, new AST.ExpStatement(loc, e)));
5287 requireDo = false;
5288 goto L1;
5289 }
5290 check(TOK.rightParenthesis);
5291 }
5292 f.fensures.push(AST.Ensure(id, parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_)));
5293 requireDo = true;
5294 goto L1;
5295
5296 case TOK.semicolon:
5297 if (!literal)
5298 {
5299 // https://issues.dlang.org/show_bug.cgi?id=15799
5300 // Semicolon becomes a part of function declaration
5301 // only when 'do' is not required
5302 if (!requireDo)
5303 nextToken();
5304 break;
5305 }
5306 goto default;
5307
5308 default:
5309 if (literal)
5310 {
5311 const(char)* sbody = requireDo ? "do " : "";
5312 error("missing `%s{ ... }` for function literal", sbody);
5313 }
5314 else if (!requireDo) // allow contracts even with no body
5315 {
5316 TOK t = token.value;
5317 if (t == TOK.const_ || t == TOK.immutable_ || t == TOK.inout_ || t == TOK.return_ ||
5318 t == TOK.shared_ || t == TOK.nothrow_ || t == TOK.pure_)
5319 error("'%s' cannot be placed after a template constraint", token.toChars);
5320 else if (t == TOK.at)
5321 error("attributes cannot be placed after a template constraint");
5322 else if (t == TOK.if_)
5323 error("cannot use function constraints for non-template functions. Use `static if` instead");
5324 else
5325 error("semicolon expected following function declaration");
5326 }
5327 break;
5328 }
5329 if (literal && !f.fbody)
5330 {
5331 // Set empty function body for error recovery
5332 f.fbody = new AST.CompoundStatement(Loc.initial, cast(AST.Statement)null);
5333 }
5334
5335 linkage = linksave;
5336
5337 return f;
5338 }
5339
5340 /*****************************************
5341 */
5342 private void checkDanglingElse(Loc elseloc)
5343 {
5344 if (token.value != TOK.else_ && token.value != TOK.catch_ && token.value != TOK.finally_ && lookingForElse.linnum != 0)
5345 {
5346 warning(elseloc, "else is dangling, add { } after condition at %s", lookingForElse.toChars());
5347 }
5348 }
5349
5350 /* *************************
5351 * Issue errors if C-style syntax
5352 * Params:
5353 * alt = !=0 for C-style syntax
5354 */
5355 private void checkCstyleTypeSyntax(Loc loc, AST.Type t, int alt, Identifier ident)
5356 {
5357 if (!alt)
5358 return;
5359
5360 const(char)* sp = !ident ? "" : " ";
5361 const(char)* s = !ident ? "" : ident.toChars();
5362 error(loc, "instead of C-style syntax, use D-style `%s%s%s`", t.toChars(), sp, s);
5363 }
5364
5365 /*****************************************
5366 * Parses `foreach` statements, `static foreach` statements and
5367 * `static foreach` declarations.
5368 * Params:
5369 * Foreach = one of Statement, StaticForeachStatement, StaticForeachDeclaration
5370 * loc = location of foreach
5371 * pLastDecl = non-null for StaticForeachDeclaration
5372 * Returns:
5373 * the Foreach generated
5374 */
5375 private Foreach parseForeach(alias Foreach)(Loc loc, AST.Dsymbol* pLastDecl)
5376 {
5377 static if (is(Foreach == AST.StaticForeachStatement) || is(Foreach == AST.StaticForeachDeclaration))
5378 {
5379 nextToken();
5380 }
5381
5382 TOK op = token.value;
5383
5384 nextToken();
5385 check(TOK.leftParenthesis);
5386
5387 auto parameters = new AST.Parameters();
5388 while (1)
5389 {
5390 Identifier ai = null;
5391 AST.Type at;
5392
5393 StorageClass storageClass = 0;
5394 StorageClass stc = 0;
5395 Lagain:
5396 if (stc)
5397 {
5398 storageClass = appendStorageClass(storageClass, stc);
5399 nextToken();
5400 }
5401 switch (token.value)
5402 {
5403 case TOK.ref_:
5404 stc = STC.ref_;
5405 goto Lagain;
5406
5407 case TOK.scope_:
5408 stc = STC.scope_;
5409 goto Lagain;
5410
5411 case TOK.enum_:
5412 stc = STC.manifest;
5413 goto Lagain;
5414
5415 case TOK.alias_:
5416 storageClass = appendStorageClass(storageClass, STC.alias_);
5417 nextToken();
5418 break;
5419
5420 case TOK.const_:
5421 if (peekNext() != TOK.leftParenthesis)
5422 {
5423 stc = STC.const_;
5424 goto Lagain;
5425 }
5426 break;
5427
5428 case TOK.immutable_:
5429 if (peekNext() != TOK.leftParenthesis)
5430 {
5431 stc = STC.immutable_;
5432 goto Lagain;
5433 }
5434 break;
5435
5436 case TOK.shared_:
5437 if (peekNext() != TOK.leftParenthesis)
5438 {
5439 stc = STC.shared_;
5440 goto Lagain;
5441 }
5442 break;
5443
5444 case TOK.inout_:
5445 if (peekNext() != TOK.leftParenthesis)
5446 {
5447 stc = STC.wild;
5448 goto Lagain;
5449 }
5450 break;
5451
5452 default:
5453 break;
5454 }
5455 if (token.value == TOK.identifier)
5456 {
5457 const tv = peekNext();
5458 if (tv == TOK.comma || tv == TOK.semicolon)
5459 {
5460 ai = token.ident;
5461 at = null; // infer argument type
5462 nextToken();
5463 goto Larg;
5464 }
5465 }
5466 at = parseType(&ai);
5467 if (!ai)
5468 error("no identifier for declarator `%s`", at.toChars());
5469 Larg:
5470 auto p = new AST.Parameter(storageClass, at, ai, null, null);
5471 parameters.push(p);
5472 if (token.value == TOK.comma)
5473 {
5474 nextToken();
5475 continue;
5476 }
5477 break;
5478 }
5479 check(TOK.semicolon);
5480
5481 AST.Expression aggr = parseExpression();
5482 if (token.value == TOK.slice && parameters.dim == 1)
5483 {
5484 AST.Parameter p = (*parameters)[0];
5485 nextToken();
5486 AST.Expression upr = parseExpression();
5487 check(TOK.rightParenthesis);
5488 Loc endloc;
5489 static if (is(Foreach == AST.Statement) || is(Foreach == AST.StaticForeachStatement))
5490 {
5491 AST.Statement _body = parseStatement(0, null, &endloc);
5492 }
5493 else
5494 {
5495 AST.Statement _body = null;
5496 }
5497 auto rangefe = new AST.ForeachRangeStatement(loc, op, p, aggr, upr, _body, endloc);
5498 static if (is(Foreach == AST.Statement))
5499 {
5500 return rangefe;
5501 }
5502 else static if(is(Foreach == AST.StaticForeachDeclaration))
5503 {
5504 return new AST.StaticForeachDeclaration(new AST.StaticForeach(loc, null, rangefe), parseBlock(pLastDecl));
5505 }
5506 else static if (is(Foreach == AST.StaticForeachStatement))
5507 {
5508 return new AST.StaticForeachStatement(loc, new AST.StaticForeach(loc, null, rangefe));
5509 }
5510 }
5511 else
5512 {
5513 check(TOK.rightParenthesis);
5514 Loc endloc;
5515 static if (is(Foreach == AST.Statement) || is(Foreach == AST.StaticForeachStatement))
5516 {
5517 AST.Statement _body = parseStatement(0, null, &endloc);
5518 }
5519 else
5520 {
5521 AST.Statement _body = null;
5522 }
5523 auto aggrfe = new AST.ForeachStatement(loc, op, parameters, aggr, _body, endloc);
5524 static if (is(Foreach == AST.Statement))
5525 {
5526 return aggrfe;
5527 }
5528 else static if(is(Foreach == AST.StaticForeachDeclaration))
5529 {
5530 return new AST.StaticForeachDeclaration(new AST.StaticForeach(loc, aggrfe, null), parseBlock(pLastDecl));
5531 }
5532 else static if (is(Foreach == AST.StaticForeachStatement))
5533 {
5534 return new AST.StaticForeachStatement(loc, new AST.StaticForeach(loc, aggrfe, null));
5535 }
5536 }
5537
5538 }
5539
5540 /***
5541 * Parse an assignment condition for if or while statements.
5542 *
5543 * Returns:
5544 * The variable that is declared inside the condition
5545 */
5546 AST.Parameter parseAssignCondition()
5547 {
5548 AST.Parameter param = null;
5549 StorageClass storageClass = 0;
5550 StorageClass stc = 0;
5551 LagainStc:
5552 if (stc)
5553 {
5554 storageClass = appendStorageClass(storageClass, stc);
5555 nextToken();
5556 }
5557 switch (token.value)
5558 {
5559 case TOK.ref_:
5560 stc = STC.ref_;
5561 goto LagainStc;
5562
5563 case TOK.scope_:
5564 stc = STC.scope_;
5565 goto LagainStc;
5566
5567 case TOK.auto_:
5568 stc = STC.auto_;
5569 goto LagainStc;
5570
5571 case TOK.const_:
5572 if (peekNext() != TOK.leftParenthesis)
5573 {
5574 stc = STC.const_;
5575 goto LagainStc;
5576 }
5577 break;
5578
5579 case TOK.immutable_:
5580 if (peekNext() != TOK.leftParenthesis)
5581 {
5582 stc = STC.immutable_;
5583 goto LagainStc;
5584 }
5585 break;
5586
5587 case TOK.shared_:
5588 if (peekNext() != TOK.leftParenthesis)
5589 {
5590 stc = STC.shared_;
5591 goto LagainStc;
5592 }
5593 break;
5594
5595 case TOK.inout_:
5596 if (peekNext() != TOK.leftParenthesis)
5597 {
5598 stc = STC.wild;
5599 goto LagainStc;
5600 }
5601 break;
5602
5603 default:
5604 break;
5605 }
5606 auto n = peek(&token);
5607 if (storageClass != 0 && token.value == TOK.identifier && n.value == TOK.assign)
5608 {
5609 Identifier ai = token.ident;
5610 AST.Type at = null; // infer parameter type
5611 nextToken();
5612 check(TOK.assign);
5613 param = new AST.Parameter(storageClass, at, ai, null, null);
5614 }
5615 else if (isDeclaration(&token, NeedDeclaratorId.must, TOK.assign, null))
5616 {
5617 Identifier ai;
5618 AST.Type at = parseType(&ai);
5619 check(TOK.assign);
5620 param = new AST.Parameter(storageClass, at, ai, null, null);
5621 }
5622 else if (storageClass != 0)
5623 error("found `%s` while expecting `=` or identifier", n.toChars());
5624
5625 return param;
5626 }
5627
5628 /*****************************************
5629 * Input:
5630 * flags PSxxxx
5631 * Output:
5632 * pEndloc if { ... statements ... }, store location of closing brace, otherwise loc of last token of statement
5633 */
5634 AST.Statement parseStatement(int flags, const(char)** endPtr = null, Loc* pEndloc = null)
5635 {
5636 AST.Statement s;
5637 AST.Condition cond;
5638 AST.Statement ifbody;
5639 AST.Statement elsebody;
5640 bool isfinal;
5641 const loc = token.loc;
5642
5643 //printf("parseStatement()\n");
5644 if (flags & ParseStatementFlags.curly && token.value != TOK.leftCurly)
5645 error("statement expected to be `{ }`, not `%s`", token.toChars());
5646
5647 switch (token.value)
5648 {
5649 case TOK.identifier:
5650 {
5651 /* A leading identifier can be a declaration, label, or expression.
5652 * The easiest case to check first is label:
5653 */
5654 if (peekNext() == TOK.colonColon)
5655 {
5656 // skip ident::
5657 nextToken();
5658 nextToken();
5659 error("use `.` for member lookup, not `::`");
5660 break;
5661 }
5662
5663 if (peekNext() == TOK.colon)
5664 {
5665 // It's a label
5666 Identifier ident = token.ident;
5667 nextToken();
5668 nextToken();
5669 if (token.value == TOK.rightCurly)
5670 s = null;
5671 else if (token.value == TOK.leftCurly)
5672 s = parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_);
5673 else
5674 s = parseStatement(ParseStatementFlags.semiOk);
5675 s = new AST.LabelStatement(loc, ident, s);
5676 break;
5677 }
5678 goto case TOK.dot;
5679 }
5680 case TOK.dot:
5681 case TOK.typeof_:
5682 case TOK.vector:
5683 case TOK.traits:
5684 /* https://issues.dlang.org/show_bug.cgi?id=15163
5685 * If tokens can be handled as
5686 * old C-style declaration or D expression, prefer the latter.
5687 */
5688 if (isDeclaration(&token, NeedDeclaratorId.mustIfDstyle, TOK.reserved, null))
5689 goto Ldeclaration;
5690 goto Lexp;
5691
5692 case TOK.assert_:
5693 case TOK.this_:
5694 case TOK.super_:
5695 case TOK.int32Literal:
5696 case TOK.uns32Literal:
5697 case TOK.int64Literal:
5698 case TOK.uns64Literal:
5699 case TOK.int128Literal:
5700 case TOK.uns128Literal:
5701 case TOK.float32Literal:
5702 case TOK.float64Literal:
5703 case TOK.float80Literal:
5704 case TOK.imaginary32Literal:
5705 case TOK.imaginary64Literal:
5706 case TOK.imaginary80Literal:
5707 case TOK.charLiteral:
5708 case TOK.wcharLiteral:
5709 case TOK.dcharLiteral:
5710 case TOK.null_:
5711 case TOK.true_:
5712 case TOK.false_:
5713 case TOK.string_:
5714 case TOK.hexadecimalString:
5715 case TOK.leftParenthesis:
5716 case TOK.cast_:
5717 case TOK.mul:
5718 case TOK.min:
5719 case TOK.add:
5720 case TOK.tilde:
5721 case TOK.not:
5722 case TOK.plusPlus:
5723 case TOK.minusMinus:
5724 case TOK.new_:
5725 case TOK.delete_:
5726 case TOK.delegate_:
5727 case TOK.function_:
5728 case TOK.typeid_:
5729 case TOK.is_:
5730 case TOK.leftBracket:
5731 case TOK.file:
5732 case TOK.fileFullPath:
5733 case TOK.line:
5734 case TOK.moduleString:
5735 case TOK.functionString:
5736 case TOK.prettyFunction:
5737 Lexp:
5738 {
5739 AST.Expression exp = parseExpression();
5740 /* https://issues.dlang.org/show_bug.cgi?id=15103
5741 * Improve declaration / initialization syntax error message
5742 * Error: found 'foo' when expecting ';' following statement
5743 * becomes Error: found `(` when expecting `;` or `=`, did you mean `Foo foo = 42`?
5744 */
5745 if (token.value == TOK.identifier && exp.op == TOK.identifier)
5746 {
5747 error("found `%s` when expecting `;` or `=`, did you mean `%s %s = %s`?", peek(&token).toChars(), exp.toChars(), token.toChars(), peek(peek(&token)).toChars());
5748 nextToken();
5749 }
5750 else
5751 check(TOK.semicolon, "statement");
5752 s = new AST.ExpStatement(loc, exp);
5753 break;
5754 }
5755 case TOK.static_:
5756 {
5757 // Look ahead to see if it's static assert() or static if()
5758 const tv = peekNext();
5759 if (tv == TOK.assert_)
5760 {
5761 s = new AST.StaticAssertStatement(parseStaticAssert());
5762 break;
5763 }
5764 if (tv == TOK.if_)
5765 {
5766 cond = parseStaticIfCondition();
5767 goto Lcondition;
5768 }
5769 if (tv == TOK.foreach_ || tv == TOK.foreach_reverse_)
5770 {
5771 s = parseForeach!(AST.StaticForeachStatement)(loc, null);
5772 if (flags & ParseStatementFlags.scope_)
5773 s = new AST.ScopeStatement(loc, s, token.loc);
5774 break;
5775 }
5776 if (tv == TOK.import_)
5777 {
5778 AST.Dsymbols* imports = parseImport();
5779 s = new AST.ImportStatement(loc, imports);
5780 if (flags & ParseStatementFlags.scope_)
5781 s = new AST.ScopeStatement(loc, s, token.loc);
5782 break;
5783 }
5784 goto Ldeclaration;
5785 }
5786 case TOK.final_:
5787 if (peekNext() == TOK.switch_)
5788 {
5789 nextToken();
5790 isfinal = true;
5791 goto Lswitch;
5792 }
5793 goto Ldeclaration;
5794
5795 case TOK.wchar_:
5796 case TOK.dchar_:
5797 case TOK.bool_:
5798 case TOK.char_:
5799 case TOK.int8:
5800 case TOK.uns8:
5801 case TOK.int16:
5802 case TOK.uns16:
5803 case TOK.int32:
5804 case TOK.uns32:
5805 case TOK.int64:
5806 case TOK.uns64:
5807 case TOK.int128:
5808 case TOK.uns128:
5809 case TOK.float32:
5810 case TOK.float64:
5811 case TOK.float80:
5812 case TOK.imaginary32:
5813 case TOK.imaginary64:
5814 case TOK.imaginary80:
5815 case TOK.complex32:
5816 case TOK.complex64:
5817 case TOK.complex80:
5818 case TOK.void_:
5819 // bug 7773: int.max is always a part of expression
5820 if (peekNext() == TOK.dot)
5821 goto Lexp;
5822 if (peekNext() == TOK.leftParenthesis)
5823 goto Lexp;
5824 goto case;
5825
5826 case TOK.alias_:
5827 case TOK.const_:
5828 case TOK.auto_:
5829 case TOK.abstract_:
5830 case TOK.extern_:
5831 case TOK.align_:
5832 case TOK.immutable_:
5833 case TOK.shared_:
5834 case TOK.inout_:
5835 case TOK.deprecated_:
5836 case TOK.nothrow_:
5837 case TOK.pure_:
5838 case TOK.ref_:
5839 case TOK.gshared:
5840 case TOK.at:
5841 case TOK.struct_:
5842 case TOK.union_:
5843 case TOK.class_:
5844 case TOK.interface_:
5845 Ldeclaration:
5846 {
5847 AST.Dsymbols* a = parseDeclarations(false, null, null);
5848 if (a.dim > 1)
5849 {
5850 auto as = new AST.Statements();
5851 as.reserve(a.dim);
5852 foreach (i; 0 .. a.dim)
5853 {
5854 AST.Dsymbol d = (*a)[i];
5855 s = new AST.ExpStatement(loc, d);
5856 as.push(s);
5857 }
5858 s = new AST.CompoundDeclarationStatement(loc, as);
5859 }
5860 else if (a.dim == 1)
5861 {
5862 AST.Dsymbol d = (*a)[0];
5863 s = new AST.ExpStatement(loc, d);
5864 }
5865 else
5866 s = new AST.ExpStatement(loc, cast(AST.Expression)null);
5867 if (flags & ParseStatementFlags.scope_)
5868 s = new AST.ScopeStatement(loc, s, token.loc);
5869 break;
5870 }
5871 case TOK.enum_:
5872 {
5873 /* Determine if this is a manifest constant declaration,
5874 * or a conventional enum.
5875 */
5876 AST.Dsymbol d;
5877 const tv = peekNext();
5878 if (tv == TOK.leftCurly || tv == TOK.colon)
5879 d = parseEnum();
5880 else if (tv != TOK.identifier)
5881 goto Ldeclaration;
5882 else
5883 {
5884 const nextv = peekNext2();
5885 if (nextv == TOK.leftCurly || nextv == TOK.colon || nextv == TOK.semicolon)
5886 d = parseEnum();
5887 else
5888 goto Ldeclaration;
5889 }
5890 s = new AST.ExpStatement(loc, d);
5891 if (flags & ParseStatementFlags.scope_)
5892 s = new AST.ScopeStatement(loc, s, token.loc);
5893 break;
5894 }
5895 case TOK.mixin_:
5896 {
5897 if (isDeclaration(&token, NeedDeclaratorId.mustIfDstyle, TOK.reserved, null))
5898 goto Ldeclaration;
5899 if (peekNext() == TOK.leftParenthesis)
5900 {
5901 // mixin(string)
5902 AST.Expression e = parseAssignExp();
5903 check(TOK.semicolon);
5904 if (e.op == TOK.mixin_)
5905 {
5906 AST.MixinExp cpe = cast(AST.MixinExp)e;
5907 s = new AST.CompileStatement(loc, cpe.exps);
5908 }
5909 else
5910 {
5911 s = new AST.ExpStatement(loc, e);
5912 }
5913 break;
5914 }
5915 AST.Dsymbol d = parseMixin();
5916 s = new AST.ExpStatement(loc, d);
5917 if (flags & ParseStatementFlags.scope_)
5918 s = new AST.ScopeStatement(loc, s, token.loc);
5919 break;
5920 }
5921 case TOK.leftCurly:
5922 {
5923 const lookingForElseSave = lookingForElse;
5924 lookingForElse = Loc.initial;
5925
5926 nextToken();
5927 //if (token.value == TOK.semicolon)
5928 // error("use `{ }` for an empty statement, not `;`");
5929 auto statements = new AST.Statements();
5930 while (token.value != TOK.rightCurly && token.value != TOK.endOfFile)
5931 {
5932 statements.push(parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope));
5933 }
5934 if (endPtr)
5935 *endPtr = token.ptr;
5936 endloc = token.loc;
5937 if (pEndloc)
5938 {
5939 *pEndloc = token.loc;
5940 pEndloc = null; // don't set it again
5941 }
5942 s = new AST.CompoundStatement(loc, statements);
5943 if (flags & (ParseStatementFlags.scope_ | ParseStatementFlags.curlyScope))
5944 s = new AST.ScopeStatement(loc, s, token.loc);
5945 check(TOK.rightCurly, "compound statement");
5946 lookingForElse = lookingForElseSave;
5947 break;
5948 }
5949 case TOK.while_:
5950 {
5951 AST.Parameter param = null;
5952 nextToken();
5953 check(TOK.leftParenthesis);
5954 param = parseAssignCondition();
5955 AST.Expression condition = parseExpression();
5956 check(TOK.rightParenthesis);
5957 Loc endloc;
5958 AST.Statement _body = parseStatement(ParseStatementFlags.scope_, null, &endloc);
5959 s = new AST.WhileStatement(loc, condition, _body, endloc, param);
5960 break;
5961 }
5962 case TOK.semicolon:
5963 if (!(flags & ParseStatementFlags.semiOk))
5964 {
5965 if (flags & ParseStatementFlags.semi)
5966 deprecation("use `{ }` for an empty statement, not `;`");
5967 else
5968 error("use `{ }` for an empty statement, not `;`");
5969 }
5970 nextToken();
5971 s = new AST.ExpStatement(loc, cast(AST.Expression)null);
5972 break;
5973
5974 case TOK.do_:
5975 {
5976 AST.Statement _body;
5977 AST.Expression condition;
5978
5979 nextToken();
5980 const lookingForElseSave = lookingForElse;
5981 lookingForElse = Loc.initial;
5982 _body = parseStatement(ParseStatementFlags.scope_);
5983 lookingForElse = lookingForElseSave;
5984 check(TOK.while_);
5985 check(TOK.leftParenthesis);
5986 condition = parseExpression();
5987 check(TOK.rightParenthesis);
5988 if (token.value == TOK.semicolon)
5989 nextToken();
5990 else
5991 error("terminating `;` required after do-while statement");
5992 s = new AST.DoStatement(loc, _body, condition, token.loc);
5993 break;
5994 }
5995 case TOK.for_:
5996 {
5997 AST.Statement _init;
5998 AST.Expression condition;
5999 AST.Expression increment;
6000
6001 nextToken();
6002 check(TOK.leftParenthesis);
6003 if (token.value == TOK.semicolon)
6004 {
6005 _init = null;
6006 nextToken();
6007 }
6008 else
6009 {
6010 const lookingForElseSave = lookingForElse;
6011 lookingForElse = Loc.initial;
6012 _init = parseStatement(0);
6013 lookingForElse = lookingForElseSave;
6014 }
6015 if (token.value == TOK.semicolon)
6016 {
6017 condition = null;
6018 nextToken();
6019 }
6020 else
6021 {
6022 condition = parseExpression();
6023 check(TOK.semicolon, "`for` condition");
6024 }
6025 if (token.value == TOK.rightParenthesis)
6026 {
6027 increment = null;
6028 nextToken();
6029 }
6030 else
6031 {
6032 increment = parseExpression();
6033 check(TOK.rightParenthesis);
6034 }
6035 Loc endloc;
6036 AST.Statement _body = parseStatement(ParseStatementFlags.scope_, null, &endloc);
6037 s = new AST.ForStatement(loc, _init, condition, increment, _body, endloc);
6038 break;
6039 }
6040 case TOK.foreach_:
6041 case TOK.foreach_reverse_:
6042 {
6043 s = parseForeach!(AST.Statement)(loc, null);
6044 break;
6045 }
6046 case TOK.if_:
6047 {
6048 AST.Parameter param = null;
6049 AST.Expression condition;
6050
6051 nextToken();
6052 check(TOK.leftParenthesis);
6053 param = parseAssignCondition();
6054 condition = parseExpression();
6055 check(TOK.rightParenthesis);
6056 {
6057 const lookingForElseSave = lookingForElse;
6058 lookingForElse = loc;
6059 ifbody = parseStatement(ParseStatementFlags.scope_);
6060 lookingForElse = lookingForElseSave;
6061 }
6062 if (token.value == TOK.else_)
6063 {
6064 const elseloc = token.loc;
6065 nextToken();
6066 elsebody = parseStatement(ParseStatementFlags.scope_);
6067 checkDanglingElse(elseloc);
6068 }
6069 else
6070 elsebody = null;
6071 if (condition && ifbody)
6072 s = new AST.IfStatement(loc, param, condition, ifbody, elsebody, token.loc);
6073 else
6074 s = null; // don't propagate parsing errors
6075 break;
6076 }
6077
6078 case TOK.else_:
6079 error("found `else` without a corresponding `if`, `version` or `debug` statement");
6080 goto Lerror;
6081
6082 case TOK.scope_:
6083 if (peekNext() != TOK.leftParenthesis)
6084 goto Ldeclaration; // scope used as storage class
6085 nextToken();
6086 check(TOK.leftParenthesis);
6087 if (token.value != TOK.identifier)
6088 {
6089 error("scope identifier expected");
6090 goto Lerror;
6091 }
6092 else
6093 {
6094 TOK t = TOK.onScopeExit;
6095 Identifier id = token.ident;
6096 if (id == Id.exit)
6097 t = TOK.onScopeExit;
6098 else if (id == Id.failure)
6099 t = TOK.onScopeFailure;
6100 else if (id == Id.success)
6101 t = TOK.onScopeSuccess;
6102 else
6103 error("valid scope identifiers are `exit`, `failure`, or `success`, not `%s`", id.toChars());
6104 nextToken();
6105 check(TOK.rightParenthesis);
6106 AST.Statement st = parseStatement(ParseStatementFlags.scope_);
6107 s = new AST.ScopeGuardStatement(loc, t, st);
6108 break;
6109 }
6110
6111 case TOK.debug_:
6112 nextToken();
6113 if (token.value == TOK.assign)
6114 {
6115 error("debug conditions can only be declared at module scope");
6116 nextToken();
6117 nextToken();
6118 goto Lerror;
6119 }
6120 cond = parseDebugCondition();
6121 goto Lcondition;
6122
6123 case TOK.version_:
6124 nextToken();
6125 if (token.value == TOK.assign)
6126 {
6127 error("version conditions can only be declared at module scope");
6128 nextToken();
6129 nextToken();
6130 goto Lerror;
6131 }
6132 cond = parseVersionCondition();
6133 goto Lcondition;
6134
6135 Lcondition:
6136 {
6137 const lookingForElseSave = lookingForElse;
6138 lookingForElse = loc;
6139 ifbody = parseStatement(0);
6140 lookingForElse = lookingForElseSave;
6141 }
6142 elsebody = null;
6143 if (token.value == TOK.else_)
6144 {
6145 const elseloc = token.loc;
6146 nextToken();
6147 elsebody = parseStatement(0);
6148 checkDanglingElse(elseloc);
6149 }
6150 s = new AST.ConditionalStatement(loc, cond, ifbody, elsebody);
6151 if (flags & ParseStatementFlags.scope_)
6152 s = new AST.ScopeStatement(loc, s, token.loc);
6153 break;
6154
6155 case TOK.pragma_:
6156 {
6157 Identifier ident;
6158 AST.Expressions* args = null;
6159 AST.Statement _body;
6160
6161 nextToken();
6162 check(TOK.leftParenthesis);
6163 if (token.value != TOK.identifier)
6164 {
6165 error("`pragma(identifier)` expected");
6166 goto Lerror;
6167 }
6168 ident = token.ident;
6169 nextToken();
6170 if (token.value == TOK.comma && peekNext() != TOK.rightParenthesis)
6171 args = parseArguments(); // pragma(identifier, args...);
6172 else
6173 check(TOK.rightParenthesis); // pragma(identifier);
6174 if (token.value == TOK.semicolon)
6175 {
6176 nextToken();
6177 _body = null;
6178 }
6179 else
6180 _body = parseStatement(ParseStatementFlags.semi);
6181 s = new AST.PragmaStatement(loc, ident, args, _body);
6182 break;
6183 }
6184 case TOK.switch_:
6185 isfinal = false;
6186 goto Lswitch;
6187
6188 Lswitch:
6189 {
6190 nextToken();
6191 check(TOK.leftParenthesis);
6192 AST.Expression condition = parseExpression();
6193 check(TOK.rightParenthesis);
6194 AST.Statement _body = parseStatement(ParseStatementFlags.scope_);
6195 s = new AST.SwitchStatement(loc, condition, _body, isfinal);
6196 break;
6197 }
6198 case TOK.case_:
6199 {
6200 AST.Expression exp;
6201 AST.Expressions cases; // array of Expression's
6202 AST.Expression last = null;
6203
6204 nextToken();
6205 do
6206 {
6207 exp = parseAssignExp();
6208 cases.push(exp);
6209 if (token.value != TOK.comma)
6210 break;
6211 nextToken(); //comma
6212 }
6213 while (token.value != TOK.colon && token.value != TOK.endOfFile);
6214 check(TOK.colon);
6215
6216 /* case exp: .. case last:
6217 */
6218 if (token.value == TOK.slice)
6219 {
6220 if (cases.dim > 1)
6221 error("only one `case` allowed for start of case range");
6222 nextToken();
6223 check(TOK.case_);
6224 last = parseAssignExp();
6225 check(TOK.colon);
6226 }
6227
6228 if (flags & ParseStatementFlags.curlyScope)
6229 {
6230 auto statements = new AST.Statements();
6231 while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
6232 {
6233 auto cur = parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope);
6234 statements.push(cur);
6235
6236 // https://issues.dlang.org/show_bug.cgi?id=21739
6237 // Stop at the last break s.t. the following non-case statements are
6238 // not merged into the current case. This can happen for
6239 // case 1: ... break;
6240 // debug { case 2: ... }
6241 if (cur && cur.isBreakStatement())
6242 break;
6243 }
6244 s = new AST.CompoundStatement(loc, statements);
6245 }
6246 else
6247 {
6248 s = parseStatement(ParseStatementFlags.semi);
6249 }
6250 s = new AST.ScopeStatement(loc, s, token.loc);
6251
6252 if (last)
6253 {
6254 s = new AST.CaseRangeStatement(loc, exp, last, s);
6255 }
6256 else
6257 {
6258 // Keep cases in order by building the case statements backwards
6259 for (size_t i = cases.dim; i; i--)
6260 {
6261 exp = cases[i - 1];
6262 s = new AST.CaseStatement(loc, exp, s);
6263 }
6264 }
6265 break;
6266 }
6267 case TOK.default_:
6268 {
6269 nextToken();
6270 check(TOK.colon);
6271
6272 if (flags & ParseStatementFlags.curlyScope)
6273 {
6274 auto statements = new AST.Statements();
6275 while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
6276 {
6277 statements.push(parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope));
6278 }
6279 s = new AST.CompoundStatement(loc, statements);
6280 }
6281 else
6282 s = parseStatement(ParseStatementFlags.semi);
6283 s = new AST.ScopeStatement(loc, s, token.loc);
6284 s = new AST.DefaultStatement(loc, s);
6285 break;
6286 }
6287 case TOK.return_:
6288 {
6289 AST.Expression exp;
6290 nextToken();
6291 exp = token.value == TOK.semicolon ? null : parseExpression();
6292 check(TOK.semicolon, "`return` statement");
6293 s = new AST.ReturnStatement(loc, exp);
6294 break;
6295 }
6296 case TOK.break_:
6297 {
6298 Identifier ident;
6299 nextToken();
6300 ident = null;
6301 if (token.value == TOK.identifier)
6302 {
6303 ident = token.ident;
6304 nextToken();
6305 }
6306 check(TOK.semicolon, "`break` statement");
6307 s = new AST.BreakStatement(loc, ident);
6308 break;
6309 }
6310 case TOK.continue_:
6311 {
6312 Identifier ident;
6313 nextToken();
6314 ident = null;
6315 if (token.value == TOK.identifier)
6316 {
6317 ident = token.ident;
6318 nextToken();
6319 }
6320 check(TOK.semicolon, "`continue` statement");
6321 s = new AST.ContinueStatement(loc, ident);
6322 break;
6323 }
6324 case TOK.goto_:
6325 {
6326 Identifier ident;
6327 nextToken();
6328 if (token.value == TOK.default_)
6329 {
6330 nextToken();
6331 s = new AST.GotoDefaultStatement(loc);
6332 }
6333 else if (token.value == TOK.case_)
6334 {
6335 AST.Expression exp = null;
6336 nextToken();
6337 if (token.value != TOK.semicolon)
6338 exp = parseExpression();
6339 s = new AST.GotoCaseStatement(loc, exp);
6340 }
6341 else
6342 {
6343 if (token.value != TOK.identifier)
6344 {
6345 error("identifier expected following `goto`");
6346 ident = null;
6347 }
6348 else
6349 {
6350 ident = token.ident;
6351 nextToken();
6352 }
6353 s = new AST.GotoStatement(loc, ident);
6354 }
6355 check(TOK.semicolon, "`goto` statement");
6356 break;
6357 }
6358 case TOK.synchronized_:
6359 {
6360 AST.Expression exp;
6361 AST.Statement _body;
6362
6363 Token* t = peek(&token);
6364 if (skipAttributes(t, &t) && t.value == TOK.class_)
6365 goto Ldeclaration;
6366
6367 nextToken();
6368 if (token.value == TOK.leftParenthesis)
6369 {
6370 nextToken();
6371 exp = parseExpression();
6372 check(TOK.rightParenthesis);
6373 }
6374 else
6375 exp = null;
6376 _body = parseStatement(ParseStatementFlags.scope_);
6377 s = new AST.SynchronizedStatement(loc, exp, _body);
6378 break;
6379 }
6380 case TOK.with_:
6381 {
6382 AST.Expression exp;
6383 AST.Statement _body;
6384 Loc endloc = loc;
6385
6386 nextToken();
6387 check(TOK.leftParenthesis);
6388 exp = parseExpression();
6389 check(TOK.rightParenthesis);
6390 _body = parseStatement(ParseStatementFlags.scope_, null, &endloc);
6391 s = new AST.WithStatement(loc, exp, _body, endloc);
6392 break;
6393 }
6394 case TOK.try_:
6395 {
6396 AST.Statement _body;
6397 AST.Catches* catches = null;
6398 AST.Statement finalbody = null;
6399
6400 nextToken();
6401 const lookingForElseSave = lookingForElse;
6402 lookingForElse = Loc.initial;
6403 _body = parseStatement(ParseStatementFlags.scope_);
6404 lookingForElse = lookingForElseSave;
6405 while (token.value == TOK.catch_)
6406 {
6407 AST.Statement handler;
6408 AST.Catch c;
6409 AST.Type t;
6410 Identifier id;
6411 const catchloc = token.loc;
6412
6413 nextToken();
6414 if (token.value != TOK.leftParenthesis)
6415 {
6416 deprecation("`catch` statement without an exception specification is deprecated");
6417 deprecationSupplemental(token.loc, "use `catch(Throwable)` for old behavior");
6418 t = null;
6419 id = null;
6420 }
6421 else
6422 {
6423 check(TOK.leftParenthesis);
6424 id = null;
6425 t = parseType(&id);
6426 check(TOK.rightParenthesis);
6427 }
6428 handler = parseStatement(0);
6429 c = new AST.Catch(catchloc, t, id, handler);
6430 if (!catches)
6431 catches = new AST.Catches();
6432 catches.push(c);
6433 }
6434
6435 if (token.value == TOK.finally_)
6436 {
6437 nextToken();
6438 finalbody = parseStatement(ParseStatementFlags.scope_);
6439 }
6440
6441 s = _body;
6442 if (!catches && !finalbody)
6443 error("`catch` or `finally` expected following `try`");
6444 else
6445 {
6446 if (catches)
6447 s = new AST.TryCatchStatement(loc, _body, catches);
6448 if (finalbody)
6449 s = new AST.TryFinallyStatement(loc, s, finalbody);
6450 }
6451 break;
6452 }
6453 case TOK.throw_:
6454 {
6455 AST.Expression exp;
6456 nextToken();
6457 exp = parseExpression();
6458 check(TOK.semicolon, "`throw` statement");
6459 s = new AST.ThrowStatement(loc, exp);
6460 break;
6461 }
6462
6463 case TOK.asm_:
6464 s = parseAsm();
6465 break;
6466
6467 case TOK.import_:
6468 {
6469 /* https://issues.dlang.org/show_bug.cgi?id=16088
6470 *
6471 * At this point it can either be an
6472 * https://dlang.org/spec/grammar.html#ImportExpression
6473 * or an
6474 * https://dlang.org/spec/grammar.html#ImportDeclaration.
6475 * See if the next token after `import` is a `(`; if so,
6476 * then it is an import expression.
6477 */
6478 if (peekNext() == TOK.leftParenthesis)
6479 {
6480 AST.Expression e = parseExpression();
6481 check(TOK.semicolon);
6482 s = new AST.ExpStatement(loc, e);
6483 }
6484 else
6485 {
6486 AST.Dsymbols* imports = parseImport();
6487 s = new AST.ImportStatement(loc, imports);
6488 if (flags & ParseStatementFlags.scope_)
6489 s = new AST.ScopeStatement(loc, s, token.loc);
6490 }
6491 break;
6492 }
6493 case TOK.template_:
6494 {
6495 AST.Dsymbol d = parseTemplateDeclaration();
6496 s = new AST.ExpStatement(loc, d);
6497 break;
6498 }
6499 default:
6500 error("found `%s` instead of statement", token.toChars());
6501 goto Lerror;
6502
6503 Lerror:
6504 while (token.value != TOK.rightCurly && token.value != TOK.semicolon && token.value != TOK.endOfFile)
6505 nextToken();
6506 if (token.value == TOK.semicolon)
6507 nextToken();
6508 s = null;
6509 break;
6510 }
6511 if (pEndloc)
6512 *pEndloc = prevloc;
6513 return s;
6514 }
6515
6516
6517 private AST.ExpInitializer parseExpInitializer(Loc loc)
6518 {
6519 auto ae = parseAssignExp();
6520 return new AST.ExpInitializer(loc, ae);
6521 }
6522
6523 private AST.Initializer parseStructInitializer(Loc loc)
6524 {
6525 /* Scan ahead to discern between a struct initializer and
6526 * parameterless function literal.
6527 *
6528 * We'll scan the topmost curly bracket level for statement-related
6529 * tokens, thereby ruling out a struct initializer. (A struct
6530 * initializer which itself contains function literals may have
6531 * statements at nested curly bracket levels.)
6532 *
6533 * It's important that this function literal check not be
6534 * pendantic, otherwise a function having the slightest syntax
6535 * error would emit confusing errors when we proceed to parse it
6536 * as a struct initializer.
6537 *
6538 * The following two ambiguous cases will be treated as a struct
6539 * initializer (best we can do without type info):
6540 * {}
6541 * {{statements...}} - i.e. it could be struct initializer
6542 * with one function literal, or function literal having an
6543 * extra level of curly brackets
6544 * If a function literal is intended in these cases (unlikely),
6545 * source can use a more explicit function literal syntax
6546 * (e.g. prefix with "()" for empty parameter list).
6547 */
6548 int braces = 1;
6549 int parens = 0;
6550 for (auto t = peek(&token); 1; t = peek(t))
6551 {
6552 switch (t.value)
6553 {
6554 case TOK.leftParenthesis:
6555 parens++;
6556 continue;
6557 case TOK.rightParenthesis:
6558 parens--;
6559 continue;
6560 // https://issues.dlang.org/show_bug.cgi?id=21163
6561 // lambda params can have the `scope` storage class, e.g
6562 // `S s = { (scope Type Id){} }`
6563 case TOK.scope_:
6564 if (!parens) goto case;
6565 continue;
6566 /* Look for a semicolon or keyword of statements which don't
6567 * require a semicolon (typically containing BlockStatement).
6568 * Tokens like "else", "catch", etc. are omitted where the
6569 * leading token of the statement is sufficient.
6570 */
6571 case TOK.asm_:
6572 case TOK.class_:
6573 case TOK.debug_:
6574 case TOK.enum_:
6575 case TOK.if_:
6576 case TOK.interface_:
6577 case TOK.pragma_:
6578 case TOK.semicolon:
6579 case TOK.struct_:
6580 case TOK.switch_:
6581 case TOK.synchronized_:
6582 case TOK.try_:
6583 case TOK.union_:
6584 case TOK.version_:
6585 case TOK.while_:
6586 case TOK.with_:
6587 if (braces == 1)
6588 return parseExpInitializer(loc);
6589 continue;
6590
6591 case TOK.leftCurly:
6592 braces++;
6593 continue;
6594
6595 case TOK.rightCurly:
6596 if (--braces == 0)
6597 break;
6598 continue;
6599
6600 case TOK.endOfFile:
6601 break;
6602
6603 default:
6604 continue;
6605 }
6606 break;
6607 }
6608
6609 auto _is = new AST.StructInitializer(loc);
6610 bool commaExpected = false;
6611 nextToken();
6612 while (1)
6613 {
6614 switch (token.value)
6615 {
6616 case TOK.identifier:
6617 {
6618
6619 if (commaExpected)
6620 error("comma expected separating field initializers");
6621 const t = peek(&token);
6622 Identifier id;
6623 if (t.value == TOK.colon)
6624 {
6625 id = token.ident;
6626 nextToken();
6627 nextToken(); // skip over ':'
6628 }
6629 auto value = parseInitializer();
6630 _is.addInit(id, value);
6631 commaExpected = true;
6632 continue;
6633 }
6634 case TOK.comma:
6635 if (!commaExpected)
6636 error("expression expected, not `,`");
6637 nextToken();
6638 commaExpected = false;
6639 continue;
6640
6641 case TOK.rightCurly: // allow trailing comma's
6642 nextToken();
6643 break;
6644
6645 case TOK.endOfFile:
6646 error("found end of file instead of initializer");
6647 break;
6648
6649 default:
6650 if (commaExpected)
6651 error("comma expected separating field initializers");
6652 auto value = parseInitializer();
6653 _is.addInit(null, value);
6654 commaExpected = true;
6655 continue;
6656 }
6657 break;
6658 }
6659 return _is;
6660
6661 }
6662
6663 /*****************************************
6664 * Parse initializer for variable declaration.
6665 */
6666 private AST.Initializer parseInitializer()
6667 {
6668 const loc = token.loc;
6669
6670 switch (token.value)
6671 {
6672 case TOK.leftCurly:
6673 return parseStructInitializer(loc);
6674
6675 case TOK.leftBracket:
6676 /* Scan ahead to see if it is an array initializer or
6677 * an expression.
6678 * If it ends with a ';' ',' or '}', it is an array initializer.
6679 */
6680 int brackets = 1;
6681 for (auto t = peek(&token); 1; t = peek(t))
6682 {
6683 switch (t.value)
6684 {
6685 case TOK.leftBracket:
6686 brackets++;
6687 continue;
6688
6689 case TOK.rightBracket:
6690 if (--brackets == 0)
6691 {
6692 t = peek(t);
6693 if (t.value != TOK.semicolon && t.value != TOK.comma && t.value != TOK.rightBracket && t.value != TOK.rightCurly)
6694 return parseExpInitializer(loc);
6695 break;
6696 }
6697 continue;
6698
6699 case TOK.endOfFile:
6700 break;
6701
6702 default:
6703 continue;
6704 }
6705 break;
6706 }
6707
6708 auto ia = new AST.ArrayInitializer(loc);
6709 bool commaExpected = false;
6710
6711 nextToken();
6712 while (1)
6713 {
6714 switch (token.value)
6715 {
6716 default:
6717 if (commaExpected)
6718 {
6719 error("comma expected separating array initializers, not `%s`", token.toChars());
6720 nextToken();
6721 break;
6722 }
6723 auto e = parseAssignExp();
6724 if (!e)
6725 break;
6726
6727 AST.Initializer value;
6728 if (token.value == TOK.colon)
6729 {
6730 nextToken();
6731 value = parseInitializer();
6732 }
6733 else
6734 {
6735 value = new AST.ExpInitializer(e.loc, e);
6736 e = null;
6737 }
6738 ia.addInit(e, value);
6739 commaExpected = true;
6740 continue;
6741
6742 case TOK.leftCurly:
6743 case TOK.leftBracket:
6744 if (commaExpected)
6745 error("comma expected separating array initializers, not `%s`", token.toChars());
6746 auto value = parseInitializer();
6747 AST.Expression e;
6748
6749 if (token.value == TOK.colon)
6750 {
6751 nextToken();
6752 if (auto ei = value.isExpInitializer())
6753 {
6754 e = ei.exp;
6755 value = parseInitializer();
6756 }
6757 else
6758 error("initializer expression expected following colon, not `%s`", token.toChars());
6759 }
6760 ia.addInit(e, value);
6761 commaExpected = true;
6762 continue;
6763
6764 case TOK.comma:
6765 if (!commaExpected)
6766 error("expression expected, not `,`");
6767 nextToken();
6768 commaExpected = false;
6769 continue;
6770
6771 case TOK.rightBracket: // allow trailing comma's
6772 nextToken();
6773 break;
6774
6775 case TOK.endOfFile:
6776 error("found `%s` instead of array initializer", token.toChars());
6777 break;
6778 }
6779 break;
6780 }
6781 return ia;
6782
6783 case TOK.void_:
6784 const tv = peekNext();
6785 if (tv == TOK.semicolon || tv == TOK.comma)
6786 {
6787 nextToken();
6788 return new AST.VoidInitializer(loc);
6789 }
6790 return parseExpInitializer(loc);
6791
6792 default:
6793 return parseExpInitializer(loc);
6794 }
6795 }
6796
6797 /*****************************************
6798 * Parses default argument initializer expression that is an assign expression,
6799 * with special handling for __FILE__, __FILE_DIR__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__.
6800 */
6801 private AST.Expression parseDefaultInitExp()
6802 {
6803 AST.Expression e = null;
6804 const tv = peekNext();
6805 if (tv == TOK.comma || tv == TOK.rightParenthesis)
6806 {
6807 switch (token.value)
6808 {
6809 case TOK.file: e = new AST.FileInitExp(token.loc, TOK.file); break;
6810 case TOK.fileFullPath: e = new AST.FileInitExp(token.loc, TOK.fileFullPath); break;
6811 case TOK.line: e = new AST.LineInitExp(token.loc); break;
6812 case TOK.moduleString: e = new AST.ModuleInitExp(token.loc); break;
6813 case TOK.functionString: e = new AST.FuncInitExp(token.loc); break;
6814 case TOK.prettyFunction: e = new AST.PrettyFuncInitExp(token.loc); break;
6815 default: goto LExp;
6816 }
6817 nextToken();
6818 return e;
6819 }
6820 LExp:
6821 return parseAssignExp();
6822 }
6823
6824 /********************
6825 * Parse inline assembler block.
6826 * Returns:
6827 * inline assembler block as a Statement
6828 */
6829 AST.Statement parseAsm()
6830 {
6831 // Parse the asm block into a sequence of AsmStatements,
6832 // each AsmStatement is one instruction.
6833 // Separate out labels.
6834 // Defer parsing of AsmStatements until semantic processing.
6835
6836 const loc = token.loc;
6837 Loc labelloc;
6838
6839 nextToken();
6840 StorageClass stc = parsePostfix(STC.undefined_, null);
6841 if (stc & (STC.const_ | STC.immutable_ | STC.shared_ | STC.wild))
6842 error("`const`/`immutable`/`shared`/`inout` attributes are not allowed on `asm` blocks");
6843
6844 check(TOK.leftCurly);
6845 Token* toklist = null;
6846 Token** ptoklist = &toklist;
6847 Identifier label = null;
6848 auto statements = new AST.Statements();
6849 size_t nestlevel = 0;
6850 while (1)
6851 {
6852 switch (token.value)
6853 {
6854 case TOK.identifier:
6855 if (!toklist)
6856 {
6857 // Look ahead to see if it is a label
6858 if (peekNext() == TOK.colon)
6859 {
6860 // It's a label
6861 label = token.ident;
6862 labelloc = token.loc;
6863 nextToken();
6864 nextToken();
6865 continue;
6866 }
6867 }
6868 goto default;
6869
6870 case TOK.leftCurly:
6871 ++nestlevel;
6872 goto default;
6873
6874 case TOK.rightCurly:
6875 if (nestlevel > 0)
6876 {
6877 --nestlevel;
6878 goto default;
6879 }
6880 if (toklist || label)
6881 {
6882 error("`asm` statements must end in `;`");
6883 }
6884 break;
6885
6886 case TOK.semicolon:
6887 if (nestlevel != 0)
6888 error("mismatched number of curly brackets");
6889
6890 if (toklist || label)
6891 {
6892 // Create AsmStatement from list of tokens we've saved
6893 AST.Statement s = new AST.AsmStatement(token.loc, toklist);
6894 toklist = null;
6895 ptoklist = &toklist;
6896 if (label)
6897 {
6898 s = new AST.LabelStatement(labelloc, label, s);
6899 label = null;
6900 }
6901 statements.push(s);
6902 }
6903 nextToken();
6904 continue;
6905
6906 case TOK.endOfFile:
6907 /* { */
6908 error("matching `}` expected, not end of file");
6909 break;
6910
6911 case TOK.colonColon: // treat as two separate : tokens for iasmgcc
6912 *ptoklist = allocateToken();
6913 memcpy(*ptoklist, &token, Token.sizeof);
6914 (*ptoklist).value = TOK.colon;
6915 ptoklist = &(*ptoklist).next;
6916
6917 *ptoklist = allocateToken();
6918 memcpy(*ptoklist, &token, Token.sizeof);
6919 (*ptoklist).value = TOK.colon;
6920 ptoklist = &(*ptoklist).next;
6921
6922 *ptoklist = null;
6923 nextToken();
6924 continue;
6925
6926 default:
6927 *ptoklist = allocateToken();
6928 memcpy(*ptoklist, &token, Token.sizeof);
6929 ptoklist = &(*ptoklist).next;
6930 *ptoklist = null;
6931 nextToken();
6932 continue;
6933 }
6934 break;
6935 }
6936 nextToken();
6937 auto s = new AST.CompoundAsmStatement(loc, statements, stc);
6938 return s;
6939 }
6940
6941 /**********************************
6942 * Issue error if the current token is not `value`,
6943 * advance to next token.
6944 * Params:
6945 * loc = location for error message
6946 * value = token value to compare with
6947 */
6948 void check(Loc loc, TOK value)
6949 {
6950 if (token.value != value)
6951 error(loc, "found `%s` when expecting `%s`", token.toChars(), Token.toChars(value));
6952 nextToken();
6953 }
6954
6955 /**********************************
6956 * Issue error if the current token is not `value`,
6957 * advance to next token.
6958 * Params:
6959 * value = token value to compare with
6960 */
6961 void check(TOK value)
6962 {
6963 check(token.loc, value);
6964 }
6965
6966 /**********************************
6967 * Issue error if the current token is not `value`,
6968 * advance to next token.
6969 * Params:
6970 * value = token value to compare with
6971 * string = for error message
6972 */
6973 void check(TOK value, const(char)* string)
6974 {
6975 if (token.value != value)
6976 error("found `%s` when expecting `%s` following %s", token.toChars(), Token.toChars(value), string);
6977 nextToken();
6978 }
6979
6980 private void checkParens(TOK value, AST.Expression e)
6981 {
6982 if (precedence[e.op] == PREC.rel && !e.parens)
6983 error(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`", e.toChars(), Token.toChars(value));
6984 }
6985
6986 ///
6987 enum NeedDeclaratorId
6988 {
6989 no, // Declarator part must have no identifier
6990 opt, // Declarator part identifier is optional
6991 must, // Declarator part must have identifier
6992 mustIfDstyle, // Declarator part must have identifier, but don't recognize old C-style syntax
6993 }
6994
6995 /************************************
6996 * Determine if the scanner is sitting on the start of a declaration.
6997 * Params:
6998 * t = current token of the scanner
6999 * needId = flag with additional requirements for a declaration
7000 * endtok = ending token
7001 * pt = will be set ending token (if not null)
7002 * Output:
7003 * true if the token `t` is a declaration, false otherwise
7004 */
7005 private bool isDeclaration(Token* t, NeedDeclaratorId needId, TOK endtok, Token** pt)
7006 {
7007 //printf("isDeclaration(needId = %d)\n", needId);
7008 int haveId = 0;
7009 int haveTpl = 0;
7010
7011 while (1)
7012 {
7013 if ((t.value == TOK.const_ || t.value == TOK.immutable_ || t.value == TOK.inout_ || t.value == TOK.shared_) && peek(t).value != TOK.leftParenthesis)
7014 {
7015 /* const type
7016 * immutable type
7017 * shared type
7018 * wild type
7019 */
7020 t = peek(t);
7021 continue;
7022 }
7023 break;
7024 }
7025
7026 if (!isBasicType(&t))
7027 {
7028 goto Lisnot;
7029 }
7030 if (!isDeclarator(&t, &haveId, &haveTpl, endtok, needId != NeedDeclaratorId.mustIfDstyle))
7031 goto Lisnot;
7032 if ((needId == NeedDeclaratorId.no && !haveId) ||
7033 (needId == NeedDeclaratorId.opt) ||
7034 (needId == NeedDeclaratorId.must && haveId) ||
7035 (needId == NeedDeclaratorId.mustIfDstyle && haveId))
7036 {
7037 if (pt)
7038 *pt = t;
7039 goto Lis;
7040 }
7041 goto Lisnot;
7042
7043 Lis:
7044 //printf("\tis declaration, t = %s\n", t.toChars());
7045 return true;
7046
7047 Lisnot:
7048 //printf("\tis not declaration\n");
7049 return false;
7050 }
7051
7052 private bool isBasicType(Token** pt)
7053 {
7054 // This code parallels parseBasicType()
7055 Token* t = *pt;
7056 switch (t.value)
7057 {
7058 case TOK.wchar_:
7059 case TOK.dchar_:
7060 case TOK.bool_:
7061 case TOK.char_:
7062 case TOK.int8:
7063 case TOK.uns8:
7064 case TOK.int16:
7065 case TOK.uns16:
7066 case TOK.int32:
7067 case TOK.uns32:
7068 case TOK.int64:
7069 case TOK.uns64:
7070 case TOK.int128:
7071 case TOK.uns128:
7072 case TOK.float32:
7073 case TOK.float64:
7074 case TOK.float80:
7075 case TOK.imaginary32:
7076 case TOK.imaginary64:
7077 case TOK.imaginary80:
7078 case TOK.complex32:
7079 case TOK.complex64:
7080 case TOK.complex80:
7081 case TOK.void_:
7082 t = peek(t);
7083 break;
7084
7085 case TOK.identifier:
7086 L5:
7087 t = peek(t);
7088 if (t.value == TOK.not)
7089 {
7090 goto L4;
7091 }
7092 goto L3;
7093 while (1)
7094 {
7095 L2:
7096 t = peek(t);
7097 L3:
7098 if (t.value == TOK.dot)
7099 {
7100 Ldot:
7101 t = peek(t);
7102 if (t.value != TOK.identifier)
7103 goto Lfalse;
7104 t = peek(t);
7105 if (t.value != TOK.not)
7106 goto L3;
7107 L4:
7108 /* Seen a !
7109 * Look for:
7110 * !( args ), !identifier, etc.
7111 */
7112 t = peek(t);
7113 switch (t.value)
7114 {
7115 case TOK.identifier:
7116 goto L5;
7117
7118 case TOK.leftParenthesis:
7119 if (!skipParens(t, &t))
7120 goto Lfalse;
7121 goto L3;
7122
7123 case TOK.wchar_:
7124 case TOK.dchar_:
7125 case TOK.bool_:
7126 case TOK.char_:
7127 case TOK.int8:
7128 case TOK.uns8:
7129 case TOK.int16:
7130 case TOK.uns16:
7131 case TOK.int32:
7132 case TOK.uns32:
7133 case TOK.int64:
7134 case TOK.uns64:
7135 case TOK.int128:
7136 case TOK.uns128:
7137 case TOK.float32:
7138 case TOK.float64:
7139 case TOK.float80:
7140 case TOK.imaginary32:
7141 case TOK.imaginary64:
7142 case TOK.imaginary80:
7143 case TOK.complex32:
7144 case TOK.complex64:
7145 case TOK.complex80:
7146 case TOK.void_:
7147 case TOK.int32Literal:
7148 case TOK.uns32Literal:
7149 case TOK.int64Literal:
7150 case TOK.uns64Literal:
7151 case TOK.int128Literal:
7152 case TOK.uns128Literal:
7153 case TOK.float32Literal:
7154 case TOK.float64Literal:
7155 case TOK.float80Literal:
7156 case TOK.imaginary32Literal:
7157 case TOK.imaginary64Literal:
7158 case TOK.imaginary80Literal:
7159 case TOK.null_:
7160 case TOK.true_:
7161 case TOK.false_:
7162 case TOK.charLiteral:
7163 case TOK.wcharLiteral:
7164 case TOK.dcharLiteral:
7165 case TOK.string_:
7166 case TOK.hexadecimalString:
7167 case TOK.file:
7168 case TOK.fileFullPath:
7169 case TOK.line:
7170 case TOK.moduleString:
7171 case TOK.functionString:
7172 case TOK.prettyFunction:
7173 goto L2;
7174
7175 default:
7176 goto Lfalse;
7177 }
7178 }
7179 break;
7180 }
7181 break;
7182
7183 case TOK.dot:
7184 goto Ldot;
7185
7186 case TOK.typeof_:
7187 case TOK.vector:
7188 case TOK.mixin_:
7189 /* typeof(exp).identifier...
7190 */
7191 t = peek(t);
7192 if (!skipParens(t, &t))
7193 goto Lfalse;
7194 goto L3;
7195
7196 case TOK.traits:
7197 // __traits(getMember
7198 t = peek(t);
7199 if (t.value != TOK.leftParenthesis)
7200 goto Lfalse;
7201 auto lp = t;
7202 t = peek(t);
7203 if (t.value != TOK.identifier || t.ident != Id.getMember)
7204 goto Lfalse;
7205 if (!skipParens(lp, &lp))
7206 goto Lfalse;
7207 // we are in a lookup for decl VS statement
7208 // so we expect a declarator following __trait if it's a type.
7209 // other usages wont be ambiguous (alias, template instance, type qual, etc.)
7210 if (lp.value != TOK.identifier)
7211 goto Lfalse;
7212
7213 break;
7214
7215 case TOK.const_:
7216 case TOK.immutable_:
7217 case TOK.shared_:
7218 case TOK.inout_:
7219 // const(type) or immutable(type) or shared(type) or wild(type)
7220 t = peek(t);
7221 if (t.value != TOK.leftParenthesis)
7222 goto Lfalse;
7223 t = peek(t);
7224 if (!isDeclaration(t, NeedDeclaratorId.no, TOK.rightParenthesis, &t))
7225 {
7226 goto Lfalse;
7227 }
7228 t = peek(t);
7229 break;
7230
7231 default:
7232 goto Lfalse;
7233 }
7234 *pt = t;
7235 //printf("is\n");
7236 return true;
7237
7238 Lfalse:
7239 //printf("is not\n");
7240 return false;
7241 }
7242
7243 private bool isDeclarator(Token** pt, int* haveId, int* haveTpl, TOK endtok, bool allowAltSyntax = true)
7244 {
7245 // This code parallels parseDeclarator()
7246 Token* t = *pt;
7247 int parens;
7248
7249 //printf("Parser::isDeclarator() %s\n", t.toChars());
7250 if (t.value == TOK.assign)
7251 return false;
7252
7253 while (1)
7254 {
7255 parens = false;
7256 switch (t.value)
7257 {
7258 case TOK.mul:
7259 //case TOK.and:
7260 t = peek(t);
7261 continue;
7262
7263 case TOK.leftBracket:
7264 t = peek(t);
7265 if (t.value == TOK.rightBracket)
7266 {
7267 t = peek(t);
7268 }
7269 else if (isDeclaration(t, NeedDeclaratorId.no, TOK.rightBracket, &t))
7270 {
7271 // It's an associative array declaration
7272 t = peek(t);
7273
7274 // ...[type].ident
7275 if (t.value == TOK.dot && peek(t).value == TOK.identifier)
7276 {
7277 t = peek(t);
7278 t = peek(t);
7279 }
7280 }
7281 else
7282 {
7283 // [ expression ]
7284 // [ expression .. expression ]
7285 if (!isExpression(&t))
7286 return false;
7287 if (t.value == TOK.slice)
7288 {
7289 t = peek(t);
7290 if (!isExpression(&t))
7291 return false;
7292 if (t.value != TOK.rightBracket)
7293 return false;
7294 t = peek(t);
7295 }
7296 else
7297 {
7298 if (t.value != TOK.rightBracket)
7299 return false;
7300 t = peek(t);
7301 // ...[index].ident
7302 if (t.value == TOK.dot && peek(t).value == TOK.identifier)
7303 {
7304 t = peek(t);
7305 t = peek(t);
7306 }
7307 }
7308 }
7309 continue;
7310
7311 case TOK.identifier:
7312 if (*haveId)
7313 return false;
7314 *haveId = true;
7315 t = peek(t);
7316 break;
7317
7318 case TOK.leftParenthesis:
7319 if (!allowAltSyntax)
7320 return false; // Do not recognize C-style declarations.
7321
7322 t = peek(t);
7323 if (t.value == TOK.rightParenthesis)
7324 return false; // () is not a declarator
7325
7326 /* Regard ( identifier ) as not a declarator
7327 * BUG: what about ( *identifier ) in
7328 * f(*p)(x);
7329 * where f is a class instance with overloaded () ?
7330 * Should we just disallow C-style function pointer declarations?
7331 */
7332 if (t.value == TOK.identifier)
7333 {
7334 Token* t2 = peek(t);
7335 if (t2.value == TOK.rightParenthesis)
7336 return false;
7337 }
7338
7339 if (!isDeclarator(&t, haveId, null, TOK.rightParenthesis))
7340 return false;
7341 t = peek(t);
7342 parens = true;
7343 break;
7344
7345 case TOK.delegate_:
7346 case TOK.function_:
7347 t = peek(t);
7348 if (!isParameters(&t))
7349 return false;
7350 skipAttributes(t, &t);
7351 continue;
7352
7353 default:
7354 break;
7355 }
7356 break;
7357 }
7358
7359 while (1)
7360 {
7361 switch (t.value)
7362 {
7363 static if (CARRAYDECL)
7364 {
7365 case TOK.leftBracket:
7366 parens = false;
7367 t = peek(t);
7368 if (t.value == TOK.rightBracket)
7369 {
7370 t = peek(t);
7371 }
7372 else if (isDeclaration(t, NeedDeclaratorId.no, TOK.rightBracket, &t))
7373 {
7374 // It's an associative array declaration
7375 t = peek(t);
7376 }
7377 else
7378 {
7379 // [ expression ]
7380 if (!isExpression(&t))
7381 return false;
7382 if (t.value != TOK.rightBracket)
7383 return false;
7384 t = peek(t);
7385 }
7386 continue;
7387 }
7388
7389 case TOK.leftParenthesis:
7390 parens = false;
7391 if (Token* tk = peekPastParen(t))
7392 {
7393 if (tk.value == TOK.leftParenthesis)
7394 {
7395 if (!haveTpl)
7396 return false;
7397 *haveTpl = 1;
7398 t = tk;
7399 }
7400 else if (tk.value == TOK.assign)
7401 {
7402 if (!haveTpl)
7403 return false;
7404 *haveTpl = 1;
7405 *pt = tk;
7406 return true;
7407 }
7408 }
7409 if (!isParameters(&t))
7410 return false;
7411 while (1)
7412 {
7413 switch (t.value)
7414 {
7415 case TOK.const_:
7416 case TOK.immutable_:
7417 case TOK.shared_:
7418 case TOK.inout_:
7419 case TOK.pure_:
7420 case TOK.nothrow_:
7421 case TOK.return_:
7422 case TOK.scope_:
7423 t = peek(t);
7424 continue;
7425
7426 case TOK.at:
7427 t = peek(t); // skip '@'
7428 t = peek(t); // skip identifier
7429 continue;
7430
7431 default:
7432 break;
7433 }
7434 break;
7435 }
7436 continue;
7437
7438 // Valid tokens that follow a declaration
7439 case TOK.rightParenthesis:
7440 case TOK.rightBracket:
7441 case TOK.assign:
7442 case TOK.comma:
7443 case TOK.dotDotDot:
7444 case TOK.semicolon:
7445 case TOK.leftCurly:
7446 case TOK.in_:
7447 case TOK.out_:
7448 case TOK.do_:
7449 // The !parens is to disallow unnecessary parentheses
7450 if (!parens && (endtok == TOK.reserved || endtok == t.value))
7451 {
7452 *pt = t;
7453 return true;
7454 }
7455 return false;
7456
7457 case TOK.identifier:
7458 if (t.ident == Id._body)
7459 {
7460 // @@@DEPRECATED@@@
7461 // https://github.com/dlang/DIPs/blob/1f5959abe482b1f9094f6484a7d0a3ade77fc2fc/DIPs/accepted/DIP1003.md
7462 // Deprecated in 2.097 - Can be removed from 2.117
7463 // The deprecation period is longer than usual as `body`
7464 // was quite widely used.
7465 deprecation("Usage of the `body` keyword is deprecated. Use `do` instead.");
7466 goto case TOK.do_;
7467 }
7468 goto default;
7469
7470 case TOK.if_:
7471 return haveTpl ? true : false;
7472
7473 // Used for mixin type parsing
7474 case TOK.endOfFile:
7475 if (endtok == TOK.endOfFile)
7476 goto case TOK.do_;
7477 return false;
7478
7479 default:
7480 return false;
7481 }
7482 }
7483 assert(0);
7484 }
7485
7486 private bool isParameters(Token** pt)
7487 {
7488 // This code parallels parseParameterList()
7489 Token* t = *pt;
7490
7491 //printf("isParameters()\n");
7492 if (t.value != TOK.leftParenthesis)
7493 return false;
7494
7495 t = peek(t);
7496 for (; 1; t = peek(t))
7497 {
7498 L1:
7499 switch (t.value)
7500 {
7501 case TOK.rightParenthesis:
7502 break;
7503
7504 case TOK.at:
7505 Token* pastAttr;
7506 if (skipAttributes(t, &pastAttr))
7507 {
7508 t = pastAttr;
7509 goto default;
7510 }
7511 break;
7512
7513 case TOK.dotDotDot:
7514 t = peek(t);
7515 break;
7516
7517 case TOK.in_:
7518 case TOK.out_:
7519 case TOK.ref_:
7520 case TOK.lazy_:
7521 case TOK.scope_:
7522 case TOK.final_:
7523 case TOK.auto_:
7524 case TOK.return_:
7525 continue;
7526
7527 case TOK.const_:
7528 case TOK.immutable_:
7529 case TOK.shared_:
7530 case TOK.inout_:
7531 t = peek(t);
7532 if (t.value == TOK.leftParenthesis)
7533 {
7534 t = peek(t);
7535 if (!isDeclaration(t, NeedDeclaratorId.no, TOK.rightParenthesis, &t))
7536 return false;
7537 t = peek(t); // skip past closing ')'
7538 goto L2;
7539 }
7540 goto L1;
7541
7542 version (none)
7543 {
7544 case TOK.static_:
7545 continue;
7546 case TOK.auto_:
7547 case TOK.alias_:
7548 t = peek(t);
7549 if (t.value == TOK.identifier)
7550 t = peek(t);
7551 if (t.value == TOK.assign)
7552 {
7553 t = peek(t);
7554 if (!isExpression(&t))
7555 return false;
7556 }
7557 goto L3;
7558 }
7559
7560 default:
7561 {
7562 if (!isBasicType(&t))
7563 return false;
7564 L2:
7565 int tmp = false;
7566 if (t.value != TOK.dotDotDot && !isDeclarator(&t, &tmp, null, TOK.reserved))
7567 return false;
7568 if (t.value == TOK.assign)
7569 {
7570 t = peek(t);
7571 if (!isExpression(&t))
7572 return false;
7573 }
7574 if (t.value == TOK.dotDotDot)
7575 {
7576 t = peek(t);
7577 break;
7578 }
7579 }
7580 if (t.value == TOK.comma)
7581 {
7582 continue;
7583 }
7584 break;
7585 }
7586 break;
7587 }
7588 if (t.value != TOK.rightParenthesis)
7589 return false;
7590 t = peek(t);
7591 *pt = t;
7592 return true;
7593 }
7594
7595 private bool isExpression(Token** pt)
7596 {
7597 // This is supposed to determine if something is an expression.
7598 // What it actually does is scan until a closing right bracket
7599 // is found.
7600
7601 Token* t = *pt;
7602 int brnest = 0;
7603 int panest = 0;
7604 int curlynest = 0;
7605
7606 for (;; t = peek(t))
7607 {
7608 switch (t.value)
7609 {
7610 case TOK.leftBracket:
7611 brnest++;
7612 continue;
7613
7614 case TOK.rightBracket:
7615 if (--brnest >= 0)
7616 continue;
7617 break;
7618
7619 case TOK.leftParenthesis:
7620 panest++;
7621 continue;
7622
7623 case TOK.comma:
7624 if (brnest || panest)
7625 continue;
7626 break;
7627
7628 case TOK.rightParenthesis:
7629 if (--panest >= 0)
7630 continue;
7631 break;
7632
7633 case TOK.leftCurly:
7634 curlynest++;
7635 continue;
7636
7637 case TOK.rightCurly:
7638 if (--curlynest >= 0)
7639 continue;
7640 return false;
7641
7642 case TOK.slice:
7643 if (brnest)
7644 continue;
7645 break;
7646
7647 case TOK.semicolon:
7648 if (curlynest)
7649 continue;
7650 return false;
7651
7652 case TOK.endOfFile:
7653 return false;
7654
7655 default:
7656 continue;
7657 }
7658 break;
7659 }
7660
7661 *pt = t;
7662 return true;
7663 }
7664
7665 /*******************************************
7666 * Skip parentheses.
7667 * Params:
7668 * t = on opening $(LPAREN)
7669 * pt = *pt is set to token past '$(RPAREN)' on true
7670 * Returns:
7671 * true successful
7672 * false some parsing error
7673 */
7674 bool skipParens(Token* t, Token** pt)
7675 {
7676 if (t.value != TOK.leftParenthesis)
7677 return false;
7678
7679 int parens = 0;
7680
7681 while (1)
7682 {
7683 switch (t.value)
7684 {
7685 case TOK.leftParenthesis:
7686 parens++;
7687 break;
7688
7689 case TOK.rightParenthesis:
7690 parens--;
7691 if (parens < 0)
7692 goto Lfalse;
7693 if (parens == 0)
7694 goto Ldone;
7695 break;
7696
7697 case TOK.endOfFile:
7698 goto Lfalse;
7699
7700 default:
7701 break;
7702 }
7703 t = peek(t);
7704 }
7705 Ldone:
7706 if (pt)
7707 *pt = peek(t); // skip found rparen
7708 return true;
7709
7710 Lfalse:
7711 return false;
7712 }
7713
7714 private bool skipParensIf(Token* t, Token** pt)
7715 {
7716 if (t.value != TOK.leftParenthesis)
7717 {
7718 if (pt)
7719 *pt = t;
7720 return true;
7721 }
7722 return skipParens(t, pt);
7723 }
7724
7725 //returns true if the next value (after optional matching parens) is expected
7726 private bool hasOptionalParensThen(Token* t, TOK expected)
7727 {
7728 Token* tk;
7729 if (!skipParensIf(t, &tk))
7730 return false;
7731 return tk.value == expected;
7732 }
7733
7734 /*******************************************
7735 * Skip attributes.
7736 * Input:
7737 * t is on a candidate attribute
7738 * Output:
7739 * *pt is set to first non-attribute token on success
7740 * Returns:
7741 * true successful
7742 * false some parsing error
7743 */
7744 private bool skipAttributes(Token* t, Token** pt)
7745 {
7746 while (1)
7747 {
7748 switch (t.value)
7749 {
7750 case TOK.const_:
7751 case TOK.immutable_:
7752 case TOK.shared_:
7753 case TOK.inout_:
7754 case TOK.final_:
7755 case TOK.auto_:
7756 case TOK.scope_:
7757 case TOK.override_:
7758 case TOK.abstract_:
7759 case TOK.synchronized_:
7760 break;
7761
7762 case TOK.deprecated_:
7763 if (peek(t).value == TOK.leftParenthesis)
7764 {
7765 t = peek(t);
7766 if (!skipParens(t, &t))
7767 goto Lerror;
7768 // t is on the next of closing parenthesis
7769 continue;
7770 }
7771 break;
7772
7773 case TOK.nothrow_:
7774 case TOK.pure_:
7775 case TOK.ref_:
7776 case TOK.gshared:
7777 case TOK.return_:
7778 break;
7779
7780 case TOK.at:
7781 t = peek(t);
7782 if (t.value == TOK.identifier)
7783 {
7784 /* @identifier
7785 * @identifier!arg
7786 * @identifier!(arglist)
7787 * any of the above followed by (arglist)
7788 * @predefined_attribute
7789 */
7790 if (isBuiltinAtAttribute(t.ident))
7791 break;
7792 t = peek(t);
7793 if (t.value == TOK.not)
7794 {
7795 t = peek(t);
7796 if (t.value == TOK.leftParenthesis)
7797 {
7798 // @identifier!(arglist)
7799 if (!skipParens(t, &t))
7800 goto Lerror;
7801 // t is on the next of closing parenthesis
7802 }
7803 else
7804 {
7805 // @identifier!arg
7806 // Do low rent skipTemplateArgument
7807 if (t.value == TOK.vector)
7808 {
7809 // identifier!__vector(type)
7810 t = peek(t);
7811 if (!skipParens(t, &t))
7812 goto Lerror;
7813 }
7814 else
7815 t = peek(t);
7816 }
7817 }
7818 if (t.value == TOK.leftParenthesis)
7819 {
7820 if (!skipParens(t, &t))
7821 goto Lerror;
7822 // t is on the next of closing parenthesis
7823 continue;
7824 }
7825 continue;
7826 }
7827 if (t.value == TOK.leftParenthesis)
7828 {
7829 // @( ArgumentList )
7830 if (!skipParens(t, &t))
7831 goto Lerror;
7832 // t is on the next of closing parenthesis
7833 continue;
7834 }
7835 goto Lerror;
7836
7837 default:
7838 goto Ldone;
7839 }
7840 t = peek(t);
7841 }
7842 Ldone:
7843 if (pt)
7844 *pt = t;
7845 return true;
7846
7847 Lerror:
7848 return false;
7849 }
7850
7851 AST.Expression parseExpression()
7852 {
7853 auto loc = token.loc;
7854
7855 //printf("Parser::parseExpression() loc = %d\n", loc.linnum);
7856 auto e = parseAssignExp();
7857 while (token.value == TOK.comma)
7858 {
7859 nextToken();
7860 auto e2 = parseAssignExp();
7861 e = new AST.CommaExp(loc, e, e2, false);
7862 loc = token.loc;
7863 }
7864 return e;
7865 }
7866
7867 /********************************* Expression Parser ***************************/
7868
7869 AST.Expression parsePrimaryExp()
7870 {
7871 AST.Expression e;
7872 AST.Type t;
7873 Identifier id;
7874 const loc = token.loc;
7875
7876 //printf("parsePrimaryExp(): loc = %d\n", loc.linnum);
7877 switch (token.value)
7878 {
7879 case TOK.identifier:
7880 {
7881 if (peekNext() == TOK.arrow)
7882 {
7883 // skip `identifier ->`
7884 nextToken();
7885 nextToken();
7886 error("use `.` for member lookup, not `->`");
7887 goto Lerr;
7888 }
7889
7890 if (peekNext() == TOK.goesTo)
7891 goto case_delegate;
7892
7893 id = token.ident;
7894 nextToken();
7895 TOK save;
7896 if (token.value == TOK.not && (save = peekNext()) != TOK.is_ && save != TOK.in_)
7897 {
7898 // identifier!(template-argument-list)
7899 auto tempinst = new AST.TemplateInstance(loc, id, parseTemplateArguments());
7900 e = new AST.ScopeExp(loc, tempinst);
7901 }
7902 else
7903 e = new AST.IdentifierExp(loc, id);
7904 break;
7905 }
7906 case TOK.dollar:
7907 if (!inBrackets)
7908 error("`$` is valid only inside [] of index or slice");
7909 e = new AST.DollarExp(loc);
7910 nextToken();
7911 break;
7912
7913 case TOK.dot:
7914 // Signal global scope '.' operator with "" identifier
7915 e = new AST.IdentifierExp(loc, Id.empty);
7916 break;
7917
7918 case TOK.this_:
7919 e = new AST.ThisExp(loc);
7920 nextToken();
7921 break;
7922
7923 case TOK.super_:
7924 e = new AST.SuperExp(loc);
7925 nextToken();
7926 break;
7927
7928 case TOK.int32Literal:
7929 e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint32);
7930 nextToken();
7931 break;
7932
7933 case TOK.uns32Literal:
7934 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns32);
7935 nextToken();
7936 break;
7937
7938 case TOK.int64Literal:
7939 e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint64);
7940 nextToken();
7941 break;
7942
7943 case TOK.uns64Literal:
7944 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns64);
7945 nextToken();
7946 break;
7947
7948 case TOK.float32Literal:
7949 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat32);
7950 nextToken();
7951 break;
7952
7953 case TOK.float64Literal:
7954 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat64);
7955 nextToken();
7956 break;
7957
7958 case TOK.float80Literal:
7959 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat80);
7960 nextToken();
7961 break;
7962
7963 case TOK.imaginary32Literal:
7964 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary32);
7965 nextToken();
7966 break;
7967
7968 case TOK.imaginary64Literal:
7969 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary64);
7970 nextToken();
7971 break;
7972
7973 case TOK.imaginary80Literal:
7974 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary80);
7975 nextToken();
7976 break;
7977
7978 case TOK.null_:
7979 e = new AST.NullExp(loc);
7980 nextToken();
7981 break;
7982
7983 case TOK.file:
7984 {
7985 const(char)* s = loc.filename ? loc.filename : mod.ident.toChars();
7986 e = new AST.StringExp(loc, s.toDString());
7987 nextToken();
7988 break;
7989 }
7990 case TOK.fileFullPath:
7991 {
7992 assert(loc.isValid(), "__FILE_FULL_PATH__ does not work with an invalid location");
7993 const s = FileName.toAbsolute(loc.filename);
7994 e = new AST.StringExp(loc, s.toDString());
7995 nextToken();
7996 break;
7997 }
7998
7999 case TOK.line:
8000 e = new AST.IntegerExp(loc, loc.linnum, AST.Type.tint32);
8001 nextToken();
8002 break;
8003
8004 case TOK.moduleString:
8005 {
8006 const(char)* s = md ? md.toChars() : mod.toChars();
8007 e = new AST.StringExp(loc, s.toDString());
8008 nextToken();
8009 break;
8010 }
8011 case TOK.functionString:
8012 e = new AST.FuncInitExp(loc);
8013 nextToken();
8014 break;
8015
8016 case TOK.prettyFunction:
8017 e = new AST.PrettyFuncInitExp(loc);
8018 nextToken();
8019 break;
8020
8021 case TOK.true_:
8022 e = new AST.IntegerExp(loc, 1, AST.Type.tbool);
8023 nextToken();
8024 break;
8025
8026 case TOK.false_:
8027 e = new AST.IntegerExp(loc, 0, AST.Type.tbool);
8028 nextToken();
8029 break;
8030
8031 case TOK.charLiteral:
8032 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tchar);
8033 nextToken();
8034 break;
8035
8036 case TOK.wcharLiteral:
8037 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.twchar);
8038 nextToken();
8039 break;
8040
8041 case TOK.dcharLiteral:
8042 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tdchar);
8043 nextToken();
8044 break;
8045
8046 case TOK.string_:
8047 case TOK.hexadecimalString:
8048 {
8049 // cat adjacent strings
8050 auto s = token.ustring;
8051 auto len = token.len;
8052 auto postfix = token.postfix;
8053 while (1)
8054 {
8055 const prev = token;
8056 nextToken();
8057 if (token.value == TOK.string_ || token.value == TOK.hexadecimalString)
8058 {
8059 if (token.postfix)
8060 {
8061 if (token.postfix != postfix)
8062 error("mismatched string literal postfixes `'%c'` and `'%c'`", postfix, token.postfix);
8063 postfix = token.postfix;
8064 }
8065
8066 error("Implicit string concatenation is error-prone and disallowed in D");
8067 errorSupplemental(token.loc, "Use the explicit syntax instead " ~
8068 "(concatenating literals is `@nogc`): %s ~ %s",
8069 prev.toChars(), token.toChars());
8070
8071 const len1 = len;
8072 const len2 = token.len;
8073 len = len1 + len2;
8074 auto s2 = cast(char*)mem.xmalloc_noscan(len * char.sizeof);
8075 memcpy(s2, s, len1 * char.sizeof);
8076 memcpy(s2 + len1, token.ustring, len2 * char.sizeof);
8077 s = s2;
8078 }
8079 else
8080 break;
8081 }
8082 e = new AST.StringExp(loc, s[0 .. len], len, 1, postfix);
8083 break;
8084 }
8085 case TOK.void_:
8086 t = AST.Type.tvoid;
8087 goto LabelX;
8088
8089 case TOK.int8:
8090 t = AST.Type.tint8;
8091 goto LabelX;
8092
8093 case TOK.uns8:
8094 t = AST.Type.tuns8;
8095 goto LabelX;
8096
8097 case TOK.int16:
8098 t = AST.Type.tint16;
8099 goto LabelX;
8100
8101 case TOK.uns16:
8102 t = AST.Type.tuns16;
8103 goto LabelX;
8104
8105 case TOK.int32:
8106 t = AST.Type.tint32;
8107 goto LabelX;
8108
8109 case TOK.uns32:
8110 t = AST.Type.tuns32;
8111 goto LabelX;
8112
8113 case TOK.int64:
8114 t = AST.Type.tint64;
8115 goto LabelX;
8116
8117 case TOK.uns64:
8118 t = AST.Type.tuns64;
8119 goto LabelX;
8120
8121 case TOK.int128:
8122 t = AST.Type.tint128;
8123 goto LabelX;
8124
8125 case TOK.uns128:
8126 t = AST.Type.tuns128;
8127 goto LabelX;
8128
8129 case TOK.float32:
8130 t = AST.Type.tfloat32;
8131 goto LabelX;
8132
8133 case TOK.float64:
8134 t = AST.Type.tfloat64;
8135 goto LabelX;
8136
8137 case TOK.float80:
8138 t = AST.Type.tfloat80;
8139 goto LabelX;
8140
8141 case TOK.imaginary32:
8142 t = AST.Type.timaginary32;
8143 goto LabelX;
8144
8145 case TOK.imaginary64:
8146 t = AST.Type.timaginary64;
8147 goto LabelX;
8148
8149 case TOK.imaginary80:
8150 t = AST.Type.timaginary80;
8151 goto LabelX;
8152
8153 case TOK.complex32:
8154 t = AST.Type.tcomplex32;
8155 goto LabelX;
8156
8157 case TOK.complex64:
8158 t = AST.Type.tcomplex64;
8159 goto LabelX;
8160
8161 case TOK.complex80:
8162 t = AST.Type.tcomplex80;
8163 goto LabelX;
8164
8165 case TOK.bool_:
8166 t = AST.Type.tbool;
8167 goto LabelX;
8168
8169 case TOK.char_:
8170 t = AST.Type.tchar;
8171 goto LabelX;
8172
8173 case TOK.wchar_:
8174 t = AST.Type.twchar;
8175 goto LabelX;
8176
8177 case TOK.dchar_:
8178 t = AST.Type.tdchar;
8179 goto LabelX;
8180 LabelX:
8181 nextToken();
8182 if (token.value == TOK.leftParenthesis)
8183 {
8184 e = new AST.TypeExp(loc, t);
8185 e = new AST.CallExp(loc, e, parseArguments());
8186 break;
8187 }
8188 check(TOK.dot, t.toChars());
8189 if (token.value != TOK.identifier)
8190 {
8191 error("found `%s` when expecting identifier following `%s`.", token.toChars(), t.toChars());
8192 goto Lerr;
8193 }
8194 e = new AST.DotIdExp(loc, new AST.TypeExp(loc, t), token.ident);
8195 nextToken();
8196 break;
8197
8198 case TOK.typeof_:
8199 {
8200 t = parseTypeof();
8201 e = new AST.TypeExp(loc, t);
8202 break;
8203 }
8204 case TOK.vector:
8205 {
8206 t = parseVector();
8207 e = new AST.TypeExp(loc, t);
8208 break;
8209 }
8210 case TOK.typeid_:
8211 {
8212 nextToken();
8213 check(TOK.leftParenthesis, "`typeid`");
8214 RootObject o = parseTypeOrAssignExp();
8215 check(TOK.rightParenthesis);
8216 e = new AST.TypeidExp(loc, o);
8217 break;
8218 }
8219 case TOK.traits:
8220 {
8221 /* __traits(identifier, args...)
8222 */
8223 Identifier ident;
8224 AST.Objects* args = null;
8225
8226 nextToken();
8227 check(TOK.leftParenthesis);
8228 if (token.value != TOK.identifier)
8229 {
8230 error("`__traits(identifier, args...)` expected");
8231 goto Lerr;
8232 }
8233 ident = token.ident;
8234 nextToken();
8235 if (token.value == TOK.comma)
8236 args = parseTemplateArgumentList(); // __traits(identifier, args...)
8237 else
8238 check(TOK.rightParenthesis); // __traits(identifier)
8239
8240 e = new AST.TraitsExp(loc, ident, args);
8241 break;
8242 }
8243 case TOK.is_:
8244 {
8245 AST.Type targ;
8246 Identifier ident = null;
8247 AST.Type tspec = null;
8248 TOK tok = TOK.reserved;
8249 TOK tok2 = TOK.reserved;
8250 AST.TemplateParameters* tpl = null;
8251
8252 nextToken();
8253 if (token.value == TOK.leftParenthesis)
8254 {
8255 nextToken();
8256 if (token.value == TOK.identifier && peekNext() == TOK.leftParenthesis)
8257 {
8258 error(loc, "unexpected `(` after `%s`, inside `is` expression. Try enclosing the contents of `is` with a `typeof` expression", token.toChars());
8259 nextToken();
8260 Token* tempTok = peekPastParen(&token);
8261 memcpy(&token, tempTok, Token.sizeof);
8262 goto Lerr;
8263 }
8264 targ = parseType(&ident);
8265 if (token.value == TOK.colon || token.value == TOK.equal)
8266 {
8267 tok = token.value;
8268 nextToken();
8269 if (tok == TOK.equal && (token.value == TOK.struct_ || token.value == TOK.union_
8270 || token.value == TOK.class_ || token.value == TOK.super_ || token.value == TOK.enum_
8271 || token.value == TOK.interface_ || token.value == TOK.package_ || token.value == TOK.module_
8272 || token.value == TOK.argumentTypes || token.value == TOK.parameters
8273 || token.value == TOK.const_ && peekNext() == TOK.rightParenthesis
8274 || token.value == TOK.immutable_ && peekNext() == TOK.rightParenthesis
8275 || token.value == TOK.shared_ && peekNext() == TOK.rightParenthesis
8276 || token.value == TOK.inout_ && peekNext() == TOK.rightParenthesis || token.value == TOK.function_
8277 || token.value == TOK.delegate_ || token.value == TOK.return_
8278 || (token.value == TOK.vector && peekNext() == TOK.rightParenthesis)))
8279 {
8280 tok2 = token.value;
8281 nextToken();
8282 }
8283 else
8284 {
8285 tspec = parseType();
8286 }
8287 }
8288 if (tspec)
8289 {
8290 if (token.value == TOK.comma)
8291 tpl = parseTemplateParameterList(1);
8292 else
8293 {
8294 tpl = new AST.TemplateParameters();
8295 check(TOK.rightParenthesis);
8296 }
8297 }
8298 else
8299 check(TOK.rightParenthesis);
8300 }
8301 else
8302 {
8303 error("`type identifier : specialization` expected following `is`");
8304 goto Lerr;
8305 }
8306 e = new AST.IsExp(loc, targ, ident, tok, tspec, tok2, tpl);
8307 break;
8308 }
8309 case TOK.assert_:
8310 {
8311 // https://dlang.org/spec/expression.html#assert_expressions
8312 AST.Expression msg = null;
8313
8314 nextToken();
8315 check(TOK.leftParenthesis, "`assert`");
8316 e = parseAssignExp();
8317 if (token.value == TOK.comma)
8318 {
8319 nextToken();
8320 if (token.value != TOK.rightParenthesis)
8321 {
8322 msg = parseAssignExp();
8323 if (token.value == TOK.comma)
8324 nextToken();
8325 }
8326 }
8327 check(TOK.rightParenthesis);
8328 e = new AST.AssertExp(loc, e, msg);
8329 break;
8330 }
8331 case TOK.mixin_:
8332 {
8333 // https://dlang.org/spec/expression.html#mixin_expressions
8334 nextToken();
8335 if (token.value != TOK.leftParenthesis)
8336 error("found `%s` when expecting `%s` following `mixin`", token.toChars(), Token.toChars(TOK.leftParenthesis));
8337 auto exps = parseArguments();
8338 e = new AST.MixinExp(loc, exps);
8339 break;
8340 }
8341 case TOK.import_:
8342 {
8343 nextToken();
8344 check(TOK.leftParenthesis, "`import`");
8345 e = parseAssignExp();
8346 check(TOK.rightParenthesis);
8347 e = new AST.ImportExp(loc, e);
8348 break;
8349 }
8350 case TOK.new_:
8351 e = parseNewExp(null);
8352 break;
8353
8354 case TOK.ref_:
8355 {
8356 if (peekNext() == TOK.leftParenthesis)
8357 {
8358 Token* tk = peekPastParen(peek(&token));
8359 if (skipAttributes(tk, &tk) && (tk.value == TOK.goesTo || tk.value == TOK.leftCurly))
8360 {
8361 // ref (arguments) => expression
8362 // ref (arguments) { statements... }
8363 goto case_delegate;
8364 }
8365 }
8366 nextToken();
8367 error("found `%s` when expecting function literal following `ref`", token.toChars());
8368 goto Lerr;
8369 }
8370 case TOK.leftParenthesis:
8371 {
8372 Token* tk = peekPastParen(&token);
8373 if (skipAttributes(tk, &tk) && (tk.value == TOK.goesTo || tk.value == TOK.leftCurly))
8374 {
8375 // (arguments) => expression
8376 // (arguments) { statements... }
8377 goto case_delegate;
8378 }
8379
8380 // ( expression )
8381 nextToken();
8382 e = parseExpression();
8383 e.parens = 1;
8384 check(loc, TOK.rightParenthesis);
8385 break;
8386 }
8387 case TOK.leftBracket:
8388 {
8389 /* Parse array literals and associative array literals:
8390 * [ value, value, value ... ]
8391 * [ key:value, key:value, key:value ... ]
8392 */
8393 auto values = new AST.Expressions();
8394 AST.Expressions* keys = null;
8395
8396 nextToken();
8397 while (token.value != TOK.rightBracket && token.value != TOK.endOfFile)
8398 {
8399 e = parseAssignExp();
8400 if (token.value == TOK.colon && (keys || values.dim == 0))
8401 {
8402 nextToken();
8403 if (!keys)
8404 keys = new AST.Expressions();
8405 keys.push(e);
8406 e = parseAssignExp();
8407 }
8408 else if (keys)
8409 {
8410 error("`key:value` expected for associative array literal");
8411 keys = null;
8412 }
8413 values.push(e);
8414 if (token.value == TOK.rightBracket)
8415 break;
8416 check(TOK.comma);
8417 }
8418 check(loc, TOK.rightBracket);
8419
8420 if (keys)
8421 e = new AST.AssocArrayLiteralExp(loc, keys, values);
8422 else
8423 e = new AST.ArrayLiteralExp(loc, null, values);
8424 break;
8425 }
8426 case TOK.leftCurly:
8427 case TOK.function_:
8428 case TOK.delegate_:
8429 case_delegate:
8430 {
8431 AST.Dsymbol s = parseFunctionLiteral();
8432 e = new AST.FuncExp(loc, s);
8433 break;
8434 }
8435 default:
8436 error("expression expected, not `%s`", token.toChars());
8437 Lerr:
8438 // Anything for e, as long as it's not NULL
8439 e = new AST.IntegerExp(loc, 0, AST.Type.tint32);
8440 nextToken();
8441 break;
8442 }
8443 return e;
8444 }
8445
8446 private AST.Expression parseUnaryExp()
8447 {
8448 AST.Expression e;
8449 const loc = token.loc;
8450
8451 switch (token.value)
8452 {
8453 case TOK.and:
8454 nextToken();
8455 e = parseUnaryExp();
8456 e = new AST.AddrExp(loc, e);
8457 break;
8458
8459 case TOK.plusPlus:
8460 nextToken();
8461 e = parseUnaryExp();
8462 //e = new AddAssignExp(loc, e, new IntegerExp(loc, 1, Type::tint32));
8463 e = new AST.PreExp(TOK.prePlusPlus, loc, e);
8464 break;
8465
8466 case TOK.minusMinus:
8467 nextToken();
8468 e = parseUnaryExp();
8469 //e = new MinAssignExp(loc, e, new IntegerExp(loc, 1, Type::tint32));
8470 e = new AST.PreExp(TOK.preMinusMinus, loc, e);
8471 break;
8472
8473 case TOK.mul:
8474 nextToken();
8475 e = parseUnaryExp();
8476 e = new AST.PtrExp(loc, e);
8477 break;
8478
8479 case TOK.min:
8480 nextToken();
8481 e = parseUnaryExp();
8482 e = new AST.NegExp(loc, e);
8483 break;
8484
8485 case TOK.add:
8486 nextToken();
8487 e = parseUnaryExp();
8488 e = new AST.UAddExp(loc, e);
8489 break;
8490
8491 case TOK.not:
8492 nextToken();
8493 e = parseUnaryExp();
8494 e = new AST.NotExp(loc, e);
8495 break;
8496
8497 case TOK.tilde:
8498 nextToken();
8499 e = parseUnaryExp();
8500 e = new AST.ComExp(loc, e);
8501 break;
8502
8503 case TOK.delete_:
8504 nextToken();
8505 e = parseUnaryExp();
8506 e = new AST.DeleteExp(loc, e, false);
8507 break;
8508
8509 case TOK.cast_: // cast(type) expression
8510 {
8511 nextToken();
8512 check(TOK.leftParenthesis);
8513 /* Look for cast(), cast(const), cast(immutable),
8514 * cast(shared), cast(shared const), cast(wild), cast(shared wild)
8515 */
8516 ubyte m = 0;
8517 while (1)
8518 {
8519 switch (token.value)
8520 {
8521 case TOK.const_:
8522 if (peekNext() == TOK.leftParenthesis)
8523 break; // const as type constructor
8524 m |= MODFlags.const_; // const as storage class
8525 nextToken();
8526 continue;
8527
8528 case TOK.immutable_:
8529 if (peekNext() == TOK.leftParenthesis)
8530 break;
8531 m |= MODFlags.immutable_;
8532 nextToken();
8533 continue;
8534
8535 case TOK.shared_:
8536 if (peekNext() == TOK.leftParenthesis)
8537 break;
8538 m |= MODFlags.shared_;
8539 nextToken();
8540 continue;
8541
8542 case TOK.inout_:
8543 if (peekNext() == TOK.leftParenthesis)
8544 break;
8545 m |= MODFlags.wild;
8546 nextToken();
8547 continue;
8548
8549 default:
8550 break;
8551 }
8552 break;
8553 }
8554 if (token.value == TOK.rightParenthesis)
8555 {
8556 nextToken();
8557 e = parseUnaryExp();
8558 e = new AST.CastExp(loc, e, m);
8559 }
8560 else
8561 {
8562 AST.Type t = parseType(); // cast( type )
8563 t = t.addMod(m); // cast( const type )
8564 check(TOK.rightParenthesis);
8565 e = parseUnaryExp();
8566 e = new AST.CastExp(loc, e, t);
8567 }
8568 break;
8569 }
8570 case TOK.inout_:
8571 case TOK.shared_:
8572 case TOK.const_:
8573 case TOK.immutable_: // immutable(type)(arguments) / immutable(type).init
8574 {
8575 StorageClass stc = parseTypeCtor();
8576
8577 AST.Type t = parseBasicType();
8578 t = t.addSTC(stc);
8579
8580 if (stc == 0 && token.value == TOK.dot)
8581 {
8582 nextToken();
8583 if (token.value != TOK.identifier)
8584 {
8585 error("identifier expected following `(type)`.");
8586 return null;
8587 }
8588 e = new AST.DotIdExp(loc, new AST.TypeExp(loc, t), token.ident);
8589 nextToken();
8590 e = parsePostExp(e);
8591 }
8592 else
8593 {
8594 e = new AST.TypeExp(loc, t);
8595 if (token.value != TOK.leftParenthesis)
8596 {
8597 error("`(arguments)` expected following `%s`", t.toChars());
8598 return e;
8599 }
8600 e = new AST.CallExp(loc, e, parseArguments());
8601 }
8602 break;
8603 }
8604 case TOK.leftParenthesis:
8605 {
8606 auto tk = peek(&token);
8607 static if (CCASTSYNTAX)
8608 {
8609 // If cast
8610 if (isDeclaration(tk, NeedDeclaratorId.no, TOK.rightParenthesis, &tk))
8611 {
8612 tk = peek(tk); // skip over right parenthesis
8613 switch (tk.value)
8614 {
8615 case TOK.not:
8616 tk = peek(tk);
8617 if (tk.value == TOK.is_ || tk.value == TOK.in_) // !is or !in
8618 break;
8619 goto case;
8620
8621 case TOK.dot:
8622 case TOK.plusPlus:
8623 case TOK.minusMinus:
8624 case TOK.delete_:
8625 case TOK.new_:
8626 case TOK.leftParenthesis:
8627 case TOK.identifier:
8628 case TOK.this_:
8629 case TOK.super_:
8630 case TOK.int32Literal:
8631 case TOK.uns32Literal:
8632 case TOK.int64Literal:
8633 case TOK.uns64Literal:
8634 case TOK.int128Literal:
8635 case TOK.uns128Literal:
8636 case TOK.float32Literal:
8637 case TOK.float64Literal:
8638 case TOK.float80Literal:
8639 case TOK.imaginary32Literal:
8640 case TOK.imaginary64Literal:
8641 case TOK.imaginary80Literal:
8642 case TOK.null_:
8643 case TOK.true_:
8644 case TOK.false_:
8645 case TOK.charLiteral:
8646 case TOK.wcharLiteral:
8647 case TOK.dcharLiteral:
8648 case TOK.string_:
8649 version (none)
8650 {
8651 case TOK.tilde:
8652 case TOK.and:
8653 case TOK.mul:
8654 case TOK.min:
8655 case TOK.add:
8656 }
8657 case TOK.function_:
8658 case TOK.delegate_:
8659 case TOK.typeof_:
8660 case TOK.traits:
8661 case TOK.vector:
8662 case TOK.file:
8663 case TOK.fileFullPath:
8664 case TOK.line:
8665 case TOK.moduleString:
8666 case TOK.functionString:
8667 case TOK.prettyFunction:
8668 case TOK.wchar_:
8669 case TOK.dchar_:
8670 case TOK.bool_:
8671 case TOK.char_:
8672 case TOK.int8:
8673 case TOK.uns8:
8674 case TOK.int16:
8675 case TOK.uns16:
8676 case TOK.int32:
8677 case TOK.uns32:
8678 case TOK.int64:
8679 case TOK.uns64:
8680 case TOK.int128:
8681 case TOK.uns128:
8682 case TOK.float32:
8683 case TOK.float64:
8684 case TOK.float80:
8685 case TOK.imaginary32:
8686 case TOK.imaginary64:
8687 case TOK.imaginary80:
8688 case TOK.complex32:
8689 case TOK.complex64:
8690 case TOK.complex80:
8691 case TOK.void_:
8692 {
8693 // (type) una_exp
8694 nextToken();
8695 auto t = parseType();
8696 check(TOK.rightParenthesis);
8697
8698 // if .identifier
8699 // or .identifier!( ... )
8700 if (token.value == TOK.dot)
8701 {
8702 if (peekNext() != TOK.identifier && peekNext() != TOK.new_)
8703 {
8704 error("identifier or new keyword expected following `(...)`.");
8705 return null;
8706 }
8707 e = new AST.TypeExp(loc, t);
8708 e.parens = true;
8709 e = parsePostExp(e);
8710 }
8711 else
8712 {
8713 e = parseUnaryExp();
8714 e = new AST.CastExp(loc, e, t);
8715 error("C style cast illegal, use `%s`", e.toChars());
8716 }
8717 return e;
8718 }
8719 default:
8720 break;
8721 }
8722 }
8723 }
8724 e = parsePrimaryExp();
8725 e = parsePostExp(e);
8726 break;
8727 }
8728 default:
8729 e = parsePrimaryExp();
8730 e = parsePostExp(e);
8731 break;
8732 }
8733 assert(e);
8734
8735 // ^^ is right associative and has higher precedence than the unary operators
8736 while (token.value == TOK.pow)
8737 {
8738 nextToken();
8739 AST.Expression e2 = parseUnaryExp();
8740 e = new AST.PowExp(loc, e, e2);
8741 }
8742
8743 return e;
8744 }
8745
8746 private AST.Expression parsePostExp(AST.Expression e)
8747 {
8748 while (1)
8749 {
8750 const loc = token.loc;
8751 switch (token.value)
8752 {
8753 case TOK.dot:
8754 nextToken();
8755 if (token.value == TOK.identifier)
8756 {
8757 Identifier id = token.ident;
8758
8759 nextToken();
8760 if (token.value == TOK.not && peekNext() != TOK.is_ && peekNext() != TOK.in_)
8761 {
8762 AST.Objects* tiargs = parseTemplateArguments();
8763 e = new AST.DotTemplateInstanceExp(loc, e, id, tiargs);
8764 }
8765 else
8766 e = new AST.DotIdExp(loc, e, id);
8767 continue;
8768 }
8769 if (token.value == TOK.new_)
8770 {
8771 e = parseNewExp(e);
8772 continue;
8773 }
8774 error("identifier or `new` expected following `.`, not `%s`", token.toChars());
8775 break;
8776
8777 case TOK.plusPlus:
8778 e = new AST.PostExp(TOK.plusPlus, loc, e);
8779 break;
8780
8781 case TOK.minusMinus:
8782 e = new AST.PostExp(TOK.minusMinus, loc, e);
8783 break;
8784
8785 case TOK.leftParenthesis:
8786 e = new AST.CallExp(loc, e, parseArguments());
8787 continue;
8788
8789 case TOK.leftBracket:
8790 {
8791 // array dereferences:
8792 // array[index]
8793 // array[]
8794 // array[lwr .. upr]
8795 AST.Expression index;
8796 AST.Expression upr;
8797 auto arguments = new AST.Expressions();
8798
8799 inBrackets++;
8800 nextToken();
8801 while (token.value != TOK.rightBracket && token.value != TOK.endOfFile)
8802 {
8803 index = parseAssignExp();
8804 if (token.value == TOK.slice)
8805 {
8806 // array[..., lwr..upr, ...]
8807 nextToken();
8808 upr = parseAssignExp();
8809 arguments.push(new AST.IntervalExp(loc, index, upr));
8810 }
8811 else
8812 arguments.push(index);
8813 if (token.value == TOK.rightBracket)
8814 break;
8815 check(TOK.comma);
8816 }
8817 check(TOK.rightBracket);
8818 inBrackets--;
8819 e = new AST.ArrayExp(loc, e, arguments);
8820 continue;
8821 }
8822 default:
8823 return e;
8824 }
8825 nextToken();
8826 }
8827 }
8828
8829 private AST.Expression parseMulExp()
8830 {
8831 const loc = token.loc;
8832 auto e = parseUnaryExp();
8833
8834 while (1)
8835 {
8836 switch (token.value)
8837 {
8838 case TOK.mul:
8839 nextToken();
8840 auto e2 = parseUnaryExp();
8841 e = new AST.MulExp(loc, e, e2);
8842 continue;
8843
8844 case TOK.div:
8845 nextToken();
8846 auto e2 = parseUnaryExp();
8847 e = new AST.DivExp(loc, e, e2);
8848 continue;
8849
8850 case TOK.mod:
8851 nextToken();
8852 auto e2 = parseUnaryExp();
8853 e = new AST.ModExp(loc, e, e2);
8854 continue;
8855
8856 default:
8857 break;
8858 }
8859 break;
8860 }
8861 return e;
8862 }
8863
8864 private AST.Expression parseAddExp()
8865 {
8866 const loc = token.loc;
8867 auto e = parseMulExp();
8868
8869 while (1)
8870 {
8871 switch (token.value)
8872 {
8873 case TOK.add:
8874 nextToken();
8875 auto e2 = parseMulExp();
8876 e = new AST.AddExp(loc, e, e2);
8877 continue;
8878
8879 case TOK.min:
8880 nextToken();
8881 auto e2 = parseMulExp();
8882 e = new AST.MinExp(loc, e, e2);
8883 continue;
8884
8885 case TOK.tilde:
8886 nextToken();
8887 auto e2 = parseMulExp();
8888 e = new AST.CatExp(loc, e, e2);
8889 continue;
8890
8891 default:
8892 break;
8893 }
8894 break;
8895 }
8896 return e;
8897 }
8898
8899 private AST.Expression parseShiftExp()
8900 {
8901 const loc = token.loc;
8902 auto e = parseAddExp();
8903
8904 while (1)
8905 {
8906 switch (token.value)
8907 {
8908 case TOK.leftShift:
8909 nextToken();
8910 auto e2 = parseAddExp();
8911 e = new AST.ShlExp(loc, e, e2);
8912 continue;
8913
8914 case TOK.rightShift:
8915 nextToken();
8916 auto e2 = parseAddExp();
8917 e = new AST.ShrExp(loc, e, e2);
8918 continue;
8919
8920 case TOK.unsignedRightShift:
8921 nextToken();
8922 auto e2 = parseAddExp();
8923 e = new AST.UshrExp(loc, e, e2);
8924 continue;
8925
8926 default:
8927 break;
8928 }
8929 break;
8930 }
8931 return e;
8932 }
8933
8934 private AST.Expression parseCmpExp()
8935 {
8936 const loc = token.loc;
8937
8938 auto e = parseShiftExp();
8939 TOK op = token.value;
8940
8941 switch (op)
8942 {
8943 case TOK.equal:
8944 case TOK.notEqual:
8945 nextToken();
8946 auto e2 = parseShiftExp();
8947 e = new AST.EqualExp(op, loc, e, e2);
8948 break;
8949
8950 case TOK.is_:
8951 op = TOK.identity;
8952 goto L1;
8953
8954 case TOK.not:
8955 {
8956 // Attempt to identify '!is'
8957 const tv = peekNext();
8958 if (tv == TOK.in_)
8959 {
8960 nextToken();
8961 nextToken();
8962 auto e2 = parseShiftExp();
8963 e = new AST.InExp(loc, e, e2);
8964 e = new AST.NotExp(loc, e);
8965 break;
8966 }
8967 if (tv != TOK.is_)
8968 break;
8969 nextToken();
8970 op = TOK.notIdentity;
8971 goto L1;
8972 }
8973 L1:
8974 nextToken();
8975 auto e2 = parseShiftExp();
8976 e = new AST.IdentityExp(op, loc, e, e2);
8977 break;
8978
8979 case TOK.lessThan:
8980 case TOK.lessOrEqual:
8981 case TOK.greaterThan:
8982 case TOK.greaterOrEqual:
8983 nextToken();
8984 auto e2 = parseShiftExp();
8985 e = new AST.CmpExp(op, loc, e, e2);
8986 break;
8987
8988 case TOK.in_:
8989 nextToken();
8990 auto e2 = parseShiftExp();
8991 e = new AST.InExp(loc, e, e2);
8992 break;
8993
8994 default:
8995 break;
8996 }
8997 return e;
8998 }
8999
9000 private AST.Expression parseAndExp()
9001 {
9002 Loc loc = token.loc;
9003 auto e = parseCmpExp();
9004 while (token.value == TOK.and)
9005 {
9006 checkParens(TOK.and, e);
9007 nextToken();
9008 auto e2 = parseCmpExp();
9009 checkParens(TOK.and, e2);
9010 e = new AST.AndExp(loc, e, e2);
9011 loc = token.loc;
9012 }
9013 return e;
9014 }
9015
9016 private AST.Expression parseXorExp()
9017 {
9018 const loc = token.loc;
9019
9020 auto e = parseAndExp();
9021 while (token.value == TOK.xor)
9022 {
9023 checkParens(TOK.xor, e);
9024 nextToken();
9025 auto e2 = parseAndExp();
9026 checkParens(TOK.xor, e2);
9027 e = new AST.XorExp(loc, e, e2);
9028 }
9029 return e;
9030 }
9031
9032 private AST.Expression parseOrExp()
9033 {
9034 const loc = token.loc;
9035
9036 auto e = parseXorExp();
9037 while (token.value == TOK.or)
9038 {
9039 checkParens(TOK.or, e);
9040 nextToken();
9041 auto e2 = parseXorExp();
9042 checkParens(TOK.or, e2);
9043 e = new AST.OrExp(loc, e, e2);
9044 }
9045 return e;
9046 }
9047
9048 private AST.Expression parseAndAndExp()
9049 {
9050 const loc = token.loc;
9051
9052 auto e = parseOrExp();
9053 while (token.value == TOK.andAnd)
9054 {
9055 nextToken();
9056 auto e2 = parseOrExp();
9057 e = new AST.LogicalExp(loc, TOK.andAnd, e, e2);
9058 }
9059 return e;
9060 }
9061
9062 private AST.Expression parseOrOrExp()
9063 {
9064 const loc = token.loc;
9065
9066 auto e = parseAndAndExp();
9067 while (token.value == TOK.orOr)
9068 {
9069 nextToken();
9070 auto e2 = parseAndAndExp();
9071 e = new AST.LogicalExp(loc, TOK.orOr, e, e2);
9072 }
9073 return e;
9074 }
9075
9076 private AST.Expression parseCondExp()
9077 {
9078 const loc = token.loc;
9079
9080 auto e = parseOrOrExp();
9081 if (token.value == TOK.question)
9082 {
9083 nextToken();
9084 auto e1 = parseExpression();
9085 check(TOK.colon);
9086 auto e2 = parseCondExp();
9087 e = new AST.CondExp(loc, e, e1, e2);
9088 }
9089 return e;
9090 }
9091
9092 AST.Expression parseAssignExp()
9093 {
9094 AST.Expression e;
9095 e = parseCondExp();
9096 if (e is null)
9097 return e;
9098
9099 // require parens for e.g. `t ? a = 1 : b = 2`
9100 if (e.op == TOK.question && !e.parens && precedence[token.value] == PREC.assign)
9101 dmd.errors.error(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`",
9102 e.toChars(), Token.toChars(token.value));
9103
9104 const loc = token.loc;
9105 switch (token.value)
9106 {
9107 case TOK.assign:
9108 nextToken();
9109 auto e2 = parseAssignExp();
9110 e = new AST.AssignExp(loc, e, e2);
9111 break;
9112
9113 case TOK.addAssign:
9114 nextToken();
9115 auto e2 = parseAssignExp();
9116 e = new AST.AddAssignExp(loc, e, e2);
9117 break;
9118
9119 case TOK.minAssign:
9120 nextToken();
9121 auto e2 = parseAssignExp();
9122 e = new AST.MinAssignExp(loc, e, e2);
9123 break;
9124
9125 case TOK.mulAssign:
9126 nextToken();
9127 auto e2 = parseAssignExp();
9128 e = new AST.MulAssignExp(loc, e, e2);
9129 break;
9130
9131 case TOK.divAssign:
9132 nextToken();
9133 auto e2 = parseAssignExp();
9134 e = new AST.DivAssignExp(loc, e, e2);
9135 break;
9136
9137 case TOK.modAssign:
9138 nextToken();
9139 auto e2 = parseAssignExp();
9140 e = new AST.ModAssignExp(loc, e, e2);
9141 break;
9142
9143 case TOK.powAssign:
9144 nextToken();
9145 auto e2 = parseAssignExp();
9146 e = new AST.PowAssignExp(loc, e, e2);
9147 break;
9148
9149 case TOK.andAssign:
9150 nextToken();
9151 auto e2 = parseAssignExp();
9152 e = new AST.AndAssignExp(loc, e, e2);
9153 break;
9154
9155 case TOK.orAssign:
9156 nextToken();
9157 auto e2 = parseAssignExp();
9158 e = new AST.OrAssignExp(loc, e, e2);
9159 break;
9160
9161 case TOK.xorAssign:
9162 nextToken();
9163 auto e2 = parseAssignExp();
9164 e = new AST.XorAssignExp(loc, e, e2);
9165 break;
9166
9167 case TOK.leftShiftAssign:
9168 nextToken();
9169 auto e2 = parseAssignExp();
9170 e = new AST.ShlAssignExp(loc, e, e2);
9171 break;
9172
9173 case TOK.rightShiftAssign:
9174 nextToken();
9175 auto e2 = parseAssignExp();
9176 e = new AST.ShrAssignExp(loc, e, e2);
9177 break;
9178
9179 case TOK.unsignedRightShiftAssign:
9180 nextToken();
9181 auto e2 = parseAssignExp();
9182 e = new AST.UshrAssignExp(loc, e, e2);
9183 break;
9184
9185 case TOK.concatenateAssign:
9186 nextToken();
9187 auto e2 = parseAssignExp();
9188 e = new AST.CatAssignExp(loc, e, e2);
9189 break;
9190
9191 default:
9192 break;
9193 }
9194
9195 return e;
9196 }
9197
9198 /*************************
9199 * Collect argument list.
9200 * Assume current token is ',', '$(LPAREN)' or '['.
9201 */
9202 private AST.Expressions* parseArguments()
9203 {
9204 // function call
9205 AST.Expressions* arguments;
9206
9207 arguments = new AST.Expressions();
9208 const endtok = token.value == TOK.leftBracket ? TOK.rightBracket : TOK.rightParenthesis;
9209
9210 nextToken();
9211
9212 while (token.value != endtok && token.value != TOK.endOfFile)
9213 {
9214 auto arg = parseAssignExp();
9215 arguments.push(arg);
9216 if (token.value != TOK.comma)
9217 break;
9218
9219 nextToken(); //comma
9220 }
9221
9222 check(endtok);
9223
9224 return arguments;
9225 }
9226
9227 /*******************************************
9228 */
9229 private AST.Expression parseNewExp(AST.Expression thisexp)
9230 {
9231 const loc = token.loc;
9232
9233 nextToken();
9234 AST.Expressions* newargs = null;
9235 AST.Expressions* arguments = null;
9236 if (token.value == TOK.leftParenthesis)
9237 {
9238 newargs = parseArguments();
9239 }
9240
9241 // An anonymous nested class starts with "class"
9242 if (token.value == TOK.class_)
9243 {
9244 nextToken();
9245 if (token.value == TOK.leftParenthesis)
9246 arguments = parseArguments();
9247
9248 AST.BaseClasses* baseclasses = null;
9249 if (token.value != TOK.leftCurly)
9250 baseclasses = parseBaseClasses();
9251
9252 Identifier id = null;
9253 AST.Dsymbols* members = null;
9254
9255 if (token.value != TOK.leftCurly)
9256 {
9257 error("`{ members }` expected for anonymous class");
9258 }
9259 else
9260 {
9261 nextToken();
9262 members = parseDeclDefs(0);
9263 if (token.value != TOK.rightCurly)
9264 error("class member expected");
9265 nextToken();
9266 }
9267
9268 auto cd = new AST.ClassDeclaration(loc, id, baseclasses, members, false);
9269 auto e = new AST.NewAnonClassExp(loc, thisexp, newargs, cd, arguments);
9270 return e;
9271 }
9272
9273 const stc = parseTypeCtor();
9274 auto t = parseBasicType(true);
9275 t = parseTypeSuffixes(t);
9276 t = t.addSTC(stc);
9277 if (t.ty == Taarray)
9278 {
9279 AST.TypeAArray taa = cast(AST.TypeAArray)t;
9280 AST.Type index = taa.index;
9281 auto edim = AST.typeToExpression(index);
9282 if (!edim)
9283 {
9284 error("cannot create a `%s` with `new`", t.toChars);
9285 return new AST.NullExp(loc);
9286 }
9287 t = new AST.TypeSArray(taa.next, edim);
9288 }
9289 else if (token.value == TOK.leftParenthesis && t.ty != Tsarray)
9290 {
9291 arguments = parseArguments();
9292 }
9293
9294 auto e = new AST.NewExp(loc, thisexp, newargs, t, arguments);
9295 return e;
9296 }
9297
9298 /**********************************************
9299 */
9300 private void addComment(AST.Dsymbol s, const(char)* blockComment)
9301 {
9302 if (s !is null)
9303 this.addComment(s, blockComment.toDString());
9304 }
9305
9306 private void addComment(AST.Dsymbol s, const(char)[] blockComment)
9307 {
9308 if (s !is null)
9309 {
9310 s.addComment(combineComments(blockComment, token.lineComment, true));
9311 token.lineComment = null;
9312 }
9313 }
9314
9315 /**********************************************
9316 * Recognize builtin @ attributes
9317 * Params:
9318 * ident = identifier
9319 * Returns:
9320 * storage class for attribute, 0 if not
9321 */
9322 static StorageClass isBuiltinAtAttribute(Identifier ident)
9323 {
9324 return (ident == Id.property) ? STC.property :
9325 (ident == Id.nogc) ? STC.nogc :
9326 (ident == Id.safe) ? STC.safe :
9327 (ident == Id.trusted) ? STC.trusted :
9328 (ident == Id.system) ? STC.system :
9329 (ident == Id.live) ? STC.live :
9330 (ident == Id.future) ? STC.future :
9331 (ident == Id.disable) ? STC.disable :
9332 0;
9333 }
9334
9335 enum StorageClass atAttrGroup =
9336 STC.property |
9337 STC.nogc |
9338 STC.safe |
9339 STC.trusted |
9340 STC.system |
9341 STC.live |
9342 /*STC.future |*/ // probably should be included
9343 STC.disable;
9344 }
9345
9346 enum PREC : int
9347 {
9348 zero,
9349 expr,
9350 assign,
9351 cond,
9352 oror,
9353 andand,
9354 or,
9355 xor,
9356 and,
9357 equal,
9358 rel,
9359 shift,
9360 add,
9361 mul,
9362 pow,
9363 unary,
9364 primary,
9365 }
9366