d: Merge upstream dmd ff57fec515, druntime ff57fec515, phobos 17bafda79.
[official-gcc.git] / gcc / d / dmd / cparse.d
blobed5f1f8b9a2165dba7deb70aa16c8b06931d46be
1 /**
2 * Takes a token stream from the lexer, and parses it into an abstract syntax tree.
4 * Specification: C11
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/cparse.d, _cparse.d)
10 * Documentation: https://dlang.org/phobos/dmd_cparse.html
11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cparse.d
14 module dmd.cparse;
16 import core.stdc.stdio;
17 import core.stdc.string : memcpy;
19 import dmd.astenums;
20 import dmd.errorsink;
21 import dmd.id;
22 import dmd.identifier;
23 import dmd.lexer;
24 import dmd.location;
25 import dmd.parse;
26 import dmd.root.array;
27 import dmd.common.outbuffer;
28 import dmd.root.rmem;
29 import dmd.tokens;
31 /***********************************************************
33 final class CParser(AST) : Parser!AST
35 AST.Dsymbols* symbols; // symbols declared in current scope
37 bool addFuncName; /// add declaration of __func__ to function symbol table
38 bool importBuiltins; /// seen use of C compiler builtins, so import __builtins;
40 private
42 structalign_t packalign; // current state of #pragma pack alignment
44 // #pragma pack stack
45 Array!Identifier* records; // identifers (or null)
46 Array!structalign_t* packs; // parallel alignment values
49 /* C cannot be parsed without determining if an identifier is a type or a variable.
50 * For expressions like `(T)-3`, is it a cast or a minus expression?
51 * It also occurs with `typedef int (F)(); F fun;`
52 * but to build the AST we need to distinguish `fun` being a function as opposed to a variable.
53 * To fix, build a symbol table for the typedefs.
54 * Symbol table of typedefs indexed by Identifier cast to void*.
55 * 1. if an identifier is a typedef, then it will return a non-null Type
56 * 2. if an identifier is not a typedef, then it will return null
58 Array!(void*) typedefTab; /// Array of AST.Type[Identifier], typedef's indexed by Identifier
60 /* This is passed in as a list of #define lines, as generated by the C preprocessor with the
61 * appropriate switch to emit them. We append to it any #define's and #undef's encountered in the source
62 * file, as cpp with the -dD embeds them in the preprocessed output file.
63 * Once the file is parsed, then the #define's are converted to D symbols and appended to the array
64 * of Dsymbols returned by parseModule().
66 OutBuffer* defines;
68 extern (D) this(TARGET)(AST.Module _module, const(char)[] input, bool doDocComment,
69 ErrorSink errorSink,
70 const ref TARGET target, OutBuffer* defines, const CompileEnv* compileEnv) scope
72 const bool doUnittests = false;
73 super(_module, input, doDocComment, errorSink, compileEnv, doUnittests);
75 //printf("CParser.this()\n");
76 mod = _module;
77 linkage = LINK.c;
78 Ccompile = true;
79 this.packalign.setDefault();
80 this.defines = defines;
82 // Configure sizes for C `long`, `long double`, `wchar_t`, ...
83 this.boolsize = target.boolsize;
84 this.shortsize = target.shortsize;
85 this.intsize = target.intsize;
86 this.longsize = target.longsize;
87 this.long_longsize = target.long_longsize;
88 this.long_doublesize = target.long_doublesize;
89 this.wchar_tsize = target.wchar_tsize;
91 // C `char` is always unsigned in ImportC
94 /********************************************
95 * Parse translation unit.
96 * C11 6.9
97 * translation-unit:
98 * external-declaration
99 * translation-unit external-declaration
101 * external-declaration:
102 * function-definition
103 * declaration
104 * Returns:
105 * array of Dsymbols that were declared
107 override AST.Dsymbols* parseModule()
109 //printf("cparseTranslationUnit()\n");
110 symbols = new AST.Dsymbols();
111 typedefTab.push(null); // C11 6.2.1-3 symbol table for "file scope"
112 while (1)
114 if (token.value == TOK.endOfFile)
116 addDefines(); // convert #define's to Dsymbols
118 // wrap the symbols in `extern (C) { symbols }`
119 auto wrap = new AST.Dsymbols();
120 auto ld = new AST.LinkDeclaration(token.loc, LINK.c, symbols);
121 wrap.push(ld);
123 if (importBuiltins)
125 /* Seen references to C builtin functions.
126 * Import their definitions
128 auto s = new AST.Import(Loc.initial, null, Id.builtins, null, false);
129 wrap.push(s);
132 // end of file scope
133 typedefTab.pop();
134 assert(typedefTab.length == 0);
136 return wrap;
139 cparseDeclaration(LVL.global);
143 /******************************************************************************/
144 /********************************* Statement Parser ***************************/
147 /**********************
148 * C11 6.8
149 * statement:
150 * labeled-statement
151 * compound-statement
152 * expression-statement
153 * selection-statement
154 * iteration-statement
155 * jump-statement
157 * Params:
158 * flags = PSxxxx
159 * endPtr = store location of closing brace
160 * pEndloc = if { ... statements ... }, store location of closing brace, otherwise loc of last token of statement
161 * Returns:
162 * parsed statement
164 AST.Statement cparseStatement(int flags, const(char)** endPtr = null, Loc* pEndloc = null)
166 AST.Statement s;
167 const loc = token.loc;
169 //printf("cparseStatement()\n");
171 const typedefTabLengthSave = typedefTab.length;
172 auto symbolsSave = symbols;
173 if (flags & ParseStatementFlags.scope_)
175 typedefTab.push(null); // introduce new block scope
178 if (!(flags & (ParseStatementFlags.scope_ | ParseStatementFlags.curlyScope)))
180 symbols = new AST.Dsymbols();
183 switch (token.value)
185 case TOK.identifier:
186 /* A leading identifier can be a declaration, label, or expression.
187 * A quick check of the next token can disambiguate most cases.
189 switch (peekNext())
191 case TOK.colon:
193 // It's a label
194 auto ident = token.ident;
195 nextToken(); // advance to `:`
196 nextToken(); // advance past `:`
197 if (token.value == TOK.rightCurly)
198 s = null;
199 else if (token.value == TOK.leftCurly)
200 s = cparseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_);
201 else
202 s = cparseStatement(0);
203 s = new AST.LabelStatement(loc, ident, s);
204 break;
207 case TOK.dot:
208 case TOK.arrow:
209 case TOK.plusPlus:
210 case TOK.minusMinus:
211 case TOK.leftBracket:
212 case TOK.question:
213 case TOK.assign:
214 case TOK.addAssign:
215 case TOK.minAssign:
216 case TOK.mulAssign:
217 case TOK.divAssign:
218 case TOK.modAssign:
219 case TOK.andAssign:
220 case TOK.orAssign:
221 case TOK.xorAssign:
222 case TOK.leftShiftAssign:
223 case TOK.rightShiftAssign:
224 goto Lexp;
226 case TOK.leftParenthesis:
227 if (auto pt = lookupTypedef(token.ident))
229 if (*pt)
230 goto Ldeclaration;
232 goto Lexp; // function call
234 case TOK.semicolon:
235 goto Lexp;
237 default:
239 /* If tokens look like a declaration, assume it is one
241 auto tk = &token;
242 if (isCDeclaration(tk))
243 goto Ldeclaration;
244 goto Lexp;
247 break;
249 case TOK.charLiteral:
250 case TOK.int32Literal:
251 case TOK.uns32Literal:
252 case TOK.int64Literal:
253 case TOK.uns64Literal:
254 case TOK.int128Literal:
255 case TOK.uns128Literal:
256 case TOK.float32Literal:
257 case TOK.float64Literal:
258 case TOK.float80Literal:
259 case TOK.imaginary32Literal:
260 case TOK.imaginary64Literal:
261 case TOK.imaginary80Literal:
262 case TOK.leftParenthesis:
263 case TOK.and:
264 case TOK.mul:
265 case TOK.min:
266 case TOK.add:
267 case TOK.tilde:
268 case TOK.not:
269 case TOK.plusPlus:
270 case TOK.minusMinus:
271 case TOK.sizeof_:
272 case TOK._Generic:
273 case TOK._assert:
274 Lexp:
275 auto exp = cparseExpression();
276 if (token.value == TOK.identifier && exp.op == EXP.identifier)
278 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());
279 nextToken();
281 else
282 check(TOK.semicolon, "statement");
283 s = new AST.ExpStatement(loc, exp);
284 break;
286 // type-specifiers
287 case TOK.void_:
288 case TOK.char_:
289 case TOK.int16:
290 case TOK.int32:
291 case TOK.int64:
292 case TOK.__int128:
293 case TOK.float32:
294 case TOK.float64:
295 case TOK.signed:
296 case TOK.unsigned:
297 case TOK._Bool:
298 //case TOK._Imaginary:
299 case TOK._Complex:
300 case TOK.struct_:
301 case TOK.union_:
302 case TOK.enum_:
303 case TOK.typeof_:
305 // storage-class-specifiers
306 case TOK.typedef_:
307 case TOK.extern_:
308 case TOK.static_:
309 case TOK._Thread_local:
310 case TOK.__thread:
311 case TOK.auto_:
312 case TOK.register:
314 // function-specifiers
315 case TOK.inline:
316 case TOK._Noreturn:
318 // type-qualifiers
319 case TOK.const_:
320 case TOK.volatile:
321 case TOK.restrict:
322 case TOK.__stdcall:
324 // alignment-specifier
325 case TOK._Alignas:
327 // atomic-type-specifier or type_qualifier
328 case TOK._Atomic:
330 case TOK.__attribute__:
331 case TOK.__declspec:
333 Ldeclaration:
335 cparseDeclaration(LVL.local);
336 if (symbols.length > 1)
338 auto as = new AST.Statements();
339 as.reserve(symbols.length);
340 foreach (d; (*symbols)[])
342 s = new AST.ExpStatement(loc, d);
343 as.push(s);
345 s = new AST.CompoundDeclarationStatement(loc, as);
346 symbols.setDim(0);
348 else if (symbols.length == 1)
350 auto d = (*symbols)[0];
351 s = new AST.ExpStatement(loc, d);
352 symbols.setDim(0);
354 else
355 s = new AST.ExpStatement(loc, cast(AST.Expression)null);
356 if (flags & ParseStatementFlags.scope_)
357 s = new AST.ScopeStatement(loc, s, token.loc);
358 break;
361 case TOK._Static_assert: // _Static_assert ( constant-expression, string-literal ) ;
362 s = new AST.StaticAssertStatement(cparseStaticAssert());
363 break;
365 case TOK.leftCurly:
367 /* C11 6.8.2
368 * compound-statement:
369 * { block-item-list (opt) }
371 * block-item-list:
372 * block-item
373 * block-item-list block-item
375 * block-item:
376 * declaration
377 * statement
379 nextToken();
380 auto statements = new AST.Statements();
381 while (token.value != TOK.rightCurly && token.value != TOK.endOfFile)
383 statements.push(cparseStatement(ParseStatementFlags.curlyScope));
385 if (endPtr)
386 *endPtr = token.ptr;
387 endloc = token.loc;
388 if (pEndloc)
390 *pEndloc = token.loc;
391 pEndloc = null; // don't set it again
393 s = new AST.CompoundStatement(loc, statements);
394 if (flags & (ParseStatementFlags.scope_ | ParseStatementFlags.curlyScope))
395 s = new AST.ScopeStatement(loc, s, token.loc);
396 check(TOK.rightCurly, "compound statement");
397 break;
400 case TOK.while_:
402 nextToken();
403 check(TOK.leftParenthesis);
404 auto condition = cparseExpression();
405 check(TOK.rightParenthesis);
406 Loc endloc;
407 auto _body = cparseStatement(ParseStatementFlags.scope_, null, &endloc);
408 s = new AST.WhileStatement(loc, condition, _body, endloc, null);
409 break;
412 case TOK.semicolon:
413 /* C11 6.8.3 null statement
415 nextToken();
416 s = new AST.ExpStatement(loc, cast(AST.Expression)null);
417 break;
419 case TOK.do_:
421 nextToken();
422 auto _body = cparseStatement(ParseStatementFlags.scope_);
423 check(TOK.while_);
424 check(TOK.leftParenthesis);
425 auto condition = cparseExpression();
426 check(TOK.rightParenthesis);
427 check(TOK.semicolon, "terminating `;` required after do-while statement");
428 s = new AST.DoStatement(loc, _body, condition, token.loc);
429 break;
432 case TOK.for_:
434 AST.Statement _init;
435 AST.Expression condition;
436 AST.Expression increment;
438 nextToken();
439 check(TOK.leftParenthesis);
440 if (token.value == TOK.semicolon)
442 _init = null;
443 nextToken();
445 else
447 _init = cparseStatement(0);
449 if (token.value == TOK.semicolon)
451 condition = null;
452 nextToken();
454 else
456 condition = cparseExpression();
457 check(TOK.semicolon, "`for` condition");
459 if (token.value == TOK.rightParenthesis)
461 increment = null;
462 nextToken();
464 else
466 increment = cparseExpression();
467 check(TOK.rightParenthesis);
469 Loc endloc;
470 auto _body = cparseStatement(ParseStatementFlags.scope_, null, &endloc);
471 s = new AST.ForStatement(loc, _init, condition, increment, _body, endloc);
472 break;
475 case TOK.if_:
477 nextToken();
478 check(TOK.leftParenthesis);
479 auto condition = cparseExpression();
480 check(TOK.rightParenthesis);
481 auto ifbody = cparseStatement(ParseStatementFlags.scope_);
482 AST.Statement elsebody;
483 if (token.value == TOK.else_)
485 nextToken();
486 elsebody = cparseStatement(ParseStatementFlags.scope_);
488 else
489 elsebody = null;
490 if (condition && ifbody)
491 s = new AST.IfStatement(loc, null, condition, ifbody, elsebody, token.loc);
492 else
493 s = null; // don't propagate parsing errors
494 break;
497 case TOK.else_:
498 error("found `else` without a corresponding `if` statement");
499 goto Lerror;
501 case TOK.switch_:
503 nextToken();
504 check(TOK.leftParenthesis);
505 auto condition = cparseExpression();
506 check(TOK.rightParenthesis);
507 auto _body = cparseStatement(ParseStatementFlags.scope_);
508 s = new AST.SwitchStatement(loc, null, condition, _body, false, token.loc);
509 break;
512 case TOK.case_:
515 nextToken();
516 auto exp = cparseAssignExp();
517 AST.Expression expHigh;
518 if (token.value == TOK.dotDotDot)
520 /* Case Ranges https://gcc.gnu.org/onlinedocs/gcc/Case-Ranges.html
522 nextToken();
523 expHigh = cparseAssignExp();
525 check(TOK.colon);
527 if (flags & ParseStatementFlags.curlyScope)
529 auto statements = new AST.Statements();
530 while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
532 auto cur = cparseStatement(ParseStatementFlags.curlyScope);
533 statements.push(cur);
535 // https://issues.dlang.org/show_bug.cgi?id=21739
536 // Stop at the last break s.t. the following non-case statements are
537 // not merged into the current case. This can happen for
538 // case 1: ... break;
539 // debug { case 2: ... }
540 if (cur && cur.isBreakStatement())
541 break;
543 s = new AST.CompoundStatement(loc, statements);
545 else
547 s = cparseStatement(0);
549 s = new AST.ScopeStatement(loc, s, token.loc);
550 if (expHigh)
551 s = new AST.CaseRangeStatement(loc, exp, expHigh, s);
552 else
553 s = new AST.CaseStatement(loc, exp, s);
554 break;
557 case TOK.default_:
559 nextToken();
560 check(TOK.colon);
562 if (flags & ParseStatementFlags.curlyScope)
564 auto statements = new AST.Statements();
565 while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
567 statements.push(cparseStatement(ParseStatementFlags.curlyScope));
569 s = new AST.CompoundStatement(loc, statements);
571 else
572 s = cparseStatement(0);
573 s = new AST.ScopeStatement(loc, s, token.loc);
574 s = new AST.DefaultStatement(loc, s);
575 break;
578 case TOK.return_:
580 /* return ;
581 * return expression ;
583 nextToken();
584 auto exp = token.value == TOK.semicolon ? null : cparseExpression();
585 check(TOK.semicolon, "`return` statement");
586 s = new AST.ReturnStatement(loc, exp);
587 break;
590 case TOK.break_:
591 nextToken();
592 check(TOK.semicolon, "`break` statement");
593 s = new AST.BreakStatement(loc, null);
594 break;
596 case TOK.continue_:
597 nextToken();
598 check(TOK.semicolon, "`continue` statement");
599 s = new AST.ContinueStatement(loc, null);
600 break;
602 case TOK.goto_:
604 Identifier ident;
605 nextToken();
606 if (token.value != TOK.identifier)
608 error("identifier expected following `goto`");
609 ident = null;
611 else
613 ident = token.ident;
614 nextToken();
616 s = new AST.GotoStatement(loc, ident);
617 check(TOK.semicolon, "`goto` statement");
618 break;
621 case TOK.asm_:
622 switch (peekNext())
624 case TOK.goto_:
625 case TOK.inline:
626 case TOK.volatile:
627 case TOK.leftParenthesis:
628 s = cparseGnuAsm();
629 break;
631 default:
632 // ImportC extensions: parse as a D asm block.
633 s = parseAsm(compileEnv.masm);
634 break;
636 break;
638 default:
639 error("found `%s` instead of statement", token.toChars());
640 goto Lerror;
642 Lerror:
643 panic();
644 if (token.value == TOK.semicolon)
645 nextToken();
646 s = null;
647 break;
649 if (pEndloc)
650 *pEndloc = prevloc;
651 symbols = symbolsSave;
652 typedefTab.setDim(typedefTabLengthSave);
653 return s;
657 /*******************************************************************************/
658 /********************************* Expression Parser ***************************/
661 /**************
662 * C11 6.5.17
663 * expression:
664 * assignment-expression
665 * expression , assignment-expression
667 AST.Expression cparseExpression()
669 auto loc = token.loc;
671 //printf("cparseExpression() loc = %d\n", loc.linnum);
672 auto e = cparseAssignExp();
673 while (token.value == TOK.comma)
675 nextToken();
676 auto e2 = cparseAssignExp();
677 e = new AST.CommaExp(loc, e, e2, false);
678 loc = token.loc;
680 return e;
684 /*********************
685 * C11 6.5.1
686 * primary-expression:
687 * identifier
688 * constant
689 * string-literal
690 * ( expression )
691 * generic-selection
692 * __builtin_va_arg(assign_expression, type)
694 AST.Expression cparsePrimaryExp()
696 AST.Expression e;
697 const loc = token.loc;
699 //printf("parsePrimaryExp(): loc = %d\n", loc.linnum);
700 switch (token.value)
702 case TOK.identifier:
703 const id = token.ident.toString();
704 if (id.length > 2 && id[0] == '_' && id[1] == '_') // leading double underscore
706 if (token.ident is Id.__func__)
708 addFuncName = true; // implicitly declare __func__
710 else if (token.ident is Id.builtin_va_arg)
712 e = cparseBuiltin_va_arg();
713 break;
715 else
716 importBuiltins = true; // probably one of those compiler extensions
718 e = new AST.IdentifierExp(loc, token.ident);
719 nextToken();
720 break;
722 case TOK.charLiteral:
723 case TOK.int32Literal:
724 e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint32);
725 nextToken();
726 break;
728 case TOK.uns32Literal:
729 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns32);
730 nextToken();
731 break;
733 case TOK.int64Literal:
734 e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint64);
735 nextToken();
736 break;
738 case TOK.uns64Literal:
739 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns64);
740 nextToken();
741 break;
743 case TOK.float32Literal:
744 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat32);
745 nextToken();
746 break;
748 case TOK.float64Literal:
749 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat64);
750 nextToken();
751 break;
753 case TOK.float80Literal:
754 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat80);
755 nextToken();
756 break;
758 case TOK.imaginary32Literal:
759 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary32);
760 nextToken();
761 break;
763 case TOK.imaginary64Literal:
764 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary64);
765 nextToken();
766 break;
768 case TOK.imaginary80Literal:
769 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary80);
770 nextToken();
771 break;
773 case TOK.string_:
775 // cat adjacent strings
776 auto s = token.ustring;
777 auto len = token.len;
778 auto postfix = token.postfix;
779 while (1)
781 nextToken();
782 if (token.value == TOK.string_)
784 if (token.postfix)
786 if (token.postfix != postfix)
787 error(token.loc, "mismatched string literal postfixes `'%c'` and `'%c'`", postfix, token.postfix);
788 postfix = token.postfix;
791 const len1 = len;
792 const len2 = token.len;
793 len = len1 + len2;
794 auto s2 = cast(char*)mem.xmalloc_noscan(len * char.sizeof);
795 memcpy(s2, s, len1 * char.sizeof);
796 memcpy(s2 + len1, token.ustring, len2 * char.sizeof);
797 s = s2;
799 else
800 break;
802 e = new AST.StringExp(loc, s[0 .. len], len, 1, postfix);
803 break;
806 case TOK.leftParenthesis:
807 nextToken();
808 if (token.value == TOK.leftCurly)
809 e = cparseStatementExpression(); // gcc extension
810 else
811 e = cparseExpression();
812 check(TOK.rightParenthesis);
813 break;
815 case TOK._Generic:
816 e = cparseGenericSelection();
817 break;
819 case TOK._assert: // __check(assign-exp) extension
820 nextToken();
821 check(TOK.leftParenthesis, "`__check`");
822 e = parseAssignExp();
823 check(TOK.rightParenthesis);
824 e = new AST.AssertExp(loc, e, null);
825 break;
827 default:
828 error("expression expected, not `%s`", token.toChars());
829 // Anything for e, as long as it's not NULL
830 e = new AST.IntegerExp(loc, 0, AST.Type.tint32);
831 nextToken();
832 break;
834 return e;
837 /*********************************
838 * C11 6.5.2
839 * postfix-expression:
840 * primary-expression
841 * postfix-expression [ expression ]
842 * postfix-expression ( argument-expression-list (opt) )
843 * postfix-expression . identifier
844 * postfix-expression -> identifier
845 * postfix-expression ++
846 * postfix-expression --
847 * ( type-name ) { initializer-list }
848 * ( type-name ) { initializer-list , }
850 * argument-expression-list:
851 * assignment-expression
852 * argument-expression-list , assignment-expression
854 private AST.Expression cparsePostfixExp(AST.Expression e)
856 e = cparsePrimaryExp();
857 return cparsePostfixOperators(e);
860 /********************************
861 * C11 6.5.2
862 * Parse a series of operators for a postfix expression after already parsing
863 * a primary-expression or compound literal expression.
864 * Params:
865 * e = parsed primary or compound literal expression
866 * Returns:
867 * parsed postfix expression
869 private AST.Expression cparsePostfixOperators(AST.Expression e)
871 while (1)
873 const loc = token.loc;
874 switch (token.value)
876 case TOK.dot:
877 nextToken();
878 if (token.value == TOK.identifier)
880 Identifier id = token.ident;
881 e = new AST.DotIdExp(loc, e, id);
882 break;
884 error("identifier expected following `.`, not `%s`", token.toChars());
885 break;
887 case TOK.arrow:
888 nextToken();
889 if (token.value == TOK.identifier)
891 Identifier id = token.ident;
892 auto die = new AST.DotIdExp(loc, e, id);
893 die.arrow = true;
894 e = die;
895 break;
897 error("identifier expected following `->`, not `%s`", token.toChars());
898 break;
900 case TOK.plusPlus:
901 e = new AST.PostExp(EXP.plusPlus, loc, e);
902 break;
904 case TOK.minusMinus:
905 e = new AST.PostExp(EXP.minusMinus, loc, e);
906 break;
908 case TOK.leftParenthesis:
909 e = new AST.CallExp(loc, e, cparseArguments());
910 continue;
912 case TOK.leftBracket:
914 // array dereferences:
915 // array[index]
916 AST.Expression index;
917 auto arguments = new AST.Expressions();
919 inBrackets++;
920 nextToken();
921 index = cparseAssignExp();
922 arguments.push(index);
923 check(TOK.rightBracket);
924 inBrackets--;
925 e = new AST.ArrayExp(loc, e, arguments);
926 continue;
928 default:
929 return e;
931 nextToken();
935 /************************
936 * C11 6.5.3
937 * unary-expression:
938 * postfix-expression
939 * ++ unary-expression
940 * -- unary-expression
941 * unary-operator cast-expression
942 * sizeof unary-expression
943 * sizeof ( type-name )
944 * _Alignof ( type-name )
946 * unary-operator:
947 * & * + - ~ !
949 private AST.Expression cparseUnaryExp()
951 AST.Expression e;
952 const loc = token.loc;
954 switch (token.value)
956 case TOK.plusPlus:
957 nextToken();
958 // Parse `++` as an unary operator so that cast expressions only give
959 // an error for being non-lvalues.
960 e = cparseCastExp();
961 e = new AST.PreExp(EXP.prePlusPlus, loc, e);
962 break;
964 case TOK.minusMinus:
965 nextToken();
966 // Parse `--` as an unary operator, same as prefix increment.
967 e = cparseCastExp();
968 e = new AST.PreExp(EXP.preMinusMinus, loc, e);
969 break;
971 case TOK.and:
972 nextToken();
973 e = cparseCastExp();
974 e = new AST.AddrExp(loc, e);
975 break;
977 case TOK.mul:
978 nextToken();
979 e = cparseCastExp();
980 e = new AST.PtrExp(loc, e);
981 break;
983 case TOK.min:
984 nextToken();
985 e = cparseCastExp();
986 e = new AST.NegExp(loc, e);
987 break;
989 case TOK.add:
990 nextToken();
991 e = cparseCastExp();
992 e = new AST.UAddExp(loc, e);
993 break;
995 case TOK.not:
996 nextToken();
997 e = cparseCastExp();
998 e = new AST.NotExp(loc, e);
999 break;
1001 case TOK.tilde:
1002 nextToken();
1003 e = cparseCastExp();
1004 e = new AST.ComExp(loc, e);
1005 break;
1007 case TOK.sizeof_:
1009 nextToken();
1010 if (token.value == TOK.leftParenthesis)
1012 auto tk = peek(&token);
1013 if (isTypeName(tk))
1015 /* Expression may be either be requesting the sizeof a type-name
1016 * or a compound literal, which requires checking whether
1017 * the next token is leftCurly
1019 nextToken();
1020 auto t = cparseTypeName();
1021 check(TOK.rightParenthesis);
1022 if (token.value == TOK.leftCurly)
1024 // ( type-name ) { initializer-list }
1025 auto ci = cparseInitializer();
1026 e = new AST.CompoundLiteralExp(loc, t, ci);
1027 e = cparsePostfixOperators(e);
1029 else
1031 // ( type-name )
1032 e = new AST.TypeExp(loc, t);
1035 else
1037 // must be an expression
1038 e = cparseUnaryExp();
1041 else
1043 //C11 6.5.3
1044 e = cparseUnaryExp();
1047 e = new AST.DotIdExp(loc, e, Id.__sizeof);
1048 break;
1051 case TOK._Alignof:
1053 nextToken();
1054 check(TOK.leftParenthesis);
1055 auto t = cparseTypeName();
1056 check(TOK.rightParenthesis);
1057 e = new AST.TypeExp(loc, t);
1058 e = new AST.DotIdExp(loc, e, Id.__xalignof);
1059 break;
1062 default:
1063 e = cparsePostfixExp(e);
1064 break;
1066 assert(e);
1067 return e;
1070 /**************
1071 * C11 6.5.4
1072 * cast-expression
1073 * unary-expression
1074 * ( type-name ) cast-expression
1076 private AST.Expression cparseCastExp()
1078 if (token.value == TOK.leftParenthesis)
1080 //printf("cparseCastExp()\n");
1081 auto tk = peek(&token);
1082 bool iscast;
1083 bool isexp;
1084 if (tk.value == TOK.identifier)
1086 iscast = isTypedef(tk.ident);
1087 isexp = !iscast;
1089 if (isexp)
1091 // ( identifier ) is an expression
1092 return cparseUnaryExp();
1095 // If ( type-name )
1096 auto pt = &token;
1098 if (isCastExpression(pt))
1100 // Expression may be either a cast or a compound literal, which
1101 // requires checking whether the next token is leftCurly
1102 const loc = token.loc;
1103 nextToken();
1104 auto t = cparseTypeName();
1105 check(TOK.rightParenthesis);
1106 pt = &token;
1108 if (token.value == TOK.leftCurly)
1110 // C11 6.5.2.5 ( type-name ) { initializer-list }
1111 auto ci = cparseInitializer();
1112 auto ce = new AST.CompoundLiteralExp(loc, t, ci);
1113 return cparsePostfixOperators(ce);
1116 if (iscast)
1118 // ( type-name ) cast-expression
1119 auto ce = cparseCastExp();
1120 return new AST.CastExp(loc, ce, t);
1123 if (t.isTypeIdentifier() &&
1124 isexp &&
1125 token.value == TOK.leftParenthesis &&
1126 !isCastExpression(pt))
1128 /* (t)(...)... might be a cast expression or a function call,
1129 * with different grammars: a cast would be cparseCastExp(),
1130 * a function call would be cparsePostfixExp(CallExp(cparseArguments())).
1131 * We can't know until t is known. So, parse it as a function call
1132 * and let semantic() rewrite the AST as a CastExp if it turns out
1133 * to be a type.
1135 auto ie = new AST.IdentifierExp(loc, t.isTypeIdentifier().ident);
1136 ie.parens = true; // let semantic know it might be a CastExp
1137 AST.Expression e = new AST.CallExp(loc, ie, cparseArguments());
1138 return cparsePostfixOperators(e);
1141 // ( type-name ) cast-expression
1142 auto ce = cparseCastExp();
1143 return new AST.CastExp(loc, ce, t);
1146 return cparseUnaryExp();
1149 /**************
1150 * C11 6.5.5
1151 * multiplicative-expression
1152 * cast-expression
1153 * multiplicative-expression * cast-expression
1154 * multiplicative-expression / cast-expression
1155 * multiplicative-expression % cast-expression
1157 private AST.Expression cparseMulExp()
1159 const loc = token.loc;
1160 auto e = cparseCastExp();
1162 while (1)
1164 switch (token.value)
1166 case TOK.mul:
1167 nextToken();
1168 auto e2 = cparseCastExp();
1169 e = new AST.MulExp(loc, e, e2);
1170 continue;
1172 case TOK.div:
1173 nextToken();
1174 auto e2 = cparseCastExp();
1175 e = new AST.DivExp(loc, e, e2);
1176 continue;
1178 case TOK.mod:
1179 nextToken();
1180 auto e2 = cparseCastExp();
1181 e = new AST.ModExp(loc, e, e2);
1182 continue;
1184 default:
1185 break;
1187 break;
1189 return e;
1192 /**************
1193 * C11 6.5.6
1194 * additive-expression
1195 * multiplicative-expression
1196 * additive-expression + multiplicative-expression
1197 * additive-expression - multiplicative-expression
1199 private AST.Expression cparseAddExp()
1201 const loc = token.loc;
1202 auto e = cparseMulExp();
1204 while (1)
1206 switch (token.value)
1208 case TOK.add:
1209 nextToken();
1210 auto e2 = cparseMulExp();
1211 e = new AST.AddExp(loc, e, e2);
1212 continue;
1214 case TOK.min:
1215 nextToken();
1216 auto e2 = cparseMulExp();
1217 e = new AST.MinExp(loc, e, e2);
1218 continue;
1220 default:
1221 break;
1223 break;
1225 return e;
1228 /**************
1229 * C11 6.5.7
1230 * shift-expression
1231 * additive-expression
1232 * shift-expression << additive-expression
1233 * shift-expression >> additive-expression
1235 private AST.Expression cparseShiftExp()
1237 const loc = token.loc;
1238 auto e = cparseAddExp();
1240 while (1)
1242 switch (token.value)
1244 case TOK.leftShift:
1245 nextToken();
1246 auto e2 = cparseAddExp();
1247 e = new AST.ShlExp(loc, e, e2);
1248 continue;
1250 case TOK.rightShift:
1251 nextToken();
1252 auto e2 = cparseAddExp();
1253 e = new AST.ShrExp(loc, e, e2);
1254 continue;
1256 default:
1257 break;
1259 break;
1261 return e;
1264 /**************
1265 * C11 6.5.8
1266 * relational-expression
1267 * shift-expression
1268 * relational-expression < shift-expression
1269 * relational-expression > shift-expression
1270 * relational-expression <= shift-expression
1271 * relational-expression >= shift-expression
1273 private AST.Expression cparseRelationalExp()
1275 const loc = token.loc;
1277 auto e = cparseShiftExp();
1279 EXP op = EXP.reserved;
1280 switch (token.value)
1282 case TOK.lessThan: op = EXP.lessThan; goto Lcmp;
1283 case TOK.lessOrEqual: op = EXP.lessOrEqual; goto Lcmp;
1284 case TOK.greaterThan: op = EXP.greaterThan; goto Lcmp;
1285 case TOK.greaterOrEqual: op = EXP.greaterOrEqual; goto Lcmp;
1286 Lcmp:
1287 nextToken();
1288 auto e2 = cparseShiftExp();
1289 e = new AST.CmpExp(op, loc, e, e2);
1290 break;
1292 default:
1293 break;
1295 return e;
1298 /**************
1299 * C11 6.5.9
1300 * equality-expression
1301 * relational-expression
1302 * equality-expression == relational-expression
1303 * equality-expression != relational-expression
1305 private AST.Expression cparseEqualityExp()
1307 const loc = token.loc;
1309 auto e = cparseRelationalExp();
1311 EXP op = EXP.reserved;
1312 switch (token.value)
1314 case TOK.equal: op = EXP.equal; goto Lequal;
1315 case TOK.notEqual: op = EXP.notEqual; goto Lequal;
1316 Lequal:
1317 nextToken();
1318 auto e2 = cparseRelationalExp();
1319 e = new AST.EqualExp(op, loc, e, e2);
1320 break;
1322 default:
1323 break;
1325 return e;
1328 /**************
1329 * C11 6.5.10
1330 * AND-expression
1331 * equality-expression
1332 * AND-expression & equality-expression
1334 private AST.Expression cparseAndExp()
1336 Loc loc = token.loc;
1337 auto e = cparseEqualityExp();
1338 while (token.value == TOK.and)
1340 nextToken();
1341 auto e2 = cparseEqualityExp();
1342 e = new AST.AndExp(loc, e, e2);
1343 loc = token.loc;
1345 return e;
1348 /**************
1349 * C11 6.5.11
1350 * exclusive-OR-expression
1351 * AND-expression
1352 * exclusive-OR-expression ^ AND-expression
1354 private AST.Expression cparseXorExp()
1356 const loc = token.loc;
1358 auto e = cparseAndExp();
1359 while (token.value == TOK.xor)
1361 nextToken();
1362 auto e2 = cparseAndExp();
1363 e = new AST.XorExp(loc, e, e2);
1365 return e;
1368 /**************
1369 * C11 6.5.12
1370 * inclusive-OR-expression
1371 * exclusive-OR-expression
1372 * inclusive-OR-expression | exclusive-OR-expression
1374 private AST.Expression cparseOrExp()
1376 const loc = token.loc;
1378 auto e = cparseXorExp();
1379 while (token.value == TOK.or)
1381 nextToken();
1382 auto e2 = cparseXorExp();
1383 e = new AST.OrExp(loc, e, e2);
1385 return e;
1388 /**************
1389 * C11 6.5.13
1390 * logical-AND-expression
1391 * inclusive-OR-expression
1392 * logical-AND-expression && inclusive-OR-expression
1394 private AST.Expression cparseAndAndExp()
1396 const loc = token.loc;
1398 auto e = cparseOrExp();
1399 while (token.value == TOK.andAnd)
1401 nextToken();
1402 auto e2 = cparseOrExp();
1403 e = new AST.LogicalExp(loc, EXP.andAnd, e, e2);
1405 return e;
1408 /**************
1409 * C11 6.5.14
1410 * logical-OR-expression
1411 * logical-AND-expression
1412 * logical-OR-expression || logical-AND-expression
1414 private AST.Expression cparseOrOrExp()
1416 const loc = token.loc;
1418 auto e = cparseAndAndExp();
1419 while (token.value == TOK.orOr)
1421 nextToken();
1422 auto e2 = cparseAndAndExp();
1423 e = new AST.LogicalExp(loc, EXP.orOr, e, e2);
1425 return e;
1428 /**************
1429 * C11 6.5.15
1430 * conditional-expression:
1431 * logical-OR-expression
1432 * logical-OR-expression ? expression : conditional-expression
1434 private AST.Expression cparseCondExp()
1436 const loc = token.loc;
1438 auto e = cparseOrOrExp();
1439 if (token.value == TOK.question)
1441 nextToken();
1442 auto e1 = cparseExpression();
1443 check(TOK.colon);
1444 auto e2 = cparseCondExp();
1445 e = new AST.CondExp(loc, e, e1, e2);
1447 return e;
1450 /**************
1451 * C11 6.5.16
1452 * assignment-expression:
1453 * conditional-expression
1454 * unary-expression assignment-operator assignment-expression
1456 * assignment-operator:
1457 * = *= /= %= += -= <<= >>= &= ^= |=
1459 AST.Expression cparseAssignExp()
1461 AST.Expression e;
1462 e = cparseCondExp(); // constrain it to being unary-expression in semantic pass
1463 if (e is null)
1464 return e;
1466 const loc = token.loc;
1467 switch (token.value)
1469 case TOK.assign:
1470 nextToken();
1471 auto e2 = cparseAssignExp();
1472 e = new AST.AssignExp(loc, e, e2);
1473 break;
1475 case TOK.addAssign:
1476 nextToken();
1477 auto e2 = cparseAssignExp();
1478 e = new AST.AddAssignExp(loc, e, e2);
1479 break;
1481 case TOK.minAssign:
1482 nextToken();
1483 auto e2 = cparseAssignExp();
1484 e = new AST.MinAssignExp(loc, e, e2);
1485 break;
1487 case TOK.mulAssign:
1488 nextToken();
1489 auto e2 = cparseAssignExp();
1490 e = new AST.MulAssignExp(loc, e, e2);
1491 break;
1493 case TOK.divAssign:
1494 nextToken();
1495 auto e2 = cparseAssignExp();
1496 e = new AST.DivAssignExp(loc, e, e2);
1497 break;
1499 case TOK.modAssign:
1500 nextToken();
1501 auto e2 = cparseAssignExp();
1502 e = new AST.ModAssignExp(loc, e, e2);
1503 break;
1505 case TOK.andAssign:
1506 nextToken();
1507 auto e2 = cparseAssignExp();
1508 e = new AST.AndAssignExp(loc, e, e2);
1509 break;
1511 case TOK.orAssign:
1512 nextToken();
1513 auto e2 = cparseAssignExp();
1514 e = new AST.OrAssignExp(loc, e, e2);
1515 break;
1517 case TOK.xorAssign:
1518 nextToken();
1519 auto e2 = cparseAssignExp();
1520 e = new AST.XorAssignExp(loc, e, e2);
1521 break;
1523 case TOK.leftShiftAssign:
1524 nextToken();
1525 auto e2 = cparseAssignExp();
1526 e = new AST.ShlAssignExp(loc, e, e2);
1527 break;
1529 case TOK.rightShiftAssign:
1530 nextToken();
1531 auto e2 = cparseAssignExp();
1532 e = new AST.ShrAssignExp(loc, e, e2);
1533 break;
1535 default:
1536 break;
1539 return e;
1542 /***********************
1543 * C11 6.5.1.1
1544 * _Generic ( assignment-expression, generic-assoc-list )
1546 * generic-assoc-list:
1547 * generic-association
1548 * generic-assoc-list generic-association
1550 * generic-association:
1551 * type-name : assignment-expression
1552 * default : assignment-expression
1554 private AST.Expression cparseGenericSelection()
1556 const loc = token.loc;
1557 nextToken();
1558 check(TOK.leftParenthesis);
1559 auto cntlExp = cparseAssignExp();
1560 check(TOK.comma);
1561 auto types = new AST.Types();
1562 auto exps = new AST.Expressions();
1563 bool sawDefault;
1564 while (1)
1566 AST.Type t;
1567 if (token.value == TOK.default_)
1569 nextToken();
1570 if (sawDefault)
1571 error("only one `default` allowed in generic-assoc-list");
1572 sawDefault = true;
1573 t = null;
1575 else
1576 t = cparseTypeName();
1577 types.push(t);
1579 check(TOK.colon);
1580 auto e = cparseAssignExp();
1581 exps.push(e);
1582 if (token.value == TOK.rightParenthesis || token.value == TOK.endOfFile)
1583 break;
1584 check(TOK.comma);
1586 check(TOK.rightParenthesis);
1587 return new AST.GenericExp(loc, cntlExp, types, exps);
1590 /***********************
1591 * C11 6.6 Constant expressions
1592 * constant-expression:
1593 * conditional-expression
1595 private AST.Expression cparseConstantExp()
1597 return cparseAssignExp();
1600 /*****************************
1601 * gcc extension:
1602 * type __builtin_va_arg(assign-expression, type)
1603 * Rewrite as `va_arg` template from `core.stdc.stdarg`:
1604 * va_arg!(type)(assign-expression);
1605 * Lexer is on `__builtin_va_arg`
1607 private AST.Expression cparseBuiltin_va_arg()
1609 importBuiltins = true; // need core.stdc.stdarg
1611 nextToken();
1612 check(TOK.leftParenthesis);
1614 auto arguments = new AST.Expressions();
1615 auto arg = cparseAssignExp();
1616 arguments.push(arg);
1618 check(TOK.comma);
1620 auto t = cparseTypeName();
1621 auto tiargs = new AST.Objects();
1622 tiargs.push(t);
1624 const loc = loc;
1625 auto ti = new AST.TemplateInstance(loc, Id.va_arg, tiargs);
1626 auto tie = new AST.ScopeExp(loc, ti);
1628 AST.Expression e = new AST.CallExp(loc, tie, arguments);
1630 check(TOK.rightParenthesis);
1631 return e;
1634 /*****************************
1635 * gcc extension: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
1636 * Represent as a function literal, then call the function literal.
1637 * Parser is on opening curly brace.
1639 private AST.Expression cparseStatementExpression()
1641 AST.ParameterList parameterList;
1642 StorageClass stc = 0;
1643 const loc = token.loc;
1644 typedefTab.push(null);
1645 auto fbody = cparseStatement(ParseStatementFlags.scope_);
1646 typedefTab.pop(); // end of function scope
1648 // Rewrite last ExpStatement (if there is one) as a ReturnStatement
1649 auto ss = fbody.isScopeStatement();
1650 auto cs = ss.statement.isCompoundStatement();
1651 assert(cs);
1652 if (const len = (*cs.statements).length)
1654 auto s = (*cs.statements)[len - 1];
1655 if (auto es = s.isExpStatement())
1656 (*cs.statements)[len - 1] = new AST.ReturnStatement(es.loc, es.exp);
1659 auto tf = new AST.TypeFunction(parameterList, null, LINK.d, stc);
1660 auto fd = new AST.FuncLiteralDeclaration(loc, token.loc, tf, TOK.delegate_, null, null, 0);
1661 fd.fbody = fbody;
1663 auto fe = new AST.FuncExp(loc, fd);
1664 auto args = new AST.Expressions();
1665 auto e = new AST.CallExp(loc, fe, args); // call the function literal
1666 return e;
1670 /********************************************************************************/
1671 /********************************* Declaration Parser ***************************/
1674 /*************************************
1675 * C11 6.7
1676 * declaration:
1677 * declaration-specifiers init-declarator-list (opt) ;
1678 * static_assert-declaration
1680 * init-declarator-list:
1681 * init-declarator
1682 * init-declarator-list , init-declarator
1684 * init-declarator:
1685 * declarator
1686 * declarator = initializer
1688 * Params:
1689 * level = declaration context
1691 void cparseDeclaration(LVL level)
1693 //printf("cparseDeclaration(level = %d)\n", level);
1694 if (token.value == TOK._Static_assert)
1696 auto s = cparseStaticAssert();
1697 symbols.push(s);
1698 return;
1701 if (token.value == TOK.__pragma)
1703 uupragmaDirective(scanloc);
1704 return;
1707 if (token.value == TOK._import) // import declaration extension
1709 auto a = parseImport();
1710 if (a && a.length)
1711 symbols.append(a);
1712 return;
1715 const typedefTabLengthSave = typedefTab.length;
1716 auto symbolsSave = symbols;
1717 Specifier specifier;
1718 specifier.packalign = this.packalign;
1719 auto tspec = cparseDeclarationSpecifiers(level, specifier);
1721 AST.Dsymbol declareTag(AST.TypeTag tt, ref Specifier specifier)
1723 /* `struct tag;` and `struct tag { ... };`
1724 * always result in a declaration in the current scope
1726 auto stag = (tt.tok == TOK.struct_) ? new AST.StructDeclaration(tt.loc, tt.id, false) :
1727 (tt.tok == TOK.union_) ? new AST.UnionDeclaration(tt.loc, tt.id) :
1728 new AST.EnumDeclaration(tt.loc, tt.id, tt.base);
1729 if (!tt.packalign.isUnknown())
1731 // saw `struct __declspec(align(N)) Tag ...`
1732 auto st = stag.isStructDeclaration();
1733 st.alignment = tt.packalign;
1735 stag.members = tt.members;
1736 tt.members = null;
1737 if (!symbols)
1738 symbols = new AST.Dsymbols();
1739 auto stags = applySpecifier(stag, specifier);
1740 symbols.push(stags);
1741 return stags;
1744 /* If a declarator does not follow, it is unnamed
1746 if (token.value == TOK.semicolon)
1748 if (!tspec)
1750 nextToken();
1751 return; // accept empty declaration as an extension
1754 if (auto ti = tspec.isTypeIdentifier())
1756 // C11 6.7.2-2
1757 error("type-specifier missing for declaration of `%s`", ti.ident.toChars());
1758 nextToken();
1759 return;
1762 nextToken();
1763 auto tt = tspec.isTypeTag();
1764 if (!tt ||
1765 !tt.id && (tt.tok == TOK.struct_ || tt.tok == TOK.union_))
1766 return; // legal but meaningless empty declaration, ignore it
1768 auto stags = declareTag(tt, specifier);
1770 if (0 && tt.tok == TOK.enum_) // C11 proscribes enums with no members, but we allow it
1772 if (!tt.members)
1773 error(tt.loc, "`enum %s` has no members", stags.toChars());
1775 return;
1778 if (!tspec)
1780 error("no type for declarator before `%s`", token.toChars());
1781 panic();
1782 nextToken();
1783 return;
1786 if (tspec && specifier.mod & MOD.xconst)
1788 tspec = toConst(tspec);
1789 specifier.mod &= ~MOD.xnone; // 'used' it
1792 void scanPastSemicolon()
1794 while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
1795 nextToken();
1796 nextToken();
1799 if (token.value == TOK.assign && tspec && tspec.isTypeIdentifier())
1801 /* C11 6.7.2-2
1802 * Special check for `const b = 1;` because some compilers allow it
1804 error("type-specifier omitted for declaration of `%s`", tspec.isTypeIdentifier().ident.toChars());
1805 return scanPastSemicolon();
1808 bool first = true;
1809 while (1)
1811 Identifier id;
1812 AST.StringExp asmName;
1813 auto dt = cparseDeclarator(DTR.xdirect, tspec, id, specifier);
1814 if (!dt)
1816 panic();
1817 nextToken();
1818 break; // error recovery
1821 /* GNU Extensions
1822 * init-declarator:
1823 * declarator simple-asm-expr (opt) gnu-attributes (opt)
1824 * declarator simple-asm-expr (opt) gnu-attributes (opt) = initializer
1826 switch (token.value)
1828 case TOK.assign:
1829 case TOK.comma:
1830 case TOK.semicolon:
1831 case TOK.asm_:
1832 case TOK.__attribute__:
1833 if (token.value == TOK.asm_)
1834 asmName = cparseGnuAsmLabel();
1835 if (token.value == TOK.__attribute__)
1837 cparseGnuAttributes(specifier);
1838 if (token.value == TOK.leftCurly)
1839 break; // function definition
1841 /* This is a data definition, there cannot now be a
1842 * function definition.
1844 first = false;
1845 break;
1847 default:
1848 break;
1851 if (specifier.alignExps && dt.isTypeFunction())
1852 error("no alignment-specifier for function declaration"); // C11 6.7.5-2
1853 if (specifier.alignExps && specifier.scw == SCW.xregister)
1854 error("no alignment-specifier for `register` storage class"); // C11 6.7.5-2
1856 /* C11 6.9.1 Function Definitions
1857 * function-definition:
1858 * declaration-specifiers declarator declaration-list (opt) compound-statement
1860 * declaration-list:
1861 * declaration
1862 * declaration-list declaration
1864 auto t = &token;
1865 if (first && // first declarator
1866 id &&
1867 dt.isTypeFunction() && // function type not inherited from a typedef
1868 isDeclarationList(t) && // optional declaration-list
1869 level == LVL.global && // function definitions only at global scope
1870 t.value == TOK.leftCurly) // start of compound-statement
1872 auto s = cparseFunctionDefinition(id, dt.isTypeFunction(), specifier);
1873 typedefTab.setDim(typedefTabLengthSave);
1874 symbols = symbolsSave;
1875 symbols.push(s);
1876 return;
1878 AST.Dsymbol s = null;
1879 typedefTab.setDim(typedefTabLengthSave);
1880 symbols = symbolsSave;
1881 if (!symbols)
1882 symbols = new AST.Dsymbols; // lazilly create it
1884 if (level != LVL.global && !tspec && !specifier.scw && !specifier.mod)
1885 error("declaration-specifier-seq required");
1886 else if (specifier.scw == SCW.xtypedef)
1888 if (token.value == TOK.assign)
1889 error("no initializer for typedef declaration");
1890 if (specifier.alignExps)
1891 error("no alignment-specifier for typedef declaration"); // C11 6.7.5-2
1893 bool isalias = true;
1894 if (auto ts = dt.isTypeStruct())
1896 if (ts.sym.isAnonymous())
1898 // This is a typedef for an anonymous struct-or-union.
1899 // Directly set the ident for the struct-or-union.
1900 ts.sym.ident = id;
1901 isalias = false;
1904 else if (auto te = dt.isTypeEnum())
1906 if (te.sym.isAnonymous())
1908 // This is a typedef for an anonymous enum.
1909 te.sym.ident = id;
1910 isalias = false;
1913 else if (auto tt = dt.isTypeTag())
1915 if (tt.id || tt.tok == TOK.enum_)
1917 if (!tt.id && id)
1918 /* This applies for enums declared as
1919 * typedef enum {A} E;
1921 tt.id = id;
1922 Specifier spec;
1923 declareTag(tt, spec);
1926 if (isalias)
1928 auto ad = new AST.AliasDeclaration(token.loc, id, dt);
1929 ad.adFlags |= ad.hidden; // do not print when generating .di files
1930 s = ad;
1933 insertTypedefToTypedefTab(id, dt); // remember typedefs
1935 else if (id)
1937 if (auto tt = dt.isTypeTag())
1939 if (tt.members && (tt.id || tt.tok == TOK.enum_))
1941 Specifier spec;
1942 declareTag(tt, spec);
1946 if (level == LVL.prototype)
1947 break; // declared later as Parameter, not VarDeclaration
1949 if (dt.ty == AST.Tvoid)
1950 error("`void` has no value");
1952 AST.Initializer initializer;
1953 bool hasInitializer;
1954 if (token.value == TOK.assign)
1956 nextToken();
1957 hasInitializer = true;
1958 initializer = cparseInitializer();
1960 // declare the symbol
1961 assert(id);
1963 if (isFunctionTypedef(dt))
1965 if (hasInitializer)
1966 error("no initializer for function declaration");
1967 if (specifier.scw & SCW.x_Thread_local)
1968 error("functions cannot be `_Thread_local`"); // C11 6.7.1-4
1969 StorageClass stc = specifiersToSTC(level, specifier);
1970 stc &= ~STC.gshared; // no gshared functions
1971 auto fd = new AST.FuncDeclaration(token.loc, Loc.initial, id, stc, dt, specifier.noreturn);
1972 specifiersToFuncDeclaration(fd, specifier);
1973 s = fd;
1975 else
1977 // Give non-extern variables an implicit void initializer
1978 // if one has not been explicitly set.
1979 if (!hasInitializer &&
1980 !(specifier.scw & (SCW.xextern | SCW.xstatic | SCW.x_Thread_local) || level == LVL.global))
1981 initializer = new AST.VoidInitializer(token.loc);
1982 auto vd = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(level, specifier));
1983 specifiersToVarDeclaration(vd, specifier);
1984 s = vd;
1986 if (level != LVL.global)
1987 insertIdToTypedefTab(id); // non-typedef declarations can hide typedefs in outer scopes
1989 if (s !is null)
1991 // Saw `asm("name")` in the function, type, or variable definition.
1992 // This is equivalent to `pragma(mangle, "name")` in D
1993 if (asmName)
1996 https://issues.dlang.org/show_bug.cgi?id=23012
1997 Ideally this would be translated to a pragma(mangle)
1998 decl. This is not possible because ImportC symbols are
1999 (currently) merged before semantic analysis is performed,
2000 so the pragma(mangle) never effects any change on the declarations
2001 it pertains too.
2003 Writing to mangleOverride directly avoids this, and is possible
2004 because C only a StringExp is allowed unlike a full fat pragma(mangle)
2005 which is more liberal.
2007 if (auto p = s.isDeclaration())
2009 auto str = asmName.peekString();
2010 p.mangleOverride = str;
2011 // p.adFlags |= AST.VarDeclaration.nounderscore;
2012 p.adFlags |= 4; // cannot get above line to compile on Ubuntu
2015 s = applySpecifier(s, specifier);
2016 if (level == LVL.local)
2018 // Wrap the declaration in `extern (C) { declaration }`
2019 // Necessary for function pointers, but harmless to apply to all.
2020 auto decls = new AST.Dsymbols(1);
2021 (*decls)[0] = s;
2022 s = new AST.LinkDeclaration(s.loc, linkage, decls);
2024 symbols.push(s);
2026 if (level == LVL.global && !id)
2027 error("expected identifier for declaration");
2029 first = false;
2031 switch (token.value)
2033 case TOK.identifier:
2034 if (s)
2036 error(token.loc, "missing comma or semicolon after declaration of `%s`, found `%s` instead", s.toChars(), token.toChars());
2037 goto Lend;
2039 goto default;
2041 case TOK.semicolon:
2042 nextToken();
2043 return;
2045 case TOK.comma:
2046 if (!symbolsSave)
2047 symbolsSave = symbols;
2048 nextToken();
2049 break;
2051 default:
2052 error("`=`, `;` or `,` expected to end declaration instead of `%s`", token.toChars());
2053 Lend:
2054 return scanPastSemicolon();
2059 /***************************************
2060 * C11 Function Definitions
2061 * function-definition
2062 * declaration-specifiers declarator declaration-list (opt) compound-statement
2064 * declaration-list:
2065 * declaration
2066 * declaration-list declaration
2068 * It's already been parsed up to the declaration-list (opt).
2069 * Pick it up from there.
2070 * Params:
2071 * id = function identifier
2072 * ft = function type
2073 * specifier = function specifiers
2074 * Returns:
2075 * Dsymbol for the function
2077 AST.Dsymbol cparseFunctionDefinition(Identifier id, AST.TypeFunction ft, ref Specifier specifier)
2079 /* Start function scope
2081 typedefTab.push(null);
2083 if (token.value != TOK.leftCurly) // if not start of a compound-statement
2085 // Do declaration-list
2088 cparseDeclaration(LVL.parameter);
2089 } while (token.value != TOK.leftCurly);
2091 /* Since there were declarations, the parameter-list must have been
2092 * an identifier-list.
2094 ft.parameterList.hasIdentifierList = true; // semantic needs to know to adjust parameter types
2095 auto pl = ft.parameterList;
2096 if (pl.varargs != AST.VarArg.none && pl.length)
2097 error("function identifier-list cannot end with `...`");
2098 ft.parameterList.varargs = AST.VarArg.KRvariadic; // but C11 allows extra arguments
2099 auto plLength = pl.length;
2100 if (symbols.length != plLength)
2101 error(token.loc, "%d identifiers does not match %d declarations", cast(int)plLength, cast(int)symbols.length);
2103 /* Transfer the types and storage classes from symbols[] to pl[]
2105 foreach (i; 0 .. plLength)
2107 auto p = pl[i]; // yes, quadratic
2109 // Convert typedef-identifier to identifier
2110 if (p.type)
2112 if (auto t = p.type.isTypeIdentifier())
2114 p.ident = t.ident;
2115 p.type = null;
2119 if (p.type || !(p.storageClass & STC.parameter))
2120 error("storage class and type are not allowed in identifier-list");
2121 foreach (s; (*symbols)[]) // yes, quadratic
2123 auto ad = s.isAttribDeclaration();
2124 if (ad)
2125 s = (*ad.decl)[0]; // AlignDeclaration wrapping the declaration
2127 auto d = s.isDeclaration();
2128 if (d && p.ident == d.ident && d.type)
2130 p.type = d.type;
2131 p.storageClass = d.storage_class;
2132 d.type = null; // don't reuse
2133 break;
2136 if (!p.type)
2138 error("no declaration for identifier `%s`", p.ident.toChars());
2139 p.type = AST.Type.terror;
2144 addFuncName = false; // gets set to true if somebody references __func__ in this function
2145 const locFunc = token.loc;
2147 auto body = cparseStatement(ParseStatementFlags.curly); // don't start a new scope; continue with parameter scope
2148 typedefTab.pop(); // end of function scope
2150 StorageClass stc = specifiersToSTC(LVL.global, specifier);
2151 stc &= ~STC.gshared; // no gshared functions
2152 auto fd = new AST.FuncDeclaration(locFunc, prevloc, id, stc, ft, specifier.noreturn);
2153 specifiersToFuncDeclaration(fd, specifier);
2155 if (addFuncName)
2157 auto s = createFuncName(locFunc, id);
2158 body = new AST.CompoundStatement(locFunc, s, body);
2160 fd.fbody = body;
2162 // TODO add `symbols` to the function's local symbol table `sc2` in FuncDeclaration::semantic3()
2164 return fd;
2167 /***************************************
2168 * C11 Initialization
2169 * initializer:
2170 * assignment-expression
2171 * { } // C23 6.7.10 addition
2172 * { initializer-list }
2173 * { initializer-list , }
2175 * initializer-list:
2176 * designation (opt) initializer
2177 * initializer-list , designation (opt) initializer
2179 * designation:
2180 * designator-list =
2182 * designator-list:
2183 * designator
2184 * designator-list designator
2186 * designator:
2187 * [ constant-expression ]
2188 * . identifier
2189 * Returns:
2190 * initializer
2192 AST.Initializer cparseInitializer()
2194 if (token.value != TOK.leftCurly)
2196 auto ae = cparseAssignExp(); // assignment-expression
2197 return new AST.ExpInitializer(token.loc, ae);
2199 nextToken();
2200 const loc = token.loc;
2202 if (token.value == TOK.rightCurly) // { }
2204 nextToken();
2205 return new AST.DefaultInitializer(loc);
2208 /* Collect one or more `designation (opt) initializer`
2209 * into ci.initializerList, but lazily create ci
2211 AST.CInitializer ci;
2212 while (1)
2214 /* There can be 0 or more designators preceding an initializer.
2215 * Collect them in desigInit
2217 AST.DesigInit desigInit;
2218 while (1)
2220 if (token.value == TOK.leftBracket) // [ constant-expression ]
2222 nextToken();
2223 auto e = cparseConstantExp();
2224 check(TOK.rightBracket);
2225 if (!desigInit.designatorList)
2226 desigInit.designatorList = new AST.Designators;
2227 desigInit.designatorList.push(AST.Designator(e));
2229 else if (token.value == TOK.dot) // . identifier
2231 nextToken();
2232 if (token.value != TOK.identifier)
2234 error("identifier expected following `.` designator");
2235 break;
2237 if (!desigInit.designatorList)
2238 desigInit.designatorList = new AST.Designators;
2239 desigInit.designatorList.push(AST.Designator(token.ident));
2240 nextToken();
2242 else
2244 if (desigInit.designatorList)
2245 check(TOK.assign);
2246 break;
2250 desigInit.initializer = cparseInitializer();
2251 if (!ci)
2252 ci = new AST.CInitializer(loc);
2253 ci.initializerList.push(desigInit);
2254 if (token.value == TOK.comma)
2256 nextToken();
2257 if (token.value != TOK.rightCurly)
2258 continue;
2260 break;
2262 check(TOK.rightCurly);
2263 //printf("ci: %s\n", ci.toChars());
2264 return ci;
2267 /*************************************
2268 * C11 6.7
2269 * declaration-specifier:
2270 * storage-class-specifier declaration-specifiers (opt)
2271 * type-specifier declaration-specifiers (opt)
2272 * type-qualifier declaration-specifiers (opt)
2273 * function-specifier declaration-specifiers (opt)
2274 * alignment-specifier declaration-specifiers (opt)
2275 * Params:
2276 * level = declaration context
2277 * specifier = specifiers in and out
2278 * Returns:
2279 * resulting type, null if not specified
2281 private AST.Type cparseDeclarationSpecifiers(LVL level, ref Specifier specifier)
2283 enum TKW : uint
2285 xnone = 0,
2286 xchar = 1,
2287 xsigned = 2,
2288 xunsigned = 4,
2289 xshort = 8,
2290 xint = 0x10,
2291 xlong = 0x20,
2292 xllong = 0x40,
2293 xfloat = 0x80,
2294 xdouble = 0x100,
2295 xldouble = 0x200,
2296 xtag = 0x400,
2297 xident = 0x800,
2298 xvoid = 0x1000,
2299 xbool = 0x4000,
2300 ximaginary = 0x8000,
2301 xcomplex = 0x10000,
2302 x_Atomic = 0x20000,
2303 xint128 = 0x40000,
2306 AST.Type t;
2307 Loc loc;
2308 //printf("parseDeclarationSpecifiers()\n");
2310 TKW tkw;
2311 SCW scw = specifier.scw & SCW.xtypedef;
2312 MOD mod;
2313 Identifier id;
2314 Identifier previd;
2316 Lwhile:
2317 while (1)
2319 //printf("token %s\n", token.toChars());
2320 TKW tkwx;
2321 SCW scwx;
2322 MOD modx;
2323 switch (token.value)
2325 // Storage class specifiers
2326 case TOK.static_: scwx = SCW.xstatic; break;
2327 case TOK.extern_: scwx = SCW.xextern; break;
2328 case TOK.auto_: scwx = SCW.xauto; break;
2329 case TOK.register: scwx = SCW.xregister; break;
2330 case TOK.typedef_: scwx = SCW.xtypedef; break;
2331 case TOK.inline: scwx = SCW.xinline; break;
2332 case TOK._Noreturn: scwx = SCW.x_Noreturn; break;
2333 case TOK.__thread:
2334 case TOK._Thread_local: scwx = SCW.x_Thread_local; break;
2336 // Type qualifiers
2337 case TOK.const_: modx = MOD.xconst; break;
2338 case TOK.volatile: modx = MOD.xvolatile; break;
2339 case TOK.restrict: modx = MOD.xrestrict; break;
2340 case TOK.__stdcall: modx = MOD.x__stdcall; break;
2342 // Type specifiers
2343 case TOK.char_: tkwx = TKW.xchar; break;
2344 case TOK.signed: tkwx = TKW.xsigned; break;
2345 case TOK.unsigned: tkwx = TKW.xunsigned; break;
2346 case TOK.int16: tkwx = TKW.xshort; break;
2347 case TOK.int32: tkwx = TKW.xint; break;
2348 case TOK.int64: tkwx = TKW.xlong; break;
2349 case TOK.__int128: tkwx = TKW.xint128; break;
2350 case TOK.float32: tkwx = TKW.xfloat; break;
2351 case TOK.float64: tkwx = TKW.xdouble; break;
2352 case TOK.void_: tkwx = TKW.xvoid; break;
2353 case TOK._Bool: tkwx = TKW.xbool; break;
2354 case TOK._Imaginary: tkwx = TKW.ximaginary; break;
2355 case TOK._Complex: tkwx = TKW.xcomplex; break;
2357 case TOK.identifier:
2358 tkwx = TKW.xident;
2359 id = token.ident;
2360 break;
2362 case TOK.struct_:
2363 case TOK.union_:
2365 const structOrUnion = token.value;
2366 const sloc = token.loc;
2367 nextToken();
2369 Specifier tagSpecifier;
2371 /* GNU Extensions
2372 * struct-or-union-specifier:
2373 * struct-or-union gnu-attributes (opt) identifier (opt) { struct-declaration-list } gnu-attributes (opt)
2374 * struct-or-union gnu-attribute (opt) identifier
2376 while (1)
2378 if (token.value == TOK.__attribute__)
2379 cparseGnuAttributes(tagSpecifier);
2380 else if (token.value == TOK.__declspec)
2381 cparseDeclspec(tagSpecifier);
2382 else if (token.value == TOK.__pragma)
2383 uupragmaDirective(sloc);
2384 else
2385 break;
2387 t = cparseStruct(sloc, structOrUnion, tagSpecifier.packalign, symbols);
2388 tkwx = TKW.xtag;
2389 break;
2392 case TOK.enum_:
2393 t = cparseEnum(symbols);
2394 tkwx = TKW.xtag;
2395 break;
2397 case TOK._Atomic:
2399 // C11 6.7.2.4
2400 // type-specifier if followed by `( type-name )`
2401 auto tk = peek(&token);
2402 if (tk.value == TOK.leftParenthesis)
2404 tk = peek(tk);
2405 if (isTypeName(tk) && tk.value == TOK.rightParenthesis)
2407 nextToken();
2408 nextToken();
2409 t = cparseTypeName();
2410 tkwx = TKW.x_Atomic;
2411 break;
2414 // C11 6.7.3 type-qualifier if not
2415 modx = MOD.x_Atomic;
2416 break;
2419 case TOK._Alignas:
2421 /* C11 6.7.5
2422 * _Alignas ( type-name )
2423 * _Alignas ( constant-expression )
2426 if (level & (LVL.parameter | LVL.prototype))
2427 error("no alignment-specifier for parameters"); // C11 6.7.5-2
2429 nextToken();
2430 check(TOK.leftParenthesis);
2431 AST.Expression exp;
2432 auto tk = &token;
2433 if (isTypeName(tk)) // _Alignas ( type-name )
2435 auto talign = cparseTypeName();
2436 /* Convert type to expression: `talign.alignof`
2438 auto e = new AST.TypeExp(loc, talign);
2439 exp = new AST.DotIdExp(loc, e, Id.__xalignof);
2441 else // _Alignas ( constant-expression )
2443 exp = cparseConstantExp();
2446 if (!specifier.alignExps)
2447 specifier.alignExps = new AST.Expressions(0);
2448 specifier.alignExps.push(exp);
2450 check(TOK.rightParenthesis);
2451 break;
2454 case TOK.__attribute__:
2456 /* GNU Extensions
2457 * declaration-specifiers:
2458 * gnu-attributes declaration-specifiers (opt)
2460 cparseGnuAttributes(specifier);
2461 break;
2464 case TOK.__declspec:
2466 /* Microsoft extension
2468 cparseDeclspec(specifier);
2469 break;
2472 case TOK.typeof_:
2474 nextToken();
2475 check(TOK.leftParenthesis);
2477 auto tk = &token;
2478 AST.Expression e;
2479 if (isTypeName(tk))
2480 e = new AST.TypeExp(loc, cparseTypeName());
2481 else
2482 e = cparseExpression();
2483 t = new AST.TypeTypeof(loc, e);
2485 if(token.value == TOK.rightParenthesis)
2486 nextToken();
2487 else
2489 t = AST.Type.terror;
2490 error("`typeof` operator expects an expression or type name in parentheses");
2492 // skipParens et. al expect to be on the opening parenthesis
2493 int parens;
2494 loop: while(1)
2496 switch(token.value)
2498 case TOK.leftParenthesis:
2499 parens++;
2500 break;
2501 case TOK.rightParenthesis:
2502 parens--;
2503 if(parens < 0)
2504 goto case;
2505 break;
2506 case TOK.endOfFile:
2507 break loop;
2508 default:
2510 nextToken();
2514 tkwx = TKW.xtag;
2515 break;
2518 default:
2519 break Lwhile;
2522 if (tkwx)
2524 if (tkw & TKW.xlong && tkwx & TKW.xlong)
2526 tkw &= ~TKW.xlong;
2527 tkwx = TKW.xllong;
2529 if (tkw && tkwx & TKW.xident)
2531 // 2nd identifier can't be a typedef
2532 break Lwhile; // leave parser on the identifier for the following declarator
2534 else if (tkwx & TKW.xident)
2536 // 1st identifier, save it for TypeIdentifier
2537 previd = id;
2539 if (tkw & TKW.xident && tkwx || // typedef-name followed by type-specifier
2540 tkw & tkwx) // duplicate type-specifiers
2542 error("illegal combination of type specifiers");
2543 tkwx = TKW.init;
2545 tkw |= tkwx;
2546 if (!(tkwx & TKW.xtag)) // if parser already advanced
2547 nextToken();
2548 continue;
2551 if (modx)
2553 mod |= modx;
2554 nextToken();
2555 continue;
2558 if (scwx)
2560 if (scw & scwx)
2561 error("duplicate storage class");
2562 scw |= scwx;
2563 // C11 6.7.1-2 At most one storage-class may be given, except that
2564 // _Thread_local may appear with static or extern.
2565 const scw2 = scw & (SCW.xstatic | SCW.xextern | SCW.xauto | SCW.xregister | SCW.xtypedef);
2566 if (scw2 & (scw2 - 1) ||
2567 scw & (SCW.x_Thread_local) && scw & (SCW.xauto | SCW.xregister | SCW.xtypedef))
2569 error("multiple storage classes in declaration specifiers");
2570 scw &= ~scwx;
2572 if (level == LVL.local &&
2573 scw & (SCW.x_Thread_local) && scw & (SCW.xinline | SCW.x_Noreturn))
2575 error("`inline` and `_Noreturn` function specifiers not allowed for `_Thread_local`");
2576 scw &= ~scwx;
2578 if (level == LVL.local &&
2579 scw & (SCW.x_Thread_local) && !(scw & (SCW.xstatic | SCW.xextern)))
2581 error("`_Thread_local` in block scope must be accompanied with `static` or `extern`"); // C11 6.7.1-3
2582 scw &= ~scwx;
2584 if (level & (LVL.parameter | LVL.prototype) &&
2585 scw & ~SCW.xregister)
2587 error("only `register` storage class allowed for function parameters");
2588 scw &= ~scwx;
2590 if (level == LVL.global &&
2591 scw & (SCW.xauto | SCW.xregister))
2593 error("`auto` and `register` storage class not allowed for global");
2594 scw &= ~scwx;
2596 nextToken();
2597 continue;
2601 specifier.scw = scw;
2602 specifier.mod = mod;
2604 // Convert TKW bits to type t
2605 switch (tkw)
2607 case TKW.xnone: t = null; break;
2609 case TKW.xchar: t = AST.Type.tchar; break;
2610 case TKW.xsigned | TKW.xchar: t = AST.Type.tint8; break;
2611 case TKW.xunsigned | TKW.xchar: t = AST.Type.tuns8; break;
2613 case TKW.xshort:
2614 case TKW.xsigned | TKW.xshort:
2615 case TKW.xsigned | TKW.xshort | TKW.xint:
2616 case TKW.xshort | TKW.xint: t = integerTypeForSize(shortsize); break;
2618 case TKW.xunsigned | TKW.xshort | TKW.xint:
2619 case TKW.xunsigned | TKW.xshort: t = unsignedTypeForSize(shortsize); break;
2621 case TKW.xint:
2622 case TKW.xsigned:
2623 case TKW.xsigned | TKW.xint: t = integerTypeForSize(intsize); break;
2625 case TKW.xunsigned:
2626 case TKW.xunsigned | TKW.xint: t = unsignedTypeForSize(intsize); break;
2628 case TKW.xlong:
2629 case TKW.xsigned | TKW.xlong:
2630 case TKW.xsigned | TKW.xlong | TKW.xint:
2631 case TKW.xlong | TKW.xint: t = integerTypeForSize(longsize); break;
2633 case TKW.xunsigned | TKW.xlong | TKW.xint:
2634 case TKW.xunsigned | TKW.xlong: t = unsignedTypeForSize(longsize); break;
2636 case TKW.xllong:
2637 case TKW.xsigned | TKW.xllong:
2638 case TKW.xsigned | TKW.xllong | TKW.xint:
2639 case TKW.xllong | TKW.xint: t = integerTypeForSize(long_longsize); break;
2641 case TKW.xunsigned | TKW.xllong | TKW.xint:
2642 case TKW.xunsigned | TKW.xllong: t = unsignedTypeForSize(long_longsize); break;
2644 case TKW.xint128:
2645 case TKW.xsigned | TKW.xint128: t = integerTypeForSize(16); break;
2647 case TKW.xunsigned | TKW.xint128: t = unsignedTypeForSize(16); break;
2649 case TKW.xvoid: t = AST.Type.tvoid; break;
2650 case TKW.xbool: t = boolsize == 1 ? AST.Type.tbool : integerTypeForSize(boolsize); break;
2652 case TKW.xfloat: t = AST.Type.tfloat32; break;
2653 case TKW.xdouble: t = AST.Type.tfloat64; break;
2654 case TKW.xlong | TKW.xdouble: t = realType(RTFlags.realfloat); break;
2656 case TKW.ximaginary | TKW.xfloat: t = AST.Type.timaginary32; break;
2657 case TKW.ximaginary | TKW.xdouble: t = AST.Type.timaginary64; break;
2658 case TKW.ximaginary | TKW.xlong | TKW.xdouble: t = realType(RTFlags.imaginary); break;
2660 case TKW.xcomplex | TKW.xfloat: t = AST.Type.tcomplex32; break;
2661 case TKW.xcomplex | TKW.xdouble: t = AST.Type.tcomplex64; break;
2662 case TKW.xcomplex | TKW.xlong | TKW.xdouble: t = realType(RTFlags.complex); break;
2664 case TKW.xident:
2666 const idx = previd.toString();
2667 if (idx.length > 2 && idx[0] == '_' && idx[1] == '_') // leading double underscore
2668 importBuiltins = true; // probably one of those compiler extensions
2669 t = null;
2671 /* Punch through to what the typedef is, to support things like:
2672 * typedef T* T;
2674 auto pt = lookupTypedef(previd);
2675 if (pt && *pt) // if previd is a known typedef
2676 t = *pt;
2678 if (!t)
2679 t = new AST.TypeIdentifier(loc, previd);
2680 break;
2683 case TKW.xtag:
2684 case TKW.x_Atomic: // no atomics for you
2685 break; // t is already set
2687 default:
2688 error("illegal type combination");
2689 t = AST.Type.terror;
2690 break;
2693 return t;
2696 /********************************
2697 * C11 6.7.6
2698 * Parse a declarator (including function definitions).
2699 * declarator:
2700 * pointer (opt) direct-declarator
2702 * direct-declarator :
2703 * identifier
2704 * ( declarator )
2705 * direct-declarator [ type-qualifier-list (opt) assignment-expression (opt) ]
2706 * direct-declarator [ static type-qualifier-list (opt) assignment-expression ]
2707 * direct-declarator [ type-qualifier-list static assignment-expression (opt) ]
2708 * direct-declarator [ type-qualifier-list (opt) * ]
2709 * direct-declarator ( parameter-type-list )
2710 * direct-declarator ( identifier-list (opt) )
2712 * pointer :
2713 * * type-qualifier-list (opt)
2714 * * type-qualifier-list (opt) pointer
2716 * type-qualifier-list :
2717 * type-qualifier
2718 * type-qualifier-list type-qualifier
2720 * parameter-type-list :
2721 * parameter-list
2722 * parameter-list , ...
2724 * parameter-list :
2725 * parameter-declaration
2726 * parameter-list , parameter-declaration
2728 * parameter-declaration :
2729 * declaration-specifiers declarator
2730 * declaration-specifiers abstract-declarator (opt)
2732 * identifier-list :
2733 * identifier
2734 * identifier-list , identifier
2736 * Params:
2737 * declarator = declarator kind
2738 * tbase = base type to start with
2739 * pident = set to Identifier if there is one, null if not
2740 * specifier = specifiers in and out
2741 * Returns:
2742 * type declared. If a TypeFunction is returned, this.symbols is the
2743 * symbol table for the parameter-type-list, which will contain any
2744 * declared struct, union or enum tags.
2746 private AST.Type cparseDeclarator(DTR declarator, AST.Type tbase,
2747 out Identifier pident, ref Specifier specifier)
2749 //printf("cparseDeclarator(%d, %s)\n", declarator, tbase.toChars());
2750 AST.Types constTypes; // all the Types that will need `const` applied to them
2752 /* Insert tx -> t into
2753 * ts -> ... -> t
2754 * so that
2755 * ts -> ... -> tx -> t
2757 static void insertTx(ref AST.Type ts, AST.Type tx, AST.Type t)
2759 AST.Type* pt;
2760 for (pt = &ts; *pt != t; pt = &(cast(AST.TypeNext)*pt).next)
2763 *pt = tx;
2766 AST.Type parseDecl(AST.Type t)
2768 //printf("parseDecl() t: %s\n", t.toChars());
2769 AST.Type ts;
2770 while (1)
2772 switch (token.value)
2774 case TOK.identifier: // identifier
2775 //printf("identifier %s\n", token.ident.toChars());
2776 if (declarator == DTR.xabstract)
2777 error("identifier not allowed in abstract-declarator");
2778 pident = token.ident;
2779 ts = t;
2780 nextToken();
2781 break;
2783 case TOK.leftParenthesis: // ( declarator )
2784 //printf("leftParen\n");
2785 /* like: T (*fp)();
2786 * T ((*fp))();
2788 auto tk = &token;
2789 if (!isCDeclarator(tk, declarator))
2791 /* Not ( declarator ), might be parameter-list
2793 ts = t;
2794 break;
2796 nextToken();
2798 if (token.value == TOK.__stdcall) // T (__stdcall*fp)();
2800 specifier.mod |= MOD.x__stdcall;
2801 nextToken();
2804 ts = parseDecl(t);
2805 check(TOK.rightParenthesis);
2806 break;
2808 case TOK.mul: // pointer
2809 //printf("star\n");
2810 t = new AST.TypePointer(t);
2811 nextToken();
2812 // add post fixes const/volatile/restrict/_Atomic
2813 const mod = cparseTypeQualifierList();
2814 if (mod & MOD.xconst)
2815 constTypes.push(t);
2816 if (token.value == TOK.__attribute__)
2817 cparseGnuAttributes(specifier);
2818 continue;
2820 default:
2821 //printf("default %s\n", token.toChars());
2822 if (declarator == DTR.xdirect)
2824 if (!t || t.isTypeIdentifier())
2826 // const arr[1];
2827 error("no type-specifier for declarator");
2828 t = AST.Type.tint32;
2830 else
2831 error("identifier or `(` expected"); // )
2832 panic();
2834 ts = t;
2835 break;
2837 break;
2840 // parse DeclaratorSuffixes
2841 while (1)
2843 switch (token.value)
2845 case TOK.leftBracket:
2847 // post [] syntax, pick up any leading type qualifiers, `static` and `*`
2848 AST.Type ta;
2849 nextToken();
2851 auto mod = cparseTypeQualifierList(); // const/volatile/restrict/_Atomic
2853 bool isStatic;
2854 bool isVLA;
2855 if (token.value == TOK.static_)
2857 isStatic = true; // `static`
2858 nextToken();
2859 if (!mod) // type qualifiers after `static`
2860 mod = cparseTypeQualifierList();
2862 else if (token.value == TOK.mul)
2864 if (peekNext() == TOK.rightBracket)
2866 isVLA = true; // `*`
2867 nextToken();
2871 if (isStatic || token.value != TOK.rightBracket)
2873 //printf("It's a static array\n");
2874 AST.Expression e = cparseAssignExp(); // [ expression ]
2875 ta = new AST.TypeSArray(t, e);
2877 else
2879 /* C11 6.7.6.2-4 An [ ] array is an incomplete array type
2881 ta = new AST.TypeSArray(t);
2883 check(TOK.rightBracket);
2885 // Issue errors for unsupported types.
2886 if (isVLA) // C11 6.7.6.2
2888 error("variable length arrays are not supported");
2890 if (isStatic) // C11 6.7.6.3
2892 error("static array parameters are not supported");
2894 if (declarator != DTR.xparameter)
2896 /* C11 6.7.6.2-4: '*' can only be used with function prototype scope.
2898 if (isVLA)
2899 error("variable length array used outside of function prototype");
2900 /* C11 6.7.6.2-1: type qualifiers and 'static' shall only appear
2901 * in a declaration of a function parameter with an array type.
2903 if (isStatic || mod)
2904 error("static or type qualifier used outside of function prototype");
2906 if (ts.isTypeSArray() || ts.isTypeDArray())
2908 /* C11 6.7.6.2-1: type qualifiers and 'static' shall only appear
2909 * in the outermost array type derivation.
2911 if (isStatic || mod)
2912 error("static or type qualifier used in non-outermost array type derivation");
2913 /* C11 6.7.6.2-1: the element type shall not be an incomplete or
2914 * function type.
2916 if (ta.isTypeSArray() && ta.isTypeSArray().isIncomplete() && !isVLA)
2917 error("array type has incomplete element type `%s`", ta.toChars());
2920 // Apply type qualifiers to the constructed type.
2921 if (mod & MOD.xconst) // ignore the other bits
2922 ta = toConst(ta);
2923 insertTx(ts, ta, t); // ts -> ... -> ta -> t
2924 continue;
2927 case TOK.leftParenthesis:
2929 // New symbol table for parameter-list
2930 auto symbolsSave = this.symbols;
2931 this.symbols = null;
2933 auto parameterList = cparseParameterList();
2934 const lkg = specifier.mod & MOD.x__stdcall ? LINK.windows : linkage;
2935 StorageClass stc = specifier._nothrow ? STC.nothrow_ : 0;
2936 if (specifier._pure)
2937 stc |= STC.pure_;
2938 AST.Type tf = new AST.TypeFunction(parameterList, t, lkg, stc);
2939 //tf = tf.addSTC(storageClass); // TODO
2940 insertTx(ts, tf, t); // ts -> ... -> tf -> t
2942 if (ts != tf)
2943 this.symbols = symbolsSave;
2944 break;
2947 default:
2948 break;
2950 break;
2952 if (declarator == DTR.xdirect && !pident)
2953 error("expected identifier for declarator");
2954 return ts;
2957 auto t = parseDecl(tbase);
2959 if (specifier.vector_size)
2961 auto length = new AST.IntegerExp(token.loc, specifier.vector_size / tbase.size(), AST.Type.tuns32);
2962 auto tsa = new AST.TypeSArray(tbase, length);
2963 AST.Type tv = new AST.TypeVector(tsa);
2964 specifier.vector_size = 0; // used it up
2966 insertTx(t, tv, tbase); // replace tbase with tv
2969 /* Because const is transitive, cannot assemble types from
2970 * fragments. Instead, types to be annotated with const are put
2971 * in constTypes[], and a bottom up scan of t is done to apply
2972 * const
2974 if (constTypes.length)
2976 AST.Type constApply(AST.Type t)
2978 if (t.nextOf())
2980 auto tn = cast(AST.TypeNext)t; // t.nextOf() should return a ref instead of this
2981 tn.next = constApply(tn.next);
2983 foreach (tc; constTypes[])
2985 if (tc is t)
2987 return toConst(t);
2990 return t;
2993 if (declarator == DTR.xparameter &&
2994 t.isTypePointer())
2996 /* Because there are instances in .h files of "const pointer to mutable",
2997 * skip applying transitive `const`
2998 * https://issues.dlang.org/show_bug.cgi?id=22534
3000 auto tn = cast(AST.TypeNext)t;
3001 tn.next = constApply(tn.next);
3003 else
3004 t = constApply(t);
3007 //printf("result: %s\n", t.toChars());
3008 return t;
3011 /******************************
3012 * C11 6.7.3
3013 * type-qualifier:
3014 * const
3015 * restrict
3016 * volatile
3017 * _Atomic
3018 * __stdcall
3020 MOD cparseTypeQualifierList()
3022 MOD mod;
3023 while (1)
3025 switch (token.value)
3027 case TOK.const_: mod |= MOD.xconst; break;
3028 case TOK.volatile: mod |= MOD.xvolatile; break;
3029 case TOK.restrict: mod |= MOD.xrestrict; break;
3030 case TOK._Atomic: mod |= MOD.x_Atomic; break;
3031 case TOK.__stdcall: mod |= MOD.x__stdcall; break;
3033 default:
3034 return mod;
3036 nextToken();
3040 /***********************************
3041 * C11 6.7.7
3043 AST.Type cparseTypeName()
3045 Specifier specifier;
3046 specifier.packalign.setDefault();
3047 auto tspec = cparseSpecifierQualifierList(LVL.global, specifier);
3048 if (!tspec)
3050 error("type-specifier is missing");
3051 tspec = AST.Type.tint32;
3053 if (tspec && specifier.mod & MOD.xconst)
3055 tspec = toConst(tspec);
3056 specifier.mod = MOD.xnone; // 'used' it
3058 Identifier id;
3059 return cparseDeclarator(DTR.xabstract, tspec, id, specifier);
3062 /***********************************
3063 * C11 6.7.2.1
3064 * specifier-qualifier-list:
3065 * type-specifier specifier-qualifier-list (opt)
3066 * type-qualifier specifier-qualifier-list (opt)
3067 * Params:
3068 * level = declaration context
3069 * specifier = specifiers in and out
3070 * Returns:
3071 * resulting type, null if not specified
3073 AST.Type cparseSpecifierQualifierList(LVL level, ref Specifier specifier)
3075 auto t = cparseDeclarationSpecifiers(level, specifier);
3076 if (specifier.scw)
3077 error("storage class not allowed in specifier-qualified-list");
3078 return t;
3081 /***********************************
3082 * C11 6.7.6.3
3083 * ( parameter-type-list )
3084 * ( identifier-list (opt) )
3086 AST.ParameterList cparseParameterList()
3088 auto parameters = new AST.Parameters();
3089 AST.VarArg varargs = AST.VarArg.none;
3090 StorageClass varargsStc;
3092 check(TOK.leftParenthesis);
3093 if (token.value == TOK.void_ && peekNext() == TOK.rightParenthesis) // func(void)
3095 nextToken();
3096 nextToken();
3097 return AST.ParameterList(parameters, varargs, varargsStc);
3100 if (token.value == TOK.rightParenthesis) // func()
3102 nextToken();
3103 return AST.ParameterList(parameters, AST.VarArg.KRvariadic, varargsStc);
3106 /* Create function prototype scope
3108 typedefTab.push(null);
3110 AST.ParameterList finish()
3112 typedefTab.pop();
3113 return AST.ParameterList(parameters, varargs, varargsStc);
3116 /* The check for identifier-list comes later,
3117 * when doing the trailing declaration-list (opt)
3119 while (1)
3121 if (token.value == TOK.rightParenthesis)
3122 break;
3123 if (token.value == TOK.dotDotDot)
3125 if (parameters.length == 0) // func(...)
3126 error("named parameter required before `...`");
3127 importBuiltins = true; // will need __va_list_tag
3128 varargs = AST.VarArg.variadic; // C-style variadics
3129 nextToken();
3130 check(TOK.rightParenthesis);
3131 return finish();
3134 Specifier specifier;
3135 specifier.packalign.setDefault();
3136 auto tspec = cparseDeclarationSpecifiers(LVL.prototype, specifier);
3137 if (!tspec)
3139 error("no type-specifier for parameter");
3140 tspec = AST.Type.tint32;
3143 if (specifier.mod & MOD.xconst)
3145 if ((token.value == TOK.rightParenthesis || token.value == TOK.comma) &&
3146 tspec.isTypeIdentifier())
3147 error("type-specifier omitted for parameter `%s`", tspec.isTypeIdentifier().ident.toChars());
3149 tspec = toConst(tspec);
3150 specifier.mod = MOD.xnone; // 'used' it
3153 Identifier id;
3154 const paramLoc = token.loc;
3155 auto t = cparseDeclarator(DTR.xparameter, tspec, id, specifier);
3156 if (token.value == TOK.__attribute__)
3157 cparseGnuAttributes(specifier);
3158 if (specifier.mod & MOD.xconst)
3159 t = toConst(t);
3160 auto param = new AST.Parameter(paramLoc, specifiersToSTC(LVL.parameter, specifier),
3161 t, id, null, null);
3162 parameters.push(param);
3163 if (token.value == TOK.rightParenthesis || token.value == TOK.endOfFile)
3164 break;
3165 check(TOK.comma);
3167 check(TOK.rightParenthesis);
3168 return finish();
3171 /***********************************
3172 * C11 6.7.10
3173 * _Static_assert ( constant-expression , string-literal ) ;
3175 private AST.StaticAssert cparseStaticAssert()
3177 const loc = token.loc;
3179 //printf("cparseStaticAssert()\n");
3180 nextToken();
3181 check(TOK.leftParenthesis);
3182 auto exp = cparseConstantExp();
3183 check(TOK.comma);
3184 if (token.value != TOK.string_)
3185 error("string literal expected");
3186 auto msg = cparsePrimaryExp();
3187 check(TOK.rightParenthesis);
3188 check(TOK.semicolon);
3189 return new AST.StaticAssert(loc, exp, msg);
3192 /*************************
3193 * Collect argument list.
3194 * Parser is on opening parenthesis.
3195 * Returns:
3196 * the arguments
3198 private AST.Expressions* cparseArguments()
3200 nextToken();
3201 auto arguments = new AST.Expressions();
3202 while (token.value != TOK.rightParenthesis && token.value != TOK.endOfFile)
3204 auto arg = cparseAssignExp();
3205 arguments.push(arg);
3206 if (token.value != TOK.comma)
3207 break;
3209 nextToken(); // consume comma
3212 check(TOK.rightParenthesis);
3214 return arguments;
3217 /*************************
3218 * __declspec parser
3219 * https://docs.microsoft.com/en-us/cpp/cpp/declspec
3220 * decl-specifier:
3221 * __declspec ( extended-decl-modifier-seq )
3223 * extended-decl-modifier-seq:
3224 * extended-decl-modifier (opt)
3225 * extended-decl-modifier extended-decl-modifier-seq
3227 * extended-decl-modifier:
3228 * align(number)
3229 * deprecated(depMsg)
3230 * dllimport
3231 * dllexport
3232 * naked
3233 * noinline
3234 * noreturn
3235 * nothrow
3236 * thread
3237 * Params:
3238 * specifier = filled in with the attribute(s)
3240 private void cparseDeclspec(ref Specifier specifier)
3242 //printf("cparseDeclspec()\n");
3243 /* Check for dllexport, dllimport
3244 * Ignore the rest
3246 nextToken(); // move past __declspec
3247 check(TOK.leftParenthesis);
3248 while (1)
3250 if (token.value == TOK.rightParenthesis)
3252 nextToken();
3253 break;
3255 else if (token.value == TOK.endOfFile)
3256 break;
3257 else if (token.value == TOK.identifier)
3259 if (token.ident == Id.dllimport)
3261 specifier.dllimport = true;
3262 nextToken();
3264 else if (token.ident == Id.dllexport)
3266 specifier.dllexport = true;
3267 nextToken();
3269 else if (token.ident == Id.naked)
3271 specifier.naked = true;
3272 nextToken();
3274 else if (token.ident == Id.noinline)
3276 specifier.scw |= SCW.xnoinline;
3277 nextToken();
3279 else if (token.ident == Id.noreturn)
3281 specifier.noreturn = true;
3282 nextToken();
3284 else if (token.ident == Id._nothrow)
3286 specifier._nothrow = true;
3287 nextToken();
3289 else if (token.ident == Id.thread)
3291 specifier.scw |= SCW.x_Thread_local;
3292 nextToken();
3294 else if (token.ident == Id._align)
3296 // Microsoft spec is very imprecise as to how this actually works
3297 nextToken();
3298 check(TOK.leftParenthesis);
3299 if (token.value == TOK.int32Literal)
3301 const n = token.unsvalue;
3302 if (n < 1 || n & (n - 1) || 8192 < n)
3303 error("__decspec(align(%lld)) must be an integer positive power of 2 and be <= 8,192", cast(ulong)n);
3304 specifier.packalign.set(cast(uint)n);
3305 specifier.packalign.setPack(true);
3306 nextToken();
3308 else
3310 error("alignment value expected, not `%s`", token.toChars());
3311 nextToken();
3314 check(TOK.rightParenthesis);
3316 else if (token.ident == Id._deprecated)
3318 specifier._deprecated = true;
3319 nextToken();
3320 if (token.value == TOK.leftParenthesis) // optional deprecation message
3322 nextToken();
3323 specifier.depMsg = cparseExpression();
3324 check(TOK.rightParenthesis);
3327 else
3329 nextToken();
3330 if (token.value == TOK.leftParenthesis)
3331 cparseParens();
3334 else if (token.value == TOK.restrict) // ImportC assigns no semantics to `restrict`, so just ignore the keyword.
3335 nextToken();
3336 else
3338 error("extended-decl-modifier expected after `__declspec(`, saw `%s` instead", token.toChars());
3339 nextToken();
3340 if (token.value != TOK.rightParenthesis)
3341 break;
3346 /*************************
3347 * Parser for asm label. It appears after the declarator, and has apparently
3348 * nothing to do with inline assembler.
3349 * https://gcc.gnu.org/onlinedocs/gcc/Asm-Labels.html
3350 * simple-asm-expr:
3351 * asm ( asm-string-literal )
3353 * asm-string-literal:
3354 * string-literal
3356 private AST.StringExp cparseGnuAsmLabel()
3358 nextToken(); // move past asm
3359 check(TOK.leftParenthesis);
3360 if (token.value != TOK.string_)
3361 error("string literal expected for Asm Label, not `%s`", token.toChars());
3362 auto label = cparsePrimaryExp();
3363 check(TOK.rightParenthesis);
3364 return cast(AST.StringExp) label;
3367 /********************
3368 * Parse C inline assembler statement in Gnu format.
3369 * https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
3370 * asm asm-qualifiers ( AssemblerTemplate : OutputOperands : InputOperands : Clobbers : GotoLabels )
3371 * Current token is on the `asm`.
3372 * Returns:
3373 * inline assembler expression as a Statement
3375 private AST.Statement cparseGnuAsm()
3377 // Defer parsing of AsmStatements until semantic processing.
3378 const loc = token.loc;
3380 nextToken();
3382 // Consume all asm-qualifiers. As a future optimization, we could record
3383 // the `inline` and `volatile` storage classes against the statement.
3384 while (token.value == TOK.goto_ ||
3385 token.value == TOK.inline ||
3386 token.value == TOK.volatile)
3387 nextToken();
3389 check(TOK.leftParenthesis);
3390 if (token.value != TOK.string_)
3391 error("string literal expected for Assembler Template, not `%s`", token.toChars());
3392 Token* toklist = null;
3393 Token** ptoklist = &toklist;
3394 //Identifier label = null;
3395 auto statements = new AST.Statements();
3397 int parens;
3398 while (1)
3400 switch (token.value)
3402 case TOK.leftParenthesis:
3403 ++parens;
3404 goto default;
3406 case TOK.rightParenthesis:
3407 --parens;
3408 if (parens >= 0)
3409 goto default;
3410 break;
3412 case TOK.semicolon:
3413 error("matching `)` expected, not `;`");
3414 break;
3416 case TOK.endOfFile:
3417 /* ( */
3418 error("matching `)` expected, not end of file");
3419 break;
3421 case TOK.colonColon: // treat as two separate : tokens for iasmgcc
3422 *ptoklist = allocateToken();
3423 memcpy(*ptoklist, &token, Token.sizeof);
3424 (*ptoklist).value = TOK.colon;
3425 ptoklist = &(*ptoklist).next;
3427 *ptoklist = allocateToken();
3428 memcpy(*ptoklist, &token, Token.sizeof);
3429 (*ptoklist).value = TOK.colon;
3430 ptoklist = &(*ptoklist).next;
3432 *ptoklist = null;
3433 nextToken();
3434 continue;
3436 default:
3437 *ptoklist = allocateToken();
3438 memcpy(*ptoklist, &token, Token.sizeof);
3439 ptoklist = &(*ptoklist).next;
3440 *ptoklist = null;
3441 nextToken();
3442 continue;
3444 if (toklist)
3446 // Create AsmStatement from list of tokens we've saved
3447 AST.Statement s = new AST.AsmStatement(token.loc, toklist);
3448 statements.push(s);
3450 break;
3452 nextToken();
3453 auto s = new AST.CompoundAsmStatement(loc, statements, 0);
3454 return s;
3457 /*************************
3458 * __attribute__ parser
3459 * https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html
3460 * gnu-attributes:
3461 * gnu-attributes gnu-attribute-specifier
3463 * gnu-attribute-specifier:
3464 * __attribute__ (( gnu-attribute-list ))
3466 * gnu-attribute-list:
3467 * gnu-attribute (opt)
3468 * gnu-attribute-list , gnu-attribute
3470 * Params:
3471 * specifier = filled in with the attribute(s)
3473 private void cparseGnuAttributes(ref Specifier specifier)
3475 while (token.value == TOK.__attribute__)
3477 nextToken(); // move past __attribute__
3478 check(TOK.leftParenthesis);
3479 check(TOK.leftParenthesis);
3481 if (token.value != TOK.rightParenthesis)
3483 while (1)
3485 cparseGnuAttribute(specifier);
3486 if (token.value != TOK.comma)
3487 break;
3488 nextToken();
3492 check(TOK.rightParenthesis);
3493 check(TOK.rightParenthesis);
3497 /*************************
3498 * Parse a single GNU attribute
3499 * gnu-attribute:
3500 * gnu-attribute-name
3501 * gnu-attribute-name ( identifier )
3502 * gnu-attribute-name ( identifier , expression-list )
3503 * gnu-attribute-name ( expression-list (opt) )
3505 * gnu-attribute-name:
3506 * keyword
3507 * identifier
3509 * expression-list:
3510 * constant-expression
3511 * expression-list , constant-expression
3513 * Params:
3514 * specifier = filled in with the attribute(s)
3516 private void cparseGnuAttribute(ref Specifier specifier)
3518 /* Check for dllimport, dllexport, naked, noreturn, vector_size(bytes)
3519 * Ignore the rest
3521 if (!isGnuAttributeName())
3522 return;
3524 if (token.value == TOK.identifier)
3526 if (token.ident == Id.aligned)
3528 nextToken();
3529 if (token.value == TOK.leftParenthesis)
3531 nextToken();
3532 if (token.value == TOK.int32Literal)
3534 const n = token.unsvalue;
3535 if (n < 1 || n & (n - 1) || ushort.max < n)
3536 error("__attribute__((aligned(%lld))) must be an integer positive power of 2 and be <= 32,768", cast(ulong)n);
3537 specifier.packalign.set(cast(uint)n);
3538 specifier.packalign.setPack(true);
3539 nextToken();
3541 else
3543 error("alignment value expected, not `%s`", token.toChars());
3544 nextToken();
3547 check(TOK.rightParenthesis);
3549 /* ignore __attribute__((aligned)), which sets the alignment to the largest value for any data
3550 * type on the target machine. It's the opposite of __attribute__((packed))
3553 else if (token.ident == Id.always_inline) // https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html
3555 specifier.scw |= SCW.xinline;
3556 nextToken();
3558 else if (token.ident == Id._deprecated)
3560 specifier._deprecated = true;
3561 nextToken();
3562 if (token.value == TOK.leftParenthesis) // optional deprecation message
3564 nextToken();
3565 specifier.depMsg = cparseExpression();
3566 check(TOK.rightParenthesis);
3569 else if (token.ident == Id.dllimport)
3571 specifier.dllimport = true;
3572 nextToken();
3574 else if (token.ident == Id.dllexport)
3576 specifier.dllexport = true;
3577 nextToken();
3579 else if (token.ident == Id.naked)
3581 specifier.naked = true;
3582 nextToken();
3584 else if (token.ident == Id.noinline)
3586 specifier.scw |= SCW.xnoinline;
3587 nextToken();
3589 else if (token.ident == Id.noreturn)
3591 specifier.noreturn = true;
3592 nextToken();
3594 else if (token.ident == Id._nothrow)
3596 specifier._nothrow = true;
3597 nextToken();
3599 else if (token.ident == Id._pure)
3601 specifier._pure = true;
3602 nextToken();
3604 else if (token.ident == Id.vector_size)
3606 nextToken();
3607 check(TOK.leftParenthesis);
3608 if (token.value == TOK.int32Literal)
3610 const n = token.unsvalue;
3611 if (n < 1 || n & (n - 1) || ushort.max < n)
3612 error("__attribute__((vector_size(%lld))) must be an integer positive power of 2 and be <= 32,768", cast(ulong)n);
3613 specifier.vector_size = cast(uint) n;
3614 nextToken();
3616 else
3618 error("value for vector_size expected, not `%s`", token.toChars());
3619 nextToken();
3621 check(TOK.rightParenthesis);
3623 else
3625 nextToken();
3626 if (token.value == TOK.leftParenthesis)
3627 cparseParens();
3630 else
3632 nextToken();
3633 if (token.value == TOK.leftParenthesis)
3634 cparseParens();
3638 /*************************
3639 * See if match for GNU attribute name, which may be any identifier,
3640 * storage-class-specifier, type-specifier, or type-qualifier.
3641 * Returns:
3642 * true if a valid GNU attribute name
3644 private bool isGnuAttributeName()
3646 switch (token.value)
3648 case TOK.identifier:
3649 case TOK.static_:
3650 case TOK.unsigned:
3651 case TOK.int64:
3652 case TOK.const_:
3653 case TOK.extern_:
3654 case TOK.register:
3655 case TOK.typedef_:
3656 case TOK.int16:
3657 case TOK.inline:
3658 case TOK._Noreturn:
3659 case TOK.volatile:
3660 case TOK.signed:
3661 case TOK.auto_:
3662 case TOK.restrict:
3663 case TOK._Complex:
3664 case TOK._Thread_local:
3665 case TOK.int32:
3666 case TOK.__int128:
3667 case TOK.char_:
3668 case TOK.float32:
3669 case TOK.float64:
3670 case TOK.void_:
3671 case TOK._Bool:
3672 case TOK._Atomic:
3673 return true;
3675 default:
3676 return false;
3680 /***************************
3681 * Like skipParens(), but consume the tokens.
3683 private void cparseParens()
3685 check(TOK.leftParenthesis);
3686 int parens = 1;
3688 while (1)
3690 switch (token.value)
3692 case TOK.leftParenthesis:
3693 ++parens;
3694 break;
3696 case TOK.rightParenthesis:
3697 --parens;
3698 if (parens < 0)
3700 error("extra right parenthesis");
3701 return;
3703 if (parens == 0)
3705 nextToken();
3706 return;
3708 break;
3710 case TOK.endOfFile:
3711 error("end of file found before right parenthesis");
3712 return;
3714 default:
3715 break;
3717 nextToken();
3722 /******************************************************************************/
3723 /***************************** Struct & Enum Parser ***************************/
3726 /*************************************
3727 * C11 6.7.2.2
3728 * enum-specifier:
3729 * enum identifier (opt) { enumerator-list }
3730 * enum identifier (opt) { enumerator-list , }
3731 * enum identifier
3733 * enumerator-list:
3734 * enumerator
3735 * enumerator-list , enumerator
3737 * enumerator:
3738 * enumeration-constant
3739 * enumeration-constant = constant-expression
3741 * enumeration-constant:
3742 * identifier
3744 * Params:
3745 * symbols = symbols to add enum declaration to
3746 * Returns:
3747 * type of the enum
3749 private AST.Type cparseEnum(ref AST.Dsymbols* symbols)
3751 const loc = token.loc;
3752 nextToken();
3754 /* GNU Extensions
3755 * enum-specifier:
3756 * enum gnu-attributes (opt) identifier (opt) { enumerator-list } gnu-attributes (opt)
3757 * enum gnu-attributes (opt) identifier (opt) { enumerator-list , } gnu-attributes (opt)
3758 * enum gnu-attributes (opt) identifier
3760 Specifier specifier;
3761 specifier.packalign.setDefault();
3762 if (token.value == TOK.__attribute__)
3763 cparseGnuAttributes(specifier);
3765 Identifier tag;
3766 if (token.value == TOK.identifier)
3768 tag = token.ident;
3769 nextToken();
3772 /* clang extension: add optional base type after the identifier
3773 * https://en.cppreference.com/w/cpp/language/enum
3774 * enum Identifier : Type
3776 //AST.Type base = AST.Type.tint32; // C11 6.7.2.2-4 implementation defined default base type
3777 AST.Type base = null; // C23 says base type is determined by enum member values
3778 if (token.value == TOK.colon)
3780 nextToken();
3781 base = cparseTypeName();
3784 AST.Dsymbols* members;
3785 if (token.value == TOK.leftCurly)
3787 nextToken();
3788 members = new AST.Dsymbols();
3790 if (token.value == TOK.rightCurly) // C11 6.7.2.2-1
3792 if (tag)
3793 error("no members for `enum %s`", tag.toChars());
3794 else
3795 error("no members for anonymous enum");
3798 while (token.value == TOK.identifier)
3800 auto ident = token.ident; // enumeration-constant
3801 nextToken();
3802 auto mloc = token.loc;
3804 if (token.value == TOK.__attribute__)
3806 /* gnu-attributes can appear here, but just scan and ignore them
3807 * https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
3809 Specifier specifierx;
3810 specifierx.packalign.setDefault();
3811 cparseGnuAttributes(specifierx);
3814 AST.Expression value;
3815 if (token.value == TOK.assign)
3817 nextToken();
3818 value = cparseConstantExp();
3819 // TODO C11 6.7.2.2-2 value must fit into an int
3822 if (token.value == TOK.__attribute__)
3824 /* gnu-attributes can appear here, but just scan and ignore them
3825 * https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
3827 Specifier specifierx;
3828 specifierx.packalign.setDefault();
3829 cparseGnuAttributes(specifierx);
3832 auto em = new AST.EnumMember(mloc, ident, value, null, 0, null, null);
3833 members.push(em);
3835 if (token.value == TOK.comma)
3837 nextToken();
3838 continue;
3840 break;
3842 check(TOK.rightCurly);
3844 /* GNU Extensions
3845 * Parse the postfix gnu-attributes (opt)
3847 if (token.value == TOK.__attribute__)
3848 cparseGnuAttributes(specifier);
3850 else if (!tag)
3851 error("missing `identifier` after `enum`");
3853 /* Need semantic information to determine if this is a declaration,
3854 * redeclaration, or reference to existing declaration.
3855 * Defer to the semantic() pass with a TypeTag.
3857 return new AST.TypeTag(loc, TOK.enum_, tag, structalign_t.init, base, members);
3860 /*************************************
3861 * C11 6.7.2.1
3862 * Parse struct and union specifiers.
3863 * Parser is advanced to the tag identifier or brace.
3864 * struct-or-union-specifier:
3865 * struct-or-union identifier (opt) { struct-declaration-list }
3866 * struct-or-union identifier
3868 * struct-or-union:
3869 * struct
3870 * union
3872 * struct-declaration-list:
3873 * struct-declaration
3874 * struct-declaration-list struct-declaration
3876 * Params:
3877 * loc = location of `struct` or `union`
3878 * structOrUnion = TOK.struct_ or TOK.union_
3879 * packalign = alignment to use for struct members
3880 * symbols = symbols to add struct-or-union declaration to
3881 * Returns:
3882 * type of the struct
3884 private AST.Type cparseStruct(Loc loc, TOK structOrUnion, structalign_t packalign, ref AST.Dsymbols* symbols)
3886 Identifier tag;
3888 if (token.value == TOK.identifier)
3890 tag = token.ident;
3891 nextToken();
3894 AST.Dsymbols* members;
3895 if (token.value == TOK.leftCurly)
3897 nextToken();
3898 members = new AST.Dsymbols(); // so `members` will be non-null even with 0 members
3899 while (token.value != TOK.rightCurly)
3901 cparseStructDeclaration(members);
3903 if (token.value == TOK.endOfFile)
3904 break;
3906 check(TOK.rightCurly);
3908 if ((*members).length == 0) // C11 6.7.2.1-8
3910 /* allow empty structs as an extension
3911 * struct-declarator-list:
3912 * struct-declarator (opt)
3916 else if (!tag)
3917 error("missing tag `identifier` after `%s`", Token.toChars(structOrUnion));
3919 // many ways and places to declare alignment
3920 if (packalign.isUnknown() && !this.packalign.isUnknown())
3921 packalign.set(this.packalign.get());
3923 /* Need semantic information to determine if this is a declaration,
3924 * redeclaration, or reference to existing declaration.
3925 * Defer to the semantic() pass with a TypeTag.
3927 return new AST.TypeTag(loc, structOrUnion, tag, packalign, null, members);
3930 /*************************************
3931 * C11 6.7.2.1
3932 * Parse a struct declaration member.
3933 * struct-declaration:
3934 * specifier-qualifier-list struct-declarator-list (opt) ;
3935 * static_assert-declaration
3937 * struct-declarator-list:
3938 * struct-declarator
3939 * struct-declarator-list , struct-declarator
3941 * struct-declarator:
3942 * declarator
3943 * declarator (opt) : constant-expression
3944 * Params:
3945 * members = where to put the fields (members)
3947 void cparseStructDeclaration(AST.Dsymbols* members)
3949 //printf("cparseStructDeclaration()\n");
3950 if (token.value == TOK._Static_assert)
3952 auto s = cparseStaticAssert();
3953 members.push(s);
3954 return;
3957 Specifier specifier;
3958 specifier.packalign = this.packalign;
3959 auto tspec = cparseSpecifierQualifierList(LVL.member, specifier);
3960 if (!tspec)
3962 error("no type-specifier for struct member");
3963 tspec = AST.Type.tint32;
3965 if (specifier.mod & MOD.xconst)
3967 tspec = toConst(tspec);
3968 specifier.mod = MOD.xnone; // 'used' it
3971 /* If a declarator does not follow, it is unnamed
3973 if (token.value == TOK.semicolon && tspec)
3975 nextToken();
3976 auto tt = tspec.isTypeTag();
3977 if (!tt)
3979 if (auto ti = tspec.isTypeIdentifier())
3981 error("type-specifier omitted before declaration of `%s`", ti.ident.toChars());
3983 return; // legal but meaningless empty declaration
3986 /* If anonymous struct declaration
3987 * struct { ... members ... };
3988 * C11 6.7.2.1-13
3990 if (!tt.id && tt.members)
3992 /* members of anonymous struct are considered members of
3993 * the containing struct
3995 auto ad = new AST.AnonDeclaration(tt.loc, tt.tok == TOK.union_, tt.members);
3996 auto s = applySpecifier(ad, specifier);
3997 members.push(s);
3998 return;
4000 if (!tt.id && !tt.members)
4001 return; // already gave error in cparseStruct()
4003 /* `struct tag;` and `struct tag { ... };`
4004 * always result in a declaration in the current scope
4006 // TODO: merge in specifier
4007 auto stag = (tt.tok == TOK.struct_)
4008 ? new AST.StructDeclaration(tt.loc, tt.id, false)
4009 : new AST.UnionDeclaration(tt.loc, tt.id);
4010 stag.members = tt.members;
4011 if (!symbols)
4012 symbols = new AST.Dsymbols();
4013 auto s = applySpecifier(stag, specifier);
4014 symbols.push(s);
4015 return;
4018 while (1)
4020 Identifier id;
4021 AST.Type dt;
4022 if (token.value == TOK.colon)
4024 if (auto ti = tspec.isTypeIdentifier())
4026 error("type-specifier omitted before bit field declaration of `%s`", ti.ident.toChars());
4027 tspec = AST.Type.tint32;
4030 // C11 6.7.2.1-12 unnamed bit-field
4031 id = Identifier.generateAnonymousId("BitField");
4032 dt = tspec;
4034 else
4036 dt = cparseDeclarator(DTR.xdirect, tspec, id, specifier);
4037 if (!dt)
4039 panic();
4040 nextToken();
4041 break; // error recovery
4045 AST.Expression width;
4046 if (token.value == TOK.colon)
4048 // C11 6.7.2.1-10 bit-field
4049 nextToken();
4050 width = cparseConstantExp();
4053 /* GNU Extensions
4054 * struct-declarator:
4055 * declarator gnu-attributes (opt)
4056 * declarator (opt) : constant-expression gnu-attributes (opt)
4058 if (token.value == TOK.__attribute__)
4059 cparseGnuAttributes(specifier);
4061 if (!tspec && !specifier.scw && !specifier.mod)
4062 error("specifier-qualifier-list required");
4063 else if (width)
4065 if (specifier.alignExps)
4066 error("no alignment-specifier for bit field declaration"); // C11 6.7.5-2
4067 auto s = new AST.BitFieldDeclaration(width.loc, dt, id, width);
4068 members.push(s);
4070 else if (id)
4072 if (dt.ty == AST.Tvoid)
4073 error("`void` has no value");
4075 // declare the symbol
4076 // Give member variables an implicit void initializer
4077 auto initializer = new AST.VoidInitializer(token.loc);
4078 AST.Dsymbol s = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(LVL.member, specifier));
4079 s = applySpecifier(s, specifier);
4080 members.push(s);
4083 switch (token.value)
4085 case TOK.identifier:
4086 error("missing comma");
4087 goto default;
4089 case TOK.semicolon:
4090 nextToken();
4091 return;
4093 case TOK.comma:
4094 nextToken();
4095 break;
4097 default:
4098 error("`;` or `,` expected");
4099 while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
4100 nextToken();
4101 nextToken();
4102 return;
4108 /******************************************************************************/
4109 /********************************* Lookahead Parser ***************************/
4112 /************************************
4113 * Determine if the scanner is sitting on the start of a declaration.
4114 * Params:
4115 * t = current token of the scanner
4116 * needId = flag with additional requirements for a declaration
4117 * endtok = ending token
4118 * pt = will be set ending token (if not null)
4119 * Returns:
4120 * true at start of a declaration
4122 private bool isCDeclaration(ref Token* pt)
4124 auto t = pt;
4125 //printf("isCDeclaration() %s\n", t.toChars());
4126 if (!isDeclarationSpecifiers(t))
4127 return false;
4129 while (1)
4131 if (t.value == TOK.semicolon)
4133 t = peek(t);
4134 pt = t;
4135 return true;
4137 if (!isCDeclarator(t, DTR.xdirect))
4138 return false;
4139 if (t.value == TOK.asm_)
4141 t = peek(t);
4142 if (t.value != TOK.leftParenthesis || !skipParens(t, &t))
4143 return false;
4145 if (t.value == TOK.__attribute__)
4147 t = peek(t);
4148 if (t.value != TOK.leftParenthesis || !skipParens(t, &t))
4149 return false;
4151 if (t.value == TOK.assign)
4153 t = peek(t);
4154 if (!isInitializer(t))
4155 return false;
4157 switch (t.value)
4159 case TOK.comma:
4160 t = peek(t);
4161 break;
4163 case TOK.semicolon:
4164 t = peek(t);
4165 pt = t;
4166 return true;
4168 default:
4169 return false;
4174 /********************************
4175 * See if match for initializer.
4176 * Params:
4177 * pt = starting token, updated to one past end of initializer if true
4178 * Returns:
4179 * true if initializer
4181 private bool isInitializer(ref Token* pt)
4183 //printf("isInitializer()\n");
4184 auto t = pt;
4186 if (t.value == TOK.leftCurly)
4188 if (!skipBraces(t))
4189 return false;
4190 pt = t;
4191 return true;
4194 // skip over assignment-expression, ending before comma or semiColon or EOF
4195 if (!isAssignmentExpression(t))
4196 return false;
4197 pt = t;
4198 return true;
4201 /********************************
4202 * See if match for:
4203 * postfix-expression ( argument-expression-list(opt) )
4204 * Params:
4205 * pt = starting token, updated to one past end of initializer if true
4206 * Returns:
4207 * true if function call
4209 private bool isFunctionCall(ref Token* pt)
4211 //printf("isFunctionCall()\n");
4212 auto t = pt;
4214 if (!isPrimaryExpression(t))
4215 return false;
4216 if (t.value != TOK.leftParenthesis)
4217 return false;
4218 t = peek(t);
4219 while (1)
4221 if (!isAssignmentExpression(t))
4222 return false;
4223 if (t.value == TOK.comma)
4225 t = peek(t);
4226 continue;
4228 if (t.value == TOK.rightParenthesis)
4230 t = peek(t);
4231 break;
4233 return false;
4235 if (t.value != TOK.semicolon)
4236 return false;
4237 pt = t;
4238 return true;
4241 /********************************
4242 * See if match for assignment-expression.
4243 * Params:
4244 * pt = starting token, updated to one past end of assignment-expression if true
4245 * Returns:
4246 * true if assignment-expression
4248 private bool isAssignmentExpression(ref Token* pt)
4250 auto t = pt;
4251 //printf("isAssignmentExpression() %s\n", t.toChars());
4253 /* This doesn't actually check for grammar matching an
4254 * assignment-expression. It just matches ( ) [ ] looking for
4255 * an ending token that would terminate one.
4257 bool any;
4258 while (1)
4260 switch (t.value)
4262 case TOK.comma:
4263 case TOK.semicolon:
4264 case TOK.rightParenthesis:
4265 case TOK.rightBracket:
4266 case TOK.endOfFile:
4267 if (!any)
4268 return false;
4269 break;
4271 case TOK.leftParenthesis:
4272 if (!skipParens(t, &t))
4273 return false;
4275 https://issues.dlang.org/show_bug.cgi?id=22267
4276 If the parser encounters the following
4277 `identifier variableName = (expression);`
4278 the initializer is not identified as such since the parentheses
4279 cause the parser to keep walking indefinitely
4280 (whereas `(1) + 1` would not be affected.).
4282 any = true;
4283 continue;
4285 case TOK.leftBracket:
4286 if (!skipBrackets(t))
4287 return false;
4288 continue;
4290 case TOK.leftCurly:
4291 if (!skipBraces(t))
4292 return false;
4293 continue;
4295 default:
4296 any = true; // assume token was part of an a-e
4297 t = peek(t);
4298 continue;
4300 pt = t;
4301 return true;
4305 /********************************
4306 * See if match for constant-expression.
4307 * Params:
4308 * pt = starting token, updated to one past end of constant-expression if true
4309 * Returns:
4310 * true if constant-expression
4312 private bool isConstantExpression(ref Token* pt)
4314 return isAssignmentExpression(pt);
4317 /********************************
4318 * See if match for declaration-specifiers.
4319 * No errors are diagnosed.
4320 * Params:
4321 * pt = starting token, updated to one past end of declaration-specifiers if true
4322 * Returns:
4323 * true if declaration-specifiers
4325 private bool isDeclarationSpecifiers(ref Token* pt)
4327 //printf("isDeclarationSpecifiers()\n");
4329 auto t = pt;
4331 bool seenType;
4332 bool any;
4333 while (1)
4335 switch (t.value)
4337 // type-specifiers
4338 case TOK.void_:
4339 case TOK.char_:
4340 case TOK.int16:
4341 case TOK.int32:
4342 case TOK.int64:
4343 case TOK.__int128:
4344 case TOK.float32:
4345 case TOK.float64:
4346 case TOK.signed:
4347 case TOK.unsigned:
4348 case TOK._Bool:
4349 //case TOK._Imaginary:
4350 case TOK._Complex:
4351 t = peek(t);
4352 seenType = true;
4353 any = true;
4354 continue;
4356 case TOK.identifier: // typedef-name
4357 if (!seenType)
4359 t = peek(t);
4360 seenType = true;
4361 any = true;
4362 continue;
4364 break;
4366 case TOK.struct_:
4367 case TOK.union_:
4368 case TOK.enum_:
4369 t = peek(t);
4370 if (t.value == TOK.__attribute__ ||
4371 t.value == TOK.__declspec)
4373 t = peek(t);
4374 if (!skipParens(t, &t))
4375 return false;
4377 if (t.value == TOK.identifier)
4379 t = peek(t);
4380 if (t.value == TOK.leftCurly)
4382 if (!skipBraces(t))
4383 return false;
4386 else if (t.value == TOK.leftCurly)
4388 if (!skipBraces(t))
4389 return false;
4391 else
4392 return false;
4393 any = true;
4394 continue;
4396 // storage-class-specifiers
4397 case TOK.typedef_:
4398 case TOK.extern_:
4399 case TOK.static_:
4400 case TOK.__thread:
4401 case TOK._Thread_local:
4402 case TOK.auto_:
4403 case TOK.register:
4405 // function-specifiers
4406 case TOK.inline:
4407 case TOK._Noreturn:
4409 // type-qualifiers
4410 case TOK.const_:
4411 case TOK.volatile:
4412 case TOK.restrict:
4413 case TOK.__stdcall:
4414 t = peek(t);
4415 any = true;
4416 continue;
4418 case TOK._Alignas: // alignment-specifier
4419 case TOK.__declspec: // decl-specifier
4420 case TOK.__attribute__: // attribute-specifier
4421 t = peek(t);
4422 if (!skipParens(t, &t))
4423 return false;
4424 any = true;
4425 continue;
4427 // either atomic-type-specifier or type_qualifier
4428 case TOK._Atomic: // TODO _Atomic ( type-name )
4429 t = peek(t);
4430 if (t.value == TOK.leftParenthesis) // maybe atomic-type-specifier
4432 auto tsave = t;
4433 t = peek(t);
4434 if (!isTypeName(t) || t.value != TOK.rightParenthesis)
4435 { // it's a type-qualifier
4436 t = tsave; // back up parser
4437 any = true;
4438 continue;
4440 t = peek(t); // move past right parenthesis of atomic-type-specifier
4442 any = true;
4443 continue;
4445 default:
4446 break;
4448 break;
4451 if (any)
4453 pt = t;
4454 return true;
4456 return false;
4459 /**************************************
4460 * See if declaration-list is present.
4461 * Returns:
4462 * true if declaration-list is present, even an empty one
4464 bool isDeclarationList(ref Token* pt)
4466 auto t = pt;
4467 while (1)
4469 if (t.value == TOK.leftCurly)
4471 pt = t;
4472 return true;
4474 if (!isCDeclaration(t))
4475 return false;
4479 /*******************************************
4480 * Skip braces.
4481 * Params:
4482 * pt = enters on left brace, set to token past right bracket on true
4483 * Returns:
4484 * true if successful
4486 private bool skipBraces(ref Token* pt)
4488 auto t = pt;
4489 if (t.value != TOK.leftCurly)
4490 return false;
4492 int braces = 0;
4494 while (1)
4496 switch (t.value)
4498 case TOK.leftCurly:
4499 ++braces;
4500 t = peek(t);
4501 continue;
4503 case TOK.rightCurly:
4504 --braces;
4505 if (braces == 0)
4507 pt = peek(t);
4508 return true;
4510 if (braces < 0)
4511 return false;
4513 t = peek(t);
4514 continue;
4516 case TOK.endOfFile:
4517 return false;
4519 default:
4520 t = peek(t);
4521 continue;
4526 /*******************************************
4527 * Skip brackets.
4528 * Params:
4529 * pt = enters on left bracket, set to token past right bracket on true
4530 * Returns:
4531 * true if successful
4533 private bool skipBrackets(ref Token* pt)
4535 auto t = pt;
4536 if (t.value != TOK.leftBracket)
4537 return false;
4539 int brackets = 0;
4541 while (1)
4543 switch (t.value)
4545 case TOK.leftBracket:
4546 ++brackets;
4547 t = peek(t);
4548 continue;
4550 case TOK.rightBracket:
4551 --brackets;
4552 if (brackets == 0)
4554 pt = peek(t);
4555 return true;
4557 if (brackets < 0)
4558 return false;
4560 t = peek(t);
4561 continue;
4563 case TOK.endOfFile:
4564 return false;
4566 default:
4567 t = peek(t);
4568 continue;
4573 /*********************************
4574 * Check to see if tokens starting with *pt form a declarator.
4575 * Params:
4576 * pt = pointer to starting token, updated to point past declarator if true is returned
4577 * declarator = declarator kind
4578 * Returns:
4579 * true if it does
4581 private bool isCDeclarator(ref Token* pt, DTR declarator)
4583 //printf("isCDeclarator()\n");
4584 auto t = pt;
4585 while (1)
4587 if (t.value == TOK.mul) // pointer
4589 t = peek(t);
4590 if (!isTypeQualifierList(t))
4591 return false;
4593 else
4594 break;
4597 if (t.value == TOK.identifier)
4599 if (declarator == DTR.xabstract)
4600 return false;
4601 t = peek(t);
4603 else if (t.value == TOK.leftParenthesis)
4605 t = peek(t);
4606 if (t.value == TOK.__stdcall)
4607 t = peek(t);
4608 if (!isCDeclarator(t, declarator))
4609 return false;
4610 if (t.value != TOK.rightParenthesis)
4611 return false;
4612 t = peek(t);
4614 else if (declarator == DTR.xdirect)
4616 return false;
4619 while (1)
4621 if (t.value == TOK.leftBracket)
4623 if (!skipBrackets(t))
4624 return false;
4626 else if (t.value == TOK.leftParenthesis)
4628 if (!skipParens(t, &t))
4629 return false;
4631 else
4632 break;
4634 pt = t;
4635 return true;
4638 /***************************
4639 * Is this the start of a type-qualifier-list?
4640 * (Can be empty.)
4641 * Params:
4642 * pt = first token; updated with past end of type-qualifier-list if true
4643 * Returns:
4644 * true if start of type-qualifier-list
4646 private bool isTypeQualifierList(ref Token* pt)
4648 auto t = pt;
4649 while (1)
4651 switch (t.value)
4653 case TOK.const_:
4654 case TOK.restrict:
4655 case TOK.volatile:
4656 case TOK._Atomic:
4657 case TOK.__stdcall:
4658 t = peek(t);
4659 continue;
4661 default:
4662 break;
4664 break;
4666 pt = t;
4667 return true;
4670 /***************************
4671 * Is this the start of a type-name?
4672 * Params:
4673 * pt = first token; updated with past end of type-name if true
4674 * Returns:
4675 * true if start of type-name
4677 private bool isTypeName(ref Token* pt)
4679 auto t = pt;
4680 //printf("isTypeName() %s\n", t.toChars());
4681 if (!isSpecifierQualifierList(t))
4682 return false;
4683 if (!isCDeclarator(t, DTR.xabstract))
4684 return false;
4685 if (t.value != TOK.rightParenthesis)
4686 return false;
4687 pt = t;
4688 return true;
4691 /***************************
4692 * Is this the start of a specifier-qualifier-list?
4693 * Params:
4694 * pt = first token; updated with past end of specifier-qualifier-list if true
4695 * Returns:
4696 * true if start of specifier-qualifier-list
4698 private bool isSpecifierQualifierList(ref Token* pt)
4700 auto t = pt;
4701 bool result;
4702 while (1)
4704 switch (t.value)
4706 // Type Qualifiers
4707 case TOK.const_:
4708 case TOK.restrict:
4709 case TOK.volatile:
4710 case TOK.__stdcall:
4712 // Type Specifiers
4713 case TOK.char_:
4714 case TOK.signed:
4715 case TOK.unsigned:
4716 case TOK.int16:
4717 case TOK.int32:
4718 case TOK.int64:
4719 case TOK.__int128:
4720 case TOK.float32:
4721 case TOK.float64:
4722 case TOK.void_:
4723 case TOK._Bool:
4724 //case TOK._Imaginary: // ? missing in Spec
4725 case TOK._Complex:
4726 t = peek(t);
4727 break;
4729 case TOK.identifier:
4730 // Use typedef table to disambiguate
4731 if (isTypedef(t.ident))
4733 t = peek(t);
4734 break;
4736 else
4738 return false;
4741 // struct-or-union-specifier
4742 // enum-specifier
4743 case TOK.struct_:
4744 case TOK.union_:
4745 case TOK.enum_:
4746 t = peek(t);
4747 if (t.value == TOK.identifier)
4749 t = peek(t);
4750 if (t.value == TOK.leftCurly)
4752 if (!skipBraces(t))
4753 return false;
4756 else if (t.value == TOK.leftCurly)
4758 if (!skipBraces(t))
4759 return false;
4761 else
4762 return false;
4763 break;
4765 // atomic-type-specifier
4766 case TOK._Atomic:
4767 case TOK.typeof_:
4768 case TOK.__attribute__:
4769 t = peek(t);
4770 if (t.value != TOK.leftParenthesis ||
4771 !skipParens(t, &t))
4772 return false;
4773 break;
4775 default:
4776 if (result)
4777 pt = t;
4778 return result;
4780 result = true;
4784 /************************************
4785 * Looking at the leading left parenthesis, and determine if it is
4786 * either of the following:
4787 * ( type-name ) cast-expression
4788 * ( type-name ) { initializer-list }
4789 * as opposed to:
4790 * ( expression )
4791 * Params:
4792 * pt = starting token, updated to one past end of constant-expression if true
4793 * afterParenType = true if already seen `( type-name )`
4794 * Returns:
4795 * true if matches ( type-name ) ...
4797 private bool isCastExpression(ref Token* pt, bool afterParenType = false)
4799 enum log = false;
4800 if (log) printf("isCastExpression(tk: `%s`, afterParenType: %d)\n", token.toChars(pt.value), afterParenType);
4801 auto t = pt;
4802 switch (t.value)
4804 case TOK.leftParenthesis:
4805 auto tk = peek(t); // move past left parenthesis
4806 if (!isTypeName(tk) || tk.value != TOK.rightParenthesis)
4808 if (afterParenType)
4809 goto default; // could be ( type-name ) ( unary-expression )
4810 return false;
4812 tk = peek(tk); // move past right parenthesis
4814 if (tk.value == TOK.leftCurly)
4816 // ( type-name ) { initializer-list }
4817 if (!isInitializer(tk))
4819 return false;
4821 t = tk;
4822 break;
4825 if (tk.value == TOK.leftParenthesis && peek(tk).value == TOK.rightParenthesis)
4827 return false; // (type-name)() is not a cast (it might be a function call)
4830 if (!isCastExpression(tk, true))
4832 if (afterParenType) // could be ( type-name ) ( unary-expression )
4833 goto default; // where unary-expression also matched type-name
4834 return true;
4836 // ( type-name ) cast-expression
4837 t = tk;
4838 break;
4840 default:
4841 if (!afterParenType || !isUnaryExpression(t, afterParenType))
4843 return false;
4845 // if we've already seen ( type-name ), then this is a cast
4846 break;
4848 pt = t;
4849 if (log) printf("isCastExpression true\n");
4850 return true;
4853 /********************************
4854 * See if match for unary-expression.
4855 * Params:
4856 * pt = starting token, updated to one past end of constant-expression if true
4857 * afterParenType = true if already seen ( type-name ) of a cast-expression
4858 * Returns:
4859 * true if unary-expression
4861 private bool isUnaryExpression(ref Token* pt, bool afterParenType = false)
4863 auto t = pt;
4864 switch (t.value)
4866 case TOK.plusPlus:
4867 case TOK.minusMinus:
4868 t = peek(t);
4869 if (!isUnaryExpression(t, afterParenType))
4870 return false;
4871 break;
4873 case TOK.and:
4874 case TOK.mul:
4875 case TOK.min:
4876 case TOK.add:
4877 case TOK.not:
4878 case TOK.tilde:
4879 t = peek(t);
4880 if (!isCastExpression(t, afterParenType))
4881 return false;
4882 break;
4884 case TOK.sizeof_:
4885 t = peek(t);
4886 if (t.value == TOK.leftParenthesis)
4888 auto tk = peek(t);
4889 if (isTypeName(tk))
4891 if (tk.value != TOK.rightParenthesis)
4892 return false;
4893 t = peek(tk);
4894 break;
4897 if (!isUnaryExpression(t, afterParenType))
4898 return false;
4899 break;
4901 case TOK._Alignof:
4902 t = peek(t);
4903 if (t.value != TOK.leftParenthesis)
4904 return false;
4905 t = peek(t);
4906 if (!isTypeName(t) || t.value != TOK.rightParenthesis)
4907 return false;
4908 break;
4910 default:
4911 // Compound literals are handled by cast and sizeof expressions,
4912 // so be content with just seeing a primary expression.
4913 if (!isPrimaryExpression(t))
4914 return false;
4915 break;
4917 pt = t;
4918 return true;
4921 /********************************
4922 * See if match for primary-expression.
4923 * Params:
4924 * pt = starting token, updated to one past end of constant-expression if true
4925 * Returns:
4926 * true if primary-expression
4928 private bool isPrimaryExpression(ref Token* pt)
4930 auto t = pt;
4931 switch (t.value)
4933 case TOK.identifier:
4934 case TOK.charLiteral:
4935 case TOK.int32Literal:
4936 case TOK.uns32Literal:
4937 case TOK.int64Literal:
4938 case TOK.uns64Literal:
4939 case TOK.float32Literal:
4940 case TOK.float64Literal:
4941 case TOK.float80Literal:
4942 case TOK.imaginary32Literal:
4943 case TOK.imaginary64Literal:
4944 case TOK.imaginary80Literal:
4945 case TOK.string_:
4946 t = peek(t);
4947 break;
4949 case TOK.leftParenthesis:
4950 // ( expression )
4951 if (!skipParens(t, &t))
4952 return false;
4953 break;
4955 case TOK._Generic:
4956 t = peek(t);
4957 if (!skipParens(t, &t))
4958 return false;
4959 break;
4961 default:
4962 return false;
4964 pt = t;
4965 return true;
4969 /******************************************************************************/
4970 /********************************* More ***************************************/
4973 /**************
4974 * Declaration context
4976 enum LVL
4978 global = 1, /// global
4979 parameter = 2, /// function parameter (declarations for function identifier-list)
4980 prototype = 4, /// function prototype
4981 local = 8, /// local
4982 member = 0x10, /// struct member
4985 /// Types of declarator to parse
4986 enum DTR
4988 xdirect = 1, /// C11 6.7.6 direct-declarator
4989 xabstract = 2, /// C11 6.7.7 abstract-declarator
4990 xparameter = 3, /// parameter declarator may be either direct or abstract
4993 /// C11 6.7.1 Storage-class specifiers
4994 enum SCW : uint
4996 xnone = 0,
4997 xtypedef = 1,
4998 xextern = 2,
4999 xstatic = 4,
5000 x_Thread_local = 8,
5001 xauto = 0x10,
5002 xregister = 0x20,
5003 // C11 6.7.4 Function specifiers
5004 xinline = 0x40,
5005 x_Noreturn = 0x80,
5007 xnoinline = 0x100,
5010 /// C11 6.7.3 Type qualifiers
5011 enum MOD : uint
5013 xnone = 0,
5014 xconst = 1,
5015 xvolatile = 2,
5016 xrestrict = 4,
5017 x_Atomic = 8,
5018 x__stdcall = 0x10, // Windows linkage extension
5021 /**********************************
5022 * Aggregate for all the various specifiers
5024 struct Specifier
5026 bool noreturn; /// noreturn attribute
5027 bool naked; /// naked attribute
5028 bool _nothrow; /// nothrow attribute
5029 bool _pure; /// pure attribute
5030 bool dllimport; /// dllimport attribute
5031 bool dllexport; /// dllexport attribute
5032 bool _deprecated; /// deprecated attribute
5033 AST.Expression depMsg; /// deprecated message
5034 uint vector_size; /// positive power of 2 multipe of base type size
5036 SCW scw; /// storage-class specifiers
5037 MOD mod; /// type qualifiers
5038 AST.Expressions* alignExps; /// alignment
5039 structalign_t packalign; /// #pragma pack alignment value
5042 /***********************
5043 * Convert from C specifiers to D storage class
5044 * Params:
5045 * level = declaration context
5046 * specifier = specifiers, context, etc.
5047 * Returns:
5048 * corresponding D storage class
5050 StorageClass specifiersToSTC(LVL level, const ref Specifier specifier)
5052 StorageClass stc;
5053 if (specifier.scw & SCW.x_Thread_local)
5055 if (level == LVL.global)
5057 if (specifier.scw & SCW.xextern)
5058 stc = AST.STC.extern_;
5059 else if (specifier.scw & SCW.xstatic)
5060 stc = AST.STC.static_;
5062 else if (level == LVL.local)
5064 if (specifier.scw & SCW.xextern)
5065 stc = AST.STC.extern_;
5066 else if (specifier.scw & SCW.xstatic)
5067 stc = AST.STC.static_;
5069 else if (level == LVL.member)
5071 if (specifier.scw & SCW.xextern)
5072 stc = AST.STC.extern_;
5073 else if (specifier.scw & SCW.xstatic)
5074 stc = AST.STC.static_;
5077 else
5079 if (level == LVL.global)
5081 if (specifier.scw & SCW.xextern)
5082 stc = AST.STC.extern_ | AST.STC.gshared;
5083 else if (specifier.scw & SCW.xstatic)
5084 stc = AST.STC.gshared | AST.STC.static_;
5085 else
5086 stc = AST.STC.gshared;
5088 else if (level == LVL.local)
5090 if (specifier.scw & SCW.xextern)
5091 stc = AST.STC.extern_ | AST.STC.gshared;
5092 else if (specifier.scw & SCW.xstatic)
5093 stc = AST.STC.gshared;
5094 else if (specifier.scw & SCW.xregister)
5095 stc = AST.STC.register;
5097 else if (level == LVL.parameter)
5099 if (specifier.scw & SCW.xregister)
5100 stc = AST.STC.register | AST.STC.parameter;
5101 else
5102 stc = AST.STC.parameter;
5104 else if (level == LVL.member)
5106 if (specifier.scw & SCW.xextern)
5107 stc = AST.STC.extern_ | AST.STC.gshared;
5108 else if (specifier.scw & SCW.xstatic)
5109 stc = AST.STC.gshared;
5112 if (specifier._deprecated && !specifier.depMsg)
5113 stc |= AST.STC.deprecated_;
5114 return stc;
5117 /***********************
5118 * Add attributes from Specifier to function
5119 * Params:
5120 * fd = function to apply them to
5121 * specifier = specifiers
5123 void specifiersToFuncDeclaration(AST.FuncDeclaration fd, const ref Specifier specifier)
5125 fd.isNaked = specifier.naked;
5126 fd.dllImport = specifier.dllimport;
5127 fd.dllExport = specifier.dllexport;
5129 if (specifier.scw & SCW.xnoinline)
5130 fd.inlining = PINLINE.never;
5131 else if (specifier.scw & SCW.xinline)
5132 fd.inlining = PINLINE.always;
5135 /***********************
5136 * Add attributes from Specifier to variable
5137 * Params:
5138 * vd = function to apply them to
5139 * specifier = specifiers
5141 void specifiersToVarDeclaration(AST.VarDeclaration vd, const ref Specifier specifier)
5143 vd.dllImport = specifier.dllimport;
5144 vd.dllExport = specifier.dllexport;
5147 /***********************
5148 * Return suitable signed integer type for the given size
5149 * Params:
5150 * size = size of type
5151 * Returns:
5152 * corresponding signed D integer type
5154 private AST.Type integerTypeForSize(ubyte size)
5156 if (size <= 1)
5157 return AST.Type.tint8;
5158 if (size <= 2)
5159 return AST.Type.tint16;
5160 if (size <= 4)
5161 return AST.Type.tint32;
5162 if (size <= 8)
5163 return AST.Type.tint64;
5164 if (size == 16)
5166 error("__int128 not supported");
5167 return AST.Type.terror;
5169 error("unsupported integer type");
5170 return AST.Type.terror;
5173 /***********************
5174 * Return suitable unsigned integer type for the given size
5175 * Params:
5176 * size = size of type
5177 * Returns:
5178 * corresponding unsigned D integer type
5180 private AST.Type unsignedTypeForSize(ubyte size)
5182 if (size <= 1)
5183 return AST.Type.tuns8;
5184 if (size <= 2)
5185 return AST.Type.tuns16;
5186 if (size <= 4)
5187 return AST.Type.tuns32;
5188 if (size <= 8)
5189 return AST.Type.tuns64;
5190 if (size == 16)
5192 error("unsigned __int128 not supported");
5193 return AST.Type.terror;
5195 error("unsupported integer type");
5196 return AST.Type.terror;
5199 /***********************
5200 * Return suitable D float type for C `long double`
5201 * Params:
5202 * flags = kind of float to return (real, imaginary, complex).
5203 * Returns:
5204 * corresponding D type
5206 private AST.Type realType(RTFlags flags)
5208 if (long_doublesize == AST.Type.tfloat80.size())
5210 // On GDC and LDC, D `real` types map to C `long double`, so never
5211 // return a double type when real.sizeof == double.sizeof.
5212 final switch (flags)
5214 case RTFlags.realfloat: return AST.Type.tfloat80;
5215 case RTFlags.imaginary: return AST.Type.timaginary80;
5216 case RTFlags.complex: return AST.Type.tcomplex80;
5219 else
5221 final switch (flags)
5223 case RTFlags.realfloat: return long_doublesize == 8 ? AST.Type.tfloat64 : AST.Type.tfloat80;
5224 case RTFlags.imaginary: return long_doublesize == 8 ? AST.Type.timaginary64 : AST.Type.timaginary80;
5225 case RTFlags.complex: return long_doublesize == 8 ? AST.Type.tcomplex64 : AST.Type.tcomplex80;
5230 /**************
5231 * Flags for realType
5233 private enum RTFlags
5235 realfloat,
5236 imaginary,
5237 complex,
5240 /********************
5241 * C11 6.4.2.2 Create declaration to predefine __func__
5242 * `static const char __func__[] = " function-name ";`
5243 * Params:
5244 * loc = location for this declaration
5245 * id = identifier of function
5246 * Returns:
5247 * statement representing the declaration of __func__
5249 private AST.Statement createFuncName(Loc loc, Identifier id)
5251 const fn = id.toString(); // function-name
5252 auto efn = new AST.StringExp(loc, fn, fn.length, 1, 'c');
5253 auto ifn = new AST.ExpInitializer(loc, efn);
5254 auto lenfn = new AST.IntegerExp(loc, fn.length + 1, AST.Type.tuns32); // +1 for terminating 0
5255 auto tfn = new AST.TypeSArray(AST.Type.tchar, lenfn);
5256 efn.type = tfn.immutableOf();
5257 efn.committed = true;
5258 auto sfn = new AST.VarDeclaration(loc, tfn, Id.__func__, ifn, STC.gshared | STC.immutable_);
5259 auto e = new AST.DeclarationExp(loc, sfn);
5260 return new AST.ExpStatement(loc, e);
5263 /************************
5264 * After encountering an error, scan forward until a right brace or ; is found
5265 * or the end of the file.
5267 void panic()
5269 while (token.value != TOK.rightCurly && token.value != TOK.semicolon && token.value != TOK.endOfFile)
5270 nextToken();
5273 /**************************
5274 * Apply `const` to a type.
5275 * Params:
5276 * t = type to add const to
5277 * Returns:
5278 * resulting type
5280 private AST.Type toConst(AST.Type t)
5282 // `const` is always applied to the return type, not the
5283 // type function itself.
5284 if (auto tf = t.isTypeFunction())
5285 tf.next = tf.next.addSTC(STC.const_);
5286 else if (auto tt = t.isTypeTag())
5287 tt.mod |= MODFlags.const_;
5288 else
5290 /* Ignore const if the result would be const pointer to mutable
5292 auto tn = t.nextOf();
5293 if (!tn || tn.isConst())
5294 t = t.addSTC(STC.const_);
5296 return t;
5299 /***************************
5300 * Apply specifier to a Dsymbol.
5301 * Params:
5302 * s = Dsymbol
5303 * specifier = specifiers to apply
5304 * Returns:
5305 * Dsymbol with specifiers applied
5307 private AST.Dsymbol applySpecifier(AST.Dsymbol s, ref Specifier specifier)
5309 //printf("applySpecifier() %s\n", s.toChars());
5310 if (specifier._deprecated)
5312 if (specifier.depMsg)
5314 // Wrap declaration in a DeprecatedDeclaration
5315 auto decls = new AST.Dsymbols(1);
5316 (*decls)[0] = s;
5317 s = new AST.DeprecatedDeclaration(specifier.depMsg, decls);
5321 if (specifier.alignExps)
5323 //printf(" applying _Alignas %s, packalign %d\n", (*specifier.alignExps)[0].toChars(), cast(int)specifier.packalign);
5324 // Wrap declaration in an AlignDeclaration
5325 auto decls = new AST.Dsymbols(1);
5326 (*decls)[0] = s;
5327 s = new AST.AlignDeclaration(s.loc, specifier.alignExps, decls);
5329 else if (!specifier.packalign.isDefault() && !specifier.packalign.isUnknown())
5331 //printf(" applying packalign %d\n", cast(int)specifier.packalign);
5332 // Wrap #pragma pack in an AlignDeclaration
5333 auto decls = new AST.Dsymbols(1);
5334 (*decls)[0] = s;
5335 s = new AST.AlignDeclaration(s.loc, specifier.packalign, decls);
5337 return s;
5342 /******************************************************************************/
5343 /************************** typedefTab symbol table ***************************/
5346 /********************************
5347 * Determines if type t is a function type.
5348 * Params:
5349 * t = type to test
5350 * Returns:
5351 * true if it represents a function
5353 bool isFunctionTypedef(AST.Type t)
5355 //printf("isFunctionTypedef() %s\n", t.toChars());
5356 if (t.isTypeFunction())
5357 return true;
5358 if (auto tid = t.isTypeIdentifier())
5360 auto pt = lookupTypedef(tid.ident);
5361 if (pt && *pt)
5363 return (*pt).isTypeFunction() !is null;
5366 return false;
5369 /********************************
5370 * Determine if `id` is a symbol for a Typedef.
5371 * Params:
5372 * id = possible typedef
5373 * Returns:
5374 * true if id is a Type
5376 bool isTypedef(Identifier id)
5378 auto pt = lookupTypedef(id);
5379 return (pt && *pt);
5382 /*******************************
5383 * Add `id` to typedefTab[], but only if it will mask an existing typedef.
5384 * Params: id = identifier for non-typedef symbol
5386 void insertIdToTypedefTab(Identifier id)
5388 //printf("insertIdToTypedefTab(id: %s) level %d\n", id.toChars(), cast(int)typedefTab.length - 1);
5389 if (isTypedef(id)) // if existing typedef
5391 /* Add id as null, so we can later distinguish it from a non-null typedef
5393 auto tab = cast(void*[void*])(typedefTab[$ - 1]);
5394 tab[cast(void*)id] = cast(void*)null;
5398 /*******************************
5399 * Add `id` to typedefTab[]
5400 * Params:
5401 * id = identifier for typedef symbol
5402 * t = type of the typedef symbol
5404 void insertTypedefToTypedefTab(Identifier id, AST.Type t)
5406 //printf("insertTypedefToTypedefTab(id: %s, t: %s) level %d\n", id.toChars(), t ? t.toChars() : "null".ptr, cast(int)typedefTab.length - 1);
5407 if (auto tid = t.isTypeIdentifier())
5409 // Try to resolve the TypeIdentifier to its type
5410 auto pt = lookupTypedef(tid.ident);
5411 if (pt && *pt)
5412 t = *pt;
5414 auto tab = cast(void*[void*])(typedefTab[$ - 1]);
5415 tab[cast(void*)id] = cast(void*)t;
5416 typedefTab[$ - 1] = cast(void*)tab;
5419 /*********************************
5420 * Lookup id in typedefTab[].
5421 * Returns:
5422 * if not found, then null.
5423 * if found, then Type*. Deferencing it will yield null if it is not
5424 * a typedef, and a type if it is a typedef.
5426 AST.Type* lookupTypedef(Identifier id)
5428 foreach_reverse (tab; typedefTab[])
5430 if (auto pt = cast(void*)id in cast(void*[void*])tab)
5432 return cast(AST.Type*)pt;
5435 return null; // not found
5440 /******************************************************************************/
5441 /********************************* Directive Parser ***************************/
5444 override bool parseSpecialTokenSequence()
5446 Token n;
5447 scan(&n);
5448 if (n.value == TOK.int32Literal)
5450 poundLine(n, true);
5451 return true;
5453 if (n.value == TOK.identifier)
5455 if (n.ident == Id.line)
5457 poundLine(n, false);
5458 return true;
5460 else if (defines && (n.ident == Id.define || n.ident == Id.undef))
5462 /* Append this line to `defines`.
5463 * Not canonicalizing it - assume it already is
5465 defines.writeByte('#');
5466 defines.writestring(n.ident.toString());
5467 skipToNextLine(defines);
5468 defines.writeByte('\n');
5469 return true;
5471 else if (n.ident == Id.__pragma)
5473 pragmaDirective(scanloc);
5474 return true;
5476 else if (n.ident == Id.ident) // #ident "string"
5478 scan(&n);
5479 if (n.value == TOK.string_ && n.ptr[0] == '"' && n.postfix == 0)
5481 /* gcc inserts string into the .comment section in the object file.
5482 * Just ignore it for now, but can support it later by writing
5483 * the string to obj_exestr()
5485 //auto comment = n.ustring;
5487 scan(&n);
5488 if (n.value == TOK.endOfFile || n.value == TOK.endOfLine)
5489 return true;
5491 error("\"string\" expected after `#ident`");
5492 return false;
5495 if (n.ident != Id.undef)
5496 error("C preprocessor directive `#%s` is not supported", n.toChars());
5497 return false;
5500 /*********************************************
5501 * VC __pragma
5502 * https://docs.microsoft.com/en-us/cpp/preprocessor/pragma-directives-and-the-pragma-keyword?view=msvc-170
5503 * Scanner is on the `__pragma`
5504 * Params:
5505 * startloc = location to use for error messages
5507 private void uupragmaDirective(const ref Loc startloc)
5509 const loc = startloc;
5510 nextToken(); // move past __pragma
5511 if (token.value != TOK.leftParenthesis)
5513 error(loc, "left parenthesis expected to follow `__pragma` instead of `%s`", token.toChars());
5514 nextToken();
5515 return;
5517 nextToken();
5519 if (token.value == TOK.identifier)
5521 if (token.ident == Id.pack)
5522 pragmaPack(startloc, false);
5523 else
5525 nextToken();
5526 if (token.value == TOK.leftParenthesis)
5527 cparseParens();
5531 else if (token.value == TOK.endOfFile)
5534 else if (token.value == TOK.rightParenthesis)
5537 else
5538 error(loc, "unrecognized `__pragma(%s)`", token.toChars());
5540 if (token.value != TOK.rightParenthesis)
5542 error(loc, "right parenthesis expected to close `__pragma(...)` instead of `%s`", token.toChars());
5543 return;
5545 nextToken();
5548 /*********************************************
5549 * C11 6.10.6 Pragma directive
5550 * # pragma pp-tokens(opt) new-line
5551 * The C preprocessor sometimes leaves pragma directives in
5552 * the preprocessed output. Ignore them.
5553 * Upon return, p is at start of next line.
5555 private void pragmaDirective(const ref Loc loc)
5557 Token n;
5558 scan(&n);
5559 if (n.value == TOK.identifier && n.ident == Id.pack)
5560 return pragmaPack(loc, true);
5561 if (n.value != TOK.endOfLine)
5562 skipToNextLine();
5565 /*********
5566 * # pragma pack
5567 * https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Structure_002dPacking-Pragmas.html
5568 * https://docs.microsoft.com/en-us/cpp/preprocessor/pack
5569 * Scanner is on the `pack`
5570 * Params:
5571 * startloc = location to use for error messages
5572 * useScan = use scan() to retrieve next token, instead of nextToken()
5574 private void pragmaPack(const ref Loc startloc, bool useScan)
5576 const loc = startloc;
5578 /* Pull tokens from scan() or nextToken()
5580 void scan(Token* t)
5582 if (useScan)
5584 Lexer.scan(t);
5586 else
5588 nextToken();
5589 *t = token;
5593 Token n;
5594 scan(&n);
5595 if (n.value != TOK.leftParenthesis)
5597 error(loc, "left parenthesis expected to follow `#pragma pack`");
5598 if (n.value != TOK.endOfLine)
5599 skipToNextLine();
5600 return;
5603 void closingParen()
5605 if (n.value != TOK.rightParenthesis)
5607 error(loc, "right parenthesis expected to close `#pragma pack(`");
5609 if (n.value != TOK.endOfLine)
5610 skipToNextLine();
5613 void setPackAlign(ref const Token t)
5615 const n = t.unsvalue;
5616 if (n < 1 || n & (n - 1) || ushort.max < n)
5617 error(loc, "pack must be an integer positive power of 2, not 0x%llx", cast(ulong)n);
5618 packalign.set(cast(uint)n);
5619 packalign.setPack(true);
5622 scan(&n);
5624 if (!records)
5626 records = new Array!Identifier;
5627 packs = new Array!structalign_t;
5630 /* # pragma pack ( show )
5632 if (n.value == TOK.identifier && n.ident == Id.show)
5634 if (packalign.isDefault())
5635 eSink.warning(startloc, "current pack attribute is default");
5636 else
5637 eSink.warning(startloc, "current pack attribute is %d", packalign.get());
5638 scan(&n);
5639 return closingParen();
5641 /* # pragma pack ( push )
5642 * # pragma pack ( push , identifier )
5643 * # pragma pack ( push , integer )
5644 * # pragma pack ( push , identifier , integer )
5646 if (n.value == TOK.identifier && n.ident == Id.push)
5648 scan(&n);
5649 Identifier record = null;
5650 if (n.value == TOK.comma)
5652 scan(&n);
5653 if (n.value == TOK.identifier)
5655 record = n.ident;
5656 scan(&n);
5657 if (n.value == TOK.comma)
5659 scan(&n);
5660 if (n.value == TOK.int32Literal)
5662 setPackAlign(n);
5663 scan(&n);
5665 else
5666 error(loc, "alignment value expected, not `%s`", n.toChars());
5669 else if (n.value == TOK.int32Literal)
5671 setPackAlign(n);
5672 scan(&n);
5674 else
5675 error(loc, "alignment value expected, not `%s`", n.toChars());
5677 this.records.push(record);
5678 this.packs.push(packalign);
5679 return closingParen();
5681 /* # pragma pack ( pop )
5682 * # pragma pack ( pop PopList )
5683 * PopList :
5684 * , IdentifierOrInteger
5685 * , IdentifierOrInteger PopList
5686 * IdentifierOrInteger:
5687 * identifier
5688 * integer
5690 if (n.value == TOK.identifier && n.ident == Id.pop)
5692 scan(&n);
5693 size_t len = this.records.length;
5694 if (n.value == TOK.rightParenthesis) // #pragma pack ( pop )
5696 if (len == 0) // nothing to pop
5697 return closingParen();
5699 this.records.setDim(len - 1);
5700 this.packs.setDim(len - 1);
5701 if (len == 1) // stack is now empty
5702 packalign.setDefault();
5703 else
5704 packalign = (*this.packs)[len - 1];
5705 return closingParen();
5707 while (n.value == TOK.comma) // #pragma pack ( pop ,
5709 scan(&n);
5710 if (n.value == TOK.identifier)
5712 /* pragma pack(pop, identifier
5713 * Pop until identifier is found, pop that one too, and set
5714 * alignment to the new top of the stack.
5715 * If identifier is not found, do nothing.
5717 for ( ; len; --len)
5719 if ((*this.records)[len - 1] == n.ident)
5721 this.records.setDim(len - 1);
5722 this.packs.setDim(len - 1);
5723 if (len > 1)
5724 packalign = (*this.packs)[len - 2];
5725 else
5726 packalign.setDefault(); // stack empty, use default
5727 break;
5730 scan(&n);
5732 else if (n.value == TOK.int32Literal)
5734 setPackAlign(n);
5735 scan(&n);
5737 else
5739 error(loc, "identifier or alignment value expected following `#pragma pack(pop,` not `%s`", n.toChars());
5740 scan(&n);
5743 return closingParen();
5745 /* # pragma pack ( integer )
5746 * Sets alignment to integer
5748 if (n.value == TOK.int32Literal)
5750 setPackAlign(n);
5751 scan(&n);
5752 return closingParen();
5754 /* # pragma pack ( )
5755 * Sets alignment to default
5757 if (n.value == TOK.rightParenthesis)
5759 packalign.setDefault();
5760 return closingParen();
5763 error(loc, "unrecognized `#pragma pack(%s)`", n.toChars());
5764 if (n.value != TOK.endOfLine)
5765 skipToNextLine();
5770 /******************************************************************************/
5771 /********************************* #define Parser *****************************/
5775 * Go through the #define's in the defines buffer and see what we can convert
5776 * to Dsymbols, which are then appended to symbols[]
5778 void addDefines()
5780 if (!defines || defines.length < 10) // minimum length of a #define line
5781 return;
5782 OutBuffer* buf = defines;
5783 defines = null; // prevent skipToNextLine() and parseSpecialTokenSequence()
5784 // from appending to slice[]
5785 const length = buf.length;
5786 buf.writeByte(0);
5787 auto slice = buf.peekChars()[0 .. length];
5788 resetDefineLines(slice); // reset lexer
5790 const(char)* endp = &slice[length - 7];
5792 size_t[void*] defineTab; // hash table of #define's turned into Symbol's
5793 // indexed by Identifier, returns index into symbols[]
5794 // The memory for this is leaked
5796 void addVar(AST.VarDeclaration v)
5798 //printf("addVar() %s\n", v.toChars());
5799 v.isCmacro(true); // mark it as coming from a C #define
5800 /* If it's already defined, replace the earlier
5801 * definition
5803 if (size_t* pd = cast(void*)v.ident in defineTab)
5805 //printf("replacing %s\n", v.toChars());
5806 (*symbols)[*pd] = v;
5807 return;
5809 defineTab[cast(void*)v.ident] = symbols.length;
5810 symbols.push(v);
5813 Token n;
5815 while (p < endp)
5817 if (p[0 .. 7] == "#define")
5819 p += 7;
5820 scan(&n);
5821 //printf("%s\n", n.toChars());
5822 if (n.value == TOK.identifier)
5824 auto id = n.ident;
5825 scan(&n);
5827 AST.Type t;
5829 switch (n.value)
5831 case TOK.endOfLine: // #define identifier
5832 nextDefineLine();
5833 continue;
5835 case TOK.int32Literal:
5836 case TOK.charLiteral: t = AST.Type.tint32; goto Linteger;
5837 case TOK.uns32Literal: t = AST.Type.tuns32; goto Linteger;
5838 case TOK.int64Literal: t = AST.Type.tint64; goto Linteger;
5839 case TOK.uns64Literal: t = AST.Type.tuns64; goto Linteger;
5841 Linteger:
5842 const intvalue = n.intvalue;
5843 scan(&n);
5844 if (n.value == TOK.endOfLine)
5846 /* Declare manifest constant:
5847 * enum id = intvalue;
5849 AST.Expression e = new AST.IntegerExp(scanloc, intvalue, t);
5850 auto v = new AST.VarDeclaration(scanloc, t, id, new AST.ExpInitializer(scanloc, e), STC.manifest);
5851 addVar(v);
5852 nextDefineLine();
5853 continue;
5855 break;
5857 case TOK.float32Literal: t = AST.Type.tfloat32; goto Lfloat;
5858 case TOK.float64Literal: t = AST.Type.tfloat64; goto Lfloat;
5859 case TOK.float80Literal: t = AST.Type.tfloat80; goto Lfloat;
5860 case TOK.imaginary32Literal: t = AST.Type.timaginary32; goto Lfloat;
5861 case TOK.imaginary64Literal: t = AST.Type.timaginary64; goto Lfloat;
5862 case TOK.imaginary80Literal: t = AST.Type.timaginary80; goto Lfloat;
5864 Lfloat:
5865 const floatvalue = n.floatvalue;
5866 scan(&n);
5867 if (n.value == TOK.endOfLine)
5869 /* Declare manifest constant:
5870 * enum id = floatvalue;
5872 AST.Expression e = new AST.RealExp(scanloc, floatvalue, t);
5873 auto v = new AST.VarDeclaration(scanloc, t, id, new AST.ExpInitializer(scanloc, e), STC.manifest);
5874 addVar(v);
5875 nextDefineLine();
5876 continue;
5878 break;
5880 case TOK.string_:
5881 const str = n.ustring;
5882 const len = n.len;
5883 const postfix = n.postfix;
5884 scan(&n);
5885 if (n.value == TOK.endOfLine)
5887 /* Declare manifest constant:
5888 * enum id = "string";
5890 AST.Expression e = new AST.StringExp(scanloc, str[0 .. len], len, 1, postfix);
5891 auto v = new AST.VarDeclaration(scanloc, null, id, new AST.ExpInitializer(scanloc, e), STC.manifest);
5892 addVar(v);
5893 nextDefineLine();
5894 continue;
5896 break;
5898 default:
5899 break;
5902 skipToNextLine();
5904 else
5906 scan(&n);
5907 if (n.value != TOK.endOfLine)
5909 skipToNextLine();
5912 nextDefineLine();
5915 defines = buf;