d: Merge upstream dmd, druntime 4c18eed967, phobos d945686a4.
[official-gcc.git] / gcc / d / dmd / cparse.d
blobd183b8227c883224d7b050fe3d267eb8906cc76a
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__:
332 Ldeclaration:
334 cparseDeclaration(LVL.local);
335 if (symbols.length > 1)
337 auto as = new AST.Statements();
338 as.reserve(symbols.length);
339 foreach (d; (*symbols)[])
341 s = new AST.ExpStatement(loc, d);
342 as.push(s);
344 s = new AST.CompoundDeclarationStatement(loc, as);
345 symbols.setDim(0);
347 else if (symbols.length == 1)
349 auto d = (*symbols)[0];
350 s = new AST.ExpStatement(loc, d);
351 symbols.setDim(0);
353 else
354 s = new AST.ExpStatement(loc, cast(AST.Expression)null);
355 if (flags & ParseStatementFlags.scope_)
356 s = new AST.ScopeStatement(loc, s, token.loc);
357 break;
360 case TOK._Static_assert: // _Static_assert ( constant-expression, string-literal ) ;
361 s = new AST.StaticAssertStatement(cparseStaticAssert());
362 break;
364 case TOK.leftCurly:
366 /* C11 6.8.2
367 * compound-statement:
368 * { block-item-list (opt) }
370 * block-item-list:
371 * block-item
372 * block-item-list block-item
374 * block-item:
375 * declaration
376 * statement
378 nextToken();
379 auto statements = new AST.Statements();
380 while (token.value != TOK.rightCurly && token.value != TOK.endOfFile)
382 statements.push(cparseStatement(ParseStatementFlags.curlyScope));
384 if (endPtr)
385 *endPtr = token.ptr;
386 endloc = token.loc;
387 if (pEndloc)
389 *pEndloc = token.loc;
390 pEndloc = null; // don't set it again
392 s = new AST.CompoundStatement(loc, statements);
393 if (flags & (ParseStatementFlags.scope_ | ParseStatementFlags.curlyScope))
394 s = new AST.ScopeStatement(loc, s, token.loc);
395 check(TOK.rightCurly, "compound statement");
396 break;
399 case TOK.while_:
401 nextToken();
402 check(TOK.leftParenthesis);
403 auto condition = cparseExpression();
404 check(TOK.rightParenthesis);
405 Loc endloc;
406 auto _body = cparseStatement(ParseStatementFlags.scope_, null, &endloc);
407 s = new AST.WhileStatement(loc, condition, _body, endloc, null);
408 break;
411 case TOK.semicolon:
412 /* C11 6.8.3 null statement
414 nextToken();
415 s = new AST.ExpStatement(loc, cast(AST.Expression)null);
416 break;
418 case TOK.do_:
420 nextToken();
421 auto _body = cparseStatement(ParseStatementFlags.scope_);
422 check(TOK.while_);
423 check(TOK.leftParenthesis);
424 auto condition = cparseExpression();
425 check(TOK.rightParenthesis);
426 check(TOK.semicolon, "terminating `;` required after do-while statement");
427 s = new AST.DoStatement(loc, _body, condition, token.loc);
428 break;
431 case TOK.for_:
433 AST.Statement _init;
434 AST.Expression condition;
435 AST.Expression increment;
437 nextToken();
438 check(TOK.leftParenthesis);
439 if (token.value == TOK.semicolon)
441 _init = null;
442 nextToken();
444 else
446 _init = cparseStatement(0);
448 if (token.value == TOK.semicolon)
450 condition = null;
451 nextToken();
453 else
455 condition = cparseExpression();
456 check(TOK.semicolon, "`for` condition");
458 if (token.value == TOK.rightParenthesis)
460 increment = null;
461 nextToken();
463 else
465 increment = cparseExpression();
466 check(TOK.rightParenthesis);
468 Loc endloc;
469 auto _body = cparseStatement(ParseStatementFlags.scope_, null, &endloc);
470 s = new AST.ForStatement(loc, _init, condition, increment, _body, endloc);
471 break;
474 case TOK.if_:
476 nextToken();
477 check(TOK.leftParenthesis);
478 auto condition = cparseExpression();
479 check(TOK.rightParenthesis);
480 auto ifbody = cparseStatement(ParseStatementFlags.scope_);
481 AST.Statement elsebody;
482 if (token.value == TOK.else_)
484 nextToken();
485 elsebody = cparseStatement(ParseStatementFlags.scope_);
487 else
488 elsebody = null;
489 if (condition && ifbody)
490 s = new AST.IfStatement(loc, null, condition, ifbody, elsebody, token.loc);
491 else
492 s = null; // don't propagate parsing errors
493 break;
496 case TOK.else_:
497 error("found `else` without a corresponding `if` statement");
498 goto Lerror;
500 case TOK.switch_:
502 nextToken();
503 check(TOK.leftParenthesis);
504 auto condition = cparseExpression();
505 check(TOK.rightParenthesis);
506 auto _body = cparseStatement(ParseStatementFlags.scope_);
507 s = new AST.SwitchStatement(loc, null, condition, _body, false, token.loc);
508 break;
511 case TOK.case_:
514 nextToken();
515 auto exp = cparseAssignExp();
516 AST.Expression expHigh;
517 if (token.value == TOK.dotDotDot)
519 /* Case Ranges https://gcc.gnu.org/onlinedocs/gcc/Case-Ranges.html
521 nextToken();
522 expHigh = cparseAssignExp();
524 check(TOK.colon);
526 if (flags & ParseStatementFlags.curlyScope)
528 auto statements = new AST.Statements();
529 while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
531 auto cur = cparseStatement(ParseStatementFlags.curlyScope);
532 statements.push(cur);
534 // https://issues.dlang.org/show_bug.cgi?id=21739
535 // Stop at the last break s.t. the following non-case statements are
536 // not merged into the current case. This can happen for
537 // case 1: ... break;
538 // debug { case 2: ... }
539 if (cur && cur.isBreakStatement())
540 break;
542 s = new AST.CompoundStatement(loc, statements);
544 else
546 s = cparseStatement(0);
548 s = new AST.ScopeStatement(loc, s, token.loc);
549 if (expHigh)
550 s = new AST.CaseRangeStatement(loc, exp, expHigh, s);
551 else
552 s = new AST.CaseStatement(loc, exp, s);
553 break;
556 case TOK.default_:
558 nextToken();
559 check(TOK.colon);
561 if (flags & ParseStatementFlags.curlyScope)
563 auto statements = new AST.Statements();
564 while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
566 statements.push(cparseStatement(ParseStatementFlags.curlyScope));
568 s = new AST.CompoundStatement(loc, statements);
570 else
571 s = cparseStatement(0);
572 s = new AST.ScopeStatement(loc, s, token.loc);
573 s = new AST.DefaultStatement(loc, s);
574 break;
577 case TOK.return_:
579 /* return ;
580 * return expression ;
582 nextToken();
583 auto exp = token.value == TOK.semicolon ? null : cparseExpression();
584 check(TOK.semicolon, "`return` statement");
585 s = new AST.ReturnStatement(loc, exp);
586 break;
589 case TOK.break_:
590 nextToken();
591 check(TOK.semicolon, "`break` statement");
592 s = new AST.BreakStatement(loc, null);
593 break;
595 case TOK.continue_:
596 nextToken();
597 check(TOK.semicolon, "`continue` statement");
598 s = new AST.ContinueStatement(loc, null);
599 break;
601 case TOK.goto_:
603 Identifier ident;
604 nextToken();
605 if (token.value != TOK.identifier)
607 error("identifier expected following `goto`");
608 ident = null;
610 else
612 ident = token.ident;
613 nextToken();
615 s = new AST.GotoStatement(loc, ident);
616 check(TOK.semicolon, "`goto` statement");
617 break;
620 case TOK.asm_:
621 switch (peekNext())
623 case TOK.goto_:
624 case TOK.inline:
625 case TOK.volatile:
626 case TOK.leftParenthesis:
627 s = cparseGnuAsm();
628 break;
630 default:
631 // ImportC extensions: parse as a D asm block.
632 s = parseAsm(compileEnv.masm);
633 break;
635 break;
637 default:
638 error("found `%s` instead of statement", token.toChars());
639 goto Lerror;
641 Lerror:
642 panic();
643 if (token.value == TOK.semicolon)
644 nextToken();
645 s = null;
646 break;
648 if (pEndloc)
649 *pEndloc = prevloc;
650 symbols = symbolsSave;
651 typedefTab.setDim(typedefTabLengthSave);
652 return s;
656 /*******************************************************************************/
657 /********************************* Expression Parser ***************************/
660 /**************
661 * C11 6.5.17
662 * expression:
663 * assignment-expression
664 * expression , assignment-expression
666 AST.Expression cparseExpression()
668 auto loc = token.loc;
670 //printf("cparseExpression() loc = %d\n", loc.linnum);
671 auto e = cparseAssignExp();
672 while (token.value == TOK.comma)
674 nextToken();
675 auto e2 = cparseAssignExp();
676 e = new AST.CommaExp(loc, e, e2, false);
677 loc = token.loc;
679 return e;
683 /*********************
684 * C11 6.5.1
685 * primary-expression:
686 * identifier
687 * constant
688 * string-literal
689 * ( expression )
690 * generic-selection
691 * __builtin_va_arg(assign_expression, type)
693 AST.Expression cparsePrimaryExp()
695 AST.Expression e;
696 const loc = token.loc;
698 //printf("parsePrimaryExp(): loc = %d\n", loc.linnum);
699 switch (token.value)
701 case TOK.identifier:
702 const id = token.ident.toString();
703 if (id.length > 2 && id[0] == '_' && id[1] == '_') // leading double underscore
705 if (token.ident is Id.__func__)
707 addFuncName = true; // implicitly declare __func__
709 else if (token.ident is Id.builtin_va_arg)
711 e = cparseBuiltin_va_arg();
712 break;
714 else
715 importBuiltins = true; // probably one of those compiler extensions
717 e = new AST.IdentifierExp(loc, token.ident);
718 nextToken();
719 break;
721 case TOK.charLiteral:
722 case TOK.int32Literal:
723 e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint32);
724 nextToken();
725 break;
727 case TOK.uns32Literal:
728 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns32);
729 nextToken();
730 break;
732 case TOK.int64Literal:
733 e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint64);
734 nextToken();
735 break;
737 case TOK.uns64Literal:
738 e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns64);
739 nextToken();
740 break;
742 case TOK.float32Literal:
743 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat32);
744 nextToken();
745 break;
747 case TOK.float64Literal:
748 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat64);
749 nextToken();
750 break;
752 case TOK.float80Literal:
753 e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat80);
754 nextToken();
755 break;
757 case TOK.imaginary32Literal:
758 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary32);
759 nextToken();
760 break;
762 case TOK.imaginary64Literal:
763 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary64);
764 nextToken();
765 break;
767 case TOK.imaginary80Literal:
768 e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary80);
769 nextToken();
770 break;
772 case TOK.string_:
774 // cat adjacent strings
775 auto s = token.ustring;
776 auto len = token.len;
777 auto postfix = token.postfix;
778 while (1)
780 nextToken();
781 if (token.value == TOK.string_)
783 if (token.postfix)
785 if (token.postfix != postfix)
786 error(token.loc, "mismatched string literal postfixes `'%c'` and `'%c'`", postfix, token.postfix);
787 postfix = token.postfix;
790 const len1 = len;
791 const len2 = token.len;
792 len = len1 + len2;
793 auto s2 = cast(char*)mem.xmalloc_noscan(len * char.sizeof);
794 memcpy(s2, s, len1 * char.sizeof);
795 memcpy(s2 + len1, token.ustring, len2 * char.sizeof);
796 s = s2;
798 else
799 break;
801 e = new AST.StringExp(loc, s[0 .. len], len, 1, postfix);
802 break;
805 case TOK.leftParenthesis:
806 nextToken();
807 if (token.value == TOK.leftCurly)
808 e = cparseStatementExpression(); // gcc extension
809 else
810 e = cparseExpression();
811 check(TOK.rightParenthesis);
812 break;
814 case TOK._Generic:
815 e = cparseGenericSelection();
816 break;
818 case TOK._assert: // __check(assign-exp) extension
819 nextToken();
820 check(TOK.leftParenthesis, "`__check`");
821 e = parseAssignExp();
822 check(TOK.rightParenthesis);
823 e = new AST.AssertExp(loc, e, null);
824 break;
826 default:
827 error("expression expected, not `%s`", token.toChars());
828 // Anything for e, as long as it's not NULL
829 e = new AST.IntegerExp(loc, 0, AST.Type.tint32);
830 nextToken();
831 break;
833 return e;
836 /*********************************
837 * C11 6.5.2
838 * postfix-expression:
839 * primary-expression
840 * postfix-expression [ expression ]
841 * postfix-expression ( argument-expression-list (opt) )
842 * postfix-expression . identifier
843 * postfix-expression -> identifier
844 * postfix-expression ++
845 * postfix-expression --
846 * ( type-name ) { initializer-list }
847 * ( type-name ) { initializer-list , }
849 * argument-expression-list:
850 * assignment-expression
851 * argument-expression-list , assignment-expression
853 private AST.Expression cparsePostfixExp(AST.Expression e)
855 e = cparsePrimaryExp();
856 return cparsePostfixOperators(e);
859 /********************************
860 * C11 6.5.2
861 * Parse a series of operators for a postfix expression after already parsing
862 * a primary-expression or compound literal expression.
863 * Params:
864 * e = parsed primary or compound literal expression
865 * Returns:
866 * parsed postfix expression
868 private AST.Expression cparsePostfixOperators(AST.Expression e)
870 while (1)
872 const loc = token.loc;
873 switch (token.value)
875 case TOK.dot:
876 nextToken();
877 if (token.value == TOK.identifier)
879 Identifier id = token.ident;
880 e = new AST.DotIdExp(loc, e, id);
881 break;
883 error("identifier expected following `.`, not `%s`", token.toChars());
884 break;
886 case TOK.arrow:
887 nextToken();
888 if (token.value == TOK.identifier)
890 Identifier id = token.ident;
891 auto die = new AST.DotIdExp(loc, e, id);
892 die.arrow = true;
893 e = die;
894 break;
896 error("identifier expected following `->`, not `%s`", token.toChars());
897 break;
899 case TOK.plusPlus:
900 e = new AST.PostExp(EXP.plusPlus, loc, e);
901 break;
903 case TOK.minusMinus:
904 e = new AST.PostExp(EXP.minusMinus, loc, e);
905 break;
907 case TOK.leftParenthesis:
908 e = new AST.CallExp(loc, e, cparseArguments());
909 continue;
911 case TOK.leftBracket:
913 // array dereferences:
914 // array[index]
915 AST.Expression index;
916 auto arguments = new AST.Expressions();
918 inBrackets++;
919 nextToken();
920 index = cparseAssignExp();
921 arguments.push(index);
922 check(TOK.rightBracket);
923 inBrackets--;
924 e = new AST.ArrayExp(loc, e, arguments);
925 continue;
927 default:
928 return e;
930 nextToken();
934 /************************
935 * C11 6.5.3
936 * unary-expression:
937 * postfix-expression
938 * ++ unary-expression
939 * -- unary-expression
940 * unary-operator cast-expression
941 * sizeof unary-expression
942 * sizeof ( type-name )
943 * _Alignof ( type-name )
945 * unary-operator:
946 * & * + - ~ !
948 private AST.Expression cparseUnaryExp()
950 AST.Expression e;
951 const loc = token.loc;
953 switch (token.value)
955 case TOK.plusPlus:
956 nextToken();
957 // Parse `++` as an unary operator so that cast expressions only give
958 // an error for being non-lvalues.
959 e = cparseCastExp();
960 e = new AST.PreExp(EXP.prePlusPlus, loc, e);
961 break;
963 case TOK.minusMinus:
964 nextToken();
965 // Parse `--` as an unary operator, same as prefix increment.
966 e = cparseCastExp();
967 e = new AST.PreExp(EXP.preMinusMinus, loc, e);
968 break;
970 case TOK.and:
971 nextToken();
972 e = cparseCastExp();
973 e = new AST.AddrExp(loc, e);
974 break;
976 case TOK.mul:
977 nextToken();
978 e = cparseCastExp();
979 e = new AST.PtrExp(loc, e);
980 break;
982 case TOK.min:
983 nextToken();
984 e = cparseCastExp();
985 e = new AST.NegExp(loc, e);
986 break;
988 case TOK.add:
989 nextToken();
990 e = cparseCastExp();
991 e = new AST.UAddExp(loc, e);
992 break;
994 case TOK.not:
995 nextToken();
996 e = cparseCastExp();
997 e = new AST.NotExp(loc, e);
998 break;
1000 case TOK.tilde:
1001 nextToken();
1002 e = cparseCastExp();
1003 e = new AST.ComExp(loc, e);
1004 break;
1006 case TOK.sizeof_:
1008 nextToken();
1009 if (token.value == TOK.leftParenthesis)
1011 auto tk = peek(&token);
1012 if (isTypeName(tk))
1014 /* Expression may be either be requesting the sizeof a type-name
1015 * or a compound literal, which requires checking whether
1016 * the next token is leftCurly
1018 nextToken();
1019 auto t = cparseTypeName();
1020 check(TOK.rightParenthesis);
1021 if (token.value == TOK.leftCurly)
1023 // ( type-name ) { initializer-list }
1024 auto ci = cparseInitializer();
1025 e = new AST.CompoundLiteralExp(loc, t, ci);
1026 e = cparsePostfixOperators(e);
1028 else
1030 // ( type-name )
1031 e = new AST.TypeExp(loc, t);
1034 else
1036 // must be an expression
1037 e = cparseUnaryExp();
1040 else
1042 //C11 6.5.3
1043 e = cparseUnaryExp();
1046 e = new AST.DotIdExp(loc, e, Id.__sizeof);
1047 break;
1050 case TOK._Alignof:
1052 nextToken();
1053 check(TOK.leftParenthesis);
1054 auto t = cparseTypeName();
1055 check(TOK.rightParenthesis);
1056 e = new AST.TypeExp(loc, t);
1057 e = new AST.DotIdExp(loc, e, Id.__xalignof);
1058 break;
1061 default:
1062 e = cparsePostfixExp(e);
1063 break;
1065 assert(e);
1066 return e;
1069 /**************
1070 * C11 6.5.4
1071 * cast-expression
1072 * unary-expression
1073 * ( type-name ) cast-expression
1075 private AST.Expression cparseCastExp()
1077 if (token.value == TOK.leftParenthesis)
1079 //printf("cparseCastExp()\n");
1080 auto tk = peek(&token);
1081 bool iscast;
1082 bool isexp;
1083 if (tk.value == TOK.identifier)
1085 iscast = isTypedef(tk.ident);
1086 isexp = !iscast;
1088 if (isexp)
1090 // ( identifier ) is an expression
1091 return cparseUnaryExp();
1094 // If ( type-name )
1095 auto pt = &token;
1097 if (isCastExpression(pt))
1099 // Expression may be either a cast or a compound literal, which
1100 // requires checking whether the next token is leftCurly
1101 const loc = token.loc;
1102 nextToken();
1103 auto t = cparseTypeName();
1104 check(TOK.rightParenthesis);
1105 pt = &token;
1107 if (token.value == TOK.leftCurly)
1109 // C11 6.5.2.5 ( type-name ) { initializer-list }
1110 auto ci = cparseInitializer();
1111 auto ce = new AST.CompoundLiteralExp(loc, t, ci);
1112 return cparsePostfixOperators(ce);
1115 if (iscast)
1117 // ( type-name ) cast-expression
1118 auto ce = cparseCastExp();
1119 return new AST.CastExp(loc, ce, t);
1122 if (t.isTypeIdentifier() &&
1123 isexp &&
1124 token.value == TOK.leftParenthesis &&
1125 !isCastExpression(pt))
1127 /* (t)(...)... might be a cast expression or a function call,
1128 * with different grammars: a cast would be cparseCastExp(),
1129 * a function call would be cparsePostfixExp(CallExp(cparseArguments())).
1130 * We can't know until t is known. So, parse it as a function call
1131 * and let semantic() rewrite the AST as a CastExp if it turns out
1132 * to be a type.
1134 auto ie = new AST.IdentifierExp(loc, t.isTypeIdentifier().ident);
1135 ie.parens = true; // let semantic know it might be a CastExp
1136 AST.Expression e = new AST.CallExp(loc, ie, cparseArguments());
1137 return cparsePostfixOperators(e);
1140 // ( type-name ) cast-expression
1141 auto ce = cparseCastExp();
1142 return new AST.CastExp(loc, ce, t);
1145 return cparseUnaryExp();
1148 /**************
1149 * C11 6.5.5
1150 * multiplicative-expression
1151 * cast-expression
1152 * multiplicative-expression * cast-expression
1153 * multiplicative-expression / cast-expression
1154 * multiplicative-expression % cast-expression
1156 private AST.Expression cparseMulExp()
1158 const loc = token.loc;
1159 auto e = cparseCastExp();
1161 while (1)
1163 switch (token.value)
1165 case TOK.mul:
1166 nextToken();
1167 auto e2 = cparseCastExp();
1168 e = new AST.MulExp(loc, e, e2);
1169 continue;
1171 case TOK.div:
1172 nextToken();
1173 auto e2 = cparseCastExp();
1174 e = new AST.DivExp(loc, e, e2);
1175 continue;
1177 case TOK.mod:
1178 nextToken();
1179 auto e2 = cparseCastExp();
1180 e = new AST.ModExp(loc, e, e2);
1181 continue;
1183 default:
1184 break;
1186 break;
1188 return e;
1191 /**************
1192 * C11 6.5.6
1193 * additive-expression
1194 * multiplicative-expression
1195 * additive-expression + multiplicative-expression
1196 * additive-expression - multiplicative-expression
1198 private AST.Expression cparseAddExp()
1200 const loc = token.loc;
1201 auto e = cparseMulExp();
1203 while (1)
1205 switch (token.value)
1207 case TOK.add:
1208 nextToken();
1209 auto e2 = cparseMulExp();
1210 e = new AST.AddExp(loc, e, e2);
1211 continue;
1213 case TOK.min:
1214 nextToken();
1215 auto e2 = cparseMulExp();
1216 e = new AST.MinExp(loc, e, e2);
1217 continue;
1219 default:
1220 break;
1222 break;
1224 return e;
1227 /**************
1228 * C11 6.5.7
1229 * shift-expression
1230 * additive-expression
1231 * shift-expression << additive-expression
1232 * shift-expression >> additive-expression
1234 private AST.Expression cparseShiftExp()
1236 const loc = token.loc;
1237 auto e = cparseAddExp();
1239 while (1)
1241 switch (token.value)
1243 case TOK.leftShift:
1244 nextToken();
1245 auto e2 = cparseAddExp();
1246 e = new AST.ShlExp(loc, e, e2);
1247 continue;
1249 case TOK.rightShift:
1250 nextToken();
1251 auto e2 = cparseAddExp();
1252 e = new AST.ShrExp(loc, e, e2);
1253 continue;
1255 default:
1256 break;
1258 break;
1260 return e;
1263 /**************
1264 * C11 6.5.8
1265 * relational-expression
1266 * shift-expression
1267 * relational-expression < shift-expression
1268 * relational-expression > shift-expression
1269 * relational-expression <= shift-expression
1270 * relational-expression >= shift-expression
1272 private AST.Expression cparseRelationalExp()
1274 const loc = token.loc;
1276 auto e = cparseShiftExp();
1278 EXP op = EXP.reserved;
1279 switch (token.value)
1281 case TOK.lessThan: op = EXP.lessThan; goto Lcmp;
1282 case TOK.lessOrEqual: op = EXP.lessOrEqual; goto Lcmp;
1283 case TOK.greaterThan: op = EXP.greaterThan; goto Lcmp;
1284 case TOK.greaterOrEqual: op = EXP.greaterOrEqual; goto Lcmp;
1285 Lcmp:
1286 nextToken();
1287 auto e2 = cparseShiftExp();
1288 e = new AST.CmpExp(op, loc, e, e2);
1289 break;
1291 default:
1292 break;
1294 return e;
1297 /**************
1298 * C11 6.5.9
1299 * equality-expression
1300 * relational-expression
1301 * equality-expression == relational-expression
1302 * equality-expression != relational-expression
1304 private AST.Expression cparseEqualityExp()
1306 const loc = token.loc;
1308 auto e = cparseRelationalExp();
1310 EXP op = EXP.reserved;
1311 switch (token.value)
1313 case TOK.equal: op = EXP.equal; goto Lequal;
1314 case TOK.notEqual: op = EXP.notEqual; goto Lequal;
1315 Lequal:
1316 nextToken();
1317 auto e2 = cparseRelationalExp();
1318 e = new AST.EqualExp(op, loc, e, e2);
1319 break;
1321 default:
1322 break;
1324 return e;
1327 /**************
1328 * C11 6.5.10
1329 * AND-expression
1330 * equality-expression
1331 * AND-expression & equality-expression
1333 private AST.Expression cparseAndExp()
1335 Loc loc = token.loc;
1336 auto e = cparseEqualityExp();
1337 while (token.value == TOK.and)
1339 nextToken();
1340 auto e2 = cparseEqualityExp();
1341 e = new AST.AndExp(loc, e, e2);
1342 loc = token.loc;
1344 return e;
1347 /**************
1348 * C11 6.5.11
1349 * exclusive-OR-expression
1350 * AND-expression
1351 * exclusive-OR-expression ^ AND-expression
1353 private AST.Expression cparseXorExp()
1355 const loc = token.loc;
1357 auto e = cparseAndExp();
1358 while (token.value == TOK.xor)
1360 nextToken();
1361 auto e2 = cparseAndExp();
1362 e = new AST.XorExp(loc, e, e2);
1364 return e;
1367 /**************
1368 * C11 6.5.12
1369 * inclusive-OR-expression
1370 * exclusive-OR-expression
1371 * inclusive-OR-expression | exclusive-OR-expression
1373 private AST.Expression cparseOrExp()
1375 const loc = token.loc;
1377 auto e = cparseXorExp();
1378 while (token.value == TOK.or)
1380 nextToken();
1381 auto e2 = cparseXorExp();
1382 e = new AST.OrExp(loc, e, e2);
1384 return e;
1387 /**************
1388 * C11 6.5.13
1389 * logical-AND-expression
1390 * inclusive-OR-expression
1391 * logical-AND-expression && inclusive-OR-expression
1393 private AST.Expression cparseAndAndExp()
1395 const loc = token.loc;
1397 auto e = cparseOrExp();
1398 while (token.value == TOK.andAnd)
1400 nextToken();
1401 auto e2 = cparseOrExp();
1402 e = new AST.LogicalExp(loc, EXP.andAnd, e, e2);
1404 return e;
1407 /**************
1408 * C11 6.5.14
1409 * logical-OR-expression
1410 * logical-AND-expression
1411 * logical-OR-expression || logical-AND-expression
1413 private AST.Expression cparseOrOrExp()
1415 const loc = token.loc;
1417 auto e = cparseAndAndExp();
1418 while (token.value == TOK.orOr)
1420 nextToken();
1421 auto e2 = cparseAndAndExp();
1422 e = new AST.LogicalExp(loc, EXP.orOr, e, e2);
1424 return e;
1427 /**************
1428 * C11 6.5.15
1429 * conditional-expression:
1430 * logical-OR-expression
1431 * logical-OR-expression ? expression : conditional-expression
1433 private AST.Expression cparseCondExp()
1435 const loc = token.loc;
1437 auto e = cparseOrOrExp();
1438 if (token.value == TOK.question)
1440 nextToken();
1441 auto e1 = cparseExpression();
1442 check(TOK.colon);
1443 auto e2 = cparseCondExp();
1444 e = new AST.CondExp(loc, e, e1, e2);
1446 return e;
1449 /**************
1450 * C11 6.5.16
1451 * assignment-expression:
1452 * conditional-expression
1453 * unary-expression assignment-operator assignment-expression
1455 * assignment-operator:
1456 * = *= /= %= += -= <<= >>= &= ^= |=
1458 AST.Expression cparseAssignExp()
1460 AST.Expression e;
1461 e = cparseCondExp(); // constrain it to being unary-expression in semantic pass
1462 if (e is null)
1463 return e;
1465 const loc = token.loc;
1466 switch (token.value)
1468 case TOK.assign:
1469 nextToken();
1470 auto e2 = cparseAssignExp();
1471 e = new AST.AssignExp(loc, e, e2);
1472 break;
1474 case TOK.addAssign:
1475 nextToken();
1476 auto e2 = cparseAssignExp();
1477 e = new AST.AddAssignExp(loc, e, e2);
1478 break;
1480 case TOK.minAssign:
1481 nextToken();
1482 auto e2 = cparseAssignExp();
1483 e = new AST.MinAssignExp(loc, e, e2);
1484 break;
1486 case TOK.mulAssign:
1487 nextToken();
1488 auto e2 = cparseAssignExp();
1489 e = new AST.MulAssignExp(loc, e, e2);
1490 break;
1492 case TOK.divAssign:
1493 nextToken();
1494 auto e2 = cparseAssignExp();
1495 e = new AST.DivAssignExp(loc, e, e2);
1496 break;
1498 case TOK.modAssign:
1499 nextToken();
1500 auto e2 = cparseAssignExp();
1501 e = new AST.ModAssignExp(loc, e, e2);
1502 break;
1504 case TOK.andAssign:
1505 nextToken();
1506 auto e2 = cparseAssignExp();
1507 e = new AST.AndAssignExp(loc, e, e2);
1508 break;
1510 case TOK.orAssign:
1511 nextToken();
1512 auto e2 = cparseAssignExp();
1513 e = new AST.OrAssignExp(loc, e, e2);
1514 break;
1516 case TOK.xorAssign:
1517 nextToken();
1518 auto e2 = cparseAssignExp();
1519 e = new AST.XorAssignExp(loc, e, e2);
1520 break;
1522 case TOK.leftShiftAssign:
1523 nextToken();
1524 auto e2 = cparseAssignExp();
1525 e = new AST.ShlAssignExp(loc, e, e2);
1526 break;
1528 case TOK.rightShiftAssign:
1529 nextToken();
1530 auto e2 = cparseAssignExp();
1531 e = new AST.ShrAssignExp(loc, e, e2);
1532 break;
1534 default:
1535 break;
1538 return e;
1541 /***********************
1542 * C11 6.5.1.1
1543 * _Generic ( assignment-expression, generic-assoc-list )
1545 * generic-assoc-list:
1546 * generic-association
1547 * generic-assoc-list generic-association
1549 * generic-association:
1550 * type-name : assignment-expression
1551 * default : assignment-expression
1553 private AST.Expression cparseGenericSelection()
1555 const loc = token.loc;
1556 nextToken();
1557 check(TOK.leftParenthesis);
1558 auto cntlExp = cparseAssignExp();
1559 check(TOK.comma);
1560 auto types = new AST.Types();
1561 auto exps = new AST.Expressions();
1562 bool sawDefault;
1563 while (1)
1565 AST.Type t;
1566 if (token.value == TOK.default_)
1568 nextToken();
1569 if (sawDefault)
1570 error("only one `default` allowed in generic-assoc-list");
1571 sawDefault = true;
1572 t = null;
1574 else
1575 t = cparseTypeName();
1576 types.push(t);
1578 check(TOK.colon);
1579 auto e = cparseAssignExp();
1580 exps.push(e);
1581 if (token.value == TOK.rightParenthesis || token.value == TOK.endOfFile)
1582 break;
1583 check(TOK.comma);
1585 check(TOK.rightParenthesis);
1586 return new AST.GenericExp(loc, cntlExp, types, exps);
1589 /***********************
1590 * C11 6.6 Constant expressions
1591 * constant-expression:
1592 * conditional-expression
1594 private AST.Expression cparseConstantExp()
1596 return cparseAssignExp();
1599 /*****************************
1600 * gcc extension:
1601 * type __builtin_va_arg(assign-expression, type)
1602 * Rewrite as `va_arg` template from `core.stdc.stdarg`:
1603 * va_arg!(type)(assign-expression);
1604 * Lexer is on `__builtin_va_arg`
1606 private AST.Expression cparseBuiltin_va_arg()
1608 importBuiltins = true; // need core.stdc.stdarg
1610 nextToken();
1611 check(TOK.leftParenthesis);
1613 auto arguments = new AST.Expressions();
1614 auto arg = cparseAssignExp();
1615 arguments.push(arg);
1617 check(TOK.comma);
1619 auto t = cparseTypeName();
1620 auto tiargs = new AST.Objects();
1621 tiargs.push(t);
1623 const loc = loc;
1624 auto ti = new AST.TemplateInstance(loc, Id.va_arg, tiargs);
1625 auto tie = new AST.ScopeExp(loc, ti);
1627 AST.Expression e = new AST.CallExp(loc, tie, arguments);
1629 check(TOK.rightParenthesis);
1630 return e;
1633 /*****************************
1634 * gcc extension: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
1635 * Represent as a function literal, then call the function literal.
1636 * Parser is on opening curly brace.
1638 private AST.Expression cparseStatementExpression()
1640 AST.ParameterList parameterList;
1641 StorageClass stc = 0;
1642 const loc = token.loc;
1643 typedefTab.push(null);
1644 auto fbody = cparseStatement(ParseStatementFlags.scope_);
1645 typedefTab.pop(); // end of function scope
1647 // Rewrite last ExpStatement (if there is one) as a ReturnStatement
1648 auto ss = fbody.isScopeStatement();
1649 auto cs = ss.statement.isCompoundStatement();
1650 assert(cs);
1651 if (const len = (*cs.statements).length)
1653 auto s = (*cs.statements)[len - 1];
1654 if (auto es = s.isExpStatement())
1655 (*cs.statements)[len - 1] = new AST.ReturnStatement(es.loc, es.exp);
1658 auto tf = new AST.TypeFunction(parameterList, null, LINK.d, stc);
1659 auto fd = new AST.FuncLiteralDeclaration(loc, token.loc, tf, TOK.delegate_, null, null, 0);
1660 fd.fbody = fbody;
1662 auto fe = new AST.FuncExp(loc, fd);
1663 auto args = new AST.Expressions();
1664 auto e = new AST.CallExp(loc, fe, args); // call the function literal
1665 return e;
1669 /********************************************************************************/
1670 /********************************* Declaration Parser ***************************/
1673 /*************************************
1674 * C11 6.7
1675 * declaration:
1676 * declaration-specifiers init-declarator-list (opt) ;
1677 * static_assert-declaration
1679 * init-declarator-list:
1680 * init-declarator
1681 * init-declarator-list , init-declarator
1683 * init-declarator:
1684 * declarator
1685 * declarator = initializer
1687 * Params:
1688 * level = declaration context
1690 void cparseDeclaration(LVL level)
1692 //printf("cparseDeclaration(level = %d)\n", level);
1693 if (token.value == TOK._Static_assert)
1695 auto s = cparseStaticAssert();
1696 symbols.push(s);
1697 return;
1700 if (token.value == TOK.__pragma)
1702 uupragmaDirective(scanloc);
1703 return;
1706 if (token.value == TOK._import) // import declaration extension
1708 auto a = parseImport();
1709 if (a && a.length)
1710 symbols.append(a);
1711 return;
1714 const typedefTabLengthSave = typedefTab.length;
1715 auto symbolsSave = symbols;
1716 Specifier specifier;
1717 specifier.packalign = this.packalign;
1718 auto tspec = cparseDeclarationSpecifiers(level, specifier);
1720 AST.Dsymbol declareTag(AST.TypeTag tt, ref Specifier specifier)
1722 /* `struct tag;` and `struct tag { ... };`
1723 * always result in a declaration in the current scope
1725 auto stag = (tt.tok == TOK.struct_) ? new AST.StructDeclaration(tt.loc, tt.id, false) :
1726 (tt.tok == TOK.union_) ? new AST.UnionDeclaration(tt.loc, tt.id) :
1727 new AST.EnumDeclaration(tt.loc, tt.id, tt.base);
1728 if (!tt.packalign.isUnknown())
1730 // saw `struct __declspec(align(N)) Tag ...`
1731 auto st = stag.isStructDeclaration();
1732 st.alignment = tt.packalign;
1734 stag.members = tt.members;
1735 tt.members = null;
1736 if (!symbols)
1737 symbols = new AST.Dsymbols();
1738 auto stags = applySpecifier(stag, specifier);
1739 symbols.push(stags);
1740 return stags;
1743 /* If a declarator does not follow, it is unnamed
1745 if (token.value == TOK.semicolon)
1747 if (!tspec)
1749 nextToken();
1750 return; // accept empty declaration as an extension
1753 if (auto ti = tspec.isTypeIdentifier())
1755 // C11 6.7.2-2
1756 error("type-specifier missing for declaration of `%s`", ti.ident.toChars());
1757 nextToken();
1758 return;
1761 nextToken();
1762 auto tt = tspec.isTypeTag();
1763 if (!tt ||
1764 !tt.id && (tt.tok == TOK.struct_ || tt.tok == TOK.union_))
1765 return; // legal but meaningless empty declaration, ignore it
1767 auto stags = declareTag(tt, specifier);
1769 if (0 && tt.tok == TOK.enum_) // C11 proscribes enums with no members, but we allow it
1771 if (!tt.members)
1772 error(tt.loc, "`enum %s` has no members", stags.toChars());
1774 return;
1777 if (!tspec)
1779 error("no type for declarator before `%s`", token.toChars());
1780 panic();
1781 nextToken();
1782 return;
1785 if (tspec && specifier.mod & MOD.xconst)
1787 tspec = toConst(tspec);
1788 specifier.mod &= ~MOD.xnone; // 'used' it
1791 void scanPastSemicolon()
1793 while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
1794 nextToken();
1795 nextToken();
1798 if (token.value == TOK.assign && tspec && tspec.isTypeIdentifier())
1800 /* C11 6.7.2-2
1801 * Special check for `const b = 1;` because some compilers allow it
1803 error("type-specifier omitted for declaration of `%s`", tspec.isTypeIdentifier().ident.toChars());
1804 return scanPastSemicolon();
1807 bool first = true;
1808 while (1)
1810 Identifier id;
1811 AST.StringExp asmName;
1812 auto dt = cparseDeclarator(DTR.xdirect, tspec, id, specifier);
1813 if (!dt)
1815 panic();
1816 nextToken();
1817 break; // error recovery
1820 /* GNU Extensions
1821 * init-declarator:
1822 * declarator simple-asm-expr (opt) gnu-attributes (opt)
1823 * declarator simple-asm-expr (opt) gnu-attributes (opt) = initializer
1825 switch (token.value)
1827 case TOK.assign:
1828 case TOK.comma:
1829 case TOK.semicolon:
1830 case TOK.asm_:
1831 case TOK.__attribute__:
1832 if (token.value == TOK.asm_)
1833 asmName = cparseGnuAsmLabel();
1834 if (token.value == TOK.__attribute__)
1836 cparseGnuAttributes(specifier);
1837 if (token.value == TOK.leftCurly)
1838 break; // function definition
1840 /* This is a data definition, there cannot now be a
1841 * function definition.
1843 first = false;
1844 break;
1846 default:
1847 break;
1850 if (specifier.alignExps && dt.isTypeFunction())
1851 error("no alignment-specifier for function declaration"); // C11 6.7.5-2
1852 if (specifier.alignExps && specifier.scw == SCW.xregister)
1853 error("no alignment-specifier for `register` storage class"); // C11 6.7.5-2
1855 /* C11 6.9.1 Function Definitions
1856 * function-definition:
1857 * declaration-specifiers declarator declaration-list (opt) compound-statement
1859 * declaration-list:
1860 * declaration
1861 * declaration-list declaration
1863 auto t = &token;
1864 if (first && // first declarator
1865 id &&
1866 dt.isTypeFunction() && // function type not inherited from a typedef
1867 isDeclarationList(t) && // optional declaration-list
1868 level == LVL.global && // function definitions only at global scope
1869 t.value == TOK.leftCurly) // start of compound-statement
1871 auto s = cparseFunctionDefinition(id, dt.isTypeFunction(), specifier);
1872 typedefTab.setDim(typedefTabLengthSave);
1873 symbols = symbolsSave;
1874 symbols.push(s);
1875 return;
1877 AST.Dsymbol s = null;
1878 typedefTab.setDim(typedefTabLengthSave);
1879 symbols = symbolsSave;
1880 if (!symbols)
1881 symbols = new AST.Dsymbols; // lazilly create it
1883 if (level != LVL.global && !tspec && !specifier.scw && !specifier.mod)
1884 error("declaration-specifier-seq required");
1885 else if (specifier.scw == SCW.xtypedef)
1887 if (token.value == TOK.assign)
1888 error("no initializer for typedef declaration");
1889 if (specifier.alignExps)
1890 error("no alignment-specifier for typedef declaration"); // C11 6.7.5-2
1892 bool isalias = true;
1893 if (auto ts = dt.isTypeStruct())
1895 if (ts.sym.isAnonymous())
1897 // This is a typedef for an anonymous struct-or-union.
1898 // Directly set the ident for the struct-or-union.
1899 ts.sym.ident = id;
1900 isalias = false;
1903 else if (auto te = dt.isTypeEnum())
1905 if (te.sym.isAnonymous())
1907 // This is a typedef for an anonymous enum.
1908 te.sym.ident = id;
1909 isalias = false;
1912 else if (auto tt = dt.isTypeTag())
1914 if (tt.id || tt.tok == TOK.enum_)
1916 if (!tt.id && id)
1917 /* This applies for enums declared as
1918 * typedef enum {A} E;
1920 tt.id = id;
1921 Specifier spec;
1922 declareTag(tt, spec);
1925 if (isalias)
1926 s = new AST.AliasDeclaration(token.loc, id, dt);
1927 insertTypedefToTypedefTab(id, dt); // remember typedefs
1929 else if (id)
1931 if (auto tt = dt.isTypeTag())
1933 if (tt.members && (tt.id || tt.tok == TOK.enum_))
1935 Specifier spec;
1936 declareTag(tt, spec);
1940 if (level == LVL.prototype)
1941 break; // declared later as Parameter, not VarDeclaration
1943 if (dt.ty == AST.Tvoid)
1944 error("`void` has no value");
1946 AST.Initializer initializer;
1947 bool hasInitializer;
1948 if (token.value == TOK.assign)
1950 nextToken();
1951 hasInitializer = true;
1952 initializer = cparseInitializer();
1954 // declare the symbol
1955 assert(id);
1957 if (isFunctionTypedef(dt))
1959 if (hasInitializer)
1960 error("no initializer for function declaration");
1961 if (specifier.scw & SCW.x_Thread_local)
1962 error("functions cannot be `_Thread_local`"); // C11 6.7.1-4
1963 auto fd = new AST.FuncDeclaration(token.loc, Loc.initial, id, specifiersToSTC(level, specifier), dt, specifier.noreturn);
1964 specifiersToFuncDeclaration(fd, specifier);
1965 s = fd;
1967 else
1969 // Give non-extern variables an implicit void initializer
1970 // if one has not been explicitly set.
1971 if (!hasInitializer &&
1972 !(specifier.scw & (SCW.xextern | SCW.xstatic | SCW.x_Thread_local) || level == LVL.global))
1973 initializer = new AST.VoidInitializer(token.loc);
1974 auto vd = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(level, specifier));
1975 specifiersToVarDeclaration(vd, specifier);
1976 s = vd;
1978 if (level != LVL.global)
1979 insertIdToTypedefTab(id); // non-typedef declarations can hide typedefs in outer scopes
1981 if (s !is null)
1983 // Saw `asm("name")` in the function, type, or variable definition.
1984 // This is equivalent to `pragma(mangle, "name")` in D
1985 if (asmName)
1988 https://issues.dlang.org/show_bug.cgi?id=23012
1989 Ideally this would be translated to a pragma(mangle)
1990 decl. This is not possible because ImportC symbols are
1991 (currently) merged before semantic analysis is performed,
1992 so the pragma(mangle) never effects any change on the declarations
1993 it pertains too.
1995 Writing to mangleOverride directly avoids this, and is possible
1996 because C only a StringExp is allowed unlike a full fat pragma(mangle)
1997 which is more liberal.
1999 if (auto p = s.isDeclaration())
2001 auto str = asmName.peekString();
2002 p.mangleOverride = str;
2003 // p.adFlags |= AST.VarDeclaration.nounderscore;
2004 p.adFlags |= 4; // cannot get above line to compile on Ubuntu
2007 s = applySpecifier(s, specifier);
2008 if (level == LVL.local)
2010 // Wrap the declaration in `extern (C) { declaration }`
2011 // Necessary for function pointers, but harmless to apply to all.
2012 auto decls = new AST.Dsymbols(1);
2013 (*decls)[0] = s;
2014 s = new AST.LinkDeclaration(s.loc, linkage, decls);
2016 symbols.push(s);
2018 first = false;
2020 switch (token.value)
2022 case TOK.identifier:
2023 if (s)
2025 error(token.loc, "missing comma or semicolon after declaration of `%s`, found `%s` instead", s.toChars(), token.toChars());
2026 goto Lend;
2028 goto default;
2030 case TOK.semicolon:
2031 nextToken();
2032 return;
2034 case TOK.comma:
2035 if (!symbolsSave)
2036 symbolsSave = symbols;
2037 nextToken();
2038 break;
2040 default:
2041 error("`=`, `;` or `,` expected to end declaration instead of `%s`", token.toChars());
2042 Lend:
2043 return scanPastSemicolon();
2048 /***************************************
2049 * C11 Function Definitions
2050 * function-definition
2051 * declaration-specifiers declarator declaration-list (opt) compound-statement
2053 * declaration-list:
2054 * declaration
2055 * declaration-list declaration
2057 * It's already been parsed up to the declaration-list (opt).
2058 * Pick it up from there.
2059 * Params:
2060 * id = function identifier
2061 * ft = function type
2062 * specifier = function specifiers
2063 * Returns:
2064 * Dsymbol for the function
2066 AST.Dsymbol cparseFunctionDefinition(Identifier id, AST.TypeFunction ft, ref Specifier specifier)
2068 /* Start function scope
2070 typedefTab.push(null);
2072 if (token.value != TOK.leftCurly) // if not start of a compound-statement
2074 // Do declaration-list
2077 cparseDeclaration(LVL.parameter);
2078 } while (token.value != TOK.leftCurly);
2080 /* Since there were declarations, the parameter-list must have been
2081 * an identifier-list.
2083 ft.parameterList.hasIdentifierList = true; // semantic needs to know to adjust parameter types
2084 auto pl = ft.parameterList;
2085 if (pl.varargs != AST.VarArg.none && pl.length)
2086 error("function identifier-list cannot end with `...`");
2087 ft.parameterList.varargs = AST.VarArg.KRvariadic; // but C11 allows extra arguments
2088 auto plLength = pl.length;
2089 if (symbols.length != plLength)
2090 error(token.loc, "%d identifiers does not match %d declarations", cast(int)plLength, cast(int)symbols.length);
2092 /* Transfer the types and storage classes from symbols[] to pl[]
2094 foreach (i; 0 .. plLength)
2096 auto p = pl[i]; // yes, quadratic
2098 // Convert typedef-identifier to identifier
2099 if (p.type)
2101 if (auto t = p.type.isTypeIdentifier())
2103 p.ident = t.ident;
2104 p.type = null;
2108 if (p.type || !(p.storageClass & STC.parameter))
2109 error("storage class and type are not allowed in identifier-list");
2110 foreach (s; (*symbols)[]) // yes, quadratic
2112 auto ad = s.isAttribDeclaration();
2113 if (ad)
2114 s = (*ad.decl)[0]; // AlignDeclaration wrapping the declaration
2116 auto d = s.isDeclaration();
2117 if (d && p.ident == d.ident && d.type)
2119 p.type = d.type;
2120 p.storageClass = d.storage_class;
2121 d.type = null; // don't reuse
2122 break;
2125 if (!p.type)
2127 error("no declaration for identifier `%s`", p.ident.toChars());
2128 p.type = AST.Type.terror;
2133 addFuncName = false; // gets set to true if somebody references __func__ in this function
2134 const locFunc = token.loc;
2136 auto body = cparseStatement(ParseStatementFlags.curly); // don't start a new scope; continue with parameter scope
2137 typedefTab.pop(); // end of function scope
2139 auto fd = new AST.FuncDeclaration(locFunc, prevloc, id, specifiersToSTC(LVL.global, specifier), ft, specifier.noreturn);
2140 specifiersToFuncDeclaration(fd, specifier);
2142 if (addFuncName)
2144 auto s = createFuncName(locFunc, id);
2145 body = new AST.CompoundStatement(locFunc, s, body);
2147 fd.fbody = body;
2149 // TODO add `symbols` to the function's local symbol table `sc2` in FuncDeclaration::semantic3()
2151 return fd;
2154 /***************************************
2155 * C11 Initialization
2156 * initializer:
2157 * assignment-expression
2158 * { initializer-list }
2159 * { initializer-list , }
2161 * initializer-list:
2162 * designation (opt) initializer
2163 * initializer-list , designation (opt) initializer
2165 * designation:
2166 * designator-list =
2168 * designator-list:
2169 * designator
2170 * designator-list designator
2172 * designator:
2173 * [ constant-expression ]
2174 * . identifier
2175 * Returns:
2176 * initializer
2178 AST.Initializer cparseInitializer()
2180 if (token.value != TOK.leftCurly)
2182 auto ae = cparseAssignExp(); // assignment-expression
2183 return new AST.ExpInitializer(token.loc, ae);
2185 nextToken();
2186 const loc = token.loc;
2188 /* Collect one or more `designation (opt) initializer`
2189 * into ci.initializerList, but lazily create ci
2191 AST.CInitializer ci;
2192 while (1)
2194 /* There can be 0 or more designators preceding an initializer.
2195 * Collect them in desigInit
2197 AST.DesigInit desigInit;
2198 while (1)
2200 if (token.value == TOK.leftBracket) // [ constant-expression ]
2202 nextToken();
2203 auto e = cparseConstantExp();
2204 check(TOK.rightBracket);
2205 if (!desigInit.designatorList)
2206 desigInit.designatorList = new AST.Designators;
2207 desigInit.designatorList.push(AST.Designator(e));
2209 else if (token.value == TOK.dot) // . identifier
2211 nextToken();
2212 if (token.value != TOK.identifier)
2214 error("identifier expected following `.` designator");
2215 break;
2217 if (!desigInit.designatorList)
2218 desigInit.designatorList = new AST.Designators;
2219 desigInit.designatorList.push(AST.Designator(token.ident));
2220 nextToken();
2222 else
2224 if (desigInit.designatorList)
2225 check(TOK.assign);
2226 break;
2230 desigInit.initializer = cparseInitializer();
2231 if (!ci)
2232 ci = new AST.CInitializer(loc);
2233 ci.initializerList.push(desigInit);
2234 if (token.value == TOK.comma)
2236 nextToken();
2237 if (token.value != TOK.rightCurly)
2238 continue;
2240 break;
2242 check(TOK.rightCurly);
2243 //printf("ci: %s\n", ci.toChars());
2244 return ci;
2247 /*************************************
2248 * C11 6.7
2249 * declaration-specifier:
2250 * storage-class-specifier declaration-specifiers (opt)
2251 * type-specifier declaration-specifiers (opt)
2252 * type-qualifier declaration-specifiers (opt)
2253 * function-specifier declaration-specifiers (opt)
2254 * alignment-specifier declaration-specifiers (opt)
2255 * Params:
2256 * level = declaration context
2257 * specifier = specifiers in and out
2258 * Returns:
2259 * resulting type, null if not specified
2261 private AST.Type cparseDeclarationSpecifiers(LVL level, ref Specifier specifier)
2263 enum TKW : uint
2265 xnone = 0,
2266 xchar = 1,
2267 xsigned = 2,
2268 xunsigned = 4,
2269 xshort = 8,
2270 xint = 0x10,
2271 xlong = 0x20,
2272 xllong = 0x40,
2273 xfloat = 0x80,
2274 xdouble = 0x100,
2275 xldouble = 0x200,
2276 xtag = 0x400,
2277 xident = 0x800,
2278 xvoid = 0x1000,
2279 xbool = 0x4000,
2280 ximaginary = 0x8000,
2281 xcomplex = 0x10000,
2282 x_Atomic = 0x20000,
2283 xint128 = 0x40000,
2286 AST.Type t;
2287 Loc loc;
2288 //printf("parseDeclarationSpecifiers()\n");
2290 TKW tkw;
2291 SCW scw = specifier.scw & SCW.xtypedef;
2292 MOD mod;
2293 Identifier id;
2294 Identifier previd;
2296 Lwhile:
2297 while (1)
2299 //printf("token %s\n", token.toChars());
2300 TKW tkwx;
2301 SCW scwx;
2302 MOD modx;
2303 switch (token.value)
2305 // Storage class specifiers
2306 case TOK.static_: scwx = SCW.xstatic; break;
2307 case TOK.extern_: scwx = SCW.xextern; break;
2308 case TOK.auto_: scwx = SCW.xauto; break;
2309 case TOK.register: scwx = SCW.xregister; break;
2310 case TOK.typedef_: scwx = SCW.xtypedef; break;
2311 case TOK.inline: scwx = SCW.xinline; break;
2312 case TOK._Noreturn: scwx = SCW.x_Noreturn; break;
2313 case TOK.__thread:
2314 case TOK._Thread_local: scwx = SCW.x_Thread_local; break;
2316 // Type qualifiers
2317 case TOK.const_: modx = MOD.xconst; break;
2318 case TOK.volatile: modx = MOD.xvolatile; break;
2319 case TOK.restrict: modx = MOD.xrestrict; break;
2320 case TOK.__stdcall: modx = MOD.x__stdcall; break;
2322 // Type specifiers
2323 case TOK.char_: tkwx = TKW.xchar; break;
2324 case TOK.signed: tkwx = TKW.xsigned; break;
2325 case TOK.unsigned: tkwx = TKW.xunsigned; break;
2326 case TOK.int16: tkwx = TKW.xshort; break;
2327 case TOK.int32: tkwx = TKW.xint; break;
2328 case TOK.int64: tkwx = TKW.xlong; break;
2329 case TOK.__int128: tkwx = TKW.xint128; break;
2330 case TOK.float32: tkwx = TKW.xfloat; break;
2331 case TOK.float64: tkwx = TKW.xdouble; break;
2332 case TOK.void_: tkwx = TKW.xvoid; break;
2333 case TOK._Bool: tkwx = TKW.xbool; break;
2334 case TOK._Imaginary: tkwx = TKW.ximaginary; break;
2335 case TOK._Complex: tkwx = TKW.xcomplex; break;
2337 case TOK.identifier:
2338 tkwx = TKW.xident;
2339 id = token.ident;
2340 break;
2342 case TOK.struct_:
2343 case TOK.union_:
2345 const structOrUnion = token.value;
2346 const sloc = token.loc;
2347 nextToken();
2349 Specifier tagSpecifier;
2351 /* GNU Extensions
2352 * struct-or-union-specifier:
2353 * struct-or-union gnu-attributes (opt) identifier (opt) { struct-declaration-list } gnu-attributes (opt)
2354 * struct-or-union gnu-attribute (opt) identifier
2356 while (1)
2358 if (token.value == TOK.__attribute__)
2359 cparseGnuAttributes(tagSpecifier);
2360 else if (token.value == TOK.__declspec)
2361 cparseDeclspec(tagSpecifier);
2362 else if (token.value == TOK.__pragma)
2363 uupragmaDirective(sloc);
2364 else
2365 break;
2367 t = cparseStruct(sloc, structOrUnion, tagSpecifier.packalign, symbols);
2368 tkwx = TKW.xtag;
2369 break;
2372 case TOK.enum_:
2373 t = cparseEnum(symbols);
2374 tkwx = TKW.xtag;
2375 break;
2377 case TOK._Atomic:
2379 // C11 6.7.2.4
2380 // type-specifier if followed by `( type-name )`
2381 auto tk = peek(&token);
2382 if (tk.value == TOK.leftParenthesis)
2384 tk = peek(tk);
2385 if (isTypeName(tk) && tk.value == TOK.rightParenthesis)
2387 nextToken();
2388 nextToken();
2389 t = cparseTypeName();
2390 tkwx = TKW.x_Atomic;
2391 break;
2394 // C11 6.7.3 type-qualifier if not
2395 modx = MOD.x_Atomic;
2396 break;
2399 case TOK._Alignas:
2401 /* C11 6.7.5
2402 * _Alignas ( type-name )
2403 * _Alignas ( constant-expression )
2406 if (level & (LVL.parameter | LVL.prototype))
2407 error("no alignment-specifier for parameters"); // C11 6.7.5-2
2409 nextToken();
2410 check(TOK.leftParenthesis);
2411 AST.Expression exp;
2412 auto tk = &token;
2413 if (isTypeName(tk)) // _Alignas ( type-name )
2415 auto talign = cparseTypeName();
2416 /* Convert type to expression: `talign.alignof`
2418 auto e = new AST.TypeExp(loc, talign);
2419 exp = new AST.DotIdExp(loc, e, Id.__xalignof);
2421 else // _Alignas ( constant-expression )
2423 exp = cparseConstantExp();
2426 if (!specifier.alignExps)
2427 specifier.alignExps = new AST.Expressions(0);
2428 specifier.alignExps.push(exp);
2430 check(TOK.rightParenthesis);
2431 break;
2434 case TOK.__attribute__:
2436 /* GNU Extensions
2437 * declaration-specifiers:
2438 * gnu-attributes declaration-specifiers (opt)
2440 cparseGnuAttributes(specifier);
2441 break;
2444 case TOK.__declspec:
2446 /* Microsoft extension
2448 cparseDeclspec(specifier);
2449 break;
2452 case TOK.typeof_:
2454 nextToken();
2455 check(TOK.leftParenthesis);
2457 auto tk = &token;
2458 AST.Expression e;
2459 if (isTypeName(tk))
2460 e = new AST.TypeExp(loc, cparseTypeName());
2461 else
2462 e = cparseExpression();
2463 t = new AST.TypeTypeof(loc, e);
2465 if(token.value == TOK.rightParenthesis)
2466 nextToken();
2467 else
2469 t = AST.Type.terror;
2470 error("`typeof` operator expects an expression or type name in parentheses");
2472 // skipParens et. al expect to be on the opening parenthesis
2473 int parens;
2474 loop: while(1)
2476 switch(token.value)
2478 case TOK.leftParenthesis:
2479 parens++;
2480 break;
2481 case TOK.rightParenthesis:
2482 parens--;
2483 if(parens < 0)
2484 goto case;
2485 break;
2486 case TOK.endOfFile:
2487 break loop;
2488 default:
2490 nextToken();
2494 tkwx = TKW.xtag;
2495 break;
2498 default:
2499 break Lwhile;
2502 if (tkwx)
2504 if (tkw & TKW.xlong && tkwx & TKW.xlong)
2506 tkw &= ~TKW.xlong;
2507 tkwx = TKW.xllong;
2509 if (tkw && tkwx & TKW.xident)
2511 // 2nd identifier can't be a typedef
2512 break Lwhile; // leave parser on the identifier for the following declarator
2514 else if (tkwx & TKW.xident)
2516 // 1st identifier, save it for TypeIdentifier
2517 previd = id;
2519 if (tkw & TKW.xident && tkwx || // typedef-name followed by type-specifier
2520 tkw & tkwx) // duplicate type-specifiers
2522 error("illegal combination of type specifiers");
2523 tkwx = TKW.init;
2525 tkw |= tkwx;
2526 if (!(tkwx & TKW.xtag)) // if parser already advanced
2527 nextToken();
2528 continue;
2531 if (modx)
2533 mod |= modx;
2534 nextToken();
2535 continue;
2538 if (scwx)
2540 if (scw & scwx)
2541 error("duplicate storage class");
2542 scw |= scwx;
2543 // C11 6.7.1-2 At most one storage-class may be given, except that
2544 // _Thread_local may appear with static or extern.
2545 const scw2 = scw & (SCW.xstatic | SCW.xextern | SCW.xauto | SCW.xregister | SCW.xtypedef);
2546 if (scw2 & (scw2 - 1) ||
2547 scw & (SCW.x_Thread_local) && scw & (SCW.xauto | SCW.xregister | SCW.xtypedef))
2549 error("multiple storage classes in declaration specifiers");
2550 scw &= ~scwx;
2552 if (level == LVL.local &&
2553 scw & (SCW.x_Thread_local) && scw & (SCW.xinline | SCW.x_Noreturn))
2555 error("`inline` and `_Noreturn` function specifiers not allowed for `_Thread_local`");
2556 scw &= ~scwx;
2558 if (level == LVL.local &&
2559 scw & (SCW.x_Thread_local) && !(scw & (SCW.xstatic | SCW.xextern)))
2561 error("`_Thread_local` in block scope must be accompanied with `static` or `extern`"); // C11 6.7.1-3
2562 scw &= ~scwx;
2564 if (level & (LVL.parameter | LVL.prototype) &&
2565 scw & ~SCW.xregister)
2567 error("only `register` storage class allowed for function parameters");
2568 scw &= ~scwx;
2570 if (level == LVL.global &&
2571 scw & (SCW.xauto | SCW.xregister))
2573 error("`auto` and `register` storage class not allowed for global");
2574 scw &= ~scwx;
2576 nextToken();
2577 continue;
2581 specifier.scw = scw;
2582 specifier.mod = mod;
2584 // Convert TKW bits to type t
2585 switch (tkw)
2587 case TKW.xnone: t = null; break;
2589 case TKW.xchar: t = AST.Type.tchar; break;
2590 case TKW.xsigned | TKW.xchar: t = AST.Type.tint8; break;
2591 case TKW.xunsigned | TKW.xchar: t = AST.Type.tuns8; break;
2593 case TKW.xshort:
2594 case TKW.xsigned | TKW.xshort:
2595 case TKW.xsigned | TKW.xshort | TKW.xint:
2596 case TKW.xshort | TKW.xint: t = integerTypeForSize(shortsize); break;
2598 case TKW.xunsigned | TKW.xshort | TKW.xint:
2599 case TKW.xunsigned | TKW.xshort: t = unsignedTypeForSize(shortsize); break;
2601 case TKW.xint:
2602 case TKW.xsigned:
2603 case TKW.xsigned | TKW.xint: t = integerTypeForSize(intsize); break;
2605 case TKW.xunsigned:
2606 case TKW.xunsigned | TKW.xint: t = unsignedTypeForSize(intsize); break;
2608 case TKW.xlong:
2609 case TKW.xsigned | TKW.xlong:
2610 case TKW.xsigned | TKW.xlong | TKW.xint:
2611 case TKW.xlong | TKW.xint: t = integerTypeForSize(longsize); break;
2613 case TKW.xunsigned | TKW.xlong | TKW.xint:
2614 case TKW.xunsigned | TKW.xlong: t = unsignedTypeForSize(longsize); break;
2616 case TKW.xllong:
2617 case TKW.xsigned | TKW.xllong:
2618 case TKW.xsigned | TKW.xllong | TKW.xint:
2619 case TKW.xllong | TKW.xint: t = integerTypeForSize(long_longsize); break;
2621 case TKW.xunsigned | TKW.xllong | TKW.xint:
2622 case TKW.xunsigned | TKW.xllong: t = unsignedTypeForSize(long_longsize); break;
2624 case TKW.xint128:
2625 case TKW.xsigned | TKW.xint128: t = integerTypeForSize(16); break;
2627 case TKW.xunsigned | TKW.xint128: t = unsignedTypeForSize(16); break;
2629 case TKW.xvoid: t = AST.Type.tvoid; break;
2630 case TKW.xbool: t = boolsize == 1 ? AST.Type.tbool : integerTypeForSize(boolsize); break;
2632 case TKW.xfloat: t = AST.Type.tfloat32; break;
2633 case TKW.xdouble: t = AST.Type.tfloat64; break;
2634 case TKW.xlong | TKW.xdouble: t = realType(RTFlags.realfloat); break;
2636 case TKW.ximaginary | TKW.xfloat: t = AST.Type.timaginary32; break;
2637 case TKW.ximaginary | TKW.xdouble: t = AST.Type.timaginary64; break;
2638 case TKW.ximaginary | TKW.xlong | TKW.xdouble: t = realType(RTFlags.imaginary); break;
2640 case TKW.xcomplex | TKW.xfloat: t = AST.Type.tcomplex32; break;
2641 case TKW.xcomplex | TKW.xdouble: t = AST.Type.tcomplex64; break;
2642 case TKW.xcomplex | TKW.xlong | TKW.xdouble: t = realType(RTFlags.complex); break;
2644 case TKW.xident:
2646 const idx = previd.toString();
2647 if (idx.length > 2 && idx[0] == '_' && idx[1] == '_') // leading double underscore
2648 importBuiltins = true; // probably one of those compiler extensions
2649 t = null;
2651 /* Punch through to what the typedef is, to support things like:
2652 * typedef T* T;
2654 auto pt = lookupTypedef(previd);
2655 if (pt && *pt) // if previd is a known typedef
2656 t = *pt;
2658 if (!t)
2659 t = new AST.TypeIdentifier(loc, previd);
2660 break;
2663 case TKW.xtag:
2664 case TKW.x_Atomic: // no atomics for you
2665 break; // t is already set
2667 default:
2668 error("illegal type combination");
2669 t = AST.Type.terror;
2670 break;
2673 return t;
2676 /********************************
2677 * C11 6.7.6
2678 * Parse a declarator (including function definitions).
2679 * declarator:
2680 * pointer (opt) direct-declarator
2682 * direct-declarator :
2683 * identifier
2684 * ( declarator )
2685 * direct-declarator [ type-qualifier-list (opt) assignment-expression (opt) ]
2686 * direct-declarator [ static type-qualifier-list (opt) assignment-expression ]
2687 * direct-declarator [ type-qualifier-list static assignment-expression (opt) ]
2688 * direct-declarator [ type-qualifier-list (opt) * ]
2689 * direct-declarator ( parameter-type-list )
2690 * direct-declarator ( identifier-list (opt) )
2692 * pointer :
2693 * * type-qualifier-list (opt)
2694 * * type-qualifier-list (opt) pointer
2696 * type-qualifier-list :
2697 * type-qualifier
2698 * type-qualifier-list type-qualifier
2700 * parameter-type-list :
2701 * parameter-list
2702 * parameter-list , ...
2704 * parameter-list :
2705 * parameter-declaration
2706 * parameter-list , parameter-declaration
2708 * parameter-declaration :
2709 * declaration-specifiers declarator
2710 * declaration-specifiers abstract-declarator (opt)
2712 * identifier-list :
2713 * identifier
2714 * identifier-list , identifier
2716 * Params:
2717 * declarator = declarator kind
2718 * tbase = base type to start with
2719 * pident = set to Identifier if there is one, null if not
2720 * specifier = specifiers in and out
2721 * Returns:
2722 * type declared. If a TypeFunction is returned, this.symbols is the
2723 * symbol table for the parameter-type-list, which will contain any
2724 * declared struct, union or enum tags.
2726 private AST.Type cparseDeclarator(DTR declarator, AST.Type tbase,
2727 out Identifier pident, ref Specifier specifier)
2729 //printf("cparseDeclarator(%d, %p)\n", declarator, t);
2730 AST.Types constTypes; // all the Types that will need `const` applied to them
2732 /* Insert tx -> t into
2733 * ts -> ... -> t
2734 * so that
2735 * ts -> ... -> tx -> t
2737 static void insertTx(ref AST.Type ts, AST.Type tx, AST.Type t)
2739 AST.Type* pt;
2740 for (pt = &ts; *pt != t; pt = &(cast(AST.TypeNext)*pt).next)
2743 *pt = tx;
2746 AST.Type parseDecl(AST.Type t)
2748 AST.Type ts;
2749 while (1)
2751 switch (token.value)
2753 case TOK.identifier: // identifier
2754 //printf("identifier %s\n", token.ident.toChars());
2755 if (declarator == DTR.xabstract)
2756 error("identifier not allowed in abstract-declarator");
2757 pident = token.ident;
2758 ts = t;
2759 nextToken();
2760 break;
2762 case TOK.leftParenthesis: // ( declarator )
2763 /* like: T (*fp)();
2764 * T ((*fp))();
2766 nextToken();
2768 if (token.value == TOK.__stdcall) // T (__stdcall*fp)();
2770 specifier.mod |= MOD.x__stdcall;
2771 nextToken();
2774 ts = parseDecl(t);
2775 check(TOK.rightParenthesis);
2776 break;
2778 case TOK.mul: // pointer
2779 t = new AST.TypePointer(t);
2780 nextToken();
2781 // add post fixes const/volatile/restrict/_Atomic
2782 const mod = cparseTypeQualifierList();
2783 if (mod & MOD.xconst)
2784 constTypes.push(t);
2785 if (token.value == TOK.__attribute__)
2786 cparseGnuAttributes(specifier);
2787 continue;
2789 default:
2790 if (declarator == DTR.xdirect)
2792 if (!t || t.isTypeIdentifier())
2794 // const arr[1];
2795 error("no type-specifier for declarator");
2796 t = AST.Type.tint32;
2798 else
2799 error("identifier or `(` expected"); // )
2800 panic();
2802 ts = t;
2803 break;
2805 break;
2808 // parse DeclaratorSuffixes
2809 while (1)
2811 switch (token.value)
2813 case TOK.leftBracket:
2815 // post [] syntax, pick up any leading type qualifiers, `static` and `*`
2816 AST.Type ta;
2817 nextToken();
2819 auto mod = cparseTypeQualifierList(); // const/volatile/restrict/_Atomic
2821 bool isStatic;
2822 bool isVLA;
2823 if (token.value == TOK.static_)
2825 isStatic = true; // `static`
2826 nextToken();
2827 if (!mod) // type qualifiers after `static`
2828 mod = cparseTypeQualifierList();
2830 else if (token.value == TOK.mul)
2832 if (peekNext() == TOK.rightBracket)
2834 isVLA = true; // `*`
2835 nextToken();
2839 if (isStatic || token.value != TOK.rightBracket)
2841 //printf("It's a static array\n");
2842 AST.Expression e = cparseAssignExp(); // [ expression ]
2843 ta = new AST.TypeSArray(t, e);
2845 else
2847 /* C11 6.7.6.2-4 An [ ] array is an incomplete array type
2849 ta = new AST.TypeSArray(t);
2851 check(TOK.rightBracket);
2853 // Issue errors for unsupported types.
2854 if (isVLA) // C11 6.7.6.2
2856 error("variable length arrays are not supported");
2858 if (isStatic) // C11 6.7.6.3
2860 error("static array parameters are not supported");
2862 if (declarator != DTR.xparameter)
2864 /* C11 6.7.6.2-4: '*' can only be used with function prototype scope.
2866 if (isVLA)
2867 error("variable length array used outside of function prototype");
2868 /* C11 6.7.6.2-1: type qualifiers and 'static' shall only appear
2869 * in a declaration of a function parameter with an array type.
2871 if (isStatic || mod)
2872 error("static or type qualifier used outside of function prototype");
2874 if (ts.isTypeSArray() || ts.isTypeDArray())
2876 /* C11 6.7.6.2-1: type qualifiers and 'static' shall only appear
2877 * in the outermost array type derivation.
2879 if (isStatic || mod)
2880 error("static or type qualifier used in non-outermost array type derivation");
2881 /* C11 6.7.6.2-1: the element type shall not be an incomplete or
2882 * function type.
2884 if (ta.isTypeSArray() && ta.isTypeSArray().isIncomplete() && !isVLA)
2885 error("array type has incomplete element type `%s`", ta.toChars());
2888 // Apply type qualifiers to the constructed type.
2889 if (mod & MOD.xconst) // ignore the other bits
2890 ta = toConst(ta);
2891 insertTx(ts, ta, t); // ts -> ... -> ta -> t
2892 continue;
2895 case TOK.leftParenthesis:
2897 // New symbol table for parameter-list
2898 auto symbolsSave = this.symbols;
2899 this.symbols = null;
2901 auto parameterList = cparseParameterList();
2902 const lkg = specifier.mod & MOD.x__stdcall ? LINK.windows : linkage;
2903 StorageClass stc = specifier._nothrow ? STC.nothrow_ : 0;
2904 if (specifier._pure)
2905 stc |= STC.pure_;
2906 AST.Type tf = new AST.TypeFunction(parameterList, t, lkg, stc);
2907 // tf = tf.addSTC(storageClass); // TODO
2908 insertTx(ts, tf, t); // ts -> ... -> tf -> t
2910 if (ts != tf)
2911 this.symbols = symbolsSave;
2912 break;
2915 default:
2916 break;
2918 break;
2920 return ts;
2923 auto t = parseDecl(tbase);
2925 if (specifier.vector_size)
2927 auto length = new AST.IntegerExp(token.loc, specifier.vector_size / tbase.size(), AST.Type.tuns32);
2928 auto tsa = new AST.TypeSArray(tbase, length);
2929 AST.Type tv = new AST.TypeVector(tsa);
2930 specifier.vector_size = 0; // used it up
2932 insertTx(t, tv, tbase); // replace tbase with tv
2935 /* Because const is transitive, cannot assemble types from
2936 * fragments. Instead, types to be annotated with const are put
2937 * in constTypes[], and a bottom up scan of t is done to apply
2938 * const
2940 if (constTypes.length)
2942 AST.Type constApply(AST.Type t)
2944 if (t.nextOf())
2946 auto tn = cast(AST.TypeNext)t; // t.nextOf() should return a ref instead of this
2947 tn.next = constApply(tn.next);
2949 foreach (tc; constTypes[])
2951 if (tc is t)
2953 return toConst(t);
2956 return t;
2959 if (declarator == DTR.xparameter &&
2960 t.isTypePointer())
2962 /* Because there are instances in .h files of "const pointer to mutable",
2963 * skip applying transitive `const`
2964 * https://issues.dlang.org/show_bug.cgi?id=22534
2966 auto tn = cast(AST.TypeNext)t;
2967 tn.next = constApply(tn.next);
2969 else
2970 t = constApply(t);
2973 //printf("result: %s\n", t.toChars());
2974 return t;
2977 /******************************
2978 * C11 6.7.3
2979 * type-qualifier:
2980 * const
2981 * restrict
2982 * volatile
2983 * _Atomic
2984 * __stdcall
2986 MOD cparseTypeQualifierList()
2988 MOD mod;
2989 while (1)
2991 switch (token.value)
2993 case TOK.const_: mod |= MOD.xconst; break;
2994 case TOK.volatile: mod |= MOD.xvolatile; break;
2995 case TOK.restrict: mod |= MOD.xrestrict; break;
2996 case TOK._Atomic: mod |= MOD.x_Atomic; break;
2997 case TOK.__stdcall: mod |= MOD.x__stdcall; break;
2999 default:
3000 return mod;
3002 nextToken();
3006 /***********************************
3007 * C11 6.7.7
3009 AST.Type cparseTypeName()
3011 Specifier specifier;
3012 specifier.packalign.setDefault();
3013 auto tspec = cparseSpecifierQualifierList(LVL.global, specifier);
3014 if (!tspec)
3016 error("type-specifier is missing");
3017 tspec = AST.Type.tint32;
3019 if (tspec && specifier.mod & MOD.xconst)
3021 tspec = toConst(tspec);
3022 specifier.mod = MOD.xnone; // 'used' it
3024 Identifier id;
3025 return cparseDeclarator(DTR.xabstract, tspec, id, specifier);
3028 /***********************************
3029 * C11 6.7.2.1
3030 * specifier-qualifier-list:
3031 * type-specifier specifier-qualifier-list (opt)
3032 * type-qualifier specifier-qualifier-list (opt)
3033 * Params:
3034 * level = declaration context
3035 * specifier = specifiers in and out
3036 * Returns:
3037 * resulting type, null if not specified
3039 AST.Type cparseSpecifierQualifierList(LVL level, ref Specifier specifier)
3041 auto t = cparseDeclarationSpecifiers(level, specifier);
3042 if (specifier.scw)
3043 error("storage class not allowed in specifier-qualified-list");
3044 return t;
3047 /***********************************
3048 * C11 6.7.6.3
3049 * ( parameter-type-list )
3050 * ( identifier-list (opt) )
3052 AST.ParameterList cparseParameterList()
3054 auto parameters = new AST.Parameters();
3055 AST.VarArg varargs = AST.VarArg.none;
3056 StorageClass varargsStc;
3058 check(TOK.leftParenthesis);
3059 if (token.value == TOK.void_ && peekNext() == TOK.rightParenthesis) // func(void)
3061 nextToken();
3062 nextToken();
3063 return AST.ParameterList(parameters, varargs, varargsStc);
3066 if (token.value == TOK.rightParenthesis) // func()
3068 nextToken();
3069 return AST.ParameterList(parameters, AST.VarArg.KRvariadic, varargsStc);
3072 /* Create function prototype scope
3074 typedefTab.push(null);
3076 AST.ParameterList finish()
3078 typedefTab.pop();
3079 return AST.ParameterList(parameters, varargs, varargsStc);
3082 /* The check for identifier-list comes later,
3083 * when doing the trailing declaration-list (opt)
3085 while (1)
3087 if (token.value == TOK.rightParenthesis)
3088 break;
3089 if (token.value == TOK.dotDotDot)
3091 if (parameters.length == 0) // func(...)
3092 error("named parameter required before `...`");
3093 importBuiltins = true; // will need __va_list_tag
3094 varargs = AST.VarArg.variadic; // C-style variadics
3095 nextToken();
3096 check(TOK.rightParenthesis);
3097 return finish();
3100 Specifier specifier;
3101 specifier.packalign.setDefault();
3102 auto tspec = cparseDeclarationSpecifiers(LVL.prototype, specifier);
3103 if (!tspec)
3105 error("no type-specifier for parameter");
3106 tspec = AST.Type.tint32;
3109 if (specifier.mod & MOD.xconst)
3111 if ((token.value == TOK.rightParenthesis || token.value == TOK.comma) &&
3112 tspec.isTypeIdentifier())
3113 error("type-specifier omitted for parameter `%s`", tspec.isTypeIdentifier().ident.toChars());
3115 tspec = toConst(tspec);
3116 specifier.mod = MOD.xnone; // 'used' it
3119 Identifier id;
3120 const paramLoc = token.loc;
3121 auto t = cparseDeclarator(DTR.xparameter, tspec, id, specifier);
3122 if (token.value == TOK.__attribute__)
3123 cparseGnuAttributes(specifier);
3124 if (specifier.mod & MOD.xconst)
3125 t = toConst(t);
3126 auto param = new AST.Parameter(paramLoc, specifiersToSTC(LVL.parameter, specifier),
3127 t, id, null, null);
3128 parameters.push(param);
3129 if (token.value == TOK.rightParenthesis || token.value == TOK.endOfFile)
3130 break;
3131 check(TOK.comma);
3133 check(TOK.rightParenthesis);
3134 return finish();
3137 /***********************************
3138 * C11 6.7.10
3139 * _Static_assert ( constant-expression , string-literal ) ;
3141 private AST.StaticAssert cparseStaticAssert()
3143 const loc = token.loc;
3145 //printf("cparseStaticAssert()\n");
3146 nextToken();
3147 check(TOK.leftParenthesis);
3148 auto exp = cparseConstantExp();
3149 check(TOK.comma);
3150 if (token.value != TOK.string_)
3151 error("string literal expected");
3152 auto msg = cparsePrimaryExp();
3153 check(TOK.rightParenthesis);
3154 check(TOK.semicolon);
3155 return new AST.StaticAssert(loc, exp, msg);
3158 /*************************
3159 * Collect argument list.
3160 * Parser is on opening parenthesis.
3161 * Returns:
3162 * the arguments
3164 private AST.Expressions* cparseArguments()
3166 nextToken();
3167 auto arguments = new AST.Expressions();
3168 while (token.value != TOK.rightParenthesis && token.value != TOK.endOfFile)
3170 auto arg = cparseAssignExp();
3171 arguments.push(arg);
3172 if (token.value != TOK.comma)
3173 break;
3175 nextToken(); // consume comma
3178 check(TOK.rightParenthesis);
3180 return arguments;
3183 /*************************
3184 * __declspec parser
3185 * https://docs.microsoft.com/en-us/cpp/cpp/declspec
3186 * decl-specifier:
3187 * __declspec ( extended-decl-modifier-seq )
3189 * extended-decl-modifier-seq:
3190 * extended-decl-modifier (opt)
3191 * extended-decl-modifier extended-decl-modifier-seq
3193 * extended-decl-modifier:
3194 * align(number)
3195 * deprecated(depMsg)
3196 * dllimport
3197 * dllexport
3198 * naked
3199 * noinline
3200 * noreturn
3201 * nothrow
3202 * thread
3203 * Params:
3204 * specifier = filled in with the attribute(s)
3206 private void cparseDeclspec(ref Specifier specifier)
3208 //printf("cparseDeclspec()\n");
3209 /* Check for dllexport, dllimport
3210 * Ignore the rest
3212 nextToken(); // move past __declspec
3213 check(TOK.leftParenthesis);
3214 while (1)
3216 if (token.value == TOK.rightParenthesis)
3218 nextToken();
3219 break;
3221 else if (token.value == TOK.endOfFile)
3222 break;
3223 else if (token.value == TOK.identifier)
3225 if (token.ident == Id.dllimport)
3227 specifier.dllimport = true;
3228 nextToken();
3230 else if (token.ident == Id.dllexport)
3232 specifier.dllexport = true;
3233 nextToken();
3235 else if (token.ident == Id.naked)
3237 specifier.naked = true;
3238 nextToken();
3240 else if (token.ident == Id.noinline)
3242 specifier.scw |= SCW.xnoinline;
3243 nextToken();
3245 else if (token.ident == Id.noreturn)
3247 specifier.noreturn = true;
3248 nextToken();
3250 else if (token.ident == Id._nothrow)
3252 specifier._nothrow = true;
3253 nextToken();
3255 else if (token.ident == Id.thread)
3257 specifier.scw |= SCW.x_Thread_local;
3258 nextToken();
3260 else if (token.ident == Id._align)
3262 // Microsoft spec is very imprecise as to how this actually works
3263 nextToken();
3264 check(TOK.leftParenthesis);
3265 if (token.value == TOK.int32Literal)
3267 const n = token.unsvalue;
3268 if (n < 1 || n & (n - 1) || 8192 < n)
3269 error("__decspec(align(%lld)) must be an integer positive power of 2 and be <= 8,192", cast(ulong)n);
3270 specifier.packalign.set(cast(uint)n);
3271 specifier.packalign.setPack(true);
3272 nextToken();
3274 else
3276 error("alignment value expected, not `%s`", token.toChars());
3277 nextToken();
3280 check(TOK.rightParenthesis);
3282 else if (token.ident == Id._deprecated)
3284 specifier._deprecated = true;
3285 nextToken();
3286 if (token.value == TOK.leftParenthesis) // optional deprecation message
3288 nextToken();
3289 specifier.depMsg = cparseExpression();
3290 check(TOK.rightParenthesis);
3293 else
3295 nextToken();
3296 if (token.value == TOK.leftParenthesis)
3297 cparseParens();
3300 else if (token.value == TOK.restrict) // ImportC assigns no semantics to `restrict`, so just ignore the keyword.
3301 nextToken();
3302 else
3304 error("extended-decl-modifier expected after `__declspec(`, saw `%s` instead", token.toChars());
3305 nextToken();
3306 if (token.value != TOK.rightParenthesis)
3307 break;
3312 /*************************
3313 * Parser for asm label. It appears after the declarator, and has apparently
3314 * nothing to do with inline assembler.
3315 * https://gcc.gnu.org/onlinedocs/gcc/Asm-Labels.html
3316 * simple-asm-expr:
3317 * asm ( asm-string-literal )
3319 * asm-string-literal:
3320 * string-literal
3322 private AST.StringExp cparseGnuAsmLabel()
3324 nextToken(); // move past asm
3325 check(TOK.leftParenthesis);
3326 if (token.value != TOK.string_)
3327 error("string literal expected for Asm Label, not `%s`", token.toChars());
3328 auto label = cparsePrimaryExp();
3329 check(TOK.rightParenthesis);
3330 return cast(AST.StringExp) label;
3333 /********************
3334 * Parse C inline assembler statement in Gnu format.
3335 * https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
3336 * asm asm-qualifiers ( AssemblerTemplate : OutputOperands : InputOperands : Clobbers : GotoLabels )
3337 * Current token is on the `asm`.
3338 * Returns:
3339 * inline assembler expression as a Statement
3341 private AST.Statement cparseGnuAsm()
3343 // Defer parsing of AsmStatements until semantic processing.
3344 const loc = token.loc;
3346 nextToken();
3348 // Consume all asm-qualifiers. As a future optimization, we could record
3349 // the `inline` and `volatile` storage classes against the statement.
3350 while (token.value == TOK.goto_ ||
3351 token.value == TOK.inline ||
3352 token.value == TOK.volatile)
3353 nextToken();
3355 check(TOK.leftParenthesis);
3356 if (token.value != TOK.string_)
3357 error("string literal expected for Assembler Template, not `%s`", token.toChars());
3358 Token* toklist = null;
3359 Token** ptoklist = &toklist;
3360 //Identifier label = null;
3361 auto statements = new AST.Statements();
3363 int parens;
3364 while (1)
3366 switch (token.value)
3368 case TOK.leftParenthesis:
3369 ++parens;
3370 goto default;
3372 case TOK.rightParenthesis:
3373 --parens;
3374 if (parens >= 0)
3375 goto default;
3376 break;
3378 case TOK.semicolon:
3379 error("matching `)` expected, not `;`");
3380 break;
3382 case TOK.endOfFile:
3383 /* ( */
3384 error("matching `)` expected, not end of file");
3385 break;
3387 case TOK.colonColon: // treat as two separate : tokens for iasmgcc
3388 *ptoklist = allocateToken();
3389 memcpy(*ptoklist, &token, Token.sizeof);
3390 (*ptoklist).value = TOK.colon;
3391 ptoklist = &(*ptoklist).next;
3393 *ptoklist = allocateToken();
3394 memcpy(*ptoklist, &token, Token.sizeof);
3395 (*ptoklist).value = TOK.colon;
3396 ptoklist = &(*ptoklist).next;
3398 *ptoklist = null;
3399 nextToken();
3400 continue;
3402 default:
3403 *ptoklist = allocateToken();
3404 memcpy(*ptoklist, &token, Token.sizeof);
3405 ptoklist = &(*ptoklist).next;
3406 *ptoklist = null;
3407 nextToken();
3408 continue;
3410 if (toklist)
3412 // Create AsmStatement from list of tokens we've saved
3413 AST.Statement s = new AST.AsmStatement(token.loc, toklist);
3414 statements.push(s);
3416 break;
3418 nextToken();
3419 auto s = new AST.CompoundAsmStatement(loc, statements, 0);
3420 return s;
3423 /*************************
3424 * __attribute__ parser
3425 * https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html
3426 * gnu-attributes:
3427 * gnu-attributes gnu-attribute-specifier
3429 * gnu-attribute-specifier:
3430 * __attribute__ (( gnu-attribute-list ))
3432 * gnu-attribute-list:
3433 * gnu-attribute (opt)
3434 * gnu-attribute-list , gnu-attribute
3436 * Params:
3437 * specifier = filled in with the attribute(s)
3439 private void cparseGnuAttributes(ref Specifier specifier)
3441 while (token.value == TOK.__attribute__)
3443 nextToken(); // move past __attribute__
3444 check(TOK.leftParenthesis);
3445 check(TOK.leftParenthesis);
3447 if (token.value != TOK.rightParenthesis)
3449 while (1)
3451 cparseGnuAttribute(specifier);
3452 if (token.value != TOK.comma)
3453 break;
3454 nextToken();
3458 check(TOK.rightParenthesis);
3459 check(TOK.rightParenthesis);
3463 /*************************
3464 * Parse a single GNU attribute
3465 * gnu-attribute:
3466 * gnu-attribute-name
3467 * gnu-attribute-name ( identifier )
3468 * gnu-attribute-name ( identifier , expression-list )
3469 * gnu-attribute-name ( expression-list (opt) )
3471 * gnu-attribute-name:
3472 * keyword
3473 * identifier
3475 * expression-list:
3476 * constant-expression
3477 * expression-list , constant-expression
3479 * Params:
3480 * specifier = filled in with the attribute(s)
3482 private void cparseGnuAttribute(ref Specifier specifier)
3484 /* Check for dllimport, dllexport, naked, noreturn, vector_size(bytes)
3485 * Ignore the rest
3487 if (!isGnuAttributeName())
3488 return;
3490 if (token.value == TOK.identifier)
3492 if (token.ident == Id.aligned)
3494 nextToken();
3495 if (token.value == TOK.leftParenthesis)
3497 nextToken();
3498 if (token.value == TOK.int32Literal)
3500 const n = token.unsvalue;
3501 if (n < 1 || n & (n - 1) || ushort.max < n)
3502 error("__attribute__((aligned(%lld))) must be an integer positive power of 2 and be <= 32,768", cast(ulong)n);
3503 specifier.packalign.set(cast(uint)n);
3504 specifier.packalign.setPack(true);
3505 nextToken();
3507 else
3509 error("alignment value expected, not `%s`", token.toChars());
3510 nextToken();
3513 check(TOK.rightParenthesis);
3515 /* ignore __attribute__((aligned)), which sets the alignment to the largest value for any data
3516 * type on the target machine. It's the opposite of __attribute__((packed))
3519 else if (token.ident == Id.always_inline) // https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html
3521 specifier.scw |= SCW.xinline;
3522 nextToken();
3524 else if (token.ident == Id._deprecated)
3526 specifier._deprecated = true;
3527 nextToken();
3528 if (token.value == TOK.leftParenthesis) // optional deprecation message
3530 nextToken();
3531 specifier.depMsg = cparseExpression();
3532 check(TOK.rightParenthesis);
3535 else if (token.ident == Id.dllimport)
3537 specifier.dllimport = true;
3538 nextToken();
3540 else if (token.ident == Id.dllexport)
3542 specifier.dllexport = true;
3543 nextToken();
3545 else if (token.ident == Id.naked)
3547 specifier.naked = true;
3548 nextToken();
3550 else if (token.ident == Id.noinline)
3552 specifier.scw |= SCW.xnoinline;
3553 nextToken();
3555 else if (token.ident == Id.noreturn)
3557 specifier.noreturn = true;
3558 nextToken();
3560 else if (token.ident == Id._nothrow)
3562 specifier._nothrow = true;
3563 nextToken();
3565 else if (token.ident == Id._pure)
3567 specifier._pure = true;
3568 nextToken();
3570 else if (token.ident == Id.vector_size)
3572 nextToken();
3573 check(TOK.leftParenthesis);
3574 if (token.value == TOK.int32Literal)
3576 const n = token.unsvalue;
3577 if (n < 1 || n & (n - 1) || ushort.max < n)
3578 error("__attribute__((vector_size(%lld))) must be an integer positive power of 2 and be <= 32,768", cast(ulong)n);
3579 specifier.vector_size = cast(uint) n;
3580 nextToken();
3582 else
3584 error("value for vector_size expected, not `%s`", token.toChars());
3585 nextToken();
3587 check(TOK.rightParenthesis);
3589 else
3591 nextToken();
3592 if (token.value == TOK.leftParenthesis)
3593 cparseParens();
3596 else
3598 nextToken();
3599 if (token.value == TOK.leftParenthesis)
3600 cparseParens();
3604 /*************************
3605 * See if match for GNU attribute name, which may be any identifier,
3606 * storage-class-specifier, type-specifier, or type-qualifier.
3607 * Returns:
3608 * true if a valid GNU attribute name
3610 private bool isGnuAttributeName()
3612 switch (token.value)
3614 case TOK.identifier:
3615 case TOK.static_:
3616 case TOK.unsigned:
3617 case TOK.int64:
3618 case TOK.const_:
3619 case TOK.extern_:
3620 case TOK.register:
3621 case TOK.typedef_:
3622 case TOK.int16:
3623 case TOK.inline:
3624 case TOK._Noreturn:
3625 case TOK.volatile:
3626 case TOK.signed:
3627 case TOK.auto_:
3628 case TOK.restrict:
3629 case TOK._Complex:
3630 case TOK._Thread_local:
3631 case TOK.int32:
3632 case TOK.__int128:
3633 case TOK.char_:
3634 case TOK.float32:
3635 case TOK.float64:
3636 case TOK.void_:
3637 case TOK._Bool:
3638 case TOK._Atomic:
3639 return true;
3641 default:
3642 return false;
3646 /***************************
3647 * Like skipParens(), but consume the tokens.
3649 private void cparseParens()
3651 check(TOK.leftParenthesis);
3652 int parens = 1;
3654 while (1)
3656 switch (token.value)
3658 case TOK.leftParenthesis:
3659 ++parens;
3660 break;
3662 case TOK.rightParenthesis:
3663 --parens;
3664 if (parens < 0)
3666 error("extra right parenthesis");
3667 return;
3669 if (parens == 0)
3671 nextToken();
3672 return;
3674 break;
3676 case TOK.endOfFile:
3677 error("end of file found before right parenthesis");
3678 return;
3680 default:
3681 break;
3683 nextToken();
3688 /******************************************************************************/
3689 /***************************** Struct & Enum Parser ***************************/
3692 /*************************************
3693 * C11 6.7.2.2
3694 * enum-specifier:
3695 * enum identifier (opt) { enumerator-list }
3696 * enum identifier (opt) { enumerator-list , }
3697 * enum identifier
3699 * enumerator-list:
3700 * enumerator
3701 * enumerator-list , enumerator
3703 * enumerator:
3704 * enumeration-constant
3705 * enumeration-constant = constant-expression
3707 * enumeration-constant:
3708 * identifier
3710 * Params:
3711 * symbols = symbols to add enum declaration to
3712 * Returns:
3713 * type of the enum
3715 private AST.Type cparseEnum(ref AST.Dsymbols* symbols)
3717 const loc = token.loc;
3718 nextToken();
3720 /* GNU Extensions
3721 * enum-specifier:
3722 * enum gnu-attributes (opt) identifier (opt) { enumerator-list } gnu-attributes (opt)
3723 * enum gnu-attributes (opt) identifier (opt) { enumerator-list , } gnu-attributes (opt)
3724 * enum gnu-attributes (opt) identifier
3726 Specifier specifier;
3727 specifier.packalign.setDefault();
3728 if (token.value == TOK.__attribute__)
3729 cparseGnuAttributes(specifier);
3731 Identifier tag;
3732 if (token.value == TOK.identifier)
3734 tag = token.ident;
3735 nextToken();
3738 /* clang extension: add optional base type after the identifier
3739 * https://en.cppreference.com/w/cpp/language/enum
3740 * enum Identifier : Type
3742 //AST.Type base = AST.Type.tint32; // C11 6.7.2.2-4 implementation defined default base type
3743 AST.Type base = null; // C23 says base type is determined by enum member values
3744 if (token.value == TOK.colon)
3746 nextToken();
3747 base = cparseTypeName();
3750 AST.Dsymbols* members;
3751 if (token.value == TOK.leftCurly)
3753 nextToken();
3754 members = new AST.Dsymbols();
3756 if (token.value == TOK.rightCurly) // C11 6.7.2.2-1
3758 if (tag)
3759 error("no members for `enum %s`", tag.toChars());
3760 else
3761 error("no members for anonymous enum");
3764 while (token.value == TOK.identifier)
3766 auto ident = token.ident; // enumeration-constant
3767 nextToken();
3768 auto mloc = token.loc;
3770 if (token.value == TOK.__attribute__)
3772 /* gnu-attributes can appear here, but just scan and ignore them
3773 * https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
3775 Specifier specifierx;
3776 specifierx.packalign.setDefault();
3777 cparseGnuAttributes(specifierx);
3780 AST.Expression value;
3781 if (token.value == TOK.assign)
3783 nextToken();
3784 value = cparseConstantExp();
3785 // TODO C11 6.7.2.2-2 value must fit into an int
3788 if (token.value == TOK.__attribute__)
3790 /* gnu-attributes can appear here, but just scan and ignore them
3791 * https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
3793 Specifier specifierx;
3794 specifierx.packalign.setDefault();
3795 cparseGnuAttributes(specifierx);
3798 auto em = new AST.EnumMember(mloc, ident, value, null, 0, null, null);
3799 members.push(em);
3801 if (token.value == TOK.comma)
3803 nextToken();
3804 continue;
3806 break;
3808 check(TOK.rightCurly);
3810 /* GNU Extensions
3811 * Parse the postfix gnu-attributes (opt)
3813 if (token.value == TOK.__attribute__)
3814 cparseGnuAttributes(specifier);
3816 else if (!tag)
3817 error("missing `identifier` after `enum`");
3819 /* Need semantic information to determine if this is a declaration,
3820 * redeclaration, or reference to existing declaration.
3821 * Defer to the semantic() pass with a TypeTag.
3823 return new AST.TypeTag(loc, TOK.enum_, tag, structalign_t.init, base, members);
3826 /*************************************
3827 * C11 6.7.2.1
3828 * Parse struct and union specifiers.
3829 * Parser is advanced to the tag identifier or brace.
3830 * struct-or-union-specifier:
3831 * struct-or-union identifier (opt) { struct-declaration-list }
3832 * struct-or-union identifier
3834 * struct-or-union:
3835 * struct
3836 * union
3838 * struct-declaration-list:
3839 * struct-declaration
3840 * struct-declaration-list struct-declaration
3842 * Params:
3843 * loc = location of `struct` or `union`
3844 * structOrUnion = TOK.struct_ or TOK.union_
3845 * packalign = alignment to use for struct members
3846 * symbols = symbols to add struct-or-union declaration to
3847 * Returns:
3848 * type of the struct
3850 private AST.Type cparseStruct(Loc loc, TOK structOrUnion, structalign_t packalign, ref AST.Dsymbols* symbols)
3852 Identifier tag;
3854 if (token.value == TOK.identifier)
3856 tag = token.ident;
3857 nextToken();
3860 AST.Dsymbols* members;
3861 if (token.value == TOK.leftCurly)
3863 nextToken();
3864 members = new AST.Dsymbols(); // so `members` will be non-null even with 0 members
3865 while (token.value != TOK.rightCurly)
3867 cparseStructDeclaration(members);
3869 if (token.value == TOK.endOfFile)
3870 break;
3872 check(TOK.rightCurly);
3874 if ((*members).length == 0) // C11 6.7.2.1-8
3876 /* allow empty structs as an extension
3877 * struct-declarator-list:
3878 * struct-declarator (opt)
3882 else if (!tag)
3883 error("missing tag `identifier` after `%s`", Token.toChars(structOrUnion));
3885 // many ways and places to declare alignment
3886 if (packalign.isUnknown() && !this.packalign.isUnknown())
3887 packalign.set(this.packalign.get());
3889 /* Need semantic information to determine if this is a declaration,
3890 * redeclaration, or reference to existing declaration.
3891 * Defer to the semantic() pass with a TypeTag.
3893 return new AST.TypeTag(loc, structOrUnion, tag, packalign, null, members);
3896 /*************************************
3897 * C11 6.7.2.1
3898 * Parse a struct declaration member.
3899 * struct-declaration:
3900 * specifier-qualifier-list struct-declarator-list (opt) ;
3901 * static_assert-declaration
3903 * struct-declarator-list:
3904 * struct-declarator
3905 * struct-declarator-list , struct-declarator
3907 * struct-declarator:
3908 * declarator
3909 * declarator (opt) : constant-expression
3910 * Params:
3911 * members = where to put the fields (members)
3913 void cparseStructDeclaration(AST.Dsymbols* members)
3915 //printf("cparseStructDeclaration()\n");
3916 if (token.value == TOK._Static_assert)
3918 auto s = cparseStaticAssert();
3919 members.push(s);
3920 return;
3923 Specifier specifier;
3924 specifier.packalign = this.packalign;
3925 auto tspec = cparseSpecifierQualifierList(LVL.member, specifier);
3926 if (!tspec)
3928 error("no type-specifier for struct member");
3929 tspec = AST.Type.tint32;
3931 if (specifier.mod & MOD.xconst)
3933 tspec = toConst(tspec);
3934 specifier.mod = MOD.xnone; // 'used' it
3937 /* If a declarator does not follow, it is unnamed
3939 if (token.value == TOK.semicolon && tspec)
3941 nextToken();
3942 auto tt = tspec.isTypeTag();
3943 if (!tt)
3945 if (auto ti = tspec.isTypeIdentifier())
3947 error("type-specifier omitted before declaration of `%s`", ti.ident.toChars());
3949 return; // legal but meaningless empty declaration
3952 /* If anonymous struct declaration
3953 * struct { ... members ... };
3954 * C11 6.7.2.1-13
3956 if (!tt.id && tt.members)
3958 /* members of anonymous struct are considered members of
3959 * the containing struct
3961 auto ad = new AST.AnonDeclaration(tt.loc, tt.tok == TOK.union_, tt.members);
3962 auto s = applySpecifier(ad, specifier);
3963 members.push(s);
3964 return;
3966 if (!tt.id && !tt.members)
3967 return; // already gave error in cparseStruct()
3969 /* `struct tag;` and `struct tag { ... };`
3970 * always result in a declaration in the current scope
3972 // TODO: merge in specifier
3973 auto stag = (tt.tok == TOK.struct_)
3974 ? new AST.StructDeclaration(tt.loc, tt.id, false)
3975 : new AST.UnionDeclaration(tt.loc, tt.id);
3976 stag.members = tt.members;
3977 if (!symbols)
3978 symbols = new AST.Dsymbols();
3979 auto s = applySpecifier(stag, specifier);
3980 symbols.push(s);
3981 return;
3984 while (1)
3986 Identifier id;
3987 AST.Type dt;
3988 if (token.value == TOK.colon)
3990 if (auto ti = tspec.isTypeIdentifier())
3992 error("type-specifier omitted before bit field declaration of `%s`", ti.ident.toChars());
3993 tspec = AST.Type.tint32;
3996 // C11 6.7.2.1-12 unnamed bit-field
3997 id = Identifier.generateAnonymousId("BitField");
3998 dt = tspec;
4000 else
4002 dt = cparseDeclarator(DTR.xdirect, tspec, id, specifier);
4003 if (!dt)
4005 panic();
4006 nextToken();
4007 break; // error recovery
4011 AST.Expression width;
4012 if (token.value == TOK.colon)
4014 // C11 6.7.2.1-10 bit-field
4015 nextToken();
4016 width = cparseConstantExp();
4019 /* GNU Extensions
4020 * struct-declarator:
4021 * declarator gnu-attributes (opt)
4022 * declarator (opt) : constant-expression gnu-attributes (opt)
4024 if (token.value == TOK.__attribute__)
4025 cparseGnuAttributes(specifier);
4027 if (!tspec && !specifier.scw && !specifier.mod)
4028 error("specifier-qualifier-list required");
4029 else if (width)
4031 if (specifier.alignExps)
4032 error("no alignment-specifier for bit field declaration"); // C11 6.7.5-2
4033 auto s = new AST.BitFieldDeclaration(width.loc, dt, id, width);
4034 members.push(s);
4036 else if (id)
4038 if (dt.ty == AST.Tvoid)
4039 error("`void` has no value");
4041 // declare the symbol
4042 // Give member variables an implicit void initializer
4043 auto initializer = new AST.VoidInitializer(token.loc);
4044 AST.Dsymbol s = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(LVL.member, specifier));
4045 s = applySpecifier(s, specifier);
4046 members.push(s);
4049 switch (token.value)
4051 case TOK.identifier:
4052 error("missing comma");
4053 goto default;
4055 case TOK.semicolon:
4056 nextToken();
4057 return;
4059 case TOK.comma:
4060 nextToken();
4061 break;
4063 default:
4064 error("`;` or `,` expected");
4065 while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
4066 nextToken();
4067 nextToken();
4068 return;
4074 /******************************************************************************/
4075 /********************************* Lookahead Parser ***************************/
4078 /************************************
4079 * Determine if the scanner is sitting on the start of a declaration.
4080 * Params:
4081 * t = current token of the scanner
4082 * needId = flag with additional requirements for a declaration
4083 * endtok = ending token
4084 * pt = will be set ending token (if not null)
4085 * Returns:
4086 * true at start of a declaration
4088 private bool isCDeclaration(ref Token* pt)
4090 auto t = pt;
4091 //printf("isCDeclaration() %s\n", t.toChars());
4092 if (!isDeclarationSpecifiers(t))
4093 return false;
4095 while (1)
4097 if (t.value == TOK.semicolon)
4099 t = peek(t);
4100 pt = t;
4101 return true;
4103 if (!isCDeclarator(t, DTR.xdirect))
4104 return false;
4105 if (t.value == TOK.asm_)
4107 t = peek(t);
4108 if (t.value != TOK.leftParenthesis || !skipParens(t, &t))
4109 return false;
4111 if (t.value == TOK.__attribute__)
4113 t = peek(t);
4114 if (t.value != TOK.leftParenthesis || !skipParens(t, &t))
4115 return false;
4117 if (t.value == TOK.assign)
4119 t = peek(t);
4120 if (!isInitializer(t))
4121 return false;
4123 switch (t.value)
4125 case TOK.comma:
4126 t = peek(t);
4127 break;
4129 case TOK.semicolon:
4130 t = peek(t);
4131 pt = t;
4132 return true;
4134 default:
4135 return false;
4140 /********************************
4141 * See if match for initializer.
4142 * Params:
4143 * pt = starting token, updated to one past end of initializer if true
4144 * Returns:
4145 * true if initializer
4147 private bool isInitializer(ref Token* pt)
4149 //printf("isInitializer()\n");
4150 auto t = pt;
4152 if (t.value == TOK.leftCurly)
4154 if (!skipBraces(t))
4155 return false;
4156 pt = t;
4157 return true;
4160 // skip over assignment-expression, ending before comma or semiColon or EOF
4161 if (!isAssignmentExpression(t))
4162 return false;
4163 pt = t;
4164 return true;
4167 /********************************
4168 * See if match for:
4169 * postfix-expression ( argument-expression-list(opt) )
4170 * Params:
4171 * pt = starting token, updated to one past end of initializer if true
4172 * Returns:
4173 * true if function call
4175 private bool isFunctionCall(ref Token* pt)
4177 //printf("isFunctionCall()\n");
4178 auto t = pt;
4180 if (!isPrimaryExpression(t))
4181 return false;
4182 if (t.value != TOK.leftParenthesis)
4183 return false;
4184 t = peek(t);
4185 while (1)
4187 if (!isAssignmentExpression(t))
4188 return false;
4189 if (t.value == TOK.comma)
4191 t = peek(t);
4192 continue;
4194 if (t.value == TOK.rightParenthesis)
4196 t = peek(t);
4197 break;
4199 return false;
4201 if (t.value != TOK.semicolon)
4202 return false;
4203 pt = t;
4204 return true;
4207 /********************************
4208 * See if match for assignment-expression.
4209 * Params:
4210 * pt = starting token, updated to one past end of assignment-expression if true
4211 * Returns:
4212 * true if assignment-expression
4214 private bool isAssignmentExpression(ref Token* pt)
4216 auto t = pt;
4217 //printf("isAssignmentExpression() %s\n", t.toChars());
4219 /* This doesn't actually check for grammar matching an
4220 * assignment-expression. It just matches ( ) [ ] looking for
4221 * an ending token that would terminate one.
4223 bool any;
4224 while (1)
4226 switch (t.value)
4228 case TOK.comma:
4229 case TOK.semicolon:
4230 case TOK.rightParenthesis:
4231 case TOK.rightBracket:
4232 case TOK.endOfFile:
4233 if (!any)
4234 return false;
4235 break;
4237 case TOK.leftParenthesis:
4238 if (!skipParens(t, &t))
4239 return false;
4241 https://issues.dlang.org/show_bug.cgi?id=22267
4242 If the parser encounters the following
4243 `identifier variableName = (expression);`
4244 the initializer is not identified as such since the parentheses
4245 cause the parser to keep walking indefinitely
4246 (whereas `(1) + 1` would not be affected.).
4248 any = true;
4249 continue;
4251 case TOK.leftBracket:
4252 if (!skipBrackets(t))
4253 return false;
4254 continue;
4256 case TOK.leftCurly:
4257 if (!skipBraces(t))
4258 return false;
4259 continue;
4261 default:
4262 any = true; // assume token was part of an a-e
4263 t = peek(t);
4264 continue;
4266 pt = t;
4267 return true;
4271 /********************************
4272 * See if match for constant-expression.
4273 * Params:
4274 * pt = starting token, updated to one past end of constant-expression if true
4275 * Returns:
4276 * true if constant-expression
4278 private bool isConstantExpression(ref Token* pt)
4280 return isAssignmentExpression(pt);
4283 /********************************
4284 * See if match for declaration-specifiers.
4285 * No errors are diagnosed.
4286 * Params:
4287 * pt = starting token, updated to one past end of declaration-specifiers if true
4288 * Returns:
4289 * true if declaration-specifiers
4291 private bool isDeclarationSpecifiers(ref Token* pt)
4293 //printf("isDeclarationSpecifiers()\n");
4295 auto t = pt;
4297 bool seenType;
4298 bool any;
4299 while (1)
4301 switch (t.value)
4303 // type-specifiers
4304 case TOK.void_:
4305 case TOK.char_:
4306 case TOK.int16:
4307 case TOK.int32:
4308 case TOK.int64:
4309 case TOK.__int128:
4310 case TOK.float32:
4311 case TOK.float64:
4312 case TOK.signed:
4313 case TOK.unsigned:
4314 case TOK._Bool:
4315 //case TOK._Imaginary:
4316 case TOK._Complex:
4317 t = peek(t);
4318 seenType = true;
4319 any = true;
4320 continue;
4322 case TOK.identifier: // typedef-name
4323 if (!seenType)
4325 t = peek(t);
4326 seenType = true;
4327 any = true;
4328 continue;
4330 break;
4332 case TOK.struct_:
4333 case TOK.union_:
4334 case TOK.enum_:
4335 t = peek(t);
4336 if (t.value == TOK.__attribute__ ||
4337 t.value == TOK.__declspec)
4339 t = peek(t);
4340 if (!skipParens(t, &t))
4341 return false;
4343 if (t.value == TOK.identifier)
4345 t = peek(t);
4346 if (t.value == TOK.leftCurly)
4348 if (!skipBraces(t))
4349 return false;
4352 else if (t.value == TOK.leftCurly)
4354 if (!skipBraces(t))
4355 return false;
4357 else
4358 return false;
4359 any = true;
4360 continue;
4362 // storage-class-specifiers
4363 case TOK.typedef_:
4364 case TOK.extern_:
4365 case TOK.static_:
4366 case TOK.__thread:
4367 case TOK._Thread_local:
4368 case TOK.auto_:
4369 case TOK.register:
4371 // function-specifiers
4372 case TOK.inline:
4373 case TOK._Noreturn:
4375 // type-qualifiers
4376 case TOK.const_:
4377 case TOK.volatile:
4378 case TOK.restrict:
4379 case TOK.__stdcall:
4380 t = peek(t);
4381 any = true;
4382 continue;
4384 case TOK._Alignas: // alignment-specifier
4385 case TOK.__declspec: // decl-specifier
4386 case TOK.__attribute__: // attribute-specifier
4387 t = peek(t);
4388 if (!skipParens(t, &t))
4389 return false;
4390 any = true;
4391 continue;
4393 // either atomic-type-specifier or type_qualifier
4394 case TOK._Atomic: // TODO _Atomic ( type-name )
4395 t = peek(t);
4396 if (t.value == TOK.leftParenthesis) // maybe atomic-type-specifier
4398 auto tsave = t;
4399 t = peek(t);
4400 if (!isTypeName(t) || t.value != TOK.rightParenthesis)
4401 { // it's a type-qualifier
4402 t = tsave; // back up parser
4403 any = true;
4404 continue;
4406 t = peek(t); // move past right parenthesis of atomic-type-specifier
4408 any = true;
4409 continue;
4411 default:
4412 break;
4414 break;
4417 if (any)
4419 pt = t;
4420 return true;
4422 return false;
4425 /**************************************
4426 * See if declaration-list is present.
4427 * Returns:
4428 * true if declaration-list is present, even an empty one
4430 bool isDeclarationList(ref Token* pt)
4432 auto t = pt;
4433 while (1)
4435 if (t.value == TOK.leftCurly)
4437 pt = t;
4438 return true;
4440 if (!isCDeclaration(t))
4441 return false;
4445 /*******************************************
4446 * Skip braces.
4447 * Params:
4448 * pt = enters on left brace, set to token past right bracket on true
4449 * Returns:
4450 * true if successful
4452 private bool skipBraces(ref Token* pt)
4454 auto t = pt;
4455 if (t.value != TOK.leftCurly)
4456 return false;
4458 int braces = 0;
4460 while (1)
4462 switch (t.value)
4464 case TOK.leftCurly:
4465 ++braces;
4466 t = peek(t);
4467 continue;
4469 case TOK.rightCurly:
4470 --braces;
4471 if (braces == 0)
4473 pt = peek(t);
4474 return true;
4476 if (braces < 0)
4477 return false;
4479 t = peek(t);
4480 continue;
4482 case TOK.endOfFile:
4483 return false;
4485 default:
4486 t = peek(t);
4487 continue;
4492 /*******************************************
4493 * Skip brackets.
4494 * Params:
4495 * pt = enters on left bracket, set to token past right bracket on true
4496 * Returns:
4497 * true if successful
4499 private bool skipBrackets(ref Token* pt)
4501 auto t = pt;
4502 if (t.value != TOK.leftBracket)
4503 return false;
4505 int brackets = 0;
4507 while (1)
4509 switch (t.value)
4511 case TOK.leftBracket:
4512 ++brackets;
4513 t = peek(t);
4514 continue;
4516 case TOK.rightBracket:
4517 --brackets;
4518 if (brackets == 0)
4520 pt = peek(t);
4521 return true;
4523 if (brackets < 0)
4524 return false;
4526 t = peek(t);
4527 continue;
4529 case TOK.endOfFile:
4530 return false;
4532 default:
4533 t = peek(t);
4534 continue;
4539 /*********************************
4540 * Check to see if tokens starting with *pt form a declarator.
4541 * Params:
4542 * pt = pointer to starting token, updated to point past declarator if true is returned
4543 * declarator = declarator kind
4544 * Returns:
4545 * true if it does
4547 private bool isCDeclarator(ref Token* pt, DTR declarator)
4549 auto t = pt;
4550 while (1)
4552 if (t.value == TOK.mul) // pointer
4554 t = peek(t);
4555 if (!isTypeQualifierList(t))
4556 return false;
4558 else
4559 break;
4562 if (t.value == TOK.identifier)
4564 if (declarator == DTR.xabstract)
4565 return false;
4566 t = peek(t);
4568 else if (t.value == TOK.leftParenthesis)
4570 t = peek(t);
4571 if (!isCDeclarator(t, declarator))
4572 return false;
4573 if (t.value != TOK.rightParenthesis)
4574 return false;
4575 t = peek(t);
4577 else if (declarator == DTR.xdirect)
4579 return false;
4582 while (1)
4584 if (t.value == TOK.leftBracket)
4586 if (!skipBrackets(t))
4587 return false;
4589 else if (t.value == TOK.leftParenthesis)
4591 if (!skipParens(t, &t))
4592 return false;
4594 else
4595 break;
4597 pt = t;
4598 return true;
4601 /***************************
4602 * Is this the start of a type-qualifier-list?
4603 * (Can be empty.)
4604 * Params:
4605 * pt = first token; updated with past end of type-qualifier-list if true
4606 * Returns:
4607 * true if start of type-qualifier-list
4609 private bool isTypeQualifierList(ref Token* pt)
4611 auto t = pt;
4612 while (1)
4614 switch (t.value)
4616 case TOK.const_:
4617 case TOK.restrict:
4618 case TOK.volatile:
4619 case TOK._Atomic:
4620 case TOK.__stdcall:
4621 t = peek(t);
4622 continue;
4624 default:
4625 break;
4627 break;
4629 pt = t;
4630 return true;
4633 /***************************
4634 * Is this the start of a type-name?
4635 * Params:
4636 * pt = first token; updated with past end of type-name if true
4637 * Returns:
4638 * true if start of type-name
4640 private bool isTypeName(ref Token* pt)
4642 auto t = pt;
4643 //printf("isTypeName() %s\n", t.toChars());
4644 if (!isSpecifierQualifierList(t))
4645 return false;
4646 if (!isCDeclarator(t, DTR.xabstract))
4647 return false;
4648 if (t.value != TOK.rightParenthesis)
4649 return false;
4650 pt = t;
4651 return true;
4654 /***************************
4655 * Is this the start of a specifier-qualifier-list?
4656 * Params:
4657 * pt = first token; updated with past end of specifier-qualifier-list if true
4658 * Returns:
4659 * true if start of specifier-qualifier-list
4661 private bool isSpecifierQualifierList(ref Token* pt)
4663 auto t = pt;
4664 bool result;
4665 while (1)
4667 switch (t.value)
4669 // Type Qualifiers
4670 case TOK.const_:
4671 case TOK.restrict:
4672 case TOK.volatile:
4673 case TOK.__stdcall:
4675 // Type Specifiers
4676 case TOK.char_:
4677 case TOK.signed:
4678 case TOK.unsigned:
4679 case TOK.int16:
4680 case TOK.int32:
4681 case TOK.int64:
4682 case TOK.__int128:
4683 case TOK.float32:
4684 case TOK.float64:
4685 case TOK.void_:
4686 case TOK._Bool:
4687 //case TOK._Imaginary: // ? missing in Spec
4688 case TOK._Complex:
4689 t = peek(t);
4690 break;
4692 case TOK.identifier:
4693 // Use typedef table to disambiguate
4694 if (isTypedef(t.ident))
4696 t = peek(t);
4697 break;
4699 else
4701 return false;
4704 // struct-or-union-specifier
4705 // enum-specifier
4706 case TOK.struct_:
4707 case TOK.union_:
4708 case TOK.enum_:
4709 t = peek(t);
4710 if (t.value == TOK.identifier)
4712 t = peek(t);
4713 if (t.value == TOK.leftCurly)
4715 if (!skipBraces(t))
4716 return false;
4719 else if (t.value == TOK.leftCurly)
4721 if (!skipBraces(t))
4722 return false;
4724 else
4725 return false;
4726 break;
4728 // atomic-type-specifier
4729 case TOK._Atomic:
4730 case TOK.typeof_:
4731 case TOK.__attribute__:
4732 t = peek(t);
4733 if (t.value != TOK.leftParenthesis ||
4734 !skipParens(t, &t))
4735 return false;
4736 break;
4738 default:
4739 if (result)
4740 pt = t;
4741 return result;
4743 result = true;
4747 /************************************
4748 * Looking at the leading left parenthesis, and determine if it is
4749 * either of the following:
4750 * ( type-name ) cast-expression
4751 * ( type-name ) { initializer-list }
4752 * as opposed to:
4753 * ( expression )
4754 * Params:
4755 * pt = starting token, updated to one past end of constant-expression if true
4756 * afterParenType = true if already seen `( type-name )`
4757 * Returns:
4758 * true if matches ( type-name ) ...
4760 private bool isCastExpression(ref Token* pt, bool afterParenType = false)
4762 enum log = false;
4763 if (log) printf("isCastExpression(tk: `%s`, afterParenType: %d)\n", token.toChars(pt.value), afterParenType);
4764 auto t = pt;
4765 switch (t.value)
4767 case TOK.leftParenthesis:
4768 auto tk = peek(t); // move past left parenthesis
4769 if (!isTypeName(tk) || tk.value != TOK.rightParenthesis)
4771 if (afterParenType)
4772 goto default; // could be ( type-name ) ( unary-expression )
4773 return false;
4775 tk = peek(tk); // move past right parenthesis
4777 if (tk.value == TOK.leftCurly)
4779 // ( type-name ) { initializer-list }
4780 if (!isInitializer(tk))
4782 return false;
4784 t = tk;
4785 break;
4788 if (tk.value == TOK.leftParenthesis && peek(tk).value == TOK.rightParenthesis)
4790 return false; // (type-name)() is not a cast (it might be a function call)
4793 if (!isCastExpression(tk, true))
4795 if (afterParenType) // could be ( type-name ) ( unary-expression )
4796 goto default; // where unary-expression also matched type-name
4797 return true;
4799 // ( type-name ) cast-expression
4800 t = tk;
4801 break;
4803 default:
4804 if (!afterParenType || !isUnaryExpression(t, afterParenType))
4806 return false;
4808 // if we've already seen ( type-name ), then this is a cast
4809 break;
4811 pt = t;
4812 if (log) printf("isCastExpression true\n");
4813 return true;
4816 /********************************
4817 * See if match for unary-expression.
4818 * Params:
4819 * pt = starting token, updated to one past end of constant-expression if true
4820 * afterParenType = true if already seen ( type-name ) of a cast-expression
4821 * Returns:
4822 * true if unary-expression
4824 private bool isUnaryExpression(ref Token* pt, bool afterParenType = false)
4826 auto t = pt;
4827 switch (t.value)
4829 case TOK.plusPlus:
4830 case TOK.minusMinus:
4831 t = peek(t);
4832 if (!isUnaryExpression(t, afterParenType))
4833 return false;
4834 break;
4836 case TOK.and:
4837 case TOK.mul:
4838 case TOK.min:
4839 case TOK.add:
4840 case TOK.not:
4841 case TOK.tilde:
4842 t = peek(t);
4843 if (!isCastExpression(t, afterParenType))
4844 return false;
4845 break;
4847 case TOK.sizeof_:
4848 t = peek(t);
4849 if (t.value == TOK.leftParenthesis)
4851 auto tk = peek(t);
4852 if (isTypeName(tk))
4854 if (tk.value != TOK.rightParenthesis)
4855 return false;
4856 t = peek(tk);
4857 break;
4860 if (!isUnaryExpression(t, afterParenType))
4861 return false;
4862 break;
4864 case TOK._Alignof:
4865 t = peek(t);
4866 if (t.value != TOK.leftParenthesis)
4867 return false;
4868 t = peek(t);
4869 if (!isTypeName(t) || t.value != TOK.rightParenthesis)
4870 return false;
4871 break;
4873 default:
4874 // Compound literals are handled by cast and sizeof expressions,
4875 // so be content with just seeing a primary expression.
4876 if (!isPrimaryExpression(t))
4877 return false;
4878 break;
4880 pt = t;
4881 return true;
4884 /********************************
4885 * See if match for primary-expression.
4886 * Params:
4887 * pt = starting token, updated to one past end of constant-expression if true
4888 * Returns:
4889 * true if primary-expression
4891 private bool isPrimaryExpression(ref Token* pt)
4893 auto t = pt;
4894 switch (t.value)
4896 case TOK.identifier:
4897 case TOK.charLiteral:
4898 case TOK.int32Literal:
4899 case TOK.uns32Literal:
4900 case TOK.int64Literal:
4901 case TOK.uns64Literal:
4902 case TOK.float32Literal:
4903 case TOK.float64Literal:
4904 case TOK.float80Literal:
4905 case TOK.imaginary32Literal:
4906 case TOK.imaginary64Literal:
4907 case TOK.imaginary80Literal:
4908 case TOK.string_:
4909 t = peek(t);
4910 break;
4912 case TOK.leftParenthesis:
4913 // ( expression )
4914 if (!skipParens(t, &t))
4915 return false;
4916 break;
4918 case TOK._Generic:
4919 t = peek(t);
4920 if (!skipParens(t, &t))
4921 return false;
4922 break;
4924 default:
4925 return false;
4927 pt = t;
4928 return true;
4932 /******************************************************************************/
4933 /********************************* More ***************************************/
4936 /**************
4937 * Declaration context
4939 enum LVL
4941 global = 1, /// global
4942 parameter = 2, /// function parameter (declarations for function identifier-list)
4943 prototype = 4, /// function prototype
4944 local = 8, /// local
4945 member = 0x10, /// struct member
4948 /// Types of declarator to parse
4949 enum DTR
4951 xdirect = 1, /// C11 6.7.6 direct-declarator
4952 xabstract = 2, /// C11 6.7.7 abstract-declarator
4953 xparameter = 3, /// parameter declarator may be either direct or abstract
4956 /// C11 6.7.1 Storage-class specifiers
4957 enum SCW : uint
4959 xnone = 0,
4960 xtypedef = 1,
4961 xextern = 2,
4962 xstatic = 4,
4963 x_Thread_local = 8,
4964 xauto = 0x10,
4965 xregister = 0x20,
4966 // C11 6.7.4 Function specifiers
4967 xinline = 0x40,
4968 x_Noreturn = 0x80,
4970 xnoinline = 0x100,
4973 /// C11 6.7.3 Type qualifiers
4974 enum MOD : uint
4976 xnone = 0,
4977 xconst = 1,
4978 xvolatile = 2,
4979 xrestrict = 4,
4980 x_Atomic = 8,
4981 x__stdcall = 0x10, // Windows linkage extension
4984 /**********************************
4985 * Aggregate for all the various specifiers
4987 struct Specifier
4989 bool noreturn; /// noreturn attribute
4990 bool naked; /// naked attribute
4991 bool _nothrow; /// nothrow attribute
4992 bool _pure; /// pure attribute
4993 bool dllimport; /// dllimport attribute
4994 bool dllexport; /// dllexport attribute
4995 bool _deprecated; /// deprecated attribute
4996 AST.Expression depMsg; /// deprecated message
4997 uint vector_size; /// positive power of 2 multipe of base type size
4999 SCW scw; /// storage-class specifiers
5000 MOD mod; /// type qualifiers
5001 AST.Expressions* alignExps; /// alignment
5002 structalign_t packalign; /// #pragma pack alignment value
5005 /***********************
5006 * Convert from C specifiers to D storage class
5007 * Params:
5008 * level = declaration context
5009 * specifier = specifiers, context, etc.
5010 * Returns:
5011 * corresponding D storage class
5013 StorageClass specifiersToSTC(LVL level, const ref Specifier specifier)
5015 StorageClass stc;
5016 if (specifier.scw & SCW.x_Thread_local)
5018 if (level == LVL.global)
5020 if (specifier.scw & SCW.xextern)
5021 stc = AST.STC.extern_;
5022 else if (specifier.scw & SCW.xstatic)
5023 stc = AST.STC.static_;
5025 else if (level == LVL.local)
5027 if (specifier.scw & SCW.xextern)
5028 stc = AST.STC.extern_;
5029 else if (specifier.scw & SCW.xstatic)
5030 stc = AST.STC.static_;
5032 else if (level == LVL.member)
5034 if (specifier.scw & SCW.xextern)
5035 stc = AST.STC.extern_;
5036 else if (specifier.scw & SCW.xstatic)
5037 stc = AST.STC.static_;
5040 else
5042 if (level == LVL.global)
5044 if (specifier.scw & SCW.xextern)
5045 stc = AST.STC.extern_ | AST.STC.gshared;
5046 else if (specifier.scw & SCW.xstatic)
5047 stc = AST.STC.gshared | AST.STC.static_;
5048 else
5049 stc = AST.STC.gshared;
5051 else if (level == LVL.local)
5053 if (specifier.scw & SCW.xextern)
5054 stc = AST.STC.extern_ | AST.STC.gshared;
5055 else if (specifier.scw & SCW.xstatic)
5056 stc = AST.STC.gshared;
5057 else if (specifier.scw & SCW.xregister)
5058 stc = AST.STC.register;
5060 else if (level == LVL.parameter)
5062 if (specifier.scw & SCW.xregister)
5063 stc = AST.STC.register | AST.STC.parameter;
5064 else
5065 stc = AST.STC.parameter;
5067 else if (level == LVL.member)
5069 if (specifier.scw & SCW.xextern)
5070 stc = AST.STC.extern_ | AST.STC.gshared;
5071 else if (specifier.scw & SCW.xstatic)
5072 stc = AST.STC.gshared;
5075 if (specifier._deprecated && !specifier.depMsg)
5076 stc |= AST.STC.deprecated_;
5077 return stc;
5080 /***********************
5081 * Add attributes from Specifier to function
5082 * Params:
5083 * fd = function to apply them to
5084 * specifier = specifiers
5086 void specifiersToFuncDeclaration(AST.FuncDeclaration fd, const ref Specifier specifier)
5088 fd.isNaked = specifier.naked;
5089 fd.dllImport = specifier.dllimport;
5090 fd.dllExport = specifier.dllexport;
5092 if (specifier.scw & SCW.xnoinline)
5093 fd.inlining = PINLINE.never;
5094 else if (specifier.scw & SCW.xinline)
5095 fd.inlining = PINLINE.always;
5098 /***********************
5099 * Add attributes from Specifier to variable
5100 * Params:
5101 * vd = function to apply them to
5102 * specifier = specifiers
5104 void specifiersToVarDeclaration(AST.VarDeclaration vd, const ref Specifier specifier)
5106 vd.dllImport = specifier.dllimport;
5107 vd.dllExport = specifier.dllexport;
5110 /***********************
5111 * Return suitable signed integer type for the given size
5112 * Params:
5113 * size = size of type
5114 * Returns:
5115 * corresponding signed D integer type
5117 private AST.Type integerTypeForSize(ubyte size)
5119 if (size <= 1)
5120 return AST.Type.tint8;
5121 if (size <= 2)
5122 return AST.Type.tint16;
5123 if (size <= 4)
5124 return AST.Type.tint32;
5125 if (size <= 8)
5126 return AST.Type.tint64;
5127 if (size == 16)
5129 error("__int128 not supported");
5130 return AST.Type.terror;
5132 error("unsupported integer type");
5133 return AST.Type.terror;
5136 /***********************
5137 * Return suitable unsigned integer type for the given size
5138 * Params:
5139 * size = size of type
5140 * Returns:
5141 * corresponding unsigned D integer type
5143 private AST.Type unsignedTypeForSize(ubyte size)
5145 if (size <= 1)
5146 return AST.Type.tuns8;
5147 if (size <= 2)
5148 return AST.Type.tuns16;
5149 if (size <= 4)
5150 return AST.Type.tuns32;
5151 if (size <= 8)
5152 return AST.Type.tuns64;
5153 if (size == 16)
5155 error("unsigned __int128 not supported");
5156 return AST.Type.terror;
5158 error("unsupported integer type");
5159 return AST.Type.terror;
5162 /***********************
5163 * Return suitable D float type for C `long double`
5164 * Params:
5165 * flags = kind of float to return (real, imaginary, complex).
5166 * Returns:
5167 * corresponding D type
5169 private AST.Type realType(RTFlags flags)
5171 if (long_doublesize == AST.Type.tfloat80.size())
5173 // On GDC and LDC, D `real` types map to C `long double`, so never
5174 // return a double type when real.sizeof == double.sizeof.
5175 final switch (flags)
5177 case RTFlags.realfloat: return AST.Type.tfloat80;
5178 case RTFlags.imaginary: return AST.Type.timaginary80;
5179 case RTFlags.complex: return AST.Type.tcomplex80;
5182 else
5184 final switch (flags)
5186 case RTFlags.realfloat: return long_doublesize == 8 ? AST.Type.tfloat64 : AST.Type.tfloat80;
5187 case RTFlags.imaginary: return long_doublesize == 8 ? AST.Type.timaginary64 : AST.Type.timaginary80;
5188 case RTFlags.complex: return long_doublesize == 8 ? AST.Type.tcomplex64 : AST.Type.tcomplex80;
5193 /**************
5194 * Flags for realType
5196 private enum RTFlags
5198 realfloat,
5199 imaginary,
5200 complex,
5203 /********************
5204 * C11 6.4.2.2 Create declaration to predefine __func__
5205 * `static const char __func__[] = " function-name ";`
5206 * Params:
5207 * loc = location for this declaration
5208 * id = identifier of function
5209 * Returns:
5210 * statement representing the declaration of __func__
5212 private AST.Statement createFuncName(Loc loc, Identifier id)
5214 const fn = id.toString(); // function-name
5215 auto efn = new AST.StringExp(loc, fn, fn.length, 1, 'c');
5216 auto ifn = new AST.ExpInitializer(loc, efn);
5217 auto lenfn = new AST.IntegerExp(loc, fn.length + 1, AST.Type.tuns32); // +1 for terminating 0
5218 auto tfn = new AST.TypeSArray(AST.Type.tchar, lenfn);
5219 efn.type = tfn.immutableOf();
5220 efn.committed = true;
5221 auto sfn = new AST.VarDeclaration(loc, tfn, Id.__func__, ifn, STC.gshared | STC.immutable_);
5222 auto e = new AST.DeclarationExp(loc, sfn);
5223 return new AST.ExpStatement(loc, e);
5226 /************************
5227 * After encountering an error, scan forward until a right brace or ; is found
5228 * or the end of the file.
5230 void panic()
5232 while (token.value != TOK.rightCurly && token.value != TOK.semicolon && token.value != TOK.endOfFile)
5233 nextToken();
5236 /**************************
5237 * Apply `const` to a type.
5238 * Params:
5239 * t = type to add const to
5240 * Returns:
5241 * resulting type
5243 private AST.Type toConst(AST.Type t)
5245 // `const` is always applied to the return type, not the
5246 // type function itself.
5247 if (auto tf = t.isTypeFunction())
5248 tf.next = tf.next.addSTC(STC.const_);
5249 else if (auto tt = t.isTypeTag())
5250 tt.mod |= MODFlags.const_;
5251 else
5253 /* Ignore const if the result would be const pointer to mutable
5255 auto tn = t.nextOf();
5256 if (!tn || tn.isConst())
5257 t = t.addSTC(STC.const_);
5259 return t;
5262 /***************************
5263 * Apply specifier to a Dsymbol.
5264 * Params:
5265 * s = Dsymbol
5266 * specifier = specifiers to apply
5267 * Returns:
5268 * Dsymbol with specifiers applied
5270 private AST.Dsymbol applySpecifier(AST.Dsymbol s, ref Specifier specifier)
5272 //printf("applySpecifier() %s\n", s.toChars());
5273 if (specifier._deprecated)
5275 if (specifier.depMsg)
5277 // Wrap declaration in a DeprecatedDeclaration
5278 auto decls = new AST.Dsymbols(1);
5279 (*decls)[0] = s;
5280 s = new AST.DeprecatedDeclaration(specifier.depMsg, decls);
5284 if (specifier.alignExps)
5286 //printf(" applying _Alignas %s, packalign %d\n", (*specifier.alignExps)[0].toChars(), cast(int)specifier.packalign);
5287 // Wrap declaration in an AlignDeclaration
5288 auto decls = new AST.Dsymbols(1);
5289 (*decls)[0] = s;
5290 s = new AST.AlignDeclaration(s.loc, specifier.alignExps, decls);
5292 else if (!specifier.packalign.isDefault())
5294 //printf(" applying packalign %d\n", cast(int)specifier.packalign);
5295 // Wrap #pragma pack in an AlignDeclaration
5296 auto decls = new AST.Dsymbols(1);
5297 (*decls)[0] = s;
5298 s = new AST.AlignDeclaration(s.loc, specifier.packalign, decls);
5300 return s;
5305 /******************************************************************************/
5306 /************************** typedefTab symbol table ***************************/
5309 /********************************
5310 * Determines if type t is a function type.
5311 * Params:
5312 * t = type to test
5313 * Returns:
5314 * true if it represents a function
5316 bool isFunctionTypedef(AST.Type t)
5318 //printf("isFunctionTypedef() %s\n", t.toChars());
5319 if (t.isTypeFunction())
5320 return true;
5321 if (auto tid = t.isTypeIdentifier())
5323 auto pt = lookupTypedef(tid.ident);
5324 if (pt && *pt)
5326 return (*pt).isTypeFunction() !is null;
5329 return false;
5332 /********************************
5333 * Determine if `id` is a symbol for a Typedef.
5334 * Params:
5335 * id = possible typedef
5336 * Returns:
5337 * true if id is a Type
5339 bool isTypedef(Identifier id)
5341 auto pt = lookupTypedef(id);
5342 return (pt && *pt);
5345 /*******************************
5346 * Add `id` to typedefTab[], but only if it will mask an existing typedef.
5347 * Params: id = identifier for non-typedef symbol
5349 void insertIdToTypedefTab(Identifier id)
5351 //printf("insertIdToTypedefTab(id: %s) level %d\n", id.toChars(), cast(int)typedefTab.length - 1);
5352 if (isTypedef(id)) // if existing typedef
5354 /* Add id as null, so we can later distinguish it from a non-null typedef
5356 auto tab = cast(void*[void*])(typedefTab[$ - 1]);
5357 tab[cast(void*)id] = cast(void*)null;
5361 /*******************************
5362 * Add `id` to typedefTab[]
5363 * Params:
5364 * id = identifier for typedef symbol
5365 * t = type of the typedef symbol
5367 void insertTypedefToTypedefTab(Identifier id, AST.Type t)
5369 //printf("insertTypedefToTypedefTab(id: %s, t: %s) level %d\n", id.toChars(), t ? t.toChars() : "null".ptr, cast(int)typedefTab.length - 1);
5370 if (auto tid = t.isTypeIdentifier())
5372 // Try to resolve the TypeIdentifier to its type
5373 auto pt = lookupTypedef(tid.ident);
5374 if (pt && *pt)
5375 t = *pt;
5377 auto tab = cast(void*[void*])(typedefTab[$ - 1]);
5378 tab[cast(void*)id] = cast(void*)t;
5379 typedefTab[$ - 1] = cast(void*)tab;
5382 /*********************************
5383 * Lookup id in typedefTab[].
5384 * Returns:
5385 * if not found, then null.
5386 * if found, then Type*. Deferencing it will yield null if it is not
5387 * a typedef, and a type if it is a typedef.
5389 AST.Type* lookupTypedef(Identifier id)
5391 foreach_reverse (tab; typedefTab[])
5393 if (auto pt = cast(void*)id in cast(void*[void*])tab)
5395 return cast(AST.Type*)pt;
5398 return null; // not found
5403 /******************************************************************************/
5404 /********************************* Directive Parser ***************************/
5407 override bool parseSpecialTokenSequence()
5409 Token n;
5410 scan(&n);
5411 if (n.value == TOK.int32Literal)
5413 poundLine(n, true);
5414 return true;
5416 if (n.value == TOK.identifier)
5418 if (n.ident == Id.line)
5420 poundLine(n, false);
5421 return true;
5423 else if (defines && (n.ident == Id.define || n.ident == Id.undef))
5425 /* Append this line to `defines`.
5426 * Not canonicalizing it - assume it already is
5428 defines.writeByte('#');
5429 defines.writestring(n.ident.toString());
5430 skipToNextLine(defines);
5431 defines.writeByte('\n');
5432 return true;
5434 else if (n.ident == Id.__pragma)
5436 pragmaDirective(scanloc);
5437 return true;
5439 else if (n.ident == Id.ident) // #ident "string"
5441 scan(&n);
5442 if (n.value == TOK.string_ && n.ptr[0] == '"' && n.postfix == 0)
5444 /* gcc inserts string into the .comment section in the object file.
5445 * Just ignore it for now, but can support it later by writing
5446 * the string to obj_exestr()
5448 //auto comment = n.ustring;
5450 scan(&n);
5451 if (n.value == TOK.endOfFile || n.value == TOK.endOfLine)
5452 return true;
5454 error("\"string\" expected after `#ident`");
5455 return false;
5458 if (n.ident != Id.undef)
5459 error("C preprocessor directive `#%s` is not supported", n.toChars());
5460 return false;
5463 /*********************************************
5464 * VC __pragma
5465 * https://docs.microsoft.com/en-us/cpp/preprocessor/pragma-directives-and-the-pragma-keyword?view=msvc-170
5466 * Scanner is on the `__pragma`
5467 * Params:
5468 * startloc = location to use for error messages
5470 private void uupragmaDirective(const ref Loc startloc)
5472 const loc = startloc;
5473 nextToken(); // move past __pragma
5474 if (token.value != TOK.leftParenthesis)
5476 error(loc, "left parenthesis expected to follow `__pragma` instead of `%s`", token.toChars());
5477 nextToken();
5478 return;
5480 nextToken();
5482 if (token.value == TOK.identifier)
5484 if (token.ident == Id.pack)
5485 pragmaPack(startloc, false);
5486 else
5488 nextToken();
5489 if (token.value == TOK.leftParenthesis)
5490 cparseParens();
5494 else if (token.value == TOK.endOfFile)
5497 else if (token.value == TOK.rightParenthesis)
5500 else
5501 error(loc, "unrecognized `__pragma(%s)`", token.toChars());
5503 if (token.value != TOK.rightParenthesis)
5505 error(loc, "right parenthesis expected to close `__pragma(...)` instead of `%s`", token.toChars());
5506 return;
5508 nextToken();
5511 /*********************************************
5512 * C11 6.10.6 Pragma directive
5513 * # pragma pp-tokens(opt) new-line
5514 * The C preprocessor sometimes leaves pragma directives in
5515 * the preprocessed output. Ignore them.
5516 * Upon return, p is at start of next line.
5518 private void pragmaDirective(const ref Loc loc)
5520 Token n;
5521 scan(&n);
5522 if (n.value == TOK.identifier && n.ident == Id.pack)
5523 return pragmaPack(loc, true);
5524 if (n.value != TOK.endOfLine)
5525 skipToNextLine();
5528 /*********
5529 * # pragma pack
5530 * https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Structure_002dPacking-Pragmas.html
5531 * https://docs.microsoft.com/en-us/cpp/preprocessor/pack
5532 * Scanner is on the `pack`
5533 * Params:
5534 * startloc = location to use for error messages
5535 * useScan = use scan() to retrieve next token, instead of nextToken()
5537 private void pragmaPack(const ref Loc startloc, bool useScan)
5539 const loc = startloc;
5541 /* Pull tokens from scan() or nextToken()
5543 void scan(Token* t)
5545 if (useScan)
5547 Lexer.scan(t);
5549 else
5551 nextToken();
5552 *t = token;
5556 Token n;
5557 scan(&n);
5558 if (n.value != TOK.leftParenthesis)
5560 error(loc, "left parenthesis expected to follow `#pragma pack`");
5561 if (n.value != TOK.endOfLine)
5562 skipToNextLine();
5563 return;
5566 void closingParen()
5568 if (n.value != TOK.rightParenthesis)
5570 error(loc, "right parenthesis expected to close `#pragma pack(`");
5572 if (n.value != TOK.endOfLine)
5573 skipToNextLine();
5576 void setPackAlign(ref const Token t)
5578 const n = t.unsvalue;
5579 if (n < 1 || n & (n - 1) || ushort.max < n)
5580 error(loc, "pack must be an integer positive power of 2, not 0x%llx", cast(ulong)n);
5581 packalign.set(cast(uint)n);
5582 packalign.setPack(true);
5585 scan(&n);
5587 if (!records)
5589 records = new Array!Identifier;
5590 packs = new Array!structalign_t;
5593 /* # pragma pack ( show )
5595 if (n.value == TOK.identifier && n.ident == Id.show)
5597 if (packalign.isDefault())
5598 eSink.warning(startloc, "current pack attribute is default");
5599 else
5600 eSink.warning(startloc, "current pack attribute is %d", packalign.get());
5601 scan(&n);
5602 return closingParen();
5604 /* # pragma pack ( push )
5605 * # pragma pack ( push , identifier )
5606 * # pragma pack ( push , integer )
5607 * # pragma pack ( push , identifier , integer )
5609 if (n.value == TOK.identifier && n.ident == Id.push)
5611 scan(&n);
5612 Identifier record = null;
5613 if (n.value == TOK.comma)
5615 scan(&n);
5616 if (n.value == TOK.identifier)
5618 record = n.ident;
5619 scan(&n);
5620 if (n.value == TOK.comma)
5622 scan(&n);
5623 if (n.value == TOK.int32Literal)
5625 setPackAlign(n);
5626 scan(&n);
5628 else
5629 error(loc, "alignment value expected, not `%s`", n.toChars());
5632 else if (n.value == TOK.int32Literal)
5634 setPackAlign(n);
5635 scan(&n);
5637 else
5638 error(loc, "alignment value expected, not `%s`", n.toChars());
5640 this.records.push(record);
5641 this.packs.push(packalign);
5642 return closingParen();
5644 /* # pragma pack ( pop )
5645 * # pragma pack ( pop PopList )
5646 * PopList :
5647 * , IdentifierOrInteger
5648 * , IdentifierOrInteger PopList
5649 * IdentifierOrInteger:
5650 * identifier
5651 * integer
5653 if (n.value == TOK.identifier && n.ident == Id.pop)
5655 scan(&n);
5656 size_t len = this.records.length;
5657 if (n.value == TOK.rightParenthesis) // #pragma pack ( pop )
5659 if (len == 0) // nothing to pop
5660 return closingParen();
5662 this.records.setDim(len - 1);
5663 this.packs.setDim(len - 1);
5664 if (len == 1) // stack is now empty
5665 packalign.setDefault();
5666 else
5667 packalign = (*this.packs)[len - 1];
5668 return closingParen();
5670 while (n.value == TOK.comma) // #pragma pack ( pop ,
5672 scan(&n);
5673 if (n.value == TOK.identifier)
5675 /* pragma pack(pop, identifier
5676 * Pop until identifier is found, pop that one too, and set
5677 * alignment to the new top of the stack.
5678 * If identifier is not found, do nothing.
5680 for ( ; len; --len)
5682 if ((*this.records)[len - 1] == n.ident)
5684 this.records.setDim(len - 1);
5685 this.packs.setDim(len - 1);
5686 if (len > 1)
5687 packalign = (*this.packs)[len - 2];
5688 else
5689 packalign.setDefault(); // stack empty, use default
5690 break;
5693 scan(&n);
5695 else if (n.value == TOK.int32Literal)
5697 setPackAlign(n);
5698 scan(&n);
5700 else
5702 error(loc, "identifier or alignment value expected following `#pragma pack(pop,` not `%s`", n.toChars());
5703 scan(&n);
5706 return closingParen();
5708 /* # pragma pack ( integer )
5709 * Sets alignment to integer
5711 if (n.value == TOK.int32Literal)
5713 setPackAlign(n);
5714 scan(&n);
5715 return closingParen();
5717 /* # pragma pack ( )
5718 * Sets alignment to default
5720 if (n.value == TOK.rightParenthesis)
5722 packalign.setDefault();
5723 return closingParen();
5726 error(loc, "unrecognized `#pragma pack(%s)`", n.toChars());
5727 if (n.value != TOK.endOfLine)
5728 skipToNextLine();
5733 /******************************************************************************/
5734 /********************************* #define Parser *****************************/
5738 * Go through the #define's in the defines buffer and see what we can convert
5739 * to Dsymbols, which are then appended to symbols[]
5741 void addDefines()
5743 if (!defines || defines.length < 10) // minimum length of a #define line
5744 return;
5745 OutBuffer* buf = defines;
5746 defines = null; // prevent skipToNextLine() and parseSpecialTokenSequence()
5747 // from appending to slice[]
5748 const length = buf.length;
5749 buf.writeByte(0);
5750 auto slice = buf.peekChars()[0 .. length];
5751 resetDefineLines(slice); // reset lexer
5753 const(char)* endp = &slice[length - 7];
5755 size_t[void*] defineTab; // hash table of #define's turned into Symbol's
5756 // indexed by Identifier, returns index into symbols[]
5757 // The memory for this is leaked
5759 void addVar(AST.VarDeclaration v)
5761 //printf("addVar() %s\n", v.toChars());
5762 v.isCmacro(true); // mark it as coming from a C #define
5763 /* If it's already defined, replace the earlier
5764 * definition
5766 if (size_t* pd = cast(void*)v.ident in defineTab)
5768 //printf("replacing %s\n", v.toChars());
5769 (*symbols)[*pd] = v;
5770 return;
5772 defineTab[cast(void*)v.ident] = symbols.length;
5773 symbols.push(v);
5776 Token n;
5778 while (p < endp)
5780 if (p[0 .. 7] == "#define")
5782 p += 7;
5783 scan(&n);
5784 //printf("%s\n", n.toChars());
5785 if (n.value == TOK.identifier)
5787 auto id = n.ident;
5788 scan(&n);
5790 AST.Type t;
5792 switch (n.value)
5794 case TOK.endOfLine: // #define identifier
5795 nextDefineLine();
5796 continue;
5798 case TOK.int32Literal:
5799 case TOK.charLiteral: t = AST.Type.tint32; goto Linteger;
5800 case TOK.uns32Literal: t = AST.Type.tuns32; goto Linteger;
5801 case TOK.int64Literal: t = AST.Type.tint64; goto Linteger;
5802 case TOK.uns64Literal: t = AST.Type.tuns64; goto Linteger;
5804 Linteger:
5805 const intvalue = n.intvalue;
5806 scan(&n);
5807 if (n.value == TOK.endOfLine)
5809 /* Declare manifest constant:
5810 * enum id = intvalue;
5812 AST.Expression e = new AST.IntegerExp(scanloc, intvalue, t);
5813 auto v = new AST.VarDeclaration(scanloc, t, id, new AST.ExpInitializer(scanloc, e), STC.manifest);
5814 addVar(v);
5815 nextDefineLine();
5816 continue;
5818 break;
5820 case TOK.float32Literal: t = AST.Type.tfloat32; goto Lfloat;
5821 case TOK.float64Literal: t = AST.Type.tfloat64; goto Lfloat;
5822 case TOK.float80Literal: t = AST.Type.tfloat80; goto Lfloat;
5823 case TOK.imaginary32Literal: t = AST.Type.timaginary32; goto Lfloat;
5824 case TOK.imaginary64Literal: t = AST.Type.timaginary64; goto Lfloat;
5825 case TOK.imaginary80Literal: t = AST.Type.timaginary80; goto Lfloat;
5827 Lfloat:
5828 const floatvalue = n.floatvalue;
5829 scan(&n);
5830 if (n.value == TOK.endOfLine)
5832 /* Declare manifest constant:
5833 * enum id = floatvalue;
5835 AST.Expression e = new AST.RealExp(scanloc, floatvalue, t);
5836 auto v = new AST.VarDeclaration(scanloc, t, id, new AST.ExpInitializer(scanloc, e), STC.manifest);
5837 addVar(v);
5838 nextDefineLine();
5839 continue;
5841 break;
5843 case TOK.string_:
5844 const str = n.ustring;
5845 const len = n.len;
5846 const postfix = n.postfix;
5847 scan(&n);
5848 if (n.value == TOK.endOfLine)
5850 /* Declare manifest constant:
5851 * enum id = "string";
5853 AST.Expression e = new AST.StringExp(scanloc, str[0 .. len], len, 1, postfix);
5854 auto v = new AST.VarDeclaration(scanloc, null, id, new AST.ExpInitializer(scanloc, e), STC.manifest);
5855 addVar(v);
5856 nextDefineLine();
5857 continue;
5859 break;
5861 default:
5862 break;
5865 skipToNextLine();
5867 else
5869 scan(&n);
5870 if (n.value != TOK.endOfLine)
5872 skipToNextLine();
5875 nextDefineLine();
5878 defines = buf;