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